multi-agents-cli 1.0.30 → 1.0.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/init.js +132 -29
  2. package/package.json +1 -1
package/init.js CHANGED
@@ -89,7 +89,8 @@ if (isReInit) {
89
89
  // Re-init from inside project or worktree - self-relocate to repo root
90
90
  try {
91
91
  const { execSync } = require('child_process');
92
- const repoRoot = execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();
92
+ const gitCommonDir = execSync('git rev-parse --git-common-dir', { encoding: 'utf8' }).trim();
93
+ const repoRoot = require('path').resolve(gitCommonDir, '..');
93
94
  process.chdir(repoRoot);
94
95
  } catch { /* stay in current directory */ }
95
96
  }
@@ -657,38 +658,140 @@ const main = async () => {
657
658
 
658
659
  if (fs.existsSync(LOCK_FILE)) {
659
660
  const ts = fs.readFileSync(LOCK_FILE, 'utf8').trim();
660
- const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
661
- const ask2 = (q) => new Promise((resolve) => rl2.question(q, (a) => resolve(a.trim())));
661
+ const trackingPath = path.join(RUNTIME_DIR, '.tracking.json');
662
+ const tracking = fs.existsSync(trackingPath) ? JSON.parse(fs.readFileSync(trackingPath, 'utf8')) : {};
663
+
664
+ // Dependency map — primary agents whose restart cascades to dependents
665
+ const DEPENDENCIES = {
666
+ client: { UI: ['LOGIC', 'FORMS', 'ROUTING', 'TESTING', 'ACCESSIBILITY'] },
667
+ backend: { DB: ['API', 'AUTH', 'LOGIC', 'EVENTS', 'JOBS', 'TESTING'] },
668
+ shared: {},
669
+ };
662
670
 
663
- console.log(`\n${yellow(' This project has already been initialized.')}`);
664
- console.log(dim(` Initialized on: ${ts}\n`));
665
- console.log(` ${dim('1.')} Continue — run ${cyan('npm run agent')}`);
666
- console.log(` ${dim('2.')} Reset — delete config and re-run initialization`);
667
- console.log(` ${dim('3.')} Exit\n`);
671
+ const getActiveAgents = (scope) => {
672
+ const agents = tracking[scope] || {};
673
+ return Object.entries(agents).filter(([, v]) => v && v.branch);
674
+ };
668
675
 
669
- const choice = await ask2(` ${bold('Select')} ${dim('(1-3)')}: `);
676
+ const showRestartProcess = async () => {
677
+ // Build list of active processes across all scopes
678
+ const active = [];
679
+ for (const scope of ['client', 'backend', 'shared']) {
680
+ for (const [agent, data] of getActiveAgents(scope)) {
681
+ active.push({ scope, agent, data });
682
+ }
683
+ }
670
684
 
671
- if (choice === '1') {
672
- console.log('');
673
- rl2.close();
674
- const child = spawn('node', [path.join(ROOT, '.workflow', 'agent.js')], {
675
- stdio: 'inherit',
676
- cwd: ROOT,
685
+ if (active.length === 0) {
686
+ console.log(yellow('\n No active processes found.\n'));
687
+ return false;
688
+ }
689
+
690
+ separator();
691
+ console.log(`\n${bold(' Which process do you want to restart?')}\n`);
692
+ active.forEach(({ scope, agent, data }, i) => {
693
+ const status = data.status || 'ACTIVE';
694
+ console.log(` ${dim(`${i + 1}.`)} ${bold(agent)} ${dim(`(${scope})`)} - ${dim(status)}`);
677
695
  });
678
- child.on('exit', (code) => process.exit(code));
679
- return;
680
- } else if (choice === '2') {
681
- console.log(yellow('\n Resetting configuration...\n'));
682
- fs.unlinkSync(LOCK_FILE);
683
- const configPath = path.join(RUNTIME_DIR, '.config.json');
684
- if (fs.existsSync(configPath)) fs.unlinkSync(configPath);
685
- rl2.close();
686
- console.log(green(' Reset complete. Re-running initialization...\n'));
687
- // Fall through to run init again
688
- } else {
689
- console.log(dim('\n Exited.\n'));
690
- rl2.close();
691
- return;
696
+ console.log(` ${dim(`${active.length + 1}.`)} ${dim('Back')}\n`);
697
+
698
+ const rl3 = readline.createInterface({ input: process.stdin, output: process.stdout });
699
+ const ask3 = (q) => new Promise((resolve) => rl3.question(q, (a) => { rl3.close(); resolve(a.trim()); }));
700
+ const pick = await ask3(` ${bold('Select')} ${dim(`(1-${active.length + 1})`)}: `);
701
+
702
+ const idx = parseInt(pick) - 1;
703
+ if (idx === active.length) return false; // Back
704
+ if (idx < 0 || idx >= active.length) return false;
705
+
706
+ const { scope, agent, data } = active[idx];
707
+ const deps = (DEPENDENCIES[scope] || {})[agent] || [];
708
+ const affectedAgents = [{ scope, agent, data }];
709
+
710
+ // Find dependent agents that are also active
711
+ for (const dep of deps) {
712
+ const depData = (tracking[scope] || {})[dep];
713
+ if (depData && depData.branch) {
714
+ affectedAgents.push({ scope, agent: dep, data: depData });
715
+ }
716
+ }
717
+
718
+ // Show warning with exact names
719
+ separator();
720
+ console.log(`\n${yellow(` ⚠ Restarting ${agent} will delete:`)}`);
721
+ for (const { agent: a, data: d } of affectedAgents) {
722
+ console.log(`\n ${bold(a)}`);
723
+ console.log(` - Branch (${d.branch})`);
724
+ console.log(` - Remote branch (origin/${d.branch})`);
725
+ if (d.worktreePath) {
726
+ const wtName = path.relative(ROOT, d.worktreePath);
727
+ console.log(` - Worktree (${wtName})`);
728
+ }
729
+ }
730
+
731
+ if (deps.length > 0) {
732
+ console.log(`\n ${yellow('Dependent processes will also be wiped.')}`);
733
+ }
734
+
735
+ console.log(`\n ${red('This cannot be undone.')}\n`);
736
+
737
+ const rl4 = readline.createInterface({ input: process.stdin, output: process.stdout });
738
+ const ask4 = (q) => new Promise((resolve) => rl4.question(q, (a) => { rl4.close(); resolve(a.trim()); }));
739
+ const confirm = await ask4(` Continue? (y/N): `);
740
+
741
+ if (confirm.toLowerCase() !== 'y') {
742
+ console.log(dim('\n Cancelled.\n'));
743
+ return false;
744
+ }
745
+
746
+ // Perform wipe
747
+ for (const { agent: a, data: d, scope: s } of affectedAgents) {
748
+ try { execSync(`git worktree remove "${d.worktreePath}" --force`, { cwd: ROOT, stdio: 'pipe' }); } catch {}
749
+ try { execSync(`git branch -D ${d.branch}`, { cwd: ROOT, stdio: 'pipe' }); } catch {}
750
+ try { execSync(`git push origin --delete ${d.branch}`, { cwd: ROOT, stdio: 'pipe' }); } catch {}
751
+ // Clear tracking
752
+ if (tracking[s] && tracking[s][a]) {
753
+ tracking[s][a] = { branch: null, timestamp: null, launchedAt: null, status: null, missingCount: 0, worktreePath: null };
754
+ }
755
+ console.log(` ${green('✓')} ${a} wiped`);
756
+ }
757
+
758
+ fs.writeFileSync(trackingPath, JSON.stringify(tracking, null, 2), 'utf8');
759
+ console.log(`\n ${green('✓')} Restart complete.\n`);
760
+ return true;
761
+ };
762
+
763
+ if (prompts && process.stdin.isTTY) {
764
+ let lockLoop = true;
765
+ while (lockLoop) {
766
+ separator();
767
+ console.log(`\n${yellow(' This project has already been initialized.')}`);
768
+ console.log(dim(` Initialized on: ${ts}\n`));
769
+
770
+ const res = await prompts({
771
+ type: 'select',
772
+ name: 'value',
773
+ message: 'What would you like to do?',
774
+ choices: [
775
+ { title: 'Resume', description: 'Pick up where you left off', value: '1' },
776
+ { title: 'Restart process', description: 'Wipe and restart a specific process', value: '2' },
777
+ { title: 'Cancel', value: '3' },
778
+ ],
779
+ }, { onCancel: () => process.exit(0) });
780
+
781
+ if (res.value === '1') {
782
+ const child = spawn('node', [path.join(ROOT, '.workflow', 'agent.js')], { stdio: 'inherit', cwd: ROOT });
783
+ child.on('exit', (code) => process.exit(code));
784
+ return;
785
+ } else if (res.value === '2') {
786
+ const didRestart = await showRestartProcess();
787
+ if (!didRestart) continue; // Back — show menu again
788
+ lockLoop = false;
789
+ return;
790
+ } else {
791
+ console.log(dim('\n Cancelled.\n'));
792
+ process.exit(0);
793
+ }
794
+ }
692
795
  }
693
796
  }
694
797
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "multi-agents-cli",
3
- "version": "1.0.30",
3
+ "version": "1.0.32",
4
4
  "description": "Multi-agent workflow orchestration for Claude Code — isolated git worktrees, structured state tracking, autonomous task chaining",
5
5
  "keywords": [
6
6
  "claude-code",