@popoverai/dotrequirements 0.24.2 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +7 -9
  2. package/dist/codebase-to-spec/cache.d.ts +6 -0
  3. package/dist/codebase-to-spec/cache.js +1 -0
  4. package/dist/codebase-to-spec/dispatch.d.ts +115 -0
  5. package/dist/codebase-to-spec/dispatch.js +850 -0
  6. package/dist/codebase-to-spec/pack.d.ts +7 -0
  7. package/dist/codebase-to-spec/pack.js +29 -8
  8. package/dist/codebase-to-spec/prompts/editor.d.ts +1 -1
  9. package/dist/codebase-to-spec/prompts/editor.js +1 -1
  10. package/dist/codebase-to-spec/prompts/specifier.d.ts +1 -1
  11. package/dist/codebase-to-spec/prompts/specifier.js +3 -2
  12. package/dist/codebase-to-spec/schemas.d.ts +528 -0
  13. package/dist/codebase-to-spec/schemas.js +244 -0
  14. package/dist/codebase-to-spec/skill-install.d.ts +41 -14
  15. package/dist/codebase-to-spec/skill-install.js +75 -26
  16. package/dist/commands/codebase-to-spec/compose-orchestrator.d.ts +14 -0
  17. package/dist/commands/codebase-to-spec/compose-orchestrator.js +54 -0
  18. package/dist/commands/codebase-to-spec/dispatch-context.d.ts +9 -0
  19. package/dist/commands/codebase-to-spec/dispatch-context.js +19 -0
  20. package/dist/commands/codebase-to-spec/dispatch-editor.d.ts +15 -0
  21. package/dist/commands/codebase-to-spec/dispatch-editor.js +70 -0
  22. package/dist/commands/codebase-to-spec/dispatch-planner.d.ts +18 -0
  23. package/dist/commands/codebase-to-spec/dispatch-planner.js +89 -0
  24. package/dist/commands/codebase-to-spec/dispatch-spec.d.ts +13 -0
  25. package/dist/commands/codebase-to-spec/dispatch-spec.js +56 -0
  26. package/dist/commands/codebase-to-spec/index.js +58 -2
  27. package/dist/commands/codebase-to-spec/pack.d.ts +5 -0
  28. package/dist/commands/codebase-to-spec/pack.js +6 -3
  29. package/dist/commands/codebase-to-spec/present-orchestrator.d.ts +20 -0
  30. package/dist/commands/codebase-to-spec/present-orchestrator.js +81 -0
  31. package/dist/commands/codebase-to-spec/skill-install.js +5 -1
  32. package/dist/templates/agents/cts-worker.md +9 -0
  33. package/dist/templates/skills/codebase-to-spec/SKILL.md +44 -77
  34. package/dist/templates/workflows/specify-codebase.js +372 -0
  35. package/package.json +2 -2
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `dotrequirements codebase-to-spec dispatch-planner [--revise]` subcommand.
3
+ *
4
+ * Initial mode: returns a dispatch payload for the first planner pass.
5
+ *
6
+ * --revise mode: reads the current `outline.yaml`, validates that its
7
+ * `review.result` is "needs-revision", and returns a payload for the next
8
+ * revise dispatch. The worker overwrites outline.yaml's content while
9
+ * preserving the review section.
10
+ *
11
+ * Requirements covered:
12
+ * - CTSO-CONV-2: orchestrator iterates on planner's outline until approved
13
+ */
14
+ export interface DispatchPlannerOptions {
15
+ revise?: boolean;
16
+ }
17
+ export declare function dispatchPlannerCommand(options?: DispatchPlannerOptions): Promise<void>;
18
+ //# sourceMappingURL=dispatch-planner.d.ts.map
@@ -0,0 +1,89 @@
1
+ /**
2
+ * `dotrequirements codebase-to-spec dispatch-planner [--revise]` subcommand.
3
+ *
4
+ * Initial mode: returns a dispatch payload for the first planner pass.
5
+ *
6
+ * --revise mode: reads the current `outline.yaml`, validates that its
7
+ * `review.result` is "needs-revision", and returns a payload for the next
8
+ * revise dispatch. The worker overwrites outline.yaml's content while
9
+ * preserving the review section.
10
+ *
11
+ * Requirements covered:
12
+ * - CTSO-CONV-2: orchestrator iterates on planner's outline until approved
13
+ */
14
+ import { existsSync, readFileSync } from "node:fs";
15
+ import { cachePaths } from "../../codebase-to-spec/cache.js";
16
+ import { PLANNER_INITIAL_DISPATCH_ID, PLANNER_REVISE_DISPATCH_ID_PREFIX, } from "../../codebase-to-spec/dispatch.js";
17
+ import { ExitCode } from "../../codebase-to-spec/exit-codes.js";
18
+ import { parseConversationalOutline, } from "../../codebase-to-spec/schemas.js";
19
+ import { findProjectRoot } from "../../utils/project-settings.js";
20
+ export async function dispatchPlannerCommand(options = {}) {
21
+ const projectRoot = findProjectRoot(process.cwd()) ?? process.cwd();
22
+ const paths = cachePaths(projectRoot);
23
+ if (options.revise) {
24
+ return dispatchPlannerRevise(paths);
25
+ }
26
+ return dispatchPlannerInitial(paths);
27
+ }
28
+ function dispatchPlannerInitial(paths) {
29
+ if (!existsSync(paths.overview)) {
30
+ process.stderr.write(`Compressed pack not found at ${paths.overview}. Run \`dotrequirements cts pack\` first.\n`);
31
+ process.exitCode = ExitCode.MissingInput;
32
+ return;
33
+ }
34
+ const payload = {
35
+ dispatch_id: PLANNER_INITIAL_DISPATCH_ID,
36
+ output_path: paths.outline,
37
+ };
38
+ process.stdout.write(`${JSON.stringify(payload)}\n`);
39
+ }
40
+ function dispatchPlannerRevise(paths) {
41
+ if (!existsSync(paths.outline)) {
42
+ process.stderr.write(`No outline.yaml found at ${paths.outline}. Dispatch the initial planner first.\n`);
43
+ process.exitCode = ExitCode.MissingInput;
44
+ return;
45
+ }
46
+ // Parse and validate the current outline. Surface clear errors rather
47
+ // than letting a malformed file silently produce a bad revise dispatch.
48
+ let outline;
49
+ try {
50
+ outline = parseConversationalOutline(readFileSync(paths.outline, "utf-8"));
51
+ }
52
+ catch (err) {
53
+ process.stderr.write(`Outline at ${paths.outline} is invalid: ${err instanceof Error ? err.message : String(err)}\n`);
54
+ process.exitCode = ExitCode.MissingInput;
55
+ return;
56
+ }
57
+ // The orchestrator should only call --revise when the outline has been
58
+ // reviewed and the result is "needs-revision". Other states are user
59
+ // errors worth surfacing clearly.
60
+ if (!outline.review) {
61
+ process.stderr.write(`Outline at ${paths.outline} has no \`review\` section. Add CA's review (with result and thread) before dispatching revise.\n`);
62
+ process.exitCode = ExitCode.MissingInput;
63
+ return;
64
+ }
65
+ if (outline.review.result === "approved") {
66
+ process.stderr.write(`Outline at ${paths.outline} has review.result "approved" — nothing to revise. Proceed to fan-out.\n`);
67
+ process.exitCode = ExitCode.MissingInput;
68
+ return;
69
+ }
70
+ // Latest thread entry must be needs-revision for there to be revisions
71
+ // to act on. Schema's discriminated union enforces revisions ≥ 1 when
72
+ // the entry is needs-revision.
73
+ const latestEntry = outline.review.thread[outline.review.thread.length - 1];
74
+ if (!latestEntry || latestEntry.result !== "needs-revision") {
75
+ process.stderr.write(`Outline at ${paths.outline} has review.result "needs-revision" but the latest thread entry isn't a needs-revision entry. The review thread is malformed.\n`);
76
+ process.exitCode = ExitCode.MissingInput;
77
+ return;
78
+ }
79
+ // Next turn = current thread length + 1 (the round being PRODUCED by
80
+ // this revise dispatch; CA's review of THAT output will append a new
81
+ // thread entry of length+1 after this dispatch completes).
82
+ const nextTurn = outline.review.thread.length + 1;
83
+ const payload = {
84
+ dispatch_id: `${PLANNER_REVISE_DISPATCH_ID_PREFIX}${nextTurn}`,
85
+ output_path: paths.outline,
86
+ };
87
+ process.stdout.write(`${JSON.stringify(payload)}\n`);
88
+ }
89
+ //# sourceMappingURL=dispatch-planner.js.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `dotrequirements codebase-to-spec dispatch-spec` subcommand.
3
+ *
4
+ * Returns an array of specifier dispatch payloads — one per area in the
5
+ * approved outline. The specify-codebase workflow reads this, fires N
6
+ * cts-worker dispatches in parallel (one per area), and collects the
7
+ * resulting partials.
8
+ *
9
+ * Errors clearly if `outline.yaml`'s `review.result` isn't `"approved"` —
10
+ * the orchestrator should approve the outline before fan-out.
11
+ */
12
+ export declare function dispatchSpecCommand(): Promise<void>;
13
+ //# sourceMappingURL=dispatch-spec.d.ts.map
@@ -0,0 +1,56 @@
1
+ /**
2
+ * `dotrequirements codebase-to-spec dispatch-spec` subcommand.
3
+ *
4
+ * Returns an array of specifier dispatch payloads — one per area in the
5
+ * approved outline. The specify-codebase workflow reads this, fires N
6
+ * cts-worker dispatches in parallel (one per area), and collects the
7
+ * resulting partials.
8
+ *
9
+ * Errors clearly if `outline.yaml`'s `review.result` isn't `"approved"` —
10
+ * the orchestrator should approve the outline before fan-out.
11
+ */
12
+ import { existsSync, readFileSync } from "node:fs";
13
+ import { cachePaths } from "../../codebase-to-spec/cache.js";
14
+ import { SPECIFIER_DISPATCH_ID_PREFIX } from "../../codebase-to-spec/dispatch.js";
15
+ import { ExitCode } from "../../codebase-to-spec/exit-codes.js";
16
+ import { sanitizeAreaName } from "../../codebase-to-spec/fan-out.js";
17
+ import { parseConversationalOutline, } from "../../codebase-to-spec/schemas.js";
18
+ import { findProjectRoot } from "../../utils/project-settings.js";
19
+ export async function dispatchSpecCommand() {
20
+ const projectRoot = findProjectRoot(process.cwd()) ?? process.cwd();
21
+ const paths = cachePaths(projectRoot);
22
+ if (!existsSync(paths.outline)) {
23
+ process.stderr.write(`No outline.yaml found at ${paths.outline}. Dispatch the planner first.\n`);
24
+ process.exitCode = ExitCode.MissingInput;
25
+ return;
26
+ }
27
+ let outline;
28
+ try {
29
+ outline = parseConversationalOutline(readFileSync(paths.outline, "utf-8"));
30
+ }
31
+ catch (err) {
32
+ process.stderr.write(`Outline at ${paths.outline} is invalid: ${err instanceof Error ? err.message : String(err)}\n`);
33
+ process.exitCode = ExitCode.MissingInput;
34
+ return;
35
+ }
36
+ if (!outline.review) {
37
+ process.stderr.write(`Outline at ${paths.outline} has no \`review\` section. Add CA's review with result=approved before dispatching specifiers.\n`);
38
+ process.exitCode = ExitCode.MissingInput;
39
+ return;
40
+ }
41
+ if (outline.review.result !== "approved") {
42
+ process.stderr.write(`Outline at ${paths.outline} has review.result "${outline.review.result}", not "approved". Approve the outline before dispatching specifiers.\n`);
43
+ process.exitCode = ExitCode.MissingInput;
44
+ return;
45
+ }
46
+ // For each area, produce a dispatch payload. The skill body fires N
47
+ // background Task calls in parallel using these payloads.
48
+ const dispatches = outline.areas.map((area) => ({
49
+ dispatch_id: `${SPECIFIER_DISPATCH_ID_PREFIX}${area.prefix}`,
50
+ output_path: paths.partial(sanitizeAreaName(area.name)),
51
+ area_name: area.name,
52
+ area_prefix: area.prefix,
53
+ }));
54
+ process.stdout.write(`${JSON.stringify(dispatches)}\n`);
55
+ }
56
+ //# sourceMappingURL=dispatch-spec.js.map
@@ -6,16 +6,31 @@
6
6
  */
