multi-agents-cli 1.0.53 → 1.0.55

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.
@@ -1183,6 +1183,15 @@ Mark each step complete. Only proceed to the task below when all are checked.
1183
1183
  );
1184
1184
  console.log(` ${green('✓')} .claude-scope written`);
1185
1185
 
1186
+ // ── Write scope.json ─────────────────────────────────────────────────────────
1187
+
1188
+ fs.writeFileSync(
1189
+ path.join(worktreePath, 'scope.json'),
1190
+ JSON.stringify({ agent, scope: project, branch: branchName, policy: project }, null, 2),
1191
+ 'utf8'
1192
+ );
1193
+ console.log(` ${green('✓')} scope.json written`);
1194
+
1186
1195
  // ── Generate IDE settings ─────────────────────────────────────────────────────
1187
1196
 
1188
1197
  const excludedFolders = {
@@ -1391,7 +1400,7 @@ ${excludedUrls}
1391
1400
  console.log(` ${bold('2.')} ${bold(yellow('Open a NEW session in Claude Code CLI or Claude Code Extension - and type go or start to initiate'))}`);
1392
1401
  console.log(dim(' Do NOT reuse a previous session.\n'));
1393
1402
  console.log(` ${bold('3.')} Start the session and let the agent run.\n`);
1394
- console.log(dim(' The agent will run `') + cyan('npm run complete') + dim('` autonomously when the task is done.\n'));
1403
+ console.log(` ${bold('4.')} When the agent is done, run: ${cyan('npm run complete')} to merge the task into main.\n`);
1395
1404
  separator();
1396
1405
  console.log('');
1397
1406
 
@@ -176,6 +176,58 @@ const main = async () => {
176
176
 
177
177
  const { path: worktreePath, branch: branchName } = selectedWorktree;
178
178
 
179
+ // ── Scope validation (Trust + Verify) ────────────────────────────────────────
180
+
181
+ const scopeJsonPath = path.join(worktreePath, 'scope.json');
182
+ const scopePolicyPath = path.join(ROOT, '.scaffold', 'scope-policy.json');
183
+
184
+ if (fs.existsSync(scopeJsonPath) && fs.existsSync(scopePolicyPath)) {
185
+ const scopeMeta = JSON.parse(fs.readFileSync(scopeJsonPath, 'utf8'));
186
+ const scopePolicy = JSON.parse(fs.readFileSync(scopePolicyPath, 'utf8'));
187
+
188
+ const policyScope = scopePolicy[scopeMeta.policy];
189
+ const agentKey = scopeMeta.agent && scopeMeta.agent.toUpperCase();
190
+ const override = policyScope && policyScope.agentOverrides && policyScope.agentOverrides[agentKey];
191
+ const scaffoldedFlag = (config.scaffolded || {})[scopeMeta.scope];
192
+ const overrideActive = override && (!override.onlyBeforeScaffolded || !scaffoldedFlag);
193
+ const allowed = (overrideActive && override.allowed) || (policyScope && policyScope.allowed) || [];
194
+ const blocked = (policyScope && policyScope.blocked) || [];
195
+
196
+ const changedFiles = execSync('git diff --name-only main...HEAD', { cwd: worktreePath, stdio: 'pipe' })
197
+ .toString().trim().split('\n').filter(Boolean);
198
+
199
+ const matchesGlob = (file, pat) => {
200
+ if (pat.endsWith('/**')) return file.startsWith(pat.slice(0, -3));
201
+ if (pat.includes('*')) {
202
+ const re = pat.replace(/[.+^${}()|[\]\\]/g, '\\$&')
203
+ .replace(/\*\*/g, 'DOUBLESTAR')
204
+ .replace(/\*/g, '[^/]*')
205
+ .replace(/DOUBLESTAR/g, '.*');
206
+ return new RegExp('^' + re + '$').test(file);
207
+ }
208
+ return file === pat;
209
+ };
210
+
211
+ const violations = changedFiles.filter(file => {
212
+ const isAllowed = allowed.some(pat => matchesGlob(file, pat));
213
+ const isBlocked = blocked.some(pat => matchesGlob(file, pat));
214
+ return !isAllowed || isBlocked;
215
+ });
216
+
217
+ if (violations.length > 0) {
218
+ console.log('\n' + red(' ✗ Scope violation - merge blocked.'));
219
+ console.log(dim(' The following files are outside the allowed scope for ' + scopeMeta.agent + ' (' + scopeMeta.scope + '):\n'));
220
+ violations.forEach(f => console.log(' ' + red('✗') + ' ' + f));
221
+ console.log('\n' + dim(' Fix the violations, then re-run npm run complete.\n'));
222
+ rl.close();
223
+ return;
224
+ }
225
+
226
+ console.log(' ' + green('✓') + ' Scope validation passed (' + changedFiles.length + ' files checked)');
227
+ } else {
228
+ console.log(' ' + dim('ℹ scope.json or scope-policy.json not found - skipping validation'));
229
+ }
230
+
179
231
  // ── Check TASK.md status ──────────────────────────────────────────────────────
180
232
 
181
233
  const taskMdPath = path.join(worktreePath, 'TASK.md');
@@ -304,6 +356,22 @@ const main = async () => {
304
356
  }
305
357
  } catch { /* best-effort */ }
306
358
 
359
+ // ── Update scaffold flags ──────────────────────────────────────────────────
360
+
361
+ try {
362
+ const parts = branchName.split('/');
363
+ if (parts.length >= 4) {
364
+ const scope = parts[1];
365
+ const agent = parts[2].toUpperCase();
366
+ const cfgPath = path.join(ROOT, '.scaffold', '.config.json');
367
+ const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
368
+ if (!cfg.scaffolded) cfg.scaffolded = { client: false, backend: false };
369
+ if (scope === 'client' && agent === 'UI') cfg.scaffolded.client = true;
370
+ if (scope === 'backend' && agent === 'INIT') cfg.scaffolded.backend = true;
371
+ fs.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2), 'utf8');
372
+ }
373
+ } catch { /* best-effort */ }
374
+
307
375
  // ── Push to origin ────────────────────────────────────────────────────────────
