compound-workflow 1.7.3 → 1.9.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 (92) hide show
  1. package/README.md +61 -69
  2. package/package.json +2 -8
  3. package/scripts/check-pack-readme.mjs +1 -16
  4. package/scripts/check-workflow-contracts.mjs +34 -44
  5. package/scripts/install-cli.mjs +273 -417
  6. package/src/AGENTS.md +59 -192
  7. package/src/{.agents/agents → agents}/research/best-practices-researcher.md +2 -2
  8. package/src/{.agents/commands → commands}/assess.md +4 -4
  9. package/src/commands/install.md +43 -0
  10. package/src/{.agents/commands → commands}/metrics.md +1 -1
  11. package/src/commands/workflow-agents.md +101 -0
  12. package/src/{.agents/commands → commands}/workflow-compound.md +1 -1
  13. package/src/{.agents/commands → commands}/workflow-plan.md +24 -33
  14. package/src/commands/workflow-work.md +836 -0
  15. package/src/{.agents/references → references}/README.md +1 -1
  16. package/src/{.agents/skills → skills}/capture-skill/SKILL.md +1 -1
  17. package/src/{.agents/skills → skills}/compound-docs/SKILL.md +6 -6
  18. package/src/{.agents/skills → skills}/compound-docs/references/yaml-schema.md +2 -2
  19. package/src/skills/setup-agents/SKILL.md +250 -0
  20. package/src/skills/standards/SKILL.md +79 -0
  21. package/src/skills/standards/references/architecture.md +228 -0
  22. package/src/skills/standards/references/code-quality.md +192 -0
  23. package/src/skills/standards/references/presentation.md +515 -0
  24. package/src/skills/standards/references/services.md +172 -0
  25. package/src/skills/standards/references/state-management.md +936 -0
  26. package/.claude-plugin/plugin.json +0 -7
  27. package/.cursor-plugin/plugin.json +0 -20
  28. package/.cursor-plugin/registration.json +0 -5
  29. package/scripts/check-version-parity.mjs +0 -36
  30. package/scripts/generate-platform-artifacts.mjs +0 -230
  31. package/src/.agents/commands/install.md +0 -51
  32. package/src/.agents/commands/workflow-work.md +0 -690
  33. package/src/.agents/registry.json +0 -48
  34. package/src/.agents/scripts/self-check.mjs +0 -227
  35. package/src/.agents/scripts/sync-opencode.mjs +0 -362
  36. package/src/.agents/skills/presentation-composability/SKILL.md +0 -72
  37. package/src/.agents/skills/react-ddd-mvc-frontend/SKILL.md +0 -51
  38. package/src/.agents/skills/react-ddd-mvc-frontend/references/feature-structure.md +0 -25
  39. package/src/.agents/skills/react-ddd-mvc-frontend/references/implementation-principles.md +0 -21
  40. package/src/.agents/skills/react-ddd-mvc-frontend/references/responsibility-gates.md +0 -41
  41. package/src/.agents/skills/react-ddd-mvc-frontend/references/source-map.md +0 -11
  42. package/src/.agents/skills/standards/SKILL.md +0 -747
  43. package/src/.agents/skills/xstate-actor-orchestration/SKILL.md +0 -197
  44. package/src/.agents/skills/xstate-actor-orchestration/agents/openai.yaml +0 -4
  45. package/src/.agents/skills/xstate-actor-orchestration/assets/statecharts/.gitkeep +0 -0
  46. package/src/.agents/skills/xstate-actor-orchestration/references/actor-system-patterns.md +0 -71
  47. package/src/.agents/skills/xstate-actor-orchestration/references/event-contracts.md +0 -73
  48. package/src/.agents/skills/xstate-actor-orchestration/references/functional-domain-patterns.md +0 -53
  49. package/src/.agents/skills/xstate-actor-orchestration/references/machine-structure-and-tags.md +0 -36
  50. package/src/.agents/skills/xstate-actor-orchestration/references/react-container-pattern.md +0 -45
  51. package/src/.agents/skills/xstate-actor-orchestration/references/reliability-observability.md +0 -39
  52. package/src/.agents/skills/xstate-actor-orchestration/references/skill-validation.md +0 -33
  53. package/src/.agents/skills/xstate-actor-orchestration/references/source-map.md +0 -44
  54. package/src/.agents/skills/xstate-actor-orchestration/references/statechart-review-and-signoff.md +0 -59
  55. package/src/.agents/skills/xstate-actor-orchestration/references/testing-strategy.md +0 -35
  56. package/src/.agents/skills/xstate-actor-orchestration/scripts/create-statechart-artifact.sh +0 -71
  57. package/src/.agents/skills/xstate-actor-orchestration/scripts/validate-skill.sh +0 -138
  58. package/src/generated/opencode.managed.json +0 -115
  59. /package/src/{.agents/agents → agents}/research/framework-docs-researcher.md +0 -0
  60. /package/src/{.agents/agents → agents}/research/git-history-analyzer.md +0 -0
  61. /package/src/{.agents/agents → agents}/research/learnings-researcher.md +0 -0
  62. /package/src/{.agents/agents → agents}/research/repo-research-analyst.md +0 -0
  63. /package/src/{.agents/agents → agents}/review/agent-native-reviewer.md +0 -0
  64. /package/src/{.agents/agents → agents}/review/planning-technical-reviewer.md +0 -0
  65. /package/src/{.agents/agents → agents}/workflow/bug-reproduction-validator.md +0 -0
  66. /package/src/{.agents/agents → agents}/workflow/lint.md +0 -0
  67. /package/src/{.agents/agents → agents}/workflow/spec-flow-analyzer.md +0 -0
  68. /package/src/{.agents/commands → commands}/test-browser.md +0 -0
  69. /package/src/{.agents/commands → commands}/workflow-brainstorm.md +0 -0
  70. /package/src/{.agents/commands → commands}/workflow-review.md +0 -0
  71. /package/src/{.agents/commands → commands}/workflow-tech-review.md +0 -0
  72. /package/src/{.agents/commands → commands}/workflow-triage.md +0 -0
  73. /package/src/{.agents/references → references}/standards/README.md +0 -0
  74. /package/src/{.agents/skills → skills}/agent-browser/SKILL.md +0 -0
  75. /package/src/{.agents/skills → skills}/audit-traceability/SKILL.md +0 -0
  76. /package/src/{.agents/skills → skills}/brainstorming/SKILL.md +0 -0
  77. /package/src/{.agents/skills → skills}/compound-docs/assets/critical-pattern-template.md +0 -0
  78. /package/src/{.agents/skills → skills}/compound-docs/assets/resolution-template.md +0 -0
  79. /package/src/{.agents/skills → skills}/compound-docs/schema.project.yaml +0 -0
  80. /package/src/{.agents/skills → skills}/compound-docs/schema.yaml +0 -0
  81. /package/src/{.agents/skills → skills}/data-foundations/SKILL.md +0 -0
  82. /package/src/{.agents/skills → skills}/document-review/SKILL.md +0 -0
  83. /package/src/{.agents/skills → skills}/file-todos/SKILL.md +0 -0
  84. /package/src/{.agents/skills → skills}/file-todos/assets/todo-template.md +0 -0
  85. /package/src/{.agents/skills → skills}/financial-workflow-integrity/SKILL.md +0 -0
  86. /package/src/{.agents/skills → skills}/git-worktree/SKILL.md +0 -0
  87. /package/src/{.agents/skills → skills}/pii-protection-prisma/SKILL.md +0 -0
  88. /package/src/{.agents/skills → skills}/process-metrics/SKILL.md +0 -0
  89. /package/src/{.agents/skills → skills}/process-metrics/assets/daily-template.md +0 -0
  90. /package/src/{.agents/skills → skills}/process-metrics/assets/monthly-template.md +0 -0
  91. /package/src/{.agents/skills → skills}/process-metrics/assets/weekly-template.md +0 -0
  92. /package/src/{.agents/skills → skills}/technical-review/SKILL.md +0 -0
