Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Each released version is tagged in git (`v0.0.1`, `v0.1.0`, etc.) and includes t

### Added

- **`draftwise clarify [<feature>]` — surface gaps in an existing product spec before drafting the technical spec.** Reads `product-spec.md` and prints an instruction telling the host coding agent to audit the spec across four categories (ambiguities, untested assumptions, internal contradictions, missing edge cases), present each issue with a specific line/section pointer, walk the PM through them one at a time (defer / reject / answer), and rewrite the file in place — preserving the YAML frontmatter and any sections the PM didn't touch. Hard rule: don't widen scope; if the PM's answer expands the feature, push back — that's a new spec, not a clarification. Same shape as `tech` (auto-picks when one product spec exists; positional slug picks one when many; errors with the available list when ambiguous). Why: drafted specs are uneven — some sections are tight, others have hand-wavy acceptance criteria or untested assumptions buried in scope. Catching those *before* the technical spec gets drafted means the engineering plan isn't built on a shaky foundation, and PMs don't have to re-litigate the same ambiguity in three files. Companion to `draftwise refine` (queued in the same release): `clarify` is the upfront quality check, `refine` rewrites after PM edits. Prompt at `src/ai/prompts/clarify.js`; command at `src/commands/clarify.js`; routed via `COMMANDS` in `src/index.js`. — Ankur

