legion-cc 0.3.0 → 0.6.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 (35) hide show
  1. package/VERSION +1 -1
  2. package/bin/install.js +126 -18
  3. package/bin/legion-tools.cjs +304 -7
  4. package/bin/lib/config.cjs +18 -3
  5. package/bin/lib/core.cjs +30 -17
  6. package/bin/lib/init.cjs +12 -1
  7. package/bin/lib/session.cjs +1 -1
  8. package/bin/lib/settings.cjs +271 -0
  9. package/bin/lib/state.cjs +70 -2
  10. package/commands/legion/devops/cycle.md +1 -0
  11. package/commands/legion/devops/init.md +1 -0
  12. package/commands/legion/devops/quick.md +2 -0
  13. package/commands/legion/resume.md +2 -0
  14. package/commands/legion/settings.md +62 -0
  15. package/commands/legion/status.md +1 -0
  16. package/commands/legion/update.md +41 -0
  17. package/hooks/legion-context-monitor.js +20 -15
  18. package/hooks/legion-statusline.js +33 -34
  19. package/package.json +1 -1
  20. package/references/agent-routing.md +35 -0
  21. package/references/devops/agent-map.md +23 -0
  22. package/references/devops/sub-agents/architect-sub-agents.md +63 -0
  23. package/references/devops/sub-agents/collapse-rules.md +80 -0
  24. package/references/devops/sub-agents/execute-sub-agents.md +74 -0
  25. package/references/devops/sub-agents/plan-sub-agents.md +36 -0
  26. package/references/devops/sub-agents/review-sub-agents.md +42 -0
  27. package/templates/config.json +9 -1
  28. package/workflows/core/context-load.md +12 -2
  29. package/workflows/devops/architect.md +6 -0
  30. package/workflows/devops/cycle.md +55 -1
  31. package/workflows/devops/execute.md +6 -0
  32. package/workflows/devops/init.md +8 -0
  33. package/workflows/devops/quick.md +11 -0
  34. package/workflows/resume.md +31 -0
  35. package/workflows/settings.md +82 -0
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.6.0
package/bin/install.js CHANGED
@@ -111,9 +111,9 @@ ${c(yellow, 'Environment:')}
111
111
 
112
112
  // ─── Determine paths ─────────────────────────────────────────────────────────
113
113
 
114
- const configDir = process.env.CLAUDE_CONFIG_DIR
115
- || flagConfigDir
116
- || path.join(os.homedir(), '.claude');
114
+ const configDir = path.resolve(
115
+ process.env.CLAUDE_CONFIG_DIR || flagConfigDir || path.join(os.homedir(), '.claude')
116
+ );
117
117
 
118
118
  // ─── Banner ──────────────────────────────────────────────────────────────────
119
119
 
@@ -215,6 +215,20 @@ function walkDir(dir, fileList) {
215
215
  return fileList;
216
216
  }
217
217
 
218
+ /**
219
+ * Check if a path is safely contained within a parent directory.
220
+ * Uses path.resolve() + separator-aware startsWith to prevent prefix collisions.
221
+ * @param {string} child - Path to check
222
+ * @param {string} parent - Expected parent directory
223
+ * @returns {boolean}
224
+ */
225
+ function isInsideDir(child, parent) {
226
+ const resolvedChild = path.resolve(child);
227
+ const resolvedParent = path.resolve(parent);
228
+ return resolvedChild === resolvedParent
229
+ || resolvedChild.startsWith(resolvedParent + path.sep);
230
+ }
231
+
218
232
  /**
219
233
  * Ensure a directory exists (create recursively if needed).
220
234
  */
@@ -476,7 +490,7 @@ function doInstall() {
476
490
  logStep('Generating file manifest...');
477
491
  generateManifest();
478
492
 
479
- // ── Step 5: Print summary ───────────────────────────────────────────────
493
+ // ── Step 6: Print summary ───────────────────────────────────────────────
480
494
 
481
495
  printSummary();
482
496
  }