@@ -2,195 +2,237 @@
2
2
  /**
3
3
  * compound-workflow install
4
4
  *
5
- * Native-only install: writes opencode.json from package metadata,
6
- * merges AGENTS.md, and ensures standard docs/todo directories.
5
+ * Copies agents, skills, and commands from the package into target platform dirs:
6
+ * .claude/agents/ — flat agent .md files (Claude Code)
7
+ * .cursor/agents/ — agents preserving subdirs (Cursor)
8
+ * .cursor/skills/ — skill dirs (Cursor)
9
+ * .cursor/commands/ — command .md files (Cursor)
10
+ * .agents/agents/ — agents (OpenCode / general)
11
+ * .agents/skills/ — skills (OpenCode / general)
12
+ * .agents/commands/ — commands (OpenCode / general)
7
13
  *
8
- * DECLARATIVE SOURCE OF TRUTH (no manual wiring):
9
- * - Commands: add/remove .md under src/.agents/commands/ (frontmatter: invocation, name, description).
10
- * Registry (src/.agents/registry.json) + generate-platform-artifacts → opencode.managed.json → install.
11
- * - Agents: add/remove .md under src/.agents/agents/ (frontmatter: name, description). Same pipeline.
12
- * - Skills: add/remove dir src/.agents/skills/<name>/SKILL.md. OpenCode uses skills path; install syncs
13
- * each skill into .cursor/skills/ (symlinks) so Cursor discovers them. Prune removes stale symlinks.
14
- * Run install (or npm install compound-workflow) after any change; no other registration needed.
14
+ * Also writes opencode.json, AGENTS.md, and standard docs directories.
15
+ *
16
+ * Usage:
17
+ * (automatic) npm install compound-workflow # runs via postinstall
18
+ * (manual) npx compound-workflow install [--root <projectDir>] [--dry-run]
15
19
  */
16
20
  import fs from "node:fs";
17
21
  import path from "node:path";
18
- import os from "node:os";
19
22
  import { fileURLToPath } from "node:url";
20
23
  import { spawnSync } from "node:child_process";
21
24
 
22
25
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
23
26
 
24
- function usage(exitCode = 0) {
25
- const msg = `
26
- Usage:
27
- (automatic) npm install compound-workflow # runs install via postinstall; no npx needed
28
- (manual) npx compound-workflow install [--root <projectDir>] [--dry-run] [--no-config] [--no-register-cursor] [--register-cursor]
29
-
30
- Install writes opencode.json (from package), merges AGENTS.md, creates standard
31
- docs/todos directories, and prompts for Repo Config Block (unless --no-config).
32
- When Cursor is detected (~/.cursor), registers the plugin so skills/commands appear.
33
-
34
- --root <dir> Project directory (default: cwd)
35
- --dry-run Print planned changes only
36
- --no-config Skip Repo Config Block reminder
37
- --no-register-cursor Do not register plugin with Cursor
38
- --register-cursor Force registration with Cursor even if ~/.cursor not found
39
- `;
40
- (exitCode === 0 ? console.log : console.error)(msg.trimStart());
41
- process.exit(exitCode);
42
- }
43
-
44
- function parseArgs(argv) {
45
- const out = { root: process.cwd(), dryRun: false, noConfig: false, noRegisterCursor: false, registerCursor: false };
46
- for (let i = 2; i < argv.length; i++) {
47
- const arg = argv[i];
48
- if (arg === "--dry-run") out.dryRun = true;
49
- else if (arg === "--no-config") out.noConfig = true;
50
- else if (arg === "--no-register-cursor") out.noRegisterCursor = true;
51
- else if (arg === "--register-cursor") out.registerCursor = true;
52
- else if (arg === "--root") {
53
- const value = argv[i + 1];
54
- if (!value) usage(1);
55
- out.root = value;
56
- i++;
57
- } else if (arg === "install") {
58
- continue;
59
- } else if (arg === "-h" || arg === "--help") usage(0);
60
- else usage(1);
61
- }
62
- return out;
63
- }
27
+ // ---------------------------------------------------------------------------
28
+ // Utilities
29
+ // ---------------------------------------------------------------------------
64
30
 
65
31
  function realpathSafe(p) {
66
- try {
67
- return fs.realpathSync(p);
68
- } catch {
69
- return path.resolve(p);
70
- }
32
+ try { return fs.realpathSync(p); } catch { return path.resolve(p); }
71
33
  }
72
34
 
35
+ const PACKAGE_ROOT = realpathSafe(path.join(__dirname, ".."));
36
+
73
37
  function hasCommand(cmd) {
74
38
  const checker = process.platform === "win32" ? "where" : "which";
75
- const result = spawnSync(checker, [cmd], { stdio: "ignore" });
76
- return result.status === 0;
39
+ return spawnSync(checker, [cmd], { stdio: "ignore" }).status === 0;
77
40
  }
78
41
 