- **`product-spec.md` opens with optional YAML frontmatter declaring `depends_on:` and `related:` slugs; `draftwise list` surfaces them.** `draftwise new` now instructs the host coding agent to list `.draftwise/specs/` first, then write a frontmatter block at the top of `product-spec.md` — `depends_on` for specs that must ship before this one, `related` for same-area specs that aren't a hard dependency. Both keys take a list of existing slugs (the agent is told never to invent slugs that aren't on disk; if there are no other specs yet the agent skips the block entirely). Frontmatter is parsed by a new `src/utils/frontmatter.js` (small wrapper over the `yaml` package — handles CRLF, malformed YAML, and non-object roots by falling back to empty data so a broken spec file doesn't break the whole list). `draftwise list` grew a fourth column — DEPENDS ON — between STATUS and TITLE; a spec with no frontmatter or no `depends_on` shows blank. Why: cross-spec dependencies were a deferred-for-later item in CLAUDE.md, but the cheap version (declarative, in the spec, surfaced in `list`) is enough to answer the actual question PMs ask ("which specs are ready to start?") without needing graph traversal, drift detection, or any of the heavier machinery. Stays in a frontmatter block so it doesn't bleed into the rendered spec body. — Ankur

- **`.draftwise/constitution.md` — user-editable rules file the host coding agent reads on every drafting command.** `draftwise init` writes a default template into the project on both the brownfield and greenfield paths. Five stable section headings every prompt module references by name: **Voice** (how the agent talks — no filler, push back on weak ideas, ground every claim, turn gaps into questions), **Spec language** (specific over generic, active voice, same term every time), **Edge case discipline** (technical specs only — empty data / errors / loading / permissions / concurrency / large data), **Project conventions** (placeholder filled in by `scan` from observed code or by the greenfield Phase 3 from the chosen stack), and **Domain glossary** (empty by default; project-specific terms). Replaces the role that `src/ai/prompts/principles.js` and `src/ai/prompts/spec-quality.js` used to play before the api-mode drop — those modules injected collaboration + spec-language rules into every system prompt; with synthesis moved entirely into the host coding agent, those rules now live in the user's repo as a markdown file the agent reads on each call. Every drafting prompt (`new`, `tech`, `tasks`, `scan`, `explain`, `greenfield` Phase 3) got a one-line "before drafting, read `.draftwise/constitution.md` if it exists and apply its Voice / Spec language / Edge case discipline sections; skip silently if absent" — back-compat for projects that ran `init` before this feature landed. `scan` and the greenfield Phase 3 additionally instruct the agent to refine the **Project conventions** section from observed code / chosen stack, replacing the placeholder. Reader utility at `src/utils/constitution.js` (`readConstitution(cwd)` returns null on ENOENT, propagates other errors); template at `src/utils/constitution-template.js`. Why: with the api path gone, the project needed somewhere stable for users to tune Draftwise's voice and spec discipline without editing prompts in the npm package — the constitution is that surface, and it's version-controlled alongside the code so changes are reviewable. — Ankur
Expand Down
11 changes: 7 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ draftwise scaffold → create initial files from the greenf
draftwise scan → refresh the structured codebase overview (brownfield)
draftwise explain <flow> → trace how a specific flow works in the actual code (brownfield)
draftwise new "<idea>" → conversational drafting → product-spec.md (host agent writes)
draftwise clarify [<feature>] → audit a product spec for ambiguities + missing edge cases; rewrite in place
draftwise tech [<feature>] → technical-spec.md from approved product spec (host agent writes)
draftwise tasks [<feature>] → ordered tasks.md from technical spec (host agent writes)
draftwise list → list all specs in .draftwise/specs/
Expand Down Expand Up @@ -172,13 +173,15 @@ Every command is implemented end-to-end and exercised by a vitest suite. The ori

4. **`new "<idea>"`** ✅ — brownfield: prints scanner data + the idea + a 3-phase instruction (plan / Q&A / synthesis) for the host agent to walk the conversation and write `product-spec.md`. Greenfield: skips the scanner and reads `overview.md` (the project plan from `init`); the instruction tells the agent to ask clarifying questions only (no affected_flows / adjacent_opportunities) and write a spec without "Affected flows" / "Adjacent changes" sections. (`src/commands/new.js`, prompt in `src/ai/prompts/new.js`)

5. **`tech [<feature>]`** ✅ — reads `product-spec.md`, prints it plus scanner output (brownfield) or the project plan (greenfield) plus an instruction for the host agent to write `technical-spec.md`. Greenfield marks every file path `(new)` and uses the chosen stack's conventions. (`src/commands/tech.js`, prompt in `src/ai/prompts/tech.js`)
5. **`clarify [<feature>]`** ✅ — reads `product-spec.md`, prints it plus an instruction telling the host agent to audit it across four categories (ambiguities, untested assumptions, internal contradictions, missing edge cases), walk the PM through each one, and rewrite the file in place — preserving the YAML frontmatter and any sections the PM didn't touch. Same auto-pick / multi-spec / unknown-slug ergonomics as `tech` and `tasks`. Designed to run *between* `new` and `tech` so the technical spec isn't built on a shaky foundation. (`src/commands/clarify.js`, prompt in `src/ai/prompts/clarify.js`)

6. **`tasks [<feature>]`** ✅ — reads `technical-spec.md`, prints it plus scanner output (brownfield) or the project plan (greenfield) plus an instruction for the host agent to write ordered `tasks.md` (Goal / Files / Depends on / Parallel with / Acceptance). Greenfield front-loads 1-3 scaffolding tasks. (`src/commands/tasks.js`, prompt in `src/ai/prompts/tasks.js`)
6. **`tech [<feature>]`** ✅ — reads `product-spec.md`, prints it plus scanner output (brownfield) or the project plan (greenfield) plus an instruction for the host agent to write `technical-spec.md`. Greenfield marks every file path `(new)` and uses the chosen stack's conventions. (`src/commands/tech.js`, prompt in `src/ai/prompts/tech.js`)

7. **`list` and `show <feature> [type]`** ✅ — file-system utilities, no AI. (`src/commands/list.js`, `src/commands/show.js`)
7. **`tasks [<feature>]`** ✅ — reads `technical-spec.md`, prints it plus scanner output (brownfield) or the project plan (greenfield) plus an instruction for the host agent to write ordered `tasks.md` (Goal / Files / Depends on / Parallel with / Acceptance). Greenfield front-loads 1-3 scaffolding tasks. (`src/commands/tasks.js`, prompt in `src/ai/prompts/tasks.js`)

8. **`scaffold`** ✅ — greenfield-only file scaffolder. Reads `.draftwise/scaffold.json` (written by the host coding agent during init's greenfield handoff), confirms with the user — including a warning that scaffolders like `create-next-app` should run first — then creates each `initial_files` entry with placeholder content (skipping any that already exist). Prints the `setup_commands` as a reminder; doesn't run them. (`src/commands/scaffold.js`)
8. **`list` and `show <feature> [type]`** ✅ — file-system utilities, no AI. (`src/commands/list.js`, `src/commands/show.js`)

9. **`scaffold`** ✅ — greenfield-only file scaffolder. Reads `.draftwise/scaffold.json` (written by the host coding agent during init's greenfield handoff), confirms with the user — including a warning that scaffolders like `create-next-app` should run first — then creates each `initial_files` entry with placeholder content (skipping any that already exist). Prints the `setup_commands` as a reminder; doesn't run them. (`src/commands/scaffold.js`)

---

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ Claude Code namespaces plugin skills as `<plugin>:<skill>`, so the slash form is
| `draftwise scan` | Refresh the codebase overview (brownfield). |
| `draftwise explain <flow>` | Trace a specific flow through the code (brownfield). |
| `draftwise new "<idea>"` | Draft a product spec — clarifying questions plus grounded synthesis. |
| `draftwise clarify [<feature>]` | Audit a product spec for ambiguities, untested assumptions, and missing edge cases; rewrite in place. |
| `draftwise tech [<feature>]` | Technical spec from the product spec, grounded in real files. |
| `draftwise tasks [<feature>]` | Implementation tasks from the tech spec, dependency-ordered. |
| `draftwise scaffold --yes` | Create initial files from a greenfield plan. |
Expand Down
31 changes: 31 additions & 0 deletions src/ai/prompts/clarify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export function buildAgentInstruction(slug) {
return `The product spec above is in .draftwise/specs/${slug}/product-spec.md. The PM wants to clarify it before drafting the technical spec.

Before clarifying, read .draftwise/constitution.md if it exists and apply its Voice and Spec language sections to the conversation and the rewritten spec. Skip silently if the file is absent.

Run a clarification conversation in three phases:

PHASE 1 — Audit:
- Read the spec end to end. Identify 4-10 specific issues across these categories:
- Ambiguities (terms used inconsistently, vague acceptance criteria, undefined scope boundaries).
- Untested assumptions (claims about user behavior, market conditions, technical feasibility — none of them validated in the spec).
- Internal contradictions (a section saying X and another saying not-X).
- Missing edge cases (empty data, errors, permissions, concurrency, large data — anything the agent or a reviewer would ask about).
- Each issue must point at a specific line, section, or sentence — not "the spec is vague."
- Skip the spec entirely if it's already tight; tell the PM and exit. Don't pad with low-value nitpicks.

PHASE 2 — Walk the PM through the issues:
- Group issues by category. Present each one in plain language: what's unclear, why it matters, what kinds of answers would resolve it.
- One question at a time. Wait for the answer.
- The PM is allowed to defer ("not now") or reject ("intentional") an issue — record those outcomes; don't re-litigate.

PHASE 3 — Rewrite the spec in place:
- Apply the answers and accepted clarifications to the existing product-spec.md. Preserve any frontmatter (\`depends_on\` / \`related\`) verbatim and any sections the PM didn't touch.
- Keep the same section order. Don't add new sections; rewrite the affected ones.
- Save back to .draftwise/specs/${slug}/product-spec.md, replacing the file.

Hard rules:
- No fabricated answers. If the PM defers an issue, leave the spec as-is for that issue and add a one-line note in the relevant section ("Open: <one-sentence framing>") so the next pass can pick it up.
- Don't widen scope. If the PM's answer expands the feature, push back — that's a new spec, not a clarification.
- Don't rewrite sections the PM didn't comment on, even if you'd word them differently.`;
}
92 changes: 92 additions & 0 deletions src/commands/clarify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { readFile } from 'node:fs/promises';
import { parseArgs } from 'node:util';
import { listSpecs as defaultListSpecs } from '../utils/specs.js';
import { requireDraftwiseDir } from '../utils/draftwise-dir.js';
import { AGENT_HANDOFF_PREFIX } from '../utils/agent-handoff.js';
import { buildAgentInstruction } from '../ai/prompts/clarify.js';

export const HELP = `draftwise clarify [<feature>] — surface gaps in an existing product spec

Usage:
draftwise clarify # auto-pick if exactly one product spec exists
draftwise clarify <feature-slug> # target a specific feature

Reads the product spec and prints an instruction for your coding agent
to audit it for ambiguities, untested assumptions, internal
contradictions, and missing edge cases — then walk the PM through each
one and rewrite the spec in place.

When multiple product specs exist and no <feature-slug> is supplied,
the command errors with the available slugs.
`;

const ARG_OPTIONS = {};

export default async function clarifyCommand(args = [], deps = {}) {
const cwd = deps.cwd ?? process.cwd();
const log = deps.log ?? ((msg) => console.error(msg));
const listSpecs = deps.listSpecs ?? defaultListSpecs;

await requireDraftwiseDir(cwd);

let parsed;
try {
parsed = parseArgs({
args,
options: ARG_OPTIONS,
allowPositionals: true,
strict: true,
});
} catch (err) {
throw new Error(`Invalid arguments to draftwise clarify: ${err.message}`, {
cause: err,
});
}
const requestedSlug = parsed.positionals[0];

const specs = (await listSpecs(cwd)).filter((s) => s.hasProductSpec);
if (specs.length === 0) {
throw new Error(
'No product specs found in .draftwise/specs/. Run `draftwise new "<idea>"` first.',
);
}

let target;
if (requestedSlug) {
target = specs.find((s) => s.slug === requestedSlug);
if (!target) {
const available = specs.map((s) => s.slug).join(', ');
throw new Error(
`No product spec found for "${requestedSlug}". Available: ${available}`,
);
}
} else if (specs.length === 1) {
target = specs[0];
log(`Using the only product spec: ${target.slug}`);
} else {
const available = specs.map((s) => s.slug).join(', ');
throw new Error(
`Multiple product specs exist. Pass one as a positional argument: draftwise clarify <slug>. Available: ${available}`,
);
}

const productSpec = await readFile(target.productSpec, 'utf8');
if (!productSpec.trim()) {
throw new Error(
`${target.slug}/product-spec.md is empty. Run \`draftwise new\` to populate it.`,
);
}

log('');
log('Handing the product spec off to your coding agent for clarification.');
log(AGENT_HANDOFF_PREFIX);
log('');
log('---');
log(`SPEC: ${target.slug}`);
log('');
log('PRODUCT SPEC');
log(productSpec);
log('');
log('INSTRUCTION');
log(buildAgentInstruction(target.slug));
}
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const COMMANDS = {
scan: () => import('./commands/scan.js'),
explain: () => import('./commands/explain.js'),
new: () => import('./commands/new.js'),
clarify: () => import('./commands/clarify.js'),
tech: () => import('./commands/tech.js'),
tasks: () => import('./commands/tasks.js'),
list: () => import('./commands/list.js'),
Expand Down Expand Up @@ -38,6 +39,7 @@ Commands:
scan Refresh the codebase overview
explain <flow> Trace how a specific flow works in the code
new "<idea>" Conversational drafting → product-spec.md
clarify [<feature>] Audit a product spec for ambiguities + missing edge cases
tech [<feature>] Draft technical-spec.md from approved product spec
tasks [<feature>] Generate ordered tasks.md from technical spec
list List all specs in .draftwise/specs/
Expand Down
Loading