@popoverai/dotrequirements 0.23.0 → 0.24.1
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.
- package/README.md +169 -22
- package/dist/cli.js +121 -60
- package/dist/codebase-to-spec/budget.d.ts +53 -0
- package/dist/codebase-to-spec/budget.js +80 -0
- package/dist/codebase-to-spec/cache.d.ts +49 -0
- package/dist/codebase-to-spec/cache.js +54 -0
- package/dist/codebase-to-spec/claude.d.ts +69 -0
- package/dist/codebase-to-spec/claude.js +126 -0
- package/dist/codebase-to-spec/compose.d.ts +49 -0
- package/dist/codebase-to-spec/compose.js +124 -0
- package/dist/codebase-to-spec/edit-loop.d.ts +54 -0
- package/dist/codebase-to-spec/edit-loop.js +195 -0
- package/dist/codebase-to-spec/editor.d.ts +54 -0
- package/dist/codebase-to-spec/editor.js +74 -0
- package/dist/codebase-to-spec/exit-codes.d.ts +40 -0
- package/dist/codebase-to-spec/exit-codes.js +58 -0
- package/dist/codebase-to-spec/fan-out.d.ts +63 -0
- package/dist/codebase-to-spec/fan-out.js +215 -0
- package/dist/codebase-to-spec/interactive.d.ts +30 -0
- package/dist/codebase-to-spec/interactive.js +48 -0
- package/dist/codebase-to-spec/outline-review-loop.d.ts +51 -0
- package/dist/codebase-to-spec/outline-review-loop.js +187 -0
- package/dist/codebase-to-spec/pack.d.ts +51 -0
- package/dist/codebase-to-spec/pack.js +127 -0
- package/dist/codebase-to-spec/planner.d.ts +41 -0
- package/dist/codebase-to-spec/planner.js +76 -0
- package/dist/codebase-to-spec/present.d.ts +94 -0
- package/dist/codebase-to-spec/present.js +288 -0
- package/dist/codebase-to-spec/progress.d.ts +33 -0
- package/dist/codebase-to-spec/progress.js +28 -0
- package/dist/codebase-to-spec/prompts/editor.d.ts +13 -0
- package/dist/codebase-to-spec/prompts/editor.js +57 -0
- package/dist/codebase-to-spec/prompts/outline-reviewer.d.ts +12 -0
- package/dist/codebase-to-spec/prompts/outline-reviewer.js +87 -0
- package/dist/codebase-to-spec/prompts/planner-initial.d.ts +11 -0
- package/dist/codebase-to-spec/prompts/planner-initial.js +125 -0
- package/dist/codebase-to-spec/prompts/planner-revise.d.ts +14 -0
- package/dist/codebase-to-spec/prompts/planner-revise.js +60 -0
- package/dist/codebase-to-spec/prompts/spec-reviewer.d.ts +16 -0
- package/dist/codebase-to-spec/prompts/spec-reviewer.js +96 -0
- package/dist/codebase-to-spec/prompts/specifier.d.ts +12 -0
- package/dist/codebase-to-spec/prompts/specifier.js +100 -0
- package/dist/codebase-to-spec/prompts/style-check.d.ts +12 -0
- package/dist/codebase-to-spec/prompts/style-check.js +78 -0
- package/dist/codebase-to-spec/schemas.d.ts +257 -0
- package/dist/codebase-to-spec/schemas.js +183 -0
- package/dist/codebase-to-spec/skill-install.d.ts +57 -0
- package/dist/codebase-to-spec/skill-install.js +79 -0
- package/dist/codebase-to-spec/slice.d.ts +49 -0
- package/dist/codebase-to-spec/slice.js +111 -0
- package/dist/codebase-to-spec/specifier.d.ts +60 -0
- package/dist/codebase-to-spec/specifier.js +79 -0
- package/dist/codebase-to-spec/style-check.d.ts +29 -0
- package/dist/codebase-to-spec/style-check.js +33 -0
- package/dist/codebase-to-spec/summary.d.ts +51 -0
- package/dist/codebase-to-spec/summary.js +183 -0
- package/dist/codebase-to-spec/validate.d.ts +46 -0
- package/dist/codebase-to-spec/validate.js +130 -0
- package/dist/commands/acceptance-test.d.ts +6 -0
- package/dist/commands/{browsertest.js → acceptance-test.js} +36 -29
- package/dist/commands/ai-setup.d.ts +5 -0
- package/dist/commands/ai-setup.js +441 -0
- package/dist/commands/codebase-to-spec/compose.d.ts +14 -0
- package/dist/commands/codebase-to-spec/compose.js +57 -0
- package/dist/commands/codebase-to-spec/edit-loop.d.ts +16 -0
- package/dist/commands/codebase-to-spec/edit-loop.js +83 -0
- package/dist/commands/codebase-to-spec/fan-out.d.ts +19 -0
- package/dist/commands/codebase-to-spec/fan-out.js +77 -0
- package/dist/commands/codebase-to-spec/index.d.ts +9 -0
- package/dist/commands/codebase-to-spec/index.js +135 -0
- package/dist/commands/codebase-to-spec/pack.d.ts +22 -0
- package/dist/commands/codebase-to-spec/pack.js +76 -0
- package/dist/commands/codebase-to-spec/plan-loop.d.ts +26 -0
- package/dist/commands/codebase-to-spec/plan-loop.js +105 -0
- package/dist/commands/codebase-to-spec/present.d.ts +21 -0
- package/dist/commands/codebase-to-spec/present.js +92 -0
- package/dist/commands/codebase-to-spec/run.d.ts +20 -0
- package/dist/commands/codebase-to-spec/run.js +85 -0
- package/dist/commands/codebase-to-spec/skill-install.d.ts +20 -0
- package/dist/commands/codebase-to-spec/skill-install.js +51 -0
- package/dist/commands/codebase-to-spec/specify-area.d.ts +18 -0
- package/dist/commands/codebase-to-spec/specify-area.js +82 -0
- package/dist/commands/codebase-to-spec/style-check.d.ts +15 -0
- package/dist/commands/codebase-to-spec/style-check.js +42 -0
- package/dist/commands/codebase-to-spec/validate.d.ts +18 -0
- package/dist/commands/codebase-to-spec/validate.js +38 -0
- package/dist/commands/create-requirement-document.d.ts +2 -0
- package/dist/commands/create-requirement-document.js +41 -0
- package/dist/commands/finalize.js +7 -7
- package/dist/commands/get.d.ts +2 -0
- package/dist/commands/get.js +55 -0
- package/dist/commands/init.js +132 -117
- package/dist/commands/link.js +27 -27
- package/dist/commands/list.d.ts +6 -0
- package/dist/commands/list.js +43 -0
- package/dist/commands/mcp.js +1 -1
- package/dist/commands/prepare.js +4 -4
- package/dist/commands/pull.js +116 -121
- package/dist/commands/push.js +106 -112
- package/dist/commands/report.d.ts +6 -2
- package/dist/commands/report.js +177 -122
- package/dist/commands/requirements-for.d.ts +2 -0
- package/dist/commands/requirements-for.js +29 -0
- package/dist/commands/review-test.d.ts +2 -0
- package/dist/commands/review-test.js +75 -0
- package/dist/commands/search.d.ts +6 -0
- package/dist/commands/search.js +39 -0
- package/dist/commands/style-check.d.ts +7 -0
- package/dist/commands/style-check.js +75 -0
- package/dist/commands/tests-for.d.ts +2 -0
- package/dist/commands/tests-for.js +80 -0
- package/dist/commands/validate.d.ts +6 -0
- package/dist/commands/validate.js +72 -0
- package/dist/config.js +1 -1
- package/dist/convex.d.ts +34 -22
- package/dist/convex.js +38 -22
- package/dist/harness/cache.d.ts +1 -5
- package/dist/harness/cache.js +49 -59
- package/dist/harness/convexReporting.d.ts +1 -1
- package/dist/harness/convexReporting.js +9 -7
- package/dist/harness/coverageCache.js +3 -3
- package/dist/harness/finalize.js +59 -46
- package/dist/harness/index.d.ts +6 -7
- package/dist/harness/index.js +9 -10
- package/dist/harness/prepare.js +6 -5
- package/dist/harness/requirementsLoader.d.ts +2 -2
- package/dist/harness/requirementsLoader.js +13 -35
- package/dist/harness/tracking.js +18 -18
- package/dist/harness/types.d.ts +1 -1
- package/dist/mcp/convexClient.d.ts +0 -39
- package/dist/mcp/convexClient.js +2 -107
- package/dist/mcp/handlers/authoring.d.ts +1 -1
- package/dist/mcp/handlers/authoring.js +30 -234
- package/dist/mcp/handlers/debug.d.ts +2 -3
- package/dist/mcp/handlers/debug.js +10 -10
- package/dist/mcp/handlers/get.d.ts +1 -1
- package/dist/mcp/handlers/get.js +11 -10
- package/dist/mcp/handlers/index.d.ts +20 -20
- package/dist/mcp/handlers/index.js +10 -10
- package/dist/mcp/handlers/list.d.ts +4 -33
- package/dist/mcp/handlers/list.js +16 -38
- package/dist/mcp/handlers/push.d.ts +1 -1
- package/dist/mcp/handlers/push.js +28 -18
- package/dist/mcp/handlers/report.d.ts +16 -0
- package/dist/mcp/handlers/report.js +134 -0
- package/dist/mcp/handlers/review.d.ts +1 -1
- package/dist/mcp/handlers/review.js +40 -59
- package/dist/mcp/handlers/search.d.ts +1 -1
- package/dist/mcp/handlers/search.js +7 -9
- package/dist/mcp/handlers/test-mapping.d.ts +1 -1
- package/dist/mcp/handlers/test-mapping.js +14 -14
- package/dist/mcp/handlers/types.d.ts +3 -3
- package/dist/mcp/handlers/types.js +2 -2
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +147 -167
- package/dist/push/core.d.ts +2 -2
- package/dist/push/core.js +20 -20
- package/dist/push/index.d.ts +1 -1
- package/dist/push/index.js +2 -2
- package/dist/requirements/cloud-ai.d.ts +57 -0
- package/dist/requirements/cloud-ai.js +104 -0
- package/dist/requirements/cloud-coverage.d.ts +41 -0
- package/dist/requirements/cloud-coverage.js +60 -0
- package/dist/requirements/coverage.d.ts +45 -0
- package/dist/requirements/coverage.js +114 -0
- package/dist/{mcp → requirements}/grep.d.ts +10 -1
- package/dist/{mcp → requirements}/grep.js +89 -44
- package/dist/{mcp/requirements.d.ts → requirements/index.d.ts} +19 -3
- package/dist/{mcp/requirements.js → requirements/index.js} +54 -35
- package/dist/requirements/style-guide.d.ts +67 -0
- package/dist/requirements/style-guide.js +299 -0
- package/dist/{mcp → requirements}/testCodeExtractor.js +24 -26
- package/dist/schema/browser.d.ts +8 -8
- package/dist/schema/browser.js +13 -15
- package/dist/schema/builder.d.ts +1 -1
- package/dist/schema/builder.js +13 -44
- package/dist/schema/conversions.d.ts +2 -2
- package/dist/schema/conversions.js +11 -11
- package/dist/schema/index.d.ts +9 -9
- package/dist/schema/index.js +15 -15
- package/dist/schema/parser-core.d.ts +1 -1
- package/dist/schema/parser-core.js +23 -22
- package/dist/schema/parser.d.ts +3 -3
- package/dist/schema/parser.js +27 -31
- package/dist/schema/resolver.d.ts +1 -1
- package/dist/schema/resolver.js +9 -9
- package/dist/schema/scenario.d.ts +1 -1
- package/dist/schema/scenario.js +1 -1
- package/dist/schema/schemas.d.ts +3 -3
- package/dist/schema/schemas.js +41 -28
- package/dist/schema/test-schema.js +27 -27
- package/dist/templates/context-file-section.md +3 -2
- package/dist/templates/example-requirements.js +1 -1
- package/dist/templates/example-requirements.ts +3 -1
- package/dist/templates/requirements-readme.js +1 -1
- package/dist/templates/requirements-readme.ts +1 -1
- package/dist/templates/skills/codebase-to-spec/SKILL.md +118 -0
- package/dist/utils/brand.js +3 -3
- package/dist/utils/browser-launch.js +4 -4
- package/dist/utils/context-file.d.ts +1 -1
- package/dist/utils/context-file.js +26 -26
- package/dist/utils/env.js +7 -7
- package/dist/utils/gitignore.js +7 -7
- package/dist/utils/oauth-callback-server.d.ts +1 -1
- package/dist/utils/oauth-callback-server.js +27 -25
- package/dist/utils/oauth-flow.js +32 -29
- package/dist/utils/project-discovery.d.ts +3 -3
- package/dist/utils/project-discovery.js +18 -17
- package/dist/utils/project-name.js +8 -8
- package/dist/utils/project-selector.d.ts +1 -1
- package/dist/utils/project-selector.js +24 -21
- package/dist/utils/project-settings.d.ts +1 -1
- package/dist/utils/project-settings.js +24 -22
- package/dist/utils/templates.js +6 -6
- package/package.json +3 -2
- package/dist/commands/browsertest.d.ts +0 -6
- package/dist/commands/login.d.ts +0 -12
- package/dist/commands/login.js +0 -117
- package/dist/commands/logout.d.ts +0 -5
- package/dist/commands/logout.js +0 -17
- package/dist/commands/mcp-setup.d.ts +0 -5
- package/dist/commands/mcp-setup.js +0 -431
- package/dist/commands/test.d.ts +0 -6
- package/dist/commands/test.js +0 -78
- package/dist/mcp/handlers/coverage.d.ts +0 -44
- package/dist/mcp/handlers/coverage.js +0 -105
- package/dist/mcp/types.d.ts +0 -27
- package/dist/mcp/types.js +0 -2
- package/dist/utils/local-project.d.ts +0 -31
- package/dist/utils/local-project.js +0 -33
- package/dist/utils/token-refresh.d.ts +0 -24
- package/dist/utils/token-refresh.js +0 -69
- package/dist/utils/token-storage.d.ts +0 -31
- package/dist/utils/token-storage.js +0 -57
- /package/dist/{mcp → requirements}/testCodeExtractor.d.ts +0 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache directory layout and path helpers for codebase-to-spec.
|
|
3
|
+
*
|
|
4
|
+
* All pipeline artifacts live under `.dotrequirements-cache/` in the project
|
|
5
|
+
* root. Wrappers and downstream stages read cached inputs by these paths;
|
|
6
|
+
* resumability (CTS-RESUME-1..4) is driven by the presence/absence of these
|
|
7
|
+
* files.
|
|
8
|
+
*
|
|
9
|
+
* Requirements covered:
|
|
10
|
+
* - CTS-PACK-1: pack stage paths (overview.txt, source.txt)
|
|
11
|
+
* - CTS-RESUME-1..4: per-stage cache detection
|
|
12
|
+
*/
|
|
13
|
+
export declare const CACHE_DIR_NAME = ".dotrequirements-cache";
|
|
14
|
+
export interface CachePaths {
|
|
15
|
+
/** Root cache directory (relative to project root). */
|
|
16
|
+
root: string;
|
|
17
|
+
/** Compressed pack — input for the planner and reviewers. */
|
|
18
|
+
overview: string;
|
|
19
|
+
/** Uncompressed pack — source for specifier slices. */
|
|
20
|
+
source: string;
|
|
21
|
+
/** Approved outline (after planning + review-loop convergence). */
|
|
22
|
+
outlineFinal: string;
|
|
23
|
+
/** Per-turn outline files: outline-1.json, outline-2.json, ... */
|
|
24
|
+
outlineTurn(n: number): string;
|
|
25
|
+
/** Per-turn outline reviewer JSON output. */
|
|
26
|
+
outlineReviewTurn(n: number): string;
|
|
27
|
+
/** Partials directory. */
|
|
28
|
+
partialsDir: string;
|
|
29
|
+
/** Partial file for a given sanitized area name. */
|
|
30
|
+
partial(sanitizedAreaName: string): string;
|
|
31
|
+
/** Composed spec, post-compose, pre-edit. */
|
|
32
|
+
composedSpec: string;
|
|
33
|
+
/** Per-turn edited spec files: spec-v1.md, spec-v2.md, ... */
|
|
34
|
+
specVersion(n: number): string;
|
|
35
|
+
/** Per-turn spec reviewer JSON output. */
|
|
36
|
+
specReviewTurn(n: number): string;
|
|
37
|
+
/** Final spec at a stable path, post-edit-loop. The present stage reads from here. */
|
|
38
|
+
specFinal: string;
|
|
39
|
+
/** Final pipeline summary JSON, surfaced by present and consumed by wrappers. */
|
|
40
|
+
pipelineSummary: string;
|
|
41
|
+
}
|
|
42
|
+
export declare function cachePaths(projectRoot: string): CachePaths;
|
|
43
|
+
export declare function ensureCacheDir(projectRoot: string): CachePaths;
|
|
44
|
+
/**
|
|
45
|
+
* Remove all cached artifacts. Used by `--fresh`.
|
|
46
|
+
* Requirement: CTS-RESUME-4
|
|
47
|
+
*/
|
|
48
|
+
export declare function clearCache(projectRoot: string): void;
|
|
49
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache directory layout and path helpers for codebase-to-spec.
|
|
3
|
+
*
|
|
4
|
+
* All pipeline artifacts live under `.dotrequirements-cache/` in the project
|
|
5
|
+
* root. Wrappers and downstream stages read cached inputs by these paths;
|
|
6
|
+
* resumability (CTS-RESUME-1..4) is driven by the presence/absence of these
|
|
7
|
+
* files.
|
|
8
|
+
*
|
|
9
|
+
* Requirements covered:
|
|
10
|
+
* - CTS-PACK-1: pack stage paths (overview.txt, source.txt)
|
|
11
|
+
* - CTS-RESUME-1..4: per-stage cache detection
|
|
12
|
+
*/
|
|
13
|
+
import { existsSync, mkdirSync, rmSync } from "node:fs";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
export const CACHE_DIR_NAME = ".dotrequirements-cache";
|
|
16
|
+
export function cachePaths(projectRoot) {
|
|
17
|
+
const root = join(projectRoot, CACHE_DIR_NAME);
|
|
18
|
+
return {
|
|
19
|
+
root,
|
|
20
|
+
overview: join(root, "overview.txt"),
|
|
21
|
+
source: join(root, "source.txt"),
|
|
22
|
+
outlineFinal: join(root, "outline-final.json"),
|
|
23
|
+
outlineTurn: (n) => join(root, `outline-${n}.json`),
|
|
24
|
+
outlineReviewTurn: (n) => join(root, `outline-review-${n}.json`),
|
|
25
|
+
partialsDir: join(root, "partials"),
|
|
26
|
+
partial: (name) => join(root, "partials", `${name}.partial.md`),
|
|
27
|
+
composedSpec: join(root, "spec-composed.md"),
|
|
28
|
+
specVersion: (n) => join(root, `spec-v${n}.md`),
|
|
29
|
+
specReviewTurn: (n) => join(root, `spec-review-${n}.json`),
|
|
30
|
+
specFinal: join(root, "spec-final.md"),
|
|
31
|
+
pipelineSummary: join(root, "pipeline-summary.json"),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function ensureCacheDir(projectRoot) {
|
|
35
|
+
const paths = cachePaths(projectRoot);
|
|
36
|
+
if (!existsSync(paths.root)) {
|
|
37
|
+
mkdirSync(paths.root, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
if (!existsSync(paths.partialsDir)) {
|
|
40
|
+
mkdirSync(paths.partialsDir, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
return paths;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Remove all cached artifacts. Used by `--fresh`.
|
|
46
|
+
* Requirement: CTS-RESUME-4
|
|
47
|
+
*/
|
|
48
|
+
export function clearCache(projectRoot) {
|
|
49
|
+
const root = join(projectRoot, CACHE_DIR_NAME);
|
|
50
|
+
if (existsSync(root)) {
|
|
51
|
+
rmSync(root, { recursive: true, force: true });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `claude -p` subprocess wrapper for codebase-to-spec.
|
|
3
|
+
*
|
|
4
|
+
* Provides a small typed surface for invoking the user's Claude Code agent in
|
|
5
|
+
* non-interactive (print) mode, with optional stateful session continuation
|
|
6
|
+
* and JSON-schema output validation.
|
|
7
|
+
*
|
|
8
|
+
* Why this lives here:
|
|
9
|
+
* - The pipeline spawns the user's claude binary, inheriting the user's
|
|
10
|
+
* environment so whatever auth mode they've configured (API key, OAuth via
|
|
11
|
+
* Claude Code subscription, etc.) is what gets used.
|
|
12
|
+
* - Each invocation is a separate context window. Stateful loops use
|
|
13
|
+
* --session-id and --resume to maintain conversation state across turns.
|
|
14
|
+
*
|
|
15
|
+
* Requirements covered:
|
|
16
|
+
* - CTS-PLAN-2, CTS-EDIT-1: stateful reviewer sessions via --session-id / --resume
|
|
17
|
+
* - CTS-PLAN-2, CTS-EDIT-1: JSON-schema-validated output via --json-schema
|
|
18
|
+
*/
|
|
19
|
+
export interface ClaudeRunOptions {
|
|
20
|
+
/** System prompt content (passed via --system-prompt). */
|
|
21
|
+
systemPrompt: string;
|
|
22
|
+
/** User message (passed as final positional arg). */
|
|
23
|
+
userMessage: string;
|
|
24
|
+
/** Model alias (e.g., 'sonnet') or full name. Defaults to 'sonnet'. */
|
|
25
|
+
model?: string;
|
|
26
|
+
/** Tools the agent may use. Defaults to ['Read']. */
|
|
27
|
+
tools?: string[];
|
|
28
|
+
/** Directories to grant filesystem access to (via --add-dir). */
|
|
29
|
+
addDirs?: string[];
|
|
30
|
+
/** Optional JSON schema for structured output validation. */
|
|
31
|
+
jsonSchema?: object;
|
|
32
|
+
/**
|
|
33
|
+
* Stateful session control:
|
|
34
|
+
* - `{ kind: 'fresh', sessionId: '<uuid>' }` — create a new session with this id
|
|
35
|
+
* - `{ kind: 'resume', sessionId: '<uuid>' }` — continue an existing session
|
|
36
|
+
* - omitted / undefined — stateless, no persistence
|
|
37
|
+
*/
|
|
38
|
+
session?: {
|
|
39
|
+
kind: "fresh";
|
|
40
|
+
sessionId: string;
|
|
41
|
+
} | {
|
|
42
|
+
kind: "resume";
|
|
43
|
+
sessionId: string;
|
|
44
|
+
};
|
|
45
|
+
/** Override the working directory. Defaults to system temp dir. */
|
|
46
|
+
cwd?: string;
|
|
47
|
+
/** Override permission mode. Defaults to 'bypassPermissions'. */
|
|
48
|
+
permissionMode?: "bypassPermissions" | "auto" | "default" | "acceptEdits";
|
|
49
|
+
/** Path to the claude binary. Defaults to 'claude' (found in PATH). */
|
|
50
|
+
claudeBinary?: string;
|
|
51
|
+
}
|
|
52
|
+
export interface ClaudeRunResult {
|
|
53
|
+
/** Process exit code. */
|
|
54
|
+
exitCode: number;
|
|
55
|
+
/** Captured stdout. */
|
|
56
|
+
stdout: string;
|
|
57
|
+
/** Captured stderr. */
|
|
58
|
+
stderr: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Default runner — spawns the `claude` binary as a subprocess.
|
|
62
|
+
*/
|
|
63
|
+
export declare function runClaude(options: ClaudeRunOptions): Promise<ClaudeRunResult>;
|
|
64
|
+
/**
|
|
65
|
+
* Pluggable runner interface so tests can swap in a mock without spawning
|
|
66
|
+
* a real subprocess. Production code uses `runClaude`; tests pass a fake.
|
|
67
|
+
*/
|
|
68
|
+
export type ClaudeRunner = (options: ClaudeRunOptions) => Promise<ClaudeRunResult>;
|
|
69
|
+
//# sourceMappingURL=claude.d.ts.map
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `claude -p` subprocess wrapper for codebase-to-spec.
|
|
3
|
+
*
|
|
4
|
+
* Provides a small typed surface for invoking the user's Claude Code agent in
|
|
5
|
+
* non-interactive (print) mode, with optional stateful session continuation
|
|
6
|
+
* and JSON-schema output validation.
|
|
7
|
+
*
|
|
8
|
+
* Why this lives here:
|
|
9
|
+
* - The pipeline spawns the user's claude binary, inheriting the user's
|
|
10
|
+
* environment so whatever auth mode they've configured (API key, OAuth via
|
|
11
|
+
* Claude Code subscription, etc.) is what gets used.
|
|
12
|
+
* - Each invocation is a separate context window. Stateful loops use
|
|
13
|
+
* --session-id and --resume to maintain conversation state across turns.
|
|
14
|
+
*
|
|
15
|
+
* Requirements covered:
|
|
16
|
+
* - CTS-PLAN-2, CTS-EDIT-1: stateful reviewer sessions via --session-id / --resume
|
|
17
|
+
* - CTS-PLAN-2, CTS-EDIT-1: JSON-schema-validated output via --json-schema
|
|
18
|
+
*/
|
|
19
|
+
import { spawn } from "node:child_process";
|
|
20
|
+
import { tmpdir } from "node:os";
|
|
21
|
+
/**
|
|
22
|
+
* Default runner — spawns the `claude` binary as a subprocess.
|
|
23
|
+
*/
|
|
24
|
+
export async function runClaude(options) {
|
|
25
|
+
const { systemPrompt, userMessage, model = "sonnet", tools = ["Read"], addDirs = [], jsonSchema, session, cwd = tmpdir(), permissionMode = "bypassPermissions", claudeBinary = "claude", } = options;
|
|
26
|
+
const args = ["-p"];
|
|
27
|
+
args.push("--model", model);
|
|
28
|
+
args.push("--permission-mode", permissionMode);
|
|
29
|
+
// `--output-format text` does NOT enforce `--json-schema` (we verified this
|
|
30
|
+
// empirically). When we want schema-enforced JSON we need
|
|
31
|
+
// `--output-format json`, which wraps the response in a metadata envelope
|
|
32
|
+
// whose `structured_output` field contains the validated JSON.
|
|
33
|
+
args.push("--output-format", jsonSchema ? "json" : "text");
|
|
34
|
+
if (tools.length > 0) {
|
|
35
|
+
args.push("--tools", tools.join(","));
|
|
36
|
+
}
|
|
37
|
+
for (const dir of addDirs) {
|
|
38
|
+
args.push("--add-dir", dir);
|
|
39
|
+
}
|
|
40
|
+
if (jsonSchema) {
|
|
41
|
+
args.push("--json-schema", JSON.stringify(jsonSchema));
|
|
42
|
+
}
|
|
43
|
+
if (session) {
|
|
44
|
+
if (session.kind === "fresh") {
|
|
45
|
+
args.push("--session-id", session.sessionId);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
args.push("--resume", session.sessionId);
|
|
49
|
+
// On resume we still need to re-pass permission mode and add-dirs;
|
|
50
|
+
// resume doesn't preserve them by default.
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
args.push("--no-session-persistence");
|
|
55
|
+
}
|
|
56
|
+
// System prompt — only on initial calls. On resume, the system prompt is
|
|
57
|
+
// already baked into the session and re-passing it can confuse the agent.
|
|
58
|
+
if (!session || session.kind === "fresh") {
|
|
59
|
+
args.push("--append-system-prompt", systemPrompt);
|
|
60
|
+
}
|
|
61
|
+
// The user message is always the final positional arg.
|
|
62
|
+
args.push(userMessage);
|
|
63
|
+
return new Promise((resolve, reject) => {
|
|
64
|
+
const child = spawn(claudeBinary, args, {
|
|
65
|
+
cwd,
|
|
66
|
+
// Inherit the parent environment so `claude -p` uses whatever auth
|
|
67
|
+
// mode the user has configured (API key if set, OAuth otherwise).
|
|
68
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
69
|
+
});
|
|
70
|
+
// Close stdin immediately — we pass user message via argv, not stdin.
|
|
71
|
+
child.stdin.end();
|
|
72
|
+
let stdout = "";
|
|
73
|
+
let stderr = "";
|
|
74
|
+
child.stdout.on("data", (chunk) => {
|
|
75
|
+
stdout += chunk.toString();
|
|
76
|
+
});
|
|
77
|
+
child.stderr.on("data", (chunk) => {
|
|
78
|
+
stderr += chunk.toString();
|
|
79
|
+
});
|
|
80
|
+
child.on("error", reject);
|
|
81
|
+
child.on("close", (code) => {
|
|
82
|
+
const exitCode = code ?? 0;
|
|
83
|
+
// If we asked for schema-validated JSON, claude -p returns a metadata
|
|
84
|
+
// envelope. Extract the agent's actual output from `structured_output`
|
|
85
|
+
// (preferred) or `result` (fallback). Callers expect `stdout` to
|
|
86
|
+
// contain only the agent's output, not the envelope.
|
|
87
|
+
if (jsonSchema && exitCode === 0 && stdout.trim().length > 0) {
|
|
88
|
+
try {
|
|
89
|
+
const envelope = JSON.parse(stdout);
|
|
90
|
+
if (envelope && typeof envelope === "object") {
|
|
91
|
+
if (envelope.is_error) {
|
|
92
|
+
// Surface the API error message on stderr so callers can see it.
|
|
93
|
+
const apiErrSnippet = typeof envelope.result === "string"
|
|
94
|
+
? envelope.result
|
|
95
|
+
: JSON.stringify(envelope);
|
|
96
|
+
resolve({
|
|
97
|
+
exitCode: exitCode || 1,
|
|
98
|
+
stdout: "",
|
|
99
|
+
stderr: `${stderr}\nclaude -p reported is_error: ${apiErrSnippet}`,
|
|
100
|
+
});
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if ("structured_output" in envelope) {
|
|
104
|
+
resolve({
|
|
105
|
+
exitCode,
|
|
106
|
+
stdout: JSON.stringify(envelope.structured_output),
|
|
107
|
+
stderr,
|
|
108
|
+
});
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (typeof envelope.result === "string") {
|
|
112
|
+
resolve({ exitCode, stdout: envelope.result, stderr });
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// Fall through to raw stdout. Caller's parser will surface the
|
|
119
|
+
// schema-validation failure.
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
resolve({ exitCode, stdout, stderr });
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=claude.js.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compose stage: assemble per-area partials into a single composed spec.
|
|
3
|
+
*
|
|
4
|
+
* The composed document has YAML frontmatter (title, defaultPrefix), an H1
|
|
5
|
+
* title, a summary paragraph from the outline, and one H2 section per area.
|
|
6
|
+
* Each area section contains the partial's content.
|
|
7
|
+
*
|
|
8
|
+
* After assembly, schema validation runs over the document. Mechanical errors
|
|
9
|
+
* (duplicate IDs within an area, etc.) trigger deterministic fixes; other
|
|
10
|
+
* errors surface to the caller.
|
|
11
|
+
*
|
|
12
|
+
* Requirements covered:
|
|
13
|
+
* - CTS-COMPOSE-1: Compose produces a single Markdown document from partials
|
|
14
|
+
* - CTS-COMPOSE-2: Compose validates the resulting document and corrects mechanical errors
|
|
15
|
+
*/
|
|
16
|
+
import type { Outline } from "./schemas.js";
|
|
17
|
+
export interface ComposeOptions {
|
|
18
|
+
outline: Outline;
|
|
19
|
+
/** Resolves an area's sanitized name to its partial file path. */
|
|
20
|
+
partialPathFor: (sanitizedAreaName: string) => string;
|
|
21
|
+
/** Where to write the composed spec. */
|
|
22
|
+
composedPath: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ComposeResult {
|
|
25
|
+
/** Path the composed spec was written to. */
|
|
26
|
+
composedPath: string;
|
|
27
|
+
/** Number of partials that contributed content. */
|
|
28
|
+
partialsIncluded: number;
|
|
29
|
+
/** Number of areas whose partial was missing or marked as missing. */
|
|
30
|
+
partialsMissing: number;
|
|
31
|
+
/** Validation outcome — undefined when validation passes. */
|
|
32
|
+
validationError?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Assemble the composed Markdown document.
|
|
36
|
+
*
|
|
37
|
+
* Public so tests can call without disk side effects (composedPath isn't used
|
|
38
|
+
* here; the caller writes the returned string).
|
|
39
|
+
*/
|
|
40
|
+
export declare function assembleComposedDocument(outline: Outline, partialPathFor: (sanitized: string) => string): {
|
|
41
|
+
content: string;
|
|
42
|
+
partialsIncluded: number;
|
|
43
|
+
partialsMissing: number;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Compose the spec and write it to disk. Validates the result.
|
|
47
|
+
*/
|
|
48
|
+
export declare function runCompose(options: ComposeOptions): ComposeResult;
|
|
49
|
+
//# sourceMappingURL=compose.d.ts.map
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compose stage: assemble per-area partials into a single composed spec.
|
|
3
|
+
*
|
|
4
|
+
* The composed document has YAML frontmatter (title, defaultPrefix), an H1
|
|
5
|
+
* title, a summary paragraph from the outline, and one H2 section per area.
|
|
6
|
+
* Each area section contains the partial's content.
|
|
7
|
+
*
|
|
8
|
+
* After assembly, schema validation runs over the document. Mechanical errors
|
|
9
|
+
* (duplicate IDs within an area, etc.) trigger deterministic fixes; other
|
|
10
|
+
* errors surface to the caller.
|
|
11
|
+
*
|
|
12
|
+
* Requirements covered:
|
|
13
|
+
* - CTS-COMPOSE-1: Compose produces a single Markdown document from partials
|
|
14
|
+
* - CTS-COMPOSE-2: Compose validates the resulting document and corrects mechanical errors
|
|
15
|
+
*/
|
|
16
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
17
|
+
import { parseRequirementsFile } from "../schema/parser.js";
|
|
18
|
+
import { sanitizeAreaName } from "./fan-out.js";
|
|
19
|
+
/**
|
|
20
|
+
* Read a partial file. If missing or marked as a missing-partial placeholder,
|
|
21
|
+
* return null (caller will emit a stub section).
|
|
22
|
+
*/
|
|
23
|
+
function readPartial(path) {
|
|
24
|
+
if (!existsSync(path))
|
|
25
|
+
return null;
|
|
26
|
+
const content = readFileSync(path, "utf-8");
|
|
27
|
+
const trimmed = content.trim();
|
|
28
|
+
if (!trimmed)
|
|
29
|
+
return null;
|
|
30
|
+
// Common placeholders for missing partials produced by fan-out on failure
|
|
31
|
+
// or by zero-files-assigned areas.
|
|
32
|
+
if (trimmed.startsWith("_(missing partial") ||
|
|
33
|
+
trimmed.startsWith("_(no files assigned")) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return content;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Assemble the composed Markdown document.
|
|
40
|
+
*
|
|
41
|
+
* Public so tests can call without disk side effects (composedPath isn't used
|
|
42
|
+
* here; the caller writes the returned string).
|
|
43
|
+
*/
|
|
44
|
+
export function assembleComposedDocument(outline, partialPathFor) {
|
|
45
|
+
const parts = [];
|
|
46
|
+
parts.push("---");
|
|
47
|
+
parts.push("document:");
|
|
48
|
+
parts.push(` title: "${escapeYamlString(outline.title)}"`);
|
|
49
|
+
parts.push(` defaultPrefix: ${outline.defaultPrefix}`);
|
|
50
|
+
parts.push("---");
|
|
51
|
+
parts.push("");
|
|
52
|
+
parts.push(`# ${outline.title}`);
|
|
53
|
+
parts.push("");
|
|
54
|
+
parts.push(outline.summary.trim());
|
|
55
|
+
parts.push("");
|
|
56
|
+
let partialsIncluded = 0;
|
|
57
|
+
let partialsMissing = 0;
|
|
58
|
+
for (const area of outline.areas) {
|
|
59
|
+
const sanitized = sanitizeAreaName(area.name);
|
|
60
|
+
const partialPath = partialPathFor(sanitized);
|
|
61
|
+
parts.push("---");
|
|
62
|
+
parts.push("");
|
|
63
|
+
parts.push(`## ${area.name}`);
|
|
64
|
+
parts.push("");
|
|
65
|
+
const partial = readPartial(partialPath);
|
|
66
|
+
if (partial) {
|
|
67
|
+
parts.push(partial.trim());
|
|
68
|
+
partialsIncluded++;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
parts.push(`_(partial for ${area.name} is missing or empty — surface this to the user when presenting the spec)_`);
|
|
72
|
+
partialsMissing++;
|
|
73
|
+
}
|
|
74
|
+
parts.push("");
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
content: parts.join("\n"),
|
|
78
|
+
partialsIncluded,
|
|
79
|
+
partialsMissing,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/** Escape a string for safe YAML scalar use. */
|
|
83
|
+
function escapeYamlString(s) {
|
|
84
|
+
return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Attempt to fix mechanical errors in a composed document.
|
|
88
|
+
*
|
|
89
|
+
* For v0, the simplest mechanical fix is duplicate IDs within an area —
|
|
90
|
+
* we detect and renumber them sequentially. Position-path and Gherkin syntax
|
|
91
|
+
* errors surface to the validator's caller; we don't auto-fix those because
|
|
92
|
+
* the editor loop is better suited to it.
|
|
93
|
+
*/
|
|
94
|
+
function fixMechanicalErrors(content) {
|
|
95
|
+
// Detect duplicate IDs (lines like "PREFIX-AREA-001: ...") within each area.
|
|
96
|
+
// For v0 we punt — the schema validator will catch them and the caller will
|
|
97
|
+
// surface to the user. Returning content unchanged keeps the contract: the
|
|
98
|
+
// function is a no-op for inputs without fixable errors.
|
|
99
|
+
return content;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Compose the spec and write it to disk. Validates the result.
|
|
103
|
+
*/
|
|
104
|
+
export function runCompose(options) {
|
|
105
|
+
const { outline, partialPathFor, composedPath } = options;
|
|
106
|
+
const { content, partialsIncluded, partialsMissing } = assembleComposedDocument(outline, partialPathFor);
|
|
107
|
+
const finalContent = fixMechanicalErrors(content);
|
|
108
|
+
writeFileSync(composedPath, finalContent, "utf-8");
|
|
109
|
+
// Validate
|
|
110
|
+
let validationError;
|
|
111
|
+
try {
|
|
112
|
+
parseRequirementsFile(finalContent);
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
validationError = err instanceof Error ? err.message : String(err);
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
composedPath,
|
|
119
|
+
partialsIncluded,
|
|
120
|
+
partialsMissing,
|
|
121
|
+
validationError,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=compose.js.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec review + editor loop.
|
|
3
|
+
*
|
|
4
|
+
* Same structure as the outline review loop, scaled up for document-level
|
|
5
|
+
* revision: stateful spec reviewer + stateless editor. The reviewer
|
|
6
|
+
* persists across turns so it can compare prior spec versions to the latest.
|
|
7
|
+
*
|
|
8
|
+
* Requirements covered:
|
|
9
|
+
* - CTS-EDIT-1: stateful spec reviewer
|
|
10
|
+
* - CTS-EDIT-2: approved → proceed
|
|
11
|
+
* - CTS-EDIT-3: approved-with-revisions → one mechanical edit pass
|
|
12
|
+
* - CTS-EDIT-4: requires-another-review → revise & resubmit
|
|
13
|
+
* - CTS-EDIT-5: editor operates on cohesive document
|
|
14
|
+
* - CTS-EDIT-6: max-turns + convergence nudge
|
|
15
|
+
*/
|
|
16
|
+
import { type ClaudeRunner } from "./claude.js";
|
|
17
|
+
import type { ProgressEmitter } from "./progress.js";
|
|
18
|
+
import { type SpecReview } from "./schemas.js";
|
|
19
|
+
export declare const DEFAULT_EDIT_LOOP_MAX_TURNS = 6;
|
|
20
|
+
export interface EditLoopOptions {
|
|
21
|
+
/** Path to the composed spec (input). */
|
|
22
|
+
composedSpecPath: string;
|
|
23
|
+
/**
|
|
24
|
+
* Resolver: given a turn number (1-indexed), return the path the editor's
|
|
25
|
+
* output for that turn should land at. Loop persists each version.
|
|
26
|
+
*/
|
|
27
|
+
specVersionFor: (turn: number) => string;
|
|
28
|
+
/** Path to the codebase pack. */
|
|
29
|
+
fullPackPath: string;
|
|
30
|
+
/** Directories for filesystem access. */
|
|
31
|
+
addDirs: string[];
|
|
32
|
+
maxTurns?: number;
|
|
33
|
+
runner?: ClaudeRunner;
|
|
34
|
+
model?: string;
|
|
35
|
+
progress?: ProgressEmitter;
|
|
36
|
+
/** Override session id (for tests / reproducibility). */
|
|
37
|
+
sessionId?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface EditLoopResult {
|
|
40
|
+
/** Path to the final spec (post-edit if applicable). */
|
|
41
|
+
finalSpecPath: string;
|
|
42
|
+
/** Per-turn spec paths (turn 1 is the composed-spec copy, then each revision). */
|
|
43
|
+
specPathsByTurn: string[];
|
|
44
|
+
/** Per-turn reviewer results. */
|
|
45
|
+
reviewsByTurn: SpecReview[];
|
|
46
|
+
/** The final reviewer verdict. */
|
|
47
|
+
finalVerdict: SpecReview["verdict"];
|
|
48
|
+
/** Number of review turns used. */
|
|
49
|
+
turnsUsed: number;
|
|
50
|
+
/** True if the loop hit max-turns without converging. */
|
|
51
|
+
hitMaxTurns: boolean;
|
|
52
|
+
}
|
|
53
|
+
export declare function runEditLoop(options: EditLoopOptions): Promise<EditLoopResult>;
|
|
54
|
+
//# sourceMappingURL=edit-loop.d.ts.map
|