79
42
  function stripJsonc(input) {
80
- let out = "";
81
- let i = 0;
82
- let inStr = false;
83
- let strQuote = "";
84
- let escape = false;
85
-
43
+ let out = "", i = 0, inStr = false, strQuote = "", escape = false;
86
44
  while (i < input.length) {
87
- const c = input[i];
88
- const n = input[i + 1];
45
+ const c = input[i], n = input[i + 1];
89
46
  if (inStr) {
90
47
  out += c;
91
48
  if (escape) escape = false;
92
49
  else if (c === "\\") escape = true;
93
50
  else if (c === strQuote) inStr = false;
94
- i++;
95
- continue;
51
+ i++; continue;
96
52
  }
97
-
98
- if (c === '"' || c === "'") {
99
- inStr = true;
100
- strQuote = c;
101
- out += c;
102
- i++;
103
- continue;
104
- }
105
-
106
- if (c === "/" && n === "/") {
107
- while (i < input.length && input[i] !== "\n") i++;
108
- continue;
109
- }
110
-
111
- if (c === "/" && n === "*") {
112
- i += 2;
113
- while (i < input.length && !(input[i] === "*" && input[i + 1] === "/")) i++;
114
- i += 2;
115
- continue;
116
- }
117
-
118
- out += c;
119
- i++;
53
+ if (c === '"' || c === "'") { inStr = true; strQuote = c; out += c; i++; continue; }
54
+ if (c === "/" && n === "/") { while (i < input.length && input[i] !== "\n") i++; continue; }
55
+ if (c === "/" && n === "*") { i += 2; while (i < input.length && !(input[i] === "*" && input[i + 1] === "/")) i++; i += 2; continue; }
56
+ out += c; i++;
120
57
  }
121
-
122
58
  return out;
123
59
  }
124
60
 
125
61
  function readJsonMaybe(fileAbs) {
126
62
  if (!fs.existsSync(fileAbs)) return null;
127
- const raw = fs.readFileSync(fileAbs, "utf8");
128
- return JSON.parse(stripJsonc(raw));
63
+ return JSON.parse(stripJsonc(fs.readFileSync(fileAbs, "utf8")));
129
64
  }
130
65
 
131
66
  function ensureObject(v) {
132
67
  return v && typeof v === "object" && !Array.isArray(v) ? v : {};
133
68
  }
134
69
 
