@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.
Files changed (103) hide show
  1. package/dist/cli.js +8 -1
  2. package/dist/codebase-to-spec/area-name.d.ts +13 -0
  3. package/dist/codebase-to-spec/area-name.js +18 -0
  4. package/dist/codebase-to-spec/cache.d.ts +31 -0
  5. package/dist/codebase-to-spec/cache.js +29 -1
  6. package/dist/codebase-to-spec/compose.d.ts +7 -4
  7. package/dist/codebase-to-spec/compose.js +10 -21
  8. package/dist/codebase-to-spec/dispatch.js +1 -1
  9. package/dist/codebase-to-spec/present.d.ts +9 -0
  10. package/dist/codebase-to-spec/present.js +30 -4
  11. package/dist/codebase-to-spec/prompts/planner-initial.d.ts +3 -2
  12. package/dist/codebase-to-spec/prompts/planner-initial.js +3 -2
  13. package/dist/codebase-to-spec/renumber.d.ts +52 -0
  14. package/dist/codebase-to-spec/renumber.js +105 -0
  15. package/dist/codebase-to-spec/schemas.d.ts +35 -240
  16. package/dist/codebase-to-spec/schemas.js +5 -173
  17. package/dist/codebase-to-spec/skill-install.d.ts +16 -6
  18. package/dist/codebase-to-spec/skill-install.js +109 -47
  19. package/dist/codebase-to-spec/version-check.d.ts +31 -0
  20. package/dist/codebase-to-spec/version-check.js +56 -0
  21. package/dist/commands/ai-setup.d.ts +12 -1
  22. package/dist/commands/ai-setup.js +65 -33
  23. package/dist/commands/codebase-to-spec/dispatch-editor.js +1 -1
  24. package/dist/commands/codebase-to-spec/dispatch-spec.js +1 -1
  25. package/dist/commands/codebase-to-spec/index.js +7 -103
  26. package/dist/commands/codebase-to-spec/pack.d.ts +4 -0
  27. package/dist/commands/codebase-to-spec/pack.js +25 -1
  28. package/dist/commands/codebase-to-spec/present-orchestrator.d.ts +3 -1
  29. package/dist/commands/codebase-to-spec/present-orchestrator.js +11 -4
  30. package/dist/commands/init.js +6 -1
  31. package/dist/commands/link-resolution.d.ts +79 -0
  32. package/dist/commands/link-resolution.js +141 -0
  33. package/dist/commands/link.d.ts +14 -4
  34. package/dist/commands/link.js +369 -16
  35. package/dist/commands/pull.js +19 -2
  36. package/dist/commands/push.js +36 -2
  37. package/dist/convex.d.ts +5 -3
  38. package/dist/convex.js +5 -3
  39. package/dist/harness/cache.d.ts +0 -14
  40. package/dist/harness/cache.js +1 -41
  41. package/dist/harness/finalize.js +2 -2
  42. package/dist/harness/prepare.js +1 -3
  43. package/dist/harness/requirementsLoader.d.ts +3 -3
  44. package/dist/harness/requirementsLoader.js +13 -8
  45. package/dist/mcp/handlers/authoring.d.ts +5 -5
  46. package/dist/mcp/handlers/authoring.js +9 -9
  47. package/dist/mcp/handlers/push.d.ts +2 -2
  48. package/dist/mcp/handlers/push.js +36 -3
  49. package/dist/mcp/handlers/review.d.ts +4 -4
  50. package/dist/mcp/handlers/review.js +4 -4
  51. package/dist/mcp/handlers/search.d.ts +1 -1
  52. package/dist/mcp/handlers/search.js +1 -1
  53. package/dist/mcp/index.js +29 -0
  54. package/dist/push/core.d.ts +18 -0
  55. package/dist/push/core.js +70 -3
  56. package/dist/push/index.d.ts +1 -1
  57. package/dist/push/index.js +1 -1
  58. package/dist/schema/parser-core.js +5 -1
  59. package/dist/schema/parser.js +5 -1
  60. package/dist/schema/run-marker.d.ts +38 -0
  61. package/dist/schema/run-marker.js +138 -0
  62. package/dist/schema/schemas.d.ts +12 -0
  63. package/dist/schema/schemas.js +1 -0
  64. package/dist/templates/agents/cts-worker.md +1 -1
  65. package/dist/templates/skills/codebase-to-spec/SKILL.md +39 -11
  66. package/dist/templates/workflows/specify-codebase.js +4 -2
  67. package/dist/utils/own-package.d.ts +10 -0
  68. package/dist/utils/own-package.js +13 -0
  69. package/dist/utils/project-selector.d.ts +5 -0
  70. package/dist/utils/project-selector.js +4 -0
  71. package/package.json +2 -2
  72. package/dist/codebase-to-spec/edit-loop.d.ts +0 -54
  73. package/dist/codebase-to-spec/edit-loop.js +0 -195
  74. package/dist/codebase-to-spec/editor.d.ts +0 -54
  75. package/dist/codebase-to-spec/editor.js +0 -74
  76. package/dist/codebase-to-spec/fan-out.d.ts +0 -63
  77. package/dist/codebase-to-spec/fan-out.js +0 -215
  78. package/dist/codebase-to-spec/outline-review-loop.d.ts +0 -51
  79. package/dist/codebase-to-spec/outline-review-loop.js +0 -187
  80. package/dist/codebase-to-spec/planner.d.ts +0 -41
  81. package/dist/codebase-to-spec/planner.js +0 -76
  82. package/dist/codebase-to-spec/prompts/outline-reviewer.d.ts +0 -12
  83. package/dist/codebase-to-spec/prompts/outline-reviewer.js +0 -89
  84. package/dist/codebase-to-spec/slice.d.ts +0 -49
  85. package/dist/codebase-to-spec/slice.js +0 -111
  86. package/dist/codebase-to-spec/specifier.d.ts +0 -60
  87. package/dist/codebase-to-spec/specifier.js +0 -85
  88. package/dist/codebase-to-spec/summary.d.ts +0 -51
  89. package/dist/codebase-to-spec/summary.js +0 -183
  90. package/dist/commands/codebase-to-spec/compose.d.ts +0 -14
  91. package/dist/commands/codebase-to-spec/compose.js +0 -57
  92. package/dist/commands/codebase-to-spec/edit-loop.d.ts +0 -16
  93. package/dist/commands/codebase-to-spec/edit-loop.js +0 -83
  94. package/dist/commands/codebase-to-spec/fan-out.d.ts +0 -19
  95. package/dist/commands/codebase-to-spec/fan-out.js +0 -77
  96. package/dist/commands/codebase-to-spec/plan-loop.d.ts +0 -26
  97. package/dist/commands/codebase-to-spec/plan-loop.js +0 -105
  98. package/dist/commands/codebase-to-spec/present.d.ts +0 -26
  99. package/dist/commands/codebase-to-spec/present.js +0 -97
  100. package/dist/commands/codebase-to-spec/run.d.ts +0 -20
  101. package/dist/commands/codebase-to-spec/run.js +0 -86
  102. package/dist/commands/codebase-to-spec/specify-area.d.ts +0 -18
  103. package/dist/commands/codebase-to-spec/specify-area.js +0 -82
