multi-agents-cli 1.0.14 → 1.0.15

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 +97 -752
  2. package/package.json +1 -1
package/init.js CHANGED
@@ -1,60 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * multi-agents - Project Initializer
5
- * Run with: npm run init (inside existing project)
6
- * or: multi-agents init my-project (global CLI)
4
+ * Multi-Agent Monorepo Template - Project Initializer
5
+ * Run with: npm run init
7
6
  *
8
7
  * Runs once. Locked after completion via .scaffold/.initialized
8
+ * Delete .scaffold/.initialized to re-run.
9
9
  */
10
10
 
11
11
  const readline = require('readline');
12
12
  const fs = require('fs');
13
13
  const path = require('path');
14
-
15
- // ── Prompts (arrow-key navigation) ───────────────────────────────────────────
16
-
17
- let prompts;
18
- try { prompts = require('prompts'); } catch { prompts = null; }
19
-
20
- const arrowSelect = async (message, choices, rl, showBack = false) => {
21
- const allChoices = showBack
22
- ? [...choices, { label: dim('← Restart configuration') }]
23
- : choices;
24
-
25
- if (prompts && process.stdin.isTTY) {
26
- const res = await prompts({
27
- type: 'select',
28
- name: 'value',
29
- message,
30
- choices: allChoices.map((c, i) => ({ title: typeof c === 'string' ? c : c.label, value: i })),
31
- }, { onCancel: () => process.exit(0) });
32
- return res.value ?? 0;
33
- }
34
- allChoices.forEach((c, i) => console.log(` ${dim(`${i + 1}.`)} ${typeof c === 'string' ? c : c.label}`));
35
- return new Promise(resolve => {
36
- rl.question(`\n Select (1-${allChoices.length}): `, ans => {
37
- const n = parseInt(ans) - 1;
38
- resolve(!isNaN(n) && n >= 0 && n < allChoices.length ? n : 0);
39
- });
40
- });
41
- };
42
-
43
- const arrowConfirm = async (message, rl) => {
44
- if (prompts && process.stdin.isTTY) {
45
- const res = await prompts({
46
- type: 'confirm',
47
- name: 'value',
48
- message,
49
- initial: true,
50
- }, { onCancel: () => process.exit(0) });
51
- return res.value ?? true;
52
- }
53
- return new Promise(resolve => {
54
- rl.question(`${message} (y/n): `, ans => resolve(ans.toLowerCase() !== 'n'));
55
- });
56
- };
57
- const os = require('os');
58
14
  const { execSync, spawn } = require('child_process');
59
15
 
60
16
  // ── Colors ────────────────────────────────────────────────────────────────────
@@ -78,69 +34,58 @@ const cyan = (s) => `${c.cyan}${s}${c.reset}`;
78
34
  const blue = (s) => `${c.blue}${s}${c.reset}`;
79
35
  const red = (s) => `${c.red}${s}${c.reset}`;
80
36
 
