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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ Each released version is tagged in git (`v0.0.1`, `v0.1.0`, etc.) and includes t

## [Unreleased]

### Added

- **`.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

### Removed

- **`@inquirer/prompts` dropped; CLI is purely flag-driven now.** Every interactive prompt is gone: init's idea input, tech/tasks' spec picker, and scaffold's confirm. Each was a TTY-only convenience layer on top of flags that already drove the canonical input path. With api mode gone (see entry below), the remaining prompts had nothing meaningful to gate — the CLI's job is to load context and hand off to the host coding agent, not run a Q&A loop. Concrete behavior changes: `tech` / `tasks` with multiple specs and no slug arg now error with the available list (was: TTY → inquirer picker, non-TTY → error); `scaffold` requires `--yes` to confirm before writing (was: TTY → inquirer confirm, non-TTY → error); `init` greenfield without `--idea` always prints the structured agent handoff (was: TTY → inquirer input, non-TTY → handoff). The TTY/non-TTY distinction is gone entirely — `src/utils/tty.js`, `test/setup.js`, and `vitest.config.js` are deleted; `deps.isInteractive` is no longer a dependency-injection seam. CLAUDE.md and README.md updated to drop "TTY-only fallback" framing and replace with "flags drive input; CLI never blocks on stdin." — Ankur
Expand Down
5 changes: 4 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ src/index.js → command router (dynamic imports, help)
src/commands/ → one file per CLI command, default export async fn
src/core/scanner.js → codebase scanning (frameworks, routes, components, models)
src/ai/prompts/ → one prompt module per command. Each exports a `buildAgentInstruction(...)` (or `AGENT_INSTRUCTION` constant for `scan`) that the host coding agent reads — section structure, hard rules, save path. No SDK call from the CLI; the agent does the synthesis.
src/utils/ → config.js (yaml loader; returns projectState/stack/scanMaxFiles), specs.js (list .draftwise/specs/), slug.js, overview.js (read .draftwise/overview.md for greenfield context), scan-cache.js (fingerprinted scan cache, drop-in for scan()), flow-filter.js (narrow scan to flow-relevant items), scan-warnings.js (truncation + missing-framework messages), fs.js (shared pathExists), scan-projection.js (shared compactScan that trims a raw scan into a prompt-sized projection), scan-context.js (shared greenfield/brownfield branch for new/tech/tasks), draftwise-dir.js (`requireDraftwiseDir` guard), agent-handoff.js (shared orienting prefix logged before every agent-mode handoff), project-state.js (filesystem auto-detect for `init` — bail-fast walk for source files using scanner's IGNORE_DIRS + CODE_EXTENSIONS), skill-providers.js (provider dir mapping + Claude-only frontmatter trim + `detectInstalledProviders` filesystem check shared across `skills install` / `uninstall` / `help`)
src/utils/ → config.js (yaml loader; returns projectState/stack/scanMaxFiles), specs.js (list .draftwise/specs/), slug.js, overview.js (read .draftwise/overview.md for greenfield context), scan-cache.js (fingerprinted scan cache, drop-in for scan()), flow-filter.js (narrow scan to flow-relevant items), scan-warnings.js (truncation + missing-framework messages), fs.js (shared pathExists), scan-projection.js (shared compactScan that trims a raw scan into a prompt-sized projection), scan-context.js (shared greenfield/brownfield branch for new/tech/tasks), draftwise-dir.js (`requireDraftwiseDir` guard), agent-handoff.js (shared orienting prefix logged before every agent-mode handoff), project-state.js (filesystem auto-detect for `init` — bail-fast walk for source files using scanner's IGNORE_DIRS + CODE_EXTENSIONS), skill-providers.js (provider dir mapping + Claude-only frontmatter trim + `detectInstalledProviders` filesystem check shared across `skills install` / `uninstall` / `help`), constitution.js (`readConstitution(cwd)` — reads `.draftwise/constitution.md`; returns null on ENOENT for back-compat with pre-constitution projects), constitution-template.js (default template `init` writes — five stable section headings prompts reference by name)
test/ → vitest, mirrors src structure
.claude-plugin/ → plugin marketplace declaration (see "Claude Code plugin" below)
plugin/ → plugin source tree shipped via the marketplace
Expand Down Expand Up @@ -106,6 +106,8 @@ scan:

**Flags drive input; no interactive prompts.** Every command takes its full input set as flags (`--mode`, `--idea`, `--yes`) or positional args, parsed via Node's built-in `util.parseArgs`. Missing required input errors with a specific usage hint — the CLI never blocks waiting for stdin. `draftwise init` is the one special case: greenfield without `--idea` prints a structured **agent handoff** (the question in chat-friendly format + a re-invocation template, all under `AGENT_HANDOFF_PREFIX`) and exits cleanly, so the host coding agent reads stderr, asks the user in chat, and re-invokes with the collected flag. A plain-terminal user reads the same handoff as a usage hint.

**The constitution is the user's voice + spec-quality dial.** `draftwise init` writes `.draftwise/constitution.md` with five stable sections — Voice, Spec language, Edge case discipline, Project conventions, Domain glossary. Every drafting prompt (`new`, `tech`, `tasks`, `scan`, `explain`, greenfield Phase 3) tells the host agent to read this file before drafting and apply the relevant sections. `scan` and the greenfield Phase 3 also refine the **Project conventions** section from observed code / chosen stack, replacing the placeholder. The file is user-editable and version-controlled — changes are reviewable and travel with the repo. Backward compat: prompts say "skip silently if absent" so projects that ran `init` before this feature landed keep working. Reader at `src/utils/constitution.js` returns null on ENOENT. Replaces the role `principles.js` + `spec-quality.js` used to play before the api-mode drop — those modules injected rules into every system prompt; the constitution is the new home for those rules now that synthesis lives in the host agent.

**Single repo, single feature spec at a time.** No cross-spec dependency tracking. No multi-repo. Keep scope tight.

---
Expand Down Expand Up @@ -138,6 +140,7 @@ Each command is a separate file under `src/commands/` with a single `export defa
├── .gitignore # written by init; excludes .cache/ from version control
├── .cache/
│ └── scan.json # fingerprinted scan cache (gitignored)
├── constitution.md # voice + spec-quality rules — agent reads on every drafting command
├── overview.md # codebase summary (brownfield) or greenfield plan
├── scaffold.json # greenfield only: structured stack data for `draftwise scaffold`
├── flows/ # `draftwise explain` snapshots (brownfield)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ Run `draftwise <command> --help` for the per-command flag list.
```
.draftwise/
├── .gitignore # written by init; keeps the cache out of version control
├── constitution.md # voice + spec-quality rules — edit to suit your project
├── overview.md # codebase summary (brownfield) or greenfield plan
├── scaffold.json # greenfield only; structured stack data for `draftwise scaffold`
├── specs/
Expand Down
6 changes: 5 additions & 1 deletion src/ai/prompts/explain.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
export function buildAgentInstruction(flow, slug) {
return `The scanner data above describes a real codebase. The user wants to understand how the "${flow}" flow works. Generate a markdown walkthrough following this section structure, grounded only in what the scanner produced:
return `The scanner data above describes a real codebase. The user wants to understand how the "${flow}" flow works.

Before writing the walkthrough, read .draftwise/constitution.md if it exists and apply its Voice and Spec language sections. Skip silently if the file is absent.

Generate a markdown walkthrough following this section structure, grounded only in what the scanner produced:

# Flow: ${flow}
> One-sentence summary of what this flow does, inferred from the code.
Expand Down
1 change: 1 addition & 0 deletions src/ai/prompts/greenfield.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ PHASE 2 — Recommend stacks:

PHASE 3 — Write the plan:
- Save the chosen stack and the conversation as .draftwise/overview.md, with sections: Idea, Discovery (Q&A), Chosen stack (name, summary, rationale, pros, cons), Directory structure, Initial files, Setup commands, Next steps.
- Also refine .draftwise/constitution.md (which init wrote with default text). Replace the Project conventions section with the chosen stack's directory layout and naming conventions; leave the Voice, Spec language, and Edge case discipline sections intact unless the user asked you to change them.
- Also save .draftwise/scaffold.json with the structured stack data so \`draftwise scaffold\` can use it later. Shape:
{
"stack": "<chosen stack name>",
Expand Down
4 changes: 4 additions & 0 deletions src/ai/prompts/new.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ export function buildAgentInstruction(idea, projectState = 'brownfield') {
if (projectState === 'greenfield') {
return `The PM has proposed a feature for a GREENFIELD project: "${idea}".

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

