Skip to main content
Welcome. This site supports keyboard navigation and screen readers. Press ? at any time for keyboard shortcuts. Press [ to focus the sidebar, ] to focus the content. High-contrast themes are available via the toolbar.
serard@dev00:~/cv

What it reifies

@frenchexdev/requirements-wizards is the third and densest Tier-2 package. Its job is to walk the user through authoring a new Feature or a new Requirement, step by step, gathering the structured fields the spec demands — EARS pattern, source kind, fit criteria, satisfaction links, refinement parent, risk taxonomy — and emitting a typed RequirementSpec or FeatureSpec value that requirements-spec-io (Part 11) serialises and requirements-sync (Part 14) writes.

The package is port-driven. The wizard cores never call @clack/prompts.text() directly; they call an abstract PromptPort whose implementations live elsewhere. The CLI wires the @clack/prompts adapter; an IDE extension would wire a VS Code QuickInput adapter; a CI replay (--yes mode) wires a fixed-answer adapter that consumes a JSON file. Same cores, three different surfaces, each adapter responsible only for the prompt mechanics — the wizard's logic, including the field-by-field validation and the EARS pattern branching, lives in the package.

The package.json description: "Interactive wizards — feature new / requirement new flow cores + orchestrate-spec-wizard (port-driven, no commander/clack)."

The public surface

src/index.ts re-exports four modules:

// @frenchexdev/requirements-wizards — Tier-2: interactive wizard flow cores.
export * from './feature-new-core';
export * from './requirement-core';
export * from './orchestrate-spec-wizard';
export * from './ac-suggester';

feature-new-core is the workflow for authoring a Feature. It collects the Feature ID, the title, the priority, the Acceptance Criteria names, the test levels each AC @Expects, and the Requirements the Feature @Satisfies. The satisfaction step is recursive: if the user names a Requirement that does not exist yet, the wizard offers to author it inline via requirement-core and resume the Feature wizard afterwards.