81
- // ── CLI argument handling ─────────────────────────────────────────────────────
82
-
83
- const args = process.argv.slice(2);
84
- const isGlobalCLI = args[0] === 'init' && args[1];
85
- const projectArg = isGlobalCLI ? args[1] : null;
86
-
87
- if (isGlobalCLI) {
88
- const targetDir = path.resolve(process.cwd(), projectArg);
89
-
90
- if (fs.existsSync(targetDir)) {
91
- console.log(`\n${red(` ✗ Directory "${projectArg}" already exists.`)}`);
92
- console.log(dim(' Choose a different project name.\n'));
93
- process.exit(1);
94
- }
37
+ // ── Lock check ────────────────────────────────────────────────────────────────
95
38
 
96
- fs.mkdirSync(targetDir, { recursive: true });
97
- process.chdir(targetDir);
39
+ const ROOT = __dirname;
40
+ const RUNTIME_DIR = path.join(ROOT, '.scaffold');
41
+ const LOCK_FILE = path.join(RUNTIME_DIR, '.initialized');
98
42
 
99
- // Initialize git
100
- try {
101
- execSync('git init -b main', { cwd: targetDir, stdio: 'pipe' });
102
- execSync('git commit --allow-empty -m "init: project created"', { cwd: targetDir, stdio: 'pipe' });
103
- } catch {
104
- // Fallback for older git versions that don't support -b flag
105
- try {
106
- execSync('git init', { cwd: targetDir, stdio: 'pipe' });
107
- execSync('git checkout -b main', { cwd: targetDir, stdio: 'pipe' });
108
- execSync('git commit --allow-empty -m "init: project created"', { cwd: targetDir, stdio: 'pipe' });
109
- } catch { /* continue */ }
110
- }
43
+ // Ensure .scaffold/ runtime dir exists
44
+ const fs_temp = require('fs');
45
+ if (!fs_temp.existsSync(path.join(__dirname, '.scaffold'))) {
46
+ fs_temp.mkdirSync(path.join(__dirname, '.scaffold'), { recursive: true });
111
47
  }
112
48
 
113
- // ── Lock check ────────────────────────────────────────────────────────────────
49
+ if (fs.existsSync(LOCK_FILE)) {
50
+ const ts = fs.readFileSync(LOCK_FILE, 'utf8').trim();
51
+ const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
52
+ const ask2 = (q) => new Promise((resolve) => rl2.question(q, (a) => resolve(a.trim())));
114
53
 
115
- const ROOT = process.cwd();
116
- const RUNTIME_DIR = path.join(ROOT, '.scaffold');
117
- const LOCK_FILE = path.join(RUNTIME_DIR, '.initialized');
54
+ console.log(`\n${yellow(' This project has already been initialized.')}`);
55
+ console.log(dim(` Initialized on: ${ts}\n`));
56
+ console.log(` ${dim('1.')} Continue — run ${cyan('npm run launch')}`);
57
+ console.log(` ${dim('2.')} Reset — delete config and re-run initialization`);
58
+ console.log(` ${dim('3.')} Exit\n`);
118
59
 
119
- // Ensure .scaffold/ exists
120
- if (!fs.existsSync(RUNTIME_DIR)) {
121
- fs.mkdirSync(RUNTIME_DIR, { recursive: true });
60
+ const choice = await ask2(` ${bold('Select')} ${dim('(1-3)')}: `);
61
+
62
+ if (choice === '1') {
63
+ console.log('');
64
+ rl2.close();
65
+ const { spawn } = require('child_process');
66
+ const child = spawn('node', [path.join(ROOT, '.workflow', 'launch.js')], {
67
+ stdio: 'inherit',
68
+ cwd: ROOT,
69
+ });
70
+ child.on('exit', (code) => process.exit(code));
71
+ return;
72
+ } else if (choice === '2') {
73
+ console.log(yellow('\n Resetting configuration...\n'));
74
+ fs.unlinkSync(LOCK_FILE);
75
+ const configPath = path.join(__dirname, '.config.json');
76
+ if (fs.existsSync(configPath)) fs.unlinkSync(configPath);
77
+ rl2.close();
78
+ console.log(green(' Reset complete. Re-running initialization...\n'));
79
+ // Continue to main() below
80
+ } else {
81
+ console.log(dim('\n Exited.\n'));
82
+ rl2.close();
83
+ process.exit(0);
84
+ }
122
85
  }
123
86
 
124
87
  // ── Decision tree ─────────────────────────────────────────────────────────────
125
88
 
126
- const FRAMEWORK_CONVENTIONS = {
127
- client: {
128
- 'Next.js': { root: 'client', typesDir: 'client/src/types', importAlias: '@/types' },
129
- 'Angular': { root: 'client', typesDir: 'client/src/app/core/types', importAlias: null },
130
- 'Nuxt': { root: 'client', typesDir: 'client/types', importAlias: '~/types' },
131
- 'SvelteKit': { root: 'client', typesDir: 'client/src/lib/types', importAlias: '$lib/types' },
132
- 'Vite+React': { root: 'client', typesDir: 'client/src/types', importAlias: null },
133
- 'Remix': { root: 'client', typesDir: 'client/app/types', importAlias: null },
134
- },
135
- backend: {
136
- 'Express': { root: 'backend', typesDir: 'backend/src/types', routesDir: 'backend/src/routes' },
137
- 'NestJS': { root: 'backend', dtoDir: 'backend/src/dto', entitiesDir:'backend/src/entities' },
138
- 'Fastify': { root: 'backend', typesDir: 'backend/src/types', routesDir: 'backend/src/routes' },
139
- 'FastAPI': { root: 'backend', schemasDir: 'backend/app/schemas', modelsDir: 'backend/app/models' },
140
- 'Django': { root: 'backend', schemasDir: 'backend/api/serializers', modelsDir: 'backend/api/models' },
141
- },
142
- };
143
-
144
89
  const CLIENT_FRAMEWORKS = [
145
90
  { label: 'Next.js', value: 'Next.js', language: 'TypeScript', integratedBackend: true },
146
91
  { label: 'Angular', value: 'Angular', language: 'TypeScript', integratedBackend: false },
@@ -155,7 +100,6 @@ const BACKEND_FRAMEWORKS = [
155
100
  { label: 'Express', value: 'Express', language: 'TypeScript' },
156
101
  { label: 'Fastify', value: 'Fastify', language: 'TypeScript' },
157
102
  { label: 'Django', value: 'Django', language: 'Python' },
158
- { label: 'FastAPI', value: 'FastAPI', language: 'Python' },
159
103
  { label: 'Laravel', value: 'Laravel', language: 'PHP' },
160
104
  { label: 'Rails', value: 'Rails', language: 'Ruby' },
161
105
  ];
@@ -191,7 +135,6 @@ const ORM_OPTIONS = {
191
135
  'Express': ['Prisma', 'TypeORM', 'Drizzle', 'Sequelize'],
192
136
  'Fastify': ['Prisma', 'TypeORM', 'Drizzle'],
193
137
  'Django': ['Django ORM (built-in)', 'SQLAlchemy'],
194
- 'FastAPI': ['SQLAlchemy', 'Tortoise ORM', 'Beanie (MongoDB)'],
195
138
  'Laravel': ['Eloquent (built-in)'],
196
139
  'Rails': ['Active Record (built-in)'],
197
140
  };
@@ -201,264 +144,11 @@ const AUTH_OPTIONS = {
201
144
  'Express': ['Passport.js', 'JWT-only', 'OAuth2'],
202
145
  'Fastify': ['fastify-jwt', 'Passport.js', 'OAuth2'],
203
146
  'Django': ['Django Auth (built-in)', 'DRF TokenAuth', 'OAuth2'],
204
- 'FastAPI': ['JWT-only', 'OAuth2', 'FastAPI-Users'],
205
147
  'Laravel': ['Laravel Sanctum', 'Laravel Passport', 'JWT'],
206
148
  'Rails': ['Devise', 'JWT', 'OAuth2'],
207
149
  };
208
150
 
209
- const IDE_CANDIDATES = [
210
- {
211
- cmd: 'code',
212
- name: 'VS Code',
213
- mac: { app: 'Visual Studio Code', args: ['--new-window'] },
214
- win: { paths: ['{LOCALAPPDATA}\\Programs\\Microsoft VS Code\\Code.exe', '{ProgramFiles}\\Microsoft VS Code\\Code.exe'], args: ['--new-window'] },
215
- linux: { paths: ['/snap/bin/code', '/usr/bin/code', '/usr/local/bin/code'], args: ['--new-window'] },
216
- },
217
- {
218
- cmd: 'cursor',
219
- name: 'Cursor',
220
- mac: { app: 'Cursor', args: ['--new-window'] },
221
- win: { paths: ['{LOCALAPPDATA}\\Programs\\cursor\\Cursor.exe'], args: ['--new-window'] },
222
- linux: { paths: ['/usr/bin/cursor', '/opt/cursor/cursor'], args: ['--new-window'] },
223
- },
224
- {
225
- cmd: 'webstorm',
226
- name: 'WebStorm',
227
- mac: { app: 'WebStorm', toolboxApp: 'WebStorm', args: [] },
228
- win: { paths: [
229
- '{LOCALAPPDATA}\\JetBrains\\Toolbox\\scripts\\webstorm.cmd',
230
- '{LOCALAPPDATA}\\Programs\\WebStorm\\bin\\webstorm64.exe',
231
- ], args: [] },
232
- linux: { paths: [
233
- `${os.homedir()}/.local/bin/webstorm`,
234
- '/opt/webstorm/bin/webstorm.sh',
235
- '/snap/webstorm/current/bin/webstorm.sh',
236
- ], args: [] },
237
- },
238
- {
239
- cmd: 'idea',
240
- name: 'IntelliJ IDEA',
241
- mac: { app: 'IntelliJ IDEA', toolboxApp: 'IntelliJ IDEA', args: [] },
242
- win: { paths: [
243
- '{LOCALAPPDATA}\\JetBrains\\Toolbox\\scripts\\idea.cmd',
244
- '{LOCALAPPDATA}\\Programs\\IntelliJ IDEA Community Edition\\bin\\idea64.exe',
245
- '{ProgramFiles}\\JetBrains\\IntelliJ IDEA\\bin\\idea64.exe',
246
- ], args: [] },
247
- linux: { paths: [
248
- `${os.homedir()}/.local/bin/idea`,
249
- '/opt/idea/bin/idea.sh',
250
- '/snap/intellij-idea-community/current/bin/idea.sh',
251
- ], args: [] },
252
- },
253
- {
254
- cmd: 'zed',
255
- name: 'Zed',
256
- mac: { app: 'Zed', args: [] },
257
- win: { paths: [], args: [] },
258
- linux: { paths: ['/usr/bin/zed', `${os.homedir()}/.local/bin/zed`], args: [] },
259
- },
260
- {
261
- cmd: null,
262
- name: 'Other / Manual',
263
- note: 'prints worktree path, open it yourself',
264
- mac: null,
265
- win: null,
266
- linux:null,
267
- },
268
- ];
269
-
270
- // Expands {LOCALAPPDATA} / {ProgramFiles} placeholders for Windows paths
271
- const expandWinPath = (p) =>
272
- p.replace('{LOCALAPPDATA}', process.env.LOCALAPPDATA || '')
273
- .replace('{ProgramFiles}', process.env.ProgramFiles || 'C:\\Program Files');
274
-
275
- const buildIDEOptions = () => {
276
- const platform = process.platform;
277
-
278
- return IDE_CANDIDATES.map(ide => {
279
- if (!ide.cmd) {
280
- const noteStr = ide.note ? dim(` (${ide.note})`) : '';
281
- return { ...ide, detected: false, strategy: 'manual', label: `${ide.name} ${dim('→')}${noteStr}` };
282
- }
283
-
284
- let detected = false;
285
- let strategy = 'cli';
286
-
287
- if (platform === 'darwin' && ide.mac) {
288
- // Mac — check .app bundle in /Applications, ~/Applications, and JetBrains Toolbox
289
- const system = `/Applications/${ide.mac.app}.app`;
290
- const user = path.join(os.homedir(), 'Applications', `${ide.mac.app}.app`);
291
- const toolbox = path.join(os.homedir(), 'Applications', 'JetBrains Toolbox', `${ide.mac.app}.app`);
292
- detected = fs.existsSync(system) || fs.existsSync(user) || fs.existsSync(toolbox);
293
- if (detected) strategy = 'mac-app';
294
-
295
- } else if (platform === 'win32' && ide.win) {
296
- // Windows — CLI first, then known exe paths
297
- try {
298
- execSync(`where ${ide.cmd}`, { stdio: 'pipe' });
299
- detected = true;
300
- strategy = 'cli';
301
- } catch {
302
- const expanded = (ide.win.paths || []).map(expandWinPath);
303
- detected = expanded.some(p => fs.existsSync(p));
304
- if (detected) strategy = 'win-exe';
305
- }
306
-
307
- } else if (platform === 'linux' && ide.linux) {
308
- // Linux — CLI first, then known install paths
309
- try {
310
- execSync(`which ${ide.cmd}`, { stdio: 'pipe' });
311
- detected = true;
312
- strategy = 'cli';
313
- } catch {
314
- detected = (ide.linux.paths || []).some(p => fs.existsSync(p));
315
- if (detected) strategy = 'linux-path';
316
- }
317
- }
318
-
319
- const statusStr = detected ? green('✓ detected') : dim('✗ not found');
320
- const noteStr = ide.note ? dim(` (${ide.note})`) : '';
321
- return {
322
- ...ide,
323
- detected,
324
- strategy,
325
- label: `${ide.name} ${statusStr}${noteStr}`,
326
- };
327
- });
328
- };
329
-
330
- const verifyIDE = (ide) => {
331
- const platform = process.platform;
332
-
333
- if (ide.strategy === 'mac-app' && ide.mac) {
334
- // Mac — confirm .app exists and try to read version from plist
335
- const appPath = `/Applications/${ide.mac.app}.app`;
336
- if (!fs.existsSync(appPath) && !fs.existsSync(path.join(os.homedir(), 'Applications', `${ide.mac.app}.app`))) {
337
- return { ok: false };
338
- }
339
- try {
340
- const version = execSync(
341
- `defaults read "/Applications/${ide.mac.app}.app/Contents/Info.plist" CFBundleShortVersionString`,
342
- { stdio: 'pipe', encoding: 'utf8' }
343
- ).trim();
344
- return { ok: true, version };
345
- } catch {
346
- return { ok: true, version: null };
347
- }
348
- }
349
-
350
- // Windows exe / Linux path / CLI — try --version
351
- try {
352
- const cmd = ide.strategy === 'win-exe'
353
- ? `"${(ide.win?.paths || []).map(expandWinPath).find(p => fs.existsSync(p))}"`
354
- : ide.strategy === 'linux-path'
355
- ? `"${(ide.linux?.paths || []).find(p => fs.existsSync(p))}"`
356
- : `"${ide.cmd}"`;
357
- const result = execSync(`${cmd} --version`, { stdio: 'pipe', encoding: 'utf8' });
358
- const version = result.split('\n')[0].trim();
359
- return { ok: true, version };
360
- } catch {
361
- return { ok: false };
362
- }
363
- };
364
-
365
- // ── Tracking structure ────────────────────────────────────────────────────────
366
-
367
- const emptySlot = () => ({
368
- branch: null,
369
- timestamp: null,
370
- launchedAt: null,
371
- status: null,
372
- missingCount: 0,
373
- worktreePath: null,
374
- });
375
-
376
- const generateTrackingStructure = (config) => {
377
- const bt = config.backend?.type;
378
-
379
- const structure = {
380
- client: {
381
- UI: emptySlot(),
382
- LOGIC: emptySlot(),
383
- FORMS: emptySlot(),
384
- ROUTING: emptySlot(),
385
- TESTING: emptySlot(),
386
- ACCESSIBILITY: emptySlot(),
387
- },
388
- shared: {
389
- SECURITY: emptySlot(),
390
- },
391
- };
392
-
393
- if (bt === 'separate') {
394
- structure.backend = {
395
- API: emptySlot(),
396
- LOGIC: emptySlot(),
397
- AUTH: emptySlot(),
398
- DB: emptySlot(),
399
- EVENTS: emptySlot(),
400
- JOBS: emptySlot(),
401
- TESTING: emptySlot(),
402
- };
403
- }
404
-
405
- return structure;
406
- };
407
-
408
- // ── GitHub remote setup ───────────────────────────────────────────────────────
409
-
410
- const detectGitHubUser = () => {
411
- try {
412
- return execSync('gh api user --jq .login',
413
- { encoding: 'utf8', stdio: 'pipe' }).trim();
414
- } catch {}
415
- try {
416
- return execSync('git config user.name',
417
- { encoding: 'utf8', stdio: 'pipe' }).trim();
418
- } catch {}
419
- return null;
420
- };
421
-
422
- const setupUserRemote = (ROOT, projectName) => {
423
- let currentOrigin = null;
424
- try {
425
- currentOrigin = execSync('git remote get-url origin',
426
- { cwd: ROOT, encoding: 'utf8', stdio: 'pipe' }).trim();
427
- } catch {}
428
-
429
- // Already has their own remote — nothing to do
430
- if (currentOrigin && !currentOrigin.includes('multi-agents-template')) return;
431
-
432
- // Demote template origin to upstream
433
- if (currentOrigin?.includes('multi-agents-template')) {
434
- try {
435
- execSync('git remote remove origin', { cwd: ROOT, stdio: 'pipe' });
436
- execSync(`git remote add upstream ${currentOrigin}`, { cwd: ROOT, stdio: 'pipe' });
437
- console.log(dim(' ℹ Template remote moved to upstream'));
438
- } catch {}
439
- }
440
-
441
- // Write flag — agent will handle remote setup on first session
442
- const flagPath = path.join(ROOT, '.scaffold', '.remote-setup-needed');
443
- fs.writeFileSync(flagPath, JSON.stringify({
444
- projectName,
445
- createdAt: new Date().toISOString(),
446
- }), 'utf8');
447
-
448
- console.log(`\n ${yellow('ℹ No remote configured.')} Your first agent session will set this up.`);
449
- console.log(dim(' All work stays local until then.\n'));
450
- };
451
-
452
- const renderTrajectoryLines = (lines) => {
453
- const HEADERS = ['Benefits', 'Best for', 'Use agents for', 'Handle manually'];
454
- lines.forEach(l => {
455
- if (!l) { console.log(''); return; }
456
- if (l.startsWith('⚠')) console.log(` ${yellow(l)}`);
457
- else if (HEADERS.includes(l)) console.log(`\n ${bold(l)}`);
458
- else if (l.startsWith('·')) console.log(` ${l}`);
459
- else console.log(` ${dim(l)}`);
460
- });
461
- };
151
+ // ── Readline ──────────────────────────────────────────────────────────────────
462
152
 
463
153
  const rl = readline.createInterface({
464
154
  input: process.stdin,
@@ -478,25 +168,30 @@ const showList = (items, showSkip = false) => {
478
168
  if (showSkip) console.log(` ${dim('0.')} Skip ${dim('(agent will propose when needed)')}`);
479
169
  };
480
170
 
481
- // Sentinel value returned when user picks ← Restart
482
- const BACK = Symbol('BACK');
483
-
484
171
  const selectRequired = async (prompt, items) => {
485
- const idx = await arrowSelect(prompt, items.map(i => ({ label: typeof i === 'string' ? i : i.label })), rl, true);
486
- if (idx === items.length) return BACK;
487
- return items[idx];
172
+ while (true) {
173
+ console.log(`\n${bold(prompt)}`);
174
+ showList(items);
175
+ const input = await ask(`\n ${bold('Select')} ${dim(`(1-${items.length})`)}: `);
176
+ const index = parseInt(input) - 1;
177
+ if (!isNaN(index) && index >= 0 && index < items.length) return items[index];
178
+ console.log(yellow(` Please enter a number between 1 and ${items.length}.`));
179
+ }
488
180
  };
489
181
 
490
182
  const selectOptional = async (prompt, items) => {
491
183
  if (!items || items.length === 0) return null;
492
- const choices = [
493
- ...items.map(i => ({ label: typeof i === 'string' ? i : i.label })),
494
- { label: dim('Skip (agent will propose when needed)') },
495
- ];
496
- const idx = await arrowSelect(prompt, choices, rl, true);
497
- if (idx === choices.length) return BACK;
498
- if (idx === items.length) return null;
499
- return typeof items[idx] === 'string' ? items[idx] : items[idx].value;
184
+ while (true) {
185
+ console.log(`\n${bold(prompt)}`);
186
+ showList(items, true);
187
+ const input = await ask(`\n ${bold('Select')} ${dim(`(0-${items.length})`)}: `);
188
+ if (input === '0' || input === '') return null;
189
+ const index = parseInt(input) - 1;
190
+ if (!isNaN(index) && index >= 0 && index < items.length) {
191
+ return typeof items[index] === 'string' ? items[index] : items[index].value;
192
+ }
193
+ console.log(yellow(` Invalid selection. Please enter a number between 0 and ${items.length}.`));
194
+ }
500
195
  };
501
196
 
502
197
  const separator = () => console.log(`\n${dim('─'.repeat(60))}`);
@@ -556,53 +251,13 @@ const copyDir = (src, dest) => {
556
251
  // ── Main ──────────────────────────────────────────────────────────────────────
557
252
 
558
253
  const main = async () => {
559
-
560
- // ── Lock check ───────────────────────────────────────────────────────────────
561
-
562
- if (fs.existsSync(LOCK_FILE)) {
563
- const ts = fs.readFileSync(LOCK_FILE, 'utf8').trim();
564
- const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
565
- const ask2 = (q) => new Promise((resolve) => rl2.question(q, (a) => resolve(a.trim())));
566
-
567
- console.log(`\n${yellow(' This project has already been initialized.')}`);
568
- console.log(dim(` Initialized on: ${ts}\n`));
569
- console.log(` ${dim('1.')} Continue — run ${cyan('npm run launch')}`);
570
- console.log(` ${dim('2.')} Reset — delete config and re-run initialization`);
571
- console.log(` ${dim('3.')} Exit\n`);
572
-
573
- const choice = await ask2(` ${bold('Select')} ${dim('(1-3)')}: `);
574
-
575
- if (choice === '1') {
576
- console.log('');
577
- rl2.close();
578
- const child = spawn('node', [path.join(ROOT, '.workflow', 'launch.js')], {
579
- stdio: 'inherit',
580
- cwd: ROOT,
581
- });
582
- child.on('exit', (code) => process.exit(code));
583
- return;
584
- } else if (choice === '2') {
585
- console.log(yellow('\n Resetting configuration...\n'));
586
- fs.unlinkSync(LOCK_FILE);
587
- const configPath = path.join(RUNTIME_DIR, '.config.json');
588
- if (fs.existsSync(configPath)) fs.unlinkSync(configPath);
589
- rl2.close();
590
- console.log(green(' Reset complete. Re-running initialization...\n'));
591
- // Fall through to run init again
592
- } else {
593
- console.log(dim('\n Exited.\n'));
594
- rl2.close();
595
- return;
596
- }
597
- }
598
-
599
254
  console.log('\n');
600
255
  console.log(bold(cyan(' Multi-Agent Monorepo Template')));
601
256
  console.log(dim(' Project Initializer\n'));
602
257
  separator();
603
258
 
604
259
  console.log(`\n${bold('Let\'s configure your project.')}`);
605
- console.log(dim(' Use arrow keys to select. Optional fields can be skipped.\n'));
260
+ console.log(dim(' Required fields must be selected. Optional fields can be skipped (press 0 or Enter).\n'));
606
261
  console.log(dim(' Skipped fields will be resolved by the agent when first needed.\n'));
607
262
 
608
263
  // ── Project name ────────────────────────────────────────────────────────────
@@ -613,14 +268,6 @@ const main = async () => {
613
268
  if (!projectName) console.log(yellow(' Project name is required. Please enter a name.'));
614
269
  }
615
270
 
616
- const restartIfBack = (val) => {
617
- if (val !== BACK) return false;
618
- rl.close();
619
- const { spawn } = require('child_process');
620
- spawn('node', [__filename], { stdio: 'inherit', cwd: ROOT }).on('exit', c => process.exit(c));
621
- return true;
622
- };
623
-
624
271
  separator();
625
272
 
626
273
  // ── Client ──────────────────────────────────────────────────────────────────
@@ -628,14 +275,10 @@ const main = async () => {
628
275
  console.log(`\n${bold(blue('Client configuration'))}`);
629
276
 
630
277
  const clientFw = await selectRequired('* Client framework (required):', CLIENT_FRAMEWORKS);
631
- if (restartIfBack(clientFw)) return;
632
278
  const clientLang = clientFw.language;
633
279
  const clientState = await selectOptional('State management:', STATE_OPTIONS[clientFw.value] || []);
634
- if (restartIfBack(clientState)) return;
635
280
  const clientUi = await selectOptional('UI library:', UI_OPTIONS[clientFw.value] || []);
636
- if (restartIfBack(clientUi)) return;
637
281
  const clientStyle = await selectOptional('Styling:', STYLING_OPTIONS);
638
- if (restartIfBack(clientStyle)) return;
639
282
 
640
283
  separator();
641
284
 
@@ -650,11 +293,11 @@ const main = async () => {
650
293
  let backendOrm = null;
651
294
  let backendAuth = null;
652
295
  let backendType = null;
653
- let backendFwObj = null;
654
296
 
655
297
  if (clientFw.integratedBackend) {
656
298
  console.log(dim(` ${clientFw.value} supports server-side rendering and API routes.\n`));
657
- useIntegratedBackend = await arrowConfirm(`Use integrated backend (${clientFw.value} API routes/SSR) instead of a separate backend?`, rl);
299
+ const integratedAnswer = await ask(` ${bold('Use integrated backend')} ${dim(`(${clientFw.value} API routes/SSR)`)} ${dim('instead of a separate backend? (y/n)')}: `);
300
+ useIntegratedBackend = integratedAnswer.toLowerCase() === 'y';
658
301
 
659
302
  if (useIntegratedBackend) {
660
303
  backendType = 'integrated';
@@ -665,87 +308,25 @@ const main = async () => {
665
308
  if (!useIntegratedBackend) {
666
309
  console.log(dim(' You can skip the backend framework and decide later.\n'));
667
310
 
668
- const backendChoices = [
669
- ...BACKEND_FRAMEWORKS.map(f => ({ label: f.label || f.value })),
670
- { label: dim('Skip (decide later)') },
671
- ];
672
- const backendIdx = await arrowSelect('Backend framework:', backendChoices, rl);
673
- backendFwObj = backendIdx === BACKEND_FRAMEWORKS.length ? null : BACKEND_FRAMEWORKS[backendIdx];
311
+ const backendFwObj = await (async () => {
312
+ console.log(`\n${bold('Backend framework:')}`);
313
+ showList(BACKEND_FRAMEWORKS, true);
314
+ const input = await ask(`\n ${bold('Select')} ${dim(`(0-${BACKEND_FRAMEWORKS.length})`)}: `);
315
+ if (input === '0' || input === '') return null;
316
+ const index = parseInt(input) - 1;
317
+ if (isNaN(index) || index < 0 || index >= BACKEND_FRAMEWORKS.length) return null;
318
+ return BACKEND_FRAMEWORKS[index];
319
+ })();
674
320
 
675
321
  backendFw = backendFwObj ? backendFwObj.value : null;
676
322
  backendLang = backendFwObj ? backendFwObj.language : null;
677
323
  backendOrm = backendFw ? await selectOptional('ORM / database layer:', ORM_OPTIONS[backendFw] || []) : null;
678
- if (restartIfBack(backendOrm)) return;
679
324
  backendAuth = backendFw ? await selectOptional('Auth strategy:', AUTH_OPTIONS[backendFw] || []) : null;
680
- if (restartIfBack(backendAuth)) return;
681
325
  backendType = backendFw ? 'separate' : null;
682
326
  }
683
327
 
684
328
  separator();
685
329
 
686
- // ── Environment ─────────────────────────────────────────────────────────────
687
-
688
- console.log(`\n${bold(blue('Environment'))}`);
689
-
690
- const osName = { darwin: 'macOS', win32: 'Windows', linux: 'Linux' }[process.platform] || process.platform;
691
- console.log(`\n ${dim('OS detected:')} ${bold(osName)}`);
692
- console.log(dim(' Scanning for installed IDEs...\n'));
693
-
694
- const ideOptions = buildIDEOptions();
695
-
696
- const detectedIDEs = ideOptions.filter(o => o.detected);
697
- const undetectedIDEs = ideOptions.filter(o => !o.detected && o.cmd);
698
- const manualOption = ideOptions.filter(o => !o.cmd);
699
-
700
- // Detected first → undetected → manual
701
- const sortedIdeOptions = [...detectedIDEs, ...undetectedIDEs, ...manualOption];
702
-
703
- if (detectedIDEs.length > 1) {
704
- console.log(`\n ${yellow('Multiple IDEs found on this machine')} — select your preference:\n`);
705
- } else if (detectedIDEs.length === 1) {
706
- console.log(`\n ${green(`1 IDE found:`)} ${bold(detectedIDEs[0].name)}\n`);
707
- } else {
708
- console.log(`\n ${yellow('No IDEs detected on this machine.')}\n`);
709
- }
710
-
711
- let ideChoice;
712
- while (true) {
713
- ideChoice = await selectRequired('* IDE / editor (required):', sortedIdeOptions);
714
- if (restartIfBack(ideChoice)) return;
715
-
716
- // ── Confirmation ──────────────────────────────────────────────────────────
717
- if (ideChoice.cmd && !ideChoice.detected) {
718
- console.log(`\n ${yellow('⚠')} ${bold(ideChoice.name)} was not detected on this machine.`);
719
- console.log(dim(' It may not open automatically when launching a task.\n'));
720
- if (!await arrowConfirm('Continue with this IDE anyway?', rl)) {
721
- console.log(dim(' Re-selecting...\n'));
722
- continue;
723
- }
724
- }
725
-
726
- // ── Double-check ──────────────────────────────────────────────────────────
727
- if (!ideChoice.cmd) {
728
- // Manual — no verification needed
729
- console.log(dim(' Manual mode — worktree path will be printed at launch.'));
730
- break;
731
- }
732
-
733
- console.log(dim(`\n Verifying ${ideChoice.name}...`));
734
- const verified = verifyIDE(ideChoice);
735
-
736
- if (verified.ok) {
737
- const versionStr = verified.version ? dim(` (${verified.version})`) : '';
738
- console.log(` ${green('✓')} ${ideChoice.name} confirmed${versionStr}`);
739
- break;
740
- }
741
-
742
- console.log(` ${yellow('!')} Could not verify ${ideChoice.name}. The CLI may not be installed or accessible.`);
743
- if (await arrowConfirm('Continue with this IDE anyway?', rl)) break;
744
- console.log(dim(' Re-selecting...\n'));
745
- }
746
-
747
- separator();
748
-
749
330
  // ── Summary ─────────────────────────────────────────────────────────────────
750
331
 
751
332
  console.log(`\n${bold('Review your configuration:')}\n`);
@@ -761,17 +342,12 @@ const main = async () => {
761
342
  summaryLine('ORM', backendOrm);
762
343
  summaryLine('Auth', backendAuth);
763
344
  }
764
- summaryLine('IDE / Editor', ideChoice.name);
765
345
 
766
346
  console.log('');
767
347
  console.log(dim(' y = confirm | n = abort | e = edit (start over)\n'));
768
- const confirmIdx = await arrowSelect('Confirm and write to config files?', [
769
- { label: `${green('✓')} Confirm — write config and set up project` },
770
- { label: `${yellow('↺')} Restart — redo configuration` },
771
- { label: `${red('✗')} Abort` },
772
- ], rl);
348
+ const confirm = await ask(`${bold('Confirm and write to config files?')} ${dim('(y/n/e)')}: `);
773
349
 
774
- if (confirmIdx === 1) {
350
+ if (confirm.toLowerCase() === 'e') {
775
351
  console.log(yellow('\n Restarting configuration...\n'));
776
352
  rl.close();
777
353
  const { spawn } = require('child_process');
@@ -780,7 +356,7 @@ const main = async () => {
780
356
  return;
781
357
  }
782
358
 
783
- if (confirmIdx === 2) {
359
+ if (confirm.toLowerCase() !== 'y') {
784
360
  console.log(yellow('\n Aborted. No files were changed.\n'));
785
361
  rl.close();
786
362
  return;
@@ -812,8 +388,6 @@ const main = async () => {
812
388
  copyDir(path.join(TEMPLATES, 'shared'), path.join(ROOT, 'shared'));
813
389
  if (backendType === 'separate') {
814
390
  copyDir(path.join(TEMPLATES, 'backend'), path.join(ROOT, 'backend'));
815
- // Ensure backend/ is tracked by git even before the API agent scaffolds
816
- fs.writeFileSync(path.join(ROOT, 'backend', '.gitkeep'), '', 'utf8');
817
391
  }
818
392
  fs.copyFileSync(path.join(TEMPLATES, 'CLAUDE.md'), path.join(ROOT, 'CLAUDE.md'));
819
393
  fs.copyFileSync(path.join(TEMPLATES, 'CONTRACTS.md'), path.join(ROOT, 'CONTRACTS.md'));
@@ -863,33 +437,12 @@ const main = async () => {
863
437
  ensureGitignore('.agents-core/');
864
438
  ensureGitignore('.scaffold/');
865
439
  ensureGitignore('.workflow/');
866
-
867
- // Remove template-specific gitignore entries so generated files can be committed
868
- const gitignorePath = path.join(ROOT, '.gitignore');
869
- let gitignoreContent = fs.readFileSync(gitignorePath, 'utf8');
870
- ['client/', 'backend/', 'shared/', 'CLAUDE.md', 'CONTRACTS.md', 'BUILD_STATE.md'].forEach(entry => {
871
- gitignoreContent = gitignoreContent.replace(`\n${entry}`, '');
872
- gitignoreContent = gitignoreContent.replace(`${entry}\n`, '');
873
- gitignoreContent = gitignoreContent.replace(entry, '');
874
- });
875
- fs.writeFileSync(gitignorePath, gitignoreContent.trim() + '\n', 'utf8');
876
440
  console.log(` ${green('✓')} .gitignore updated`);
877
441
 
878
442
  // ── Write .config.json ───────────────────────────────────────────────────────
879
443
 
880
444
  const config = {
881
445
  projectName,
882
- ide: {
883
- name: ideChoice.name,
884
- strategy: ideChoice.strategy,
885
- cmd: ideChoice.cmd || null,
886
- app: ideChoice.mac?.app || null,
887
- openArgs: process.platform === 'darwin' ? (ideChoice.mac?.args || [])
888
- : process.platform === 'win32' ? (ideChoice.win?.args || [])
889
- : (ideChoice.linux?.args || []),
890
- winPaths: (ideChoice.win?.paths || []).map(expandWinPath),
891
- linuxPaths: ideChoice.linux?.paths || [],
892
- },
893
446
  client: {
894
447
  framework: clientFw.value,
895
448
  language: clientLang,
@@ -989,67 +542,6 @@ If a dependency is not met:
989
542
  fs.writeFileSync(path.join(ROOT, 'BUILD_STATE.md'), buildState, 'utf8');
990
543
  console.log(` ${green('✓')} BUILD_STATE.md generated`);
991
544
 
992
- // ── Generate user project package.json ───────────────────────────────────────
993
-
994
- const userPackage = {
995
- name: projectName.toLowerCase().replace(/\s+/g, '-'),
996
- version: '1.0.0',
997
- private: true,
998
- dependencies: {
999
- prompts: '^2.4.2',
1000
- },
1001
- scripts: {
1002
- launch: 'cd "$(git rev-parse --git-common-dir)/.." && node .workflow/launch.js',
1003
- complete: 'cd "$(git rev-parse --git-common-dir)/.." && node .workflow/complete.js',
1004
- },
1005
- };
1006
- fs.writeFileSync(path.join(ROOT, 'package.json'), JSON.stringify(userPackage, null, 2), 'utf8');
1007
- console.log(` ${green('✓')} package.json generated`);
1008
-
1009
- // ── Install dependencies ──────────────────────────────────────────────────────
1010
-
1011
- try {
1012
- console.log(dim(' Installing dependencies...'));
1013
- execSync('npm install', { cwd: ROOT, stdio: 'pipe' });
1014
- console.log(` ${green('✓')} Dependencies installed`);
1015
- } catch {
1016
- console.log(yellow(' ⚠ npm install failed — run npm install manually before launching'));
1017
- }
1018
-
1019
- // ── Tracking ──────────────────────────────────────────────────────────────────
1020
-
1021
- const trackingPath = path.join(RUNTIME_DIR, '.tracking.json');
1022
- if (!fs.existsSync(trackingPath)) {
1023
- const trackingStructure = generateTrackingStructure(config);
1024
- fs.writeFileSync(trackingPath, JSON.stringify(trackingStructure, null, 2), 'utf8');
1025
- console.log(` ${green('✓')} .tracking.json generated`);
1026
- } else {
1027
- console.log(dim(' ℹ .tracking.json already exists — preserved'));
1028
- }
1029
-
1030
- // ── Generate .paths.json ──────────────────────────────────────────────────────
1031
-
1032
- const pathsMap = {};
1033
- const clientConventions = FRAMEWORK_CONVENTIONS.client[clientFw?.value] || {};
1034
- const backendConventions = FRAMEWORK_CONVENTIONS.backend[backendFwObj?.value] || {};
1035
-
1036
- if (Object.keys(clientConventions).length) {
1037
- pathsMap.client = {};
1038
- Object.entries(clientConventions).forEach(([key, value]) => {
1039
- pathsMap.client[key] = { expected: value, current: null, status: 'pending' };
1040
- });
1041
- }
1042
-
1043
- if (Object.keys(backendConventions).length) {
1044
- pathsMap.backend = {};
1045
- Object.entries(backendConventions).forEach(([key, value]) => {
1046
- pathsMap.backend[key] = { expected: value, current: null, status: 'pending' };
1047
- });
1048
- }
1049
-
1050
- fs.writeFileSync(path.join(RUNTIME_DIR, '.paths.json'), JSON.stringify(pathsMap, null, 2), 'utf8');
1051
- console.log(` ${green('✓')} .paths.json generated`);
1052
-
1053
545
  // ── Lock ─────────────────────────────────────────────────────────────────────
1054
546
 
1055
547
  fs.writeFileSync(LOCK_FILE, new Date().toISOString());
@@ -1066,180 +558,33 @@ If a dependency is not met:
1066
558
  console.log(dim(' git add . && git commit -m "init: project configuration"'));
1067
559
  }
1068
560
 
1069
- // ── Pre-commit hook — block direct commits to main ───────────────────────────
1070
-
1071
- try {
1072
- const hooksDir = path.join(ROOT, '.git', 'hooks');
1073
- const hookPath = path.join(hooksDir, 'pre-commit');
1074
- const hookScript = `#!/bin/sh
1075
- branch=$(git symbolic-ref --short HEAD 2>/dev/null)
1076
- if [ "$branch" = "main" ]; then
1077
- echo ""
1078
- echo " ⚠ Direct commits to main are not allowed."
1079
- echo " Use npm run launch to start a task."
1080
- echo ""
1081
- exit 1
1082
- fi
1083
- `;
1084
- if (!fs.existsSync(hookPath)) {
1085
- fs.writeFileSync(hookPath, hookScript, { mode: 0o755 });
1086
- console.log(dim(' ℹ Pre-commit hook installed — direct main commits blocked'));
1087
- }
1088
- } catch { /* best-effort */ }
1089
-
1090
- // ── Remote setup ─────────────────────────────────────────────────────────────
1091
-
1092
- setupUserRemote(ROOT, projectName);
1093
-
1094
- // ── Trajectory selection ─────────────────────────────────────────────────────
561
+ // ── Chain to launch.js ────────────────────────────────────────────────────────
1095
562
 
1096
563
  separator();
1097
564
  console.log(`\n${bold(green(' Project initialized successfully!'))}\n`);
1098
- console.log(` ${bold('How do you want to build?')}\n`);
1099
-
1100
- console.log(` ${dim('1.')} ${bold('Multi-Agent Driven Orchestration')}`);
1101
- console.log(`${dim(' · Every task should start with npm run launch')}`);
1102
- console.log(`${dim(' · Each agent runs in its own git worktree — an isolated branch')}`);
1103
- console.log(`${dim(' and folder that merges back into main via npm run complete')}`);
1104
- console.log(`${dim(' · Faster builds and lower token spend than a single long session')}`);
1105
- console.log(`${yellow(' ⚠ If you commit directly to main yourself, you bypass the framework')}`);
1106
- console.log(`${yellow(' and break task tracking for any active agent branches')}\n`);
1107
-
1108
- console.log(` ${dim('2.')} ${bold('Shared Orchestration')}`);
1109
- console.log(`${dim(' · You and agents co-build — each owning a defined part of the codebase')}`);
1110
- console.log(`${dim(' · Agent tasks run in git worktrees; your work happens directly in the project')}`);
1111
- console.log(`${dim(' · Agent tasks are token-efficient; your tasks cost only what you prompt')}`);
1112
- console.log(`${dim(' · Define boundaries before work begins — agents for well-scoped work,')}`);
1113
- console.log(`${dim(' you for areas where requirements are still evolving')}`);
1114
- console.log(`${yellow(' ⚠ If you and an agent touch the same file, expect merge conflicts')}\n`);
1115
-
1116
- const TRAJECTORY_DETAILS = {
1117
- '1': {
1118
- label: 'Multi-Agent Driven Orchestration',
1119
- full: [
1120
- 'Every task must start with npm run launch.',
1121
- 'Agent sessions load only task-relevant context, enabling reliable',
1122
- 'chaining, predictable behavior, and efficient token usage.',
1123
- '',
1124
- '⚠ If you commit directly to main yourself, you bypass the framework',
1125
- ' and break task tracking for any active agent branches.',
1126
- '',
1127
- 'Benefits',
1128
- '· Scoped context per task',
1129
- '· Predictable token consumption',
1130
- '· Lower cost than maintaining large, persistent sessions',
1131
- '· Better isolation between parallel work streams',
1132
- ],
1133
- next: 'launch',
1134
- },
1135
- '2': {
1136
- label: 'Shared Orchestration',
1137
- full: [
1138
- 'You and agents work in the same codebase, each with clearly',
1139
- 'defined ownership. File boundaries must be established before',
1140
- 'work begins and remain fixed throughout the task.',
1141
- 'Agents excel when scope is well-defined;',
1142
- 'you excel when requirements are evolving.',
1143
- '',
1144
- 'Use agents for',
1145
- '· Multi-file features',
1146
- '· Structured implementation work',
1147
- '· Domain-specific tasks',
1148
- '· Changes expected to exceed ~200 lines',
1149
- '',
1150
- 'Handle manually',
1151
- '· Targeted bug fixes',
1152
- '· Configuration changes',
1153
- '· Small refactors',
1154
- '· Single-file edits under ~50 lines',
1155
- '',
1156
- '⚠ Avoid overlapping file ownership. Working on the same files',
1157
- ' as an active agent will create merge conflicts when merged.',
1158
- '⚠ If you are spending time repeatedly clarifying scope, stop',
1159
- ' and do the task yourself. The coordination cost often',
1160
- ' exceeds the implementation cost.',
1161
- '',
1162
- 'Benefits',
1163
- '· Maximum agent efficiency for well-defined work',
1164
- '· Human flexibility where requirements change',
1165
- '· Scales well across large projects',
1166
- '· Most adaptable workflow — requires the most discipline',
1167
- ],
1168
- next: 'launch',
1169
- },
1170
- };
1171
565
 
1172
- // Wrap in loop to support back navigation
1173
- let trajectory = null;
1174
- trajectoryLoop: while (true) {
1175
- const trajIdx = await arrowSelect('How do you want to build?', [
1176
- { label: bold('Multi-Agent Driven Orchestration') },
1177
- { label: bold('Shared Orchestration') },
1178
- ], rl);
1179
- trajectory = String(trajIdx + 1);
566
+ const launchInput = await ask(` ${bold('Ready to launch your first task?')} ${dim('(y/n — default: n)')}: `);
567
+ const launch = launchInput.toLowerCase() || 'n';
1180
568
 
1181
- const selected = TRAJECTORY_DETAILS[trajectory];
1182
- separator();
1183
- console.log(`\n ${green('✓')} ${bold(selected.label)}\n`);
1184
- renderTrajectoryLines(selected.full);
569
+ if (launch === 'y') {
570
+ rl.close();
1185
571
  console.log('');
1186
-
1187
- const confirmIdx = await arrowSelect('Confirm?', [
1188
- { label: `${green('✓')} Confirm` },
1189
- { label: `${yellow('←')} Back — pick differently` },
1190
- ], rl);
1191
- if (confirmIdx === 0) break trajectoryLoop;
1192
- trajectory = null;
572
+ const child = spawn('node', [path.join(ROOT, '.workflow', 'launch.js')], {
573
+ stdio: 'inherit',
574
+ cwd: ROOT,
575
+ });
576
+ child.on('exit', (code) => process.exit(code));
577
+ } else {
578
+ console.log('');
579
+ console.log(` ${bold('When ready, run:')}`);
580
+ console.log(` ${cyan('npm run launch')}\n`);
1193
581
  separator();
1194
- console.log(`\n ${bold('How do you want to build?')}\n`);
1195
- console.log(` ${dim('1.')} ${bold('Multi-Agent Driven Orchestration')}`);
1196
- console.log(`${dim(' · Every task should start with npm run launch')}`);
1197
- console.log(`${dim(' · Each agent runs in its own git worktree — an isolated branch')}`);
1198
- console.log(`${dim(' and folder that merges back into main via npm run complete')}`);
1199
- console.log(`${dim(' · Faster builds and lower token spend than a single long session')}`);
1200
- console.log(`${yellow(' ⚠ If you commit directly to main yourself, you bypass the framework')}`);
1201
- console.log(`${yellow(' and break task tracking for any active agent branches')}\n`);
1202
- console.log(` ${dim('2.')} ${bold('Shared Orchestration')}`);
1203
- console.log(`${dim(' · You and agents co-build — each owning a defined part of the codebase')}`);
1204
- console.log(`${dim(' · Agent tasks run in git worktrees; your work happens directly in the project')}`);
1205
- console.log(`${dim(' · Agent tasks are token-efficient; your tasks cost only what you prompt')}`);
1206
- console.log(`${dim(' · Define boundaries before work begins — agents for well-scoped work,')}`);
1207
- console.log(`${dim(' you for areas where requirements are still evolving')}`);
1208
- console.log(`${yellow(' ⚠ If you and an agent touch the same file, expect merge conflicts')}\n`);
1209
- }
1210
-
1211
- const selected = TRAJECTORY_DETAILS[trajectory];
1212
-
1213
- // Store trajectory in config
1214
- try {
1215
- const cfg = JSON.parse(fs.readFileSync(path.join(RUNTIME_DIR, '.config.json'), 'utf8'));
1216
- cfg.trajectory = selected.label.toLowerCase().replace(/ /g, '-');
1217
- fs.writeFileSync(path.join(RUNTIME_DIR, '.config.json'), JSON.stringify(cfg, null, 2), 'utf8');
1218
- } catch { /* best-effort */ }
1219
-
1220
- if (selected.next === 'launch') {
1221
- const launchConfirm = await arrowConfirm('Ready to launch your first task?', rl);
1222
- if (launchConfirm) {
1223
- rl.close();
1224
- console.log('');
1225
- const child = spawn('node', [path.join(ROOT, '.workflow', 'launch.js')], {
1226
- stdio: 'inherit',
1227
- cwd: ROOT,
1228
- });
1229
- child.on('exit', (code) => process.exit(code));
1230
- return;
1231
- }
582
+ console.log('');
583
+ rl.close();
1232
584
  }
1233
-
1234
- console.log('');
1235
- console.log(` ${bold('When ready, run:')}`);
1236
- console.log(` ${cyan('npm run launch')}\n`);
1237
- separator();
1238
- console.log('');
1239
- rl.close();
1240
585
  };
1241
586
 
1242
587
  main().catch((err) => {
1243
588
  console.error('\n Error:', err.message);
1244
589
  process.exit(1);
1245
- });
590
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "multi-agents-cli",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
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",