@popoverai/dotrequirements 0.23.0 → 0.24.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 (229) hide show
  1. package/README.md +167 -20
  2. package/dist/cli.js +121 -60
  3. package/dist/codebase-to-spec/budget.d.ts +53 -0
  4. package/dist/codebase-to-spec/budget.js +80 -0
  5. package/dist/codebase-to-spec/cache.d.ts +49 -0
  6. package/dist/codebase-to-spec/cache.js +54 -0
  7. package/dist/codebase-to-spec/claude.d.ts +69 -0
  8. package/dist/codebase-to-spec/claude.js +126 -0
  9. package/dist/codebase-to-spec/compose.d.ts +49 -0
  10. package/dist/codebase-to-spec/compose.js +124 -0
  11. package/dist/codebase-to-spec/edit-loop.d.ts +54 -0
  12. package/dist/codebase-to-spec/edit-loop.js +195 -0
  13. package/dist/codebase-to-spec/editor.d.ts +54 -0
  14. package/dist/codebase-to-spec/editor.js +74 -0
  15. package/dist/codebase-to-spec/exit-codes.d.ts +40 -0
  16. package/dist/codebase-to-spec/exit-codes.js +58 -0
  17. package/dist/codebase-to-spec/fan-out.d.ts +63 -0
  18. package/dist/codebase-to-spec/fan-out.js +215 -0
  19. package/dist/codebase-to-spec/interactive.d.ts +30 -0
  20. package/dist/codebase-to-spec/interactive.js +48 -0
  21. package/dist/codebase-to-spec/outline-review-loop.d.ts +51 -0
  22. package/dist/codebase-to-spec/outline-review-loop.js +187 -0
  23. package/dist/codebase-to-spec/pack.d.ts +51 -0
  24. package/dist/codebase-to-spec/pack.js +127 -0
  25. package/dist/codebase-to-spec/planner.d.ts +41 -0
  26. package/dist/codebase-to-spec/planner.js +76 -0
  27. package/dist/codebase-to-spec/present.d.ts +94 -0
  28. package/dist/codebase-to-spec/present.js +288 -0
  29. package/dist/codebase-to-spec/progress.d.ts +33 -0
  30. package/dist/codebase-to-spec/progress.js +28 -0
  31. package/dist/codebase-to-spec/prompts/editor.d.ts +13 -0
  32. package/dist/codebase-to-spec/prompts/editor.js +57 -0
  33. package/dist/codebase-to-spec/prompts/outline-reviewer.d.ts +12 -0
  34. package/dist/codebase-to-spec/prompts/outline-reviewer.js +87 -0
  35. package/dist/codebase-to-spec/prompts/planner-apply.d.ts +12 -0
  36. package/dist/codebase-to-spec/prompts/planner-apply.js +32 -0
  37. package/dist/codebase-to-spec/prompts/planner-initial.d.ts +11 -0
  38. package/dist/codebase-to-spec/prompts/planner-initial.js +125 -0
  39. package/dist/codebase-to-spec/prompts/planner-revise.d.ts +14 -0
  40. package/dist/codebase-to-spec/prompts/planner-revise.js +60 -0
  41. package/dist/codebase-to-spec/prompts/spec-reviewer.d.ts +16 -0
  42. package/dist/codebase-to-spec/prompts/spec-reviewer.js +96 -0
  43. package/dist/codebase-to-spec/prompts/specifier.d.ts +12 -0
  44. package/dist/codebase-to-spec/prompts/specifier.js +100 -0
  45. package/dist/codebase-to-spec/prompts/style-check.d.ts +12 -0
  46. package/dist/codebase-to-spec/prompts/style-check.js +78 -0
  47. package/dist/codebase-to-spec/schemas.d.ts +257 -0
  48. package/dist/codebase-to-spec/schemas.js +183 -0
  49. package/dist/codebase-to-spec/skill-install.d.ts +57 -0
  50. package/dist/codebase-to-spec/skill-install.js +79 -0
  51. package/dist/codebase-to-spec/slice.d.ts +49 -0
  52. package/dist/codebase-to-spec/slice.js +111 -0
  53. package/dist/codebase-to-spec/specifier.d.ts +60 -0
  54. package/dist/codebase-to-spec/specifier.js +79 -0
  55. package/dist/codebase-to-spec/style-check.d.ts +29 -0
  56. package/dist/codebase-to-spec/style-check.js +33 -0
  57. package/dist/codebase-to-spec/summary.d.ts +51 -0
  58. package/dist/codebase-to-spec/summary.js +183 -0
  59. package/dist/codebase-to-spec/validate.d.ts +46 -0
  60. package/dist/codebase-to-spec/validate.js +130 -0
  61. package/dist/commands/acceptance-test.d.ts +6 -0
  62. package/dist/commands/acceptance-test.js +212 -0
  63. package/dist/commands/ai-setup.d.ts +5 -0
  64. package/dist/commands/ai-setup.js +441 -0
  65. package/dist/commands/browsertest.js +34 -27
  66. package/dist/commands/codebase-to-spec/compose.d.ts +14 -0
  67. package/dist/commands/codebase-to-spec/compose.js +57 -0
  68. package/dist/commands/codebase-to-spec/edit-loop.d.ts +16 -0
  69. package/dist/commands/codebase-to-spec/edit-loop.js +83 -0
  70. package/dist/commands/codebase-to-spec/fan-out.d.ts +19 -0
  71. package/dist/commands/codebase-to-spec/fan-out.js +77 -0
  72. package/dist/commands/codebase-to-spec/index.d.ts +9 -0
  73. package/dist/commands/codebase-to-spec/index.js +135 -0
  74. package/dist/commands/codebase-to-spec/pack.d.ts +22 -0
  75. package/dist/commands/codebase-to-spec/pack.js +76 -0
  76. package/dist/commands/codebase-to-spec/plan-loop.d.ts +26 -0
  77. package/dist/commands/codebase-to-spec/plan-loop.js +105 -0
  78. package/dist/commands/codebase-to-spec/present.d.ts +21 -0
  79. package/dist/commands/codebase-to-spec/present.js +92 -0
  80. package/dist/commands/codebase-to-spec/run.d.ts +20 -0
  81. package/dist/commands/codebase-to-spec/run.js +85 -0
  82. package/dist/commands/codebase-to-spec/skill-install.d.ts +20 -0
  83. package/dist/commands/codebase-to-spec/skill-install.js +51 -0
  84. package/dist/commands/codebase-to-spec/specify-area.d.ts +18 -0
  85. package/dist/commands/codebase-to-spec/specify-area.js +82 -0
  86. package/dist/commands/codebase-to-spec/style-check.d.ts +15 -0
  87. package/dist/commands/codebase-to-spec/style-check.js +42 -0
  88. package/dist/commands/codebase-to-spec/validate.d.ts +18 -0
  89. package/dist/commands/codebase-to-spec/validate.js +38 -0
  90. package/dist/commands/create-requirement-document.d.ts +2 -0
  91. package/dist/commands/create-requirement-document.js +41 -0
  92. package/dist/commands/finalize.js +7 -7
  93. package/dist/commands/get.d.ts +2 -0
  94. package/dist/commands/get.js +55 -0
  95. package/dist/commands/init.js +132 -117
  96. package/dist/commands/link.js +27 -27
  97. package/dist/commands/list.d.ts +6 -0
  98. package/dist/commands/list.js +43 -0
  99. package/dist/commands/mcp-setup.js +159 -149
  100. package/dist/commands/mcp.js +1 -1
  101. package/dist/commands/prepare.js +4 -4
  102. package/dist/commands/pull.js +116 -121
  103. package/dist/commands/push.js +106 -112
  104. package/dist/commands/report.d.ts +6 -2
  105. package/dist/commands/report.js +177 -122
  106. package/dist/commands/requirements-for.d.ts +2 -0
  107. package/dist/commands/requirements-for.js +29 -0
  108. package/dist/commands/review-test.d.ts +2 -0
  109. package/dist/commands/review-test.js +75 -0
  110. package/dist/commands/search.d.ts +6 -0
  111. package/dist/commands/search.js +39 -0
  112. package/dist/commands/style-check.d.ts +7 -0
  113. package/dist/commands/style-check.js +75 -0
  114. package/dist/commands/test.js +53 -59
  115. package/dist/commands/tests-for.d.ts +2 -0
  116. package/dist/commands/tests-for.js +80 -0
  117. package/dist/commands/validate.d.ts +6 -0
  118. package/dist/commands/validate.js +72 -0
  119. package/dist/config.js +1 -1
  120. package/dist/convex.d.ts +34 -22
  121. package/dist/convex.js +38 -22
  122. package/dist/harness/cache.d.ts +1 -5
  123. package/dist/harness/cache.js +49 -59
  124. package/dist/harness/convexReporting.d.ts +1 -1
  125. package/dist/harness/convexReporting.js +9 -7
  126. package/dist/harness/coverageCache.js +3 -3
  127. package/dist/harness/finalize.js +59 -46
  128. package/dist/harness/index.d.ts +6 -7
  129. package/dist/harness/index.js +9 -10
  130. package/dist/harness/prepare.js +6 -5
  131. package/dist/harness/requirementsLoader.d.ts +2 -2
  132. package/dist/harness/requirementsLoader.js +13 -35
  133. package/dist/harness/tracking.js +18 -18
  134. package/dist/harness/types.d.ts +1 -1
  135. package/dist/mcp/convexClient.d.ts +0 -39
  136. package/dist/mcp/convexClient.js +2 -107
  137. package/dist/mcp/grep.d.ts +1 -1
  138. package/dist/mcp/grep.js +87 -42
  139. package/dist/mcp/handlers/authoring.d.ts +1 -1
  140. package/dist/mcp/handlers/authoring.js +30 -234
  141. package/dist/mcp/handlers/coverage.d.ts +1 -1
  142. package/dist/mcp/handlers/coverage.js +13 -15
  143. package/dist/mcp/handlers/debug.d.ts +2 -3
  144. package/dist/mcp/handlers/debug.js +10 -10
  145. package/dist/mcp/handlers/get.d.ts +1 -1
  146. package/dist/mcp/handlers/get.js +11 -10
  147. package/dist/mcp/handlers/index.d.ts +20 -20
  148. package/dist/mcp/handlers/index.js +10 -10
  149. package/dist/mcp/handlers/list.d.ts +4 -33
  150. package/dist/mcp/handlers/list.js +16 -38
  151. package/dist/mcp/handlers/push.d.ts +1 -1
  152. package/dist/mcp/handlers/push.js +28 -18
  153. package/dist/mcp/handlers/report.d.ts +16 -0
  154. package/dist/mcp/handlers/report.js +134 -0
  155. package/dist/mcp/handlers/review.d.ts +1 -1
  156. package/dist/mcp/handlers/review.js +40 -59
  157. package/dist/mcp/handlers/search.d.ts +1 -1
  158. package/dist/mcp/handlers/search.js +7 -9
  159. package/dist/mcp/handlers/test-mapping.d.ts +1 -1
  160. package/dist/mcp/handlers/test-mapping.js +14 -14
  161. package/dist/mcp/handlers/types.d.ts +3 -3
  162. package/dist/mcp/handlers/types.js +2 -2
  163. package/dist/mcp/index.d.ts +1 -1
  164. package/dist/mcp/index.js +147 -167
  165. package/dist/mcp/requirements.d.ts +2 -2
  166. package/dist/mcp/requirements.js +30 -30
  167. package/dist/mcp/testCodeExtractor.js +24 -26
  168. package/dist/mcp/types.d.ts +1 -1
  169. package/dist/push/core.d.ts +2 -2
  170. package/dist/push/core.js +20 -20
  171. package/dist/push/index.d.ts +1 -1
  172. package/dist/push/index.js +2 -2
  173. package/dist/requirements/cloud-ai.d.ts +57 -0
  174. package/dist/requirements/cloud-ai.js +104 -0
  175. package/dist/requirements/cloud-coverage.d.ts +41 -0
  176. package/dist/requirements/cloud-coverage.js +60 -0
  177. package/dist/requirements/coverage.d.ts +45 -0
  178. package/dist/requirements/coverage.js +114 -0
  179. package/dist/requirements/grep.d.ts +33 -0
  180. package/dist/requirements/grep.js +306 -0
  181. package/dist/requirements/index.d.ts +73 -0
  182. package/dist/requirements/index.js +174 -0
  183. package/dist/requirements/style-guide.d.ts +67 -0
  184. package/dist/requirements/style-guide.js +299 -0
  185. package/dist/requirements/testCodeExtractor.d.ts +22 -0
  186. package/dist/requirements/testCodeExtractor.js +150 -0
  187. package/dist/schema/browser.d.ts +8 -8
  188. package/dist/schema/browser.js +13 -15
  189. package/dist/schema/builder.d.ts +1 -1
  190. package/dist/schema/builder.js +13 -44
  191. package/dist/schema/conversions.d.ts +2 -2
  192. package/dist/schema/conversions.js +11 -11
  193. package/dist/schema/index.d.ts +9 -9
  194. package/dist/schema/index.js +15 -15
  195. package/dist/schema/parser-core.d.ts +1 -1
  196. package/dist/schema/parser-core.js +23 -22
  197. package/dist/schema/parser.d.ts +3 -3
  198. package/dist/schema/parser.js +27 -31
  199. package/dist/schema/resolver.d.ts +1 -1
  200. package/dist/schema/resolver.js +9 -9
  201. package/dist/schema/scenario.d.ts +1 -1
  202. package/dist/schema/scenario.js +1 -1
  203. package/dist/schema/schemas.d.ts +3 -3
  204. package/dist/schema/schemas.js +41 -28
  205. package/dist/schema/test-schema.js +27 -27
  206. package/dist/templates/context-file-section.md +3 -2
  207. package/dist/templates/example-requirements.js +1 -1
  208. package/dist/templates/example-requirements.ts +3 -1
  209. package/dist/templates/requirements-readme.js +1 -1
  210. package/dist/templates/requirements-readme.ts +1 -1
  211. package/dist/templates/skills/codebase-to-spec/SKILL.md +118 -0
  212. package/dist/utils/brand.js +3 -3
  213. package/dist/utils/browser-launch.js +4 -4
  214. package/dist/utils/context-file.d.ts +1 -1
  215. package/dist/utils/context-file.js +26 -26
  216. package/dist/utils/env.js +7 -7
  217. package/dist/utils/gitignore.js +7 -7
  218. package/dist/utils/oauth-callback-server.d.ts +1 -1
  219. package/dist/utils/oauth-callback-server.js +27 -25
  220. package/dist/utils/oauth-flow.js +32 -29
  221. package/dist/utils/project-discovery.d.ts +3 -3
  222. package/dist/utils/project-discovery.js +18 -17
  223. package/dist/utils/project-name.js +8 -8
  224. package/dist/utils/project-selector.d.ts +1 -1
  225. package/dist/utils/project-selector.js +24 -21
  226. package/dist/utils/project-settings.d.ts +1 -1
  227. package/dist/utils/project-settings.js +24 -22
  228. package/dist/utils/templates.js +6 -6
  229. package/package.json +2 -1
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Editor worker: revise the composed spec in place based on a reviewer's
3
+ * critique.
4
+ *
5
+ * Two modes:
6
+ * - apply: apply mechanical revisions verbatim (when verdict is `approved-with-revisions`)
7
+ * - revise: address the critique using the editor's own judgment (when verdict is `requires-another-review`)
8
+ *
9
+ * Requirements covered:
10
+ * - CTS-EDIT-3: approved-with-revisions triggers apply mode
11
+ * - CTS-EDIT-4: requires-another-review triggers revise mode
12
+ * - CTS-EDIT-5: editor operates on the cohesive document, reads codebase only on demand
13
+ */
14
+ import { type ClaudeRunner } from "./claude.js";
15
+ import type { SpecReview } from "./schemas.js";
16
+ export type EditorMode = "apply" | "revise";
17
+ export interface EditorContext {
18
+ /**
19
+ * Input spec path. The editor reads from here.
20
+ * If `outputSpecPath` differs, the input is copied to the output path first
21
+ * (so the editor's in-place edits land on the output).
22
+ */
23
+ inputSpecPath: string;
24
+ /** Output spec path (may equal inputSpecPath for true in-place editing). */
25
+ outputSpecPath: string;
26
+ /** Path to the codebase pack the editor may consult on demand. */
27
+ fullPackPath: string;
28
+ /** Directories to grant filesystem access to. */
29
+ addDirs: string[];
30
+ /** Reviewer's critique. */
31
+ critique: SpecReview;
32
+ /** Apply or revise. */
33
+ mode: EditorMode;
34
+ runner?: ClaudeRunner;
35
+ model?: string;
36
+ }
37
+ export interface EditorResult {
38
+ /** True if the output file exists and is non-empty after the editor ran. */
39
+ wroteSpec: boolean;
40
+ /** Size of the output in bytes (0 if missing). */
41
+ outputBytes: number;
42
+ /** Process exit code. */
43
+ exitCode: number;
44
+ /** Captured stdout (one-line confirmation). */
45
+ stdout: string;
46
+ /** Captured stderr. */
47
+ stderr: string;
48
+ }
49
+ /**
50
+ * Run the editor. If outputSpecPath differs from inputSpecPath, copy first
51
+ * so the in-place edits land on the output.
52
+ */
53
+ export declare function runEditor(ctx: EditorContext): Promise<EditorResult>;
54
+ //# sourceMappingURL=editor.d.ts.map
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Editor worker: revise the composed spec in place based on a reviewer's
3
+ * critique.
4
+ *
5
+ * Two modes:
6
+ * - apply: apply mechanical revisions verbatim (when verdict is `approved-with-revisions`)
7
+ * - revise: address the critique using the editor's own judgment (when verdict is `requires-another-review`)
8
+ *
9
+ * Requirements covered:
10
+ * - CTS-EDIT-3: approved-with-revisions triggers apply mode
11
+ * - CTS-EDIT-4: requires-another-review triggers revise mode
12
+ * - CTS-EDIT-5: editor operates on the cohesive document, reads codebase only on demand
13
+ */
14
+ import { copyFileSync, existsSync, statSync } from "node:fs";
15
+ import { runClaude } from "./claude.js";
16
+ import { EDITOR_PROMPT } from "./prompts/editor.js";
17
+ function userMessage(ctx) {
18
+ const mode = ctx.mode === "apply" ? "APPLY" : "REVISE";
19
+ const lines = [
20
+ `Mode: ${mode}`,
21
+ ``,
22
+ `Spec to edit (in place via Edit tool): ${ctx.outputSpecPath}`,
23
+ `Codebase pack (consult only on demand): ${ctx.fullPackPath}`,
24
+ ``,
25
+ `## Reviewer critique`,
26
+ `\`\`\`json`,
27
+ JSON.stringify(ctx.critique, null, 2),
28
+ `\`\`\``,
29
+ ``,
30
+ ];
31
+ if (ctx.mode === "apply") {
32
+ lines.push(`You are in APPLY mode. The reviewer's verdict is \`approved-with-revisions\`. Apply each revision in the \`revisions\` array verbatim. Do not restructure. When done, output a one-line confirmation.`);
33
+ }
34
+ else {
35
+ lines.push(`You are in REVISE mode. The reviewer's verdict is \`requires-another-review\`. Address each finding using your judgment. Use the Edit tool for targeted changes. Consult the codebase only to verify specific findings before adding requirements.`);
36
+ }
37
+ return lines.join("\n");
38
+ }
39
+ /**
40
+ * Run the editor. If outputSpecPath differs from inputSpecPath, copy first
41
+ * so the in-place edits land on the output.
42
+ */
43
+ export async function runEditor(ctx) {
44
+ if (ctx.inputSpecPath !== ctx.outputSpecPath) {
45
+ copyFileSync(ctx.inputSpecPath, ctx.outputSpecPath);
46
+ }
47
+ const runner = ctx.runner ?? runClaude;
48
+ const result = await runner({
49
+ systemPrompt: EDITOR_PROMPT,
50
+ userMessage: userMessage(ctx),
51
+ model: ctx.model,
52
+ tools: ["Read", "Edit", "Grep", "Glob"],
53
+ addDirs: ctx.addDirs,
54
+ });
55
+ let wroteSpec = false;
56
+ let outputBytes = 0;
57
+ if (existsSync(ctx.outputSpecPath)) {
58
+ try {
59
+ outputBytes = statSync(ctx.outputSpecPath).size;
60
+ wroteSpec = outputBytes > 0;
61
+ }
62
+ catch {
63
+ // Treat as not written.
64
+ }
65
+ }
66
+ return {
67
+ wroteSpec,
68
+ outputBytes,
69
+ exitCode: result.exitCode,
70
+ stdout: result.stdout,
71
+ stderr: result.stderr,
72
+ };
73
+ }
74
+ //# sourceMappingURL=editor.js.map
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Stable exit codes for the codebase-to-spec CLI.
3
+ *
4
+ * Wrappers (the skill, CI tooling, scripts) rely on these to branch on outcome.
5
+ * Breaking changes are part of the CLI's public contract — see CTS-CLI-5.
6
+ *
7
+ * Requirements covered:
8
+ * - CTS-CLI-5: CLI exit codes and progress format are part of the public contract
9
+ */
10
+ export declare const ExitCode: {
11
+ /** Success — pipeline completed normally. */
12
+ readonly Success: 0;
13
+ /** Generic failure — unspecified error. */
14
+ readonly GenericFailure: 1;
15
+ /** A required input was missing (e.g., a stage was invoked without its prerequisite cache artifacts). */
16
+ readonly MissingInput: 2;
17
+ /** A pipeline stage failed (specifier, reviewer, editor, etc.). */
18
+ readonly StageFailed: 3;
19
+ /**
20
+ * The codebase exceeds the working-context budget after compression.
21
+ * The CLI exits with this code when CTS-CLI-3 fires.
22
+ */
23
+ readonly BudgetExceeded: 10;
24
+ /**
25
+ * A review loop hit its max-turns cap without converging on `approved` or
26
+ * `approved-with-revisions`. The latest artifact is still usable; this code
27
+ * signals to callers that the residual review should be surfaced.
28
+ */
29
+ readonly MaxTurnsHit: 11;
30
+ /** Output would overwrite an existing `.requirements/` file in non-interactive mode without an explicit flag. */
31
+ readonly OverwriteRefused: 12;
32
+ /** Invalid combination of CLI flags (e.g., --overwrite and --skip-existing both supplied). */
33
+ readonly InvalidFlags: 13;
34
+ };
35
+ export type ExitCodeValue = (typeof ExitCode)[keyof typeof ExitCode];
36
+ /**
37
+ * Human-readable name for an exit code, suitable for logging and error messages.
38
+ */
39
+ export declare function exitCodeName(code: ExitCodeValue): string;
40
+ //# sourceMappingURL=exit-codes.d.ts.map
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Stable exit codes for the codebase-to-spec CLI.
3
+ *
4
+ * Wrappers (the skill, CI tooling, scripts) rely on these to branch on outcome.
5
+ * Breaking changes are part of the CLI's public contract — see CTS-CLI-5.
6
+ *
7
+ * Requirements covered:
8
+ * - CTS-CLI-5: CLI exit codes and progress format are part of the public contract
9
+ */
10
+ export const ExitCode = {
11
+ /** Success — pipeline completed normally. */
12
+ Success: 0,
13
+ /** Generic failure — unspecified error. */
14
+ GenericFailure: 1,
15
+ /** A required input was missing (e.g., a stage was invoked without its prerequisite cache artifacts). */
16
+ MissingInput: 2,
17
+ /** A pipeline stage failed (specifier, reviewer, editor, etc.). */
18
+ StageFailed: 3,
19
+ /**
20
+ * The codebase exceeds the working-context budget after compression.
21
+ * The CLI exits with this code when CTS-CLI-3 fires.
22
+ */
23
+ BudgetExceeded: 10,
24
+ /**
25
+ * A review loop hit its max-turns cap without converging on `approved` or
26
+ * `approved-with-revisions`. The latest artifact is still usable; this code
27
+ * signals to callers that the residual review should be surfaced.
28
+ */
29
+ MaxTurnsHit: 11,
30
+ /** Output would overwrite an existing `.requirements/` file in non-interactive mode without an explicit flag. */
31
+ OverwriteRefused: 12,
32
+ /** Invalid combination of CLI flags (e.g., --overwrite and --skip-existing both supplied). */
33
+ InvalidFlags: 13,
34
+ };
35
+ /**
36
+ * Human-readable name for an exit code, suitable for logging and error messages.
37
+ */
38
+ export function exitCodeName(code) {
39
+ switch (code) {
40
+ case ExitCode.Success:
41
+ return "Success";
42
+ case ExitCode.GenericFailure:
43
+ return "GenericFailure";
44
+ case ExitCode.MissingInput:
45
+ return "MissingInput";
46
+ case ExitCode.StageFailed:
47
+ return "StageFailed";
48
+ case ExitCode.BudgetExceeded:
49
+ return "BudgetExceeded";
50
+ case ExitCode.MaxTurnsHit:
51
+ return "MaxTurnsHit";
52
+ case ExitCode.OverwriteRefused:
53
+ return "OverwriteRefused";
54
+ case ExitCode.InvalidFlags:
55
+ return "InvalidFlags";
56
+ }
57
+ }
58
+ //# sourceMappingURL=exit-codes.js.map
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Specifier fan-out orchestrator.
3
+ *
4
+ * For each area in the outline, extract a slice, spawn a specifier worker,
5
+ * and write the resulting partial to the cache. Workers run in parallel up
6
+ * to a bounded concurrency limit. On per-area failure (no partial written,
7
+ * empty partial), the worker is retried once; on second failure, the area
8
+ * is recorded as failed for surfacing to the user.
9
+ *
10
+ * Requirements covered:
11
+ * - CTS-SPEC-1.2: bounded concurrent execution
12
+ * - CTS-SPEC-5: failed/empty specifier output is detected and retried
13
+ * - CTS-OBSERVE-2.0: failure details are logged with area name + cause
14
+ * - CTS-RESUME-1.2: existing non-empty partial means the specifier is skipped
15
+ */
16
+ import type { ClaudeRunner } from "./claude.js";
17
+ import type { ProgressEmitter } from "./progress.js";
18
+ import type { Area, Outline } from "./schemas.js";
19
+ export declare const DEFAULT_FAN_OUT_CONCURRENCY = 8;
20
+ export interface FanOutOptions {
21
+ outline: Outline;
22
+ /** Path to the uncompressed pack (slices come from here). */
23
+ fullPackPath: string;
24
+ /** Path resolver: given a sanitized area name, return the partial path. */
25
+ partialPathFor: (sanitizedAreaName: string) => string;
26
+ /** Path resolver: given a sanitized area name, return the slice path. */
27
+ slicePathFor: (sanitizedAreaName: string) => string;
28
+ /** Command (path + args) the specifier should run to invoke schema validation. Run before style-check. */
29
+ validateCommand: string;
30
+ /** Command (path + args) the specifier should run to invoke style-check. */
31
+ styleCheckCommand: string;
32
+ /** Directories the specifier needs filesystem access to. */
33
+ addDirs: string[];
34
+ concurrency?: number;
35
+ runner?: ClaudeRunner;
36
+ model?: string;
37
+ progress?: ProgressEmitter;
38
+ }
39
+ export interface AreaOutcome {
40
+ area: Area;
41
+ status: "completed" | "skipped-resume" | "failed";
42
+ /** Number of attempts made (1 = first attempt succeeded; 2 = retry succeeded; 2 with failed = retried and still failed). */
43
+ attempts: number;
44
+ /** Reason the area failed, if applicable. */
45
+ failureCause?: string;
46
+ /** Path to the partial (whether or not it was written successfully). */
47
+ partialPath: string;
48
+ }
49
+ export interface FanOutResult {
50
+ outcomes: AreaOutcome[];
51
+ /** True if every area produced a non-empty partial (either now or from cache). */
52
+ allSucceeded: boolean;
53
+ }
54
+ /**
55
+ * Convert an area name to a stable filesystem-safe identifier.
56
+ * E.g., "Reading & Editing Files" → "reading-editing-files"
57
+ */
58
+ export declare function sanitizeAreaName(name: string): string;
59
+ /**
60
+ * Run the fan-out: extract slices, invoke specifiers, retry on failure.
61
+ */
62
+ export declare function runFanOut(options: FanOutOptions): Promise<FanOutResult>;
63
+ //# sourceMappingURL=fan-out.d.ts.map
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Specifier fan-out orchestrator.
3
+ *
4
+ * For each area in the outline, extract a slice, spawn a specifier worker,
5
+ * and write the resulting partial to the cache. Workers run in parallel up
6
+ * to a bounded concurrency limit. On per-area failure (no partial written,
7
+ * empty partial), the worker is retried once; on second failure, the area
8
+ * is recorded as failed for surfacing to the user.
9
+ *
10
+ * Requirements covered:
11
+ * - CTS-SPEC-1.2: bounded concurrent execution
12
+ * - CTS-SPEC-5: failed/empty specifier output is detected and retried
13
+ * - CTS-OBSERVE-2.0: failure details are logged with area name + cause
14
+ * - CTS-RESUME-1.2: existing non-empty partial means the specifier is skipped
15
+ */
16
+ import { existsSync, mkdirSync, statSync, writeFileSync } from "node:fs";
17
+ import { dirname } from "node:path";
18
+ import { extractSlice } from "./slice.js";
19
+ import { runSpecifier } from "./specifier.js";
20
+ export const DEFAULT_FAN_OUT_CONCURRENCY = 8;
21
+ /**
22
+ * Convert an area name to a stable filesystem-safe identifier.
23
+ * E.g., "Reading & Editing Files" → "reading-editing-files"
24
+ */
25
+ export function sanitizeAreaName(name) {
26
+ return (name
27
+ .replace(/[^A-Za-z0-9]+/g, "-")
28
+ .replace(/^-+|-+$/g, "")
29
+ .toLowerCase() || "area");
30
+ }
31
+ /**
32
+ * Run a bounded-concurrency loop over items. Workers pull items off a shared
33
+ * counter; at most `limit` workers run at a time.
34
+ */
35
+ async function withLimit(items, limit, fn) {
36
+ let next = 0;
37
+ async function worker() {
38
+ while (true) {
39
+ const i = next++;
40
+ if (i >= items.length)
41
+ return;
42
+ await fn(items[i], i);
43
+ }
44
+ }
45
+ const workerCount = Math.min(Math.max(1, limit), items.length);
46
+ const workers = Array.from({ length: workerCount }, () => worker());
47
+ await Promise.all(workers);
48
+ }
49
+ function partialIsUsable(path) {
50
+ if (!existsSync(path))
51
+ return false;
52
+ try {
53
+ return statSync(path).size > 0;
54
+ }
55
+ catch {
56
+ return false;
57
+ }
58
+ }
59
+ /**
60
+ * Run the fan-out: extract slices, invoke specifiers, retry on failure.
61
+ */
62
+ export async function runFanOut(options) {
63
+ const { outline, fullPackPath, partialPathFor, slicePathFor, validateCommand, styleCheckCommand, addDirs, concurrency = DEFAULT_FAN_OUT_CONCURRENCY, runner, model, progress, } = options;
64
+ if (!existsSync(fullPackPath)) {
65
+ throw new Error(`Missing full pack at ${fullPackPath}`);
66
+ }
67
+ // Sanitization is lossy ("Read & Write" and "read/write" both become
68
+ // "read-write"). If two areas collide, they'd share a partial file path and
69
+ // silently overwrite each other downstream. Catch this up front.
70
+ const sanitizedNames = new Map();
71
+ for (const area of outline.areas) {
72
+ const key = sanitizeAreaName(area.name);
73
+ const bucket = sanitizedNames.get(key) ?? [];
74
+ bucket.push(area.name);
75
+ sanitizedNames.set(key, bucket);
76
+ }
77
+ const collisions = [...sanitizedNames.entries()].filter(([, names]) => names.length > 1);
78
+ if (collisions.length > 0) {
79
+ const detail = collisions
80
+ .map(([key, names]) => ` "${key}" ← ${names.map((n) => `"${n}"`).join(", ")}`)
81
+ .join("\n");
82
+ throw new Error(`Outline contains area names that collide after sanitization. Rename them so each produces a unique partial file path:\n${detail}`);
83
+ }
84
+ const outcomes = outline.areas.map((area) => ({
85
+ area,
86
+ status: "failed",
87
+ attempts: 0,
88
+ partialPath: partialPathFor(sanitizeAreaName(area.name)),
89
+ }));
90
+ // Ensure partials directory exists for each area.
91
+ for (const outcome of outcomes) {
92
+ mkdirSync(dirname(outcome.partialPath), { recursive: true });
93
+ }
94
+ await withLimit(outcomes, concurrency, async (outcome) => {
95
+ const sanitized = sanitizeAreaName(outcome.area.name);
96
+ const slicePath = slicePathFor(sanitized);
97
+ // CTS-RESUME-1.2: skip if a non-empty partial exists from a prior run.
98
+ if (partialIsUsable(outcome.partialPath)) {
99
+ outcome.status = "skipped-resume";
100
+ outcome.attempts = 0;
101
+ progress?.emit({
102
+ stage: "specify",
103
+ step: outcome.area.prefix,
104
+ message: `(resume) skipping ${outcome.area.name} — partial exists`,
105
+ });
106
+ return;
107
+ }
108
+ // Extract this area's slice from the full pack.
109
+ if (outcome.area.files.length === 0) {
110
+ // No files assigned — write a stub partial so compose has something.
111
+ writeFileSync(outcome.partialPath, "_(no files assigned to this area)_\n", "utf-8");
112
+ outcome.status = "failed";
113
+ outcome.attempts = 0;
114
+ outcome.failureCause = "no files assigned in outline";
115
+ progress?.emit({
116
+ stage: "specify",
117
+ step: outcome.area.prefix,
118
+ message: `!! ${outcome.area.name} has no files; marking as failed`,
119
+ });
120
+ return;
121
+ }
122
+ let sliceMatched = 0;
123
+ try {
124
+ const sliceResult = extractSlice(fullPackPath, slicePath, outcome.area.files);
125
+ sliceMatched = sliceResult.matched;
126
+ }
127
+ catch (err) {
128
+ outcome.failureCause = `slice extraction failed: ${err instanceof Error ? err.message : String(err)}`;
129
+ progress?.emit({
130
+ stage: "specify",
131
+ step: outcome.area.prefix,
132
+ message: `!! ${outcome.failureCause}`,
133
+ });
134
+ return;
135
+ }
136
+ if (sliceMatched === 0) {
137
+ progress?.emit({
138
+ stage: "specify",
139
+ step: outcome.area.prefix,
140
+ message: `(warning) slice for ${outcome.area.name} matched 0 files; specifier will need to fall back to full-pack reads`,
141
+ });
142
+ }
143
+ // Attempt 1
144
+ progress?.emit({
145
+ stage: "specify",
146
+ step: outcome.area.prefix,
147
+ message: `Running specifier for ${outcome.area.name} (attempt 1/2)`,
148
+ });
149
+ outcome.attempts = 1;
150
+ const r1 = await runSpecifier({
151
+ outline,
152
+ area: outcome.area,
153
+ slicePath,
154
+ fullPackPath,
155
+ partialPath: outcome.partialPath,
156
+ validateCommand,
157
+ styleCheckCommand,
158
+ addDirs,
159
+ runner,
160
+ model,
161
+ });
162
+ if (r1.wrotePartial) {
163
+ outcome.status = "completed";
164
+ progress?.emit({
165
+ stage: "specify",
166
+ step: outcome.area.prefix,
167
+ message: `${outcome.area.name} done (${r1.partialBytes} bytes)`,
168
+ });
169
+ return;
170
+ }
171
+ // Attempt 2 (retry once on no/empty partial)
172
+ progress?.emit({
173
+ stage: "specify",
174
+ step: outcome.area.prefix,
175
+ message: `!! ${outcome.area.name} produced no partial; retrying (attempt 2/2)`,
176
+ });
177
+ outcome.attempts = 2;
178
+ const r2 = await runSpecifier({
179
+ outline,
180
+ area: outcome.area,
181
+ slicePath,
182
+ fullPackPath,
183
+ partialPath: outcome.partialPath,
184
+ validateCommand,
185
+ styleCheckCommand,
186
+ addDirs,
187
+ runner,
188
+ model,
189
+ });
190
+ if (r2.wrotePartial) {
191
+ outcome.status = "completed";
192
+ progress?.emit({
193
+ stage: "specify",
194
+ step: outcome.area.prefix,
195
+ message: `${outcome.area.name} done on retry (${r2.partialBytes} bytes)`,
196
+ });
197
+ return;
198
+ }
199
+ // Two failures — record and continue. CTS-SPEC-5 says other areas still proceed.
200
+ outcome.status = "failed";
201
+ outcome.failureCause =
202
+ r2.exitCode !== 0
203
+ ? `specifier exited with code ${r2.exitCode}: ${r2.stderr.slice(0, 200)}`
204
+ : "specifier wrote no partial (or empty partial) on both attempts";
205
+ writeFileSync(outcome.partialPath, `_(missing partial — specifier failed after retry: ${outcome.failureCause})_\n`, "utf-8");
206
+ progress?.emit({
207
+ stage: "specify",
208
+ step: outcome.area.prefix,
209
+ message: `!! ${outcome.area.name} failed: ${outcome.failureCause}`,
210
+ });
211
+ });
212
+ const allSucceeded = outcomes.every((o) => o.status === "completed" || o.status === "skipped-resume");
213
+ return { outcomes, allSucceeded };
214
+ }
215
+ //# sourceMappingURL=fan-out.js.map
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Interactive / non-interactive mode detection and overwrite prompting.
3
+ *
4
+ * Requirements covered:
5
+ * - CTS-CLI-4: CLI distinguishes interactive and non-interactive invocation modes
6
+ * - CTS-PRESENT-2: Overwrites in interactive mode prompt the Engineer
7
+ * - CTS-PRESENT-3: Overwrites in non-interactive mode require an explicit flag
8
+ */
9
+ export type OverwriteChoice = "overwrite" | "skip" | "merge";
10
+ export interface InteractiveMode {
11
+ /** True when running in interactive mode (TTY on stdin, or --interactive forced). */
12
+ isInteractive: boolean;
13
+ /** What the engineer chose for a given path (only used in interactive mode). */
14
+ prompt: (path: string) => Promise<OverwriteChoice | "cancel">;
15
+ }
16
+ /**
17
+ * Detect interactive mode. Defaults to TTY detection on stdin; can be forced
18
+ * by the caller via the `force` argument.
19
+ *
20
+ * @param force 'interactive' or 'non-interactive' to override TTY detection.
21
+ */
22
+ export declare function detectMode(force?: "interactive" | "non-interactive"): {
23
+ isInteractive: boolean;
24
+ };
25
+ /**
26
+ * Prompt the engineer for an overwrite decision in interactive mode.
27
+ * Returns the chosen action, or `'cancel'` if the engineer dismissed the prompt.
28
+ */
29
+ export declare function promptOverwriteChoice(path: string): Promise<OverwriteChoice | "cancel">;
30
+ //# sourceMappingURL=interactive.d.ts.map
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Interactive / non-interactive mode detection and overwrite prompting.
3
+ *
4
+ * Requirements covered:
5
+ * - CTS-CLI-4: CLI distinguishes interactive and non-interactive invocation modes
6
+ * - CTS-PRESENT-2: Overwrites in interactive mode prompt the Engineer
7
+ * - CTS-PRESENT-3: Overwrites in non-interactive mode require an explicit flag
8
+ */
9
+ import prompts from "prompts";
10
+ /**
11
+ * Detect interactive mode. Defaults to TTY detection on stdin; can be forced
12
+ * by the caller via the `force` argument.
13
+ *
14
+ * @param force 'interactive' or 'non-interactive' to override TTY detection.
15
+ */
16
+ export function detectMode(force) {
17
+ if (force === "interactive")
18
+ return { isInteractive: true };
19
+ if (force === "non-interactive")
20
+ return { isInteractive: false };
21
+ // TTY detection
22
+ return { isInteractive: !!process.stdin.isTTY };
23
+ }
24
+ /**
25
+ * Prompt the engineer for an overwrite decision in interactive mode.
26
+ * Returns the chosen action, or `'cancel'` if the engineer dismissed the prompt.
27
+ */
28
+ export async function promptOverwriteChoice(path) {
29
+ const response = await prompts({
30
+ type: "select",
31
+ name: "choice",
32
+ message: `Overwrite ${path}?`,
33
+ choices: [
34
+ { title: "Overwrite (replace the existing file)", value: "overwrite" },
35
+ { title: "Skip (leave the existing file untouched)", value: "skip" },
36
+ {
37
+ title: "Merge (keep both — append new requirements to existing)",
38
+ value: "merge",
39
+ },
40
+ ],
41
+ initial: 0,
42
+ });
43
+ if (response.choice === undefined) {
44
+ return "cancel";
45
+ }
46
+ return response.choice;
47
+ }
48
+ //# sourceMappingURL=interactive.js.map
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Outline review loop: planner → reviewer → revise → reviewer → ... → approved.
3
+ *
4
+ * The reviewer runs in a SINGLE stateful claude session across turns (so it
5
+ * can compare prior outlines to the latest one). The planner is stateless —
6
+ * each invocation is fresh.
7
+ *
8
+ * Requirements covered:
9
+ * - CTS-PLAN-2: stateful reviewer session
10
+ * - CTS-PLAN-3: approved → proceed
11
+ * - CTS-PLAN-4: approved-with-revisions → one revision pass, then proceed
12
+ * - CTS-PLAN-5: requires-another-review → revision loop
13
+ * - CTS-PLAN-6: max-turns + convergence nudge
14
+ */
15
+ import { type ClaudeRunner } from "./claude.js";
16
+ import type { ProgressEmitter } from "./progress.js";
17
+ import { type Outline, type OutlineReview } from "./schemas.js";
18
+ export declare const DEFAULT_OUTLINE_LOOP_MAX_TURNS = 3;
19
+ export interface OutlineReviewLoopOptions {
20
+ overviewPath: string;
21
+ addDirs: string[];
22
+ maxTurns?: number;
23
+ runner?: ClaudeRunner;
24
+ model?: string;
25
+ progress?: ProgressEmitter;
26
+ /** Override session id (for tests and reproducibility). Otherwise random. */
27
+ sessionId?: string;
28
+ }
29
+ export interface OutlineReviewLoopResult {
30
+ /** The final outline after the loop converges (or hits max-turns). */
31
+ outline: Outline;
32
+ /** Per-turn outline snapshots (turn 1 is the initial, then each revision). */
33
+ outlinesByTurn: Outline[];
34
+ /** Per-turn reviewer results. */
35
+ reviewsByTurn: OutlineReview[];
36
+ /** The final reviewer verdict. */
37
+ finalVerdict: OutlineReview["verdict"];
38
+ /** Number of review turns used (= length of reviewsByTurn). */
39
+ turnsUsed: number;
40
+ /** True if the loop hit max-turns without converging. */
41
+ hitMaxTurns: boolean;
42
+ }
43
+ /**
44
+ * Run the outline review loop end-to-end.
45
+ *
46
+ * Returns the final outline (after applying mechanical revisions if the
47
+ * verdict was `approved-with-revisions`), the per-turn trace, and the final
48
+ * verdict.
49
+ */
50
+ export declare function runOutlineReviewLoop(options: OutlineReviewLoopOptions): Promise<OutlineReviewLoopResult>;
51
+ //# sourceMappingURL=outline-review-loop.d.ts.map