@popoverai/dotrequirements 0.24.3 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +7 -8
  2. package/dist/cli.js +8 -1
  3. package/dist/codebase-to-spec/dispatch.d.ts +60 -14
  4. package/dist/codebase-to-spec/dispatch.js +381 -15
  5. package/dist/codebase-to-spec/pack.d.ts +7 -0
  6. package/dist/codebase-to-spec/pack.js +29 -8
  7. package/dist/codebase-to-spec/present.d.ts +9 -0
  8. package/dist/codebase-to-spec/present.js +23 -2
  9. package/dist/codebase-to-spec/prompts/editor.d.ts +1 -1
  10. package/dist/codebase-to-spec/prompts/editor.js +1 -1
  11. package/dist/codebase-to-spec/prompts/specifier.d.ts +1 -1
  12. package/dist/codebase-to-spec/prompts/specifier.js +3 -2
  13. package/dist/codebase-to-spec/schemas.d.ts +153 -0
  14. package/dist/codebase-to-spec/schemas.js +111 -0
  15. package/dist/codebase-to-spec/skill-install.d.ts +42 -29
  16. package/dist/codebase-to-spec/skill-install.js +122 -112
  17. package/dist/codebase-to-spec/version-check.d.ts +31 -0
  18. package/dist/codebase-to-spec/version-check.js +56 -0
  19. package/dist/commands/ai-setup.d.ts +12 -1
  20. package/dist/commands/ai-setup.js +65 -33
  21. package/dist/commands/codebase-to-spec/dispatch-context.d.ts +2 -5
  22. package/dist/commands/codebase-to-spec/dispatch-context.js +2 -5
  23. package/dist/commands/codebase-to-spec/dispatch-editor.d.ts +0 -1
  24. package/dist/commands/codebase-to-spec/dispatch-editor.js +0 -1
  25. package/dist/commands/codebase-to-spec/dispatch-planner.d.ts +0 -1
  26. package/dist/commands/codebase-to-spec/dispatch-planner.js +0 -1
  27. package/dist/commands/codebase-to-spec/dispatch-spec.d.ts +3 -6
  28. package/dist/commands/codebase-to-spec/dispatch-spec.js +3 -6
  29. package/dist/commands/codebase-to-spec/index.js +3 -2
  30. package/dist/commands/codebase-to-spec/pack.d.ts +9 -0
  31. package/dist/commands/codebase-to-spec/pack.js +23 -3
  32. package/dist/commands/codebase-to-spec/skill-install.js +2 -9
  33. package/dist/commands/init.js +6 -1
  34. package/dist/commands/link-resolution.d.ts +79 -0
  35. package/dist/commands/link-resolution.js +141 -0
  36. package/dist/commands/link.d.ts +14 -4
  37. package/dist/commands/link.js +369 -16
  38. package/dist/commands/pull.js +19 -2
  39. package/dist/commands/push.js +36 -2
  40. package/dist/convex.d.ts +5 -3
  41. package/dist/convex.js +5 -3
  42. package/dist/harness/cache.d.ts +0 -14
  43. package/dist/harness/cache.js +1 -41
  44. package/dist/harness/finalize.js +2 -2
  45. package/dist/harness/prepare.js +1 -3
  46. package/dist/harness/requirementsLoader.d.ts +3 -3
  47. package/dist/harness/requirementsLoader.js +13 -8
  48. package/dist/mcp/handlers/authoring.d.ts +5 -5
  49. package/dist/mcp/handlers/authoring.js +9 -9
  50. package/dist/mcp/handlers/push.d.ts +2 -2
  51. package/dist/mcp/handlers/push.js +36 -3
  52. package/dist/mcp/handlers/review.d.ts +4 -4
  53. package/dist/mcp/handlers/review.js +4 -4
  54. package/dist/mcp/handlers/search.d.ts +1 -1
  55. package/dist/mcp/handlers/search.js +1 -1
  56. package/dist/mcp/index.js +29 -0
  57. package/dist/push/core.d.ts +18 -0
  58. package/dist/push/core.js +70 -3
  59. package/dist/push/index.d.ts +1 -1
  60. package/dist/push/index.js +1 -1
  61. package/dist/schema/parser-core.js +5 -1
  62. package/dist/schema/parser.js +5 -1
  63. package/dist/schema/run-marker.d.ts +38 -0
  64. package/dist/schema/run-marker.js +138 -0
  65. package/dist/schema/schemas.d.ts +12 -0
  66. package/dist/schema/schemas.js +1 -0
  67. package/dist/templates/agents/cts-worker.md +3 -3
  68. package/dist/templates/skills/codebase-to-spec/SKILL.md +56 -158
  69. package/dist/templates/workflows/specify-codebase.js +374 -0
  70. package/dist/utils/own-package.d.ts +10 -0
  71. package/dist/utils/own-package.js +13 -0
  72. package/dist/utils/project-selector.d.ts +5 -0
  73. package/dist/utils/project-selector.js +4 -0
  74. package/package.json +3 -3
  75. package/dist/templates/hooks/cts-worker-persona.sh +0 -76
