@popoverai/dotrequirements 0.25.0 → 0.26.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/dist/cli.js +8 -1
- package/dist/codebase-to-spec/area-name.d.ts +13 -0
- package/dist/codebase-to-spec/area-name.js +18 -0
- package/dist/codebase-to-spec/cache.d.ts +31 -0
- package/dist/codebase-to-spec/cache.js +29 -1
- package/dist/codebase-to-spec/compose.d.ts +7 -4
- package/dist/codebase-to-spec/compose.js +10 -21
- package/dist/codebase-to-spec/dispatch.js +1 -1
- package/dist/codebase-to-spec/present.d.ts +9 -0
- package/dist/codebase-to-spec/present.js +30 -4
- package/dist/codebase-to-spec/prompts/planner-initial.d.ts +3 -2
- package/dist/codebase-to-spec/prompts/planner-initial.js +3 -2
- package/dist/codebase-to-spec/renumber.d.ts +52 -0
- package/dist/codebase-to-spec/renumber.js +105 -0
- package/dist/codebase-to-spec/schemas.d.ts +35 -240
- package/dist/codebase-to-spec/schemas.js +5 -173
- package/dist/codebase-to-spec/skill-install.d.ts +16 -6
- package/dist/codebase-to-spec/skill-install.js +109 -47
- package/dist/codebase-to-spec/version-check.d.ts +31 -0
- package/dist/codebase-to-spec/version-check.js +56 -0
- package/dist/commands/ai-setup.d.ts +12 -1
- package/dist/commands/ai-setup.js +65 -33
- package/dist/commands/codebase-to-spec/dispatch-editor.js +1 -1
- package/dist/commands/codebase-to-spec/dispatch-spec.js +1 -1
- package/dist/commands/codebase-to-spec/index.js +7 -103
- package/dist/commands/codebase-to-spec/pack.d.ts +4 -0
- package/dist/commands/codebase-to-spec/pack.js +25 -1
- package/dist/commands/codebase-to-spec/present-orchestrator.d.ts +3 -1
- package/dist/commands/codebase-to-spec/present-orchestrator.js +11 -4
- package/dist/commands/init.js +6 -1
- package/dist/commands/link-resolution.d.ts +79 -0
- package/dist/commands/link-resolution.js +141 -0
- package/dist/commands/link.d.ts +14 -4
- package/dist/commands/link.js +369 -16
- package/dist/commands/pull.js +19 -2
- package/dist/commands/push.js +36 -2
- package/dist/convex.d.ts +5 -3
- package/dist/convex.js +5 -3
- package/dist/harness/cache.d.ts +0 -14
- package/dist/harness/cache.js +1 -41
- package/dist/harness/finalize.js +2 -2
- package/dist/harness/prepare.js +1 -3
- package/dist/harness/requirementsLoader.d.ts +3 -3
- package/dist/harness/requirementsLoader.js +13 -8
- package/dist/mcp/handlers/authoring.d.ts +5 -5
- package/dist/mcp/handlers/authoring.js +9 -9
- package/dist/mcp/handlers/push.d.ts +2 -2
- package/dist/mcp/handlers/push.js +36 -3
- package/dist/mcp/handlers/review.d.ts +4 -4
- package/dist/mcp/handlers/review.js +4 -4
- package/dist/mcp/handlers/search.d.ts +1 -1
- package/dist/mcp/handlers/search.js +1 -1
- package/dist/mcp/index.js +29 -0
- package/dist/push/core.d.ts +18 -0
- package/dist/push/core.js +70 -3
- package/dist/push/index.d.ts +1 -1
- package/dist/push/index.js +1 -1
- package/dist/schema/parser-core.js +5 -1
- package/dist/schema/parser.js +5 -1
- package/dist/schema/run-marker.d.ts +38 -0
- package/dist/schema/run-marker.js +138 -0
- package/dist/schema/schemas.d.ts +12 -0
- package/dist/schema/schemas.js +1 -0
- package/dist/templates/agents/cts-worker.md +1 -1
- package/dist/templates/skills/codebase-to-spec/SKILL.md +39 -11
- package/dist/templates/workflows/specify-codebase.js +4 -2
- package/dist/utils/own-package.d.ts +10 -0
- package/dist/utils/own-package.js +13 -0
- package/dist/utils/project-selector.d.ts +5 -0
- package/dist/utils/project-selector.js +4 -0
- package/package.json +2 -2
- package/dist/codebase-to-spec/edit-loop.d.ts +0 -54
- package/dist/codebase-to-spec/edit-loop.js +0 -195
- package/dist/codebase-to-spec/editor.d.ts +0 -54
- package/dist/codebase-to-spec/editor.js +0 -74
- package/dist/codebase-to-spec/fan-out.d.ts +0 -63
- package/dist/codebase-to-spec/fan-out.js +0 -215
- package/dist/codebase-to-spec/outline-review-loop.d.ts +0 -51
- package/dist/codebase-to-spec/outline-review-loop.js +0 -187
- package/dist/codebase-to-spec/planner.d.ts +0 -41
- package/dist/codebase-to-spec/planner.js +0 -76
- package/dist/codebase-to-spec/prompts/outline-reviewer.d.ts +0 -12
- package/dist/codebase-to-spec/prompts/outline-reviewer.js +0 -89
- package/dist/codebase-to-spec/slice.d.ts +0 -49
- package/dist/codebase-to-spec/slice.js +0 -111
- package/dist/codebase-to-spec/specifier.d.ts +0 -60
- package/dist/codebase-to-spec/specifier.js +0 -85
- package/dist/codebase-to-spec/summary.d.ts +0 -51
- package/dist/codebase-to-spec/summary.js +0 -183
- package/dist/commands/codebase-to-spec/compose.d.ts +0 -14
- package/dist/commands/codebase-to-spec/compose.js +0 -57
- package/dist/commands/codebase-to-spec/edit-loop.d.ts +0 -16
- package/dist/commands/codebase-to-spec/edit-loop.js +0 -83
- package/dist/commands/codebase-to-spec/fan-out.d.ts +0 -19
- package/dist/commands/codebase-to-spec/fan-out.js +0 -77
- package/dist/commands/codebase-to-spec/plan-loop.d.ts +0 -26
- package/dist/commands/codebase-to-spec/plan-loop.js +0 -105
- package/dist/commands/codebase-to-spec/present.d.ts +0 -26
- package/dist/commands/codebase-to-spec/present.js +0 -97
- package/dist/commands/codebase-to-spec/run.d.ts +0 -20
- package/dist/commands/codebase-to-spec/run.js +0 -86
- package/dist/commands/codebase-to-spec/specify-area.d.ts +0 -18
- package/dist/commands/codebase-to-spec/specify-area.js +0 -82
|
@@ -5,30 +5,23 @@
|
|
|
5
5
|
* be invoked granularly (e.g., `dotrequirements cts pack`) per CTS-CLI-2.
|
|
6
6
|
*/
|
|
7
7
|
import { installCtsAbortHandlers } from "../../codebase-to-spec/progress.js";
|
|
8
|
-
import { composeCommand } from "./compose.js";
|
|
9
8
|
import { composeOrchestratorCommand } from "./compose-orchestrator.js";
|
|
10
9
|
import { dispatchContextCommand } from "./dispatch-context.js";
|
|
11
10
|
import { dispatchEditorCommand } from "./dispatch-editor.js";
|
|
12
11
|
import { dispatchPlannerCommand } from "./dispatch-planner.js";
|
|
13
12
|
import { dispatchSpecCommand } from "./dispatch-spec.js";
|
|
14
|
-
import { editLoopCommand } from "./edit-loop.js";
|
|
15
|
-
import { fanOutCommand } from "./fan-out.js";
|
|
16
13
|
import { packCommand } from "./pack.js";
|
|
17
|
-
import { planLoopCommand } from "./plan-loop.js";
|
|
18
|
-
import { presentCommand } from "./present.js";
|
|
19
14
|
import { presentOrchestratorCommand } from "./present-orchestrator.js";
|
|
20
|
-
import { runCommand } from "./run.js";
|
|
21
15
|
import { skillInstallCommand } from "./skill-install.js";
|
|
22
|
-
import { specifyAreaCommand } from "./specify-area.js";
|
|
23
16
|
import { styleCheckCommand } from "./style-check.js";
|
|
24
17
|
import { validateCommand } from "./validate.js";
|
|
25
18
|
/**
|
|
26
19
|
* Marker used to hide a command from `--help` output without removing it.
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* in
|
|
30
|
-
*
|
|
31
|
-
*
|
|
20
|
+
* The conversational orchestrator's plumbing commands (the `dispatch-*` payload
|
|
21
|
+
* builders and the `-orchestrator` compose/present variants) and the
|
|
22
|
+
* skill-install entry point ship in the npm package but aren't surfaced in help
|
|
23
|
+
* listings — they're invoked by name by the orchestrator skill and workflow,
|
|
24
|
+
* not by users, so they stay out of the user-facing CLI surface.
|
|
32
25
|
*/
|
|
33
26
|
const HIDDEN = { hidden: true };
|
|
34
27
|
export function registerCodebaseToSpec(program) {
|
|
@@ -37,9 +30,8 @@ export function registerCodebaseToSpec(program) {
|
|
|
37
30
|
.alias("cts")
|
|
38
31
|
.description("Generate dotrequirements behavioral specifications from a codebase");
|
|
39
32
|
// Install SIGTERM/SIGINT handlers for every cts subcommand so an interrupted
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
// subcommand action runs.
|
|
33
|
+
// stage emits a structured `pipeline/aborted` line before exiting. preAction
|
|
34
|
+
// fires after argv parsing and before the subcommand action runs.
|
|
43
35
|
cts.hook("preAction", () => {
|
|
44
36
|
installCtsAbortHandlers();
|
|
45
37
|
});
|
|
@@ -52,31 +44,6 @@ export function registerCodebaseToSpec(program) {
|
|
|
52
44
|
.option("--budget <tokens>", "Override the working-context budget (in tokens)", (v) => parseInt(v, 10))
|
|
53
45
|
.option("--ignore-requirements", "Exclude `.requirements/**` from the pack (use when testing cts against a codebase whose existing requirements should not influence the output)")
|
|
54
46
|
.action((opts) => packCommand({ ...opts, budgetTokens: opts.budget }));
|
|
55
|
-
cts
|
|
56
|
-
.command("plan-loop")
|
|
57
|
-
.description("Run the planner + outline review loop on the cached pack")
|
|
58
|
-
.option("--max-turns <n>", "Hard cap on review turns", (v) => parseInt(v, 10))
|
|
59
|
-
.option("--model <name>", "Override the model used by the planner and reviewer")
|
|
60
|
-
.action((opts) => planLoopCommand({
|
|
61
|
-
maxTurns: opts.maxTurns,
|
|
62
|
-
model: opts.model,
|
|
63
|
-
}));
|
|
64
|
-
cts
|
|
65
|
-
.command("specify-area <name>")
|
|
66
|
-
.description("Run the specifier for one named area from the cached outline")
|
|
67
|
-
.option("--model <name>", "Override the model used by the specifier")
|
|
68
|
-
.action((name, opts) => specifyAreaCommand(name, {
|
|
69
|
-
model: opts.model,
|
|
70
|
-
}));
|
|
71
|
-
cts
|
|
72
|
-
.command("fan-out")
|
|
73
|
-
.description("Run all specifiers in parallel for areas in the cached outline")
|
|
74
|
-
.option("-c, --concurrency <n>", "Max concurrent specifiers", (v) => parseInt(v, 10))
|
|
75
|
-
.option("--model <name>", "Override the model used by specifiers")
|
|
76
|
-
.action((opts) => fanOutCommand({
|
|
77
|
-
concurrency: opts.concurrency,
|
|
78
|
-
model: opts.model,
|
|
79
|
-
}));
|
|
80
47
|
cts
|
|
81
48
|
.command("validate <partial-path>")
|
|
82
49
|
.description("Validate the schema/syntax of a partial-spec file (deterministic, offline)")
|
|
@@ -86,69 +53,6 @@ export function registerCodebaseToSpec(program) {
|
|
|
86
53
|
.description("Run a local style-check on a partial-spec file (severity-categorized feedback)")
|
|
87
54
|
.option("--model <name>", "Override the model used for the check")
|
|
88
55
|
.action((partialPath, opts) => styleCheckCommand(partialPath, { model: opts.model }));
|
|
89
|
-
cts
|
|
90
|
-
.command("compose")
|
|
91
|
-
.description("Assemble per-area partials into a single composed spec")
|
|
92
|
-
.action(() => composeCommand());
|
|
93
|
-
cts
|
|
94
|
-
.command("edit-loop")
|
|
95
|
-
.description("Run the spec reviewer + editor loop on the composed spec")
|
|
96
|
-
.option("--max-turns <n>", "Hard cap on edit-loop turns", (v) => parseInt(v, 10))
|
|
97
|
-
.option("--model <name>", "Override the model used by the reviewer and editor")
|
|
98
|
-
.action((opts) => editLoopCommand({
|
|
99
|
-
maxTurns: opts.maxTurns,
|
|
100
|
-
model: opts.model,
|
|
101
|
-
}));
|
|
102
|
-
cts
|
|
103
|
-
.command("present")
|
|
104
|
-
.description("Write the final spec to .requirements/ and emit a pipeline summary")
|
|
105
|
-
.option("--interactive", "Force interactive mode (override TTY detection)")
|
|
106
|
-
.option("--non-interactive", "Force non-interactive mode (override TTY detection)")
|
|
107
|
-
.option("--overwrite", "Replace existing .requirements/ files without prompting")
|
|
108
|
-
.option("--skip-existing", "Leave existing .requirements/ files untouched")
|
|
109
|
-
.option("--ignore-requirements", "Write output to `.requirements/cts/` instead of `.requirements/` (use together with `cts pack --ignore-requirements` when isolating cts from an existing requirements directory)")
|
|
110
|
-
.action((opts) => {
|
|
111
|
-
const forceMode = opts.interactive
|
|
112
|
-
? "interactive"
|
|
113
|
-
: opts.nonInteractive
|
|
114
|
-
? "non-interactive"
|
|
115
|
-
: undefined;
|
|
116
|
-
return presentCommand({
|
|
117
|
-
forceMode,
|
|
118
|
-
overwrite: opts.overwrite,
|
|
119
|
-
skipExisting: opts.skipExisting,
|
|
120
|
-
ignoreRequirements: opts.ignoreRequirements,
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
cts
|
|
124
|
-
.command("run")
|
|
125
|
-
.description("Run the full pipeline (pack → plan → specify → compose → edit → present)")
|
|
126
|
-
.option("-s, --scope <path>", "Limit to files under this path (subdir of project root)")
|
|
127
|
-
.option("--fresh", "Clear the cache before running")
|
|
128
|
-
.option("--budget <tokens>", "Override the working-context budget (in tokens)", (v) => parseInt(v, 10))
|
|
129
|
-
.option("--max-turns <n>", "Hard cap on outline review turns", (v) => parseInt(v, 10))
|
|
130
|
-
.option("--model <name>", "Override the model used by agent calls")
|
|
131
|
-
.option("--interactive", "Force interactive mode for the present stage")
|
|
132
|
-
.option("--non-interactive", "Force non-interactive mode for the present stage")
|
|
133
|
-
.option("--overwrite", "Replace existing .requirements/ files without prompting")
|
|
134
|
-
.option("--skip-existing", "Leave existing .requirements/ files untouched")
|
|
135
|
-
.option("--ignore-requirements", "Exclude `.requirements/**` from the pack and write output to `.requirements/cts/` instead of `.requirements/` (use when testing cts against a codebase whose existing requirements should not influence the output)")
|
|
136
|
-
.action((opts) => {
|
|
137
|
-
const forceMode = opts.interactive
|
|
138
|
-
? "interactive"
|
|
139
|
-
: opts.nonInteractive
|
|
140
|
-
? "non-interactive"
|
|
141
|
-
: undefined;
|
|
142
|
-
return runCommand({
|
|
143
|
-
...opts,
|
|
144
|
-
budgetTokens: opts.budget,
|
|
145
|
-
maxTurns: opts.maxTurns,
|
|
146
|
-
forceMode,
|
|
147
|
-
overwrite: opts.overwrite,
|
|
148
|
-
skipExisting: opts.skipExisting,
|
|
149
|
-
ignoreRequirements: opts.ignoreRequirements,
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
56
|
cts
|
|
153
57
|
.command("dispatch-context <dispatch-id>", HIDDEN)
|
|
154
58
|
.description("Return the composed prompt for a worker dispatch as JSON on stdout (run by cts-worker subagents to fetch their dispatch prompt)")
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
* - CTS-CLI-3: Pipeline refuses to spec a codebase that exceeds the working context budget
|
|
8
8
|
* - CTS-PACK-1: Pack stage produces compressed and uncompressed views of the codebase
|
|
9
9
|
* - CTS-PACK-2: Engineer can extend the ignore list per project
|
|
10
|
+
* - CTS-PACK-4: Pack surfaces when a newer CLI version is available
|
|
10
11
|
* - CTS-OBSERVE-1: CLI emits structured progress as the pipeline runs
|
|
11
12
|
*/
|
|
13
|
+
import { checkForNewerVersion } from "../../codebase-to-spec/version-check.js";
|
|
12
14
|
export interface PackOptions {
|
|
13
15
|
scope?: string;
|
|
14
16
|
/**
|
|
@@ -28,6 +30,8 @@ export interface PackOptions {
|
|
|
28
30
|
progress?: import("../../codebase-to-spec/progress.js").ProgressEmitter;
|
|
29
31
|
/** Override the project root, mainly for tests. */
|
|
30
32
|
projectRoot?: string;
|
|
33
|
+
/** Override the registry version check, mainly for tests. */
|
|
34
|
+
checkVersion?: typeof checkForNewerVersion;
|
|
31
35
|
}
|
|
32
36
|
export declare function packCommand(rawOptions?: PackOptions): Promise<void>;
|
|
33
37
|
//# sourceMappingURL=pack.d.ts.map
|
|
@@ -7,19 +7,25 @@
|
|
|
7
7
|
* - CTS-CLI-3: Pipeline refuses to spec a codebase that exceeds the working context budget
|
|
8
8
|
* - CTS-PACK-1: Pack stage produces compressed and uncompressed views of the codebase
|
|
9
9
|
* - CTS-PACK-2: Engineer can extend the ignore list per project
|
|
10
|
+
* - CTS-PACK-4: Pack surfaces when a newer CLI version is available
|
|
10
11
|
* - CTS-OBSERVE-1: CLI emits structured progress as the pipeline runs
|
|
11
12
|
*/
|
|
12
13
|
import { resolve } from "node:path";
|
|
13
14
|
import { checkBudget, countTokensInFileExact, DEFAULT_BUDGET_TOKENS, formatBudgetExceededMessage, } from "../../codebase-to-spec/budget.js";
|
|
14
|
-
import { clearCache, ensureCacheDir } from "../../codebase-to-spec/cache.js";
|
|
15
|
+
import { clearCache, ensureCacheDir, writeRunConfig, } from "../../codebase-to-spec/cache.js";
|
|
15
16
|
import { ExitCode } from "../../codebase-to-spec/exit-codes.js";
|
|
16
17
|
import { runPack } from "../../codebase-to-spec/pack.js";
|
|
17
18
|
import { StdoutProgress } from "../../codebase-to-spec/progress.js";
|
|
19
|
+
import { checkForNewerVersion } from "../../codebase-to-spec/version-check.js";
|
|
18
20
|
import { findProjectRoot } from "../../utils/project-settings.js";
|
|
19
21
|
export async function packCommand(rawOptions = {}) {
|
|
20
22
|
const progress = rawOptions.progress ?? new StdoutProgress();
|
|
21
23
|
const projectRoot = rawOptions.projectRoot ?? findProjectRoot(process.cwd()) ?? process.cwd();
|
|
22
24
|
const budgetTokens = rawOptions.budgetTokens ?? DEFAULT_BUDGET_TOKENS;
|
|
25
|
+
// Fail-soft staleness check (CTS-PACK-4), started now so the registry
|
|
26
|
+
// round-trip overlaps the pack work and adds no wall-clock on the happy
|
|
27
|
+
// path. Resolves to undefined on any failure or timeout.
|
|
28
|
+
const versionCheck = (rawOptions.checkVersion ?? checkForNewerVersion)();
|
|
23
29
|
if (rawOptions.fresh) {
|
|
24
30
|
progress.emit({
|
|
25
31
|
stage: "pack",
|
|
@@ -29,6 +35,13 @@ export async function packCommand(rawOptions = {}) {
|
|
|
29
35
|
clearCache(projectRoot);
|
|
30
36
|
}
|
|
31
37
|
const paths = ensureCacheDir(projectRoot);
|
|
38
|
+
// Record the isolation decision once, here, so later stages (present) honor
|
|
39
|
+
// it without the caller — or the orchestrating agent — having to carry it
|
|
40
|
+
// across the run. Written on every pack so a re-pack without the flag clears
|
|
41
|
+
// a stale isolated marker. See CTS-PRESENT-5.2.
|
|
42
|
+
writeRunConfig(projectRoot, {
|
|
43
|
+
isolated: rawOptions.ignoreRequirements === true,
|
|
44
|
+
});
|
|
32
45
|
const scope = rawOptions.scope
|
|
33
46
|
? resolve(projectRoot, rawOptions.scope)
|
|
34
47
|
: undefined;
|
|
@@ -54,6 +67,17 @@ export async function packCommand(rawOptions = {}) {
|
|
|
54
67
|
message: `Packed ${packResult.fileCount} files → ${paths.overview} (compressed), ${paths.source} (uncompressed)`,
|
|
55
68
|
data: { fileCount: packResult.fileCount },
|
|
56
69
|
});
|
|
70
|
+
// Surface a stale install (CTS-PACK-4). Informational only: outputs and
|
|
71
|
+
// exit code are unaffected, and a failed check emits nothing.
|
|
72
|
+
const versionInfo = await versionCheck.catch(() => undefined);
|
|
73
|
+
if (versionInfo?.stale) {
|
|
74
|
+
progress.emit({
|
|
75
|
+
stage: "pack",
|
|
76
|
+
step: "stale-version",
|
|
77
|
+
message: `A newer dotrequirements CLI is available: ${versionInfo.latest} (running ${versionInfo.current}). Re-run skill-install to refresh.`,
|
|
78
|
+
data: { current: versionInfo.current, latest: versionInfo.latest },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
57
81
|
// Budget check (CTS-CLI-3)
|
|
58
82
|
const measured = countTokensInFileExact(paths.overview);
|
|
59
83
|
const budget = checkBudget(measured, budgetTokens);
|
|
@@ -8,13 +8,15 @@
|
|
|
8
8
|
* rather than specFinal.
|
|
9
9
|
*
|
|
10
10
|
* Requirements covered:
|
|
11
|
-
* - CTS-PRESENT-1..
|
|
11
|
+
* - CTS-PRESENT-1..3 (shared present logic in present.ts)
|
|
12
12
|
* - CTSO-INTEG-1: orchestrator uses existing deterministic stages unchanged
|
|
13
13
|
*/
|
|
14
14
|
export interface PresentOrchestratorOptions {
|
|
15
15
|
forceMode?: "interactive" | "non-interactive";
|
|
16
16
|
overwrite?: boolean;
|
|
17
17
|
skipExisting?: boolean;
|
|
18
|
+
/** Override the project root, mainly for tests. */
|
|
19
|
+
projectRoot?: string;
|
|
18
20
|
}
|
|
19
21
|
export declare function presentOrchestratorCommand(options?: PresentOrchestratorOptions): Promise<void>;
|
|
20
22
|
//# sourceMappingURL=present-orchestrator.d.ts.map
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
* rather than specFinal.
|
|
9
9
|
*
|
|
10
10
|
* Requirements covered:
|
|
11
|
-
* - CTS-PRESENT-1..
|
|
11
|
+
* - CTS-PRESENT-1..3 (shared present logic in present.ts)
|
|
12
12
|
* - CTSO-INTEG-1: orchestrator uses existing deterministic stages unchanged
|
|
13
13
|
*/
|
|
14
14
|
import { existsSync, readFileSync } from "node:fs";
|
|
15
|
-
import { cachePaths } from "../../codebase-to-spec/cache.js";
|
|
15
|
+
import { cachePaths, readRunConfig } from "../../codebase-to-spec/cache.js";
|
|
16
16
|
import { ExitCode } from "../../codebase-to-spec/exit-codes.js";
|
|
17
17
|
import { detectMode } from "../../codebase-to-spec/interactive.js";
|
|
18
18
|
import { runPresent, } from "../../codebase-to-spec/present.js";
|
|
@@ -31,7 +31,7 @@ function resolvePolicy(isInteractive, options) {
|
|
|
31
31
|
return "fail-fast";
|
|
32
32
|
}
|
|
33
33
|
export async function presentOrchestratorCommand(options = {}) {
|
|
34
|
-
const projectRoot = findProjectRoot(process.cwd()) ?? process.cwd();
|
|
34
|
+
const projectRoot = options.projectRoot ?? findProjectRoot(process.cwd()) ?? process.cwd();
|
|
35
35
|
const paths = cachePaths(projectRoot);
|
|
36
36
|
if (!existsSync(paths.outline)) {
|
|
37
37
|
process.stderr.write(`No outline.yaml found at ${paths.outline}. Run the orchestrator's planner first.\n`);
|
|
@@ -60,11 +60,17 @@ export async function presentOrchestratorCommand(options = {}) {
|
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
62
|
const legacyOutline = conversationalOutlineToLegacy(outline);
|
|
63
|
+
// Honor the isolation decision recorded at pack time (CTS-PRESENT-5.2). When
|
|
64
|
+
// the run is isolated, write under `.requirements/cts/` so the existing
|
|
65
|
+
// requirements directory is never overwritten — the choice rides the cache,
|
|
66
|
+
// not the agent's memory, so it holds even across a resumed or compacted run.
|
|
67
|
+
const { isolated } = readRunConfig(projectRoot);
|
|
63
68
|
const result = await runPresent({
|
|
64
69
|
outline: legacyOutline,
|
|
65
70
|
finalSpecPath: paths.composedSpec,
|
|
66
71
|
projectRoot,
|
|
67
72
|
overwritePolicy: policy,
|
|
73
|
+
outputSubdir: isolated ? "cts" : undefined,
|
|
68
74
|
});
|
|
69
75
|
if (!result.allWritten) {
|
|
70
76
|
if (result.conflictPath) {
|
|
@@ -76,6 +82,7 @@ export async function presentOrchestratorCommand(options = {}) {
|
|
|
76
82
|
for (const action of result.actions) {
|
|
77
83
|
process.stdout.write(` ${action.action}: ${action.path}\n`);
|
|
78
84
|
}
|
|
79
|
-
|
|
85
|
+
const outputDirLabel = isolated ? ".requirements/cts/" : ".requirements/";
|
|
86
|
+
process.stdout.write(`Wrote ${result.actions.filter((a) => a.action === "created" || a.action === "overwrote").length} file(s) to ${outputDirLabel}\n`);
|
|
80
87
|
}
|
|
81
88
|
//# sourceMappingURL=present-orchestrator.js.map
|
package/dist/commands/init.js
CHANGED
|
@@ -353,8 +353,13 @@ async function inviteFlow(cwd, token) {
|
|
|
353
353
|
console.log(`✓ Joined team "${inviteInfo.teamName}"`);
|
|
354
354
|
}
|
|
355
355
|
catch (err) {
|
|
356
|
+
// Production redacts ConvexError messages to "Server Error" but keeps
|
|
357
|
+
// error.data — check both, or this graceful path only works in dev
|
|
358
|
+
const data = err.data;
|
|
359
|
+
const dataMessage = typeof data?.message === "string" ? data.message : "";
|
|
356
360
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
357
|
-
if (
|
|
361
|
+
if (dataMessage.includes("already a member") ||
|
|
362
|
+
errorMessage.includes("already a member")) {
|
|
358
363
|
console.log(`Already a member of "${inviteInfo.teamName}"`);
|
|
359
364
|
}
|
|
360
365
|
else {
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure decision logic for `dotrequirements link`'s non-interactive mode
|
|
3
|
+
* (LINK-10/11/12). No I/O: the link command gathers state (teams, projects)
|
|
4
|
+
* and these functions decide what to do — use a project, create one, rename,
|
|
5
|
+
* or stop with a decision-needed result that carries everything an AI
|
|
6
|
+
* assistant needs to ask the user and retry with a deviation flag.
|
|
7
|
+
*/
|
|
8
|
+
export interface TeamOption {
|
|
9
|
+
teamId: string;
|
|
10
|
+
name: string;
|
|
11
|
+
tier: string;
|
|
12
|
+
tierName: string;
|
|
13
|
+
projectCount: number;
|
|
14
|
+
projectLimit: number;
|
|
15
|
+
}
|
|
16
|
+
export interface ProjectOption {
|
|
17
|
+
projectId: string;
|
|
18
|
+
projectName: string;
|
|
19
|
+
projectSlug: string | null;
|
|
20
|
+
isDefault: boolean;
|
|
21
|
+
hasUserContent: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface LinkFlags {
|
|
24
|
+
yes?: boolean;
|
|
25
|
+
team?: string;
|
|
26
|
+
connect?: string;
|
|
27
|
+
create?: boolean;
|
|
28
|
+
name?: string;
|
|
29
|
+
json?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* LINK-12.3: any deviation flag implies non-interactive mode.
|
|
33
|
+
*/
|
|
34
|
+
export declare function isNonInteractive(flags: LinkFlags): boolean;
|
|
35
|
+
export declare function hasCapacity(team: TeamOption): boolean;
|
|
36
|
+
/** A choice the user must make, with everything needed to ask and retry. */
|
|
37
|
+
export interface DecisionNeeded {
|
|
38
|
+
kind: "decision";
|
|
39
|
+
decision: "team" | "project" | "capacity";
|
|
40
|
+
message: string;
|
|
41
|
+
options: Array<{
|
|
42
|
+
label: string;
|
|
43
|
+
/** Exact flag to pass on retry (LINK-11.4), when retryable via link */
|
|
44
|
+
retryFlag?: string;
|
|
45
|
+
}>;
|
|
46
|
+
}
|
|
47
|
+
export interface NoMatch {
|
|
48
|
+
kind: "no-match";
|
|
49
|
+
flag: "team" | "connect";
|
|
50
|
+
provided: string;
|
|
51
|
+
alternatives: string[];
|
|
52
|
+
}
|
|
53
|
+
export type TeamResolution = {
|
|
54
|
+
kind: "use";
|
|
55
|
+
team: TeamOption;
|
|
56
|
+
} | DecisionNeeded | NoMatch;
|
|
57
|
+
/**
|
|
58
|
+
* LINK-10.1, LINK-11.0, LINK-12.0, LINK-12.4
|
|
59
|
+
*/
|
|
60
|
+
export declare function resolveTeam(teams: TeamOption[], flags: LinkFlags): TeamResolution;
|
|
61
|
+
export type ProjectResolution = {
|
|
62
|
+
kind: "connect";
|
|
63
|
+
project: ProjectOption;
|
|
64
|
+
rename: boolean;
|
|
65
|
+
} | {
|
|
66
|
+
kind: "create";
|
|
67
|
+
} | DecisionNeeded | NoMatch;
|
|
68
|
+
/**
|
|
69
|
+
* LINK-10.2/10.3 (default-project rule), LINK-11.1/11.2 (decision menus),
|
|
70
|
+
* LINK-12.1/12.2/12.4 (deviation flags).
|
|
71
|
+
*/
|
|
72
|
+
export declare function resolveProject(projects: ProjectOption[], team: TeamOption, flags: LinkFlags): ProjectResolution;
|
|
73
|
+
/**
|
|
74
|
+
* Prompt-free project naming for non-interactive mode (LINK-9.0): explicit
|
|
75
|
+
* flag, then package metadata, then directory name, then a fixed fallback —
|
|
76
|
+
* never a prompt.
|
|
77
|
+
*/
|
|
78
|
+
export declare function resolveProjectName(flags: LinkFlags, packageName: string | undefined, directoryName: string | undefined): string;
|
|
79
|
+
//# sourceMappingURL=link-resolution.d.ts.map
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure decision logic for `dotrequirements link`'s non-interactive mode
|
|
3
|
+
* (LINK-10/11/12). No I/O: the link command gathers state (teams, projects)
|
|
4
|
+
* and these functions decide what to do — use a project, create one, rename,
|
|
5
|
+
* or stop with a decision-needed result that carries everything an AI
|
|
6
|
+
* assistant needs to ask the user and retry with a deviation flag.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* LINK-12.3: any deviation flag implies non-interactive mode.
|
|
10
|
+
*/
|
|
11
|
+
export function isNonInteractive(flags) {
|
|
12
|
+
return Boolean(flags.yes || flags.team || flags.connect || flags.create);
|
|
13
|
+
}
|
|
14
|
+
export function hasCapacity(team) {
|
|
15
|
+
return team.projectLimit === -1 || team.projectCount < team.projectLimit;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* LINK-10.1, LINK-11.0, LINK-12.0, LINK-12.4
|
|
19
|
+
*/
|
|
20
|
+
export function resolveTeam(teams, flags) {
|
|
21
|
+
if (flags.team) {
|
|
22
|
+
const wanted = flags.team.toLowerCase();
|
|
23
|
+
const match = teams.find((t) => t.teamId === flags.team || t.name.toLowerCase() === wanted);
|
|
24
|
+
if (match)
|
|
25
|
+
return { kind: "use", team: match };
|
|
26
|
+
return {
|
|
27
|
+
kind: "no-match",
|
|
28
|
+
flag: "team",
|
|
29
|
+
provided: flags.team,
|
|
30
|
+
alternatives: teams.map((t) => `${t.name} (${t.teamId})`),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (teams.length === 1) {
|
|
34
|
+
return { kind: "use", team: teams[0] };
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
kind: "decision",
|
|
38
|
+
decision: "team",
|
|
39
|
+
message: "You belong to multiple teams — which should this project join?",
|
|
40
|
+
options: teams.map((t) => ({
|
|
41
|
+
label: `${t.name} (${t.tierName}, ${formatCapacity(t)})`,
|
|
42
|
+
retryFlag: `--team ${t.teamId}`,
|
|
43
|
+
})),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* LINK-10.2/10.3 (default-project rule), LINK-11.1/11.2 (decision menus),
|
|
48
|
+
* LINK-12.1/12.2/12.4 (deviation flags).
|
|
49
|
+
*/
|
|
50
|
+
export function resolveProject(projects, team, flags) {
|
|
51
|
+
if (flags.connect) {
|
|
52
|
+
const match = projects.find((p) => p.projectSlug === flags.connect);
|
|
53
|
+
if (match)
|
|
54
|
+
return { kind: "connect", project: match, rename: false };
|
|
55
|
+
return {
|
|
56
|
+
kind: "no-match",
|
|
57
|
+
flag: "connect",
|
|
58
|
+
provided: flags.connect,
|
|
59
|
+
alternatives: projects
|
|
60
|
+
.filter((p) => p.projectSlug)
|
|
61
|
+
.map((p) => `${p.projectName} (${p.projectSlug})`),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
if (flags.create) {
|
|
65
|
+
if (hasCapacity(team))
|
|
66
|
+
return { kind: "create" };
|
|
67
|
+
return atCapacityDecision(projects, team);
|
|
68
|
+
}
|
|
69
|
+
if (projects.length === 0) {
|
|
70
|
+
// A decision with a single possible answer is not a decision: create.
|
|
71
|
+
if (hasCapacity(team))
|
|
72
|
+
return { kind: "create" };
|
|
73
|
+
return atCapacityDecision(projects, team);
|
|
74
|
+
}
|
|
75
|
+
if (projects.length === 1 && projects[0].isDefault) {
|
|
76
|
+
// LINK-10.2: rename the untouched default project to match this repo;
|
|
77
|
+
// LINK-10.3: if the user has put content in it, connect without renaming
|
|
78
|
+
return {
|
|
79
|
+
kind: "connect",
|
|
80
|
+
project: projects[0],
|
|
81
|
+
rename: !projects[0].hasUserContent,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// LINK-11.1: existing projects the user chose to create — their call
|
|
85
|
+
return {
|
|
86
|
+
kind: "decision",
|
|
87
|
+
decision: "project",
|
|
88
|
+
message: "This team already has projects — connect to one, or create a new one?",
|
|
89
|
+
options: [
|
|
90
|
+
...projects.map((p) => ({
|
|
91
|
+
label: `${p.projectName}${p.hasUserContent ? "" : " (empty)"}`,
|
|
92
|
+
retryFlag: p.projectSlug ? `--connect ${p.projectSlug}` : undefined,
|
|
93
|
+
})),
|
|
94
|
+
...(hasCapacity(team)
|
|
95
|
+
? [{ label: "Create a new project", retryFlag: "--create" }]
|
|
96
|
+
: []),
|
|
97
|
+
],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* LINK-11.2: creating is required (or requested) but the team is at its
|
|
102
|
+
* project limit. None of the remedies is a plain link retry flag.
|
|
103
|
+
*/
|
|
104
|
+
function atCapacityDecision(projects, team) {
|
|
105
|
+
return {
|
|
106
|
+
kind: "decision",
|
|
107
|
+
decision: "capacity",
|
|
108
|
+
message: `Team "${team.name}" is at its ${team.tierName} project limit (${formatCapacity(team)}).`,
|
|
109
|
+
options: [
|
|
110
|
+
...projects
|
|
111
|
+
.filter((p) => p.projectSlug)
|
|
112
|
+
.map((p) => ({
|
|
113
|
+
label: `Connect to existing project ${p.projectName}`,
|
|
114
|
+
retryFlag: `--connect ${p.projectSlug}`,
|
|
115
|
+
})),
|
|
116
|
+
{ label: "Delete a project you no longer need (in the web app)" },
|
|
117
|
+
{ label: "Upgrade the team for more projects" },
|
|
118
|
+
],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function formatCapacity(team) {
|
|
122
|
+
return team.projectLimit === -1
|
|
123
|
+
? `${team.projectCount}/unlimited projects`
|
|
124
|
+
: `${team.projectCount}/${team.projectLimit} projects`;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Prompt-free project naming for non-interactive mode (LINK-9.0): explicit
|
|
128
|
+
* flag, then package metadata, then directory name, then a fixed fallback —
|
|
129
|
+
* never a prompt.
|
|
130
|
+
*/
|
|
131
|
+
export function resolveProjectName(flags, packageName, directoryName) {
|
|
132
|
+
if (flags.name)
|
|
133
|
+
return flags.name;
|
|
134
|
+
if (packageName)
|
|
135
|
+
return packageName;
|
|
136
|
+
if (directoryName && directoryName !== "/" && directoryName !== ".") {
|
|
137
|
+
return directoryName;
|
|
138
|
+
}
|
|
139
|
+
return "my-project";
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=link-resolution.js.map
|
package/dist/commands/link.d.ts
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
|
+
import { type LinkFlags } from "./link-resolution.js";
|
|
2
|
+
export interface LinkCommandOptions extends LinkFlags {
|
|
3
|
+
}
|
|
1
4
|
/**
|
|
2
|
-
* Link command - connect or reconnect local project to cloud
|
|
5
|
+
* Link command - connect or reconnect local project to cloud, creating the
|
|
6
|
+
* cloud project when needed. Link owns the "upgrade local to cloud"
|
|
7
|
+
* transition.
|
|
3
8
|
*
|
|
4
9
|
* Requirements:
|
|
5
10
|
* - LINK-1: Requires existing .requirements/ folder
|
|
6
11
|
* - LINK-2: Offers refresh when credentials exist
|
|
7
12
|
* - LINK-3: Uses inline OAuth (no persistent tokens)
|
|
8
|
-
* - LINK-4, LINK-5: Team and project selection
|
|
13
|
+
* - LINK-4, LINK-5: Team and project selection (including the create option)
|
|
9
14
|
* - LINK-6: Saves credentials to project-settings.json
|
|
10
|
-
* - LINK-7: Optionally pulls requirements
|
|
15
|
+
* - LINK-7: Optionally pulls requirements (interactive only)
|
|
11
16
|
* - LINK-8: Handles network errors gracefully
|
|
17
|
+
* - LINK-9: Creates the cloud project when chosen
|
|
18
|
+
* - LINK-10/11/12: Non-interactive mode for AI-driven setup
|
|
19
|
+
* - LINK-13: Non-interactive success includes share artifacts (team-invite
|
|
20
|
+
* URL + read-only pull command) so one "yes" puts the spec in front of
|
|
21
|
+
* the team
|
|
12
22
|
*/
|
|
13
|
-
export declare function linkCommand(): Promise<void>;
|
|
23
|
+
export declare function linkCommand(options?: LinkCommandOptions): Promise<void>;
|
|
14
24
|
//# sourceMappingURL=link.d.ts.map
|