7
7
  import { installCtsAbortHandlers } from "../../codebase-to-spec/progress.js";
8
8
  import { composeCommand } from "./compose.js";
9
+ import { composeOrchestratorCommand } from "./compose-orchestrator.js";
10
+ import { dispatchContextCommand } from "./dispatch-context.js";
11
+ import { dispatchEditorCommand } from "./dispatch-editor.js";
12
+ import { dispatchPlannerCommand } from "./dispatch-planner.js";
13
+ import { dispatchSpecCommand } from "./dispatch-spec.js";
9
14
  import { editLoopCommand } from "./edit-loop.js";
10
15
  import { fanOutCommand } from "./fan-out.js";
11
16
  import { packCommand } from "./pack.js";
12
17
  import { planLoopCommand } from "./plan-loop.js";
13
18
  import { presentCommand } from "./present.js";
19
+ import { presentOrchestratorCommand } from "./present-orchestrator.js";
14
20
  import { runCommand } from "./run.js";
15
21
  import { skillInstallCommand } from "./skill-install.js";
16
22
  import { specifyAreaCommand } from "./specify-area.js";
17
23
  import { styleCheckCommand } from "./style-check.js";
18
24
  import { validateCommand } from "./validate.js";
25
+ /**
26
+ * Marker used to hide a command from `--help` output without removing it.
27
+ * Used for dark-ship: the conversational orchestrator's plumbing commands and
28
+ * the skill-install entry point ship in the npm package but aren't surfaced
29
+ * in help listings or documentation. They remain fully invocable (the
30
+ * orchestrator skill calls them by name) — they just don't pollute the
31
+ * user-facing CLI surface until we flip the default path in Phase 6.
32
+ */
33
+ const HIDDEN = { hidden: true };
19
34
  export function registerCodebaseToSpec(program) {
20
35
  const cts = program
21
36
  .command("codebase-to-spec")
@@ -31,7 +46,8 @@ export function registerCodebaseToSpec(program) {
31
46
  cts
32
47
  .command("pack")
33
48
  .description("Pack the codebase (compressed and uncompressed views) into the cache")
34
- .option("-s, --scope <path>", "Limit to files under this path (subdir of project root)")
49
+ .option("-s, --scope <path>", "Limit to files under this path (subdir of project root, or of the remote repo when --remote is set)")
50
+ .option("--remote <url>", "Pack a remote repository (GitHub URL or owner/repo) instead of the local project; repomix clones, packs, and cleans up. Combine with --scope to pack just a subdirectory.")
35
51
  .option("--fresh", "Clear the cache before running")
36
52
  .option("--budget <tokens>", "Override the working-context budget (in tokens)", (v) => parseInt(v, 10))
37
53
  .option("--ignore-requirements", "Exclude `.requirements/**` from the pack (use when testing cts against a codebase whose existing requirements should not influence the output)")
@@ -134,7 +150,47 @@ export function registerCodebaseToSpec(program) {
134
150
  });
135
151
  });