@@ -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");
@@ -2,11 +2,8 @@
2
2
  * `dotrequirements codebase-to-spec dispatch-context <dispatch-id>` subcommand.
3
3
  *
4
4
  * Returns the composed prompt for a worker dispatch as a JSON object on stdout.
5
- * Called by the cts-worker PreToolUse hook to compose the subagent's first-turn
6
- * prompt via `modifiedInput`.
7
- *
8
- * Requirements covered:
9
- * - CTSO-CLI-1: CLI exposes commands that return dispatch instructions
5
+ * Run by cts-worker subagents at the start of their turn to fetch the composed
6
+ * first-turn prompt for their dispatch-id (see the specify-codebase workflow).
10
7
  */
11
8
  export declare function dispatchContextCommand(dispatchId: string): Promise<void>;
12
9
  //# sourceMappingURL=dispatch-context.d.ts.map
@@ -2,11 +2,8 @@
2
2
  * `dotrequirements codebase-to-spec dispatch-context <dispatch-id>` subcommand.
3
3
  *
4
4
  * Returns the composed prompt for a worker dispatch as a JSON object on stdout.
5
- * Called by the cts-worker PreToolUse hook to compose the subagent's first-turn
6
- * prompt via `modifiedInput`.
7
- *
8
- * Requirements covered:
9
- * - CTSO-CLI-1: CLI exposes commands that return dispatch instructions
5
+ * Run by cts-worker subagents at the start of their turn to fetch the composed
6
+ * first-turn prompt for their dispatch-id (see the specify-codebase workflow).
10
7
  */
11
8
  import { composeDispatchContext } from "../../codebase-to-spec/dispatch.js";
12
9
  import { ExitCode } from "../../codebase-to-spec/exit-codes.js";
@@ -9,7 +9,6 @@
9
9
  * in outline.yaml stays where it is (the orchestrator manages it).
10
10
  *
11
11
  * Requirements covered:
12
- * - CTSO-CLI-1: CLI exposes commands that return dispatch instructions
13
12
  * - CTSO-CONV-3: orchestrator dispatches an editor when revisions are needed
14
13
  */
15
14
  export declare function dispatchEditorCommand(areaPrefix: string): Promise<void>;
@@ -9,7 +9,6 @@
9
9
  * in outline.yaml stays where it is (the orchestrator manages it).
10
10
  *
11
11
  * Requirements covered:
12
- * - CTSO-CLI-1: CLI exposes commands that return dispatch instructions
13
12
  * - CTSO-CONV-3: orchestrator dispatches an editor when revisions are needed
14
13
  */
15
14
  import { existsSync, readFileSync } from "node:fs";
@@ -9,7 +9,6 @@
9
9
  * preserving the review section.
10
10
  *
11
11
  * Requirements covered:
12
- * - CTSO-CLI-1: CLI exposes commands that return dispatch instructions
13
12
  * - CTSO-CONV-2: orchestrator iterates on planner's outline until approved
14
13
  */
