@agentbridge1/cli 0.0.1

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 (69) hide show
  1. package/bin/agentbridge.js +11 -0
  2. package/dist/acceptance-block.js +21 -0
  3. package/dist/acceptance-preflight.js +91 -0
  4. package/dist/api-client.js +6 -0
  5. package/dist/authority-request.js +25 -0
  6. package/dist/briefing.js +26 -0
  7. package/dist/bug-registry.js +350 -0
  8. package/dist/build-info.json +6 -0
  9. package/dist/canonical-state.js +11 -0
  10. package/dist/claimed-paths.js +42 -0
  11. package/dist/cli-failure-log.js +34 -0
  12. package/dist/commands/accept.js +241 -0
  13. package/dist/commands/attention.js +85 -0
  14. package/dist/commands/autopilot.js +93 -0
  15. package/dist/commands/bug.js +106 -0
  16. package/dist/commands/check.js +283 -0
  17. package/dist/commands/connect.js +159 -0
  18. package/dist/commands/dist-freshness.js +105 -0
  19. package/dist/commands/doctor.js +300 -0
  20. package/dist/commands/done.js +292 -0
  21. package/dist/commands/handoff.js +189 -0
  22. package/dist/commands/handshake.js +78 -0
  23. package/dist/commands/health.js +154 -0
  24. package/dist/commands/identity.js +57 -0
  25. package/dist/commands/init.js +5 -0
  26. package/dist/commands/memory.js +400 -0
  27. package/dist/commands/next.js +21 -0
  28. package/dist/commands/precommit-check.js +17 -0
  29. package/dist/commands/recover.js +116 -0
  30. package/dist/commands/session.js +229 -0
  31. package/dist/commands/setup-mcp.js +56 -0
  32. package/dist/commands/start.js +626 -0
  33. package/dist/commands/status.js +486 -0
  34. package/dist/commands/use.js +13 -0
  35. package/dist/commands/verify.js +264 -0
  36. package/dist/commands/version.js +32 -0
  37. package/dist/commands/watch.js +1718 -0
  38. package/dist/config.js +55 -0
  39. package/dist/domain-resolution.js +63 -0
  40. package/dist/error-catalog.js +494 -0
  41. package/dist/errors.js +276 -0
  42. package/dist/file-fingerprints.js +45 -0
  43. package/dist/gates.js +200 -0
  44. package/dist/git-evidence.js +285 -0
  45. package/dist/git-status.js +81 -0
  46. package/dist/http.js +151 -0
  47. package/dist/index.js +622 -0
  48. package/dist/init.js +458 -0
  49. package/dist/memory-context-render.js +51 -0
  50. package/dist/operator-snapshot.js +99 -0
  51. package/dist/precommit.js +72 -0
  52. package/dist/preflight-changed-files.js +109 -0
  53. package/dist/proof-guidance.js +110 -0
  54. package/dist/redact-secrets.js +15 -0
  55. package/dist/revert-crossing.js +73 -0
  56. package/dist/server-sync.js +433 -0
  57. package/dist/session-state.js +138 -0
  58. package/dist/session.js +89 -0
  59. package/dist/supervision.js +212 -0
  60. package/dist/terminal-ui.js +18 -0
  61. package/dist/test-runner.js +62 -0
  62. package/dist/types.js +2 -0
  63. package/dist/verification-conditions.js +185 -0
  64. package/dist/watch-core.js +208 -0
  65. package/dist/watch-packet-handshake.js +71 -0
  66. package/dist/watcher.js +62 -0
  67. package/dist/work-context-resolver.js +412 -0
  68. package/dist/work-contract.js +110 -0
  69. package/package.json +44 -0
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ const { spawnSync } = require("node:child_process");
3
+ const path = require("node:path");
4
+
5
+ const entry = path.join(__dirname, "..", "dist", "index.js");
6
+ const result = spawnSync(process.execPath, [entry, ...process.argv.slice(2)], {
7
+ stdio: "inherit",
8
+ env: process.env,
9
+ });
10
+
11
+ process.exit(result.status === null ? 1 : result.status);
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BLOCKING_ACCEPTANCE_DECISIONS = void 0;
4
+ exports.isBlockingAcceptanceDecision = isBlockingAcceptanceDecision;
5
+ exports.isBlockingCompletionState = isBlockingCompletionState;
6
+ exports.shouldBlockAcceptanceAction = shouldBlockAcceptanceAction;
7
+ /** Decisions that block action commands (handoff, accept, done-as-action). */
8
+ exports.BLOCKING_ACCEPTANCE_DECISIONS = new Set(["needs_proof", "stale_evidence", "failed", "needs_review"]);
9
+ function isBlockingAcceptanceDecision(decision) {
10
+ return exports.BLOCKING_ACCEPTANCE_DECISIONS.has(decision);
11
+ }
12
+ function isBlockingCompletionState(state) {
13
+ return (state === "needs_proof" ||
14
+ state === "stale_evidence" ||
15
+ state === "failed" ||
16
+ state === "needs_review");
17
+ }
18
+ function shouldBlockAcceptanceAction(report) {
19
+ return (isBlockingAcceptanceDecision(report.decision) ||
20
+ (report.completion != null && isBlockingCompletionState(report.completion.state)));
21
+ }
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveAcceptanceWorkContext = resolveAcceptanceWorkContext;
4
+ const server_sync_1 = require("./server-sync");
5
+ const http_1 = require("./http");
6
+ const work_context_resolver_1 = require("./work-context-resolver");
7
+ const preflight_changed_files_1 = require("./preflight-changed-files");
8
+ const errors_1 = require("./errors");
9
+ /**
10
+ * Shared path for check/done/handoff/accept: git dirty sync, local-session-only
11
+ * work context (no config CR adoption), then fresh acceptance report from server.
12
+ */
13
+ async function resolveAcceptanceWorkContext(ctx, options = {}) {
14
+ let preflight = { syncedFiles: [], observedFiles: [] };
15
+ try {
16
+ preflight = await (0, preflight_changed_files_1.preflightSyncChangedFiles)(ctx);
17
+ }
18
+ catch (err) {
19
+ const debugMessage = err instanceof Error ? err.message : String(err);
20
+ throw new errors_1.SafeCliError({
21
+ code: "WORK_CONTEXT_SYNC_FAILED",
22
+ category: "WORK_CONTEXT_ERROR",
23
+ what: "Failed to sync the local changed-file snapshot before fetching acceptance status.",
24
+ why: "If preflight sync is skipped, acceptance can read stale server drift and block incorrectly.",
25
+ next: `Retry the command after fixing sync errors. ${process.env.AGENTBRIDGE_DEBUG_HTTP === "1" ? `Debug: ${debugMessage}` : "Run `agentbridge doctor` if this keeps failing."}`,
26
+ });
27
+ }
28
+ const resolution = await (0, work_context_resolver_1.resolveWorkContext)(ctx, {
29
+ useConfigActiveChangeRequestId: false,
30
+ includeOtherActiveSessions: true,
31
+ skipAcceptanceFetchInBind: true,
32
+ ...options,
33
+ });
34
+ if (resolution.state !== "current_session_resolved" || !resolution.binding) {
35
+ return { resolution, preflight, report: null };
36
+ }
37
+ let report;
38
+ try {
39
+ report = await (0, server_sync_1.fetchAcceptanceCheck)(ctx, {
40
+ workSessionId: resolution.binding.workSessionId,
41
+ });
42
+ }
43
+ catch (err) {
44
+ if (err instanceof http_1.CliHttpError && err.status === 404) {
45
+ return {
46
+ resolution: {
47
+ ...resolution,
48
+ state: "stale_orphan_session_detected",
49
+ binding: null,
50
+ resolvedServerSessionId: null,
51
+ resolvedChangeRequestId: null,
52
+ message: `Local session ${resolution.binding.workSessionId} was not found on the server. Run agentbridge session abandon to clear it.`,
53
+ },
54
+ preflight,
55
+ report: null,
56
+ };
57
+ }
58
+ throw err;
59
+ }
60
+ const requestedCr = resolution.requestedChangeRequestId;
61
+ const reportCr = report.change_request_id?.trim() ?? null;
62
+ if (requestedCr && reportCr && requestedCr !== reportCr) {
63
+ return {
64
+ resolution: {
65
+ ...resolution,
66
+ state: "mismatch",
67
+ binding: null,
68
+ resolvedServerSessionId: report.work_session_id,
69
+ resolvedChangeRequestId: reportCr,
70
+ message: `Local session belongs to change request ${reportCr}, not ${requestedCr}.`,
71
+ },
72
+ preflight,
73
+ report: null,
74
+ };
75
+ }
76
+ const binding = {
77
+ workSessionId: report.work_session_id,
78
+ changeRequestId: report.change_request_id ?? null,
79
+ report,
80
+ };
81
+ return {
82
+ resolution: {
83
+ ...resolution,
84
+ binding,
85
+ resolvedServerSessionId: report.work_session_id,
86
+ resolvedChangeRequestId: report.change_request_id ?? null,
87
+ },
88
+ preflight,
89
+ report,
90
+ };
91
+ }
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CliHttpError = exports.postJson = void 0;
4
+ var http_1 = require("./http");
5
+ Object.defineProperty(exports, "postJson", { enumerable: true, get: function () { return http_1.postJson; } });
6
+ Object.defineProperty(exports, "CliHttpError", { enumerable: true, get: function () { return http_1.CliHttpError; } });
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderAuthorityRequest = renderAuthorityRequest;
4
+ function renderAuthorityRequest(input) {
5
+ const title = input.severity === "tier_c" ? "HANDSHAKE REQUIRED" : "AUTHORITY REQUEST";
6
+ return [
7
+ "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
8
+ ` ${title}`,
9
+ "",
10
+ ` ${input.requestingAgent} is attempting an out-of-lane change.`,
11
+ "",
12
+ ` Current task: ${input.currentTask}`,
13
+ ` Authorised lane: ${input.authorizedLane}`,
14
+ ` Change detected: ${input.targetFile}`,
15
+ ` Owned by: ${input.targetDomainOwner}`,
16
+ "",
17
+ " Decision:",
18
+ " [a] approve",
19
+ " [d] deny",
20
+ " [l] limit scope",
21
+ " [h] handoff",
22
+ " [x] abandon",
23
+ "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
24
+ ].join("\n");
25
+ }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderSessionBriefing = renderSessionBriefing;
4
+ function renderSessionBriefing(input) {
5
+ const { state, testRun } = input;
6
+ const inLane = state.changedFiles.filter((file) => {
7
+ const crossing = state.crossings.find((c) => c.file === file);
8
+ return !crossing || crossing.domain === state.laneDomain;
9
+ }).length;
10
+ const outOfLane = state.changedFiles.length - inLane;
11
+ const approvals = state.crossings.filter((c) => c.status === "approved").length;
12
+ const denied = state.crossings.filter((c) => c.status === "denied").length;
13
+ const line1 = `Session closed — ${state.agentId} · ${state.laneDomain ?? "unclassified"} lane`;
14
+ const line2 = `Touched: ${inLane} in-lane, ${outOfLane} cross-domain file(s)`;
15
+ const line3 = approvals + denied > 0
16
+ ? `Crossings: ${approvals} approved, ${denied} denied`
17
+ : `Crossings: ${state.crossings.length} observed`;
18
+ const line4 = testRun
19
+ ? `Tests: ${testRun.passed ? "PASS" : "FAIL"} via ${testRun.command}`
20
+ : "Tests: not run";
21
+ const line5 = state.closeReason
22
+ ? `Close reason: ${state.closeReason}`
23
+ : "Close reason: completed";
24
+ const line6 = "Next: continue in-lane | handoff if cross-domain work is needed";
25
+ return [line1, line2, line3, line4, line5, line6].join("\n");
26
+ }
@@ -0,0 +1,350 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BUG_FOUND_IN = exports.BUG_STATUSES = exports.BUG_SEVERITIES = exports.BUG_CATEGORIES = void 0;
4
+ exports.sanitizeBugText = sanitizeBugText;
5
+ exports.slugifyTitle = slugifyTitle;
6
+ exports.renderBugMarkdown = renderBugMarkdown;
7
+ exports.parseSimpleYamlFrontmatter = parseSimpleYamlFrontmatter;
8
+ exports.parseBugRecordFromFile = parseBugRecordFromFile;
9
+ exports.validateBugReportInput = validateBugReportInput;
10
+ exports.validateBugCloseReadiness = validateBugCloseReadiness;
11
+ exports.resolveBugRegistryDir = resolveBugRegistryDir;
12
+ exports.buildBugRecord = buildBugRecord;
13
+ exports.writeBugReportFile = writeBugReportFile;
14
+ const node_fs_1 = require("node:fs");
15
+ const node_path_1 = require("node:path");
16
+ exports.BUG_CATEGORIES = [
17
+ "PRE_FLIGHT",
18
+ "CONFIG_AUTH",
19
+ "IDENTITY_RESOLUTION",
20
+ "WORK_CONTEXT",
21
+ "START_FLOW",
22
+ "WATCH_SCOPE",
23
+ "PROOF_SCOPE",
24
+ "STALE_PROOF",
25
+ "SCOPE_DRIFT",
26
+ "HANDOFF_ACCEPT",
27
+ "MEMORY_BLOAT",
28
+ "MEMORY_NOT_APPLIED",
29
+ "UI_RETURN_PATH",
30
+ "OAUTH_AUTH",
31
+ "CLI_UX",
32
+ "SERVER_API",
33
+ "UNKNOWN",
34
+ ];
35
+ exports.BUG_SEVERITIES = ["P0", "P1", "P2", "P3"];
36
+ exports.BUG_STATUSES = [
37
+ "open",
38
+ "investigating",
39
+ "fixing",
40
+ "fixed_pending_verification",
41
+ "verified",
42
+ "closed",
43
+ "wont_fix",
44
+ ];
45
+ exports.BUG_FOUND_IN = ["dogfood", "production", "unit_test", "user_report"];
46
+ const SECRET_PATTERNS = [
47
+ /\bsk-[a-zA-Z0-9]{8,}\b/g,
48
+ /\bBearer\s+[a-zA-Z0-9._\-+/=]{8,}\b/gi,
49
+ /\bAGENTBRIDGE_API_KEY\s*=\s*\S+/gi,
50
+ /\bapi[_-]?key\s*[:=]\s*\S+/gi,
51
+ /\bpassword\s*[:=]\s*\S+/gi,
52
+ /\btoken\s*[:=]\s*\S+/gi,
53
+ ];
54
+ function sanitizeBugText(value) {
55
+ let out = value;
56
+ for (const pattern of SECRET_PATTERNS) {
57
+ out = out.replace(pattern, "[REDACTED]");
58
+ }
59
+ return out;
60
+ }
61
+ function slugifyTitle(title) {
62
+ return title
63
+ .toLowerCase()
64
+ .replace(/[^a-z0-9]+/g, "-")
65
+ .replace(/^-+|-+$/g, "")
66
+ .slice(0, 60);
67
+ }
68
+ function quoteYaml(value) {
69
+ if (/[:#\n"'&*]|^\s|-\s/.test(value)) {
70
+ return JSON.stringify(value);
71
+ }
72
+ return value;
73
+ }
74
+ function yamlStringList(key, values) {
75
+ if (!values || values.length === 0) {
76
+ return [`${key}: []`];
77
+ }
78
+ return [`${key}:`, ...values.map((v) => ` - ${quoteYaml(v)}`)];
79
+ }
80
+ function renderBugMarkdown(record) {
81
+ const createdAt = record.created_at ?? new Date().toISOString();
82
+ const lines = [
83
+ "---",
84
+ `id: ${quoteYaml(record.id)}`,
85
+ `title: ${quoteYaml(record.title)}`,
86
+ `severity: ${record.severity}`,
87
+ `category: ${record.category}`,
88
+ `status: ${record.status}`,
89
+ "found_in:",
90
+ ...record.found_in.map((v) => ` - ${v}`),
91
+ `project_id: ${quoteYaml(record.project_id ?? "")}`,
92
+ `work_session_id: ${quoteYaml(record.work_session_id ?? "")}`,
93
+ `change_request_id: ${quoteYaml(record.change_request_id ?? "")}`,
94
+ ...yamlStringList("affected_commands", record.affected_commands),
95
+ ...yamlStringList("affected_files", record.affected_files),
96
+ `user_impact: ${quoteYaml(record.user_impact ?? "")}`,
97
+ `root_cause: ${quoteYaml(record.root_cause ?? "")}`,
98
+ `fix_summary: ${quoteYaml(record.fix_summary ?? "")}`,
99
+ ...yamlStringList("regression_tests", record.regression_tests),
100
+ ...yamlStringList("verification_commands", record.verification_commands),
101
+ `agentbridge_should_have_caught_by: ${quoteYaml(record.agentbridge_should_have_caught_by ?? "")}`,
102
+ "memory_candidate:",
103
+ ` invariant: ${quoteYaml(record.memory_candidate?.invariant ?? "")}`,
104
+ ` known_trap: ${quoteYaml(record.memory_candidate?.known_trap ?? "")}`,
105
+ ` future_proof_requirement: ${quoteYaml(record.memory_candidate?.future_proof_requirement ?? "")}`,
106
+ `created_at: ${quoteYaml(createdAt)}`,
107
+ `closed_at: ${quoteYaml(record.closed_at ?? "")}`,
108
+ "---",
109
+ "",
110
+ `# ${record.title}`,
111
+ "",
112
+ "## Observed behavior",
113
+ "",
114
+ record.observed_behavior?.trim() ? record.observed_behavior.trim() : "_TBD_",
115
+ "",
116
+ "## Expected behavior",
117
+ "",
118
+ record.expected_behavior?.trim() ? record.expected_behavior.trim() : "_TBD_",
119
+ "",
120
+ "## Reproduction",
121
+ "",
122
+ "_TBD_",
123
+ "",
124
+ "## Notes",
125
+ "",
126
+ "_TBD_",
127
+ "",
128
+ ];
129
+ return lines.join("\n");
130
+ }
131
+ function parseSimpleYamlFrontmatter(content) {
132
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
133
+ if (!match) {
134
+ throw new Error("Bug file missing YAML frontmatter");
135
+ }
136
+ const block = match[1];
137
+ const result = {};
138
+ let currentListKey = null;
139
+ let currentNestedKey = null;
140
+ const nested = {};
141
+ for (const rawLine of block.split("\n")) {
142
+ const line = rawLine.trimEnd();
143
+ if (line.trim().length === 0)
144
+ continue;
145
+ const listItem = line.match(/^\s+-\s+(.*)$/);
146
+ if (listItem && currentListKey) {
147
+ const arr = result[currentListKey] ?? [];
148
+ arr.push(parseYamlScalar(listItem[1] ?? ""));
149
+ result[currentListKey] = arr;
150
+ continue;
151
+ }
152
+ const nestedScalar = line.match(/^\s{2}([a-z_]+):\s*(.*)$/);
153
+ if (nestedScalar && currentNestedKey) {
154
+ const [, nk, nv] = nestedScalar;
155
+ if (!nested[currentNestedKey])
156
+ nested[currentNestedKey] = {};
157
+ nested[currentNestedKey][nk ?? ""] = parseYamlScalar(nv ?? "");
158
+ continue;
159
+ }
160
+ const kv = line.match(/^([a-z_]+):\s*(.*)$/);
161
+ if (!kv)
162
+ continue;
163
+ const [, key, rawValue] = kv;
164
+ if (!key)
165
+ continue;
166
+ if (rawValue === "") {
167
+ currentListKey = key;
168
+ currentNestedKey = key === "memory_candidate" ? key : null;
169
+ if (key !== "memory_candidate" && key !== "found_in") {
170
+ result[key] = [];
171
+ }
172
+ if (key === "memory_candidate") {
173
+ result[key] = {};
174
+ }
175
+ continue;
176
+ }
177
+ currentListKey = null;
178
+ currentNestedKey = null;
179
+ result[key] = parseYamlScalar(rawValue);
180
+ }
181
+ if (Object.keys(nested).length > 0) {
182
+ for (const [k, v] of Object.entries(nested)) {
183
+ result[k] = v;
184
+ }
185
+ }
186
+ return result;
187
+ }
188
+ function parseYamlScalar(raw) {
189
+ const trimmed = raw.trim();
190
+ if ((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
191
+ (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
192
+ return trimmed.slice(1, -1);
193
+ }
194
+ return trimmed;
195
+ }
196
+ function parseBugRecordFromFile(content) {
197
+ const fm = parseSimpleYamlFrontmatter(content);
198
+ const bodyMatch = content.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n([\s\S]*)$/);
199
+ const body = bodyMatch?.[1] ?? "";
200
+ const observed = body.match(/## Observed behavior\r?\n\r?\n([\s\S]*?)(?:\r?\n## |\r?\n$)/)?.[1]?.trim() ??
201
+ "";
202
+ const expected = body.match(/## Expected behavior\r?\n\r?\n([\s\S]*?)(?:\r?\n## |\r?\n$)/)?.[1]?.trim() ??
203
+ "";
204
+ const memoryRaw = fm.memory_candidate;
205
+ const memoryCandidate = memoryRaw && typeof memoryRaw === "object" && !Array.isArray(memoryRaw)
206
+ ? memoryRaw
207
+ : undefined;
208
+ return {
209
+ id: String(fm.id ?? ""),
210
+ title: String(fm.title ?? ""),
211
+ severity: fm.severity,
212
+ category: fm.category,
213
+ status: fm.status,
214
+ found_in: Array.isArray(fm.found_in) ? fm.found_in : [],
215
+ project_id: fm.project_id ? String(fm.project_id) : undefined,
216
+ work_session_id: fm.work_session_id ? String(fm.work_session_id) : undefined,
217
+ change_request_id: fm.change_request_id ? String(fm.change_request_id) : undefined,
218
+ affected_commands: Array.isArray(fm.affected_commands)
219
+ ? fm.affected_commands
220
+ : undefined,
221
+ affected_files: Array.isArray(fm.affected_files)
222
+ ? fm.affected_files
223
+ : undefined,
224
+ user_impact: fm.user_impact ? String(fm.user_impact) : undefined,
225
+ root_cause: fm.root_cause ? String(fm.root_cause) : undefined,
226
+ fix_summary: fm.fix_summary ? String(fm.fix_summary) : undefined,
227
+ regression_tests: Array.isArray(fm.regression_tests)
228
+ ? fm.regression_tests
229
+ : undefined,
230
+ verification_commands: Array.isArray(fm.verification_commands)
231
+ ? fm.verification_commands
232
+ : undefined,
233
+ agentbridge_should_have_caught_by: fm.agentbridge_should_have_caught_by
234
+ ? String(fm.agentbridge_should_have_caught_by)
235
+ : undefined,
236
+ memory_candidate: memoryCandidate,
237
+ created_at: fm.created_at ? String(fm.created_at) : undefined,
238
+ closed_at: fm.closed_at ? String(fm.closed_at) : undefined,
239
+ observed_behavior: observed === "_TBD_" ? "" : observed,
240
+ expected_behavior: expected === "_TBD_" ? "" : expected,
241
+ };
242
+ }
243
+ function isNonEmpty(value) {
244
+ return Boolean(value && value.trim().length > 0);
245
+ }
246
+ function hasItems(values) {
247
+ return Boolean(values && values.some((v) => v.trim().length > 0));
248
+ }
249
+ function validateBugReportInput(input) {
250
+ const errors = [];
251
+ if (!isNonEmpty(input.title))
252
+ errors.push("title is required");
253
+ if (!input.severity || !exports.BUG_SEVERITIES.includes(input.severity)) {
254
+ errors.push(`severity must be one of: ${exports.BUG_SEVERITIES.join(", ")}`);
255
+ }
256
+ if (!input.category || !exports.BUG_CATEGORIES.includes(input.category)) {
257
+ errors.push(`category must be one of: ${exports.BUG_CATEGORIES.join(", ")}`);
258
+ }
259
+ const foundIn = input.foundIn ?? [];
260
+ if (foundIn.length === 0) {
261
+ errors.push("found_in requires at least one value");
262
+ }
263
+ else {
264
+ for (const item of foundIn) {
265
+ if (!exports.BUG_FOUND_IN.includes(item)) {
266
+ errors.push(`invalid found_in value: ${item}`);
267
+ }
268
+ }
269
+ }
270
+ if (!isNonEmpty(input.observed))
271
+ errors.push("observed behavior is required");
272
+ if (!isNonEmpty(input.expected))
273
+ errors.push("expected behavior is required");
274
+ return { ok: errors.length === 0, errors };
275
+ }
276
+ function validateBugCloseReadiness(record) {
277
+ const errors = [];
278
+ if (!exports.BUG_SEVERITIES.includes(record.severity)) {
279
+ errors.push(`invalid severity: ${record.severity}`);
280
+ }
281
+ if (!exports.BUG_STATUSES.includes(record.status)) {
282
+ errors.push(`invalid status: ${record.status}`);
283
+ }
284
+ const closingStatuses = ["verified", "closed"];
285
+ if (!closingStatuses.includes(record.status)) {
286
+ return { ok: errors.length === 0, errors };
287
+ }
288
+ if (record.severity === "P0" || record.severity === "P1") {
289
+ if (!isNonEmpty(record.root_cause))
290
+ errors.push("root_cause is required for P0/P1 close");
291
+ if (!isNonEmpty(record.fix_summary))
292
+ errors.push("fix_summary is required for P0/P1 close");
293
+ if (!hasItems(record.regression_tests)) {
294
+ errors.push("regression_tests requires at least one entry for P0/P1 close");
295
+ }
296
+ if (!hasItems(record.verification_commands)) {
297
+ errors.push("verification_commands requires at least one entry for P0/P1 close");
298
+ }
299
+ if (!isNonEmpty(record.agentbridge_should_have_caught_by)) {
300
+ errors.push("agentbridge_should_have_caught_by is required for P0/P1 close");
301
+ }
302
+ }
303
+ return { ok: errors.length === 0, errors };
304
+ }
305
+ function resolveBugRegistryDir(repoRoot) {
306
+ return (0, node_path_1.join)(repoRoot, "docs", "bugs");
307
+ }
308
+ function buildBugRecord(options) {
309
+ const date = new Date().toISOString().slice(0, 10);
310
+ const slug = slugifyTitle(options.title);
311
+ const id = `bug-${date}-${slug}`;
312
+ const observed = sanitizeBugText([options.observed, options.logs ? `\n\nLogs:\n${options.logs}` : ""]
313
+ .filter(Boolean)
314
+ .join("\n"));
315
+ const expected = sanitizeBugText(options.expected);
316
+ return {
317
+ id,
318
+ title: sanitizeBugText(options.title),
319
+ severity: options.severity,
320
+ category: options.category,
321
+ status: options.status ?? "open",
322
+ found_in: options.foundIn,
323
+ project_id: options.projectId ? sanitizeBugText(options.projectId) : undefined,
324
+ work_session_id: options.workSessionId ? sanitizeBugText(options.workSessionId) : undefined,
325
+ change_request_id: options.changeRequestId
326
+ ? sanitizeBugText(options.changeRequestId)
327
+ : undefined,
328
+ affected_commands: options.affectedCommands?.map(sanitizeBugText),
329
+ affected_files: options.affectedFiles?.map(sanitizeBugText),
330
+ user_impact: options.userImpact ? sanitizeBugText(options.userImpact) : undefined,
331
+ observed_behavior: observed,
332
+ expected_behavior: expected,
333
+ regression_tests: [],
334
+ verification_commands: [],
335
+ memory_candidate: {
336
+ invariant: "",
337
+ known_trap: "",
338
+ future_proof_requirement: "",
339
+ },
340
+ created_at: new Date().toISOString(),
341
+ };
342
+ }
343
+ function writeBugReportFile(repoRoot, record) {
344
+ const dir = resolveBugRegistryDir(repoRoot);
345
+ (0, node_fs_1.mkdirSync)(dir, { recursive: true });
346
+ const filename = `${record.id.replace(/^bug-/, "")}.md`;
347
+ const path = (0, node_path_1.join)(dir, filename);
348
+ (0, node_fs_1.writeFileSync)(path, renderBugMarkdown(record), "utf8");
349
+ return path;
350
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "builtAt": "2026-06-01T12:25:02.951Z",
3
+ "gitHead": "6278b82",
4
+ "sourceLatestMtime": "2026-06-01T11:23:58.375Z",
5
+ "sourceLatestFile": "build.mjs"
6
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toCanonicalWorkContextState = toCanonicalWorkContextState;
4
+ function toCanonicalWorkContextState(state) {
5
+ if (state === "mismatch")
6
+ return "mismatch";
7
+ if (state === "other_active_sessions_exist" || state === "ambiguous_active_sessions") {
8
+ return "other_active_sessions_exist";
9
+ }
10
+ return "no_current_session";
11
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fileWithinClaimedPaths = fileWithinClaimedPaths;
4
+ exports.filesOutsideClaimedPaths = filesOutsideClaimedPaths;
5
+ function escapeRegex(text) {
6
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7
+ }
8
+ function globToRegex(glob) {
9
+ const normalized = glob.replaceAll("\\", "/");
10
+ const withDouble = normalized.replaceAll("**", "__DOUBLE_STAR__");
11
+ const withSingle = withDouble.replaceAll("*", "__SINGLE_STAR__");
12
+ const escaped = escapeRegex(withSingle)
13
+ .replaceAll("__DOUBLE_STAR__", ".*")
14
+ .replaceAll("__SINGLE_STAR__", "[^/]*");
15
+ return new RegExp(`^${escaped}$`);
16
+ }
17
+ /** True when `file` matches an explicit claimed scope path or glob. */
18
+ function fileWithinClaimedPaths(file, claimedPaths) {
19
+ if (claimedPaths.length === 0)
20
+ return false;
21
+ const normalizedFile = file.trim().replaceAll("\\", "/");
22
+ return claimedPaths.some((claim) => {
23
+ const normalized = claim.trim().replaceAll("\\", "/");
24
+ if (!normalized)
25
+ return false;
26
+ if (normalized === normalizedFile)
27
+ return true;
28
+ if (normalized.endsWith("/**")) {
29
+ const prefix = normalized.slice(0, -3);
30
+ return normalizedFile.startsWith(prefix);
31
+ }
32
+ if (normalized.includes("*")) {
33
+ return globToRegex(normalized).test(normalizedFile);
34
+ }
35
+ return normalizedFile.startsWith(normalized.endsWith("/") ? normalized : `${normalized}/`);
36
+ });
37
+ }
38
+ function filesOutsideClaimedPaths(files, claimedPaths) {
39
+ if (claimedPaths.length === 0)
40
+ return [...files];
41
+ return files.filter((file) => !fileWithinClaimedPaths(file, claimedPaths));
42
+ }
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logCliFailure = logCliFailure;
4
+ const errors_1 = require("./errors");
5
+ const redact_secrets_1 = require("./redact-secrets");
6
+ function failureLogEnabled() {
7
+ const debug = process.env.AGENTBRIDGE_DEBUG;
8
+ const failureLog = process.env.AGENTBRIDGE_FAILURE_LOG;
9
+ return debug === "1" || debug === "true" || failureLog === "1" || failureLog === "true";
10
+ }
11
+ function logCliFailure(err, ctx = {}) {
12
+ if (!failureLogEnabled())
13
+ return;
14
+ const view = (0, errors_1.formatCliError)(err);
15
+ const payload = {
16
+ timestamp: new Date().toISOString(),
17
+ command: ctx.command ?? null,
18
+ projectId: ctx.projectId ?? null,
19
+ workSessionId: ctx.workSessionId ?? null,
20
+ changeRequestId: ctx.changeRequestId ?? null,
21
+ correlation_id: ctx.correlationId ?? null,
22
+ error_code: view.code,
23
+ error_category: view.category,
24
+ user_visible_message: (0, redact_secrets_1.redactSecrets)(view.what),
25
+ next_action: (0, redact_secrets_1.redactSecrets)(view.next),
26
+ };
27
+ Object.keys(payload).forEach((key) => {
28
+ const value = payload[key];
29
+ if (typeof value === "string") {
30
+ payload[key] = (0, redact_secrets_1.redactSecrets)(value);
31
+ }
32
+ });
33
+ process.stderr.write(`[agentbridge:failure] ${JSON.stringify(payload)}\n`);
34
+ }