@popoverai/dotrequirements 0.24.3 → 0.25.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 (30) hide show
  1. package/README.md +7 -8
  2. package/dist/codebase-to-spec/dispatch.d.ts +60 -14
  3. package/dist/codebase-to-spec/dispatch.js +381 -15
  4. package/dist/codebase-to-spec/pack.d.ts +7 -0
  5. package/dist/codebase-to-spec/pack.js +29 -8
  6. package/dist/codebase-to-spec/prompts/editor.d.ts +1 -1
  7. package/dist/codebase-to-spec/prompts/editor.js +1 -1
  8. package/dist/codebase-to-spec/prompts/specifier.d.ts +1 -1
  9. package/dist/codebase-to-spec/prompts/specifier.js +3 -2
  10. package/dist/codebase-to-spec/schemas.d.ts +153 -0
  11. package/dist/codebase-to-spec/schemas.js +111 -0
  12. package/dist/codebase-to-spec/skill-install.d.ts +28 -25
  13. package/dist/codebase-to-spec/skill-install.js +31 -83
  14. package/dist/commands/codebase-to-spec/dispatch-context.d.ts +2 -5
  15. package/dist/commands/codebase-to-spec/dispatch-context.js +2 -5
  16. package/dist/commands/codebase-to-spec/dispatch-editor.d.ts +0 -1
  17. package/dist/commands/codebase-to-spec/dispatch-editor.js +0 -1
  18. package/dist/commands/codebase-to-spec/dispatch-planner.d.ts +0 -1
  19. package/dist/commands/codebase-to-spec/dispatch-planner.js +0 -1
  20. package/dist/commands/codebase-to-spec/dispatch-spec.d.ts +3 -6
  21. package/dist/commands/codebase-to-spec/dispatch-spec.js +3 -6
  22. package/dist/commands/codebase-to-spec/index.js +3 -2
  23. package/dist/commands/codebase-to-spec/pack.d.ts +5 -0
  24. package/dist/commands/codebase-to-spec/pack.js +6 -3
  25. package/dist/commands/codebase-to-spec/skill-install.js +2 -9
  26. package/dist/templates/agents/cts-worker.md +3 -3
  27. package/dist/templates/skills/codebase-to-spec/SKILL.md +44 -168
  28. package/dist/templates/workflows/specify-codebase.js +372 -0
  29. package/package.json +2 -2
  30. 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,9 +15,9 @@
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 { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
21
21
  import { homedir } from "node:os";
22
22
  import { dirname, join, resolve } from "node:path";
23
23
  import { loadTemplate } from "../utils/templates.js";
@@ -34,7 +34,7 @@ export function resolveSkillDir(options) {
34
34
  return join(root, ".claude", "skills", "codebase-to-spec");
35
35
  }
36
36
  case "global":
37
- return join(homedir(), ".claude", "skills", "codebase-to-spec");
37
+ return join(options.globalRoot ?? homedir(), ".claude", "skills", "codebase-to-spec");
38
38
  case "custom":
39
39
  if (!options.targetDir) {
40
40
  throw new Error('targetDir is required when scope === "custom"');
@@ -57,10 +57,10 @@ export function loadAgentTemplate() {
57
57
  return loadTemplate("agents/cts-worker.md");
58
58
  }
59
59
  /**
60
- * Load the bundled persona-injection hook script template.
60
+ * Load the bundled specify-codebase dynamic-workflow script template.
61
61
  */
62
- export function loadHookTemplate() {
63
- return loadTemplate("hooks/cts-worker-persona.sh");
62
+ export function loadWorkflowTemplate() {
63
+ return loadTemplate("workflows/specify-codebase.js");
64
64
  }
65
65
  /**
66
66
  * Install (or refuse to overwrite) one template file at a target path.
@@ -79,101 +79,49 @@ function installTemplateFile(templateContent, targetPath, overwrite, description
79
79
  writeFileSync(targetPath, templateContent, "utf-8");
80
80
  return { installed: true, overwrote: exists };
81
81
  }
82
- /**
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.
87
- */
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
- }
107
- }
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 };
123
- }
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 };
130
- }
131
82
  /**
132
83
  * Install (or refuse to overwrite) the codebase-to-spec skill bundle.
133
84
  *
134
- * For `project` scope: installs skill body, cts-worker agent, persona hook
135
- * script, and registers the hook in settings.local.json.
85
+ * For `project` and `global` scope: installs the full bundle — the skill body,
86
+ * the cts-worker agent, and the specify-codebase workflow — under the matching
87
+ * `.claude/` base (the project's, or the user's `~/.claude/`).
136
88
  *
137
- * For `global` and `custom` scopes: installs only the skill body.
89
+ * For `custom` scope: installs only the skill body, because Claude Code does
90
+ * not auto-discover agents or workflows from an arbitrary directory.
138
91
  */