@@ -59,17 +59,27 @@ export interface SkillInstallResult {
59
59
  */
60
60
  export declare function resolveSkillDir(options: SkillInstallOptions): string;
61
61
  /**
62
- * Load the bundled SKILL.md template from the package.
62
+ * Resolve the CLI invocation stamped into the installed artifacts
63
+ * (CTSO-INSTALL-5): a version-pinned `npx` invocation of this very package,
64
+ * so the installed bundle runs without `dotrequirements` on PATH and always
65
+ * shells out to the same CLI version that performed the install.
63
66
  */
64
- export declare function loadSkillTemplate(): string;
67
+ export declare function resolveCliInvocation(): string;
65
68
  /**
66
- * Load the bundled cts-worker agent definition template.
69
+ * Load the bundled SKILL.md template, rendering `{{DOTREQ_CLI}}` to the given
70
+ * CLI invocation (defaults to the version-pinned npx form).
67
71
  */
68
- export declare function loadAgentTemplate(): string;
72
+ export declare function loadSkillTemplate(cliInvocation?: string): string;
69
73
  /**
70
- * Load the bundled specify-codebase dynamic-workflow script template.
74
+ * Load the bundled cts-worker agent definition template, rendering
75
+ * `{{DOTREQ_CLI}}` to the given CLI invocation.
71
76
  */
72
- export declare function loadWorkflowTemplate(): string;
77
+ export declare function loadAgentTemplate(cliInvocation?: string): string;
78
+ /**
79
+ * Load the bundled specify-codebase dynamic-workflow script template,
80
+ * rendering `{{DOTREQ_CLI}}` to the given CLI invocation.
81
+ */
82
+ export declare function loadWorkflowTemplate(cliInvocation?: string): string;
73
83
  /**
74
84
  * Install (or refuse to overwrite) the codebase-to-spec skill bundle.
75
85
  *
@@ -17,9 +17,11 @@
17
17
  * - CTS-SKILL-1, CTS-SKILL-5
18
18
  * - CTSO-INSTALL-1 (bundles the skill, the cts-worker agent, and the workflow)
19
19
  */
20
+ import { createHash } from "node:crypto";
20
21
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
21
22
  import { homedir } from "node:os";
22
23
  import { dirname, join, resolve } from "node:path";
24
+ import { getOwnPackage } from "../utils/own-package.js";
23
25
  import { loadTemplate } from "../utils/templates.js";
24
26
  /**
25
27
  * Resolve the directory in which the skill should live, given the user's
@@ -45,39 +47,76 @@ export function resolveSkillDir(options) {
45
47
  }
46
48
  }
47
49
  /**
48
- * Load the bundled SKILL.md template from the package.
50
+ * Resolve the CLI invocation stamped into the installed artifacts
51
+ * (CTSO-INSTALL-5): a version-pinned `npx` invocation of this very package,
52
+ * so the installed bundle runs without `dotrequirements` on PATH and always
53
+ * shells out to the same CLI version that performed the install.
49
54
  */
50
- export function loadSkillTemplate() {
51
- return loadTemplate("skills/codebase-to-spec/SKILL.md");
55
+ export function resolveCliInvocation() {
56
+ const pkg = getOwnPackage();
57
+ return `npx -y ${pkg.name}@${pkg.version}`;
52
58
  }
53
59
  /**
54
- * Load the bundled cts-worker agent definition template.
60
+ * Load the bundled SKILL.md template, rendering `{{DOTREQ_CLI}}` to the given
61
+ * CLI invocation (defaults to the version-pinned npx form).
55
62
  */
56
- export function loadAgentTemplate() {
57
- return loadTemplate("agents/cts-worker.md");
63
+ export function loadSkillTemplate(cliInvocation = resolveCliInvocation()) {
64
+ return loadTemplate("skills/codebase-to-spec/SKILL.md", {
65
+ DOTREQ_CLI: cliInvocation,
66
+ });
58
67
  }
59
68
  /**
60
- * Load the bundled specify-codebase dynamic-workflow script template.
69
+ * Load the bundled cts-worker agent definition template, rendering
70
+ * `{{DOTREQ_CLI}}` to the given CLI invocation.
61
71
  */
62
- export function loadWorkflowTemplate() {
63
- return loadTemplate("workflows/specify-codebase.js");
72
+ export function loadAgentTemplate(cliInvocation = resolveCliInvocation()) {
73
+ return loadTemplate("agents/cts-worker.md", { DOTREQ_CLI: cliInvocation });
64
74
  }
65
75
  /**
66
- * Install (or refuse to overwrite) one template file at a target path.
67
- * Returns true if a write happened.
76
+ * Load the bundled specify-codebase dynamic-workflow script template,
77
+ * rendering `{{DOTREQ_CLI}}` to the given CLI invocation.
68
78
  */
69
- function installTemplateFile(templateContent, targetPath, overwrite, description) {
70
- const exists = existsSync(targetPath);
71
- if (exists && !overwrite) {
72
- const onDisk = readFileSync(targetPath, "utf-8");
73
- if (onDisk === templateContent) {
74
- return { installed: false, overwrote: false };
75
- }
76
- throw new Error(`${description} already exists at ${targetPath} and differs from the bundled template. Pass --overwrite to replace it, or delete the file first.`);
79
+ export function loadWorkflowTemplate(cliInvocation = resolveCliInvocation()) {
80
+ return loadTemplate("workflows/specify-codebase.js", {
81
+ DOTREQ_CLI: cliInvocation,
82
+ });
83
+ }
84
+ /**
85
+ * Manifest written next to the installed SKILL.md, recording the content hash
86
+ * of every file the install wrote. On a later re-run, an on-disk file whose
87
+ * hash still matches its manifest entry is a *stock* install (possibly from an
88
+ * older CLI version) and is safe to refresh without --overwrite; a mismatch
89
+ * means the user hand-edited it (CTSO-INSTALL-6).
90
+ */
91
+ const MANIFEST_FILENAME = ".install-manifest.json";
92
+ function sha256(content) {
93
+ return createHash("sha256").update(content, "utf-8").digest("hex");
94
+ }
95
+ function readManifest(skillDir) {
96
+ const manifestPath = join(skillDir, MANIFEST_FILENAME);
97
+ if (!existsSync(manifestPath))
98
+ return undefined;
99
+ try {
100
+ return JSON.parse(readFileSync(manifestPath, "utf-8"));
101
+ }
102
+ catch {
103
+ // An unreadable manifest gives no provenance; fall back to treating
104
+ // differing files as hand-edited (the safe direction).
105
+ return undefined;
106
+ }
107
+ }
108
+ function classifyPiece(key, description, targetPath, content, manifest) {
109
+ if (!existsSync(targetPath)) {
110
+ return { key, description, targetPath, content, state: "missing" };
77
111
  }
78
- mkdirSync(dirname(targetPath), { recursive: true });
79
- writeFileSync(targetPath, templateContent, "utf-8");
80
- return { installed: true, overwrote: exists };
112
+ const onDisk = readFileSync(targetPath, "utf-8");
113
+ if (onDisk === content) {
114
+ return { key, description, targetPath, content, state: "current" };
115
+ }
116
+ if (manifest?.files[key] === sha256(onDisk)) {
117
+ return { key, description, targetPath, content, state: "stock" };
118
+ }
119
+ return { key, description, targetPath, content, state: "modified" };
81
120
  }
82
121
  /**
83
122
  * Install (or refuse to overwrite) the codebase-to-spec skill bundle.
@@ -92,37 +131,60 @@ function installTemplateFile(templateContent, targetPath, overwrite, description
92
131
  export function installSkill(options = {}) {
93
132
  const skillDir = resolveSkillDir(options);
94
133
  const skillPath = join(skillDir, "SKILL.md");
95
- const skillTemplate = loadSkillTemplate();
96
- const skillResult = installTemplateFile(skillTemplate, skillPath, options.overwrite === true, "SKILL.md");
134
+ const scope = options.scope ?? "project";
135
+ const cliInvocation = resolveCliInvocation();
136
+ const manifest = readManifest(skillDir);
137
+ // Classify every piece before writing anything, so a hand-edit refusal
138
+ // never leaves a partially refreshed bundle (CTSO-INSTALL-6).
139
+ const pieces = [
140
+ classifyPiece("skill", "SKILL.md", skillPath, loadSkillTemplate(cliInvocation), manifest),
141
+ ];
97
142
  // A custom target dir is an arbitrary location CC won't read agents or
98
143
  // workflows from, so it gets the skill body only. Project and global both
99
144
  // install the full bundle under their `.claude/` base.
100
- const scope = options.scope ?? "project";
101
- if (scope === "custom") {
102
- return {
103
- installedPath: skillPath,
104
- installed: skillResult.installed,
105
- overwrote: skillResult.overwrote,
106
- };
145
+ let companions;
146
+ if (scope !== "custom") {
147
+ const claudeBase = scope === "global"
148
+ ? join(options.globalRoot ?? homedir(), ".claude")
149
+ : join(options.projectRoot ?? process.cwd(), ".claude");
150
+ const agentPath = join(claudeBase, "agents", "cts-worker.md");
151
+ const workflowPath = join(claudeBase, "workflows", "specify-codebase.js");
152
+ pieces.push(classifyPiece("agent", "cts-worker agent definition", agentPath, loadAgentTemplate(cliInvocation), manifest),
153
+ // The workflow script is discovered by name from .claude/workflows/;
154
+ // no settings registration needed, unlike a hook.
155
+ classifyPiece("workflow", "specify-codebase workflow script", workflowPath, loadWorkflowTemplate(cliInvocation), manifest));
156
+ companions = { agentPath, workflowPath };
107
157
  }
108
- const claudeBase = scope === "global"
109
- ? join(options.globalRoot ?? homedir(), ".claude")
110
- : join(options.projectRoot ?? process.cwd(), ".claude");
111
- const agentPath = join(claudeBase, "agents", "cts-worker.md");
112
- const workflowPath = join(claudeBase, "workflows", "specify-codebase.js");
113
- // Install the cts-worker agent definition.
114
- installTemplateFile(loadAgentTemplate(), agentPath, options.overwrite === true, "cts-worker agent definition");
115
- // Install the specify-codebase workflow script (discovered by name from
116
- // .claude/workflows/; no settings registration needed, unlike a hook).
117
- installTemplateFile(loadWorkflowTemplate(), workflowPath, options.overwrite === true, "specify-codebase workflow script");
158
+ // Hand-edited files block the whole refresh (named individually) unless
159
+ // --overwrite; unmodified-stock files from any prior version refresh freely.
160
+ if (options.overwrite !== true) {
161
+ const modified = pieces.filter((p) => p.state === "modified");
162
+ if (modified.length > 0) {
163
+ const lines = modified.map((p) => ` ${p.description}: ${p.targetPath}`);
164
+ throw new Error(`Refusing to replace file(s) modified since install (or installed without manifest tracking):\n${lines.join("\n")}\nPass --overwrite to replace, or delete the file(s) first.`);
165
+ }
166
+ }
167
+ const skillPiece = pieces[0];
168
+ for (const piece of pieces) {
169
+ if (piece.state === "current")
170
+ continue;
171
+ mkdirSync(dirname(piece.targetPath), { recursive: true });
172
+ writeFileSync(piece.targetPath, piece.content, "utf-8");
173
+ }
174
+ // Record what this install wrote (or confirmed current) so the next re-run
175
+ // can tell stock content from hand-edits. Also adopts manifest-less installs
176
+ // whose content matches the current templates.
177
+ const manifestOut = {
178
+ version: getOwnPackage().version,
179
+ files: Object.fromEntries(pieces.map((p) => [p.key, sha256(p.content)])),
180
+ };
181
+ mkdirSync(skillDir, { recursive: true });
182
+ writeFileSync(join(skillDir, MANIFEST_FILENAME), `${JSON.stringify(manifestOut, null, 2)}\n`, "utf-8");
118
183
  return {
119
184
  installedPath: skillPath,
120
- installed: skillResult.installed,
121
- overwrote: skillResult.overwrote,
122
- companions: {
123
- agentPath,
124
- workflowPath,
125
- },
185
+ installed: skillPiece.state !== "current",
186
+ overwrote: skillPiece.state === "stock" || skillPiece.state === "modified",
187
+ companions,
126
188
  };
127
189
  }
128
190
  //# sourceMappingURL=skill-install.js.map
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Fail-soft check for a newer published CLI version (CTS-PACK-4).
3
+ *
4
+ * The skill bundle pins the CLI version it was installed with
5
+ * (CTSO-INSTALL-5), so a long-lived install would otherwise run stale
6
+ * forever without noticing. Pack — the first deterministic step of every
7
+ * run — surfaces staleness so the skill can offer a refresh (CTSO-CONV-7).
8
+ *
9
+ * Any failure (offline, slow or unreachable registry, malformed response)
10
+ * yields `undefined`: the check must never fail or delay the pack beyond a
11
+ * short timeout.
12
+ */
13
+ export interface VersionCheckResult {
14
+ current: string;
15
+ latest: string;
16
+ stale: boolean;
17
+ }
18
+ export declare const DEFAULT_VERSION_CHECK_TIMEOUT_MS = 2000;
19
+ /**
20
+ * True when `latest` is a strictly newer release than `current`, comparing
21
+ * numeric segments. Prerelease suffixes are ignored; when the numeric
22
+ * segments tie, the versions are treated as equal (not stale).
23
+ */
24
+ export declare function isNewerVersion(latest: string, current: string): boolean;
25
+ export interface VersionCheckOptions {
26
+ timeoutMs?: number;
27
+ /** Injectable for tests. */
28
+ fetchImpl?: typeof fetch;
29
+ }
30
+ export declare function checkForNewerVersion(options?: VersionCheckOptions): Promise<VersionCheckResult | undefined>;
31
+ //# sourceMappingURL=version-check.d.ts.map
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Fail-soft check for a newer published CLI version (CTS-PACK-4).
3
+ *
4
+ * The skill bundle pins the CLI version it was installed with
5
+ * (CTSO-INSTALL-5), so a long-lived install would otherwise run stale
6
+ * forever without noticing. Pack — the first deterministic step of every
7
+ * run — surfaces staleness so the skill can offer a refresh (CTSO-CONV-7).
8
+ *
9
+ * Any failure (offline, slow or unreachable registry, malformed response)
10
+ * yields `undefined`: the check must never fail or delay the pack beyond a
11
+ * short timeout.
12
+ */
13
+ import { getOwnPackage } from "../utils/own-package.js";
14
+ export const DEFAULT_VERSION_CHECK_TIMEOUT_MS = 2_000;
15
+ /**
16
+ * True when `latest` is a strictly newer release than `current`, comparing
17
+ * numeric segments. Prerelease suffixes are ignored; when the numeric
18
+ * segments tie, the versions are treated as equal (not stale).
19
+ */
20
+ export function isNewerVersion(latest, current) {
21
+ const parse = (v) => v
22
+ .split("-")[0]
23
+ .split(".")
24
+ .map((seg) => Number.parseInt(seg, 10) || 0);
25
+ const a = parse(latest);
26
+ const b = parse(current);
27
+ for (let i = 0; i < Math.max(a.length, b.length); i++) {
28
+ const diff = (a[i] ?? 0) - (b[i] ?? 0);
29
+ if (diff !== 0)
30
+ return diff > 0;
31
+ }
32
+ return false;
33
+ }
34
+ export async function checkForNewerVersion(options = {}) {
35
+ try {
36
+ const { name, version } = getOwnPackage();
37
+ const fetchImpl = options.fetchImpl ?? fetch;
38
+ const response = await fetchImpl(`https://registry.npmjs.org/${encodeURIComponent(name)}/latest`, {
39
+ signal: AbortSignal.timeout(options.timeoutMs ?? DEFAULT_VERSION_CHECK_TIMEOUT_MS),
40
+ });
41
+ if (!response.ok)
42
+ return undefined;
43
+ const body = (await response.json());
44
+ if (typeof body.version !== "string")
45
+ return undefined;
46
+ return {
47
+ current: version,
48
+ latest: body.version,
49
+ stale: isNewerVersion(body.version, version),
50
+ };
51
+ }
52
+ catch {
53
+ return undefined;
54
+ }
55
+ }
56
+ //# sourceMappingURL=version-check.js.map
@@ -1,5 +1,16 @@
1
+ /**
2
+ * Assistant identifiers accepted by the non-interactive --assistant flag
3
+ * (AISETUP-1). "other" is interactive-only: it prompts for a custom path.
4
+ */
5
+ export declare const SUPPORTED_ASSISTANTS: readonly ["claude-code", "claude-desktop", "cursor", "antigravity", "codex", "github-copilot"];
6
+ export interface AiSetupOptions {
7
+ assistant?: string;
8
+ }
1
9
  /**
2
10
  * AI setup command - configures the MCP server for AI assistants
11
+ *
12
+ * AISETUP-1: with --assistant <id>, setup runs without any interactive prompt
13
+ * (an AI assistant configuring itself knows which assistant it is).
3
14
  */
4
- export declare function aiSetupCommand(): Promise<void>;
15
+ export declare function aiSetupCommand(options?: AiSetupOptions): Promise<void>;
5
16
  //# sourceMappingURL=ai-setup.d.ts.map
@@ -13,9 +13,11 @@ function loadContextFileSection() {
13
13
  return loadTemplate("context-file-section.md");
14
14
  }
15
15
  /**
16
- * Install workflow guidance to the appropriate context file for a platform
16
+ * Install workflow guidance to the appropriate context file for a platform.
17
+ * With `yes` (the non-interactive --assistant path, AISETUP-1.0), both
18
+ * confirmations are auto-accepted — matching their interactive defaults.
17
19
  */
18
- async function installContextFileSection(platform) {
20
+ async function installContextFileSection(platform, yes = false) {
19
21
  const fileName = getContextFileName(platform);
20
22
  if (!fileName) {
21
23
  console.error(`\n❌ Unknown platform: ${platform}`);
@@ -37,24 +39,26 @@ async function installContextFileSection(platform) {
37
39
  existingSection = section.content;
38
40
  }
39
41
  }
40
- // Prompt user
42
+ // Prompt user (auto-accepted in non-interactive mode)
41
43
  const action = existingSection
42
44
  ? "Update"
43
45
  : existsSync(contextFilePath)
44
46
  ? "Add to"
45
47
  : "Create";
46
- const response = await prompts({
47
- type: "confirm",
48
- name: "install",
49
- message: `${action} ${fileName} with dotrequirements workflow guidance?`,
50
- initial: true,
51
- });
52
- if (!response.install) {
53
- console.log("");
54
- return;
48
+ if (!yes) {
49
+ const response = await prompts({
50
+ type: "confirm",
51
+ name: "install",
52
+ message: `${action} ${fileName} with dotrequirements workflow guidance?`,
53
+ initial: true,
54
+ });
55
+ if (!response.install) {
56
+ console.log("");
57
+ return;
58
+ }
55
59
  }
56
- // If updating, show what will change
57
- if (existingSection) {
60
+ // If updating, show what will change (auto-accepted in non-interactive mode)
61
+ if (existingSection && !yes) {
58
62
  const confirmUpdate = await prompts({
59
63
  type: "confirm",
60
64
  name: "confirm",
@@ -80,12 +84,37 @@ async function installContextFileSection(platform) {
80
84
  console.log(` Error: ${error instanceof Error ? error.message : String(error)}\n`);
81
85
  }
82
86
  }
87
+ /**
88
+ * Assistant identifiers accepted by the non-interactive --assistant flag
89
+ * (AISETUP-1). "other" is interactive-only: it prompts for a custom path.
90
+ */
91
+ export const SUPPORTED_ASSISTANTS = [
92
+ "claude-code",
93
+ "claude-desktop",
94
+ "cursor",
95
+ "antigravity",
96
+ "codex",
97
+ "github-copilot",
98
+ ];
83
99
  /**
84
100
  * AI setup command - configures the MCP server for AI assistants
101
+ *
102
+ * AISETUP-1: with --assistant <id>, setup runs without any interactive prompt
103
+ * (an AI assistant configuring itself knows which assistant it is).
85
104
  */
86
- export async function aiSetupCommand() {
105
+ export async function aiSetupCommand(options = {}) {
87
106
  console.log(`🔧 Setting up ${brand} MCP server\n`);
88
- // Interactive client selector
107
+ if (options.assistant) {
108
+ const id = options.assistant.toLowerCase();
109
+ if (!SUPPORTED_ASSISTANTS.includes(id)) {
110
+ // AISETUP-1.1: unsupported value → error listing supported identifiers
111
+ throw new Error(`Unsupported assistant "${options.assistant}". Supported: ${SUPPORTED_ASSISTANTS.join(", ")}`);
112
+ }
113
+ // AISETUP-1.0: no prompt; same setup path as the interactive selector
114
+ await dispatchSetup(id, true);
115
+ return;
116
+ }
117
+ // AISETUP-1.2: no flag → interactive selector, unchanged
89
118
  const response = await prompts({
90
119
  type: "select",
91
120
  name: "client",
@@ -105,34 +134,37 @@ export async function aiSetupCommand() {
105
134
  console.log("\nSetup cancelled.");
106
135
  return;
107
136
  }
108
- switch (response.client) {
137
+ await dispatchSetup(response.client);
138
+ }
139
+ async function dispatchSetup(client, yes = false) {
140
+ switch (client) {
109
141
  case "claude-code":
110
- await setupClaudeCode();
142
+ await setupClaudeCode(yes);
111
143
  break;
112
144
  case "claude-desktop":
113
145
  await setupClaudeDesktop();
114
146
  break;
115
147
  case "cursor":
116
- await setupCursor();
148
+ await setupCursor(yes);
117
149
  break;
118
150
  case "antigravity":
119
- await setupAntigravity();
151
+ await setupAntigravity(yes);
120
152
  break;
121
153
  case "codex":
122
- await setupCodex();
154
+ await setupCodex(yes);
123
155
  break;
124
156
  case "github-copilot":
125
- await setupGitHubCopilot();
157
+ await setupGitHubCopilot(yes);
126
158
  break;
127
159
  case "other":
128
160
  await setupOther();
129
161
  break;
130
162
  default:
131
- console.log(`\n❌ ${response.client} is not yet supported.`);
163
+ console.log(`\n❌ ${client} is not yet supported.`);
132
164
  console.log("Please refer to the documentation for manual setup.");
133
165
  }
134
166
  }
135
- async function setupClaudeCode() {
167
+ async function setupClaudeCode(yes = false) {
136
168
  console.log("\n📦 Configuring MCP for Claude Code...\n");
137
169
  const command = `claude mcp add-json dotrequirements '{"type":"stdio","command":"npx","args":["-y","@popoverai/dotrequirements","mcp"]}'`;
138
170
  let mcpConfigured = false;
@@ -162,7 +194,7 @@ async function setupClaudeCode() {
162
194
  if (mcpConfigured) {
163
195
  console.log("\n⚠️ Important: Restart Claude Code for changes to take effect.");
164
196
  console.log("\nYou can now ask your AI assistant to help write requirements!\n");
165
- await installContextFileSection("claude-code");
197
+ await installContextFileSection("claude-code", yes);
166
198
  }
167
199
  }
168
200
  async function setupClaudeDesktop() {
@@ -237,7 +269,7 @@ async function setupClaudeDesktop() {
237
269
  console.log(JSON.stringify(manualConfig, null, 2));
238
270
  }
239
271
  }
240
- async function setupCursor() {
272
+ async function setupCursor(yes = false) {
241
273
  console.log("\n📦 Configuring MCP for Cursor (project-specific)...\n");
242
274
  const configPath = join(process.cwd(), ".cursor", "mcp.json");
243
275
  const configDir = join(process.cwd(), ".cursor");
@@ -272,7 +304,7 @@ async function setupCursor() {
272
304
  console.log(` Config file: ${configPath}\n`);
273
305
  console.log("⚠️ Important: Reload or restart Cursor for changes to take effect.");
274
306
  console.log("\nYou can now ask your AI assistant to help write requirements!\n");
275
- await installContextFileSection("cursor");
307
+ await installContextFileSection("cursor", yes);
276
308
  }
277
309
  catch (error) {
278
310
  console.error("❌ Failed to configure MCP automatically.");
@@ -288,7 +320,7 @@ async function setupCursor() {
288
320
  console.log(JSON.stringify(manualConfig, null, 2));
289
321
  }
290
322
  }
291
- async function setupAntigravity() {
323
+ async function setupAntigravity(yes = false) {
292
324
  console.log("\n📦 Configuring MCP for Google Antigravity...\n");
293
325
  const configPath = join(homedir(), ".gemini", "antigravity", "mcp_config.json");
294
326
  const configDir = join(homedir(), ".gemini", "antigravity");
@@ -367,7 +399,7 @@ async function setupAntigravity() {
367
399
  else {
368
400
  console.log("");
369
401
  }
370
- await installContextFileSection("antigravity");
402
+ await installContextFileSection("antigravity", yes);
371
403
  }
372
404
  catch (error) {
373
405
  console.error("❌ Failed to configure MCP automatically.");
@@ -376,7 +408,7 @@ async function setupAntigravity() {
376
408
  console.log(` ${configPath}`);
377
409
  }
378
410
  }
379
- async function setupCodex() {
411
+ async function setupCodex(yes = false) {
380
412
  console.log("\n📦 Configuring MCP for OpenAI Codex...\n");
381
413
  const command = `codex mcp add dotrequirements -- npx -y @popoverai/dotrequirements mcp`;
382
414
  try {
@@ -384,7 +416,7 @@ async function setupCodex() {
384
416
  console.log("\n✅ MCP configured successfully for Codex!");
385
417
  console.log("\n⚠️ Important: Restart Codex for changes to take effect.");
386
418
  console.log("\nYou can now ask your AI assistant to help write requirements!\n");
387
- await installContextFileSection("codex");
419
+ await installContextFileSection("codex", yes);
388
420
  }
389
421
  catch {
390
422
  console.error("\n❌ Failed to configure MCP automatically.");
@@ -396,12 +428,12 @@ async function setupCodex() {
396
428
  console.log('args = ["-y", "@popoverai/dotrequirements", "mcp"]');
397
429
  }
398
430
  }
399
- async function setupGitHubCopilot() {
431
+ async function setupGitHubCopilot(yes = false) {
400
432
  console.log("\n📦 Configuring skills for GitHub Copilot...\n");
401
433
  console.log("ℹ️ GitHub Copilot discovers MCP servers from your IDE configuration.");
402
434
  console.log(" Please configure the MCP server in VS Code settings or GitHub settings.\n");
403
435
  console.log(" MCP server command: npx -y @popoverai/dotrequirements mcp\n");
404
- await installContextFileSection("github-copilot");
436
+ await installContextFileSection("github-copilot", yes);
405
437
  }
406
438
  async function setupOther() {
407
439
  console.log("\n📦 Custom Context File Setup\n");
@@ -12,10 +12,10 @@
12
12
  * - CTSO-CONV-3: orchestrator dispatches an editor when revisions are needed
13
13
  */
14
14
  import { existsSync, readFileSync } from "node:fs";
15
+ import { sanitizeAreaName } from "../../codebase-to-spec/area-name.js";
15
16
  import { cachePaths } from "../../codebase-to-spec/cache.js";
16
17
  import { EDITOR_DISPATCH_ID_PREFIX } from "../../codebase-to-spec/dispatch.js";
17
18
  import { ExitCode } from "../../codebase-to-spec/exit-codes.js";
18
- import { sanitizeAreaName } from "../../codebase-to-spec/fan-out.js";
19
19
  import { parseConversationalOutline, } from "../../codebase-to-spec/schemas.js";
20
20
  import { findProjectRoot } from "../../utils/project-settings.js";
21
21
  export async function dispatchEditorCommand(areaPrefix) {
@@ -10,10 +10,10 @@
10
10
  * the orchestrator should approve the outline before fan-out.
11
11
  */
12
12
  import { existsSync, readFileSync } from "node:fs";
13
+ import { sanitizeAreaName } from "../../codebase-to-spec/area-name.js";
13
14
  import { cachePaths } from "../../codebase-to-spec/cache.js";
14
15
  import { SPECIFIER_DISPATCH_ID_PREFIX } from "../../codebase-to-spec/dispatch.js";
15
16
  import { ExitCode } from "../../codebase-to-spec/exit-codes.js";
16
- import { sanitizeAreaName } from "../../codebase-to-spec/fan-out.js";
17
17
  import { parseConversationalOutline, } from "../../codebase-to-spec/schemas.js";
18
18
  import { findProjectRoot } from "../../utils/project-settings.js";
19
19
  export async function dispatchSpecCommand() {