15
14
  export interface DispatchPlannerOptions {
@@ -9,7 +9,6 @@
9
9
  * preserving the review section.
10
10
  *
11
11
  * Requirements covered:
12
- * - CTSO-CLI-1: CLI exposes commands that return dispatch instructions
13
12
  * - CTSO-CONV-2: orchestrator iterates on planner's outline until approved
14
13
  */
15
14
  import { existsSync, readFileSync } from "node:fs";
@@ -2,15 +2,12 @@
2
2
  * `dotrequirements codebase-to-spec dispatch-spec` subcommand.
3
3
  *
4
4
  * Returns an array of specifier dispatch payloads — one per area in the
5
- * approved outline. The conversational orchestrator reads this, fires N
6
- * background Task dispatches in parallel (one cts-worker per area), and
7
- * collects the resulting partials.
5
+ * approved outline. The specify-codebase workflow reads this, fires N
6
+ * cts-worker dispatches in parallel (one per area), and collects the
7
+ * resulting partials.
8
8
  *
9
9
  * Errors clearly if `outline.yaml`'s `review.result` isn't `"approved"` —
10
10
  * the orchestrator should approve the outline before fan-out.
11
- *
12
- * Requirements covered:
13
- * - CTSO-CLI-1: CLI exposes commands that return dispatch instructions
14
11
  */
15
12
  export declare function dispatchSpecCommand(): Promise<void>;
16
13
  //# sourceMappingURL=dispatch-spec.d.ts.map
@@ -2,15 +2,12 @@
2
2
  * `dotrequirements codebase-to-spec dispatch-spec` subcommand.
3
3
  *
4
4
  * Returns an array of specifier dispatch payloads — one per area in the
5
- * approved outline. The conversational orchestrator reads this, fires N
6
- * background Task dispatches in parallel (one cts-worker per area), and
7
- * collects the resulting partials.
5
+ * approved outline. The specify-codebase workflow reads this, fires N
6
+ * cts-worker dispatches in parallel (one per area), and collects the
7
+ * resulting partials.
8
8
  *
9
9
  * Errors clearly if `outline.yaml`'s `review.result` isn't `"approved"` —
10
10
  * the orchestrator should approve the outline before fan-out.
11
- *
12
- * Requirements covered:
13
- * - CTSO-CLI-1: CLI exposes commands that return dispatch instructions
14
11
  */
15
12
  import { existsSync, readFileSync } from "node:fs";
16
13
  import { cachePaths } from "../../codebase-to-spec/cache.js";
@@ -46,7 +46,8 @@ export function registerCodebaseToSpec(program) {
46
46
  cts
47
47
  .command("pack")
48
48
  .description("Pack the codebase (compressed and uncompressed views) into the cache")
49
- .option("-s, --scope <path>", "Limit to files under this path (subdir of project root)")
49
+ .option("-s, --scope <path>", "Limit to files under this path (subdir of project root, or of the remote repo when --remote is set)")
50
+ .option("--remote <url>", "Pack a remote repository (GitHub URL or owner/repo) instead of the local project; repomix clones, packs, and cleans up. Combine with --scope to pack just a subdirectory.")
50
51
  .option("--fresh", "Clear the cache before running")
51
52
  .option("--budget <tokens>", "Override the working-context budget (in tokens)", (v) => parseInt(v, 10))
52
53
  .option("--ignore-requirements", "Exclude `.requirements/**` from the pack (use when testing cts against a codebase whose existing requirements should not influence the output)")
@@ -150,7 +151,7 @@ export function registerCodebaseToSpec(program) {
150
151
  });
151
152
  cts
152
153
  .command("dispatch-context <dispatch-id>", HIDDEN)
153
- .description("Return the composed prompt for a worker dispatch as JSON on stdout (called by the cts-worker PreToolUse hook)")
154
+ .description("Return the composed prompt for a worker dispatch as JSON on stdout (run by cts-worker subagents to fetch their dispatch prompt)")
154
155
  .action((dispatchId) => dispatchContextCommand(dispatchId));
155
156
  cts
156
157
  .command("dispatch-planner", HIDDEN)
@@ -7,10 +7,17 @@
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;
16
+ /**
17
+ * Pack a remote repository (GitHub URL or `owner/repo`) instead of the local
18
+ * project. Combine with `scope` to pack just a subdirectory of the remote.
19
+ */
20
+ remote?: string;
14
21
  fresh?: boolean;
15
22
  budgetTokens?: number;
16
23
  /**
@@ -23,6 +30,8 @@ export interface PackOptions {
23
30
  progress?: import("../../codebase-to-spec/progress.js").ProgressEmitter;
24
31
  /** Override the project root, mainly for tests. */
25
32
  projectRoot?: string;
33
+ /** Override the registry version check, mainly for tests. */
34
+ checkVersion?: typeof checkForNewerVersion;
26
35
  }
