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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "compound-workflow",
3
- "version": "1.7.3",
3
+ "version": "1.8.0",
4
4
  "description": "Clarify -> plan -> execute -> verify -> capture workflow for Cursor",
5
5
  "author": {
6
6
  "name": "Compound Workflow"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "compound-workflow",
3
- "version": "1.7.3",
3
+ "version": "1.8.0",
4
4
  "description": "Clarify → plan → execute → verify → capture. One Install action for Cursor, Claude, and OpenCode.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -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; install syncs
13
- * each skill into .cursor/skills/ (symlinks) so Cursor discovers them. Prune removes stale symlinks.
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] [--no-register-cursor] [--register-cursor]
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, and prompts for Repo Config Block (unless --no-config).
32
- When Cursor is detected (~/.cursor), registers the plugin so skills/commands appear.
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, noRegisterCursor: false, registerCursor: 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
- function applyCursorRegistration(targetRoot, dryRun, noRegisterCursor, forceRegister, isSelfInstall) {
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
- applyCursorRegistration(targetRoot, args.dryRun, args.noRegisterCursor, args.registerCursor, isSelfInstall);
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);