308
376
 
309
377
  try {
package/init.js CHANGED
@@ -1192,6 +1192,10 @@ const main = async () => {
1192
1192
  orm: backendOrm,
1193
1193
  auth: backendAuth,
1194
1194
  },
1195
+ scaffolded: {
1196
+ client: false,
1197
+ backend: false,
1198
+ },
1195
1199
  };
1196
1200
 
1197
1201
  fs.writeFileSync(
@@ -1201,6 +1205,42 @@ const main = async () => {
1201
1205
  );
1202
1206
  console.log(` ${green('✓')} .scaffold/.config.json written`);
1203
1207
 
1208
+ // ── Write scope-policy.json ──────────────────────────────────────────────────
1209
+
1210
+ const scopePolicy = {
1211
+ client: {
1212
+ allowed: ['client/**'],
1213
+ blocked: ['backend/**', 'shared/**', 'CONTRACTS.md', 'BUILD_STATE.md', 'TASKS_HISTORY.md'],
1214
+ agentOverrides: {
1215
+ UI: {
1216
+ allowed: ['client/**', 'shared/wiring.config.json'],
1217
+ onlyBeforeScaffolded: true,
1218
+ },
1219
+ },
1220
+ },
1221
+ backend: {
1222
+ allowed: ['backend/**'],
1223
+ blocked: ['client/**', 'shared/**', 'CONTRACTS.md', 'BUILD_STATE.md', 'TASKS_HISTORY.md'],
1224
+ agentOverrides: {
1225
+ INIT: {
1226
+ allowed: ['backend/**', 'shared/wiring.config.json', 'CONTRACTS.md'],
1227
+ onlyBeforeScaffolded: true,
1228
+ },
1229
+ },
1230
+ },
1231
+ shared: {
1232
+ allowed: ['CLOUD_STATE.md', 'CLOUD.md', 'CLOUD_TEARDOWN.md', '.github/**', 'docker-compose*.yml', 'Dockerfile', '**/.env.example'],
1233
+ blocked: ['client/**', 'backend/**', 'CONTRACTS.md', 'BUILD_STATE.md', 'TASKS_HISTORY.md'],
1234
+ },
1235
+ };
1236
+
1237
+ fs.writeFileSync(
1238
+ path.join(RUNTIME_DIR, 'scope-policy.json'),
1239
+ JSON.stringify(scopePolicy, null, 2),
1240
+ 'utf8'
1241
+ );
1242
+ console.log(` ${green('✓')} .scaffold/scope-policy.json written`);
1243
+
1204
1244
  // ── Generate BUILD_STATE.md ──────────────────────────────────────────────────
1205
1245
 
1206
1246
  const backendDisplay = backendType === 'integrated'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "multi-agents-cli",
3
- "version": "1.0.53",
3
+ "version": "1.0.55",
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",