136
152
  cts
137
- .command("skill-install")
153
+ .command("dispatch-context <dispatch-id>", HIDDEN)
154
+ .description("Return the composed prompt for a worker dispatch as JSON on stdout (run by cts-worker subagents to fetch their dispatch prompt)")
155
+ .action((dispatchId) => dispatchContextCommand(dispatchId));
156
+ cts
157
+ .command("dispatch-planner", HIDDEN)
158
+ .description("Return a dispatch payload for a planner pass (initial, or --revise to scaffold the next turn from the latest cached outline + critique)")
159
+ .option("--revise", "Scaffold a revise dispatch using the latest cached outline + sibling critique file")
160
+ .action((opts) => dispatchPlannerCommand({ revise: opts.revise }));
161
+ cts
162
+ .command("dispatch-spec", HIDDEN)
163
+ .description("Return an array of specifier dispatch payloads — one per area in the approved outline (used by the conversational orchestrator skill to fan out N specifiers in parallel)")
164
+ .action(() => dispatchSpecCommand());
165
+ cts
166
+ .command("dispatch-editor <area-prefix>", HIDDEN)
167
+ .description("Return an editor dispatch payload for one area whose review is needs-revision (used by the conversational orchestrator skill to drive per-area convergence)")
168
+ .action((areaPrefix) => dispatchEditorCommand(areaPrefix));
169
+ cts
170
+ .command("compose-orchestrator", HIDDEN)
171
+ .description("Compose-orchestrator variant: reads outline.yaml (approved) + partials/, writes composedSpec")
172
+ .action(() => composeOrchestratorCommand());
173
+ cts
174
+ .command("present-orchestrator", HIDDEN)
175
+ .description("Present-orchestrator variant: reads outline.yaml + composedSpec, writes final files to .requirements/")
176
+ .option("--interactive", "Force interactive mode (override TTY detection)")
177
+ .option("--non-interactive", "Force non-interactive mode (override TTY detection)")
178
+ .option("--overwrite", "Replace existing .requirements/ files without prompting")
179
+ .option("--skip-existing", "Leave existing .requirements/ files untouched")
180
+ .action((opts) => {
181
+ const forceMode = opts.interactive
182
+ ? "interactive"
183
+ : opts.nonInteractive
184
+ ? "non-interactive"
185
+ : undefined;
186
+ return presentOrchestratorCommand({
187
+ forceMode,
188
+ overwrite: opts.overwrite,
189
+ skipExisting: opts.skipExisting,
190
+ });
191
+ });
192
+ cts
193
+ .command("skill-install", HIDDEN)
138
194
  .description("Install the codebase-to-spec skill (SKILL.md) into the host's skills directory")
