prdforge
Turns a vague idea into a deep PRD by self-interviewing: instead of asking the user ten questions one at a time (native plan / deep-interview), it fans out interviewer agents that ask and answer the questions themselves, flag what they can’t confidently answer as Open Questions, and emit a styled HTML PRD + a paste-ready prompt.
This is the local replacement for the native /ultraplan, which requires a cloud session and a git repo. prdforge needs neither.
When to invoke
- The user wants a deep plan / PRD / spec for a non-trivial feature, refactor, or system.
- The request reads like a one-liner (“add billing”, “make it multi-tenant”) that needs unpacking before code.
- Triggers: “prdforge”, “ultraprd”, “selfplan”, “deepplan”, “make a PRD”, “plan this deeply”, “write me a spec”.
Skip for: one-line fixes, lookups, trivial edits, or “just write the code”.
How to run (exact steps)
- Parse the request. Strip any flags from the user’s text. Mode is always
deep(see Decision rules); strip--quick/--balancedif given. Recognize the--handoffflag (extra artifact: cheap-model execution plan, step 7),--inputs <file> [<file>...](documents whose facts, requirement IDs and demand evidence feed the PRD, for example a requirements register or an earlier analysis), and an optional output path. Map the mode flag tomodeforargsbelow. - Compute
slugfrom the idea: kebab-case, lowercased, max ~6 words (e.g. “rate limiting for the API” →rate-limiting-for-the-api). You (the main loop) MUST compute this; the Workflow script cannot (noDate.now()/Math.random()inside scripts). Also computedate(today, e.g.24 September 2026): the script cannot callDate. - Resolve
outDir=plansrelative to cwd, unless the user named a directory. - Detect
brownfield= does cwd look like a code project? (presence ofpackage.json,.git,src/,Cargo.toml,go.mod,pyproject.toml, etc.) If unsure, treat as brownfield. -
Run the workflow: this is the ultracode opt-in:
Requires
mmdcfor the PRD, not only for the plan (npm i -g @mermaid-js/mermaid-cli). Every PRD diagram is baked to SVG at render time; withoutmmdcon PATH the render exits 1 (it will not ship raw mermaid text) and the bundle refuses to build.Paths must be absolute. Expand
~to your home directory before calling: theWorkflowtool’sscriptPathand the workflow’s innerRead(templatePath)do NOT expand~: a literal~fails to resolve, andtemplatePaththen silently degrades to the generated-from-contract fallback (un-themed PRD). Pass e.g./home/<you>/.claude/skills/prdforge/assets/....Workflow({ scriptPath: "<HOME>/.claude/skills/prdforge/assets/self-interview.workflow.js", args: { idea: "the cleaned request", brownfield: true | false, mode: "deep", outDir: "plans", slug: "computed-slug", templatePath: "<HOME>/.claude/skills/prdforge/assets/prd-template.html", rendererPath: "<HOME>/.claude/skills/prdforge/assets/render_prd.py", lintPath: "<HOME>/.claude/skills/prd-review/scripts/score.py", inputs: ["/absolute/path/to/input.md"], // [] when none date: "24 September 2026" } })Pass
argsas a real object, not a JSON string (the script self-parses either, but an object is correct). - If
verifiedis false, STOP. The render failed a check (diagrams, dashes, external references, anything) even after the fix pass. Do not runbundle.pyand do not publish anything. Report the render’s issues and the file paths, and say what has to change. On return withverifiedtrue, print a tight summary: one line on what was planned, the readiness score (readiness.score/100 + label), the linter score (lintScore/100 from prd-review, or “not run”), the four artifact paths (<slug>-prd.html,<slug>-prd.md,<slug>-prompt.md,<slug>-prd.json) plus the bundle link (below), and the PRD’s Open Questions (Q-n, question, confidence, owner: the low-confidence defaults prdforge assumed and wants confirmed). Then offer (do not force) either to hand the paste-ready prompt straight to an implementation agent (whatever the user has: a general-purposeAgentcall is the stock option), or to answer the open questions so prdforge can tighten the prompt first. Never auto-implement and never auto-ask; prdforge stops at the PRD. Bundle last, as the one link: after steps 7 and 8 have written their files, runpython3 <HOME>/.claude/skills/prdforge/assets/bundle.py --out-dir <outDir> --slug <slug>(must exit 0) and publish<slug>-bundle.htmlas the single Artifact link in the summary.bundle.pyre-runs the diagrams gate on<slug>-prd.jsonand the PRD page itself and exits 1 without writing if it fails; a non-zero exit means stop, never publish an older file. - Handoff render (only if
--handoff): spawn ONE writer agent (model=sonnet) that readsoutDir/<slug>-prd.jsonand writesoutDir/<slug>-handoff.mdin the HANDOFF PLAN format defined inreferences/prd-contract.md. Rules for the agent:- TASKS come from the FR IDs: each task names the FR it serves and its done-check is that FR’s Given/When/Then scenarios. Milestones come from the PRD’s
releases. - Every TASK sized for one Sonnet session: names the files it touches + a done-check (command or observable) a model can verify alone.
- SUCCESS items must be mechanically checkable (command + expected output), never “works well”.
- Open Questions from the PRD land in HANDOFF > Gotchas as “ASK USER FIRST” items. Add the handoff path to the step-6 summary.
- TASKS come from the FR IDs: each task names the FR it serves and its done-check is that FR’s Given/When/Then scenarios. Milestones come from the PRD’s
- Plan-style render: ON BY DEFAULT (the user’s rule, 24 Sep 2026: he wants the diagrams and artwork every run; skip only on
--no-plan). After the PRD, spawn ONE writer agent (inherit top tier: this is judgment writing, not mechanical) that readsoutDir/<slug>-prd.jsonplus the source material and writesoutDir/<slug>-PLAN.mdin the chaptered three-act format defined inassets/plan-style/STYLE.md, then build and style it per that file’s pipeline (write aplan-config.json, run inject and assemble), then rebuild the bundle (step 6) so the plan appears as its tab; publish the bundle, not the plan on its own. This is the approval-grade, leadership-readable format; the PRD stays the machine artifact. The PLAN is not a specification: it must cite the PRD’s FR/G/D/Q IDs rather than restate capabilities at module level, and its release chapter must carry the PRD’s sizes. The styled HTML build needsnpx marked,mmdcand a localchrome-headless-shell; if they are missing, deliver the markdown PLAN and say the styling step was skipped. Artwork (hero plus act dividers) comes from theimagegenskill as 16:9 photographs of real objects (not abstract shapes), no text, no faces; resize to 1800px quality-82 JPEGs inoutDir/art/. Add the share-file path to the step-6 summary.
Decision rules
- Mode: always full (
deep). the user’s rule (24 Sep 2026): every run is 7 personas, 2 rounds, critic and revise, about 20 agents. Three personas hold fixed stances (6 Oct 2026, from the rotating-teams thread): product speaks for the end user, skeptic cuts scope, brief questions the brief.--quick/--balancedare ignored and logged. Measured 24 Sep 2026 with 6 personas (expect a little more with 7): 1.17M subagent tokens and 17 min on a one-flag feature, so say the cost before a run. - Critic pass → after synthesis,
criticadversarially reviews the draft PRD (fabrication, scope creep, unverifiable criteria, missing risks); a reviser applies any med/high findings before render. Separate writer/reviewer lanes. - Greenfield (no codebase) → the Scout phase is skipped; personas reason from the idea alone.
- Brownfield → a Scout (Explore) agent gathers repo facts AND up to ~60 behaviour rules (
<AREA>-n, Preserve/Decide,file:line) so requirements trace below module level. If the idea targets a specific path, name it in the idea text so Scout focuses there. - Input documents (
--inputs) → a second Scout reads them; requirement IDs, demand evidence and figures come through tagged[Doc: name], and the PRD states its prioritisation rule from them. - Scope discipline → the Skeptic persona exists to cut over-built scope; honor its “simplest version” output in Non-Goals.
Output
Four files in outDir (five with --handoff), all built by assets/render_prd.py from the JSON:
<slug>-prd.html: self-contained PRD in the Project-F visual system (same tokens and furniture asassets/plan-style/): sidebar nav with scrollspy, a hero with title, one-line summary and stat cards (readiness meter in the first), a “five-minute version” panel, 3 or 4 required captioned diagrams (system, main flow, delivery; the render fails without them), chapters grouped into acts, requirement cards with Given/When/Then, gate cards, light and dark themes plus a flip button, copy-prompt, and an Open-Questions checklist whose ticks persist in the browser (with an answered count). Every ID mention (FR, G, D, Q, R, legacy rules) links to where it is defined and highlights it; requirements have a priority mix bar plus MoSCoW filter and search; risks get a likelihood by impact heatmap; stat tiles jump to their chapter; a reading bar tracks progress. No sideways scroll at phone width. Opens offline, zero external requests. It is a whole document, so it can be emailed as is or published as an Artifact.<slug>-prd.md: the same PRD as markdown, the input the prd-review linter scores.<slug>-prompt.md: the implementation prompt in Context / Objective / Boundaries / Validation form.<slug>-prd.json: the final PRD model, including the lint result, for downstream tooling.<slug>-handoff.md: (only with--handoff) cheap-model execution plan.<slug>-PLAN.mdplus<slug>-plan.html/<slug>-plan-share.html: (default, skip with--no-plan) the chaptered delivery plan with baked diagrams and generated artwork (step 8).<slug>-bundle.html: every output above in one offline page behind a sticky tab bar (PRD, Plan, Prompt, Handoff; a tab is skipped when its file is missing), one theme toggle for all, tab in the URL hash (#plan). Built byassets/bundle.py; this is the link to publish.
Section contract, the readiness/openQuestions shapes, and the prompt format are defined in references/prd-contract.md (reuses the spec skill’s framing). Consult it if the PRD model needs adjusting.
Common Issues
Workflowtool not available or not opted in (most likely first failure on a fresh install) → run the same phases by hand with theAgenttool: spawn the personas as parallelAgent(subagent_type="general-purpose")calls, synthesize their answers inline, send the draft to one moregeneral-purposeagent for an adversarial critique pass, then write the PRD JSON to<outDir>/<slug>-prd.jsonand runrender_prd.pyon it yourself. Same artifacts, no deterministic orchestration and no retry/resume.Writedoesn’t create parent dirs → the Render agent runsmkdir -p <outDir>before writing.- Render failed → run
python3 <rendererPath> --json <outDir>/<slug>-prd.json --template <templatePath> --lint <lintPath>by hand; it prints what failed. IfrendererPathwas not passed, the render agent hand-builds the files fromreferences/prd-contract.mdand no lint score is produced. - Linter score low → Verify already ran one fix pass. Open
<slug>-prd.mdand run/prd-reviewon it for the semantic review the linter cannot do. - Slug or timestamp logic → never inside the Workflow script (
Date.now()/Math.random()throw). Computeslugin the main loop and pass viaargs. - Cost → every run is full depth (about 1.2M subagent tokens). There is no cheap mode by design.
- Interactive HTML broken / not themed → the template’s inline
<script>and<style>must survive;render_prd.pyexits 1 if either is missing. Edit the shell, never the generated file.
References
references/prd-contract.md: PRD section definitions + implementation-spec format.assets/self-interview.workflow.js: the ultracode orchestration script.assets/prd-template.html: the HTML shell (CSS + JS); the body is generated.assets/render_prd.py: deterministic JSON to HTML + markdown + prompt renderer, self-check, lint.assets/bundle.py: deterministic tabbed bundle of all outputs for one slug (iframe per document).assets/check_contrast.py: WCAG AA gate for the theme families; run it on any built page (--allfor every family).assets/plan-style/STYLE.md: the chaptered approval-grade PLAN format + styled HTML shell (Stripe-derived, sidebar nav, gate cards, CSS gantt, five-minute TLDR).inject.pyandassemble.pybuild it from a per-projectplan-config.json; seeplan-config.example.jsonfor the shape. Used by step 8 (default;--no-planskips it).