135
- const PACKAGE_ROOT = realpathSafe(path.join(__dirname, ".."));
136
- const PACKAGE_AGENTS_ROOT = path.join(PACKAGE_ROOT, "src", ".agents");
137
- const GENERATED_MANIFEST_PATH = path.join(PACKAGE_ROOT, "src", "generated", "opencode.managed.json");
138
-
139
- function readGeneratedManifest() {
140
- if (!fs.existsSync(GENERATED_MANIFEST_PATH)) {
141
- throw new Error(
142
- `Missing generated OpenCode manifest at ${GENERATED_MANIFEST_PATH}. Run: npm run generate:artifacts`
143
- );
70
+ function parseFrontmatter(md) {
71
+ if (!md.startsWith("---\n") && !md.startsWith("---\r\n")) return {};
72
+ const end = md.indexOf("\n---", 4);
73
+ if (end === -1) return {};
74
+ const block = md.slice(4, end + 1);
75
+ const out = {};
76
+ for (const line of block.split(/\r?\n/)) {
77
+ const match = line.match(/^([A-Za-z0-9_-]+):\s*(.*)\s*$/);
78
+ if (!match) continue;
79
+ out[match[1]] = (match[2] ?? "").replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1");
144
80
  }
145
- const manifest = JSON.parse(fs.readFileSync(GENERATED_MANIFEST_PATH, "utf8"));
146
- if (!manifest?.commandRoot || !manifest?.agentRoot || !manifest?.skillsPath) {
147
- throw new Error("Invalid generated OpenCode manifest: missing root path fields.");
81
+ return out;
82
+ }
83
+
84
+ function walkFiles(dirAbs, ext) {
85
+ const out = [];
86
+ const stack = [dirAbs];
87
+ while (stack.length) {
88
+ const cur = stack.pop();
89
+ let entries;
90
+ try { entries = fs.readdirSync(cur, { withFileTypes: true }); } catch { continue; }
91
+ for (const e of entries) {
92
+ const p = path.join(cur, e.name);
93
+ if (e.isDirectory()) stack.push(p);
94
+ else if (e.isFile() && (!ext || p.endsWith(ext))) out.push(p);
95
+ }
148
96
  }
149
- if (!Array.isArray(manifest?.commands) || !Array.isArray(manifest?.agents)) {
150
- throw new Error("Invalid generated OpenCode manifest: commands/agents must be arrays.");
97
+ return out.sort();
98
+ }
99
+
100
+ function copyDirRecursive(srcDir, destDir) {
101
+ fs.mkdirSync(destDir, { recursive: true });
102
+ for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
103
+ const srcPath = path.join(srcDir, entry.name);
104
+ const destPath = path.join(destDir, entry.name);
105
+ if (entry.isDirectory()) {
106
+ copyDirRecursive(srcPath, destPath);
107
+ } else if (entry.isFile()) {
108
+ // Remove symlinks before copying so we write a real file, not through a broken link
109
+ try { if (fs.lstatSync(destPath).isSymbolicLink()) fs.rmSync(destPath, { force: true }); } catch { /* doesn't exist */ }
110
+ fs.copyFileSync(srcPath, destPath);
111
+ }
151
112
  }
152
- return manifest;
153
113
  }
154
114
 
155
- let GENERATED_MANIFEST;
156
- let PACKAGE_COMMAND_ROOT;
157
- let PACKAGE_AGENT_ROOT;
158
- let PACKAGE_SKILL_ROOT;
115
+ // ---------------------------------------------------------------------------
116
+ // Copy operations
117
+ // ---------------------------------------------------------------------------
159
118
 
160
- function getLegacyCommandRoots() {
161
- return [".agents/compound-workflow/commands", PACKAGE_COMMAND_ROOT, "src/.agents/commands"];
162
- }
163
- function getLegacyAgentRoots() {
164
- return [".agents/compound-workflow/agents", PACKAGE_AGENT_ROOT, "src/.agents/agents"];
119
+ /**
120
+ * Copy all .md files from srcDir (recursively) into destDir (flat).
121
+ * Prunes .md files in destDir not in current source.
122
+ */
123
+ function copyAgentsFlat(srcDir, destDir, dryRun, label) {
124
+ const files = walkFiles(srcDir, ".md");
125
+ const srcNames = new Set(files.map((f) => path.basename(f)));
126
+ if (dryRun) { console.log(`[dry-run] Would copy ${files.length} agents (flat) to ${label}`); return; }
127
+ fs.mkdirSync(destDir, { recursive: true });
128
+ try {
129
+ for (const e of fs.readdirSync(destDir, { withFileTypes: true })) {
130
+ if (e.name.endsWith(".md") && !srcNames.has(e.name)) fs.rmSync(path.join(destDir, e.name), { force: true });
131
+ }
132
+ } catch { /* ignore */ }
133
+ for (const f of files) {
134
+ const dest = path.join(destDir, path.basename(f));
135
+ try { if (fs.lstatSync(dest).isSymbolicLink()) fs.rmSync(dest, { force: true }); } catch { /* doesn't exist */ }
136
+ fs.copyFileSync(f, dest);
137
+ }
138
+ console.log(`Copied ${files.length} agents to ${label}`);
165
139
  }
166
140
 
167
- function managedCommandPath(entry) {
168
- const template = entry?.template;
169
- if (typeof template !== "string") return null;
170
- const match = template.match(/@([^\n\r]+)\nArguments: \$ARGUMENTS\n?$/m);
171
- if (!match) return null;
172
- return match[1].trim();
141
+ /**
142
+ * Copy srcDir recursively to destDir, pruning top-level subdirs no longer in source.
143
+ */
144
+ function copyAgentsRecursive(srcDir, destDir, dryRun, label) {
145
+ const files = walkFiles(srcDir, ".md");
146
+ if (dryRun) { console.log(`[dry-run] Would copy ${files.length} agents (recursive) to ${label}`); return; }
147
+ const validSubdirs = new Set();
148
+ for (const f of files) {
149
+ const sub = path.relative(srcDir, path.dirname(f));
150
+ if (sub && sub !== ".") validSubdirs.add(sub.split(path.sep)[0]);
151
+ }
152
+ fs.mkdirSync(destDir, { recursive: true });
153
+ try {
154
+ for (const e of fs.readdirSync(destDir, { withFileTypes: true })) {
155
+ if (e.isDirectory() && !validSubdirs.has(e.name)) fs.rmSync(path.join(destDir, e.name), { recursive: true, force: true });
156
+ }
157
+ } catch { /* ignore */ }
158
+ copyDirRecursive(srcDir, destDir);
159
+ console.log(`Copied ${files.length} agents to ${label}`);
173
160
  }
174
161
 
175
- function managedAgentPath(entry) {
176
- const prompt = entry?.prompt;
177
- if (typeof prompt !== "string") return null;
178
- const match = prompt.match(/^\{file:([^}]+)\}$/);
179
- return match ? match[1].trim() : null;
162
+ /**
163
+ * Copy skill directories (each containing SKILL.md) to destDir.
164
+ * Prunes skill dirs in destDir no longer in source.
165
+ */
166
+ function copySkills(srcDir, destDir, dryRun, label) {
167
+ if (!fs.existsSync(srcDir)) return;
168
+ const skillDirs = fs.readdirSync(srcDir, { withFileTypes: true })
169
+ .filter((e) => e.isDirectory() && fs.existsSync(path.join(srcDir, e.name, "SKILL.md")))
170
+ .map((e) => e.name);
171
+ if (dryRun) { console.log(`[dry-run] Would copy ${skillDirs.length} skills to ${label}`); return; }
172
+ const skillSet = new Set(skillDirs);
173
+ fs.mkdirSync(destDir, { recursive: true });
174
+ try {
175
+ for (const e of fs.readdirSync(destDir, { withFileTypes: true })) {
176
+ if (e.isDirectory() && fs.existsSync(path.join(destDir, e.name, "SKILL.md")) && !skillSet.has(e.name))
177
+ fs.rmSync(path.join(destDir, e.name), { recursive: true, force: true });
178
+ }
179
+ } catch { /* ignore */ }
180
+ for (const name of skillDirs) {
181
+ const dest = path.join(destDir, name);
182
+ fs.rmSync(dest, { recursive: true, force: true });
183
+ copyDirRecursive(path.join(srcDir, name), dest);
184
+ }
185
+ console.log(`Copied ${skillDirs.length} skills to ${label}`);
180
186
  }
181
187
 
182
- function isManagedCommandPath(commandPath) {
183
- return getLegacyCommandRoots().some((root) => commandPath.startsWith(`${root}/`));
188
+ /**
189
+ * Copy .md command files from srcDir to destDir (flat).
190
+ * Prunes .md files in destDir not in current source.
191
+ */
192
+ function copyCommands(srcDir, destDir, dryRun, label) {
193
+ if (!fs.existsSync(srcDir)) return;
194
+ const files = fs.readdirSync(srcDir, { withFileTypes: true })
195
+ .filter((e) => e.isFile() && e.name.endsWith(".md"))
196
+ .map((e) => e.name);
197
+ if (dryRun) { console.log(`[dry-run] Would copy ${files.length} commands to ${label}`); return; }
198
+ const fileSet = new Set(files);
199
+ fs.mkdirSync(destDir, { recursive: true });
200
+ try {
201
+ for (const e of fs.readdirSync(destDir, { withFileTypes: true })) {
202
+ if (e.name.endsWith(".md") && !fileSet.has(e.name)) fs.rmSync(path.join(destDir, e.name), { force: true });
203
+ }
204
+ } catch { /* ignore */ }
205
+ for (const name of files) fs.copyFileSync(path.join(srcDir, name), path.join(destDir, name));
206
+ console.log(`Copied ${files.length} commands to ${label}`);
184
207
  }
185
208
 
186
- function isManagedAgentPath(agentPath) {
187
- return getLegacyAgentRoots().some((root) => agentPath.startsWith(`${root}/`));
188
- }
209
+ // ---------------------------------------------------------------------------
210
+ // opencode.json
211
+ // ---------------------------------------------------------------------------
212
+
213
+ function writeOpenCodeJson(targetRoot, srcRoot, dryRun) {
214
+ const commandsDir = path.join(srcRoot, "commands");
215
+ const agentsDir = path.join(srcRoot, "agents");
216
+
217
+ const commands = [];
218
+ if (fs.existsSync(commandsDir)) {
219
+ for (const f of walkFiles(commandsDir, ".md")) {
220
+ const rel = path.relative(commandsDir, f).replaceAll(path.sep, "/");
221
+ const fm = parseFrontmatter(fs.readFileSync(f, "utf8"));
222
+ const id = fm.invocation || fm.name || path.basename(f, ".md");
223
+ commands.push({ id: id.trim(), description: (fm.description || id).trim(), rel });
224
+ }
225
+ }
189
226
 
190
- function writeOpenCodeJson(targetRoot, dryRun, isSelfInstall) {
191
- const commandRoot = isSelfInstall ? "src/.agents/commands" : PACKAGE_COMMAND_ROOT;
192
- const agentRoot = isSelfInstall ? "src/.agents/agents" : PACKAGE_AGENT_ROOT;
193
- const skillRoot = isSelfInstall ? "src/.agents/skills" : PACKAGE_SKILL_ROOT;
227
+ const agents = [];
228
+ if (fs.existsSync(agentsDir)) {
229
+ for (const f of walkFiles(agentsDir, ".md")) {
230
+ const rel = path.relative(agentsDir, f).replaceAll(path.sep, "/");
231
+ const fm = parseFrontmatter(fs.readFileSync(f, "utf8"));
232
+ const id = fm.name || path.basename(f, ".md");
233
+ agents.push({ id: id.trim(), description: (fm.description || id).trim(), rel });
234
+ }
235
+ }
194
236
 
195
237
  const opencodeAbs = path.join(targetRoot, "opencode.json");
196
238
  const existing = readJsonMaybe(opencodeAbs) ?? {};
@@ -199,112 +241,76 @@ function writeOpenCodeJson(targetRoot, dryRun, isSelfInstall) {
199
241
  next.$schema = next.$schema || "https://opencode.ai/config.json";
200
242
  next.skills = ensureObject(next.skills);
201
243
  next.skills.paths = Array.isArray(next.skills.paths) ? next.skills.paths : [];
202
-
203
- next.skills.paths = next.skills.paths.filter((p) => p !== ".agents/compound-workflow-skills");
204
- if (!next.skills.paths.includes(skillRoot)) {
205
- next.skills.paths.unshift(skillRoot);
206
- }
244
+ // Remove old package-relative paths, ensure .agents/skills is present
245
+ next.skills.paths = next.skills.paths.filter((p) => !p.includes("compound-workflow") && !p.includes("src/.agents"));
246
+ if (!next.skills.paths.includes(".agents/skills")) next.skills.paths.unshift(".agents/skills");
207
247
 
208
248
  next.command = ensureObject(next.command);
209
249
  next.agent = ensureObject(next.agent);
210
250
 
211
- const commands = GENERATED_MANIFEST.commands;
212
- const agents = GENERATED_MANIFEST.agents;
213
-
214
- for (const command of commands) {
215
- next.command[command.id] = {
216
- ...ensureObject(next.command[command.id]),
217
- description: command.description,
251
+ for (const cmd of commands) {
252
+ next.command[cmd.id] = {
253
+ ...ensureObject(next.command[cmd.id]),
254
+ description: cmd.description,
218
255
  agent: "build",
219
- template: `@AGENTS.md\n@${commandRoot}/${command.rel}\nArguments: $ARGUMENTS\n`,
256
+ template: `@AGENTS.md\n@.agents/commands/${cmd.rel}\nArguments: $ARGUMENTS\n`,
220
257
  };
221
258
  }
222
259
 
223
- const managedCommandTargets = new Set(
224
- commands.map((command) => `${commandRoot}/${command.rel}`)
225
- );
226
- for (const [id, entry] of Object.entries(next.command)) {
227
- const commandPath = managedCommandPath(entry);
228
- if (!commandPath || !isManagedCommandPath(commandPath)) continue;
229
- if (!managedCommandTargets.has(commandPath)) delete next.command[id];
230
- }
231
-
232
- for (const agent of agents) {
233
- next.agent[agent.id] = {
234
- ...ensureObject(next.agent[agent.id]),
235
- description: agent.description,
260
+ for (const ag of agents) {
261
+ next.agent[ag.id] = {
262
+ ...ensureObject(next.agent[ag.id]),
263
+ description: ag.description,
236
264
  mode: "subagent",
237
- prompt: `{file:${agentRoot}/${agent.rel}}`,
238
- permission: { ...ensureObject(next.agent[agent.id]?.permission), edit: "deny" },
265
+ prompt: `{file:.agents/agents/${ag.rel}}`,
266
+ permission: { ...ensureObject(next.agent[ag.id]?.permission), edit: "deny" },
239
267
  };
240
268
  }
241
269
 
242
- const managedAgentTargets = new Set(
243
- agents.map((agent) => `${agentRoot}/${agent.rel}`)
244
- );
245
- for (const [id, entry] of Object.entries(next.agent)) {
246
- const agentPath = managedAgentPath(entry);
247
- if (!agentPath || !isManagedAgentPath(agentPath)) continue;
248
- if (!managedAgentTargets.has(agentPath)) delete next.agent[id];
249
- }
250
-
251
- const out = JSON.stringify(next, null, 2) + "\n";
252
- if (dryRun) {
253
- console.log("[dry-run] Would write opencode.json:", opencodeAbs);
254
- return;
255
- }
256
-
257
- fs.writeFileSync(opencodeAbs, out, "utf8");
258
- console.log("Wrote:", opencodeAbs);
270
+ if (dryRun) { console.log("[dry-run] Would write opencode.json"); return; }
271
+ fs.writeFileSync(opencodeAbs, JSON.stringify(next, null, 2) + "\n", "utf8");
272
+ console.log("Wrote: opencode.json");
259
273
  }
260
274
 
275
+ // ---------------------------------------------------------------------------
276
+ // AGENTS.md merge
277
+ // ---------------------------------------------------------------------------
278
+
261
279
  function extractRepoConfigBlock(md) {
262
280
  const match = md.match(/(### Repo Config Block[^\n]*\n)?\s*```yaml\n([\s\S]*?)```/);
263
281
  if (!match) return { block: null, rest: md };
264
- const full = match[0];
265
282
  const block = match[2].trim();
266
- const rest = md.replace(full, "").replace(/\n{3,}/g, "\n\n").trim();
283
+ const rest = md.replace(match[0], "").replace(/\n{3,}/g, "\n\n").trim();
267
284
  return { block, rest };
268
285
  }
269
286
 
270
- function mergeAgentsMd(templateMd, existingMd) {
271
- const { block: existingBlock } = existingMd
272
- ? extractRepoConfigBlock(existingMd)
273
- : { block: null };
287
+ function writeAgentsMd(targetRoot, packageRoot, dryRun) {
288
+ const templatePath = path.join(packageRoot, "src", "AGENTS.md");
289
+ const targetPath = path.join(targetRoot, "AGENTS.md");
290
+ const templateMd = fs.readFileSync(templatePath, "utf8");
291
+ const existingMd = fs.existsSync(targetPath) ? fs.readFileSync(targetPath, "utf8") : null;
292
+
293
+ const { block: existingBlock } = existingMd ? extractRepoConfigBlock(existingMd) : { block: null };
274
294
  const { rest: templateRest } = extractRepoConfigBlock(templateMd);
275
295
 
276
296
  let out = templateRest;
277
297
  if (existingBlock) {
278
298
  const repoSection = `### Repo Config Block (Optional)\n\n\`\`\`yaml\n${existingBlock}\n\`\`\`\n`;
279
-
280
299
  if (!out.includes("### Repo Config Block")) {
281
- out = out.replace(
282
- "## Repo Configuration (Optional)",
283
- `## Repo Configuration (Optional)\n\n${repoSection}`
284
- );
300
+ out = out.replace("## Repo Configuration (Optional)", `## Repo Configuration (Optional)\n\n${repoSection}`);
285
301
  } else {
286
302
  out = out.replace(/### Repo Config Block[^\n]*\n\s*```yaml\n[\s\S]*?```/, repoSection);
287
303
  }
288
304
  }
289
305
 
290
- return out;
306
+ if (dryRun) { console.log("[dry-run] Would write AGENTS.md"); return; }
307
+ fs.writeFileSync(targetPath, out, "utf8");
308
+ console.log("Wrote: AGENTS.md");
291
309
  }
292
310
 
293
- function writeAgentsMd(targetRoot, dryRun) {
294
- const templatePath = path.join(PACKAGE_ROOT, "src", "AGENTS.md");
295
- const targetPath = path.join(targetRoot, "AGENTS.md");
296
- const templateMd = fs.readFileSync(templatePath, "utf8");
297
- const existingMd = fs.existsSync(targetPath) ? fs.readFileSync(targetPath, "utf8") : null;
298
- const merged = mergeAgentsMd(templateMd, existingMd);
299
-
300
- if (dryRun) {
301
- console.log("[dry-run] Would write AGENTS.md:", targetPath);
302
- return;
303
- }
304
-
305
- fs.writeFileSync(targetPath, merged, "utf8");
306
- console.log("Wrote:", targetPath);
307
- }
311
+ // ---------------------------------------------------------------------------
312
+ // Standard dirs
313
+ // ---------------------------------------------------------------------------
308
314
 
309
315
  const DIRS = [
310
316
  "docs/brainstorms",
@@ -319,240 +325,90 @@ const DIRS = [
319
325
  function ensureDirs(targetRoot, dryRun) {
320
326
  for (const d of DIRS) {
321
327
  const abs = path.join(targetRoot, d);
322
- if (dryRun && !fs.existsSync(abs)) {
323
- console.log("[dry-run] Would create:", d);
324
- } else if (!fs.existsSync(abs)) {
325
- fs.mkdirSync(abs, { recursive: true });
326
- console.log("Created:", d);
328
+ if (!fs.existsSync(abs)) {
329
+ if (dryRun) console.log("[dry-run] Would create:", d);
330
+ else { fs.mkdirSync(abs, { recursive: true }); console.log("Created:", d); }
327
331
  }
328
332
  }
329
333
  }
330
334
 
331
- /**
332
- * Writes .cursor-plugin/plugin.json and .claude-plugin/plugin.json at targetRoot.
333
- * Paths (commands, agents, skills) in the written manifests are relative to project root
334
- * (parent of .cursor-plugin / .claude-plugin) so Cursor/Claude resolve assets correctly.
335
- */
336
- function writePluginManifests(targetRoot, dryRun, isSelfInstall) {
337
- const pathsBase = isSelfInstall ? "./src/.agents" : "./node_modules/compound-workflow/src/.agents";
338
- const cursorSrc = path.join(PACKAGE_ROOT, ".cursor-plugin", "plugin.json");
339
- const claudeSrc = path.join(PACKAGE_ROOT, ".claude-plugin", "plugin.json");
340
- const cursorManifest = readJsonMaybe(cursorSrc);
341
- const claudeManifest = readJsonMaybe(claudeSrc);
342
- if (!cursorManifest || !claudeManifest) return;
343
-
344
- // All Cursor paths point directly at the package source — no symlink indirection.
345
- // This ensures frontmatter (descriptions) is parsed correctly by Cursor for all components.
346
- const cursorOut = {
347
- ...cursorManifest,
348
- commands: `${pathsBase}/commands`,
349
- agents: `${pathsBase}/agents`,
350
- skills: `${pathsBase}/skills`,
351
- };
352
- // Claude Code only accepts name, description, author in plugin.json.
353
- // Agents are discovered from the adjacent agents/ directory (flat .md files).
354
- const claudeOut = {
355
- name: claudeManifest.name,
356
- description: claudeManifest.description,
357
- author: claudeManifest.author,
358
- };
359
- const cursorDir = path.join(targetRoot, ".cursor-plugin");
360
- const claudeDir = path.join(targetRoot, ".claude-plugin");
361
-
362
- const installPathAbs = realpathSafe(targetRoot);
363
- const registrationDescriptor = {
364
- pluginId: "compound-workflow@local",
365
- scope: "user",
366
- installPath: installPathAbs,
367
- };
368
-
369
- if (dryRun) {
370
- console.log("[dry-run] Would write .cursor-plugin/plugin.json, .claude-plugin/plugin.json, .cursor-plugin/registration.json" + (isSelfInstall ? "" : ", .claude-plugin/marketplace.json"));
371
- return;
372
- }
373
- fs.mkdirSync(cursorDir, { recursive: true });
374
- fs.mkdirSync(claudeDir, { recursive: true });
375
- fs.writeFileSync(path.join(cursorDir, "plugin.json"), JSON.stringify(cursorOut, null, 2) + "\n", "utf8");
376
- fs.writeFileSync(path.join(claudeDir, "plugin.json"), JSON.stringify(claudeOut, null, 2) + "\n", "utf8");
377
- fs.writeFileSync(path.join(cursorDir, "registration.json"), JSON.stringify(registrationDescriptor, null, 2) + "\n", "utf8");
378
-
379
- // Sync flat agent symlinks into .claude-plugin/agents/ so Claude Code discovers them.
380
- // Claude Code only scans the root of the agents/ directory (not subdirectories).
381
- const claudeAgentsDir = path.join(claudeDir, "agents");
382
- const packageAgentsDirAbs = isSelfInstall
383
- ? path.join(PACKAGE_ROOT, "src", ".agents", "agents")
384
- : path.join(targetRoot, "node_modules", "compound-workflow", "src", ".agents", "agents");
385
- if (fs.existsSync(packageAgentsDirAbs)) {
386
- fs.mkdirSync(claudeAgentsDir, { recursive: true });
387
- const agentBasenames = new Set(GENERATED_MANIFEST.agents.map((a) => path.basename(a.rel)));
388
- // Prune stale symlinks
389
- try {
390
- for (const entry of fs.readdirSync(claudeAgentsDir, { withFileTypes: true })) {
391
- if (!agentBasenames.has(entry.name)) {
392
- fs.rmSync(path.join(claudeAgentsDir, entry.name), { force: true });
393
- }
394
- }
395
- } catch { /* ignore */ }
396
- for (const agent of GENERATED_MANIFEST.agents) {
397
- const linkPath = path.join(claudeAgentsDir, path.basename(agent.rel));
398
- const targetPath = path.join(packageAgentsDirAbs, agent.rel);
399
- try {
400
- if (fs.lstatSync(linkPath)) fs.rmSync(linkPath, { force: true });
401
- } catch { /* doesn't exist */ }
402
- try {
403
- fs.symlinkSync(targetPath, linkPath);
404
- } catch (err) {
405
- console.warn("[claude] Could not symlink agent", agent.id, err.message);
406
- }
407
- }
408
- }
409
-
410
- // Claude Code 2.1.x+ no longer loads from installed_plugins.json; it requires marketplace flow.
411
- // Write a project-level marketplace so user can: /plugin marketplace add . then /plugin install compound-workflow@compound-workflow-local
412
- if (!isSelfInstall) {
413
- const marketplaceManifest = {
414
- name: "compound-workflow-local",
415
- owner: { name: "Compound Workflow" },
416
- plugins: [
417
- {
418
- name: "compound-workflow",
419
- source: "./node_modules/compound-workflow",
420
- description: claudeOut.description || "Clarify → plan → execute → verify → capture workflow.",
421
- },
422
- ],
423
- };
424
- fs.writeFileSync(path.join(claudeDir, "marketplace.json"), JSON.stringify(marketplaceManifest, null, 2) + "\n", "utf8");
425
- }
426
- console.log("Wrote: .cursor-plugin/plugin.json, .claude-plugin/plugin.json, .cursor-plugin/registration.json" + (isSelfInstall ? "" : ", .claude-plugin/marketplace.json"));
427
- }
335
+ // ---------------------------------------------------------------------------
336
+ // CLI args
337
+ // ---------------------------------------------------------------------------
428
338
 
339
+ function usage(exitCode = 0) {
340
+ const msg = `
341
+ Usage:
342
+ (automatic) npm install compound-workflow # runs install via postinstall
343
+ (manual) npx compound-workflow install [--root <projectDir>] [--dry-run]
429
344
 
345
+ Copies agents, skills, and commands into .claude/, .cursor/, and .agents/.
346
+ Also writes opencode.json, AGENTS.md, and standard docs directories.
430
347
 
431
- function cursorDetected() {
432
- return fs.existsSync(path.join(os.homedir(), ".cursor"));
348
+ --root <dir> Project directory (default: cwd)
349
+ --dry-run Print planned changes only
350
+ `;
351
+ (exitCode === 0 ? console.log : console.error)(msg.trimStart());
352
+ process.exit(exitCode);
433
353
  }
434
354
 
435
-
436
- function applyCursorRegistration(targetRoot, dryRun, noRegisterCursor, forceRegister, isSelfInstall) {
437
- const projectRoot = isSelfInstall ? PACKAGE_ROOT : targetRoot;
438
- const pluginId = "compound-workflow@compound-workflow-local";
439
-
440
- if (dryRun) {
441
- console.log("[dry-run] Would register Claude plugin (project-scoped) at:", projectRoot);
442
- return;
443
- }
444
-
445
- // Registration is strictly project-scoped: write only to <project>/.claude/settings.json.
446
- // Never touch ~/.claude — Claude Code manages user-level plugin state itself.
447
- const projectSettingsPath = path.join(projectRoot, ".claude", "settings.json");
448
- let projectSettings = {};
449
- if (fs.existsSync(projectSettingsPath)) {
450
- try { projectSettings = readJsonMaybe(projectSettingsPath) ?? {}; } catch { projectSettings = {}; }
451
- }
452
- projectSettings.enabledPlugins = ensureObject(projectSettings.enabledPlugins);
453
- projectSettings.enabledPlugins[pluginId] = true;
454
- // Remove stale/invalid marketplace keys left by earlier install methods
455
- if (projectSettings.extraKnownMarketplaces?.["compound-workflow"]) {
456
- delete projectSettings.extraKnownMarketplaces["compound-workflow"];
457
- }
458
- projectSettings.extraKnownMarketplaces = ensureObject(projectSettings.extraKnownMarketplaces);
459
- projectSettings.extraKnownMarketplaces["compound-workflow-local"] = {
460
- source: { source: "file", path: "." },
461
- };
462
- fs.mkdirSync(path.join(projectRoot, ".claude"), { recursive: true });
463
- fs.writeFileSync(projectSettingsPath, JSON.stringify(projectSettings, null, 2) + "\n", "utf8");
464
-
465
- console.log("Registered compound-workflow with Claude Code (project-scoped).");
466
- if (!isSelfInstall) {
467
- console.log(" Claude Code 2.1+: open /plugin, go to Discover; install 'compound-workflow' from marketplace 'compound-workflow-local', or run: claude --plugin-dir ./node_modules/compound-workflow");
468
- }
469
- console.log(" Restart Claude Code; enable 'Include third-party Plugins, Skills, and other configs' in Settings if needed.");
470
-
471
- if (noRegisterCursor && !forceRegister) return;
472
- const shouldApply = forceRegister || (cursorDetected() && !noRegisterCursor);
473
- if (!shouldApply) {
474
- console.log("[cursor] Cursor not detected; skipped Cursor plugin registration. Use --register-cursor to force.");
475
- return;
355
+ function parseArgs(argv) {
356
+ const out = { root: process.cwd(), dryRun: false };
357
+ for (let i = 2; i < argv.length; i++) {
358
+ const arg = argv[i];
359
+ if (arg === "--dry-run") out.dryRun = true;
360
+ else if (arg === "--root") { const v = argv[++i]; if (!v) usage(1); out.root = v; }
361
+ else if (arg === "install") { /* subcommand, ignore */ }
362
+ else if (arg === "-h" || arg === "--help") usage(0);
363
+ else usage(1);
476
364
  }
477
- const registrationPath = path.join(targetRoot, ".cursor-plugin", "registration.json");
478
- if (!fs.existsSync(registrationPath)) return;
479
- console.log("Registered compound-workflow with Cursor. Restart Cursor; enable 'Include third-party Plugins, Skills, and other configs' in Settings if needed.");
365
+ return out;
480
366
  }
481
367
 
482
- function reportOpenCodeIntegration(targetRoot, dryRun) {
483
- if (dryRun) {
484
- console.log("[dry-run] OpenCode integration check skipped (state would be updated by install).");
485
- return;
486
- }
487
-
488
- const opencodeAbs = path.join(targetRoot, "opencode.json");
489
- const opencode = readJsonMaybe(opencodeAbs) ?? {};
490
- const skillPaths = Array.isArray(opencode?.skills?.paths) ? opencode.skills.paths : [];
491
- const hasSkillPath = skillPaths.includes(PACKAGE_SKILL_ROOT) || skillPaths.includes("src/.agents/skills");
492
-
493
- console.log(
494
- "OpenCode integration:",
495
- hasSkillPath ? "ok" : "incomplete",
496
- `(skills.path=${hasSkillPath ? "yes" : "no"})`
497
- );
498
- }
368
+ // ---------------------------------------------------------------------------
369
+ // Main
370
+ // ---------------------------------------------------------------------------
499
371
 
500
372
  function main() {
501
373
  const args = parseArgs(process.argv);
502
374
  const targetRoot = realpathSafe(args.root);
375
+ const isSelfInstall = targetRoot === PACKAGE_ROOT;
503
376
 
504
- const genScript = path.join(PACKAGE_ROOT, "scripts", "generate-platform-artifacts.mjs");
505
- if (fs.existsSync(genScript)) {
506
- console.log("[compound-workflow] Regenerating manifest from package source...");
507
- const result = spawnSync(process.execPath, [genScript], {
508
- cwd: PACKAGE_ROOT,
509
- stdio: "pipe",
510
- encoding: "utf8",
511
- });
512
- if (result.status !== 0) {
513
- console.error("Failed to regenerate manifest:", result.stderr || result.error || "unknown");
514
- process.exit(1);
515
- }
516
- }
377
+ const packageSrc = isSelfInstall
378
+ ? path.join(PACKAGE_ROOT, "src")
379
+ : path.join(targetRoot, "node_modules", "compound-workflow", "src");
517
380
 
518
- try {
519
- GENERATED_MANIFEST = readGeneratedManifest();
520
- PACKAGE_COMMAND_ROOT = GENERATED_MANIFEST.commandRoot;
521
- PACKAGE_AGENT_ROOT = GENERATED_MANIFEST.agentRoot;
522
- PACKAGE_SKILL_ROOT = GENERATED_MANIFEST.skillsPath;
523
- } catch (err) {
524
- console.error("Error: OpenCode manifest not found. Run 'npm run generate:artifacts' in the package or reinstall compound-workflow.");
381
+ if (!isSelfInstall && !fs.existsSync(packageSrc) && !args.dryRun) {
382
+ console.error("Error: compound-workflow not found in project. Run: npm install compound-workflow");
525
383
  process.exit(2);
526
384
  }
527
385
 
528
- if (!fs.existsSync(PACKAGE_AGENTS_ROOT)) {
529
- console.error("Error: package agents dir not found:", PACKAGE_AGENTS_ROOT);
530
- process.exit(2);
531
- }
386
+ const srcAgents = path.join(packageSrc, "agents");
387
+ const srcSkills = path.join(packageSrc, "skills");
388
+ const srcCommands = path.join(packageSrc, "commands");
532
389
 
533
- const isSelfInstall = realpathSafe(targetRoot) === realpathSafe(PACKAGE_ROOT);
534
- const pkgInTarget = path.join(targetRoot, "node_modules", "compound-workflow");
535
- if (!isSelfInstall && !fs.existsSync(pkgInTarget) && !args.dryRun) {
536
- console.error("Error: compound-workflow not found in project. Run: npm install compound-workflow");
537
- process.exit(2);
538
- }
390
+ console.log("Target:", targetRoot);
391
+ console.log("Package:", PACKAGE_ROOT);
392
+ console.log("OpenCode CLI:", hasCommand("opencode") ? "yes" : "no");
539
393
 
540
- console.log("Target root:", targetRoot);
541
- console.log("Package root:", PACKAGE_ROOT);
542
- console.log("OpenCode CLI detected:", hasCommand("opencode") ? "yes" : "no");
394
+ // .claude/agents/ — flat (Claude Code requires flat .md files)
395
+ copyAgentsFlat(srcAgents, path.join(targetRoot, ".claude", "agents"), args.dryRun, ".claude/agents/");
543
396
 
544
- writeOpenCodeJson(targetRoot, args.dryRun, isSelfInstall);
545
- writePluginManifests(targetRoot, args.dryRun, isSelfInstall);
546
- applyCursorRegistration(targetRoot, args.dryRun, args.noRegisterCursor, args.registerCursor, isSelfInstall);
547
- reportOpenCodeIntegration(targetRoot, args.dryRun);
548
- writeAgentsMd(targetRoot, args.dryRun);
549
- ensureDirs(targetRoot, args.dryRun);
397
+ // .cursor/agents/, .cursor/skills/, .cursor/commands/
398
+ copyAgentsRecursive(srcAgents, path.join(targetRoot, ".cursor", "agents"), args.dryRun, ".cursor/agents/");
399
+ copySkills(srcSkills, path.join(targetRoot, ".cursor", "skills"), args.dryRun, ".cursor/skills/");
400
+ copyCommands(srcCommands, path.join(targetRoot, ".cursor", "commands"), args.dryRun, ".cursor/commands/");
550
401
 
551
- if (!args.noConfig && !args.dryRun && process.stdin.isTTY) {
552
- console.log("\nRepo Config: edit AGENTS.md to set default_branch, test_command, lint_command, dev_server_url.");
553
- }
402
+ // .agents/agents/, .agents/skills/, .agents/commands/ (OpenCode / general)
403
+ copyAgentsRecursive(srcAgents, path.join(targetRoot, ".agents", "agents"), args.dryRun, ".agents/agents/");
404
+ copySkills(srcSkills, path.join(targetRoot, ".agents", "skills"), args.dryRun, ".agents/skills/");
405
+ copyCommands(srcCommands, path.join(targetRoot, ".agents", "commands"), args.dryRun, ".agents/commands/");
406
+
407
+ writeOpenCodeJson(targetRoot, packageSrc, args.dryRun);
408
+ writeAgentsMd(targetRoot, PACKAGE_ROOT, args.dryRun);
409
+ ensureDirs(targetRoot, args.dryRun);
554
410
 
555
- console.log("\nDone. Run opencode debug config to verify.");
411
+ console.log("\nDone.");
556
412
  }
557
413
 
558
414
  main();