multi-agents-cli 1.1.9 → 1.1.11

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 (44) hide show
  1. package/README.md +18 -2
  2. package/core/templates/.agents/backend/API.md +270 -0
  3. package/core/templates/.agents/backend/AUTH.md +257 -0
  4. package/core/templates/.agents/backend/DB.md +268 -0
  5. package/core/templates/.agents/backend/EVENTS.md +264 -0
  6. package/core/templates/.agents/backend/INIT.md +250 -0
  7. package/core/templates/.agents/backend/JOBS.md +267 -0
  8. package/core/templates/.agents/backend/LOGIC.md +302 -0
  9. package/core/templates/.agents/backend/TESTING.md +277 -0
  10. package/core/templates/.agents/client/ACCESSIBILITY.md +277 -0
  11. package/core/templates/.agents/client/FORMS.md +245 -0
  12. package/core/templates/.agents/client/LOGIC.md +288 -0
  13. package/core/templates/.agents/client/ROUTING.md +246 -0
  14. package/core/templates/.agents/client/TESTING.md +252 -0
  15. package/core/templates/.agents/client/UI.md +237 -0
  16. package/core/templates/.agents/shared/CLOUD.md +229 -0
  17. package/core/templates/.agents/shared/CLOUD_TEARDOWN.md +158 -0
  18. package/core/templates/.agents/shared/SECURITY.md +297 -0
  19. package/core/templates/.frameworks/backend/django.md +55 -0
  20. package/core/templates/.frameworks/backend/express.md +74 -0
  21. package/core/templates/.frameworks/backend/fastapi.md +107 -0
  22. package/core/templates/.frameworks/backend/fastify.md +74 -0
  23. package/core/templates/.frameworks/backend/laravel.md +79 -0
  24. package/core/templates/.frameworks/backend/nestjs.md +75 -0
  25. package/core/templates/.frameworks/backend/rails.md +84 -0
  26. package/core/templates/.frameworks/client/angular.md +80 -0
  27. package/core/templates/.frameworks/client/nextjs.md +47 -0
  28. package/core/templates/.frameworks/client/nuxt.md +45 -0
  29. package/core/templates/.frameworks/client/remix.md +44 -0
  30. package/core/templates/.frameworks/client/sveltekit.md +44 -0
  31. package/core/templates/.frameworks/client/vite-react.md +45 -0
  32. package/core/templates/CLAUDE.md +562 -0
  33. package/core/templates/CONTRACTS.md +16 -0
  34. package/core/templates/backend/CLAUDE.md +207 -0
  35. package/core/templates/client/CLAUDE.md +213 -0
  36. package/core/workflow/agent.js +285 -6
  37. package/core/workflow/complete.js +155 -36
  38. package/core/workflow/reset.js +28 -21
  39. package/core/workflow/run.js +3 -0
  40. package/init.js +2 -2
  41. package/lib/questions-flow.js +13 -2
  42. package/lib/steps.js +13 -1
  43. package/lib/ui.js +8 -0
  44. package/package.json +1 -1
@@ -17,7 +17,6 @@
17
17
 
18
18
  const fs = require('fs');
19
19
  const path = require('path');
20
- const readline = require('readline');
21
20
  const { execSync } = require('child_process');
22
21
 
23
22
  let prompts = null;
