@axiomatic-labs/claudeflow 2.49.8 → 2.49.10
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/bin/cli.js +2 -2
- package/lib/install.js +258 -15
- package/package.json +3 -2
package/bin/cli.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Claudeflow CLI entry point.
|
|
4
4
|
//
|
|
5
5
|
// Dual role:
|
|
6
|
-
// 1. Package manager: `claudeflow install` / `version` (npx-friendly).
|
|
6
|
+
// 1. Package manager: `claudeflow install` / `version` (npx-friendly; installs Claude Code + Codex layers).
|
|
7
7
|
// 2. Daily driver wrapper: `claudeflow [claude-args...]` spawns
|
|
8
8
|
// `claude` with `--append-system-prompt-file` auto-injected when
|
|
9
9
|
// an `append-system-prompt.md` is present in the project tree.
|
|
@@ -92,7 +92,7 @@ async function runSubcommand(command, args = []) {
|
|
|
92
92
|
console.log("");
|
|
93
93
|
console.log(" Claudeflow commands:");
|
|
94
94
|
console.log(
|
|
95
|
-
` ${ui.CYAN}install${ui.RESET} Install or update Claudeflow
|
|
95
|
+
` ${ui.CYAN}install${ui.RESET} Install or update Claudeflow for Claude Code and Codex`,
|
|
96
96
|
);
|
|
97
97
|
console.log(` ${ui.CYAN}version${ui.RESET} Show version info`);
|
|
98
98
|
console.log(
|
package/lib/install.js
CHANGED
|
@@ -8,15 +8,16 @@ const { writeLocalVersion, readLocalVersion } = require('./version.js');
|
|
|
8
8
|
const ui = require('./ui.js');
|
|
9
9
|
|
|
10
10
|
// claudeflow installs a small, namespaced footprint. Most of it is self-contained files that are simply
|
|
11
|
-
// PLACED (playbooks, scripts, skills) — those can't conflict with the user's project. The things that
|
|
12
|
-
// MERGE (never a clobber) are: .claude/settings.json
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
// (auth.js/download.js) is untouched.
|
|
11
|
+
// PLACED (playbooks, scripts, skills, agents) — those can't conflict with the user's project. The things that
|
|
12
|
+
// need a MERGE (never a clobber) are: .claude/settings.json, .codex/hooks.json, .codex/config.toml, CLAUDE.md,
|
|
13
|
+
// AGENTS.md, the root .mcp.json, and the local Codex marketplace entry. Those merges live here, in Node, so
|
|
14
|
+
// install works on every platform with no shell dependency. The GitHub auth gate (auth.js/download.js) is
|
|
15
|
+
// untouched.
|
|
17
16
|
|
|
18
17
|
const CLAUDE_MARK = '<!-- claudeflow:begin -->';
|
|
19
18
|
const CLAUDE_END = '<!-- claudeflow:end -->';
|
|
19
|
+
const CODEX_MARK = '<!-- codexflow:begin -->';
|
|
20
|
+
const CODEX_END = '<!-- codexflow:end -->';
|
|
20
21
|
const HOOK_TAG = 'scripts/claudeflow/hook_'; // identifies OUR hook entries in settings.json
|
|
21
22
|
// create-if-absent, never clobber. `test-context.json` ships NO default (it's filled at runtime by the
|
|
22
23
|
// orchestrator with the project's TEST auth) — listing it here just means an update never clobbers it.
|
|
@@ -25,8 +26,9 @@ const PER_PROJECT_CONFIGS = ['brand.json', 'risk-signals.json', 'test-context.js
|
|
|
25
26
|
// Paths appended to the project's .gitignore (add-if-absent). These hold TEST credentials / Playwright
|
|
26
27
|
// storage state — they must NEVER be committed. test-context.json is per-project runtime state; .auth/
|
|
27
28
|
// holds storageState files the orchestrator seeds for auth-gated functional verification.
|
|
28
|
-
const GITIGNORE_LINES = ['.claudeflow/test-context.json', '.claudeflow/.auth/'];
|
|
29
|
+
const GITIGNORE_LINES = ['.claudeflow/test-context.json', '.claudeflow/.auth/', '.claudeflow/telemetry/', '.claudeflow/certification/'];
|
|
29
30
|
const SHARED_CONFIGS = ['playbook-map.json', 'agent-map.json']; // always refreshed (shared system)
|
|
31
|
+
const CODEX_DOCS = ['claudeflow-codex-migration.md', 'codexflow-codex-native-workflow.md', 'codexflow-local-actions.md'];
|
|
30
32
|
|
|
31
33
|
function rmrf(p) { fs.rmSync(p, { recursive: true, force: true }); }
|
|
32
34
|
|
|
@@ -78,6 +80,142 @@ function mergeSettings(cwd, payloadSettings) {
|
|
|
78
80
|
fs.writeFileSync(sp, JSON.stringify(data, null, 2) + '\n');
|
|
79
81
|
}
|
|
80
82
|
|
|
83
|
+
function mergeCodexHooks(cwd, payloadHooksFile) {
|
|
84
|
+
const hp = path.join(cwd, '.codex', 'hooks.json');
|
|
85
|
+
const payloadHooks = (payloadHooksFile && payloadHooksFile.hooks) || {};
|
|
86
|
+
if (!Object.keys(payloadHooks).length) return false;
|
|
87
|
+
|
|
88
|
+
let data = {};
|
|
89
|
+
if (fs.existsSync(hp)) {
|
|
90
|
+
try { data = JSON.parse(fs.readFileSync(hp, 'utf8')); } catch { return false; }
|
|
91
|
+
if (typeof data !== 'object' || data === null || Array.isArray(data)) return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const hooks = data.hooks || (data.hooks = {});
|
|
95
|
+
for (const evt of Object.keys(hooks)) {
|
|
96
|
+
hooks[evt] = (hooks[evt] || []).filter(e => !(e.hooks || []).some(h => (h.command || '').includes(HOOK_TAG)));
|
|
97
|
+
if (!hooks[evt].length) delete hooks[evt];
|
|
98
|
+
}
|
|
99
|
+
for (const evt of Object.keys(payloadHooks)) {
|
|
100
|
+
const arr = hooks[evt] || (hooks[evt] = []);
|
|
101
|
+
for (const entry of payloadHooks[evt]) arr.push(entry);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fs.mkdirSync(path.dirname(hp), { recursive: true });
|
|
105
|
+
fs.writeFileSync(hp, JSON.stringify(data, null, 2) + '\n');
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function tomlKey(line) {
|
|
110
|
+
const m = line.match(/^\s*([A-Za-z0-9_.-]+)\s*=/);
|
|
111
|
+
return m ? m[1] : null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function escapeRe(s) {
|
|
115
|
+
return String(s).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function hasTomlKey(text, key) {
|
|
119
|
+
return new RegExp(`^\\s*${escapeRe(key)}\\s*=`, 'm').test(text);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function splitTomlSections(text) {
|
|
123
|
+
const top = [];
|
|
124
|
+
const sections = [];
|
|
125
|
+
let current = top;
|
|
126
|
+
for (const line of String(text).split('\n')) {
|
|
127
|
+
const m = line.match(/^\s*\[([^\]]+)\]\s*$/);
|
|
128
|
+
if (m) {
|
|
129
|
+
const section = { name: m[1], lines: [line] };
|
|
130
|
+
sections.push(section);
|
|
131
|
+
current = section.lines;
|
|
132
|
+
} else {
|
|
133
|
+
current.push(line);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return { top, sections };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function insertBeforeFirstTomlSection(text, lines) {
|
|
140
|
+
if (!lines.length) return text;
|
|
141
|
+
const block = lines.join('\n') + '\n';
|
|
142
|
+
const firstSection = text.search(/^\s*\[[^\]]+\]\s*$/m);
|
|
143
|
+
if (firstSection === -1) return text.replace(/\s*$/, '\n') + block;
|
|
144
|
+
const prefix = text.slice(0, firstSection).replace(/\s*$/, '\n');
|
|
145
|
+
return prefix + block + '\n' + text.slice(firstSection);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function tomlSectionRange(text, name) {
|
|
149
|
+
const headerRe = new RegExp(`^\\s*\\[${escapeRe(name)}\\]\\s*$`, 'm');
|
|
150
|
+
const match = headerRe.exec(text);
|
|
151
|
+
if (!match) return null;
|
|
152
|
+
const nextRe = /^\s*\[[^\]]+\]\s*$/gm;
|
|
153
|
+
nextRe.lastIndex = match.index + match[0].length;
|
|
154
|
+
const next = nextRe.exec(text);
|
|
155
|
+
return { start: match.index, end: next ? next.index : text.length };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function mergeTomlSection(text, section) {
|
|
159
|
+
const range = tomlSectionRange(text, section.name);
|
|
160
|
+
if (!range) {
|
|
161
|
+
return text.replace(/\s*$/, '\n') + '\n' + section.lines.join('\n').replace(/\s*$/, '') + '\n';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const current = text.slice(range.start, range.end);
|
|
165
|
+
const missing = section.lines.slice(1).filter(line => {
|
|
166
|
+
const key = tomlKey(line);
|
|
167
|
+
return key && !hasTomlKey(current, key);
|
|
168
|
+
});
|
|
169
|
+
if (!missing.length) return text;
|
|
170
|
+
const insert = (text[range.end - 1] === '\n' ? '' : '\n') + missing.join('\n') + '\n';
|
|
171
|
+
return text.slice(0, range.end) + insert + text.slice(range.end);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function mergeCodexConfig(cwd, payloadConfigText) {
|
|
175
|
+
if (!payloadConfigText || !String(payloadConfigText).trim()) return false;
|
|
176
|
+
const cp = path.join(cwd, '.codex', 'config.toml');
|
|
177
|
+
fs.mkdirSync(path.dirname(cp), { recursive: true });
|
|
178
|
+
if (!fs.existsSync(cp)) {
|
|
179
|
+
fs.writeFileSync(cp, String(payloadConfigText).replace(/\s*$/, '\n'));
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const before = fs.readFileSync(cp, 'utf8');
|
|
184
|
+
const payload = splitTomlSections(payloadConfigText);
|
|
185
|
+
const topAssignments = payload.top.filter(line => {
|
|
186
|
+
const key = tomlKey(line);
|
|
187
|
+
return key && !hasTomlKey(before, key);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
let updated = insertBeforeFirstTomlSection(before, topAssignments);
|
|
191
|
+
for (const section of payload.sections) updated = mergeTomlSection(updated, section);
|
|
192
|
+
if (updated === before) return false;
|
|
193
|
+
fs.writeFileSync(cp, updated.replace(/\s*$/, '\n'));
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function mergeCodexMarketplace(cwd, payloadMarketplace) {
|
|
198
|
+
const payloadPlugins = (payloadMarketplace && payloadMarketplace.plugins) || [];
|
|
199
|
+
if (!Array.isArray(payloadPlugins) || !payloadPlugins.length) return false;
|
|
200
|
+
|
|
201
|
+
const mp = path.join(cwd, '.agents', 'plugins', 'marketplace.json');
|
|
202
|
+
let data = {};
|
|
203
|
+
if (fs.existsSync(mp)) {
|
|
204
|
+
try { data = JSON.parse(fs.readFileSync(mp, 'utf8')); } catch { return false; }
|
|
205
|
+
if (typeof data !== 'object' || data === null || Array.isArray(data)) return false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!data.name && payloadMarketplace.name) data.name = payloadMarketplace.name;
|
|
209
|
+
if (!data.interface && payloadMarketplace.interface) data.interface = payloadMarketplace.interface;
|
|
210
|
+
const managedNames = new Set(payloadPlugins.map(p => p && p.name).filter(Boolean));
|
|
211
|
+
const existing = Array.isArray(data.plugins) ? data.plugins : [];
|
|
212
|
+
data.plugins = existing.filter(p => !managedNames.has(p && p.name)).concat(payloadPlugins);
|
|
213
|
+
|
|
214
|
+
fs.mkdirSync(path.dirname(mp), { recursive: true });
|
|
215
|
+
fs.writeFileSync(mp, JSON.stringify(data, null, 2) + '\n');
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
|
|
81
219
|
// Seed the BASE MCP server set (playwright for visual QA / browser_verification, context7 for live library
|
|
82
220
|
// docs during Discover) into the project's root `.mcp.json` — the set the old framework shipped, restored.
|
|
83
221
|
// ADD-IF-ABSENT by server key: create the file if missing, else merge in only the keys the user doesn't
|
|
@@ -126,32 +264,57 @@ function mergeGitignore(cwd, lines) {
|
|
|
126
264
|
// • CLAUDE.md WITH the block → REPLACE the marked region in place, so guidance updates PROPAGATE on `update`
|
|
127
265
|
// (the old code appended-once and never refreshed → a stale project never got new disciplines).
|
|
128
266
|
// • begin marker but no end (corrupted) → leave it untouched (don't risk eating user content)
|
|
129
|
-
function
|
|
267
|
+
function syncMarkedMd(cwd, targetName, snippetPath, beginMark, endMark) {
|
|
130
268
|
if (!fs.existsSync(snippetPath)) return false;
|
|
131
|
-
const target = path.join(cwd,
|
|
269
|
+
const target = path.join(cwd, targetName);
|
|
132
270
|
const block = fs.readFileSync(snippetPath, 'utf8').trim();
|
|
133
271
|
if (!fs.existsSync(target)) {
|
|
134
272
|
fs.writeFileSync(target, block + '\n');
|
|
135
273
|
return true;
|
|
136
274
|
}
|
|
137
275
|
const cur = fs.readFileSync(target, 'utf8');
|
|
138
|
-
const b = cur.indexOf(
|
|
276
|
+
const b = cur.indexOf(beginMark);
|
|
139
277
|
if (b === -1) {
|
|
140
278
|
fs.writeFileSync(target, cur.replace(/\s*$/, '') + '\n\n' + block + '\n');
|
|
141
279
|
return true;
|
|
142
280
|
}
|
|
143
|
-
const e = cur.indexOf(
|
|
281
|
+
const e = cur.indexOf(endMark, b);
|
|
144
282
|
if (e === -1) return false; // begin without end → corrupt region; don't clobber
|
|
145
|
-
const updated = cur.slice(0, b) + block + cur.slice(e +
|
|
283
|
+
const updated = cur.slice(0, b) + block + cur.slice(e + endMark.length);
|
|
146
284
|
if (updated === cur) return false; // already current
|
|
147
285
|
fs.writeFileSync(target, updated);
|
|
148
286
|
return true;
|
|
149
287
|
}
|
|
150
288
|
|
|
289
|
+
function syncClaudeMd(cwd, snippetPath) {
|
|
290
|
+
return syncMarkedMd(cwd, 'CLAUDE.md', snippetPath, CLAUDE_MARK, CLAUDE_END);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function syncAgentsMd(cwd, snippetPath) {
|
|
294
|
+
return syncMarkedMd(cwd, 'AGENTS.md', snippetPath, CODEX_MARK, CODEX_END);
|
|
295
|
+
}
|
|
296
|
+
|
|
151
297
|
// Place + merge the framework from an extracted payload dir into a project. Pure filesystem, no network —
|
|
152
298
|
// so it is testable on a throwaway dir.
|
|
153
299
|
function installFromPayload(src, cwd) {
|
|
154
|
-
const out = {
|
|
300
|
+
const out = {
|
|
301
|
+
playbooks: 0,
|
|
302
|
+
scripts: 0,
|
|
303
|
+
skills: 0,
|
|
304
|
+
agents: 0,
|
|
305
|
+
codexSkills: 0,
|
|
306
|
+
codexAgents: 0,
|
|
307
|
+
codexHooks: false,
|
|
308
|
+
codexConfig: false,
|
|
309
|
+
codexMarketplace: false,
|
|
310
|
+
codexPlugin: 0,
|
|
311
|
+
docs: 0,
|
|
312
|
+
mcp: 0,
|
|
313
|
+
gitignore: 0,
|
|
314
|
+
claudeMd: false,
|
|
315
|
+
agentsMd: false,
|
|
316
|
+
failed: [],
|
|
317
|
+
};
|
|
155
318
|
// Each placement step is ISOLATED: a failure in one (a copy error, a permission hiccup, a weird file in the
|
|
156
319
|
// user's tree) must NOT abort the rest — above all it must not skip the settings/env + CLAUDE.md config, the
|
|
157
320
|
// two steps a user most needs (the env block wires agent-teams + the autocompact window, etc.). Before this,
|
|
@@ -222,6 +385,33 @@ function installFromPayload(src, cwd) {
|
|
|
222
385
|
}
|
|
223
386
|
});
|
|
224
387
|
|
|
388
|
+
// 4c) Codex skills — replace shipped claudeflow-* skills in .agents; preserve user skills.
|
|
389
|
+
step('codex-skills', () => {
|
|
390
|
+
const srcSkills = path.join(src, '.agents', 'skills');
|
|
391
|
+
if (fs.existsSync(srcSkills)) {
|
|
392
|
+
const dst = path.join(cwd, '.agents', 'skills');
|
|
393
|
+
fs.mkdirSync(dst, { recursive: true });
|
|
394
|
+
for (const e of fs.readdirSync(srcSkills, { withFileTypes: true })) {
|
|
395
|
+
if (!e.isDirectory() || !e.name.startsWith('claudeflow-')) continue;
|
|
396
|
+
const d = path.join(dst, e.name);
|
|
397
|
+
try { rmrf(d); copyDir(path.join(srcSkills, e.name), d); out.codexSkills++; } catch {}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
// 4d) Codex agents — replace shipped claudeflow-*.toml agents; preserve user agents.
|
|
403
|
+
step('codex-agents', () => {
|
|
404
|
+
const srcAgents = path.join(src, '.codex', 'agents');
|
|
405
|
+
if (fs.existsSync(srcAgents)) {
|
|
406
|
+
const dst = path.join(cwd, '.codex', 'agents');
|
|
407
|
+
fs.mkdirSync(dst, { recursive: true });
|
|
408
|
+
for (const e of fs.readdirSync(srcAgents, { withFileTypes: true })) {
|
|
409
|
+
if (!e.isFile() || !e.name.startsWith('claudeflow-') || !e.name.endsWith('.toml')) continue;
|
|
410
|
+
try { fs.copyFileSync(path.join(srcAgents, e.name), path.join(dst, e.name)); out.codexAgents++; } catch {}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
|
|
225
415
|
// 5) settings.json — MERGE the shipped hooks + env (add-if-absent) into the user's settings (never clobber).
|
|
226
416
|
// ALWAYS runs (isolated above) — this is what wires CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS et al.
|
|
227
417
|
step('settings', () => {
|
|
@@ -246,7 +436,50 @@ function installFromPayload(src, cwd) {
|
|
|
246
436
|
}
|
|
247
437
|
});
|
|
248
438
|
|
|
249
|
-
// 8) .
|
|
439
|
+
// 8) AGENTS.md — upsert the Codex guidance block between markers, preserving project guidance.
|
|
440
|
+
step('agents-md', () => {
|
|
441
|
+
out.agentsMd = syncAgentsMd(cwd, path.join(src, 'assets', 'AGENTS.snippet.md'));
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
// 9) Codex hooks/config — merge hooks, and add missing default config keys/sections only.
|
|
445
|
+
step('codex-hooks', () => {
|
|
446
|
+
const srcHooks = path.join(src, '.codex', 'hooks.json');
|
|
447
|
+
if (fs.existsSync(srcHooks)) out.codexHooks = mergeCodexHooks(cwd, JSON.parse(fs.readFileSync(srcHooks, 'utf8')));
|
|
448
|
+
});
|
|
449
|
+
step('codex-config', () => {
|
|
450
|
+
const srcConfig = path.join(src, '.codex', 'config.toml');
|
|
451
|
+
if (fs.existsSync(srcConfig)) out.codexConfig = mergeCodexConfig(cwd, fs.readFileSync(srcConfig, 'utf8'));
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
// 10) Codex marketplace + local plugin — managed codexflow entry only; user plugins are preserved.
|
|
455
|
+
step('codex-marketplace', () => {
|
|
456
|
+
const srcMarketplace = path.join(src, '.agents', 'plugins', 'marketplace.json');
|
|
457
|
+
if (fs.existsSync(srcMarketplace)) out.codexMarketplace = mergeCodexMarketplace(cwd, JSON.parse(fs.readFileSync(srcMarketplace, 'utf8')));
|
|
458
|
+
});
|
|
459
|
+
step('codex-plugin', () => {
|
|
460
|
+
const srcPlugin = path.join(src, 'plugins', 'codexflow');
|
|
461
|
+
if (fs.existsSync(srcPlugin)) {
|
|
462
|
+
const dst = path.join(cwd, 'plugins', 'codexflow');
|
|
463
|
+
rmrf(dst); copyDir(srcPlugin, dst);
|
|
464
|
+
out.codexPlugin = countFiles(dst);
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
// 11) Codex docs — managed filenames only.
|
|
469
|
+
step('codex-docs', () => {
|
|
470
|
+
const srcDocs = path.join(src, 'docs');
|
|
471
|
+
if (fs.existsSync(srcDocs)) {
|
|
472
|
+
const dstDocs = path.join(cwd, 'docs');
|
|
473
|
+
fs.mkdirSync(dstDocs, { recursive: true });
|
|
474
|
+
for (const doc of CODEX_DOCS) {
|
|
475
|
+
const s = path.join(srcDocs, doc);
|
|
476
|
+
if (!fs.existsSync(s)) continue;
|
|
477
|
+
try { fs.copyFileSync(s, path.join(dstDocs, doc)); out.docs++; } catch {}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
// 12) .gitignore — ensure runtime and credential paths are ignored (add-if-absent; never commit creds).
|
|
250
483
|
step('gitignore', () => { out.gitignore = mergeGitignore(cwd, GITIGNORE_LINES); });
|
|
251
484
|
|
|
252
485
|
return out;
|
|
@@ -311,8 +544,14 @@ async function run() {
|
|
|
311
544
|
|
|
312
545
|
ui.success(`claudeflow v${version} installed`);
|
|
313
546
|
ui.info(` ${summary.playbooks} playbooks · ${summary.scripts} scripts · ${summary.skills} skills`
|
|
547
|
+
+ (summary.codexSkills ? ` · ${summary.codexSkills} Codex skills` : '')
|
|
548
|
+
+ (summary.codexAgents ? ` · ${summary.codexAgents} Codex agents` : '')
|
|
549
|
+
+ (summary.codexHooks ? ' · Codex hooks merged' : '')
|
|
550
|
+
+ (summary.codexConfig ? ' · Codex config synced' : '')
|
|
551
|
+
+ (summary.codexMarketplace ? ' · Codex plugin registered' : '')
|
|
314
552
|
+ (summary.mcp ? ` · ${summary.mcp} MCP server${summary.mcp > 1 ? 's' : ''} added to .mcp.json` : '')
|
|
315
|
-
+ (summary.claudeMd ? ' · CLAUDE.md guidance synced' : '')
|
|
553
|
+
+ (summary.claudeMd ? ' · CLAUDE.md guidance synced' : '')
|
|
554
|
+
+ (summary.agentsMd ? ' · AGENTS.md guidance synced' : ''));
|
|
316
555
|
if (summary.failed && summary.failed.length) {
|
|
317
556
|
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).`);
|
|
318
557
|
}
|
|
@@ -323,5 +562,9 @@ module.exports = run;
|
|
|
323
562
|
module.exports.installFromPayload = installFromPayload;
|
|
324
563
|
module.exports.mergeSettings = mergeSettings;
|
|
325
564
|
module.exports.syncClaudeMd = syncClaudeMd;
|
|
565
|
+
module.exports.syncAgentsMd = syncAgentsMd;
|
|
326
566
|
module.exports.mergeMcp = mergeMcp;
|
|
327
567
|
module.exports.mergeGitignore = mergeGitignore;
|
|
568
|
+
module.exports.mergeCodexHooks = mergeCodexHooks;
|
|
569
|
+
module.exports.mergeCodexConfig = mergeCodexConfig;
|
|
570
|
+
module.exports.mergeCodexMarketplace = mergeCodexMarketplace;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axiomatic-labs/claudeflow",
|
|
3
|
-
"version": "2.49.
|
|
4
|
-
"description": "Claudeflow — AI-powered development toolkit for Claude Code. Skills, agents, hooks, and quality gates that ship production apps.",
|
|
3
|
+
"version": "2.49.10",
|
|
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"
|
|
7
7
|
},
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"keywords": [
|
|
19
19
|
"claude",
|
|
20
20
|
"claude-code",
|
|
21
|
+
"codex",
|
|
21
22
|
"ai",
|
|
22
23
|
"ai-agents",
|
|
23
24
|
"ai-coding",
|