@agentbridge1/cli 0.0.5 → 0.0.6
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/dist/build-info.json +4 -4
- package/dist/commands/accept.js +4 -4
- package/dist/commands/check.js +10 -1
- package/dist/commands/connect.js +2 -2
- package/dist/commands/doctor.js +98 -22
- package/dist/commands/proof-guidance.js +30 -0
- package/dist/commands/recover.js +109 -22
- package/dist/commands/setup-mcp.js +22 -1
- package/dist/commands/start.js +33 -63
- package/dist/commands/verify.js +124 -91
- package/dist/commands/watch.js +428 -113
- package/dist/error-catalog.js +50 -15
- package/dist/gates.js +3 -3
- package/dist/git-evidence.js +2 -0
- package/dist/http.js +29 -0
- package/dist/index.js +46 -30
- package/dist/init.js +124 -18
- package/dist/local-memory.js +33 -0
- package/dist/local-proof.js +158 -0
- package/dist/local-session-mirror.js +247 -0
- package/dist/local-supervision.js +250 -0
- package/dist/mcp/agentbridge-mcp.js +22947 -0
- package/dist/mcp/agentbridge-mcp.js.map +7 -0
- package/dist/mcp-runtime.js +31 -0
- package/dist/preflight-changed-files.js +24 -17
- package/dist/proof-obligations.js +155 -0
- package/dist/recovery-reconcile.js +183 -0
- package/dist/server-sync.js +24 -0
- package/dist/session-state.js +119 -21
- package/dist/session.js +9 -2
- package/dist/supervision.js +100 -6
- package/package.json +5 -2
package/dist/error-catalog.js
CHANGED
|
@@ -44,7 +44,7 @@ const CATALOG = {
|
|
|
44
44
|
title: "No active tracked work session.",
|
|
45
45
|
what: "No active tracked work session was found for this command.",
|
|
46
46
|
why: "Proof, verify, accept, and handoff require a bound work session.",
|
|
47
|
-
next:
|
|
47
|
+
next: "Run `agentbridge watch` beside Cursor for live supervision, or `agentbridge verify -- <test command>` to record proof.",
|
|
48
48
|
blocksAcceptance: true,
|
|
49
49
|
},
|
|
50
50
|
WORK_CONTEXT_BINDING_REQUIRED: {
|
|
@@ -53,7 +53,7 @@ const CATALOG = {
|
|
|
53
53
|
title: "Work session binding required.",
|
|
54
54
|
what: "This command needs a linked local/server work session.",
|
|
55
55
|
why: "The CLI and server must agree on the active change request and session.",
|
|
56
|
-
next: 'Run `agentbridge
|
|
56
|
+
next: 'Run `agentbridge watch --allow-dirty` for default review, or `agentbridge watch --task "<task>" --scope "<path>"` for strict mode.',
|
|
57
57
|
blocksAcceptance: true,
|
|
58
58
|
},
|
|
59
59
|
PROOF_STALE_AFTER_CHANGE: {
|
|
@@ -70,8 +70,8 @@ const CATALOG = {
|
|
|
70
70
|
category: "SCOPE_DRIFT_ERROR",
|
|
71
71
|
title: "Scope drift detected.",
|
|
72
72
|
what: "The agent changed a file outside the declared scope.",
|
|
73
|
-
why: "Out-of-scope edits
|
|
74
|
-
next: 'Options: (1) revert the out-of-scope file, (2)
|
|
73
|
+
why: "Out-of-scope edits are not safe to trust until reviewed or rescoped.",
|
|
74
|
+
next: 'Options: (1) revert the out-of-scope file, (2) widen strict scope via `agentbridge watch --task "<work>" --scope "<wider-path>"`, or (3) split into a separate task.',
|
|
75
75
|
blocksAcceptance: true,
|
|
76
76
|
},
|
|
77
77
|
HANDOFF_REQUIRED: {
|
|
@@ -176,9 +176,12 @@ const CATALOG = {
|
|
|
176
176
|
code: "START_MISSING_ACTIVE_AGENT",
|
|
177
177
|
category: "IDENTITY_ERROR",
|
|
178
178
|
title: "No active agent configured.",
|
|
179
|
-
what: "Neither activeAgentId nor --agent was provided for start.",
|
|
180
|
-
why: "
|
|
181
|
-
next:
|
|
179
|
+
what: "Neither activeAgentId nor --agent was provided for strict tracked start.",
|
|
180
|
+
why: "Strict tracked work (--summary/--scope, --resume, or --change-request) must bind to a WorkIdentity in this project.",
|
|
181
|
+
next: [
|
|
182
|
+
"For strict tracked work: run `agentbridge identity list` then `agentbridge use <agent-id>`, or pass --agent.",
|
|
183
|
+
"For room-level watching only: run `agentbridge start` with no strict flags.",
|
|
184
|
+
].join("\n"),
|
|
182
185
|
},
|
|
183
186
|
START_EXECUTION_SURFACE_REQUIRED: {
|
|
184
187
|
code: "START_EXECUTION_SURFACE_REQUIRED",
|
|
@@ -237,10 +240,10 @@ const CATALOG = {
|
|
|
237
240
|
START_CR_OWNERSHIP_MISMATCH: {
|
|
238
241
|
code: "START_CR_OWNERSHIP_MISMATCH",
|
|
239
242
|
category: "START_ERROR",
|
|
240
|
-
title: "
|
|
241
|
-
what: "
|
|
242
|
-
why: "
|
|
243
|
-
next: "
|
|
243
|
+
title: "Tracked task belongs to another connected agent.",
|
|
244
|
+
what: "AgentBridge found an old tracked task owned by a different connected agent.",
|
|
245
|
+
why: "AgentBridge will not let one connected agent automatically take over another agent's tracked work.",
|
|
246
|
+
next: "Run `agentbridge watch` for live supervision, or switch back to the original connected agent if you intend to resume old tracked work.",
|
|
244
247
|
},
|
|
245
248
|
START_CR_NOT_EXECUTABLE: {
|
|
246
249
|
code: "START_CR_NOT_EXECUTABLE",
|
|
@@ -293,6 +296,33 @@ const CATALOG = {
|
|
|
293
296
|
next: "Run `agentbridge verify -- <command>` then `agentbridge check`.",
|
|
294
297
|
blocksAcceptance: true,
|
|
295
298
|
},
|
|
299
|
+
PROOF_TOO_WEAK: {
|
|
300
|
+
code: "PROOF_TOO_WEAK",
|
|
301
|
+
category: "PROOF_ERROR",
|
|
302
|
+
title: "Proof too weak.",
|
|
303
|
+
what: "Verification ran, but proof strength is below what this change requires.",
|
|
304
|
+
why: "High-risk or source changes need stronger proof than file-existence checks.",
|
|
305
|
+
next: "Rerun with a stronger command from proof guidance (e.g. `agentbridge verify -- npm test`).",
|
|
306
|
+
blocksAcceptance: true,
|
|
307
|
+
},
|
|
308
|
+
PROOF_NOT_RELEVANT: {
|
|
309
|
+
code: "PROOF_NOT_RELEVANT",
|
|
310
|
+
category: "PROOF_ERROR",
|
|
311
|
+
title: "Proof not relevant.",
|
|
312
|
+
what: "Recorded proof does not cover the files or impact of this change.",
|
|
313
|
+
why: "Proof must match the changed files — unrelated passing tests do not count.",
|
|
314
|
+
next: "Rerun verification scoped to the changed files listed in the acceptance report.",
|
|
315
|
+
blocksAcceptance: true,
|
|
316
|
+
},
|
|
317
|
+
PROOF_IMPACT_COVERAGE_GAP: {
|
|
318
|
+
code: "PROOF_IMPACT_COVERAGE_GAP",
|
|
319
|
+
category: "PROOF_ERROR",
|
|
320
|
+
title: "Proof impact coverage gap.",
|
|
321
|
+
what: "Proof exists but does not cover required impact areas for this work type.",
|
|
322
|
+
why: "Some change profiles require broader verification than a single lightweight check.",
|
|
323
|
+
next: "Run the minimum commands listed under proof guidance in `agentbridge check`.",
|
|
324
|
+
blocksAcceptance: true,
|
|
325
|
+
},
|
|
296
326
|
PROOF_EVIDENCE_NOT_RECORDED: {
|
|
297
327
|
code: "PROOF_EVIDENCE_NOT_RECORDED",
|
|
298
328
|
category: "PROOF_ERROR",
|
|
@@ -341,7 +371,7 @@ const CATALOG = {
|
|
|
341
371
|
title: "Work already closed.",
|
|
342
372
|
what: "This change request or session is already in a terminal acceptance state.",
|
|
343
373
|
why: "Accept and verify cannot mutate closed work.",
|
|
344
|
-
next: "
|
|
374
|
+
next: "Run `agentbridge watch --allow-dirty` to review new coding work.",
|
|
345
375
|
blocksAcceptance: true,
|
|
346
376
|
},
|
|
347
377
|
WORK_CONTEXT_SESSION_CR_MISMATCH: {
|
|
@@ -359,7 +389,7 @@ const CATALOG = {
|
|
|
359
389
|
title: "Stale local session.",
|
|
360
390
|
what: "Local session id no longer exists on the server.",
|
|
361
391
|
why: "Watch and verify would track orphaned state.",
|
|
362
|
-
next: 'Local session state has been cleared. Run `agentbridge
|
|
392
|
+
next: 'Local session state has been cleared. Run `agentbridge watch --allow-dirty` to review current work.',
|
|
363
393
|
blocksAcceptance: true,
|
|
364
394
|
},
|
|
365
395
|
WORK_CONTEXT_SCOPE_MISMATCH: {
|
|
@@ -368,7 +398,7 @@ const CATALOG = {
|
|
|
368
398
|
title: "Session scope or task mismatch.",
|
|
369
399
|
what: "The active session was started with a different task or scope than requested.",
|
|
370
400
|
why: "Resuming the wrong session would silently corrupt proof and scope boundaries.",
|
|
371
|
-
next: 'Finish or abandon the current session (`agentbridge session abandon --reason "..."`) then rerun `agentbridge
|
|
401
|
+
next: 'Finish or abandon the current session (`agentbridge session abandon --reason "..."`) then rerun `agentbridge watch --task "<task>" --scope "<path>"`.',
|
|
372
402
|
blocksAcceptance: true,
|
|
373
403
|
},
|
|
374
404
|
WORK_CONTEXT_AMBIGUOUS_SESSIONS: {
|
|
@@ -414,6 +444,9 @@ const SERVER_CODE_ALIASES = {
|
|
|
414
444
|
proof_stale: "PROOF_STALE_AFTER_CHANGE",
|
|
415
445
|
stale_evidence_after_change: "PROOF_STALE_AFTER_CHANGE",
|
|
416
446
|
evidence_missing: "PROOF_MISSING",
|
|
447
|
+
proof_too_weak: "PROOF_TOO_WEAK",
|
|
448
|
+
proof_not_relevant: "PROOF_NOT_RELEVANT",
|
|
449
|
+
proof_impact_coverage_gap: "PROOF_IMPACT_COVERAGE_GAP",
|
|
417
450
|
scope_drift: "SCOPE_DRIFT_OUT_OF_SCOPE_FILE",
|
|
418
451
|
out_of_scope_file: "SCOPE_DRIFT_OUT_OF_SCOPE_FILE",
|
|
419
452
|
cr_no_owner: "START_OWNER_UNRESOLVED",
|
|
@@ -424,11 +457,13 @@ const SERVER_CODE_ALIASES = {
|
|
|
424
457
|
local_session_not_found: "WORK_CONTEXT_STALE_LOCAL_SESSION",
|
|
425
458
|
};
|
|
426
459
|
function mapServerCodeToCatalog(code) {
|
|
460
|
+
if (typeof code !== "string" || code.trim().length === 0)
|
|
461
|
+
return "";
|
|
427
462
|
const normalized = code.toLowerCase();
|
|
428
463
|
return SERVER_CODE_ALIASES[normalized] ?? code;
|
|
429
464
|
}
|
|
430
465
|
function catalogEntryForCode(code) {
|
|
431
|
-
if (!code)
|
|
466
|
+
if (!code || typeof code !== "string")
|
|
432
467
|
return null;
|
|
433
468
|
const direct = CATALOG[code];
|
|
434
469
|
if (direct)
|
package/dist/gates.js
CHANGED
|
@@ -19,9 +19,9 @@ exports.ensureGatesFile = ensureGatesFile;
|
|
|
19
19
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
20
20
|
const node_path_1 = __importDefault(require("node:path"));
|
|
21
21
|
exports.DEFAULT_GATES = {
|
|
22
|
-
rollout_proof_too_weak: "
|
|
23
|
-
rollout_proof_not_relevant: "
|
|
24
|
-
rollout_impact_coverage_gap: "
|
|
22
|
+
rollout_proof_too_weak: "hard_fail",
|
|
23
|
+
rollout_proof_not_relevant: "hard_fail",
|
|
24
|
+
rollout_impact_coverage_gap: "hard_fail",
|
|
25
25
|
};
|
|
26
26
|
exports.STATS_HISTORY_CAP = 20;
|
|
27
27
|
exports.GITIGNORE_SESSION_LINE = ".agentbridge/session.json";
|
package/dist/git-evidence.js
CHANGED
package/dist/http.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.CliMissingConfigError = exports.CliHttpError = void 0;
|
|
4
4
|
exports.isCliHttpError = isCliHttpError;
|
|
5
5
|
exports.parseCliHttpErrorBody = parseCliHttpErrorBody;
|
|
6
|
+
exports.extractHttpErrorCode = extractHttpErrorCode;
|
|
6
7
|
exports.postJson = postJson;
|
|
7
8
|
exports.putJson = putJson;
|
|
8
9
|
exports.getJson = getJson;
|
|
@@ -33,6 +34,34 @@ function parseCliHttpErrorBody(err) {
|
|
|
33
34
|
}
|
|
34
35
|
return null;
|
|
35
36
|
}
|
|
37
|
+
/** Extract a string error code from a parsed HTTP error body (handles nested error objects). */
|
|
38
|
+
function extractHttpErrorCode(parsed) {
|
|
39
|
+
if (!parsed)
|
|
40
|
+
return "";
|
|
41
|
+
const topCode = parsed["code"];
|
|
42
|
+
if (typeof topCode === "string" && topCode.trim().length > 0) {
|
|
43
|
+
return topCode.trim();
|
|
44
|
+
}
|
|
45
|
+
const err = parsed["error"];
|
|
46
|
+
if (typeof err === "string" && err.trim().length > 0) {
|
|
47
|
+
return err.trim();
|
|
48
|
+
}
|
|
49
|
+
if (err !== null && typeof err === "object" && !Array.isArray(err)) {
|
|
50
|
+
const nested = err["code"];
|
|
51
|
+
if (typeof nested === "string" && nested.trim().length > 0) {
|
|
52
|
+
return nested.trim();
|
|
53
|
+
}
|
|
54
|
+
const nestedMessage = err["message"];
|
|
55
|
+
if (typeof nestedMessage === "string" && nestedMessage.trim().length > 0) {
|
|
56
|
+
return nestedMessage.trim();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const message = parsed["message"];
|
|
60
|
+
if (typeof message === "string" && message.trim().length > 0) {
|
|
61
|
+
return message.trim();
|
|
62
|
+
}
|
|
63
|
+
return "";
|
|
64
|
+
}
|
|
36
65
|
class CliMissingConfigError extends Error {
|
|
37
66
|
code = "CONFIG_INCOMPLETE";
|
|
38
67
|
constructor(message) {
|
package/dist/index.js
CHANGED
|
@@ -30,9 +30,10 @@ const bug_1 = require("./commands/bug");
|
|
|
30
30
|
const autopilot_1 = require("./commands/autopilot");
|
|
31
31
|
const attention_1 = require("./commands/attention");
|
|
32
32
|
const recover_1 = require("./commands/recover");
|
|
33
|
+
const proof_guidance_1 = require("./commands/proof-guidance");
|
|
33
34
|
const node_path_1 = require("node:path");
|
|
34
35
|
const node_child_process_1 = require("node:child_process");
|
|
35
|
-
const
|
|
36
|
+
const mcp_runtime_1 = require("./mcp-runtime");
|
|
36
37
|
const errors_1 = require("./errors");
|
|
37
38
|
const cli_failure_log_1 = require("./cli-failure-log");
|
|
38
39
|
const session_state_1 = require("./session-state");
|
|
@@ -48,15 +49,19 @@ const COMMAND_HELP = {
|
|
|
48
49
|
" Run `agentbridge connect` first to save credentials.\n",
|
|
49
50
|
use: " agentbridge use <agent-id>\n" +
|
|
50
51
|
" Set the active agent identity used by `watch` and `start`.\n",
|
|
51
|
-
start: " agentbridge start [\"intent\"] [
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
watch: " agentbridge watch [--
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
" --
|
|
59
|
-
" --
|
|
52
|
+
start: " agentbridge start [\"intent\"] [--details] [--yes]\n" +
|
|
53
|
+
" Open or close a task checkpoint (not the live watcher).\n" +
|
|
54
|
+
" For live supervision beside Cursor, run `agentbridge watch`.\n" +
|
|
55
|
+
" Record proof: `agentbridge verify -- <test command>`.\n",
|
|
56
|
+
watch: " agentbridge watch [--allow-dirty] [--once] [--daemon] [--details] [--task \"<summary>\"] [--scope \"<path>\"]\n" +
|
|
57
|
+
" Primary live supervision command — run beside Cursor while the agent codes.\n" +
|
|
58
|
+
" Shows MCP-declared intent and disk changes; warns on scope drift.\n" +
|
|
59
|
+
" No --task or --scope required for normal use.\n" +
|
|
60
|
+
" --task / --scope Optional strict mode (both required together).\n" +
|
|
61
|
+
" --allow-dirty Skip the pre-existing dirty-files prompt.\n" +
|
|
62
|
+
" --once Run one supervision pass and exit.\n" +
|
|
63
|
+
" --daemon Non-interactive mode: skip prompts, auto-deny domain crossings.\n" +
|
|
64
|
+
" --change-request / --execution-surface Advanced session binding.\n",
|
|
60
65
|
"setup-mcp": " agentbridge setup-mcp [--editor cursor|windsurf|vscode]\n" +
|
|
61
66
|
" Print the MCP server configuration snippet to add to your editor.\n",
|
|
62
67
|
mcp: " agentbridge mcp\n" +
|
|
@@ -64,6 +69,8 @@ const COMMAND_HELP = {
|
|
|
64
69
|
status: " agentbridge status [--repair] [--json]\n Show current work session status.\n",
|
|
65
70
|
health: " agentbridge health [--json]\n Check project health against server.\n",
|
|
66
71
|
check: " agentbridge check [--json]\n Check whether current work session can be accepted.\n",
|
|
72
|
+
"proof-guidance": " agentbridge proof-guidance [--work-session <id>] [--change-request <id>] [--rollout-proof-too-weak warn_only|soft_fail|hard_fail]\n" +
|
|
73
|
+
" Print deterministic proof guidance JSON for the current or specified session.\n",
|
|
67
74
|
next: " agentbridge next [--json]\n Show the recommended next action.\n",
|
|
68
75
|
verify: " agentbridge verify [--json] -- <command>\n" +
|
|
69
76
|
" Run a command and verify output meets acceptance criteria.\n",
|
|
@@ -103,30 +110,26 @@ function usage(command, opts) {
|
|
|
103
110
|
"Usage: agentbridge <command> [flags]",
|
|
104
111
|
"",
|
|
105
112
|
"Quick start:",
|
|
106
|
-
"
|
|
107
|
-
"
|
|
108
|
-
"
|
|
109
|
-
"
|
|
110
|
-
" agentbridge doctor",
|
|
111
|
-
" agentbridge recover",
|
|
112
|
-
" agentbridge start",
|
|
113
|
+
" agentbridge recover Understand an existing repo (first time)",
|
|
114
|
+
" agentbridge watch Live supervision beside Cursor (primary watcher)",
|
|
115
|
+
" agentbridge start [\"intent\"] Task checkpoint / close (not the live watcher)",
|
|
116
|
+
" agentbridge verify -- <command> Record verification proof AgentBridge trusts",
|
|
113
117
|
"",
|
|
114
|
-
"
|
|
115
|
-
" doctor
|
|
116
|
-
"
|
|
117
|
-
" start Start or attach to supervised agent work",
|
|
118
|
+
"Setup:",
|
|
119
|
+
" agentbridge doctor Check connection and repo readiness",
|
|
120
|
+
" agentbridge connect Save credentials (cloud projects)",
|
|
118
121
|
"",
|
|
119
122
|
"Advanced commands:",
|
|
120
|
-
"
|
|
123
|
+
" start --task ... --scope ... Strict scoped session (both flags required)",
|
|
121
124
|
" init Recover domain map from git history and bootstrap the project",
|
|
122
125
|
" use Set active agent identity",
|
|
123
|
-
" watch
|
|
124
|
-
" start --summary ... --scope ... Backward-compatible explicit start mode",
|
|
126
|
+
" watch --task ... --scope ... Strict scope mode (optional flags on watch)",
|
|
125
127
|
" setup-mcp Print MCP server config for your editor",
|
|
126
128
|
" mcp Start local MCP stdio server",
|
|
127
129
|
" status Show current session status",
|
|
128
130
|
" health Check project health",
|
|
129
131
|
" check Check acceptance readiness",
|
|
132
|
+
" proof-guidance Print deterministic proof guidance JSON",
|
|
130
133
|
" next Show recommended next action",
|
|
131
134
|
" verify Run command and verify output",
|
|
132
135
|
" handoff Submit a handoff with proof of work",
|
|
@@ -269,27 +272,27 @@ async function main() {
|
|
|
269
272
|
return;
|
|
270
273
|
}
|
|
271
274
|
if (command === "mcp") {
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
if (!(0, node_fs_1.existsSync)(localMcpEntrypoint)) {
|
|
275
|
+
const runtime = (0, mcp_runtime_1.resolveMcpRuntime)(__dirname);
|
|
276
|
+
if (!runtime) {
|
|
275
277
|
process.stderr.write([
|
|
276
278
|
"",
|
|
277
279
|
"AgentBridge MCP runtime is unavailable in this installation.",
|
|
278
280
|
"",
|
|
279
281
|
"Try:",
|
|
280
|
-
" npm install -g @
|
|
282
|
+
" npm install -g @agentbridge1/cli@latest",
|
|
281
283
|
" agentbridge doctor",
|
|
282
284
|
"",
|
|
283
285
|
].join("\n"));
|
|
284
286
|
process.exit(2);
|
|
285
287
|
return;
|
|
286
288
|
}
|
|
287
|
-
const child = (0, node_child_process_1.spawnSync)(
|
|
289
|
+
const child = (0, node_child_process_1.spawnSync)(runtime.command, runtime.args, {
|
|
288
290
|
stdio: "inherit",
|
|
289
291
|
env: process.env,
|
|
290
292
|
});
|
|
291
293
|
if (child.error) {
|
|
292
|
-
|
|
294
|
+
const via = runtime.mode === "bundled" ? "node" : "npx/tsx";
|
|
295
|
+
process.stderr.write(`Failed to start MCP server via ${via}: ${child.error.message}\n`);
|
|
293
296
|
process.exit(2);
|
|
294
297
|
return;
|
|
295
298
|
}
|
|
@@ -336,6 +339,7 @@ async function main() {
|
|
|
336
339
|
allowDirty: flags["--allow-dirty"] === "true",
|
|
337
340
|
daemon: flags["--daemon"] === "true",
|
|
338
341
|
once: flags["--once"] === "true",
|
|
342
|
+
details: flags["--details"] === "true",
|
|
339
343
|
});
|
|
340
344
|
return;
|
|
341
345
|
}
|
|
@@ -353,6 +357,8 @@ async function main() {
|
|
|
353
357
|
executionSurfaceId: flags["--execution-surface"],
|
|
354
358
|
changeRequestId: flags["--change-request"],
|
|
355
359
|
resume: flags["--resume"] === "true",
|
|
360
|
+
details: flags["--details"] === "true",
|
|
361
|
+
confirmClose: flags["--yes"] === "true",
|
|
356
362
|
});
|
|
357
363
|
return;
|
|
358
364
|
}
|
|
@@ -381,6 +387,16 @@ async function main() {
|
|
|
381
387
|
await (0, check_1.runCheck)({ json: flags["--json"] === "true" });
|
|
382
388
|
return;
|
|
383
389
|
}
|
|
390
|
+
if (command === "proof-guidance") {
|
|
391
|
+
await (0, proof_guidance_1.runProofGuidance)({
|
|
392
|
+
workSessionId: flags["--work-session"],
|
|
393
|
+
changeRequestId: flags["--change-request"],
|
|
394
|
+
rolloutProofTooWeak: flags["--rollout-proof-too-weak"],
|
|
395
|
+
rolloutProofNotRelevant: flags["--rollout-proof-not-relevant"],
|
|
396
|
+
rolloutImpactCoverageGap: flags["--rollout-impact-coverage-gap"],
|
|
397
|
+
});
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
384
400
|
if (command === "next") {
|
|
385
401
|
await (0, next_1.runNext)({ json: flags["--json"] === "true" });
|
|
386
402
|
return;
|
package/dist/init.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.refineRecoveredDomains = refineRecoveredDomains;
|
|
|
5
5
|
exports.writeLocalRulesArtifacts = writeLocalRulesArtifacts;
|
|
6
6
|
exports.buildBootstrapPayloadFromEvidence = buildBootstrapPayloadFromEvidence;
|
|
7
7
|
exports.runInit = runInit;
|
|
8
|
+
exports.scanRecoveryFromRepo = scanRecoveryFromRepo;
|
|
8
9
|
exports.runBootstrapRecovery = runBootstrapRecovery;
|
|
9
10
|
const promises_1 = require("node:readline/promises");
|
|
10
11
|
const node_child_process_1 = require("node:child_process");
|
|
@@ -16,6 +17,7 @@ const git_evidence_1 = require("./git-evidence");
|
|
|
16
17
|
const config_1 = require("./config");
|
|
17
18
|
const watch_core_1 = require("./watch-core");
|
|
18
19
|
const precommit_1 = require("./precommit");
|
|
20
|
+
const recovery_reconcile_1 = require("./recovery-reconcile");
|
|
19
21
|
const WORKFLOW_SUMMARY = "Recover domains from repository evidence, map ownership boundaries, enforce lane discipline, and require authority requests before cross-domain edits.";
|
|
20
22
|
const KNOWN_DOMAIN_LABELS = {
|
|
21
23
|
communications: "Communications",
|
|
@@ -126,9 +128,26 @@ function deriveDomainKeyFromPath(path) {
|
|
|
126
128
|
if (normalized.startsWith("db/") || normalized.startsWith("prisma/") || normalized.startsWith("supabase/")) {
|
|
127
129
|
return "database";
|
|
128
130
|
}
|
|
131
|
+
if (normalized.startsWith("backend/supabase/") ||
|
|
132
|
+
normalized.includes("/supabase/functions/") ||
|
|
133
|
+
normalized.includes("/supabase/migrations/")) {
|
|
134
|
+
return "database";
|
|
135
|
+
}
|
|
129
136
|
if (/^supabase_.*\.sql$/i.test(normalized)) {
|
|
130
137
|
return "database";
|
|
131
138
|
}
|
|
139
|
+
const xcodeAppMatch = normalized.match(/^([^/]+)\/\1\//);
|
|
140
|
+
if (xcodeAppMatch) {
|
|
141
|
+
const appName = normalizeToken(xcodeAppMatch[1] ?? "");
|
|
142
|
+
if (appName)
|
|
143
|
+
return appName;
|
|
144
|
+
}
|
|
145
|
+
const topLevelMatch = normalized.match(/^([^/]+)\//);
|
|
146
|
+
if (topLevelMatch) {
|
|
147
|
+
const top = normalizeToken(topLevelMatch[1] ?? "");
|
|
148
|
+
if (top === "backend")
|
|
149
|
+
return "backend";
|
|
150
|
+
}
|
|
132
151
|
return null;
|
|
133
152
|
}
|
|
134
153
|
function extractDomainPrefix(path) {
|
|
@@ -147,8 +166,18 @@ function extractDomainPrefix(path) {
|
|
|
147
166
|
return "prisma/";
|
|
148
167
|
if (normalized.startsWith("supabase/"))
|
|
149
168
|
return "supabase/";
|
|
169
|
+
if (normalized.startsWith("backend/supabase/"))
|
|
170
|
+
return "backend/supabase/";
|
|
150
171
|
if (/^supabase_.*\.sql$/i.test(normalized))
|
|
151
172
|
return "supabase_*.sql";
|
|
173
|
+
const xcodeAppMatch = normalized.match(/^([^/]+)\/\1\//);
|
|
174
|
+
if (xcodeAppMatch) {
|
|
175
|
+
return `${xcodeAppMatch[1]}/${xcodeAppMatch[1]}/`;
|
|
176
|
+
}
|
|
177
|
+
const topLevelMatch = normalized.match(/^([^/]+)\//);
|
|
178
|
+
if (topLevelMatch?.[1] === "backend") {
|
|
179
|
+
return "backend/";
|
|
180
|
+
}
|
|
152
181
|
return null;
|
|
153
182
|
}
|
|
154
183
|
function classifyDomainKey(path) {
|
|
@@ -218,6 +247,19 @@ function collectWorkspaceDomainSeeds() {
|
|
|
218
247
|
seeds.add("database");
|
|
219
248
|
}
|
|
220
249
|
}
|
|
250
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.resolve)(process.cwd(), "backend", "supabase"))) {
|
|
251
|
+
seeds.add("database");
|
|
252
|
+
}
|
|
253
|
+
for (const topLevelEntry of (0, node_fs_1.readdirSync)(process.cwd(), { withFileTypes: true })) {
|
|
254
|
+
if (!topLevelEntry.isDirectory())
|
|
255
|
+
continue;
|
|
256
|
+
const nestedSameName = (0, node_path_1.resolve)(process.cwd(), topLevelEntry.name, topLevelEntry.name);
|
|
257
|
+
if ((0, node_fs_1.existsSync)(nestedSameName)) {
|
|
258
|
+
const folder = normalizeToken(topLevelEntry.name);
|
|
259
|
+
if (folder)
|
|
260
|
+
seeds.add(folder);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
221
263
|
return Array.from(seeds);
|
|
222
264
|
}
|
|
223
265
|
function refineRecoveredDomains(inferredClusters, evidence, architectureDomains) {
|
|
@@ -366,14 +408,42 @@ function toCursorRule(domains) {
|
|
|
366
408
|
"",
|
|
367
409
|
].join("\n");
|
|
368
410
|
}
|
|
369
|
-
function writeLocalRulesArtifacts(domains) {
|
|
411
|
+
function writeLocalRulesArtifacts(domains, opts = {}) {
|
|
370
412
|
const md = toRulesMarkdown(domains);
|
|
371
413
|
const cursorRule = toCursorRule(domains);
|
|
372
414
|
const rootFile = (0, node_path_1.resolve)(process.cwd(), "AGENTBRIDGE.md");
|
|
373
415
|
const cursorRulesDir = (0, node_path_1.resolve)(process.cwd(), ".cursor", "rules");
|
|
416
|
+
const cursorFile = (0, node_path_1.resolve)(cursorRulesDir, "agentbridge.mdc");
|
|
417
|
+
const mdTarget = `${md}\n`;
|
|
418
|
+
const cursorTarget = `${cursorRule}\n`;
|
|
419
|
+
if ((0, node_fs_1.existsSync)(rootFile) && (0, node_fs_1.existsSync)(cursorFile) && !opts.force) {
|
|
420
|
+
const existingMd = (0, node_fs_1.readFileSync)(rootFile, "utf8");
|
|
421
|
+
const existingCursor = (0, node_fs_1.readFileSync)(cursorFile, "utf8");
|
|
422
|
+
if (existingMd === mdTarget && existingCursor === cursorTarget) {
|
|
423
|
+
return { wrote: false, skipped: true, reason: "already up to date" };
|
|
424
|
+
}
|
|
425
|
+
if (!existingMd.startsWith("# AgentBridge Local Rules") ||
|
|
426
|
+
!existingCursor.includes("Recovered domains:")) {
|
|
427
|
+
return { wrote: false, skipped: true, reason: "local edits preserved" };
|
|
428
|
+
}
|
|
429
|
+
}
|
|
374
430
|
(0, node_fs_1.mkdirSync)(cursorRulesDir, { recursive: true });
|
|
375
|
-
(0, node_fs_1.writeFileSync)(rootFile,
|
|
376
|
-
(0, node_fs_1.writeFileSync)(
|
|
431
|
+
(0, node_fs_1.writeFileSync)(rootFile, mdTarget, "utf8");
|
|
432
|
+
(0, node_fs_1.writeFileSync)(cursorFile, cursorTarget, "utf8");
|
|
433
|
+
return { wrote: true, skipped: false };
|
|
434
|
+
}
|
|
435
|
+
function refsToBootstrapDomains(refs) {
|
|
436
|
+
return refs.map((ref) => ({
|
|
437
|
+
name: ref.name,
|
|
438
|
+
pathPatterns: ref.pathPatterns.length > 0 ? ref.pathPatterns : [`${ref.name.toLowerCase()}/`],
|
|
439
|
+
files: [],
|
|
440
|
+
evidenceSource: "git_history",
|
|
441
|
+
confidence: 0.72,
|
|
442
|
+
protectionTier: (0, watch_core_1.inferTierFromDomainName)(ref.name),
|
|
443
|
+
knownTraps: [],
|
|
444
|
+
relatedDomains: [],
|
|
445
|
+
openRisks: [],
|
|
446
|
+
}));
|
|
377
447
|
}
|
|
378
448
|
async function buildBootstrapPayloadFromEvidence(ctx, productSummary, architectureDomains, evidence) {
|
|
379
449
|
const clusterResult = await (0, http_1.postJson)(ctx, `/v1/dev/projects/${encodeURIComponent(ctx.projectId)}/recovery/evidence`, evidence);
|
|
@@ -484,16 +554,12 @@ async function runInit(ctx) {
|
|
|
484
554
|
rl.close();
|
|
485
555
|
}
|
|
486
556
|
}
|
|
487
|
-
async function
|
|
557
|
+
async function scanRecoveryFromRepo(ctx, opts = {}) {
|
|
488
558
|
assertInsideGitRepo();
|
|
489
|
-
const runtimeCliPath = process.argv[1] ? (0, node_path_1.resolve)(process.argv[1]) : undefined;
|
|
490
|
-
const hookCliPath = runtimeCliPath ?? (0, node_path_1.resolve)(__dirname, "index.js");
|
|
491
559
|
const productSummary = opts.productSummary?.trim() || "Recovered existing repository baseline for supervised agent work.";
|
|
492
560
|
const architectureDomains = opts.architectureDomains ?? [];
|
|
493
561
|
const collectedEvidence = (0, git_evidence_1.collectGitEvidence)();
|
|
494
|
-
const
|
|
495
|
-
const { inferredClusters, payload: bootstrapPayload } = await buildBootstrapPayloadFromEvidence(ctx, productSummary, architectureDomains, evidence);
|
|
496
|
-
await (0, http_1.postJson)(ctx, `/v1/dev/projects/${encodeURIComponent(ctx.projectId)}/bootstrap`, bootstrapPayload);
|
|
562
|
+
const { inferredClusters, payload: bootstrapPayload } = await buildBootstrapPayloadFromEvidence(ctx, productSummary, architectureDomains, collectedEvidence.payload);
|
|
497
563
|
const recoveredDomains = inferredClusters
|
|
498
564
|
.filter(isValidAuthorityDomain)
|
|
499
565
|
.map((cluster) => ({
|
|
@@ -502,25 +568,65 @@ async function runBootstrapRecovery(ctx, opts = {}) {
|
|
|
502
568
|
ownerAgentId: `${cluster.suggested_name} Agent`,
|
|
503
569
|
tier: cluster.protection_tier ?? (0, watch_core_1.inferTierFromDomainName)(cluster.suggested_name),
|
|
504
570
|
}));
|
|
571
|
+
const candidateRefs = recoveredDomains.map((domain) => ({
|
|
572
|
+
name: domain.domain,
|
|
573
|
+
pathPatterns: domain.pathPatterns,
|
|
574
|
+
}));
|
|
575
|
+
return { inferredClusters, bootstrapPayload, recoveredDomains, candidateRefs };
|
|
576
|
+
}
|
|
577
|
+
async function runBootstrapRecovery(ctx, opts = {}) {
|
|
578
|
+
assertInsideGitRepo();
|
|
579
|
+
const runtimeCliPath = process.argv[1] ? (0, node_path_1.resolve)(process.argv[1]) : undefined;
|
|
580
|
+
const hookCliPath = runtimeCliPath ?? (0, node_path_1.resolve)(__dirname, "index.js");
|
|
581
|
+
const scan = opts.scan ??
|
|
582
|
+
(await scanRecoveryFromRepo(ctx, {
|
|
583
|
+
productSummary: opts.productSummary,
|
|
584
|
+
architectureDomains: opts.architectureDomains,
|
|
585
|
+
}));
|
|
586
|
+
const { inferredClusters, bootstrapPayload, recoveredDomains } = scan;
|
|
587
|
+
if (opts.reconcilePlan) {
|
|
588
|
+
const candidateBootstrap = toBootstrapDomains(scan.candidateRefs.map((ref) => ({
|
|
589
|
+
name: ref.name,
|
|
590
|
+
files: [],
|
|
591
|
+
pathPatterns: ref.pathPatterns,
|
|
592
|
+
confidence: 0.72,
|
|
593
|
+
protectionTier: (0, watch_core_1.inferTierFromDomainName)(ref.name),
|
|
594
|
+
knownTraps: [],
|
|
595
|
+
rationale: "candidate from repository scan",
|
|
596
|
+
})));
|
|
597
|
+
const existingBootstrap = refsToBootstrapDomains(opts.reconcilePlan.existingActive);
|
|
598
|
+
bootstrapPayload.domains = (0, recovery_reconcile_1.mergeBootstrapDomainEntries)(opts.reconcilePlan, candidateBootstrap, existingBootstrap);
|
|
599
|
+
}
|
|
600
|
+
await (0, http_1.postJson)(ctx, `/v1/dev/projects/${encodeURIComponent(ctx.projectId)}/bootstrap`, bootstrapPayload);
|
|
601
|
+
const payloadDomains = Array.isArray(bootstrapPayload.domains)
|
|
602
|
+
? bootstrapPayload.domains
|
|
603
|
+
: [];
|
|
604
|
+
const payloadDomainKeys = new Set(payloadDomains.map((d) => (d.name ?? "").trim().toLowerCase()).filter(Boolean));
|
|
605
|
+
const mergedLocalDomains = payloadDomainKeys.size > 0
|
|
606
|
+
? recoveredDomains.filter((d) => payloadDomainKeys.has(d.domain.trim().toLowerCase()))
|
|
607
|
+
: recoveredDomains;
|
|
505
608
|
(0, config_1.updateConfig)({
|
|
506
609
|
apiBaseUrl: ctx.apiBaseUrl,
|
|
507
610
|
projectId: ctx.projectId,
|
|
508
|
-
domains:
|
|
611
|
+
domains: (0, watch_core_1.toDomainOwnershipInput)(mergedLocalDomains),
|
|
509
612
|
cliPath: runtimeCliPath,
|
|
510
613
|
});
|
|
511
|
-
writeLocalRulesArtifacts(
|
|
614
|
+
writeLocalRulesArtifacts(mergedLocalDomains.map((domain) => ({
|
|
512
615
|
domain: domain.domain,
|
|
513
616
|
owner: domain.ownerAgentId,
|
|
514
617
|
tier: domain.tier,
|
|
515
|
-
})));
|
|
618
|
+
})), { force: opts.reconcilePlan?.mode === "force" });
|
|
516
619
|
if (opts.installHook) {
|
|
517
620
|
(0, precommit_1.installPrecommitHookWithPath)(hookCliPath);
|
|
518
621
|
(0, config_1.updateConfig)({ precommitHookInstalled: true, cliPath: runtimeCliPath });
|
|
519
622
|
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
623
|
+
if (!opts.reconcilePlan) {
|
|
624
|
+
node_process_1.stdout.write([
|
|
625
|
+
"Recovery baseline created.",
|
|
626
|
+
`Domains mapped: ${mergedLocalDomains.length}.`,
|
|
627
|
+
"Project context and local rules are now ready.",
|
|
628
|
+
"",
|
|
629
|
+
].join("\n"));
|
|
630
|
+
}
|
|
631
|
+
return { recoveredDomains: mergedLocalDomains };
|
|
526
632
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.writeLocalMemory = writeLocalMemory;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const MEMORY_DIR = (0, node_path_1.resolve)(process.cwd(), ".agentbridge", "memory");
|
|
7
|
+
function summarizeProof(state) {
|
|
8
|
+
const run = state.lastLocalVerificationRun;
|
|
9
|
+
if (!run) {
|
|
10
|
+
return state.changedFiles.length > 0 ? { status: "missing" } : { status: "none" };
|
|
11
|
+
}
|
|
12
|
+
if (run.exitCode !== 0) {
|
|
13
|
+
return { status: "failed", command: run.command, finishedAt: run.finishedAt };
|
|
14
|
+
}
|
|
15
|
+
return { status: "ok", command: run.command, finishedAt: run.finishedAt };
|
|
16
|
+
}
|
|
17
|
+
function writeLocalMemory(state, options) {
|
|
18
|
+
const closedAt = state.closedAt ?? new Date().toISOString();
|
|
19
|
+
const record = {
|
|
20
|
+
sessionId: state.id,
|
|
21
|
+
intent: state.intent?.trim() || "Untitled task",
|
|
22
|
+
mode: state.mode ?? "local_supervision",
|
|
23
|
+
changedFiles: [...state.changedFiles].sort(),
|
|
24
|
+
proofSummary: summarizeProof(state),
|
|
25
|
+
openedAt: state.startedAt ?? state.createdAt,
|
|
26
|
+
closedAt,
|
|
27
|
+
cloudSync: options.cloudSync,
|
|
28
|
+
};
|
|
29
|
+
(0, node_fs_1.mkdirSync)(MEMORY_DIR, { recursive: true });
|
|
30
|
+
const filePath = (0, node_path_1.resolve)(MEMORY_DIR, `${state.id}.json`);
|
|
31
|
+
(0, node_fs_1.writeFileSync)(filePath, `${JSON.stringify(record, null, 2)}\n`, "utf8");
|
|
32
|
+
return filePath;
|
|
33
|
+
}
|