multi-agents-cli 1.0.53 → 1.0.54
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 +9 -0
- package/core/workflow/complete.js +49 -0
- package/init.js +29 -0
- package/package.json +1 -1
package/core/workflow/agent.js
CHANGED
|
@@ -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 = {
|
|
@@ -176,6 +176,55 @@ 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 allowed = (policyScope && policyScope.agentOverrides && policyScope.agentOverrides[agentKey] && policyScope.agentOverrides[agentKey].allowed) || (policyScope && policyScope.allowed) || [];
|
|
191
|
+
const blocked = (policyScope && policyScope.blocked) || [];
|
|
192
|
+
|
|
193
|
+
const changedFiles = execSync('git diff --name-only main...HEAD', { cwd: worktreePath, stdio: 'pipe' })
|
|
194
|
+
.toString().trim().split('\n').filter(Boolean);
|
|
195
|
+
|
|
196
|
+
const matchesGlob = (file, pat) => {
|
|
197
|
+
if (pat.endsWith('/**')) return file.startsWith(pat.slice(0, -3));
|
|
198
|
+
if (pat.includes('*')) {
|
|
199
|
+
const re = pat.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
200
|
+
.replace(/\*\*/g, 'DOUBLESTAR')
|
|
201
|
+
.replace(/\*/g, '[^/]*')
|
|
202
|
+
.replace(/DOUBLESTAR/g, '.*');
|
|
203
|
+
return new RegExp('^' + re + '$').test(file);
|
|
204
|
+
}
|
|
205
|
+
return file === pat;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const violations = changedFiles.filter(file => {
|
|
209
|
+
const isAllowed = allowed.some(pat => matchesGlob(file, pat));
|
|
210
|
+
const isBlocked = blocked.some(pat => matchesGlob(file, pat));
|
|
211
|
+
return !isAllowed || isBlocked;
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
if (violations.length > 0) {
|
|
215
|
+
console.log('\n' + red(' ✗ Scope violation - merge blocked.'));
|
|
216
|
+
console.log(dim(' The following files are outside the allowed scope for ' + scopeMeta.agent + ' (' + scopeMeta.scope + '):\n'));
|
|
217
|
+
violations.forEach(f => console.log(' ' + red('✗') + ' ' + f));
|
|
218
|
+
console.log('\n' + dim(' Fix the violations, then re-run npm run complete.\n'));
|
|
219
|
+
rl.close();
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
console.log(' ' + green('✓') + ' Scope validation passed (' + changedFiles.length + ' files checked)');
|
|
224
|
+
} else {
|
|
225
|
+
console.log(' ' + dim('ℹ scope.json or scope-policy.json not found - skipping validation'));
|
|
226
|
+
}
|
|
227
|
+
|
|
179
228
|
// ── Check TASK.md status ──────────────────────────────────────────────────────
|
|
180
229
|
|
|
181
230
|
const taskMdPath = path.join(worktreePath, 'TASK.md');
|
package/init.js
CHANGED
|
@@ -1201,6 +1201,35 @@ const main = async () => {
|
|
|
1201
1201
|
);
|
|
1202
1202
|
console.log(` ${green('✓')} .scaffold/.config.json written`);
|
|
1203
1203
|
|
|
1204
|
+
// ── Write scope-policy.json ──────────────────────────────────────────────────
|
|
1205
|
+
|
|
1206
|
+
const scopePolicy = {
|
|
1207
|
+
client: {
|
|
1208
|
+
allowed: ['client/**'],
|
|
1209
|
+
blocked: ['backend/**', 'shared/**', 'CONTRACTS.md', 'BUILD_STATE.md', 'TASKS_HISTORY.md'],
|
|
1210
|
+
},
|
|
1211
|
+
backend: {
|
|
1212
|
+
allowed: ['backend/**'],
|
|
1213
|
+
blocked: ['client/**', 'shared/**', 'CONTRACTS.md', 'BUILD_STATE.md', 'TASKS_HISTORY.md'],
|
|
1214
|
+
agentOverrides: {
|
|
1215
|
+
INIT: {
|
|
1216
|
+
allowed: ['backend/**', 'shared/wiring.config.json', 'CONTRACTS.md'],
|
|
1217
|
+
},
|
|
1218
|
+
},
|
|
1219
|
+
},
|
|
1220
|
+
shared: {
|
|
1221
|
+
allowed: ['CLOUD_STATE.md', 'CLOUD.md', 'CLOUD_TEARDOWN.md', '.github/**', 'docker-compose*.yml', 'Dockerfile', '**/.env.example'],
|
|
1222
|
+
blocked: ['client/**', 'backend/**', 'CONTRACTS.md', 'BUILD_STATE.md', 'TASKS_HISTORY.md'],
|
|
1223
|
+
},
|
|
1224
|
+
};
|
|
1225
|
+
|
|
1226
|
+
fs.writeFileSync(
|
|
1227
|
+
path.join(RUNTIME_DIR, 'scope-policy.json'),
|
|
1228
|
+
JSON.stringify(scopePolicy, null, 2),
|
|
1229
|
+
'utf8'
|
|
1230
|
+
);
|
|
1231
|
+
console.log(` ${green('✓')} .scaffold/scope-policy.json written`);
|
|
1232
|
+
|
|
1204
1233
|
// ── Generate BUILD_STATE.md ──────────────────────────────────────────────────
|
|
1205
1234
|
|
|
1206
1235
|
const backendDisplay = backendType === 'integrated'
|
package/package.json
CHANGED