@axiomatic-labs/claudeflow 2.50.5 → 2.51.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 (2) hide show
  1. package/lib/install.js +56 -1
  2. package/package.json +1 -1
package/lib/install.js CHANGED
@@ -439,11 +439,13 @@ function pathHash(p) {
439
439
  }
440
440
 
441
441
  function syncManagedGlobalDir(srcDir, dstDir, backupRoot, filter, label) {
442
- const out = { synced: 0, changed: 0, backups: 0, failed: 0, failures: [] };
442
+ const out = { synced: 0, changed: 0, removed: 0, backups: 0, failed: 0, failures: [] };
443
443
  if (!fs.existsSync(srcDir)) return out;
444
444
  fs.mkdirSync(dstDir, { recursive: true });
445
+ const shipped = new Set();
445
446
  for (const e of fs.readdirSync(srcDir, { withFileTypes: true })) {
446
447
  if (!filter(e)) continue;
448
+ shipped.add(e.name);
447
449
  const s = path.join(srcDir, e.name);
448
450
  const d = path.join(dstDir, e.name);
449
451
  try {
@@ -461,6 +463,26 @@ function syncManagedGlobalDir(srcDir, dstDir, backupRoot, filter, label) {
461
463
  out.failures.push(`${label}:${e.name}:${err && err.code ? err.code : 'ERROR'}`);
462
464
  }
463
465
  }
466
+ // Prune orphans: a global claudeflow-* that is NO LONGER shipped (a removed or renamed skill/agent, e.g.
467
+ // claudeflow-method-bdd-lite after its merge into atdd-lite) must be DELETED, not left behind. Claude
468
+ // prioritizes ~/.claude over the project copy, so a stale global artifact silently shadows the current set —
469
+ // the exact redundancy where a deleted skill keeps loading. Overwriting the shipped ones is not enough; the
470
+ // set must MIRROR what ships. Only the managed claudeflow-* namespace is pruned (filter gates it); the user's
471
+ // own skills/agents never match the filter and are untouched. Backed up first, like every managed change.
472
+ for (const e of fs.readdirSync(dstDir, { withFileTypes: true })) {
473
+ if (!filter(e) || shipped.has(e.name)) continue;
474
+ const d = path.join(dstDir, e.name);
475
+ try {
476
+ copyPath(d, path.join(backupRoot, path.basename(dstDir), e.name));
477
+ out.backups++;
478
+ fs.rmSync(d, { recursive: true, force: true });
479
+ out.removed++;
480
+ out.changed++;
481
+ } catch (err) {
482
+ out.failed++;
483
+ out.failures.push(`${label}:prune:${e.name}:${err && err.code ? err.code : 'ERROR'}`);
484
+ }
485
+ }
464
486
  return out;
465
487
  }
466
488
 
@@ -506,6 +528,31 @@ function countFiles(dir) {
506
528
  return n;
507
529
  }
508
530
 
531
+ // Remove managed (claudeflow-*) artifacts present in dst but NO LONGER shipped in src — a removed or renamed
532
+ // skill/agent must disappear, not linger. Overwriting the shipped set is not enough: a leftover copy (project OR
533
+ // global) keeps being discovered and loaded, the redundancy that let a deleted skill keep running. The filter
534
+ // gates the managed namespace, so the user's OWN skills/agents never match and are untouched. Backs up first.
535
+ function pruneManagedOrphans(srcDir, dstDir, filter, backupRoot, label) {
536
+ const out = { removed: 0, backups: 0, failed: 0, failures: [] };
537
+ if (!fs.existsSync(srcDir) || !fs.existsSync(dstDir)) return out;
538
+ const shipped = new Set();
539
+ for (const e of fs.readdirSync(srcDir, { withFileTypes: true })) if (filter(e)) shipped.add(e.name);
540
+ for (const e of fs.readdirSync(dstDir, { withFileTypes: true })) {
541
+ if (!filter(e) || shipped.has(e.name)) continue;
542
+ const d = path.join(dstDir, e.name);
543
+ try {
544
+ copyPath(d, path.join(backupRoot, path.basename(dstDir), e.name));
545
+ out.backups++;
546
+ fs.rmSync(d, { recursive: true, force: true });
547
+ out.removed++;
548
+ } catch (err) {
549
+ out.failed++;
550
+ out.failures.push(`${label}:prune:${e.name}:${err && err.code ? err.code : 'ERROR'}`);
551
+ }
552
+ }
553
+ return out;
554
+ }
555
+
509
556
  function mergeSettings(cwd, payloadSettings) {
510
557
  const sp = path.join(cwd, '.claude', 'settings.json');
511
558
  let data = {};
@@ -908,6 +955,8 @@ function installFromPayload(src, cwd, options = {}) {
908
955
  const d = path.join(dst, e.name);
909
956
  if (replace(path.join(srcSkills, e.name), d, cwd, `skills:${e.name}`)) out.skills++;
910
957
  }
958
+ out.obsoleteRemoved += pruneManagedOrphans(srcSkills, dst,
959
+ e => e.isDirectory() && e.name.startsWith('claudeflow-'), backupRoot, 'skills').removed;
911
960
  }
912
961
  });
913
962
 
@@ -921,6 +970,8 @@ function installFromPayload(src, cwd, options = {}) {
921
970
  if (!e.isFile() || !e.name.startsWith('claudeflow-') || !e.name.endsWith('.md')) continue;
922
971
  if (replace(path.join(srcAgents, e.name), path.join(dst, e.name), cwd, `agents:${e.name}`)) out.agents++;
923
972
  }
973
+ out.obsoleteRemoved += pruneManagedOrphans(srcAgents, dst,
974
+ e => e.isFile() && e.name.startsWith('claudeflow-') && e.name.endsWith('.md'), backupRoot, 'agents').removed;
924
975
  }
925
976
  });
926
977
 
@@ -952,6 +1003,8 @@ function installFromPayload(src, cwd, options = {}) {
952
1003
  const d = path.join(dst, e.name);
953
1004
  if (replace(path.join(srcSkills, e.name), d, cwd, `codex-skills:${e.name}`)) out.codexSkills++;
954
1005
  }
1006
+ out.obsoleteRemoved += pruneManagedOrphans(srcSkills, dst,
1007
+ e => e.isDirectory() && e.name.startsWith('claudeflow-'), backupRoot, 'codex-skills').removed;
955
1008
  }
956
1009
  });
957
1010
 
@@ -965,6 +1018,8 @@ function installFromPayload(src, cwd, options = {}) {
965
1018
  if (!e.isFile() || !e.name.startsWith('claudeflow-') || !e.name.endsWith('.toml')) continue;
966
1019
  if (replace(path.join(srcAgents, e.name), path.join(dst, e.name), cwd, `codex-agents:${e.name}`)) out.codexAgents++;
967
1020
  }
1021
+ out.obsoleteRemoved += pruneManagedOrphans(srcAgents, dst,
1022
+ e => e.isFile() && e.name.startsWith('claudeflow-') && e.name.endsWith('.toml'), backupRoot, 'codex-agents').removed;
968
1023
  }
969
1024
  });
970
1025
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiomatic-labs/claudeflow",
3
- "version": "2.50.5",
3
+ "version": "2.51.0",
4
4
  "description": "Claudeflow — AI-powered development toolkit for Claude Code and Codex. Skills, agents, hooks, and quality gates that ship production apps.",
5
5
  "bin": {
6
6
  "claudeflow": "./bin/cli.js"