@@ -580,25 +594,47 @@ function cleanupStaleFiles() {
580
594
 
581
595
  if (!oldManifest || !oldManifest.files) return;
582
596
 
583
- // Build set of files that exist in the NEW install (walk current dirs)
597
+ // Build set of files that exist in the NEW install by walking SOURCE dirs
598
+ // and mapping them to their destination relative paths.
599
+ // This avoids the bug of scanning DESTINATION (which already has new + old files
600
+ // after cpSync), making all files appear "current" and never detecting stale ones.
584
601
  const newFiles = new Set();
585
- const scanDirs = [
586
- path.join(configDir, 'legion'),
587
- path.join(configDir, 'commands', 'legion'),
602
+
603
+ const sourceMappings = [
604
+ { src: path.join(srcDir, 'commands', 'legion'), destPrefix: path.join('commands', 'legion') },
605
+ { src: path.join(srcDir, 'workflows'), destPrefix: path.join('legion', 'workflows') },
606
+ { src: path.join(srcDir, 'templates'), destPrefix: path.join('legion', 'templates') },
607
+ { src: path.join(srcDir, 'references'), destPrefix: path.join('legion', 'references') },
608
+ { src: path.join(srcDir, 'bin', 'lib'), destPrefix: path.join('legion', 'bin', 'lib') },
588
609
  ];
589
- const scanFiles = [
590
- path.join(configDir, 'hooks', 'legion-context-monitor.js'),
591
- path.join(configDir, 'hooks', 'legion-statusline.js'),
610
+
611
+ for (const { src, destPrefix } of sourceMappings) {
612
+ for (const filePath of walkDir(src)) {
613
+ const relInSrc = path.relative(src, filePath);
614
+ newFiles.add(path.join(destPrefix, relInSrc));
615
+ }
616
+ }
617
+
618
+ // Individual source files with explicit destination paths
619
+ const singleFiles = [
620
+ { src: path.join(srcDir, 'bin', 'legion-tools.cjs'), dest: path.join('legion', 'bin', 'legion-tools.cjs') },
621
+ { src: path.join(srcDir, 'VERSION'), dest: path.join('legion', 'VERSION') },
622
+ { src: path.join(srcDir, 'hooks', 'legion-context-monitor.js'), dest: path.join('hooks', 'legion-context-monitor.js') },
623
+ { src: path.join(srcDir, 'hooks', 'legion-statusline.js'), dest: path.join('hooks', 'legion-statusline.js') },
592
624
  ];
593
625
 
594
- for (const dir of scanDirs) {
595
- for (const filePath of walkDir(dir)) {
596
- newFiles.add(path.relative(configDir, filePath));
626
+ for (const { src, dest } of singleFiles) {
627
+ if (fs.existsSync(src)) {
628
+ newFiles.add(dest);
597
629
  }
598
630
  }
599
- for (const f of scanFiles) {
600
- if (fs.existsSync(f)) {
601
- newFiles.add(path.relative(configDir, f));
631
+
632
+ // Agent files (these exist in source agents/ dir)
633
+ const agentsSrcDir = path.join(srcDir, 'agents');
634
+ if (fs.existsSync(agentsSrcDir)) {
635
+ const agentFiles = fs.readdirSync(agentsSrcDir).filter(f => f.endsWith('.md'));
636
+ for (const agentFile of agentFiles) {
637
+ newFiles.add(path.join('agents', agentFile));
602
638
  }
603
639
  }
604
640
 
@@ -606,7 +642,11 @@ function cleanupStaleFiles() {
606
642
  let removedCount = 0;
607
643
  for (const rel of Object.keys(oldManifest.files)) {
608
644
  if (!newFiles.has(rel)) {
609
- const absPath = path.join(configDir, rel);
645
+ const absPath = path.resolve(configDir, rel);
646
+ if (!isInsideDir(absPath, configDir)) {
647
+ logWarn(`Skipping suspicious manifest entry: ${rel}`);
648
+ continue;
649
+ }
610
650
  if (fs.existsSync(absPath)) {
611
651
  if (flagDryRun) {
612
652
  console.log(` ${c(dim, '[dry-run]')} would remove stale: ${rel}`);
@@ -623,6 +663,74 @@ function cleanupStaleFiles() {
623
663
  if (removedCount > 0) {
624
664
  logOk(`Cleaned up ${removedCount} stale file(s) from previous install`);
625
665
  }
666
+
667
+ // Clean up empty directories left behind after stale file removal
668
+ cleanEmptyDirs();
669
+ }
670
+
671
+ // ─── Cleanup empty directories ───────────────────────────────────────────────
672
+
673
+ /**
674
+ * Bottom-up removal of empty directories within the configDir subtree.
675
+ * Called after stale file cleanup to remove directories that are now empty.
676
+ * Guard: only deletes directories within configDir.
677
+ */
678
+ function cleanEmptyDirs() {
679
+ const legionDir = path.join(configDir, 'legion');
680
+ const commandsLegionDir = path.join(configDir, 'commands', 'legion');
681
+
682
+ const dirsToCheck = [legionDir, commandsLegionDir];
683
+
684
+ for (const rootDir of dirsToCheck) {
685
+ if (!fs.existsSync(rootDir)) continue;
686
+ _removeEmptyDirsRecursive(rootDir);
687
+ }
688
+ }
689
+
690
+ /**
691
+ * Recursively remove empty directories bottom-up.
692
+ * Returns true if the directory itself was removed (i.e. it became empty).
693
+ *
694
+ * @param {string} dir - Absolute path to directory
695
+ * @returns {boolean}
696
+ */
697
+ function _removeEmptyDirsRecursive(dir) {
698
+ // Guard: never delete outside configDir
699
+ if (!isInsideDir(dir, configDir)) return false;
700
+
701
+ let entries;
702
+ try {
703
+ entries = fs.readdirSync(dir, { withFileTypes: true });
704
+ } catch (_e) {
705
+ return false;
706
+ }
707
+
708
+ // Recurse into subdirectories first (bottom-up)
709
+ for (const entry of entries) {
710
+ if (entry.isDirectory()) {
711
+ _removeEmptyDirsRecursive(path.join(dir, entry.name));
712
+ }
713
+ }
714
+
715
+ // Re-read after potential subdirectory removals
716
+ try {
717
+ entries = fs.readdirSync(dir);
718
+ } catch (_e) {
719
+ return false;
720
+ }
721
+
722
+ if (entries.length === 0) {
723
+ if (flagDryRun) {
724
+ console.log(` ${c(dim, '[dry-run]')} would remove empty dir: ${path.relative(configDir, dir)}/`);
725
+ } else {
726
+ try {
727
+ fs.rmdirSync(dir);
728
+ return true;
729
+ } catch (_e) { /* silent */ }
730
+ }
731
+ }
732
+
733
+ return false;
626
734
  }
627
735
 
628
736
  // ─── Generate manifest ──────────────────────────────────────────────────────
@@ -2,6 +2,7 @@
2
2
  'use strict';
3
3
 
4
4
  const path = require('path');
5
+ const fs = require('fs');
5
6
 
6
7
  // ─── Library Imports ─────────────────────────────────────────────────────────
7
8
 
@@ -11,6 +12,7 @@ const state = require('./lib/state.cjs');
11
12
  const init = require('./lib/init.cjs');
12
13
  const session = require('./lib/session.cjs');
13
14
  const domain = require('./lib/domain.cjs');
15
+ const settings = require('./lib/settings.cjs');
14
16
 
15
17
  // ─── CLI Helpers ─────────────────────────────────────────────────────────────
16
18
 
@@ -28,8 +30,12 @@ function usage() {
28
30
  ' task-record <type> <desc> <status> Add a task record to STATE.md',
29
31
  ' context-load [path] Load context from .legion/codebase/ and .legion/planning/',
30
32
  ' domain list|info [name] Domain registry operations',
31
- ' session-record <summary> Create a session record in .legion/planning/sessions/',
32
- ' update-check Check for newer version on npm',
33
+ ' settings list|get|set|reset|migrate View and manage project settings',
34
+ ' session-record <summary> Create a session record in .legion/planning/sessions/',
35
+ ' artifacts scan|latest Find untracked artifacts in planning dir',
36
+ ' clean [--dry-run] Remove orphaned files not in manifest',
37
+ ' update Check for updates and print install command',
38
+ ' update-check Check for newer version on npm',
33
39
  '',
34
40
  'Examples:',
35
41
  ' legion-tools init devops',
@@ -61,7 +67,7 @@ function out(data) {
61
67
  */
62
68
  function die(msg, code) {
63
69
  console.error(`${core.UI.cross} Error: ${msg}`);
64
- process.exit(code || 1);
70
+ process.exit(code ?? 1);
65
71
  }
66
72
 
67
73
  /**
@@ -123,7 +129,7 @@ function cmdState(args) {
123
129
  case 'update': {
124
130
  const field = args[1];
125
131
  const value = args.slice(2).join(' ');
126
- if (!field || !value) {
132
+ if (!field || args.length < 3) {
127
133
  die('Usage: legion-tools state update <field> <value>');
128
134
  }
129
135
  state.updateField(planningDir, field, value);
@@ -151,6 +157,10 @@ function cmdState(args) {
151
157
  target = target[part];
152
158
  }
153
159
 
160
+ if (target === undefined) {
161
+ die(`Field not found: ${field}`);
162
+ }
163
+
154
164
  if (typeof target === 'object') {
155
165
  out(target);
156
166
  } else {
@@ -332,7 +342,7 @@ function cmdContextLoad(args) {
332
342
  */
333
343
  function _listFiles(dir, base, depth, results) {
334
344
  if (depth > 3 || results.length >= 100) return results;
335
- const entries = require('fs').readdirSync(dir, { withFileTypes: true });
345
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
336
346
  for (const entry of entries) {
337
347
  if (results.length >= 100) break;
338
348
  const full = path.join(dir, entry.name);
@@ -382,7 +392,7 @@ function cmdDomain(args) {
382
392
  core.banner(info.name, 'info');
383
393
  console.log(` Source: ${info.source}`);
384
394
  console.log(` Version: ${info.version}`);
385
- console.log(` Pipeline: ${info.pipeline.join(' -> ')}`);
395
+ console.log(` Pipeline: ${Array.isArray(info.pipeline) ? info.pipeline.join(' -> ') : 'not configured'}`);
386
396
  console.log('');
387
397
  console.log(' Agents:');
388
398
  for (const [role, agent] of Object.entries(info.agents)) {
@@ -403,6 +413,197 @@ function cmdDomain(args) {
403
413
  }
404
414
  }
405
415
 
416
+ // ─── Command: artifacts ───────────────────────────────────────────────────
417
+
418
+ function cmdArtifacts(args) {
419
+ const subcommand = args[0];
420
+
421
+ if (!subcommand) {
422
+ die('Usage: legion-tools artifacts scan|latest');
423
+ }
424
+
425
+ const planningDir = requirePlanningDir();
426
+
427
+ switch (subcommand) {
428
+ case 'scan': {
429
+ const result = state.findUntrackedArtifacts(planningDir);
430
+
431
+ if (result.untracked.length === 0) {
432
+ console.log(` ${core.UI.check} No untracked artifacts found (max tracked: #${result.maxTracked}).`);
433
+ console.log('');
434
+ out({ untracked: [], maxTracked: result.maxTracked });
435
+ return;
436
+ }
437
+
438
+ console.log(` ${core.UI.diamond} Found ${result.untracked.length} untracked artifact(s) (max tracked: #${result.maxTracked}):`);
439
+ console.log('');
440
+ for (const art of result.untracked) {
441
+ console.log(` #${String(art.num).padStart(3, '0')} [${art.type}] ${path.basename(art.file)}`);
442
+ }
443
+ console.log('');
444
+
445
+ out(result);
446
+ break;
447
+ }
448
+
449
+ case 'latest': {
450
+ const result = state.findUntrackedArtifacts(planningDir);
451
+
452
+ if (result.untracked.length === 0) {
453
+ console.log(` ${core.UI.check} No untracked artifacts.`);
454
+ console.log('');
455
+ out(null);
456
+ return;
457
+ }
458
+
459
+ const latest = result.untracked[result.untracked.length - 1];
460
+ console.log(` ${core.UI.diamond} Latest untracked: #${String(latest.num).padStart(3, '0')} [${latest.type}]`);
461
+ console.log(` ${latest.file}`);
462
+ console.log('');
463
+
464
+ out(latest);
465
+ break;
466
+ }
467
+
468
+ default:
469
+ die(`Unknown artifacts subcommand: ${subcommand}. Use scan|latest`);
470
+ }
471
+ }
472
+
473
+ // ─── Command: clean ───────────────────────────────────────────────────────
474
+
475
+ function cmdClean(args) {
476
+ const fs = require('fs');
477
+ const os = require('os');
478
+
479
+ const dryRun = args.includes('--dry-run');
480
+ const configDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');
481
+ const manifestPath = path.join(configDir, 'legion-file-manifest.json');
482
+
483
+ if (!fs.existsSync(manifestPath)) {
484
+ die('No legion-file-manifest.json found. Install Legion first with: npx legion-cc');
485
+ }
486
+
487
+ const manifest = core.readJsonSafe(manifestPath, null);
488
+ if (!manifest || !manifest.files) {
489
+ die('Invalid manifest file.');
490
+ }
491
+
492
+ const manifestFiles = new Set(Object.keys(manifest.files));
493
+
494
+ // Walk installed dirs and find files not in manifest
495
+ const installedDirs = [
496
+ path.join(configDir, 'legion'),
497
+ path.join(configDir, 'commands', 'legion'),
498
+ ];
499
+
500
+ const installedSingleFiles = [
501
+ path.join(configDir, 'hooks', 'legion-context-monitor.js'),
502
+ path.join(configDir, 'hooks', 'legion-statusline.js'),
503
+ ];
504
+
505
+ const allInstalled = new Set();
506
+
507
+ for (const dir of installedDirs) {
508
+ if (!fs.existsSync(dir)) continue;
509
+ _walkDirForClean(dir, configDir, allInstalled);
510
+ }
511
+
512
+ for (const f of installedSingleFiles) {
513
+ if (fs.existsSync(f)) {
514
+ allInstalled.add(path.relative(configDir, f));
515
+ }
516
+ }
517
+
518
+ // Find orphans: files on disk but not in manifest
519
+ const orphans = [];
520
+ for (const rel of allInstalled) {
521
+ if (!manifestFiles.has(rel)) {
522
+ orphans.push(rel);
523
+ }
524
+ }
525
+
526
+ if (orphans.length === 0) {
527
+ console.log(` ${core.UI.check} No orphaned files found. Installation is clean.`);
528
+ console.log('');
529
+ return;
530
+ }
531
+
532
+ console.log(` Found ${orphans.length} orphaned file(s):`);
533
+ console.log('');
534
+
535
+ let removedCount = 0;
536
+ for (const rel of orphans.sort()) {
537
+ const absPath = path.join(configDir, rel);
538
+ if (dryRun) {
539
+ console.log(` [dry-run] would remove: ${rel}`);
540
+ } else {
541
+ try {
542
+ if (rel.includes('..') || rel.includes('\0')) continue;
543
+ fs.unlinkSync(absPath);
544
+ console.log(` ${core.UI.check} removed: ${rel}`);
545
+ removedCount++;
546
+ } catch (err) {
547
+ console.log(` ${core.UI.cross} failed: ${rel} (${err.message})`);
548
+ }
549
+ }
550
+ }
551
+
552
+ console.log('');
553
+ if (dryRun) {
554
+ console.log(` [dry-run] Would remove ${orphans.length} file(s).`);
555
+ } else {
556
+ console.log(` ${core.UI.check} Removed ${removedCount} orphaned file(s).`);
557
+ }
558
+ console.log('');
559
+ }
560
+
561
+ /**
562
+ * Walk a directory recursively and add relative paths to the set.
563
+ */
564
+ function _walkDirForClean(dir, baseDir, resultSet) {
565
+ const fs = require('fs');
566
+ if (!fs.existsSync(dir)) return;
567
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
568
+ for (const entry of entries) {
569
+ const full = path.join(dir, entry.name);
570
+ if (entry.isDirectory()) {
571
+ _walkDirForClean(full, baseDir, resultSet);
572
+ } else if (entry.isFile()) {
573
+ resultSet.add(path.relative(baseDir, full));
574
+ }
575
+ }
576
+ }
577
+
578
+ // ─── Command: update ──────────────────────────────────────────────────────
579
+
580
+ function cmdUpdate() {
581
+ const vc = require('./lib/version-check.cjs');
582
+ const current = vc.getInstalledVersion();
583
+
584
+ console.log('');
585
+ console.log(` Installed: v${current}`);
586
+ console.log(' Checking npm registry...');
587
+
588
+ const result = vc.checkForUpdate();
589
+
590
+ if (!result) {
591
+ console.log(` ${core.UI.warn} Could not reach npm registry.`);
592
+ console.log('');
593
+ return;
594
+ }
595
+
596
+ if (result.available) {
597
+ console.log(` ${core.UI.diamond} Update available: v${result.current} \u2192 v${result.latest}`);
598
+ console.log('');
599
+ console.log(' To update, run:');
600
+ console.log(` npx legion-cc@latest`);
601
+ } else {
602
+ console.log(` ${core.UI.check} Already on latest version (v${result.current})`);
603
+ }
604
+ console.log('');
605
+ }
606
+
406
607
  // ─── Command: update-check ───────────────────────────────────────────────
407
608
 
408
609
  function cmdUpdateCheck() {
@@ -431,6 +632,86 @@ function cmdUpdateCheck() {
431
632
  out(result);
432
633
  }
433
634
 
635
+ // ─── Command: settings ──────────────────────────────────────────────────────
636
+
637
+ function cmdSettings(args) {
638
+ const subcommand = args[0] || 'list';
639
+ const planningDir = requirePlanningDir();
640
+ let cfg = config.loadConfig(planningDir);
641
+
642
+ if (!cfg) {
643
+ die('No config.json found. Run "legion-tools init <domain>" first.');
644
+ }
645
+
646
+ switch (subcommand) {
647
+ case 'list': {
648
+ core.banner(cfg.domain || 'legion', 'settings');
649
+ console.log(settings.formatSettingsTree(cfg));
650
+ out(cfg);
651
+ break;
652
+ }
653
+
654
+ case 'get': {
655
+ const key = args[1];
656
+ if (!key) {
657
+ die('Usage: legion-tools settings get <key>');
658
+ }
659
+ const value = settings.getSetting(cfg, key);
660
+ if (value === undefined) {
661
+ die(`Setting not found: ${key}`);
662
+ }
663
+ if (typeof value === 'object') {
664
+ out(value);
665
+ } else {
666
+ console.log(value);
667
+ }
668
+ break;
669
+ }
670
+
671
+ case 'set': {
672
+ const key = args[1];
673
+ const value = args.slice(2).join(' ');
674
+ if (!key || args.length < 3) {
675
+ die('Usage: legion-tools settings set <key> <value>');
676
+ }
677
+ const result = settings.setSetting(cfg, key, value);
678
+ if (!result.success) {
679
+ die(result.error);
680
+ }
681
+ config.saveConfig(planningDir, cfg);
682
+ console.log(`${core.UI.check} ${key} = ${settings.getSetting(cfg, key)}`);
683
+ break;
684
+ }
685
+
686
+ case 'reset': {
687
+ const key = args[1];
688
+ cfg = settings.resetSetting(cfg, key);
689
+ config.saveConfig(planningDir, cfg);
690
+ if (key) {
691
+ console.log(`${core.UI.check} Reset ${key} to default`);
692
+ } else {
693
+ console.log(`${core.UI.check} Reset all settings to ${cfg.domain} defaults`);
694
+ }
695
+ break;
696
+ }
697
+
698
+ case 'migrate': {
699
+ const result = settings.migrateConfig(cfg);
700
+ if (result.migrated) {
701
+ config.saveConfig(planningDir, result.config);
702
+ console.log(`${core.UI.check} Config migrated to v${result.config.version}`);
703
+ console.log(settings.formatSettingsTree(result.config));
704
+ } else {
705
+ console.log(`${core.UI.check} Config is already up to date (v${cfg.version})`);
706
+ }
707
+ break;
708
+ }
709
+
710
+ default:
711
+ die(`Unknown settings subcommand: ${subcommand}. Use list|get|set|reset|migrate`);
712
+ }
713
+ }
714
+
434
715
  // ─── Main Router ─────────────────────────────────────────────────────────────
435
716
 
436
717
  function main() {
@@ -471,6 +752,18 @@ function main() {
471
752
  case 'domain':
472
753
  cmdDomain(commandArgs);
473
754
  break;
755
+ case 'settings':
756
+ cmdSettings(commandArgs);
757
+ break;
758
+ case 'artifacts':
759
+ cmdArtifacts(commandArgs);
760
+ break;
761
+ case 'clean':
762
+ cmdClean(commandArgs);
763
+ break;
764
+ case 'update':
765
+ cmdUpdate();
766
+ break;
474
767
  case 'update-check':
475
768
  cmdUpdateCheck();
476
769
  break;
@@ -479,4 +772,8 @@ function main() {
479
772
  }
480
773
  }
481
774
 
482
- main();
775
+ try {
776
+ main();
777
+ } catch (err) {
778
+ die(err.message || String(err));
779
+ }
@@ -7,7 +7,7 @@ const { readJsonSafe, writeJsonSafe } = require('./core.cjs');
7
7
 
8
8
  const DOMAIN_DEFAULTS = {
9
9
  devops: {
10
- version: '0.1.0',
10
+ version: '0.2.0',
11
11
  domain: 'devops',
12
12
  agents: {
13
13
  architect: 'devops-architect',
@@ -30,9 +30,14 @@ const DOMAIN_DEFAULTS = {
30
30
  after_execute: false,
31
31
  after_review: false,
32
32
  },
33
+ parallelism: { minAgents: 5, maxAgents: 10 },
34
+ mcp: {
35
+ enforce: true,
36
+ discovery: true,
37
+ },
33
38
  },
34
39
  backend: {
35
- version: '0.1.0',
40
+ version: '0.2.0',
36
41
  domain: 'backend',
37
42
  agents: {
38
43
  architect: 'backend-architect',
@@ -55,9 +60,14 @@ const DOMAIN_DEFAULTS = {
55
60
  after_execute: false,
56
61
  after_review: false,
57
62
  },
63
+ parallelism: { minAgents: 5, maxAgents: 10 },
64
+ mcp: {
65
+ enforce: true,
66
+ discovery: true,
67
+ },
58
68
  },
59
69
  frontend: {
60
- version: '0.1.0',
70
+ version: '0.2.0',
61
71
  domain: 'frontend',
62
72
  agents: {
63
73
  architect: 'frontend-architect',
@@ -80,6 +90,11 @@ const DOMAIN_DEFAULTS = {
80
90
  after_execute: false,
81
91
  after_review: false,
82
92
  },
93
+ parallelism: { minAgents: 5, maxAgents: 10 },
94
+ mcp: {
95
+ enforce: true,
96
+ discovery: true,
97
+ },
83
98
  },
84
99
  };
85
100