@@ -69,6 +68,7 @@ const main = async () => {
69
68
 
70
69
  // ── Load config and tracking ─────────────────────────────────────────────────
71
70
 
71
+ const confirmed = process.argv.includes('--confirmed');
72
72
  const config = fs.existsSync(CONFIG_PATH) ? JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8')) : {};
73
73
  const tracking = fs.existsSync(TRACKING_PATH) ? JSON.parse(fs.readFileSync(TRACKING_PATH, 'utf8')) : {};
74
74
  const projectName = config.projectName || path.basename(ROOT);
@@ -79,7 +79,7 @@ const main = async () => {
79
79
  for (const scope of ['client', 'backend', 'shared']) {
80
80
  const agents = tracking[scope] || {};
81
81
  for (const [agent, data] of Object.entries(agents)) {
82
- if (data && data.branch) {
82
+ if (data && data.branch && data.status !== 'COMPLETED') {
83
83
  activeAgents.push({ scope, agent, data });
84
84
  }
85
85
  }
@@ -89,11 +89,12 @@ const main = async () => {
89
89
 
90
90
  let remoteUrl = null;
91
91
  try {
92
- remoteUrl = execSync('git remote get-url origin', { cwd: ROOT, encoding: 'utf8' }).trim();
92
+ remoteUrl = execSync('git remote get-url origin', { cwd: ROOT, encoding: 'utf8', stdio: 'pipe' }).trim();
93
93
  } catch {}
94
94
 
95
95
  // ── Display warning ───────────────────────────────────────────────────────────
96
96
 
97
+ if (!confirmed) {
97
98
  separator();
98
99
  console.log(`${red(bold(' ⚠ FULL PROJECT RESET'))}\n`);
99
100
  console.log(` ${bold('This will permanently delete:')}\n`);
@@ -128,8 +129,7 @@ const main = async () => {
128
129
 
129
130
  // ── Step 1: First confirmation ────────────────────────────────────────────────
130
131
 
131
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
132
- const ask = (q) => new Promise((resolve) => rl.question(q, (a) => resolve(a.trim())));
132
+
133
133
 
134
134
  if (prompts && process.stdin.isTTY) {
135
135
  const step1 = await prompts({
@@ -146,13 +146,6 @@ const main = async () => {
146
146
  console.log(dim('\n Reset cancelled.\n'));
147
147
  process.exit(0);
148
148
  }
149
- } else {
150
- const ans = await ask(` ${bold('Are you sure?')} ${dim('(y/N)')}: `);
151
- if (ans.toLowerCase() !== 'y') {
152
- console.log(dim('\n Reset cancelled.\n'));
153
- rl.close();
154
- process.exit(0);
155
- }
156
149
  }
157
150
 
158
151
  // ── Step 2: Type project name ─────────────────────────────────────────────────
@@ -161,14 +154,20 @@ const main = async () => {
161
154
  console.log(` ${yellow('To confirm, type the project name exactly:')}`);
162
155
  console.log(` ${cyan(bold(projectName))}\n`);
163
156
 
164
- const typed = await ask(` ${bold('Project name')}: `);
165
- rl.close();
157
+ const step2 = await prompts({
158
+ type: 'text',
159
+ name: 'value',
160
+ message: 'Project name',
161
+ }, { onCancel: () => process.exit(0) });
162
+ const typed = (step2.value || '').trim();
166
163
 
167
164
  if (typed !== projectName) {
168
165
  console.log(`\n${red(' ✗ Project name does not match. Reset cancelled.')}\n`);
169
166
  process.exit(1);
170
167
  }
171
168
 
169
+ } // end if (!confirmed)
170
+
172
171
  // ── Execute wipe ──────────────────────────────────────────────────────────────
173
172
 
174
173
  separator();
@@ -212,7 +211,7 @@ const main = async () => {
212
211
  }
213
212
  }
214
213
 
215
- // Wipe project files (keep .git temporarily for branch ops above)
214
+ // Wipe project files (keep .git git repo is infrastructure, not framework state)
216
215
  const keepList = ['.git'];
217
216
  const entries = fs.readdirSync(ROOT);
218
217
  for (const entry of entries) {
@@ -223,18 +222,26 @@ const main = async () => {
223
222
  }
224
223
  console.log(` ${green('✓')} Project files removed`);
225
224
 
226
- // Remove .git
225
+
226
+ // ── Re-plant package.json ────────────────────────────────────────────
227
+
227
228
  try {
228
- fs.rmSync(path.join(ROOT, '.git'), { recursive: true, force: true });
229
- console.log(` ${green('')} Git history removed`);
230
- } catch {}
229
+ const freshPkg = {
230
+ name: projectName.toLowerCase().replace(/\s+/g, '-'),
231
+ version: '1.0.0',
232
+ scripts: { init: 'multi-agents init' },
233
+ };
234
+ fs.writeFileSync(path.join(ROOT, 'package.json'), JSON.stringify(freshPkg, null, 2) + '\n', 'utf8');
235
+ console.log(' ' + green('\u2713') + ' package.json re-planted');
236
+ } catch { /* best-effort */ }
231
237
 
232
238
  // ── Post-wipe instructions ────────────────────────────────────────────────────
233
239
 
234
240
  separator();
235
241
  console.log(`${green(bold(' Project wiped successfully.'))}\n`);
236
- console.log(` To start fresh:\n`);
237
- console.log(` ${cyan(`cd .. && multi-agents init ${projectName}`)}\n`);
242
+ console.log(` Run to start fresh:\n`);
243
+ console.log(` ${cyan('npm run init')}
244
+ `);
238
245
  separator();
239
246
 
240
247
  process.exit(0);
@@ -56,6 +56,9 @@ const copyWorkflow = (cliRoot) => {
56
56
  };
57
57
 
58
58
  const findCliRoot = () => {
59
+ // Local node_modules first (npx or local install)
60
+ const localPkg = path.join(__dirname, '..', 'node_modules', 'multi-agents-cli');
61
+ if (fs.existsSync(localPkg)) return localPkg;
59
62
  try {
60
63
  const globalPkg = execSync('npm root -g', { stdio: 'pipe', encoding: 'utf8' }).trim();
61
64
  const p = path.join(globalPkg, 'multi-agents-cli');
package/init.js CHANGED
@@ -78,7 +78,7 @@ const projectArg = isGlobalCLI ? args[1] : null;
78
78
 
79
79
  if (isReInit) {
80
80
  try {
81
- const gitCommonDir = execSync('git rev-parse --git-common-dir', { encoding: 'utf8' }).trim();
81
+ const gitCommonDir = execSync('git rev-parse --git-common-dir', { encoding: 'utf8', stdio: 'pipe' }).trim();
82
82
  const repoRoot = path.resolve(gitCommonDir, '..');
83
83
  process.chdir(repoRoot);
84
84
  } catch { /* stay in current directory */ }
@@ -275,7 +275,7 @@ const main = async () => {
275
275
  ],
276
276
  }, { onCancel: () => process.exit(0) });
277
277
  if (confirm.value !== 'yes') { console.log(dim('\n Cancelled.\n')); process.exit(0); }
278
- const resetChild = spawn('node', [path.join(ROOT, '.workflow', 'reset.js')], { stdio: 'inherit', cwd: ROOT });
278
+ const resetChild = spawn('node', [path.join(ROOT, '.workflow', 'reset.js'), '--confirmed'], { stdio: 'inherit', cwd: ROOT });
279
279
  resetChild.on('exit', code => process.exit(code ?? 0));
280
280
  return;
281
281
  } else {
@@ -2,7 +2,7 @@
2
2
 
3
3
  // ── Imports ───────────────────────────────────────────────────────────────────
4
4
 
5
- const { dim, bold, blue, green, cyan, separator, arrowSelect, arrowConfirm, selectRequired, selectOptional } = require('./ui');
5
+ const { dim, bold, blue, green, cyan, separator, arrowSelect, arrowConfirm, selectRequired, selectOptional, stepHeader } = require('./ui');
6
6
  const { BACK, RESTART } = require('./steps');
7
7
  const {
8
8
  CLIENT_FRAMEWORKS, BACKEND_FRAMEWORKS, FRAMEWORK_VERSION_FALLBACK,
@@ -21,6 +21,7 @@ const buildStepDefs = (IDE_CANDIDATES) => [
21
21
  {
22
22
  name: 'clientFw',
23
23
  run: async (machine, answers) => {
24
+ stepHeader(1, 12);
24
25
  console.log(`\n${bold(blue('Client configuration'))}`);
25
26
  return await selectRequired('* Client framework (required):', CLIENT_FRAMEWORKS, machine, 0);
26
27
  },
@@ -30,6 +31,7 @@ const buildStepDefs = (IDE_CANDIDATES) => [
30
31
  {
31
32
  name: 'clientFwVersion',
32
33
  run: async (machine, answers) => {
34
+ stepHeader(2, 12);
33
35
  const fw = answers.clientFw;
34
36
  const versions = await fetchLatestVersions(fw.value) || FRAMEWORK_VERSION_FALLBACK[fw.value] || [];
35
37
  if (!versions.length) return null; // skip silently
@@ -56,6 +58,7 @@ const buildStepDefs = (IDE_CANDIDATES) => [
56
58
  {
57
59
  name: 'clientState',
58
60
  run: async (machine, answers) => {
61
+ stepHeader(3, 12);
59
62
  const opts = STATE_OPTIONS[answers.clientFw.value] || [];
60
63
  return await selectOptional('State management:', opts, machine, 2);
61
64
  },
@@ -65,6 +68,7 @@ const buildStepDefs = (IDE_CANDIDATES) => [
65
68
  {
66
69
  name: 'clientUi',
67
70
  run: async (machine, answers) => {
71
+ stepHeader(4, 12);
68
72
  const opts = UI_OPTIONS[answers.clientFw.value] || [];
69
73
  return await selectOptional('UI library:', opts, machine, 3);
70
74
  },
@@ -74,6 +78,7 @@ const buildStepDefs = (IDE_CANDIDATES) => [
74
78
  {
75
79
  name: 'clientStyle',
76
80
  run: async (machine, answers) => {
81
+ stepHeader(5, 12);
77
82
  return await selectOptional('Styling:', STYLING_OPTIONS, machine, 4);
78
83
  },
79
84
  },
@@ -82,6 +87,7 @@ const buildStepDefs = (IDE_CANDIDATES) => [
82
87
  {
83
88
  name: 'useIntegratedBackend',
84
89
  run: async (machine, answers) => {
90
+ stepHeader(6, 12);
85
91
  const fw = answers.clientFw;
86
92
  if (!fw.integratedBackend) return false; // skip — not applicable
87
93
 
@@ -98,6 +104,7 @@ const buildStepDefs = (IDE_CANDIDATES) => [
98
104
  {
99
105
  name: 'backendFwObj',
100
106
  run: async (machine, answers) => {
107
+ stepHeader(7, 12);
101
108
  if (answers.useIntegratedBackend) return null; // skip
102
109
 
103
110
  separator();
@@ -121,6 +128,7 @@ const buildStepDefs = (IDE_CANDIDATES) => [
121
128
  {
122
129
  name: 'backendFwVersion',
123
130
  run: async (machine, answers) => {
131
+ stepHeader(8, 12);
124
132
  const fwObj = answers.backendFwObj;
125
133
  if (!fwObj) return null; // skip — no backend selected
126
134
 
@@ -147,6 +155,7 @@ const buildStepDefs = (IDE_CANDIDATES) => [
147
155
  {
148
156
  name: 'backendDb',
149
157
  run: async (machine, answers) => {
158
+ stepHeader(9, 12);
150
159
  if (!answers.backendFwObj) return null;
151
160
  return await selectOptional('Database type:', DB_OPTIONS, machine, 8);
152
161
  },
@@ -156,6 +165,7 @@ const buildStepDefs = (IDE_CANDIDATES) => [
156
165
  {
157
166
  name: 'backendOrm',
158
167
  run: async (machine, answers) => {
168
+ stepHeader(10, 12);
159
169
  const { backendFwObj, backendDb } = answers;
160
170
  if (!backendFwObj || !backendDb) return null;
161
171
  const ormChoices = ORM_OPTIONS_BY_DB[backendDb] || ORM_OPTIONS[backendFwObj.value] || [];
@@ -167,6 +177,7 @@ const buildStepDefs = (IDE_CANDIDATES) => [
167
177
  {
168
178
  name: 'backendAuth',
169
179
  run: async (machine, answers) => {
180
+ stepHeader(11, 12);
170
181
  const { backendFwObj } = answers;
171
182
  if (!backendFwObj) return null;
172
183
  return await selectOptional('Auth strategy:', AUTH_OPTIONS[backendFwObj.value] || [], machine, 10);
@@ -177,6 +188,7 @@ const buildStepDefs = (IDE_CANDIDATES) => [
177
188
  {
178
189
  name: 'ideChoice',
179
190
  run: async (machine, answers) => {
191
+ stepHeader(12, 12);
180
192
  separator();
181
193
  console.log(`\n${bold(blue('Environment'))}`);
182
194
 
@@ -186,7 +198,6 @@ const buildStepDefs = (IDE_CANDIDATES) => [
186
198
 
187
199
  const ideOptions = buildIDEOptions(IDE_CANDIDATES);
188
200
  const detectedIDEs = ideOptions.filter(o => o.detected);
189
- const undetectedIDEs = ideOptions.filter(o => !o.detected && o.cmd);
190
201
  const manualOption = ideOptions.filter(o => !o.cmd);
191
202
  const sorted = [...detectedIDEs, ...manualOption];
192
203
 
package/lib/steps.js CHANGED
@@ -103,13 +103,25 @@ const runQuestions = async (stepDefs, machine) => {
103
103
  if (result === RESTART) return RESTART;
104
104
 
105
105
  if (result === BACK) {
106
- if (i === 0) return RESTART; // nowhere to go back to from step 0
106
+ if (i === 0) return RESTART; // nowhere to go back from step 0
107
+ machine.enterBackNav(i); // record where back-nav started
107
108
  const popped = machine.pop();
108
109
  if (popped) delete answers[popped.stepName];
109
110
  i--;
110
111
  continue;
111
112
  }
112
113
 
114
+ if (result === CONTINUE) {
115
+ // User chose to resume — skip forward to where they first pressed Back
116
+ const resumeTarget = machine.resumeIndex ?? i + 1;
117
+ machine.exitBackNav();
118
+ i = resumeTarget;
119
+ continue;
120
+ }
121
+
122
+ // Normal answer — if re-answering during back-nav, exit back-nav mode
123
+ if (machine.inBackNav) machine.exitBackNav();
124
+
113
125
  machine.push(i, def.name, result);
114
126
  answers[def.name] = result;
115
127
  i++;
package/lib/ui.js CHANGED
@@ -109,6 +109,13 @@ const renderTrajectoryLines = (lines) => {
109
109
  });
110
110
  };
111
111
 
112
+ // ── Step progress header ─────────────────────────────────────────────────────
113
+
114
+ const stepHeader = (stepIndex, totalSteps) => {
115
+ console.log(`
116
+ ${dim(`Step ${stepIndex} of ${totalSteps}`)}`);
117
+ };
118
+
112
119
  // ── Step selection helpers ────────────────────────────────────────────────────
113
120
 
114
121
  const { BACK, RESTART } = require('./steps');
@@ -150,6 +157,7 @@ const selectOptional = async (prompt, items, stepMachine, stepIndex) => {
150
157
  // ── Exports ───────────────────────────────────────────────────────────────────
151
158
 
152
159
  module.exports = {
160
+ stepHeader,
153
161
  c, bold, green, yellow, dim, cyan, blue, red,
154
162
  rl, ask,
155
163
  arrowSelect, arrowConfirm,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "multi-agents-cli",
3
- "version": "1.1.9",
3
+ "version": "1.1.11",
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",