The project plan (overview.md above) describes the chosen stack and directory structure. There is no existing code yet. Run a conversation with the PM in three phases:

PHASE 1 — Plan the conversation:
Expand All @@ -21,6 +23,8 @@ Hard rule: ASK don't assume; ground every claim in the answers and the project p
}
return `The PM has proposed: "${idea}".

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

Use the scanner data above as ground truth for the existing codebase. Run a conversation with the PM in three phases:

PHASE 1 — Plan the conversation (in your head, but share the plan with the PM):
Expand Down
6 changes: 5 additions & 1 deletion src/ai/prompts/scan.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
export const AGENT_INSTRUCTION = `The scanner data above describes a real codebase. Generate an overview.md grounded only in what the scanner produced. Use these top-level sections, in order:
export const AGENT_INSTRUCTION = `The scanner data above describes a real codebase.

Before writing the overview, read .draftwise/constitution.md if it exists and apply its Voice and Spec language sections. After writing overview.md, also refine the Project conventions section of constitution.md with the naming patterns, directory layout, and architectural style you observe in the scanner data — replacing the placeholder text. Skip both steps silently if constitution.md is absent.

Generate an overview.md grounded only in what the scanner produced. Use these top-level sections, in order:

# <Product name> (use the package name as a starting point)
> One-sentence description of what this product appears to do, inferred from the codebase.
Expand Down
8 changes: 7 additions & 1 deletion src/ai/prompts/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ export function buildAgentInstruction(slug, projectState = 'brownfield') {
if (projectState === 'greenfield') {
return `The technical spec above is approved. The project is GREENFIELD — there's no existing code yet. The chosen stack and directory plan are in overview.md (above).

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