139
195
  .option("--global", "Install to ~/.claude/skills/ instead of the project")
140
196
  .option("--target-dir <path>", "Install into a custom skills directory (for non-Claude-Code hosts)")
@@ -11,6 +11,11 @@
11
11
  */
12
12
  export interface PackOptions {
13
13
  scope?: string;
14
+ /**
15
+ * Pack a remote repository (GitHub URL or `owner/repo`) instead of the local
16
+ * project. Combine with `scope` to pack just a subdirectory of the remote.
17
+ */
18
+ remote?: string;
14
19
  fresh?: boolean;
15
20
  budgetTokens?: number;
16
21
  /**
@@ -35,14 +35,17 @@ export async function packCommand(rawOptions = {}) {
35
35
  progress.emit({
36
36
  stage: "pack",
37
37
  step: "start",
38
- message: scope
39
- ? `Packing codebase at ${scope}`
40
- : `Packing codebase at ${projectRoot}`,
38
+ message: rawOptions.remote
39
+ ? `Packing remote ${rawOptions.remote}${rawOptions.scope ? ` (scope: ${rawOptions.scope})` : ""}`
40
+ : scope
41
+ ? `Packing codebase at ${scope}`
42
+ : `Packing codebase at ${projectRoot}`,
41
43
  });
42
44
  const packResult = await runPack({
43
45
  projectRoot,
44
46
  paths,
45
47
  scope: rawOptions.scope,
48
+ remote: rawOptions.remote,
46
49
  ignoreRequirements: rawOptions.ignoreRequirements,
47
50
  });
48
51
  progress.emit({
@@ -0,0 +1,20 @@
1
+ /**
2
+ * `dotrequirements codebase-to-spec present-orchestrator` subcommand.
3
+ *
4
+ * Conversational-orchestrator variant of `present`. Reads outline.yaml +
5
+ * composedSpec (produced by compose-orchestrator), writes the final spec
6
+ * to `.requirements/`. The orchestrator doesn't have a separate
7
+ * document-level edit-loop, so this reads from composedSpec directly
8
+ * rather than specFinal.
9
+ *
10
+ * Requirements covered:
11
+ * - CTS-PRESENT-1..4 (reuses the legacy present logic)
12
+ * - CTSO-INTEG-1: orchestrator uses existing deterministic stages unchanged
13
+ */
14
+ export interface PresentOrchestratorOptions {
15
+ forceMode?: "interactive" | "non-interactive";
16
+ overwrite?: boolean;
17
+ skipExisting?: boolean;
18
+ }
19
+ export declare function presentOrchestratorCommand(options?: PresentOrchestratorOptions): Promise<void>;
20
+ //# sourceMappingURL=present-orchestrator.d.ts.map
@@ -0,0 +1,81 @@
1
+ /**
2
+ * `dotrequirements codebase-to-spec present-orchestrator` subcommand.
3
+ *
4
+ * Conversational-orchestrator variant of `present`. Reads outline.yaml +
5
+ * composedSpec (produced by compose-orchestrator), writes the final spec
6
+ * to `.requirements/`. The orchestrator doesn't have a separate
7
+ * document-level edit-loop, so this reads from composedSpec directly
8
+ * rather than specFinal.
9
+ *
10
+ * Requirements covered:
11
+ * - CTS-PRESENT-1..4 (reuses the legacy present logic)
12
+ * - CTSO-INTEG-1: orchestrator uses existing deterministic stages unchanged
13
+ */
14
+ import { existsSync, readFileSync } from "node:fs";
15
+ import { cachePaths } from "../../codebase-to-spec/cache.js";
16
+ import { ExitCode } from "../../codebase-to-spec/exit-codes.js";
17
+ import { detectMode } from "../../codebase-to-spec/interactive.js";
18
+ import { runPresent, } from "../../codebase-to-spec/present.js";
19
+ import { conversationalOutlineToLegacy, parseConversationalOutline, } from "../../codebase-to-spec/schemas.js";
20
+ import { findProjectRoot } from "../../utils/project-settings.js";
21
+ function resolvePolicy(isInteractive, options) {
22
+ if (options.overwrite && options.skipExisting) {
23
+ return { error: "--overwrite and --skip-existing are mutually exclusive." };
24
+ }
25
+ if (options.overwrite)
26
+ return "overwrite";
27
+ if (options.skipExisting)
28
+ return "skip-existing";
29
+ if (isInteractive)
30
+ return "prompt";
31
+ return "fail-fast";
32
+ }
33
+ export async function presentOrchestratorCommand(options = {}) {
34
+ const projectRoot = findProjectRoot(process.cwd()) ?? process.cwd();
35
+ const paths = cachePaths(projectRoot);
36
+ if (!existsSync(paths.outline)) {
37
+ process.stderr.write(`No outline.yaml found at ${paths.outline}. Run the orchestrator's planner first.\n`);
38
+ process.exitCode = ExitCode.MissingInput;
39
+ return;
40
+ }
41
+ if (!existsSync(paths.composedSpec)) {
42
+ process.stderr.write(`No composed spec at ${paths.composedSpec}. Run \`cts compose-orchestrator\` first.\n`);
43
+ process.exitCode = ExitCode.MissingInput;
44
+ return;
45
+ }
46
+ let outline;
47
+ try {
48
+ outline = parseConversationalOutline(readFileSync(paths.outline, "utf-8"));
49
+ }
50
+ catch (err) {
51
+ process.stderr.write(`Outline at ${paths.outline} is invalid: ${err instanceof Error ? err.message : String(err)}\n`);
52
+ process.exitCode = ExitCode.MissingInput;
53
+ return;
54
+ }
55
+ const { isInteractive } = detectMode(options.forceMode);
56
+ const policy = resolvePolicy(isInteractive, options);
57
+ if (typeof policy === "object") {
58
+ process.stderr.write(`${policy.error}\n`);
59
+ process.exitCode = ExitCode.InvalidFlags;
60
+ return;
61
+ }
62
+ const legacyOutline = conversationalOutlineToLegacy(outline);
63
+ const result = await runPresent({
64
+ outline: legacyOutline,
65
+ finalSpecPath: paths.composedSpec,
66
+ projectRoot,
67
+ overwritePolicy: policy,
68
+ });
69
+ if (!result.allWritten) {
70
+ if (result.conflictPath) {
71
+ process.stderr.write(`Refusing to overwrite ${result.conflictPath}. Pass --overwrite or --skip-existing.\n`);
72
+ }
73
+ process.exitCode = ExitCode.OverwriteRefused;
74
+ return;
75
+ }
76
+ for (const action of result.actions) {
77
+ process.stdout.write(` ${action.action}: ${action.path}\n`);
78
+ }
79
+ process.stdout.write(`Wrote ${result.actions.filter((a) => a.action === "created" || a.action === "overwrote").length} file(s) to .requirements/\n`);
80
+ }
81
+ //# sourceMappingURL=present-orchestrator.js.map
@@ -41,7 +41,11 @@ export async function skillInstallCommand(options = {}) {
41
41
  else {
42
42
  process.stdout.write(`SKILL.md already up to date at ${result.installedPath}\n`);
43
43
  }
44
- process.stdout.write("Skill ready. In Claude Code, invoke it with `/codebase-to-spec` (or via natural language).\n");
44
+ if (result.companions) {
45
+ process.stdout.write(`Installed cts-worker agent at ${result.companions.agentPath}\n`);
46
+ process.stdout.write(`Installed specify-codebase workflow at ${result.companions.workflowPath}\n`);
47
+ }
48
+ process.stdout.write("\nSkill ready. In Claude Code, invoke it with `/codebase-to-spec` (or via natural language). Requires a Claude Code version with dynamic-workflow support.\n");
45
49
  }
46
50
  catch (err) {
47
51
  process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
@@ -0,0 +1,9 @@
1
+ ---
2
+ name: cts-worker
3
+ description: Generic worker subagent for the codebase-to-spec workflow. Handles planner, outline-reviewer, specifier, reviewer, and editor dispatches. Each worker fetches its own role-specific instructions from the CLI at dispatch time.
4
+ tools: Bash, Read, Edit, Write, Glob, Grep
5
+ ---
6
+
7
+ You are a codebase-to-spec worker. The prompt you receive tells you which CLI dispatch to fetch: it will instruct you to run `dotrequirements cts dispatch-context <dispatch-id>` (the exact invocation is given in your prompt), read the `"prompt"` field of the JSON it prints, and follow those instructions exactly.
8
+
9
+ Those fetched instructions are your real task — which files to read, what to write and where, and how to validate and style-check your output via Bash. Follow them exactly and do not improvise beyond what they ask. When your prompt also asks you to return a structured result, return exactly that shape.
@@ -1,118 +1,85 @@
1
1
  ---
2
2
  name: codebase-to-spec
3
- description: Generate dotrequirements behavioral specifications from a codebase. Use this when the user wants to capture what an existing codebase does as a set of testable behavioral requirements — e.g. for legacy systems, third-party libraries, or before refactoring.
3
+ description: Generate dotrequirements behavioral specifications from a codebase. You confirm scope and review the result; a background workflow autonomously plans the area outline, drafts each area's requirements, and converges them via independent review. Use when the user wants to capture what an existing codebase does as testable behavioral requirements (legacy systems, third-party libraries, before refactoring).
4
+ allowed-tools: Bash, Read, Workflow, AskUserQuestion
4
5
  ---
5
6
 
6
7
  # Codebase to Spec
7
8
 
8
- You are a thin conversational wrapper around the `dotrequirements cts run` CLI. **You do not implement any pipeline logic yourself.** The CLI handles packing, planning, fan-out parallelism, retry, resumability, review loops, and presentation. Your job is to:
9
-
10
- 1. Confirm the user's scope.
11
- 2. Run the CLI via the Bash tool.
12
- 3. Narrate progress as the CLI emits it.
13
- 4. Surface the final summary and residual notes.
14
- 5. Offer sensible follow-ups (push to cloud, re-run, narrow scope, etc.).
9
+ You turn an existing codebase into a dotrequirements behavioral spec. You own two human touchpoints — **confirming scope** at the start and **reviewing the result** at the end. Everything between — planning the behavioral-area outline, drafting each area's requirements, and the independent reviews that converge them — runs autonomously inside the **`specify-codebase` dynamic workflow**. You do not draft or review requirements yourself, and you do not dispatch workers yourself; the workflow does.
15
10
 
16
11
  ## When this skill is right
17
12
 
18
- Use it when the user wants behavioral requirements *from* code that already exists. Typical phrasings:
19
- - "Generate requirements from this codebase"
20
- - "Capture the behavior of this library"
21
- - "I want a behavioral spec for the auth module"
22
- - "Document what this service does"
13
+ Use when the user wants behavioral requirements *from* code that already exists:
14
+ - "Generate requirements from this codebase" / "capture what this library does" / "spec the auth module before I refactor it"
23
15
 
24
- Do **not** use it for:
25
- - New feature design (use `dotreq-requirements` / the capture flow instead)
26
- - Bug investigation
27
- - Code review
16
+ Do **not** use for new-feature design (use the `dotreq-requirements` skill), bug investigation, or code review.
28
17
 
29
- ## Workflow
18
+ ## Prerequisite: dynamic workflows
30
19
 
31
- ### Step 1 — Confirm scope
20
+ This skill runs its pipeline as a dynamic Workflow. If the `Workflow` tool is not available in this environment, **stop** and tell the user:
32
21
 
33
- If the user passed a scope argument (e.g. a path like `src/auth`), use it. Otherwise, ask one concise question:
22
+ > "codebase-to-spec runs as a dynamic workflow, which this Claude Code version/configuration doesn't support. For CI or headless use, run `dotrequirements cts run --scope <path>` instead."
34
23
 
35
- > "Which part of the codebase should I spec? You can give me a path (e.g. `src/auth`) or say 'the whole thing'."
24
+ Do not attempt a non-workflow fallback.
36
25
 
37
- Whole-codebase runs are fine for small repos. For larger ones, encourage scoping to a single area — the CLI will tell you if the working-context budget is exceeded.
26
+ ## Workflow
38
27
 
39
- ### Step 2 — Run the CLI
28
+ ### 1. Confirm scope
40
29
 
41
- Invoke the pipeline via Bash. The command is:
30
+ If the user gave a scope (a path), use it. Otherwise ask one concise question:
42
31
 
43
- ```bash
44
- dotrequirements cts run --scope <PATH> --non-interactive
45
- ```
32
+ > "Which part of the codebase should I spec? Give me a path (e.g. `src/auth`) or say 'the whole thing'."
33
+
34
+ For large codebases, encourage scoping to a single area — the pack has a context budget.
46
35
 
47
- Flags:
48
- - `--scope <path>` — limit packing to a subdirectory. Omit for the whole repo.
49
- - `--non-interactive` — important: you're running in an agentic context, so prompts won't work.
50
- - `--overwrite` — replace existing `.requirements/*.requirements.md` files.
51
- - `--skip-existing` — leave existing files untouched.
52
- - `--fresh` — clear the cache before running (use only if a previous run is corrupt).
36
+ ### 2. Pack (deterministic)
53
37
 
54
- Default to `--non-interactive`. Decide between `--overwrite` and `--skip-existing` based on what the user wants if there are existing `.requirements/` files; otherwise neither flag is needed (the default `fail-fast` policy will surface conflicts).
38
+ Run `dotrequirements cts pack --scope <PATH>` via Bash. Deterministic, no LLM. It emits `[CTS] pack/done` then `pack/budget-ok`, or exits **10 (BudgetExceeded)** if the compressed pack is too large. On BudgetExceeded, surface the limit and offer a narrower scope (back to step 1).
55
39
 
56
- ### Step 3 — Narrate progress
40
+ ### 3. Run the workflow
57
41
 
58
- The CLI emits lines like:
42
+ Launch the bundled workflow:
59
43
 
60
44
  ```
61
- [CTS] pack/done Packed 16 files → ... (compressed), ... (uncompressed)
62
- [CTS] plan/done Plan loop converged at turn 1 with verdict: approved-with-revisions
63
- [CTS] specify/summary Completed: 7 / Skipped (resume): 0 / Failed: 0
64
- [CTS] spec-review/done Edit loop converged at turn 1 with verdict: approved-with-revisions
65
- [CTS] present/created created: .../sample.requirements.md
45
+ Workflow({ name: "specify-codebase" })
66
46
  ```
67
47
 
68
- Surface these to the user in conversational form as they appear. **Do not invent progress claims** — narrate only what the CLI has actually emitted. If the CLI is silent for a long stretch (specifier fan-out can take minutes), a single reassuring note ("still working — the specifier fan-out runs in parallel and takes a few minutes for larger areas") is fine. Don't repeat it.
48
+ You normally pass no `args` — `cli` defaults to `dotrequirements` on PATH, and the iteration caps default to `roundsCap: 5` / `outlineRoundsCap: 4`. Override only if needed, passing `args` as a real object (not a JSON string). *(Only in the dotrequirements dev repo, pass `args: { cli: "node <repo>/packages/cli/dist/cli.js" }`.)*
69
49
 
70
- ### Step 4 — Surface the final summary
50
+ The workflow plans the outline (planner + an independent reviewer, looping to convergence), enumerates the areas, fans out one specifier per area, converges each area (specify → review → edit), then composes the partials into one spec and runs a document-level cross-area review/edit pass (dedup, terminology, seam gaps). It runs in the background — watch progress in `/workflows`. Do not narrate every step; only surface the gates below.
71
51
 
72
- When the CLI finishes successfully, it prints a "Pipeline summary:" block with:
73
- - total areas
74
- - total top-level requirements
75
- - outline review turns and verdict
76
- - specifier completion count
77
- - spec review turns and verdict
78
- - the list of output files written
79
- - residual notes (issues the reviewer flagged in `approved-with-revisions` outcomes)
52
+ ### 4. Read the result
80
53
 
81
- Present that summary back to the user. **Highlight the residual notes** prominently — these are concerns the reviewer wanted the human to verify. Common categories:
82
- - `outline.coverage_gap` — files the planner didn't assign to any area
83
- - `spec.coverage_gap` — behaviors the spec missed
84
- - `spec.framing_error` — requirements written as guidance/explanation rather than observable behavior
85
- - `spec.cross_area` — duplication across areas
86
- - `spec.internal_mechanics` — internal vocabulary that leaked into a user-facing spec
54
+ The workflow returns one of:
55
+ - **`status: "done"`** — with `areas` (per-area `{ area_prefix, area_name, partial_path, status, rounds }`), `converged_count`, `unconverged` (prefixes that hit the round cap), `failed` (prefixes whose specifier failed outright — no draft was produced), `total`, and the cross-area pass result `cross_area_rounds` / `cross_area_converged`. The workflow has already composed and reconciled the spec. Proceed to step 5.
56
+ - **`status: "outline-unconverged"`** — the outline reviewer didn't approve the decomposition within `outlineRoundsCap`. The latest outline is at `.dotrequirements-cache/outline.yaml`. Surface this; offer to re-run, narrow scope, or hand-edit the outline. Do **not** present.
57
+ - **`status: "enumerate-failed"`** — the approved outline's areas couldn't be enumerated (the agent died). Nothing was drafted. Surface this and offer to re-run. Do **not** present.
87
58
 
88
- ### Step 5 — Offer follow-ups
59
+ ### 5. Present (deterministic)
89
60
 
90
- After surfacing the summary, ask the user what they want next. Useful options:
61
+ On `status: "done"`, the workflow has already composed the spec and run the cross-area pass. Write the final file(s):
62
+ - `dotrequirements cts present-orchestrator` — writes the composed spec to file(s) under `.requirements/`.
91
63
 
92
- - **Push to cloud** — `dotrequirements push` syncs the generated `.requirements/*.requirements.md` files to dotrequirements cloud. Only suggest this if the user has a cloud account (you can check by reading `.dotrequirements/config.json` or asking).
93
- - **Refine a specific area** — re-run `dotrequirements cts specify-area "<name>"` for one area, optionally with `--model <name>` for a stronger model.
94
- - **Refine the whole spec** — re-run `dotrequirements cts edit-loop` to iterate on the composed spec.
95
- - **Narrow the scope** — re-run with a smaller `--scope`.
96
- - **Hand-edit** — the output files are plain Markdown; the user can edit them directly.
64
+ `present-orchestrator` errors on existing-file conflicts unless given `--overwrite` or `--skip-existing`. When a conflict is reported, ask the user which they want, then re-run with that flag.
97
65
 
98
- ## Failure handling
66
+ When `cross_area_converged` is `false`, the cross-area pass hit `crossAreaRoundsCap` without a clean approval — present the spec, but surface that in the summary so the user gives the composed spec an extra look.
99
67
 
100
- The CLI uses stable exit codes (see `dotrequirements cts run --help`). Common cases:
68
+ ### 6. Summarize and offer follow-ups
101
69
 
102
- - **Exit code 10 (BudgetExceeded)** — the codebase is too large for a single agent's context window after compression. Explain this in plain language and offer to retry with a narrower `--scope`.
103
- - **Exit code 11 (MaxTurnsHit)** — a review loop hit its cap without converging. The CLI still writes the latest artifact; surface the residual review and ask the user whether to ship it, re-run the stage (`dotrequirements cts edit-loop`), or hand-edit.
104
- - **Exit code 12 (OverwriteRefused)** — existing `.requirements/` files would be overwritten in non-interactive mode. Offer `--overwrite` or `--skip-existing`.
105
- - **Exit code 3 (StageFailed)** — a stage errored. Read stderr for details. Offer: resume (re-run the same command — the cache means earlier stages are skipped), restart fresh (`--fresh`), or investigate (read files in `.dotrequirements-cache/`).
106
- - **Any other non-zero exit** — surface stderr verbatim, then offer the same three options.
70
+ Present a readable summary: how many areas and requirements, the file path(s) written, and — importantly — any `unconverged` areas (they hit the round cap; their latest draft was kept and is worth a look) and any `failed` areas (no draft was produced; they are absent from the spec). Then offer, **without auto-running any of them**:
71
+ - **Push to cloud** — `dotrequirements push` (only if cloud is configured; it's a destructive sync).
72
+ - **Refine an area** — re-run on a narrower scope.
73
+ - **Re-run on a different scope.**
107
74
 
108
75
  ## What you must NOT do
109
76
 
110
- - Don't try to generate requirements yourself; always use the CLI.
111
- - Don't paraphrase or summarize the CLI's progress lines in ways that change their meaning.
112
- - Don't claim the pipeline did something it didn't (e.g. don't say "the reviewer approved this" if the verdict was `approved-with-revisions`).
113
- - Don't push to cloud without asking — `dotrequirements push` is a destructive sync.
114
- - Don't suggest editing files inside `.dotrequirements-cache/`; that's internal CLI state.
77
+ - Don't draft, review, or edit requirements yourself — the workflow's workers do that.
78
+ - Don't dispatch workers via the Task tool — the workflow owns all agent work.
79
+ - Don't claim convergence when `unconverged` or `failed` is non-empty — surface those areas honestly.
80
+ - Don't present when `status` is `outline-unconverged` or `enumerate-failed`.
81
+ - Don't push to cloud without asking.
115
82
 
116
83
  ## Host portability
117
84
 
118
- This skill makes no assumptions about subagent mechanisms, Task tools, or framework-specific features. Its only host requirements are: Agent Skills format support, a Bash tool (or equivalent shell-out), and `dotrequirements` installed on the user's machine.
85
+ This skill requires Claude Code with dynamic-workflow support (it launches the `specify-codebase` workflow). On hosts without it, or for CI/headless use, the legacy `dotrequirements cts run` CLI runs the same pipeline non-interactively.