multi-agents-cli 1.0.91 → 1.0.93
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.
- package/core/workflow/agent.js +152 -0
- package/core/workflow/complete.js +41 -0
- package/package.json +1 -1
package/core/workflow/agent.js
CHANGED
|
@@ -863,6 +863,42 @@ const main = async () => {
|
|
|
863
863
|
}
|
|
864
864
|
}
|
|
865
865
|
|
|
866
|
+
|
|
867
|
+
// Soft gate - shared selected but missing prerequisites
|
|
868
|
+
if (project === 'shared') {
|
|
869
|
+
const clientCompleted = buildEntries.filter(e => e.scope === 'client' && e.status === 'COMPLETED');
|
|
870
|
+
const backendCompleted = buildEntries.filter(e => e.scope === 'backend' && e.status === 'COMPLETED');
|
|
871
|
+
const clientUIДone = clientCompleted.some(e => e.agent === 'UI');
|
|
872
|
+
const backendINITDone = backendCompleted.some(e => e.agent === 'INIT');
|
|
873
|
+
const missing = [];
|
|
874
|
+
|
|
875
|
+
if (!clientUIДone) {
|
|
876
|
+
missing.push({ item: 'Client scaffold', detail: 'no completed client work found — SECURITY needs to know what auth patterns the client uses' });
|
|
877
|
+
}
|
|
878
|
+
if (!backendINITDone) {
|
|
879
|
+
missing.push({ item: 'Backend INIT', detail: 'backend not scaffolded yet — SECURITY needs to know the backend auth strategy and DB schema' });
|
|
880
|
+
}
|
|
881
|
+
if (!contracts.hasContent) {
|
|
882
|
+
missing.push({ item: 'CONTRACTS.md', detail: 'no shared types defined — auth utilities should align with established contracts' });
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
if (missing.length > 0) {
|
|
886
|
+
console.log(`\n${yellow(' ⚠ Shared prerequisites not fully met:')}
|
|
887
|
+
`);
|
|
888
|
+
missing.forEach(m => {
|
|
889
|
+
console.log(` ${dim('→')} ${bold(m.item)}`);
|
|
890
|
+
console.log(` ${m.detail}\n`);
|
|
891
|
+
});
|
|
892
|
+
console.log(` ${dim('The agent will proceed autonomously and make assumptions about auth patterns.')}`);
|
|
893
|
+
console.log(` ${dim('These may need revision once client and backend are fully established.\n')}`);
|
|
894
|
+
const proceedIdx = await arrowSelect('Shared prerequisites not met:', [
|
|
895
|
+
{ label: `${green('→')} Proceed anyway` },
|
|
896
|
+
{ label: `${yellow('←')} Go back — pick a different scope ${dim('← recommended')}` },
|
|
897
|
+
], rl);
|
|
898
|
+
if (proceedIdx === 1) continue flowLoop;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
866
902
|
// ── Select agent (with re-selection loop + back to scope) ────────────────────
|
|
867
903
|
|
|
868
904
|
const agentOptions = AGENTS[project] || [];
|
|
@@ -1143,6 +1179,122 @@ const main = async () => {
|
|
|
1143
1179
|
}
|
|
1144
1180
|
}
|
|
1145
1181
|
|
|
1182
|
+
|
|
1183
|
+
// ── Backend simultaneous flow (client/LOGIC only, separate backend, first run) ──
|
|
1184
|
+
let backendLaunchTiming = null;
|
|
1185
|
+
const isSimultaneousCandidate =
|
|
1186
|
+
project === 'client' &&
|
|
1187
|
+
agent === 'LOGIC' &&
|
|
1188
|
+
config.backend?.type === 'separate' &&
|
|
1189
|
+
!argAgent &&
|
|
1190
|
+
!buildEntries.some(e => e.scope === 'backend') &&
|
|
1191
|
+
!tracking?.backend?.INIT?.backendPromptShown;
|
|
1192
|
+
|
|
1193
|
+
if (isSimultaneousCandidate) {
|
|
1194
|
+
separator();
|
|
1195
|
+
console.log(`\n ${yellow('⚡ Backend not started yet')}\n`);
|
|
1196
|
+
console.log(` Your backend has not been scaffolded yet.`);
|
|
1197
|
+
console.log(` Since client/LOGIC establishes integration contracts, this is the ideal moment to plan backend scaffolding.\n`);
|
|
1198
|
+
|
|
1199
|
+
const intentIdx = await arrowSelect('Would you like to include backend/INIT?', [
|
|
1200
|
+
{ label: `${green('→')} Yes — I want to scaffold the backend` },
|
|
1201
|
+
{ label: `${dim('→')} No — continue with client/LOGIC only` },
|
|
1202
|
+
], rl);
|
|
1203
|
+
|
|
1204
|
+
if (intentIdx === 0) {
|
|
1205
|
+
console.log('');
|
|
1206
|
+
const timingIdx = await arrowSelect('When would you like to scaffold backend/INIT?', [
|
|
1207
|
+
{ label: `${green('→')} Now — open backend/INIT workspace alongside this LOGIC session\n ${dim('A second IDE window will open. Both agents run in parallel.')}` },
|
|
1208
|
+
{ label: `${dim('→')} After — launch backend/INIT when LOGIC completes\n ${dim('complete.js will prompt you immediately after merge.')}` },
|
|
1209
|
+
{ label: `${dim('→')} Skip — changed my mind, continue with client/LOGIC only` },
|
|
1210
|
+
], rl);
|
|
1211
|
+
|
|
1212
|
+
if (timingIdx === 0) backendLaunchTiming = 'now';
|
|
1213
|
+
else if (timingIdx === 1) backendLaunchTiming = 'after';
|
|
1214
|
+
else backendLaunchTiming = 'skip';
|
|
1215
|
+
} else {
|
|
1216
|
+
backendLaunchTiming = 'skip';
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
// Write flag to backend/INIT tracking slot — prevents prompt from showing again
|
|
1220
|
+
guards.updateTrackingSlot(tracking, 'backend', 'INIT', {
|
|
1221
|
+
backendPromptShown: true,
|
|
1222
|
+
backendLaunchTiming,
|
|
1223
|
+
}, ROOT);
|
|
1224
|
+
|
|
1225
|
+
// Inject backend coordination block into TASK.md contextSection
|
|
1226
|
+
if (backendLaunchTiming === 'now') {
|
|
1227
|
+
contextSection += `\n---\n\n## Backend Coordination\n\nBackend/INIT is running in parallel in a separate workspace.\nEstablish CONTRACTS.md entries early — the backend agent will consume them.\nDo not wait for backend confirmation. Work autonomously on client scope.\n`;
|
|
1228
|
+
} else if (backendLaunchTiming === 'after') {
|
|
1229
|
+
contextSection += `\n---\n\n## Backend Coordination\n\nBackend/INIT will launch after this LOGIC session completes.\nBe deliberate about API contracts — document all integration points in CONTRACTS.md.\nThe backend agent will use these as its starting reference.\n`;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
// If Now — create backend/INIT worktree immediately
|
|
1233
|
+
if (backendLaunchTiming === 'now') {
|
|
1234
|
+
const beTimestamp = Date.now();
|
|
1235
|
+
const beBranchName = `agent/backend/init/${beTimestamp}`;
|
|
1236
|
+
const beWorktreeName = `${config.projectName.toLowerCase().replace(/\s+/g, '-')}-backend-init-${beTimestamp}`;
|
|
1237
|
+
const beWorktreePath = path.join(ROOT, 'worktrees', beWorktreeName);
|
|
1238
|
+
|
|
1239
|
+
console.log(`\n ${dim('Setting up backend/INIT workspace...')}\n`);
|
|
1240
|
+
|
|
1241
|
+
try {
|
|
1242
|
+
execSync(`git worktree add "${beWorktreePath}" -b ${beBranchName}`, { cwd: ROOT, stdio: 'pipe' });
|
|
1243
|
+
|
|
1244
|
+
// Framework files
|
|
1245
|
+
const beScope = generateClaudeScope({ project: 'backend', agent: 'INIT', branchName: beBranchName, worktreePath: beWorktreePath });
|
|
1246
|
+
fs.writeFileSync(path.join(beWorktreePath, '.claude-scope'), beScope, 'utf8');
|
|
1247
|
+
|
|
1248
|
+
const beTask = AGENT_DESCRIPTIONS.backend?.INIT || 'scaffolds backend architecture, folder structure, DB setup, wiring config and contracts';
|
|
1249
|
+
const beTaskMd = generateTaskMd({ project: 'backend', agent: 'INIT', task: beTask, branchName: beBranchName, contextSection: '', remoteSetupSection: '' });
|
|
1250
|
+
fs.writeFileSync(path.join(beWorktreePath, 'TASK.md'), beTaskMd, 'utf8');
|
|
1251
|
+
|
|
1252
|
+
const bePkg = {
|
|
1253
|
+
name: `${config.projectName.toLowerCase().replace(/\s+/g, '-')}-worktree`,
|
|
1254
|
+
version: '1.0.0', private: true,
|
|
1255
|
+
scripts: {
|
|
1256
|
+
init: 'multi-agents init',
|
|
1257
|
+
agent: `node "${ROOT}/.workflow/run.js"`,
|
|
1258
|
+
restart: `node "${ROOT}/.workflow/restart.js"`,
|
|
1259
|
+
reset: `node "${ROOT}/.workflow/reset.js"`,
|
|
1260
|
+
complete: `node "${ROOT}/.workflow/complete.js"`,
|
|
1261
|
+
},
|
|
1262
|
+
};
|
|
1263
|
+
fs.writeFileSync(path.join(beWorktreePath, 'package.json'), JSON.stringify(bePkg, null, 2), 'utf8');
|
|
1264
|
+
|
|
1265
|
+
const beGitignore = ['# Framework files — never commit these to the agent branch', 'package.json', '.claude-scope', 'scope.json', 'TASK.md', '.vscode/', '.idea/', '.zed/', 'node_modules/'].join('\n') + '\n';
|
|
1266
|
+
fs.writeFileSync(path.join(beWorktreePath, '.gitignore'), beGitignore, 'utf8');
|
|
1267
|
+
|
|
1268
|
+
// Tracking
|
|
1269
|
+
guards.updateTrackingSlot(tracking, 'backend', 'INIT', {
|
|
1270
|
+
branch: beBranchName,
|
|
1271
|
+
timestamp: beTimestamp,
|
|
1272
|
+
launchedAt: new Date().toISOString(),
|
|
1273
|
+
status: 'ACTIVE',
|
|
1274
|
+
worktreePath: beWorktreePath,
|
|
1275
|
+
backendPromptShown: true,
|
|
1276
|
+
backendLaunchTiming: 'now',
|
|
1277
|
+
}, ROOT);
|
|
1278
|
+
|
|
1279
|
+
// TASKS_HISTORY
|
|
1280
|
+
tasksHistory.ensureHistoryFile(ROOT);
|
|
1281
|
+
tasksHistory.writeSessionEntry(ROOT, { scope: 'backend', agent: 'INIT', branch: beBranchName, task: beTask, launchedAt: new Date().toISOString() });
|
|
1282
|
+
|
|
1283
|
+
console.log(` ${green('✓')} Backend/INIT worktree created: worktrees/${beWorktreeName}`);
|
|
1284
|
+
|
|
1285
|
+
// Open backend IDE window
|
|
1286
|
+
const beIdeOpened = openIDE(beWorktreePath);
|
|
1287
|
+
if (!beIdeOpened) console.log(` ${yellow('!')} Could not open IDE for backend — open manually at: ${dim(beWorktreePath)}`);
|
|
1288
|
+
console.log(` ${green('✓')} Backend/INIT workspace open — start a Claude Code session there when ready\n`);
|
|
1289
|
+
|
|
1290
|
+
} catch (e) {
|
|
1291
|
+
console.log(` ${yellow('⚠')} Could not create backend/INIT worktree: ${e.message}`);
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
separator();
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1146
1298
|
separator();
|
|
1147
1299
|
|
|
1148
1300
|
// ── Confirm ───────────────────────────────────────────────────────────────────
|
|
@@ -448,6 +448,47 @@ const main = async () => {
|
|
|
448
448
|
separator();
|
|
449
449
|
console.log('');
|
|
450
450
|
|
|
451
|
+
|
|
452
|
+
// ── Backend/INIT launch prompt (if user chose 'after' during LOGIC session) ──
|
|
453
|
+
try {
|
|
454
|
+
const _tracking = guards.loadTracking(ROOT, config);
|
|
455
|
+
const beSlot = _tracking?.backend?.INIT;
|
|
456
|
+
if (
|
|
457
|
+
_scope === 'logic' &&
|
|
458
|
+
_parts[0] === 'agent' && _parts[1] === 'client' &&
|
|
459
|
+
beSlot?.backendLaunchTiming === 'after' &&
|
|
460
|
+
beSlot?.status !== 'ACTIVE' &&
|
|
461
|
+
beSlot?.status !== 'COMPLETED'
|
|
462
|
+
) {
|
|
463
|
+
separator();
|
|
464
|
+
console.log(`\n ${yellow('⚡ Ready to scaffold your backend?')}\n`);
|
|
465
|
+
console.log(` client/LOGIC is merged. This is the ideal moment to launch backend/INIT.\n`);
|
|
466
|
+
|
|
467
|
+
const { spawn: _spawn } = require('child_process');
|
|
468
|
+
const launchIdx = await new Promise(resolve => {
|
|
469
|
+
rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
470
|
+
const choices = [
|
|
471
|
+
'1. Launch backend/INIT now ← recommended',
|
|
472
|
+
'2. Skip — I will handle it manually',
|
|
473
|
+
];
|
|
474
|
+
choices.forEach(c => console.log(` ${c}`));
|
|
475
|
+
rl2.question('\n Select (1-2): ', ans => {
|
|
476
|
+
rl2.close();
|
|
477
|
+
resolve(parseInt(ans));
|
|
478
|
+
});
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
if (launchIdx === 1) {
|
|
482
|
+
console.log(`\n ${green('✓')} Launching backend/INIT...\n`);
|
|
483
|
+
rl.close();
|
|
484
|
+
_spawn('node', [path.join(ROOT, '.workflow', 'agent.js'), '--scope=backend', '--agent=INIT'], {
|
|
485
|
+
cwd: ROOT, stdio: 'inherit',
|
|
486
|
+
});
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
} catch { /* best-effort */ }
|
|
491
|
+
|
|
451
492
|
rl.close();
|
|
452
493
|
};
|
|
453
494
|
|
package/package.json
CHANGED