Generate tasks.md and save it to .draftwise/specs/${slug}/tasks.md.

Sections in order:
Expand All @@ -16,7 +18,11 @@ Hard rules:
- The first 1-3 tasks must be foundational scaffolding (run setup commands, install deps, configure env). Don't skip them.
- Each "Depends on" link must point at a task number you've actually defined.`;
}
return `The technical spec above is approved. Use the scanner data as ground truth. Generate tasks.md following the section structure below, ordered so each task's dependencies appear before it. Save it to .draftwise/specs/${slug}/tasks.md.
return `The technical spec above is approved. Use the scanner data as ground truth.

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

Generate tasks.md following the section structure below, ordered so each task's dependencies appear before it. Save it to .draftwise/specs/${slug}/tasks.md.

Sections in order:
- Title + one-sentence framing
Expand Down
8 changes: 7 additions & 1 deletion src/ai/prompts/tech.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ export function buildAgentInstruction(slug, projectState = 'brownfield') {
if (projectState === 'greenfield') {
return `The product spec above is approved. The project is GREENFIELD — there's no existing code yet. The chosen stack and directory plan are in overview.md (above).

Before drafting, read .draftwise/constitution.md if it exists and apply its Voice, Spec language, and Edge case discipline sections. Skip silently if the file is absent.

Generate technical-spec.md and save it to .draftwise/specs/${slug}/technical-spec.md.

Sections in order:
Expand All @@ -16,7 +18,11 @@ Sections in order:

Hard rule: every file path must be marked "(new)" and must follow the directory structure from overview.md. Match the chosen stack's conventions exactly — don't impose foreign patterns.`;
}
return `The product spec above is approved. Use the scanner data as ground truth for the existing codebase. Generate the technical-spec.md following the section structure below, grounded in real files/routes/models from the scanner. Save it to .draftwise/specs/${slug}/technical-spec.md.
return `The product spec above is approved. Use the scanner data as ground truth for the existing codebase.

Before drafting, read .draftwise/constitution.md if it exists and apply its Voice, Spec language, and Edge case discipline sections. Skip silently if the file is absent.

Generate the technical-spec.md following the section structure below, grounded in real files/routes/models from the scanner. Save it to .draftwise/specs/${slug}/technical-spec.md.

Sections in order:
- Title + one-sentence framing
Expand Down
29 changes: 21 additions & 8 deletions src/commands/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { pathExists } from '../utils/fs.js';
import { AGENT_HANDOFF_PREFIX } from '../utils/agent-handoff.js';
import { detectProjectState as defaultDetectProjectState } from '../utils/project-state.js';
import { buildAgentInstruction as buildGreenfieldAgentInstruction } from '../ai/prompts/greenfield.js';
import { CONSTITUTION_TEMPLATE } from '../utils/constitution-template.js';

export const HELP = `draftwise init — set up .draftwise/ for the current project

Expand Down Expand Up @@ -140,12 +141,18 @@ async function runBrownfield({ cwd, log, scan, draftwiseDir }) {
'utf8',
);
await writeFile(join(draftwiseDir, '.gitignore'), DRAFTWISE_GITIGNORE, 'utf8');
await writeFile(
join(draftwiseDir, 'constitution.md'),
CONSTITUTION_TEMPLATE,
'utf8',
);

log('Created .draftwise/ with:');
log(' • overview.md (placeholder — `draftwise scan` will fill it in)');
log(' • specs/ (your specs will live here)');
log(' • config.yaml (project state)');
log(' • .gitignore (excludes .cache/ from version control)');
log(' • overview.md (placeholder — `draftwise scan` will fill it in)');
log(' • constitution.md (voice + spec-quality rules — edit to suit your project)');
log(' • specs/ (your specs will live here)');
log(' • config.yaml (project state)');
log(' • .gitignore (excludes .cache/ from version control)');
log('');
log(
'Run draftwise commands inside your coding agent (Claude Code, Cursor, etc.).',
Expand Down Expand Up @@ -178,12 +185,18 @@ async function runGreenfield({ log, draftwiseDir, idea }) {
'utf8',
);
await writeFile(join(draftwiseDir, '.gitignore'), DRAFTWISE_GITIGNORE, 'utf8');
await writeFile(
join(draftwiseDir, 'constitution.md'),
CONSTITUTION_TEMPLATE,
'utf8',
);

log('Created .draftwise/ with:');
log(' • overview.md (placeholder — your agent will rewrite from the conversation)');
log(' • specs/ (your specs will live here)');
log(' • config.yaml (project state)');
log(' • .gitignore (excludes .cache/ from version control)');
log(' • overview.md (placeholder — your agent will rewrite from the conversation)');
log(' • constitution.md (voice + spec-quality rules — edit to suit your project)');
log(' • specs/ (your specs will live here)');
log(' • config.yaml (project state)');
log(' • .gitignore (excludes .cache/ from version control)');
log('');
log('Run draftwise commands inside your coding agent (Claude Code, Cursor, etc.).');
}
Expand Down
Loading