139
92
  export function installSkill(options = {}) {
140
93
  const skillDir = resolveSkillDir(options);
141
94
  const skillPath = join(skillDir, "SKILL.md");
142
95
  const skillTemplate = loadSkillTemplate();
143
96
  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.
97
+ // A custom target dir is an arbitrary location CC won't read agents or
98
+ // workflows from, so it gets the skill body only. Project and global both
99
+ // install the full bundle under their `.claude/` base.
146
100
  const scope = options.scope ?? "project";
147
- if (scope !== "project") {
101
+ if (scope === "custom") {
148
102
  return {
149
103
  installedPath: skillPath,
150
104
  installed: skillResult.installed,
151
105
  overwrote: skillResult.overwrote,
152
106
  };
153
107
  }
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");
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");
157
113
  // Install the cts-worker agent definition.
158
114
  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);
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");
168
118
  return {
169
119
  installedPath: skillPath,
170
120
  installed: skillResult.installed,
171
121
  overwrote: skillResult.overwrote,
172
122
  companions: {
173
123
  agentPath,
174
- hookScriptPath,
175
- settingsPath,
176
- hookRegistered: registered,
124
+ workflowPath,
177
125
  },
178
126
  };
179
127
  }
@@ -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)
@@ -11,6 +11,11 @@
11
11
  */
12
12
  export interface PackOptions {
13
13
  scope?: string;
14
+ /**
15
+ * Pack a remote repository (GitHub URL or `owner/repo`) instead of the local
16
+ * project. Combine with `scope` to pack just a subdirectory of the remote.
17
+ */
18
+ remote?: string;
14
19
  fresh?: boolean;
15
20
  budgetTokens?: number;
16
21
  /**
@@ -35,14 +35,17 @@ export async function packCommand(rawOptions = {}) {
35
35
  progress.emit({
36
36
  stage: "pack",
37
37
  step: "start",
38
- message: scope
39
- ? `Packing codebase at ${scope}`
40
- : `Packing codebase at ${projectRoot}`,
38
+ message: rawOptions.remote
39
+ ? `Packing remote ${rawOptions.remote}${rawOptions.scope ? ` (scope: ${rawOptions.scope})` : ""}`
40
+ : scope
41
+ ? `Packing codebase at ${scope}`
42
+ : `Packing codebase at ${projectRoot}`,
41
43
  });
42
44
  const packResult = await runPack({
43
45
  projectRoot,
44
46
  paths,
45
47
  scope: rawOptions.scope,
48
+ remote: rawOptions.remote,
46
49
  ignoreRequirements: rawOptions.ignoreRequirements,
47
50
  });
48
51
  progress.emit({
@@ -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`);
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  name: cts-worker
3
- description: Generic worker subagent for the codebase-to-spec conversational orchestrator. Used for planner, specifier, editor, and reviewer dispatches. The per-dispatch persona is injected by the cts-worker-persona PreToolUse hook at dispatch time.
3
+ description: Generic worker subagent for the codebase-to-spec workflow. Handles planner, outline-reviewer, specifier, reviewer, and editor dispatches. Each worker fetches its own role-specific instructions from the CLI at dispatch time.
4
4
  tools: Bash, Read, Edit, Write, Glob, Grep
5
5
  ---
6
6
 
7
- You are a codebase-to-spec worker. Your dispatched prompt is composed externally and supplied to you at dispatch time. It will specify your role for this dispatch (planner, specifier, editor, or reviewer), the artifacts you're working with, and the output convention you must follow.
7
+ You are a codebase-to-spec worker. The prompt you receive tells you which CLI dispatch to fetch: it will instruct you to run `dotrequirements cts dispatch-context <dispatch-id>` (the exact invocation is given in your prompt), read the `"prompt"` field of the JSON it prints, and follow those instructions exactly.
8
8
 
9
- Follow the dispatched prompt exactly. Do not improvise behavior beyond what it asks. When your work is done, submit via the convention specified in the dispatch (typically a `dotrequirements cts submit` call).
9
+ Those fetched instructions are your real task — which files to read, what to write and where, and how to validate and style-check your output via Bash. Follow them exactly and do not improvise beyond what they ask. When your prompt also asks you to return a structured result, return exactly that shape.