@axiomatic-labs/claudeflow 2.12.84 → 2.12.85

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 -2
  2. package/package.json +1 -1
package/lib/install.js CHANGED
@@ -13,6 +13,8 @@ const TEMPLATE_AGENTS = new Set(['claudeflow-explorer', 'claudeflow-planner', 'e
13
13
  const TEMPLATE_MANAGED_MANIFEST = path.join('.claudeflow', 'config', 'template-managed-manifest.json');
14
14
  const CLAUDE_BACKUP_ROOT = '.claudeflow-backups';
15
15
  const REQUIRED_TEMPLATE_RULES = ['claudeflow-implementation.md'];
16
+ const TEMPLATE_MANAGED_SETTINGS_KEYS = ['$schema', 'enabledMcpjsonServers', 'env', 'permissions', 'hooks'];
17
+ const TEMPLATE_SEEDED_SETTINGS_KEYS = new Set(['statusLine']);
16
18
 
17
19
  async function run() {
18
20
  const current = readLocalVersion();
@@ -71,12 +73,13 @@ async function run() {
71
73
  const staleManagedPaths = [...previousManagedPaths].filter((relPath) => !nextManagedPaths.has(relPath));
72
74
  staleRemovedCount = pruneStaleManagedPaths(cwd, staleManagedPaths);
73
75
 
74
- // Copy settings.json
76
+ // Merge settings.json so template-managed keys refresh on update while
77
+ // preserving user customizations like custom statusLine commands.
75
78
  const srcSettings = path.join(srcClaude, 'settings.json');
76
79
  const dstSettings = path.join(cwd, '.claude', 'settings.json');
77
80
  fs.mkdirSync(path.join(cwd, '.claude'), { recursive: true });
78
81
  if (fs.existsSync(srcSettings)) {
79
- fs.copyFileSync(srcSettings, dstSettings);
82
+ syncClaudeSettings(srcSettings, dstSettings);
80
83
  }
81
84
 
82
85
  // Upsert playwright entry in .mcp.json with project-specific CDP port.
@@ -247,6 +250,7 @@ async function run() {
247
250
  ui.success(`${docCount} docs updated`);
248
251
  ui.success(`${staleRemovedCount} stale template-managed files removed`);
249
252
  ui.success(`Template-managed manifest updated (${managedPathCount} paths)`);
253
+ ui.info('Default Claude Code status line is seeded when missing. Existing custom statusLine settings are preserved on update and can be overridden in .claude/settings.local.json.');
250
254
  if (backupPath) {
251
255
  ui.success(`Backup created: ${path.relative(cwd, backupPath)}`);
252
256
  }
@@ -261,6 +265,7 @@ async function run() {
261
265
  ui.success(`${hookCount} hooks installed`);
262
266
  ui.success(`${docCount} docs installed`);
263
267
  ui.success(`Template-managed manifest written (${managedPathCount} paths)`);
268
+ ui.info('Default Claude Code status line installed. Override it in .claude/settings.json or .claude/settings.local.json.');
264
269
  if (backupPath) {
265
270
  ui.success(`Backup created: ${path.relative(cwd, backupPath)}`);
266
271
  }
@@ -333,6 +338,73 @@ function showGettingStarted(cwd) {
333
338
  console.log('');
334
339
  }
335
340
 
341
+ function syncClaudeSettings(srcSettingsPath, dstSettingsPath) {
342
+ const templateSettings = readJsonObject(srcSettingsPath, {
343
+ label: 'template .claude/settings.json',
344
+ throwOnInvalid: true,
345
+ });
346
+ const existingSettings = readJsonObject(dstSettingsPath, {
347
+ label: 'existing .claude/settings.json',
348
+ warnOnInvalid: true,
349
+ }) || {};
350
+ const mergedSettings = mergeClaudeSettings(templateSettings, existingSettings);
351
+ fs.writeFileSync(dstSettingsPath, `${JSON.stringify(mergedSettings, null, 2)}\n`);
352
+ }
353
+
354
+ function mergeClaudeSettings(templateSettings, existingSettings = {}) {
355
+ const template = isPlainObject(templateSettings) ? templateSettings : {};
356
+ const existing = isPlainObject(existingSettings) ? existingSettings : {};
357
+ const merged = {};
358
+
359
+ for (const [key, value] of Object.entries(template)) {
360
+ if (TEMPLATE_MANAGED_SETTINGS_KEYS.includes(key)) {
361
+ merged[key] = cloneJson(value);
362
+ continue;
363
+ }
364
+ if (TEMPLATE_SEEDED_SETTINGS_KEYS.has(key)) {
365
+ merged[key] = Object.prototype.hasOwnProperty.call(existing, key)
366
+ ? cloneJson(existing[key])
367
+ : cloneJson(value);
368
+ }
369
+ }
370
+
371
+ for (const [key, value] of Object.entries(existing)) {
372
+ if (Object.prototype.hasOwnProperty.call(merged, key)) continue;
373
+ if (TEMPLATE_MANAGED_SETTINGS_KEYS.includes(key)) continue;
374
+ merged[key] = cloneJson(value);
375
+ }
376
+
377
+ return merged;
378
+ }
379
+
380
+ function readJsonObject(filePath, options = {}) {
381
+ if (!fs.existsSync(filePath)) return null;
382
+ const { label = filePath, throwOnInvalid = false, warnOnInvalid = false } = options;
383
+
384
+ try {
385
+ const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
386
+ if (isPlainObject(parsed)) return parsed;
387
+
388
+ const message = `${label} must contain a top-level JSON object`;
389
+ if (throwOnInvalid) throw new Error(message);
390
+ if (warnOnInvalid) ui.warn(`${message}. Replacing it with template-managed defaults.`);
391
+ return null;
392
+ } catch (error) {
393
+ if (throwOnInvalid) throw error;
394
+ if (warnOnInvalid) ui.warn(`${label} is invalid JSON. Replacing it with template-managed defaults.`);
395
+ return null;
396
+ }
397
+ }
398
+
399
+ function isPlainObject(value) {
400
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
401
+ }
402
+
403
+ function cloneJson(value) {
404
+ if (value === undefined) return undefined;
405
+ return JSON.parse(JSON.stringify(value));
406
+ }
407
+
336
408
  // Install ast-grep CLI, uv, and Serena MCP for runtime and analysis.
337
409
  // All steps are best-effort — failures warn but never block the install.
338
410
  function installAnalysisTools() {
@@ -756,4 +828,5 @@ function copyDirSync(src, dst, skipNames) {
756
828
 
757
829
  module.exports = Object.assign(run, {
758
830
  assertRequiredTemplateRules,
831
+ mergeClaudeSettings,
759
832
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiomatic-labs/claudeflow",
3
- "version": "2.12.84",
3
+ "version": "2.12.85",
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"