@axiomatic-labs/claudeflow 2.49.11 → 2.49.13

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 +108 -6
  2. package/package.json +1 -1
package/lib/install.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
3
  const os = require('os');
4
+ const crypto = require('crypto');
4
5
  const { execSync } = require('child_process');
5
6
  const { requireAuth } = require('./auth.js');
6
7
  const { getLatestRelease, downloadReleaseAsset } = require('./download.js');
@@ -41,6 +42,83 @@ function copyDir(src, dst) {
41
42
  }
42
43
  }
43
44
 
45
+ function copyPath(src, dst) {
46
+ const st = fs.statSync(src);
47
+ if (st.isDirectory()) copyDir(src, dst);
48
+ else if (st.isFile()) {
49
+ fs.mkdirSync(path.dirname(dst), { recursive: true });
50
+ fs.copyFileSync(src, dst);
51
+ }
52
+ }
53
+
54
+ function pathHash(p) {
55
+ if (!fs.existsSync(p)) return null;
56
+ const h = crypto.createHash('sha256');
57
+ const walk = (cur, rel) => {
58
+ const st = fs.statSync(cur);
59
+ if (st.isDirectory()) {
60
+ h.update(`dir:${rel}\n`);
61
+ for (const name of fs.readdirSync(cur).sort()) walk(path.join(cur, name), path.join(rel, name));
62
+ return;
63
+ }
64
+ if (!st.isFile()) return;
65
+ h.update(`file:${rel}\n`);
66
+ h.update(fs.readFileSync(cur));
67
+ h.update('\n');
68
+ };
69
+ walk(p, '');
70
+ return h.digest('hex');
71
+ }
72
+
73
+ function syncManagedGlobalDir(srcDir, dstDir, backupRoot, filter) {
74
+ const out = { synced: 0, changed: 0, backups: 0, failed: 0 };
75
+ if (!fs.existsSync(srcDir)) return out;
76
+ fs.mkdirSync(dstDir, { recursive: true });
77
+ for (const e of fs.readdirSync(srcDir, { withFileTypes: true })) {
78
+ if (!filter(e)) continue;
79
+ const s = path.join(srcDir, e.name);
80
+ const d = path.join(dstDir, e.name);
81
+ try {
82
+ out.synced++;
83
+ if (pathHash(s) === pathHash(d)) continue;
84
+ if (fs.existsSync(d)) {
85
+ copyPath(d, path.join(backupRoot, path.basename(dstDir), e.name));
86
+ out.backups++;
87
+ }
88
+ rmrf(d);
89
+ copyPath(s, d);
90
+ out.changed++;
91
+ } catch {
92
+ out.failed++;
93
+ }
94
+ }
95
+ return out;
96
+ }
97
+
98
+ function syncGlobalClaudeArtifacts(src, homeDir = os.homedir()) {
99
+ const stamp = new Date().toISOString().replace(/\D/g, '').slice(0, 14) + '-' + process.pid;
100
+ const backupRoot = path.join(homeDir, '.claude', 'backup', `claudeflow-install-${stamp}`);
101
+ const skillStats = syncManagedGlobalDir(
102
+ path.join(src, '.claude', 'skills'),
103
+ path.join(homeDir, '.claude', 'skills'),
104
+ backupRoot,
105
+ e => e.isDirectory() && e.name.startsWith('claudeflow-'),
106
+ );
107
+ const agentStats = syncManagedGlobalDir(
108
+ path.join(src, '.claude', 'agents'),
109
+ path.join(homeDir, '.claude', 'agents'),
110
+ backupRoot,
111
+ e => e.isFile() && e.name.startsWith('claudeflow-') && e.name.endsWith('.md'),
112
+ );
113
+ return {
114
+ skills: skillStats.synced,
115
+ agents: agentStats.synced,
116
+ changed: skillStats.changed + agentStats.changed,
117
+ backups: skillStats.backups + agentStats.backups,
118
+ failed: skillStats.failed + agentStats.failed,
119
+ };
120
+ }
121
+
44
122
  function countFiles(dir) {
45
123
  let n = 0;
46
124
  for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
@@ -217,7 +295,7 @@ function mergeCodexMarketplace(cwd, payloadMarketplace) {
217
295
  }
218
296
 
219
297
  // Seed the BASE MCP server set (playwright for visual QA / browser_verification, context7 for live library
220
- // docs during Discover) into the project's root `.mcp.json` — the set the old framework shipped, restored.
298
+ // docs during Discover, chrome-devtools for Chrome DevTools inspection) into the project's root `.mcp.json`.
221
299
  // ADD-IF-ABSENT by server key: create the file if missing, else merge in only the keys the user doesn't
222
300
  // already have (never clobber a server they configured). A corrupt/unexpected `.mcp.json` is left untouched
223
301
  // (don't risk eating the user's servers). Returns the number of servers added (0 = nothing to do). Like the
@@ -295,13 +373,18 @@ function syncAgentsMd(cwd, snippetPath) {
295
373
  }
296
374
 
297
375
  // Place + merge the framework from an extracted payload dir into a project. Pure filesystem, no network —
298
- // so it is testable on a throwaway dir.
299
- function installFromPayload(src, cwd) {
376
+ // so it is testable on a throwaway dir. The real CLI turns on `syncGlobalClaude` so stale personal
377
+ // Claude Code skills/agents cannot shadow the project-local copy; tests keep it off unless explicit.
378
+ function installFromPayload(src, cwd, options = {}) {
300
379
  const out = {
301
380
  playbooks: 0,
302
381
  scripts: 0,
303
382
  skills: 0,
304
383
  agents: 0,
384
+ globalSkills: 0,
385
+ globalAgents: 0,
386
+ globalChanged: 0,
387
+ globalBackups: 0,
305
388
  codexSkills: 0,
306
389
  codexAgents: 0,
307
390
  codexHooks: false,
@@ -385,7 +468,20 @@ function installFromPayload(src, cwd) {
385
468
  }
386
469
  });
387
470
 
388
- // 4c) Codex skillsreplace shipped claudeflow-* skills in .agents; preserve user skills.
471
+ // 4c) Personal Claude Code cache repair Claude prioritizes ~/.claude skills/agents over project-local
472
+ // ones. A stale personal claudeflow-* copy shadows the version just installed in this project, so the real
473
+ // CLI refreshes only our namespaced personal artifacts and backs up any changed copy first.
474
+ step('global-claude-artifacts', () => {
475
+ if (options.syncGlobalClaude !== true) return;
476
+ const r = syncGlobalClaudeArtifacts(src, options.globalHome || os.homedir());
477
+ out.globalSkills = r.skills;
478
+ out.globalAgents = r.agents;
479
+ out.globalChanged = r.changed;
480
+ out.globalBackups = r.backups;
481
+ if (r.failed) out.failed.push(`global-claude-artifacts:${r.failed}`);
482
+ });
483
+
484
+ // 4d) Codex skills — replace shipped claudeflow-* skills in .agents; preserve user skills.
389
485
  step('codex-skills', () => {
390
486
  const srcSkills = path.join(src, '.agents', 'skills');
391
487
  if (fs.existsSync(srcSkills)) {
@@ -399,7 +495,7 @@ function installFromPayload(src, cwd) {
399
495
  }
400
496
  });
401
497
 
402
- // 4d) Codex agents — replace shipped claudeflow-*.toml agents; preserve user agents.
498
+ // 4e) Codex agents — replace shipped claudeflow-*.toml agents; preserve user agents.
403
499
  step('codex-agents', () => {
404
500
  const srcAgents = path.join(src, '.codex', 'agents');
405
501
  if (fs.existsSync(srcAgents)) {
@@ -537,13 +633,18 @@ async function run() {
537
633
  execSync(`unzip -qq -o "${tmpZip}" -d "${tmp}"`, { stdio: 'ignore' });
538
634
 
539
635
  ui.step('Installing framework...');
540
- const summary = installFromPayload(tmp, cwd);
636
+ const summary = installFromPayload(tmp, cwd, { syncGlobalClaude: process.env.CLAUDEFLOW_NO_GLOBAL_SYNC !== '1' });
541
637
  writeLocalVersion(`v${version}`);
542
638
 
543
639
  try { rmrf(tmp); fs.rmSync(tmpZip, { force: true }); } catch {}
544
640
 
545
641
  ui.success(`claudeflow v${version} installed`);
546
642
  ui.info(` ${summary.playbooks} playbooks · ${summary.scripts} scripts · ${summary.skills} skills`
643
+ + (summary.globalSkills || summary.globalAgents
644
+ ? ` · global Claude repaired (${summary.globalSkills} skills, ${summary.globalAgents} agents`
645
+ + (summary.globalBackups ? `, ${summary.globalBackups} backups` : '')
646
+ + ')'
647
+ : '')
547
648
  + (summary.codexSkills ? ` · ${summary.codexSkills} Codex skills` : '')
548
649
  + (summary.codexAgents ? ` · ${summary.codexAgents} Codex agents` : '')
549
650
  + (summary.codexHooks ? ' · Codex hooks merged' : '')
@@ -563,6 +664,7 @@ module.exports.installFromPayload = installFromPayload;
563
664
  module.exports.mergeSettings = mergeSettings;
564
665
  module.exports.syncClaudeMd = syncClaudeMd;
565
666
  module.exports.syncAgentsMd = syncAgentsMd;
667
+ module.exports.syncGlobalClaudeArtifacts = syncGlobalClaudeArtifacts;
566
668
  module.exports.mergeMcp = mergeMcp;
567
669
  module.exports.mergeGitignore = mergeGitignore;
568
670
  module.exports.mergeCodexHooks = mergeCodexHooks;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiomatic-labs/claudeflow",
3
- "version": "2.49.11",
3
+ "version": "2.49.13",
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"