@axiomatic-labs/claudeflow 2.43.30 → 2.43.32

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 +75 -48
  2. package/package.json +1 -1
package/lib/install.js CHANGED
@@ -102,66 +102,90 @@ function syncClaudeMd(cwd, snippetPath) {
102
102
  // Place + merge the framework from an extracted payload dir into a project. Pure filesystem, no network —
103
103
  // so it is testable on a throwaway dir.
104
104
  function installFromPayload(src, cwd) {
105
- const out = { playbooks: 0, scripts: 0, skills: 0, agents: 0, claudeMd: false };
105
+ const out = { playbooks: 0, scripts: 0, skills: 0, agents: 0, claudeMd: false, failed: [] };
106
+ // Each placement step is ISOLATED: a failure in one (a copy error, a permission hiccup, a weird file in the
107
+ // user's tree) must NOT abort the rest — above all it must not skip the settings/env + CLAUDE.md config, the
108
+ // two steps a user most needs (the env block wires agent-teams + the autocompact window, etc.). Before this,
109
+ // any throw in steps 1–4 silently left settings/CLAUDE.md unconfigured. Each step records its name on failure
110
+ // so `run()` can warn instead of reporting a clean success over a partial install.
111
+ const step = (name, fn) => { try { fn(); } catch { out.failed.push(name); } };
106
112
 
107
113
  // 1) Playbooks — always refreshed (shared system).
108
- const srcPlaybooks = path.join(src, '.claudeflow', 'playbooks');
109
- if (fs.existsSync(srcPlaybooks)) {
110
- const dst = path.join(cwd, '.claudeflow', 'playbooks');
111
- rmrf(dst); copyDir(srcPlaybooks, dst);
112
- out.playbooks = countFiles(dst);
113
- }
114
+ step('playbooks', () => {
115
+ const srcPlaybooks = path.join(src, '.claudeflow', 'playbooks');
116
+ if (fs.existsSync(srcPlaybooks)) {
117
+ const dst = path.join(cwd, '.claudeflow', 'playbooks');
118
+ rmrf(dst); copyDir(srcPlaybooks, dst);
119
+ out.playbooks = countFiles(dst);
120
+ }
121
+ });
114
122
 
115
123
  // 2) Configs — per-project ones create-if-absent (never clobber a customized brand); shared refresh.
116
- fs.mkdirSync(path.join(cwd, '.claudeflow'), { recursive: true });
117
- for (const c of PER_PROJECT_CONFIGS) {
118
- const s = path.join(src, '.claudeflow', c), d = path.join(cwd, '.claudeflow', c);
119
- if (fs.existsSync(s) && !fs.existsSync(d)) fs.copyFileSync(s, d);
120
- }
121
- for (const c of SHARED_CONFIGS) {
122
- const s = path.join(src, '.claudeflow', c);
123
- if (fs.existsSync(s)) fs.copyFileSync(s, path.join(cwd, '.claudeflow', c));
124
- }
125
-
126
- // 3) Scripts (hooks + scans + servers) — always refreshed.
127
- const srcScripts = path.join(src, 'scripts', 'claudeflow');
128
- if (fs.existsSync(srcScripts)) {
129
- const dst = path.join(cwd, 'scripts', 'claudeflow');
130
- fs.mkdirSync(dst, { recursive: true });
131
- for (const e of fs.readdirSync(srcScripts)) { fs.copyFileSync(path.join(srcScripts, e), path.join(dst, e)); out.scripts++; }
132
- }
124
+ step('configs', () => {
125
+ fs.mkdirSync(path.join(cwd, '.claudeflow'), { recursive: true });
126
+ for (const c of PER_PROJECT_CONFIGS) {
127
+ const s = path.join(src, '.claudeflow', c), d = path.join(cwd, '.claudeflow', c);
128
+ if (fs.existsSync(s) && !fs.existsSync(d)) fs.copyFileSync(s, d);
129
+ }
130
+ for (const c of SHARED_CONFIGS) {
131
+ const s = path.join(src, '.claudeflow', c);
132
+ if (fs.existsSync(s)) fs.copyFileSync(s, path.join(cwd, '.claudeflow', c));
133
+ }
134
+ });
135
+
136
+ // 3) Scripts (hooks + scans + servers) — always refreshed. Per-file isolation so ONE bad file (a socket, a
137
+ // permission denial) can't abort the whole copy — let alone the later settings/CLAUDE.md steps.
138
+ step('scripts', () => {
139
+ const srcScripts = path.join(src, 'scripts', 'claudeflow');
140
+ if (fs.existsSync(srcScripts)) {
141
+ const dst = path.join(cwd, 'scripts', 'claudeflow');
142
+ fs.mkdirSync(dst, { recursive: true });
143
+ for (const e of fs.readdirSync(srcScripts)) {
144
+ try { fs.copyFileSync(path.join(srcScripts, e), path.join(dst, e)); out.scripts++; } catch {}
145
+ }
146
+ }
147
+ });
133
148
 
134
149
  // 4) Skills — replace each shipped claudeflow-* skill; preserve the user's own skills.
135
- const srcSkills = path.join(src, '.claude', 'skills');
136
- if (fs.existsSync(srcSkills)) {
137
- const dst = path.join(cwd, '.claude', 'skills');
138
- fs.mkdirSync(dst, { recursive: true });
139
- for (const e of fs.readdirSync(srcSkills, { withFileTypes: true })) {
140
- if (!e.isDirectory() || !e.name.startsWith('claudeflow-')) continue;
141
- const d = path.join(dst, e.name);
142
- rmrf(d); copyDir(path.join(srcSkills, e.name), d); out.skills++;
150
+ step('skills', () => {
151
+ const srcSkills = path.join(src, '.claude', 'skills');
152
+ if (fs.existsSync(srcSkills)) {
153
+ const dst = path.join(cwd, '.claude', 'skills');
154
+ fs.mkdirSync(dst, { recursive: true });
155
+ for (const e of fs.readdirSync(srcSkills, { withFileTypes: true })) {
156
+ if (!e.isDirectory() || !e.name.startsWith('claudeflow-')) continue;
157
+ const d = path.join(dst, e.name);
158
+ try { rmrf(d); copyDir(path.join(srcSkills, e.name), d); out.skills++; } catch {}
159
+ }
143
160
  }
144
- }
161
+ });
145
162
 
146
163
  // 4b) Agents — replace each shipped claudeflow-* agent; preserve the user's own agents.
147
- const srcAgents = path.join(src, '.claude', 'agents');
148
- if (fs.existsSync(srcAgents)) {
149
- const dst = path.join(cwd, '.claude', 'agents');
150
- fs.mkdirSync(dst, { recursive: true });
151
- for (const e of fs.readdirSync(srcAgents, { withFileTypes: true })) {
152
- if (!e.isFile() || !e.name.startsWith('claudeflow-') || !e.name.endsWith('.md')) continue;
153
- fs.copyFileSync(path.join(srcAgents, e.name), path.join(dst, e.name)); out.agents++;
164
+ step('agents', () => {
165
+ const srcAgents = path.join(src, '.claude', 'agents');
166
+ if (fs.existsSync(srcAgents)) {
167
+ const dst = path.join(cwd, '.claude', 'agents');
168
+ fs.mkdirSync(dst, { recursive: true });
169
+ for (const e of fs.readdirSync(srcAgents, { withFileTypes: true })) {
170
+ if (!e.isFile() || !e.name.startsWith('claudeflow-') || !e.name.endsWith('.md')) continue;
171
+ try { fs.copyFileSync(path.join(srcAgents, e.name), path.join(dst, e.name)); out.agents++; } catch {}
172
+ }
154
173
  }
155
- }
156
-
157
- // 5) settings.json hooks — MERGE (never clobber the user's settings).
158
- const srcSettings = path.join(src, '.claude', 'settings.json');
159
- if (fs.existsSync(srcSettings)) {
160
- try { mergeSettings(cwd, JSON.parse(fs.readFileSync(srcSettings, 'utf8'))); } catch {}
161
- }
174
+ });
175
+
176
+ // 5) settings.json — MERGE the shipped hooks + env (add-if-absent) into the user's settings (never clobber).
177
+ // ALWAYS runs (isolated above) — this is what wires CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS et al.
178
+ step('settings', () => {
179
+ const srcSettings = path.join(src, '.claude', 'settings.json');
180
+ if (fs.existsSync(srcSettings)) {
181
+ mergeSettings(cwd, JSON.parse(fs.readFileSync(srcSettings, 'utf8')));
182
+ }
183
+ });
162
184
 
163
185
  // 6) CLAUDE.md — upsert the managed guidance block between markers (idempotent; updates propagate on re-install).
164
- out.claudeMd = syncClaudeMd(cwd, path.join(src, 'assets', 'CLAUDE.snippet.md'));
186
+ step('claude-md', () => {
187
+ out.claudeMd = syncClaudeMd(cwd, path.join(src, 'assets', 'CLAUDE.snippet.md'));
188
+ });
165
189
 
166
190
  return out;
167
191
  }
@@ -200,6 +224,9 @@ async function run() {
200
224
  ui.success(`claudeflow v${version} installed`);
201
225
  ui.info(` ${summary.playbooks} playbooks · ${summary.scripts} scripts · ${summary.skills} skills`
202
226
  + (summary.claudeMd ? ' · CLAUDE.md guidance synced' : ''));
227
+ if (summary.failed && summary.failed.length) {
228
+ ui.warn(` some steps did not complete: ${summary.failed.join(', ')}. Re-run \`npx @axiomatic-labs/claudeflow@latest install\` (the @latest avoids a stale npx cache).`);
229
+ }
203
230
  ui.done(`Describe a change and the ${ui.BOLD}claudeflow-implementer${ui.RESET} agent will build it.`);
204
231
  }
205
232
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiomatic-labs/claudeflow",
3
- "version": "2.43.30",
3
+ "version": "2.43.32",
4
4
  "description": "Claudeflow — AI-powered development toolkit for Claude Code. Skills, agents, hooks, and quality gates that ship production apps.",
5
5
  "bin": {
6
6
  "claudeflow": "./bin/cli.js"