@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
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * Skill installation logic for the codebase-to-spec skill.
3
3
  *
4
- * Ships as a bundle: SKILL.md + cts-worker agent definition + persona-
5
- * injection hook script + hook registration in `.claude/settings.local.json`.
6
- * The skill body (the conversational orchestrator) DEPENDS on the companion
7
- * files — installing just the skill without them would leave Task dispatches
8
- * unable to resolve their persona. For `project` scope, all four pieces are
9
- * installed in one shot. For `global` and `custom` scopes, only the skill
10
- * body is installed; the companions are project-scoped by CC convention.
4
+ * Ships as a bundle: SKILL.md + cts-worker agent definition + the `codebase-to-spec`
5
+ * dynamic-workflow script. The skill body (the conversational orchestrator)
6
+ * launches the workflow, which dispatches cts-worker agents; both companions
7
+ * must be present for the skill to run. For `project` and `global` scope, all
8
+ * three pieces are installed in one shot (under the project's `.claude/` or the
9
+ * user's `~/.claude/`). Only a `custom` target dir is skill-only, because
10
+ * assistants do not auto-discover agents or workflows from arbitrary paths.
11
11
  *
12
12
  * Host portability (CTS-SKILL-5): the install logic supports any host that
13
13
  * follows the Agent Skills format. The default target is Claude Code's
@@ -15,25 +15,28 @@
15
15
  *
16
16
  * Requirements covered:
17
17
  * - CTS-SKILL-1, CTS-SKILL-5
18
- * - CTSO-CLI-1 (bundles the agent + hook that PreToolUse-injects persona bodies)
18
+ * - CTSO-INSTALL-1 (bundles the skill, the cts-worker agent, and the workflow)
19
19
  */
20
20
  export type SkillInstallScope = "project" | "global" | "custom";
21
21
  export interface SkillInstallOptions {
22
22
  /**
23
- * Where to install:
24
- * - `project` (default): `<projectRoot>/.claude/skills/codebase-to-spec/`
25
- * (plus companions under `<projectRoot>/.claude/agents/` and
26
- * `<projectRoot>/.claude/hooks/`, and hook registration in
27
- * `<projectRoot>/.claude/settings.local.json`)
28
- * - `global`: `~/.claude/skills/codebase-to-spec/` (skill body only)
29
- * - `custom`: requires `targetDir` (skill body only)
23
+ * Where to install. `project` and `global` install the full bundle (skill +
24
+ * cts-worker agent + specify-codebase workflow) under the corresponding
25
+ * `.claude/` base, so the skill can launch the workflow; `custom` installs
26
+ * the skill body only.
27
+ * - `project` (default): `<projectRoot>/.claude/{skills,agents,workflows}/`
28
+ * - `global`: `<globalRoot>/.claude/{skills,agents,workflows}/`
29
+ * - `custom`: `<targetDir>/codebase-to-spec/SKILL.md` only — Claude Code does
30
+ * not auto-discover agents or workflows from an arbitrary directory
30
31
  */
31
32
  scope?: SkillInstallScope;
32
33
  /** Required when `scope === 'custom'`. The skill directory will be created here. */
33
34
  targetDir?: string;
34
35
  /** Project root (used when scope === 'project'). Defaults to cwd. */
35
36
  projectRoot?: string;
36
- /** Overwrite existing SKILL.md / agent / hook if present. Default false. */
37
+ /** Home root for `global` scope (the parent of `.claude`). Defaults to homedir(); tests inject a temp dir. */
38
+ globalRoot?: string;
39
+ /** Overwrite existing SKILL.md / agent / workflow if present. Default false. */
37
40
  overwrite?: boolean;
38
41
  }
39
42
  export interface SkillInstallResult {
@@ -43,12 +46,10 @@ export interface SkillInstallResult {
43
46
  installed: boolean;
44
47
  /** True if an existing file was replaced. */
45
48
  overwrote: boolean;
46
- /** Companion files installed alongside the skill (project scope only). */
49
+ /** Companion files installed alongside the skill (project + global scope). */
47
50
  companions?: {
48
51
  agentPath: string;
49
- hookScriptPath: string;
50
- settingsPath: string;
51
- hookRegistered: boolean;
52
+ workflowPath: string;
52
53
  };
53
54
  }
54
55
  /**
@@ -58,24 +59,36 @@ export interface SkillInstallResult {
58
59
  */
59
60
  export declare function resolveSkillDir(options: SkillInstallOptions): string;
60
61
  /**
61
- * 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.
62
66
  */
63
- export declare function loadSkillTemplate(): string;
67
+ export declare function resolveCliInvocation(): string;
64
68
  /**
65
- * 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).
66
71
  */
67
- export declare function loadAgentTemplate(): string;
72
+ export declare function loadSkillTemplate(cliInvocation?: string): string;
68
73
  /**
69
- * Load the bundled persona-injection hook script template.
74
+ * Load the bundled cts-worker agent definition template, rendering
75
+ * `{{DOTREQ_CLI}}` to the given CLI invocation.
70
76
  */
71
- export declare function loadHookTemplate(): 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;
72
83
  /**
73
84
  * Install (or refuse to overwrite) the codebase-to-spec skill bundle.
74
85
  *
75
- * For `project` scope: installs skill body, cts-worker agent, persona hook
76
- * script, and registers the hook in settings.local.json.
86
+ * For `project` and `global` scope: installs the full bundle — the skill body,
87
+ * the cts-worker agent, and the specify-codebase workflow — under the matching
88
+ * `.claude/` base (the project's, or the user's `~/.claude/`).
77
89
  *
78
- * For `global` and `custom` scopes: installs only the skill body.
90
+ * For `custom` scope: installs only the skill body, because Claude Code does
91
+ * not auto-discover agents or workflows from an arbitrary directory.
79
92
  */
80
93
  export declare function installSkill(options?: SkillInstallOptions): SkillInstallResult;
81
94
  //# sourceMappingURL=skill-install.d.ts.map
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * Skill installation logic for the codebase-to-spec skill.
3
3
  *
4
- * Ships as a bundle: SKILL.md + cts-worker agent definition + persona-
5
- * injection hook script + hook registration in `.claude/settings.local.json`.
6
- * The skill body (the conversational orchestrator) DEPENDS on the companion
7
- * files — installing just the skill without them would leave Task dispatches
8
- * unable to resolve their persona. For `project` scope, all four pieces are
9
- * installed in one shot. For `global` and `custom` scopes, only the skill
10
- * body is installed; the companions are project-scoped by CC convention.
4
+ * Ships as a bundle: SKILL.md + cts-worker agent definition + the `codebase-to-spec`
5
+ * dynamic-workflow script. The skill body (the conversational orchestrator)
6
+ * launches the workflow, which dispatches cts-worker agents; both companions
7
+ * must be present for the skill to run. For `project` and `global` scope, all
8
+ * three pieces are installed in one shot (under the project's `.claude/` or the
9
+ * user's `~/.claude/`). Only a `custom` target dir is skill-only, because
10
+ * assistants do not auto-discover agents or workflows from arbitrary paths.
11
11
  *
12
12
  * Host portability (CTS-SKILL-5): the install logic supports any host that
13
13
  * follows the Agent Skills format. The default target is Claude Code's
@@ -15,11 +15,13 @@
15
15
  *
16
16
  * Requirements covered:
17
17
  * - CTS-SKILL-1, CTS-SKILL-5
18
- * - CTSO-CLI-1 (bundles the agent + hook that PreToolUse-injects persona bodies)
18
+ * - CTSO-INSTALL-1 (bundles the skill, the cts-worker agent, and the workflow)
19
19
  */
20
- import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync, } from "node:fs";
20
+ import { createHash } from "node:crypto";
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
@@ -34,7 +36,7 @@ export function resolveSkillDir(options) {
34
36
  return join(root, ".claude", "skills", "codebase-to-spec");
35
37
  }
36
38
  case "global":
37
- return join(homedir(), ".claude", "skills", "codebase-to-spec");
39
+ return join(options.globalRoot ?? homedir(), ".claude", "skills", "codebase-to-spec");
38
40
  case "custom":
39
41
  if (!options.targetDir) {
40
42
  throw new Error('targetDir is required when scope === "custom"');
@@ -45,136 +47,144 @@ 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 persona-injection hook 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 loadHookTemplate() {
63
- return loadTemplate("hooks/cts-worker-persona.sh");
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.`);
77
- }
78
- mkdirSync(dirname(targetPath), { recursive: true });
79
- writeFileSync(targetPath, templateContent, "utf-8");
80
- return { installed: true, overwrote: exists };
79
+ export function loadWorkflowTemplate(cliInvocation = resolveCliInvocation()) {
80
+ return loadTemplate("workflows/specify-codebase.js", {
81
+ DOTREQ_CLI: cliInvocation,
82
+ });
81
83
  }
82
84
  /**
83
- * Register the cts-worker-persona PreToolUse hook in the project's
84
- * `.claude/settings.local.json`, preserving any existing keys.
85
- *
86
- * Returns true if the hook registration was added or updated.
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).
87
90
  */
88
- function registerHookInSettings(projectRoot, hookScriptPath) {
89
- const settingsPath = join(projectRoot, ".claude", "settings.local.json");
90
- const hookEntry = {
91
- matcher: "Task",
92
- hooks: [{ type: "command", command: hookScriptPath }],
93
- };
94
- // Read existing settings if present.
95
- let settings = {};
96
- if (existsSync(settingsPath)) {
97
- try {
98
- const content = readFileSync(settingsPath, "utf-8");
99
- if (content.trim()) {
100
- settings = JSON.parse(content);
101
- }
102
- }
103
- catch {
104
- // Invalid JSON — refuse to clobber. Tell caller to fix manually.
105
- throw new Error(`Existing ${settingsPath} contains invalid JSON. Fix it or delete it before installing the orchestrator hook.`);
106
- }
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"));
107
101
  }
108
- // Initialize the hooks tree if missing.
109
- // biome-ignore lint/suspicious/noExplicitAny: settings.local.json is user-owned; we narrow ad-hoc.
110
- const hooks = (settings.hooks ?? {});
111
- const preToolUse = (hooks.PreToolUse ?? []);
112
- // Check if an entry already references our hook script (idempotent).
113
- const alreadyRegistered = preToolUse.some((entry) => {
114
- if (entry.matcher !== "Task")
115
- return false;
116
- const innerHooks = entry.hooks;
117
- if (!Array.isArray(innerHooks))
118
- return false;
119
- return innerHooks.some((h) => h.command === hookScriptPath);
120
- });
121
- if (alreadyRegistered) {
122
- return { settingsPath, registered: false };
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" };
111
+ }
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" };
123
118
  }
124
- preToolUse.push(hookEntry);
125
- hooks.PreToolUse = preToolUse;
126
- settings.hooks = hooks;
127
- mkdirSync(dirname(settingsPath), { recursive: true });
128
- writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, "utf-8");
129
- return { settingsPath, registered: true };
119
+ return { key, description, targetPath, content, state: "modified" };
130
120
  }
131
121
  /**
132
122
  * Install (or refuse to overwrite) the codebase-to-spec skill bundle.
133
123
  *
134
- * For `project` scope: installs skill body, cts-worker agent, persona hook
135
- * script, and registers the hook in settings.local.json.
124
+ * For `project` and `global` scope: installs the full bundle — the skill body,
125
+ * the cts-worker agent, and the specify-codebase workflow — under the matching
126
+ * `.claude/` base (the project's, or the user's `~/.claude/`).
136
127
  *
137
- * For `global` and `custom` scopes: installs only the skill body.
128
+ * For `custom` scope: installs only the skill body, because Claude Code does
129
+ * not auto-discover agents or workflows from an arbitrary directory.
138
130
  */
139
131
  export function installSkill(options = {}) {
140
132
  const skillDir = resolveSkillDir(options);
141
133
  const skillPath = join(skillDir, "SKILL.md");
142
- const skillTemplate = loadSkillTemplate();
143
- const skillResult = installTemplateFile(skillTemplate, skillPath, options.overwrite === true, "SKILL.md");
144
- // Skill body only for global/custom scopes — CC reads agents and hooks
145
- // per-project, so the companions don't make sense outside a project.
146
134
  const scope = options.scope ?? "project";
147
- if (scope !== "project") {
148
- return {
149
- installedPath: skillPath,
150
- installed: skillResult.installed,
151
- overwrote: skillResult.overwrote,
152
- };
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
+ ];
142
+ // A custom target dir is an arbitrary location CC won't read agents or
143
+ // workflows from, so it gets the skill body only. Project and global both
144
+ // install the full bundle under their `.claude/` base.
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 };
153
157
  }
154
- const projectRoot = options.projectRoot ?? process.cwd();
155
- const agentPath = join(projectRoot, ".claude", "agents", "cts-worker.md");
156
- const hookScriptPath = join(projectRoot, ".claude", "hooks", "cts-worker-persona.sh");
157
- // Install the cts-worker agent definition.
158
- installTemplateFile(loadAgentTemplate(), agentPath, options.overwrite === true, "cts-worker agent definition");
159
- // Install the persona-injection hook script (executable bit set separately).
160
- installTemplateFile(loadHookTemplate(), hookScriptPath, options.overwrite === true, "cts-worker-persona hook script");
161
- // chmod the hook script to be executable so Claude Code's PreToolUse
162
- // hook can invoke it. (Imported statically — the CLI package is ESM, so
163
- // `require("node:fs")` here would throw ReferenceError and silently
164
- // leave the script at 0644, breaking the hook on every install.)
165
- chmodSync(hookScriptPath, 0o755);
166
- // Register the hook in settings.local.json.
167
- const { settingsPath, registered } = registerHookInSettings(projectRoot, hookScriptPath);
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");
168
183
  return {
169
184
  installedPath: skillPath,
170
- installed: skillResult.installed,
171
- overwrote: skillResult.overwrote,
172
- companions: {
173
- agentPath,
174
- hookScriptPath,
175
- settingsPath,
176
- hookRegistered: registered,
177
- },
185
+ installed: skillPiece.state !== "current",
186
+ overwrote: skillPiece.state === "stock" || skillPiece.state === "modified",
187
+ companions,
178
188
  };
179
189
  }
180
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