27
36
  export declare function packCommand(rawOptions?: PackOptions): Promise<void>;
28
37
  //# sourceMappingURL=pack.d.ts.map
@@ -7,6 +7,7 @@
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";
@@ -15,11 +16,16 @@ import { clearCache, ensureCacheDir } 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",
@@ -35,14 +41,17 @@ export async function packCommand(rawOptions = {}) {
35
41
  progress.emit({
36
42
  stage: "pack",
37
43
  step: "start",
38
- message: scope
39
- ? `Packing codebase at ${scope}`
40
- : `Packing codebase at ${projectRoot}`,
44
+ message: rawOptions.remote
45
+ ? `Packing remote ${rawOptions.remote}${rawOptions.scope ? ` (scope: ${rawOptions.scope})` : ""}`
46
+ : scope
47
+ ? `Packing codebase at ${scope}`
48
+ : `Packing codebase at ${projectRoot}`,
41
49
  });
42
50
  const packResult = await runPack({
43
51
  projectRoot,
44
52
  paths,
45
53
  scope: rawOptions.scope,
54
+ remote: rawOptions.remote,
46
55
  ignoreRequirements: rawOptions.ignoreRequirements,
47
56
  });
48
57
  progress.emit({
@@ -51,6 +60,17 @@ export async function packCommand(rawOptions = {}) {
51
60
  message: `Packed ${packResult.fileCount} files → ${paths.overview} (compressed), ${paths.source} (uncompressed)`,
52
61
  data: { fileCount: packResult.fileCount },
53
62
  });
63
+ // Surface a stale install (CTS-PACK-4). Informational only: outputs and
64
+ // exit code are unaffected, and a failed check emits nothing.
65
+ const versionInfo = await versionCheck.catch(() => undefined);
66
+ if (versionInfo?.stale) {
67
+ progress.emit({
68
+ stage: "pack",
69
+ step: "stale-version",
70
+ message: `A newer dotrequirements CLI is available: ${versionInfo.latest} (running ${versionInfo.current}). Re-run skill-install to refresh.`,
71
+ data: { current: versionInfo.current, latest: versionInfo.latest },
72
+ });
73
+ }
54
74
  // Budget check (CTS-CLI-3)
55
75
  const measured = countTokensInFileExact(paths.overview);
56
76
  const budget = checkBudget(measured, budgetTokens);
@@ -43,16 +43,9 @@ export async function skillInstallCommand(options = {}) {
43
43
  }
44
44
  if (result.companions) {
45
45
  process.stdout.write(`Installed cts-worker agent at ${result.companions.agentPath}\n`);
46
- process.stdout.write(`Installed persona-injection hook at ${result.companions.hookScriptPath}\n`);
47
- if (result.companions.hookRegistered) {
48
- process.stdout.write(`Registered PreToolUse hook in ${result.companions.settingsPath}\n`);
49
- }
50
- else {
51
- process.stdout.write(`PreToolUse hook already registered in ${result.companions.settingsPath}\n`);
52
- }
53
- process.stdout.write("\n⚠️ The hook script uses `dotrequirements` from PATH. If you're working in a local dev clone, set DOTREQUIREMENTS_CLI to override (see the hook script for details).\n");
46
+ process.stdout.write(`Installed specify-codebase workflow at ${result.companions.workflowPath}\n`);
54
47
  }
55
- process.stdout.write("\nSkill ready. In Claude Code, invoke it with `/codebase-to-spec` (or via natural language).\n");
48
+ process.stdout.write("\nSkill ready. In Claude Code, invoke it with `/codebase-to-spec` (or via natural language). Requires a Claude Code version with dynamic-workflow support.\n");
56
49
  }
57
50
  catch (err) {
58
51
  process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
@@ -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 (errorMessage.includes("already a member")) {
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