compound-workflow 1.7.3 → 1.8.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.
- package/.cursor-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/scripts/install-cli.mjs +164 -26
package/package.json
CHANGED
package/scripts/install-cli.mjs
CHANGED
|
@@ -9,8 +9,12 @@
|
|
|
9
9
|
* - Commands: add/remove .md under src/.agents/commands/ (frontmatter: invocation, name, description).
|
|
10
10
|
* Registry (src/.agents/registry.json) + generate-platform-artifacts → opencode.managed.json → install.
|
|
11
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
|
|
13
|
-
*
|
|
12
|
+
* - Skills: add/remove dir src/.agents/skills/<name>/SKILL.md. OpenCode uses skills path.
|
|
13
|
+
*
|
|
14
|
+
* PLATFORM STRATEGY:
|
|
15
|
+
* - Cursor: copies files into .cursor/skills/, .cursor/commands/, .cursor/agents/ for native discovery.
|
|
16
|
+
* - Claude Code: marketplace flow via .claude-plugin/ + project .claude/settings.json.
|
|
17
|
+
* - OpenCode: opencode.json with paths into package source.
|
|
14
18
|
* Run install (or npm install compound-workflow) after any change; no other registration needed.
|
|
15
19
|
*/
|
|
16
20
|
import fs from "node:fs";
|
|
@@ -25,30 +29,26 @@ function usage(exitCode = 0) {
|
|
|
25
29
|
const msg = `
|
|
26
30
|
Usage:
|
|
27
31
|
(automatic) npm install compound-workflow # runs install via postinstall; no npx needed
|
|
28
|
-
(manual) npx compound-workflow install [--root <projectDir>] [--dry-run] [--no-config]
|
|
32
|
+
(manual) npx compound-workflow install [--root <projectDir>] [--dry-run] [--no-config]
|
|
29
33
|
|
|
30
34
|
Install writes opencode.json (from package), merges AGENTS.md, creates standard
|
|
31
|
-
docs/todos directories,
|
|
32
|
-
|
|
35
|
+
docs/todos directories, copies skills/commands/agents into .cursor/ for Cursor,
|
|
36
|
+
and registers the Claude Code plugin (project-scoped).
|
|
33
37
|
|
|
34
38
|
--root <dir> Project directory (default: cwd)
|
|
35
39
|
--dry-run Print planned changes only
|
|
36
40
|
--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
41
|
`;
|
|
40
42
|
(exitCode === 0 ? console.log : console.error)(msg.trimStart());
|
|
41
43
|
process.exit(exitCode);
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
function parseArgs(argv) {
|
|
45
|
-
const out = { root: process.cwd(), dryRun: false, noConfig: false
|
|
47
|
+
const out = { root: process.cwd(), dryRun: false, noConfig: false };
|
|
46
48
|
for (let i = 2; i < argv.length; i++) {
|
|
47
49
|
const arg = argv[i];
|
|
48
50
|
if (arg === "--dry-run") out.dryRun = true;
|
|
49
51
|
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
52
|
else if (arg === "--root") {
|
|
53
53
|
const value = argv[i + 1];
|
|
54
54
|
if (!value) usage(1);
|
|
@@ -428,12 +428,160 @@ function writePluginManifests(targetRoot, dryRun, isSelfInstall) {
|
|
|
428
428
|
|
|
429
429
|
|
|
430
430
|
|
|
431
|
+
function copyDirRecursive(srcDir, destDir) {
|
|
432
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
433
|
+
for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
|
|
434
|
+
const srcPath = path.join(srcDir, entry.name);
|
|
435
|
+
const destPath = path.join(destDir, entry.name);
|
|
436
|
+
if (entry.isDirectory()) {
|
|
437
|
+
copyDirRecursive(srcPath, destPath);
|
|
438
|
+
} else if (entry.isFile()) {
|
|
439
|
+
fs.copyFileSync(srcPath, destPath);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Cursor discovers skills from .cursor/skills/ (each subdir with SKILL.md).
|
|
446
|
+
* Copy package skills so Cursor finds them with full frontmatter metadata.
|
|
447
|
+
*/
|
|
448
|
+
function copyCursorSkills(targetRoot, dryRun, isSelfInstall) {
|
|
449
|
+
const packageSkillsAbs = path.join(
|
|
450
|
+
isSelfInstall ? PACKAGE_ROOT : path.join(targetRoot, "node_modules", "compound-workflow"),
|
|
451
|
+
"src", ".agents", "skills"
|
|
452
|
+
);
|
|
453
|
+
if (!fs.existsSync(packageSkillsAbs)) return;
|
|
454
|
+
|
|
455
|
+
const cursorSkillsDir = path.join(targetRoot, ".cursor", "skills");
|
|
456
|
+
const skillDirs = fs.readdirSync(packageSkillsAbs, { withFileTypes: true })
|
|
457
|
+
.filter((e) => e.isDirectory() && fs.existsSync(path.join(packageSkillsAbs, e.name, "SKILL.md")))
|
|
458
|
+
.map((e) => e.name);
|
|
459
|
+
if (skillDirs.length === 0) return;
|
|
460
|
+
|
|
461
|
+
if (dryRun) {
|
|
462
|
+
console.log("[dry-run] Would copy", skillDirs.length, "skills into .cursor/skills/");
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
fs.mkdirSync(cursorSkillsDir, { recursive: true });
|
|
467
|
+
const skillSet = new Set(skillDirs);
|
|
468
|
+
|
|
469
|
+
// Prune stale entries that we manage (contain SKILL.md) but are no longer in the package
|
|
470
|
+
try {
|
|
471
|
+
for (const entry of fs.readdirSync(cursorSkillsDir, { withFileTypes: true })) {
|
|
472
|
+
if (!entry.isDirectory()) continue;
|
|
473
|
+
const skillMd = path.join(cursorSkillsDir, entry.name, "SKILL.md");
|
|
474
|
+
if (!fs.existsSync(skillMd)) continue;
|
|
475
|
+
if (!skillSet.has(entry.name)) {
|
|
476
|
+
fs.rmSync(path.join(cursorSkillsDir, entry.name), { recursive: true, force: true });
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
} catch { /* ignore */ }
|
|
480
|
+
|
|
481
|
+
for (const name of skillDirs) {
|
|
482
|
+
const dest = path.join(cursorSkillsDir, name);
|
|
483
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
484
|
+
copyDirRecursive(path.join(packageSkillsAbs, name), dest);
|
|
485
|
+
}
|
|
486
|
+
console.log("Copied", skillDirs.length, "skills to .cursor/skills/");
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Cursor discovers commands from .cursor/commands/ (.md files).
|
|
491
|
+
* Copy package commands so Cursor finds them with full frontmatter metadata.
|
|
492
|
+
*/
|
|
493
|
+
function copyCursorCommands(targetRoot, dryRun, isSelfInstall) {
|
|
494
|
+
const packageCommandsAbs = path.join(
|
|
495
|
+
isSelfInstall ? PACKAGE_ROOT : path.join(targetRoot, "node_modules", "compound-workflow"),
|
|
496
|
+
"src", ".agents", "commands"
|
|
497
|
+
);
|
|
498
|
+
if (!fs.existsSync(packageCommandsAbs)) return;
|
|
499
|
+
|
|
500
|
+
const cursorCommandsDir = path.join(targetRoot, ".cursor", "commands");
|
|
501
|
+
const commandFiles = fs.readdirSync(packageCommandsAbs, { withFileTypes: true })
|
|
502
|
+
.filter((e) => e.isFile() && e.name.endsWith(".md"))
|
|
503
|
+
.map((e) => e.name);
|
|
504
|
+
if (commandFiles.length === 0) return;
|
|
505
|
+
|
|
506
|
+
if (dryRun) {
|
|
507
|
+
console.log("[dry-run] Would copy", commandFiles.length, "commands into .cursor/commands/");
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
fs.mkdirSync(cursorCommandsDir, { recursive: true });
|
|
512
|
+
const commandSet = new Set(commandFiles);
|
|
513
|
+
|
|
514
|
+
// Prune stale .md files and old symlinks that we previously managed
|
|
515
|
+
try {
|
|
516
|
+
for (const entry of fs.readdirSync(cursorCommandsDir, { withFileTypes: true })) {
|
|
517
|
+
if (!entry.name.endsWith(".md")) continue;
|
|
518
|
+
if (entry.isSymbolicLink() || !commandSet.has(entry.name)) {
|
|
519
|
+
fs.rmSync(path.join(cursorCommandsDir, entry.name), { force: true });
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
} catch { /* ignore */ }
|
|
523
|
+
|
|
524
|
+
for (const name of commandFiles) {
|
|
525
|
+
fs.copyFileSync(path.join(packageCommandsAbs, name), path.join(cursorCommandsDir, name));
|
|
526
|
+
}
|
|
527
|
+
console.log("Copied", commandFiles.length, "commands to .cursor/commands/");
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Cursor discovers agents from .cursor/agents/ (.md files, supports subdirs).
|
|
532
|
+
* Copy package agents preserving subdirectory structure (research/, review/, workflow/).
|
|
533
|
+
*/
|
|
534
|
+
function copyCursorAgents(targetRoot, dryRun, isSelfInstall) {
|
|
535
|
+
const packageAgentsAbs = path.join(
|
|
536
|
+
isSelfInstall ? PACKAGE_ROOT : path.join(targetRoot, "node_modules", "compound-workflow"),
|
|
537
|
+
"src", ".agents", "agents"
|
|
538
|
+
);
|
|
539
|
+
if (!fs.existsSync(packageAgentsAbs)) return;
|
|
540
|
+
|
|
541
|
+
const cursorAgentsDir = path.join(targetRoot, ".cursor", "agents");
|
|
542
|
+
const agentRels = GENERATED_MANIFEST.agents.map((a) => a.rel);
|
|
543
|
+
if (agentRels.length === 0) return;
|
|
544
|
+
|
|
545
|
+
if (dryRun) {
|
|
546
|
+
console.log("[dry-run] Would copy", agentRels.length, "agents into .cursor/agents/");
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
fs.mkdirSync(cursorAgentsDir, { recursive: true });
|
|
551
|
+
|
|
552
|
+
// Collect valid subdirectories from manifest
|
|
553
|
+
const validSubdirs = new Set();
|
|
554
|
+
for (const rel of agentRels) {
|
|
555
|
+
const subdir = path.dirname(rel);
|
|
556
|
+
if (subdir !== ".") validSubdirs.add(subdir);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// Prune stale subdirs and files
|
|
560
|
+
try {
|
|
561
|
+
for (const entry of fs.readdirSync(cursorAgentsDir, { withFileTypes: true })) {
|
|
562
|
+
if (entry.isDirectory() && !validSubdirs.has(entry.name)) {
|
|
563
|
+
fs.rmSync(path.join(cursorAgentsDir, entry.name), { recursive: true, force: true });
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
} catch { /* ignore */ }
|
|
567
|
+
|
|
568
|
+
for (const rel of agentRels) {
|
|
569
|
+
const destPath = path.join(cursorAgentsDir, rel);
|
|
570
|
+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
571
|
+
fs.copyFileSync(path.join(packageAgentsAbs, rel), destPath);
|
|
572
|
+
}
|
|
573
|
+
console.log("Copied", agentRels.length, "agents to .cursor/agents/");
|
|
574
|
+
}
|
|
575
|
+
|
|
431
576
|
function cursorDetected() {
|
|
432
577
|
return fs.existsSync(path.join(os.homedir(), ".cursor"));
|
|
433
578
|
}
|
|
434
579
|
|
|
435
|
-
|
|
436
|
-
|
|
580
|
+
/**
|
|
581
|
+
* Register compound-workflow with Claude Code (project-scoped only).
|
|
582
|
+
* Cursor discovery is handled separately via copyCursor* functions.
|
|
583
|
+
*/
|
|
584
|
+
function applyClaudeRegistration(targetRoot, dryRun, isSelfInstall) {
|
|
437
585
|
const projectRoot = isSelfInstall ? PACKAGE_ROOT : targetRoot;
|
|
438
586
|
const pluginId = "compound-workflow@compound-workflow-local";
|
|
439
587
|
|
|
@@ -442,8 +590,6 @@ function applyCursorRegistration(targetRoot, dryRun, noRegisterCursor, forceRegi
|
|
|
442
590
|
return;
|
|
443
591
|
}
|
|
444
592
|
|
|
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
593
|
const projectSettingsPath = path.join(projectRoot, ".claude", "settings.json");
|
|
448
594
|
let projectSettings = {};
|
|
449
595
|
if (fs.existsSync(projectSettingsPath)) {
|
|
@@ -451,7 +597,6 @@ function applyCursorRegistration(targetRoot, dryRun, noRegisterCursor, forceRegi
|
|
|
451
597
|
}
|
|
452
598
|
projectSettings.enabledPlugins = ensureObject(projectSettings.enabledPlugins);
|
|
453
599
|
projectSettings.enabledPlugins[pluginId] = true;
|
|
454
|
-
// Remove stale/invalid marketplace keys left by earlier install methods
|
|
455
600
|
if (projectSettings.extraKnownMarketplaces?.["compound-workflow"]) {
|
|
456
601
|
delete projectSettings.extraKnownMarketplaces["compound-workflow"];
|
|
457
602
|
}
|
|
@@ -467,16 +612,6 @@ function applyCursorRegistration(targetRoot, dryRun, noRegisterCursor, forceRegi
|
|
|
467
612
|
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
613
|
}
|
|
469
614
|
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;
|
|
476
|
-
}
|
|
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.");
|
|
480
615
|
}
|
|
481
616
|
|
|
482
617
|
function reportOpenCodeIntegration(targetRoot, dryRun) {
|
|
@@ -543,7 +678,10 @@ function main() {
|
|
|
543
678
|
|
|
544
679
|
writeOpenCodeJson(targetRoot, args.dryRun, isSelfInstall);
|
|
545
680
|
writePluginManifests(targetRoot, args.dryRun, isSelfInstall);
|
|
546
|
-
|
|
681
|
+
applyClaudeRegistration(targetRoot, args.dryRun, isSelfInstall);
|
|
682
|
+
copyCursorSkills(targetRoot, args.dryRun, isSelfInstall);
|
|
683
|
+
copyCursorCommands(targetRoot, args.dryRun, isSelfInstall);
|
|
684
|
+
copyCursorAgents(targetRoot, args.dryRun, isSelfInstall);
|
|
547
685
|
reportOpenCodeIntegration(targetRoot, args.dryRun);
|
|
548
686
|
writeAgentsMd(targetRoot, args.dryRun);
|
|
549
687
|
ensureDirs(targetRoot, args.dryRun);
|