requirement-core is the workflow for authoring a Requirement. It walks the user through the abstract fields one by one: title, priority, kind (narrowed by the project's Style), status, statement (EARS pattern selector → pattern-specific slots), rationale (claim, kind, evidence array), fit criteria (loop over criterion-kind → kind-specific slots), verification method, source (kind selector → kind-specific slots), risk (level, ifNotMet), optional tracedTo (link kind selector → kind-specific slots), optional @Refines parent.

orchestrate-spec-wizard is the meta-wizard that chooses between the two cores. The CLI's feature new subcommand calls this orchestrator; the orchestrator picks the appropriate core based on what the user is creating.

ac-suggester is the heuristic helper. Given a Feature's id and title, it proposes likely AC names — the user can accept, reject, or override each suggestion. The heuristics are AST-based and use the same vocabulary as the Feature class, so the suggestions look like the names the maintainer would write by hand.

The cores expose three function signatures each — featureNewCore(port, options), requirementCore(port, options), orchestrateSpecWizard(port, options) — with the port parameter being the abstract PromptPort interface and options carrying replay JSON, dry-run flags, template selection, and the Style instance to narrow against.

Where it sits

Tier 2, the package with the most dependencies in the family. It composes every other Tier-2 sibling plus four Tier-1 analyzers:

"dependencies": {
  "@frenchexdev/requirements-core": "workspace:*",
  "@frenchexdev/requirements-requirements": "workspace:*",
  "@frenchexdev/requirements-spec-io": "workspace:*",
  "@frenchexdev/requirements-scaffolders": "workspace:*",
  "@frenchexdev/requirements-sync": "workspace:*",
  "@frenchexdev/requirements-versioning": "workspace:*",
  "ts-morph": "^24.0.0"
}

Five workspace deps plus ts-morph for the source-rewriting step. The wizard ends by handing the produced spec to spec-io for source-string emission, to sync for diff/apply, to versioning for pinning, and to scaffolders for emitting the test stubs the new Feature needs. Each Tier-2 sibling does one thing; the wizard composes them.

Three things the package must not do:

  • Import @clack/prompts or any concrete prompt library. The PromptPort is the boundary. Concrete adapters live in the CLI package (Part 16) and in future consumers.
  • Write files. The wizard produces typed values; sync writes them.
  • Hard-code a Style. The cores accept the project's RequirementStyle instance as a parameter and narrow validations against it. A project on the Industrial style sees thirteen status states; a project on the Lean style sees four.

A concrete call-site

The CLI's feature new subcommand wires the wizard end-to-end:

import {
  orchestrateSpecWizard,
  type OrchestrateOptions,
} from '@frenchexdev/requirements-wizards';
import { applySyncPlan, diffSpecToDisk } from '@frenchexdev/requirements-sync';
import { generateScaffoldForSpec, createScaffolderRegistry }
  from '@frenchexdev/requirements-scaffolders';
import { specToSource } from '@frenchexdev/requirements-spec-io';
import { pinVersion } from '@frenchexdev/requirements-versioning';
import { clackPromptAdapter } from './clack-prompt-adapter';
import { DEFAULT_STYLE } from '@frenchexdev/requirements-styles/default';
import { fs } from '@frenchexdev/requirements-core/ports';

const result = await orchestrateSpecWizard(clackPromptAdapter, {
  style: DEFAULT_STYLE,
  replay: options.replay,
  dryRun: options.dryRun,
  template: options.template,
});

if (result.kind === 'feature') {
  const sourceFile = specToSource(result.spec);
  const writePlan = await generateScaffoldForSpec({
    featureId: result.spec.id,
    level: TestLevel.Unit,
    registry: createScaffolderRegistry(),
  });
  const allProposed = [sourceFile, ...writePlan.sources];
  const syncPlan = await diffSpecToDisk(fs, allProposed);
  await applySyncPlan(fs, syncPlan, { mode: options.reviewPerFile ? 'per-file' : 'all' });
  await fs.writeFile('versions.json', JSON.stringify([pinVersion(result.spec, '1.0.0')]));
}

Three Tier-2 packages composed (wizards, scaffolders, sync) plus two Tier-1 packages (spec-io, versioning) plus a Tier-0 style preset. The CLI is the place where the composition happens; the wizard core itself only knows about its own ports.

Why it is its own package

Three arguments.

First, the port-driven design is the package's product. The same wizard cores need to run in three surface forms: the CLI (today), the IDE extension (planned), and CI replay (today, via --yes). If the cores depended on @clack/prompts directly, the IDE extension would either re-implement the prompts or run clack inside a VS Code terminal — both unacceptable. The PromptPort abstraction is what lets a single core implementation drive every surface. Extracting the wizards into their own package makes the port the explicit contract: anyone reading feature-new-core.ts sees the port parameter and knows the wizard is surface-agnostic.

Second, the wizard's composition surface is large enough to deserve isolation. The package depends on five workspace siblings; bundling it into requirements-lib meant any change to any of those siblings forced a full re-test of the wizard logic. Extracting the wizards into their own package means the dependency closure is documented in one package.json, the re-test boundary is clear, and changes to (say) the scaffolder registry only force re-testing the wizard if the wizard's calls to the scaffolder change.

Third, wizards are the most interactive code in the family and benefit most from per-package coverage discipline. The cores' coverage threshold is held to the same 98%+ floor as the rest of the family, but the test surface — every EARS pattern branch, every Style narrowing path, every recursive @Satisfies resolution — is genuinely large. Isolating the wizards in their own package means their test suite can grow without inflating the test time of every other consumer. Pre-split, running vitest on requirements-lib ran the wizard tests too, slowing every iteration.

That ends Tier 2. Three application services, each composing Tier-1 analyzers and Tier-0 primitives into a coherent use case. The next page covers the only Tier-3 package in the family: the requirements CLI itself.

⬇ Download