@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,241 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runAccept = runAccept;
4
+ const config_1 = require("../config");
5
+ const errors_1 = require("../errors");
6
+ const http_1 = require("../http");
7
+ const session_state_1 = require("../session-state");
8
+ const memory_1 = require("./memory");
9
+ const server_sync_1 = require("../server-sync");
10
+ const operator_snapshot_1 = require("../operator-snapshot");
11
+ const work_context_resolver_1 = require("../work-context-resolver");
12
+ const error_catalog_1 = require("../error-catalog");
13
+ const acceptance_preflight_1 = require("../acceptance-preflight");
14
+ const acceptance_block_1 = require("../acceptance-block");
15
+ function resolveNetworkContext() {
16
+ const cfg = (0, config_1.readConfig)();
17
+ const projectId = process.env.AGENTBRIDGE_PROJECT_ID ?? cfg.projectId;
18
+ const apiKey = process.env.AGENTBRIDGE_API_KEY ?? cfg.apiKey ?? "";
19
+ const apiBaseUrl = process.env.AGENTBRIDGE_BASE_URL ?? cfg.apiBaseUrl ?? "https://agentauth-api-production.up.railway.app";
20
+ if (!projectId) {
21
+ throw new errors_1.SafeCliError("Missing AGENTBRIDGE_PROJECT_ID. Set AGENTBRIDGE_PROJECT_ID (or run `agentbridge init --project ...`).");
22
+ }
23
+ if (!apiKey) {
24
+ throw new errors_1.SafeCliError("Missing AGENTBRIDGE_API_KEY. Provide --api-key, set AGENTBRIDGE_API_KEY, or save apiKey in .agentbridge/config.json.");
25
+ }
26
+ return { projectId, apiKey, apiBaseUrl };
27
+ }
28
+ function buildBlockReasons(report) {
29
+ return report.completion.blockingReasons;
30
+ }
31
+ function blockedErrorCode(report) {
32
+ if (report.completion.state === "ready_for_handoff" ||
33
+ buildBlockReasons(report).some((reason) => reason.toLowerCase().includes("handoff required"))) {
34
+ return "HANDOFF_REQUIRED";
35
+ }
36
+ if ((report.out_of_scope_files ?? []).length > 0 || report.scope_status === "drift") {
37
+ return "SCOPE_DRIFT_OUT_OF_SCOPE_FILE";
38
+ }
39
+ if (report.decision === "needs_proof")
40
+ return "PROOF_MISSING";
41
+ if (report.decision === "stale_evidence")
42
+ return "PROOF_STALE_AFTER_CHANGE";
43
+ return "ACCEPTANCE_BLOCKED";
44
+ }
45
+ function buildStructuredBlockedOutput(report) {
46
+ const code = blockedErrorCode(report);
47
+ const entry = (0, error_catalog_1.catalogEntryForCode)(code);
48
+ const lines = ["Acceptance blocked."];
49
+ lines.push(`What happened: ${entry?.what ?? "Acceptance preconditions are not satisfied."}`);
50
+ lines.push(`Why it matters: ${entry?.why ?? "Blocked work cannot be accepted or closed."}`);
51
+ lines.push("Next action:");
52
+ if (entry?.next)
53
+ lines.push(`- ${entry.next}`);
54
+ if (report.completion.nextCommand)
55
+ lines.push(`- ${report.completion.nextCommand}`);
56
+ for (const action of report.next_required_action) {
57
+ lines.push(`- ${action}`);
58
+ }
59
+ for (const reason of buildBlockReasons(report)) {
60
+ lines.push(`- ${reason}`);
61
+ }
62
+ if (code === "SCOPE_DRIFT_OUT_OF_SCOPE_FILE" && (report.out_of_scope_files ?? []).length > 0) {
63
+ lines.push("Out-of-scope changed files:");
64
+ for (const file of report.out_of_scope_files ?? []) {
65
+ lines.push(`- ${file}`);
66
+ }
67
+ }
68
+ lines.push(`Error code: ${code}`);
69
+ return lines;
70
+ }
71
+ function isNoActiveSessionHttpError(err) {
72
+ if (!(err instanceof http_1.CliHttpError) || err.status !== 404)
73
+ return false;
74
+ try {
75
+ const parsed = JSON.parse(err.body);
76
+ return parsed.code === "active_work_session_not_found" || parsed.error === "active_work_session_not_found";
77
+ }
78
+ catch {
79
+ return false;
80
+ }
81
+ }
82
+ async function runAccept(options = {}) {
83
+ const ctx = resolveNetworkContext();
84
+ const { resolution, report } = await (0, acceptance_preflight_1.resolveAcceptanceWorkContext)(ctx);
85
+ if (resolution.state !== "current_session_resolved" || !report) {
86
+ if (options.json) {
87
+ const snapshot = (0, operator_snapshot_1.buildOperatorSnapshot)(resolution);
88
+ process.stdout.write(`${JSON.stringify({ snapshot }, null, 2)}\n`);
89
+ }
90
+ else {
91
+ process.stdout.write(`${(0, work_context_resolver_1.renderWorkContextLines)(resolution).join("\n")}\n`);
92
+ }
93
+ const code = (0, error_catalog_1.workContextStateToCode)(resolution.state);
94
+ const entry = (0, error_catalog_1.catalogEntryForCode)(code);
95
+ throw new errors_1.SafeCliError({
96
+ code,
97
+ category: entry?.category ?? "WORK_CONTEXT_ERROR",
98
+ what: resolution.message,
99
+ why: entry?.why ?? "Acceptance requires a bound active work session.",
100
+ next: entry?.next ?? "Run `agentbridge watch` to bind a session, then retry accept.",
101
+ });
102
+ }
103
+ const binding = (0, work_context_resolver_1.requireWorkContextBinding)(resolution, "accept");
104
+ const snapshot = (0, operator_snapshot_1.buildOperatorSnapshot)(resolution);
105
+ const locallyAcceptable = report.completion.state === "ready_to_accept" || report.completion.state === "accepted";
106
+ if (!locallyAcceptable || (0, acceptance_block_1.shouldBlockAcceptanceAction)(report)) {
107
+ if (options.json) {
108
+ process.stdout.write(`${JSON.stringify({
109
+ snapshot,
110
+ acceptance: {
111
+ accepted: false,
112
+ blocked_reasons: buildBlockReasons(report),
113
+ next_action: report.next_required_action,
114
+ },
115
+ }, null, 2)}\n`);
116
+ process.exitCode = 1;
117
+ return;
118
+ }
119
+ for (const line of buildStructuredBlockedOutput(report))
120
+ process.stdout.write(`${line}\n`);
121
+ process.exitCode = 1;
122
+ return;
123
+ }
124
+ try {
125
+ const result = await (0, server_sync_1.acceptWorkSession)(ctx, binding);
126
+ const sessionClosed = result.session_status === "closed";
127
+ if (!options.json) {
128
+ if (!result.accepted) {
129
+ const blockedReport = {
130
+ ...report,
131
+ decision: result.decision,
132
+ next_required_action: result.next_action.length > 0 ? result.next_action : report.next_required_action,
133
+ completion: {
134
+ ...report.completion,
135
+ blockingReasons: result.reasons.length > 0 ? result.reasons : report.completion.blockingReasons,
136
+ },
137
+ };
138
+ for (const line of buildStructuredBlockedOutput(blockedReport)) {
139
+ process.stdout.write(`${line}\n`);
140
+ }
141
+ }
142
+ else if (sessionClosed) {
143
+ process.stdout.write("Accepted. Session fully closed.\n");
144
+ }
145
+ else {
146
+ process.stdout.write("Accepted, but session is still open. Manual close required.\n");
147
+ }
148
+ if (result.accepted) {
149
+ process.stdout.write([
150
+ `CR/session: ${result.change_request_id ?? "none"} / ${result.work_session_id}`,
151
+ `Decision: ${result.decision}`,
152
+ `Session status: ${result.session_status ?? "unchanged"}`,
153
+ ].join("\n") + "\n");
154
+ if (result.reasons.length > 0) {
155
+ for (const reason of result.reasons)
156
+ process.stdout.write(`- ${reason}\n`);
157
+ }
158
+ if (result.next_action.length > 0) {
159
+ process.stdout.write("Next action:\n");
160
+ for (const action of result.next_action)
161
+ process.stdout.write(`- ${action}\n`);
162
+ }
163
+ }
164
+ }
165
+ if (options.json) {
166
+ process.stdout.write(`${JSON.stringify({
167
+ snapshot,
168
+ acceptance: {
169
+ accepted: result.accepted,
170
+ decision: result.decision,
171
+ reasons: result.reasons,
172
+ next_action: result.next_action,
173
+ work_session_id: result.work_session_id,
174
+ change_request_id: result.change_request_id,
175
+ session_status: result.session_status ?? "unchanged",
176
+ },
177
+ }, null, 2)}\n`);
178
+ if (!result.accepted) {
179
+ process.exitCode = 1;
180
+ return;
181
+ }
182
+ }
183
+ if (!result.accepted) {
184
+ process.exitCode = 1;
185
+ return;
186
+ }
187
+ if (sessionClosed) {
188
+ (0, session_state_1.clearSessionState)();
189
+ const cfg = (0, config_1.readConfig)();
190
+ const nextCfg = {
191
+ ...cfg,
192
+ lastAcceptedSessionId: result.work_session_id,
193
+ lastAcceptedAt: new Date().toISOString(),
194
+ };
195
+ if (cfg.activeChangeRequestId &&
196
+ result.change_request_id &&
197
+ cfg.activeChangeRequestId === result.change_request_id) {
198
+ delete nextCfg.activeChangeRequestId;
199
+ }
200
+ (0, config_1.writeConfig)(nextCfg);
201
+ try {
202
+ await (0, memory_1.runMemorySuggest)({ workSessionId: result.work_session_id, compact: true });
203
+ }
204
+ catch {
205
+ process.stderr.write("Memory suggest failed (session was accepted). Run `agentbridge memory suggest` manually.\n");
206
+ }
207
+ }
208
+ }
209
+ catch (err) {
210
+ if (isNoActiveSessionHttpError(err)) {
211
+ if (options.json) {
212
+ const fallbackSnapshot = (0, operator_snapshot_1.buildOperatorSnapshot)(resolution);
213
+ process.stdout.write(`${JSON.stringify({ snapshot: fallbackSnapshot }, null, 2)}\n`);
214
+ process.exitCode = 1;
215
+ return;
216
+ }
217
+ process.stdout.write(`${(0, work_context_resolver_1.renderWorkContextLines)(resolution).join("\n")}\n`);
218
+ process.exitCode = 1;
219
+ return;
220
+ }
221
+ if (err instanceof http_1.CliHttpError && err.status === 409) {
222
+ if (options.json) {
223
+ process.stdout.write(`${JSON.stringify({
224
+ snapshot,
225
+ acceptance: {
226
+ accepted: false,
227
+ blocked_reasons: buildBlockReasons(report),
228
+ next_action: report.next_required_action,
229
+ },
230
+ }, null, 2)}\n`);
231
+ process.exitCode = 1;
232
+ return;
233
+ }
234
+ for (const line of buildStructuredBlockedOutput(report))
235
+ process.stdout.write(`${line}\n`);
236
+ process.exitCode = 1;
237
+ return;
238
+ }
239
+ throw err;
240
+ }
241
+ }
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runAttentionList = runAttentionList;
4
+ exports.runAttentionResolve = runAttentionResolve;
5
+ exports.runAttention = runAttention;
6
+ const config_1 = require("../config");
7
+ const errors_1 = require("../errors");
8
+ const http_1 = require("../http");
9
+ function resolveNetworkContext() {
10
+ const cfg = (0, config_1.readConfig)();
11
+ const projectId = process.env.AGENTBRIDGE_PROJECT_ID ?? cfg.projectId;
12
+ const apiKey = process.env.AGENTBRIDGE_API_KEY ?? cfg.apiKey ?? "";
13
+ const apiBaseUrl = process.env.AGENTBRIDGE_BASE_URL ?? cfg.apiBaseUrl ?? "https://agentauth-api-production.up.railway.app";
14
+ if (!projectId || !apiKey) {
15
+ throw (0, errors_1.catalogCliError)("CONFIG_INCOMPLETE");
16
+ }
17
+ return { projectId, apiKey, apiBaseUrl };
18
+ }
19
+ const REASON_LABELS = {
20
+ ambiguous_intent: "Ambiguous intent",
21
+ scope_conflict: "Scope conflict",
22
+ handoff_rejected: "Handoff rejected",
23
+ close_blocked: "Close blocked",
24
+ accept_blocked: "Accept blocked",
25
+ policy_violation: "Policy violation",
26
+ stale_draft: "Stale draft",
27
+ manual_escalation: "Manual escalation",
28
+ };
29
+ function fmtReason(code) {
30
+ return REASON_LABELS[code] ?? code;
31
+ }
32
+ async function runAttentionList() {
33
+ const ctx = resolveNetworkContext();
34
+ const res = await (0, http_1.getJson)(ctx, `/v1/dev/projects/${ctx.projectId}/attention`);
35
+ const items = res.attention ?? res.items ?? [];
36
+ if (items.length === 0) {
37
+ console.log("All clear — no pending attention items.");
38
+ return;
39
+ }
40
+ console.log(`Pending attention items — project ${ctx.projectId}\n`);
41
+ for (const item of items) {
42
+ const session = item.workSessionId ? item.workSessionId.slice(0, 8) : "n/a";
43
+ const severity = (item.attentionPayload?.severity ?? "info").toUpperCase();
44
+ const reason = fmtReason(item.reason ?? "");
45
+ const label = item.title || reason;
46
+ console.log(` ${item.id.slice(0, 8)} [${severity}] ${label} session=${session}`);
47
+ if (item.attentionPayload?.suggestedAction) {
48
+ console.log(` Suggested: ${item.attentionPayload.suggestedAction}`);
49
+ }
50
+ }
51
+ }
52
+ async function runAttentionResolve(argv) {
53
+ const id = argv[0];
54
+ if (!id) {
55
+ console.error("Usage: agentbridge attention resolve <id> --resolution dismissed|actioned");
56
+ process.exit(1);
57
+ }
58
+ const resolutionIdx = argv.indexOf("--resolution");
59
+ const resolution = resolutionIdx !== -1 ? argv[resolutionIdx + 1] : undefined;
60
+ if (!resolution || !["dismissed", "actioned"].includes(resolution)) {
61
+ console.error('--resolution must be one of: dismissed, actioned');
62
+ process.exit(1);
63
+ }
64
+ const ctx = resolveNetworkContext();
65
+ await (0, http_1.postJson)(ctx, `/v1/dev/projects/${ctx.projectId}/attention/${id}/resolve`, { resolution });
66
+ console.log(`Attention item ${id.slice(0, 8)} resolved as "${resolution}".`);
67
+ }
68
+ async function runAttention(argv) {
69
+ const sub = argv[0];
70
+ if (!sub || sub === "list") {
71
+ await runAttentionList();
72
+ return;
73
+ }
74
+ if (sub === "resolve") {
75
+ await runAttentionResolve(argv.slice(1));
76
+ return;
77
+ }
78
+ console.log([
79
+ "Usage: agentbridge attention [command]",
80
+ "",
81
+ "Commands:",
82
+ " (no args) List pending attention items",
83
+ " resolve <id> Resolve an item (--resolution dismissed|actioned)",
84
+ ].join("\n"));
85
+ }
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runAutopilotStatus = runAutopilotStatus;
4
+ exports.runAutopilotEnable = runAutopilotEnable;
5
+ exports.runAutopilotDisable = runAutopilotDisable;
6
+ exports.runAutopilot = runAutopilot;
7
+ const config_1 = require("../config");
8
+ const errors_1 = require("../errors");
9
+ const http_1 = require("../http");
10
+ const server_sync_1 = require("../server-sync");
11
+ function resolveNetworkContext() {
12
+ const cfg = (0, config_1.readConfig)();
13
+ const projectId = process.env.AGENTBRIDGE_PROJECT_ID ?? cfg.projectId;
14
+ const apiKey = process.env.AGENTBRIDGE_API_KEY ?? cfg.apiKey ?? "";
15
+ const apiBaseUrl = process.env.AGENTBRIDGE_BASE_URL ?? cfg.apiBaseUrl ?? "https://agentauth-api-production.up.railway.app";
16
+ if (!projectId || !apiKey) {
17
+ throw (0, errors_1.catalogCliError)("CONFIG_INCOMPLETE");
18
+ }
19
+ return { projectId, apiKey, apiBaseUrl };
20
+ }
21
+ const AUTOMATION_STATES = {
22
+ idle: "idle",
23
+ draft_ready: "draft ready",
24
+ ready_to_close: "ready to close",
25
+ ready_to_accept: "ready to accept",
26
+ blocked_needs_attention: "BLOCKED – needs attention",
27
+ };
28
+ function fmtState(status) {
29
+ return AUTOMATION_STATES[status] ?? status;
30
+ }
31
+ async function runAutopilotStatus() {
32
+ const ctx = resolveNetworkContext();
33
+ const sessions = await (0, server_sync_1.listWorkSessions)(ctx, { status: "active" });
34
+ if (sessions.length === 0) {
35
+ console.log("No active sessions found.");
36
+ return;
37
+ }
38
+ console.log(`Autopilot status — project ${ctx.projectId}\n`);
39
+ for (const s of sessions) {
40
+ const mode = s.automation_mode ? "enabled" : "disabled";
41
+ const state = fmtState(s.status);
42
+ console.log(` ${s.id.slice(0, 8)} mode=${mode} state=${state} (${s.intent ?? "no intent"})`);
43
+ }
44
+ }
45
+ async function runAutopilotEnable() {
46
+ const ctx = resolveNetworkContext();
47
+ const sessions = await (0, server_sync_1.listWorkSessions)(ctx, { status: "active" });
48
+ if (sessions.length === 0) {
49
+ console.error("No active sessions found for this project.");
50
+ process.exit(1);
51
+ }
52
+ const target = sessions[0];
53
+ await (0, http_1.postJson)(ctx, `/v1/dev/projects/${ctx.projectId}/work-sessions/${target.id}/automation/evaluate`, {
54
+ enable: true,
55
+ });
56
+ console.log(`Autopilot enabled on session ${target.id.slice(0, 8)}.`);
57
+ }
58
+ async function runAutopilotDisable() {
59
+ const ctx = resolveNetworkContext();
60
+ const sessions = await (0, server_sync_1.listWorkSessions)(ctx, { status: "active" });
61
+ if (sessions.length === 0) {
62
+ console.error("No active sessions found for this project.");
63
+ process.exit(1);
64
+ }
65
+ const target = sessions[0];
66
+ await (0, http_1.postJson)(ctx, `/v1/dev/projects/${ctx.projectId}/work-sessions/${target.id}/automation/evaluate`, {
67
+ enable: false,
68
+ });
69
+ console.log(`Autopilot disabled on session ${target.id.slice(0, 8)}.`);
70
+ }
71
+ async function runAutopilot(argv) {
72
+ const sub = argv[0];
73
+ switch (sub) {
74
+ case "status":
75
+ await runAutopilotStatus();
76
+ break;
77
+ case "enable":
78
+ await runAutopilotEnable();
79
+ break;
80
+ case "disable":
81
+ await runAutopilotDisable();
82
+ break;
83
+ default:
84
+ console.log([
85
+ "Usage: agentbridge autopilot <command>",
86
+ "",
87
+ "Commands:",
88
+ " status Show automation state for active sessions",
89
+ " enable Enable autopilot on the current active session",
90
+ " disable Disable autopilot on the current active session",
91
+ ].join("\n"));
92
+ }
93
+ }
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runBugReport = runBugReport;
4
+ exports.runBugValidate = runBugValidate;
5
+ const node_fs_1 = require("node:fs");
6
+ const node_path_1 = require("node:path");
7
+ const bug_registry_1 = require("../bug-registry");
8
+ function splitListFlag(value) {
9
+ if (!value)
10
+ return undefined;
11
+ const parts = value
12
+ .split(/[,\n]/)
13
+ .map((s) => s.trim())
14
+ .filter((s) => s.length > 0);
15
+ return parts.length > 0 ? parts : undefined;
16
+ }
17
+ function findRepoRoot(startDir) {
18
+ let current = (0, node_path_1.resolve)(startDir);
19
+ for (let i = 0; i < 12; i += 1) {
20
+ try {
21
+ (0, node_fs_1.readFileSync)((0, node_path_1.resolve)(current, "package.json"), "utf8");
22
+ return current;
23
+ }
24
+ catch {
25
+ const parent = (0, node_path_1.resolve)(current, "..");
26
+ if (parent === current)
27
+ break;
28
+ current = parent;
29
+ }
30
+ }
31
+ return startDir;
32
+ }
33
+ async function runBugReport(options) {
34
+ const foundIn = splitListFlag(options.foundIn) ?? ["dogfood"];
35
+ const validation = (0, bug_registry_1.validateBugReportInput)({
36
+ title: options.title,
37
+ severity: options.severity,
38
+ category: options.category,
39
+ foundIn,
40
+ observed: options.observed,
41
+ expected: options.expected,
42
+ });
43
+ if (!validation.ok) {
44
+ for (const err of validation.errors) {
45
+ process.stderr.write(`bug report: ${err}\n`);
46
+ }
47
+ process.exit(1);
48
+ return;
49
+ }
50
+ const repoRoot = options.repoRoot ?? findRepoRoot(process.cwd());
51
+ const record = (0, bug_registry_1.buildBugRecord)({
52
+ title: options.title.trim(),
53
+ severity: options.severity,
54
+ category: options.category,
55
+ foundIn: foundIn,
56
+ observed: options.observed.trim(),
57
+ expected: options.expected.trim(),
58
+ projectId: options.projectId,
59
+ workSessionId: options.workSessionId,
60
+ changeRequestId: options.changeRequestId,
61
+ affectedCommands: splitListFlag(options.commands),
62
+ affectedFiles: splitListFlag(options.files),
63
+ userImpact: options.userImpact,
64
+ logs: options.logs,
65
+ repoRoot,
66
+ });
67
+ const path = (0, bug_registry_1.writeBugReportFile)(repoRoot, record);
68
+ process.stdout.write(`Bug report written: ${path}\n`);
69
+ process.stdout.write(`Next: classify in docs/launch-bugs.md; retro: docs/templates/escaped-bug-retro.md\n`);
70
+ }
71
+ async function runBugValidate(options) {
72
+ if (!options.file || options.file.trim().length === 0) {
73
+ process.stderr.write("Missing --file <path> to bug markdown record.\n");
74
+ process.exit(1);
75
+ return;
76
+ }
77
+ const abs = (0, node_path_1.resolve)(process.cwd(), options.file.trim());
78
+ let content;
79
+ try {
80
+ content = (0, node_fs_1.readFileSync)(abs, "utf8");
81
+ }
82
+ catch (err) {
83
+ process.stderr.write(`Failed to read bug file ${abs}: ${err instanceof Error ? err.message : String(err)}\n`);
84
+ process.exit(1);
85
+ return;
86
+ }
87
+ let record;
88
+ try {
89
+ record = (0, bug_registry_1.parseBugRecordFromFile)(content);
90
+ }
91
+ catch (err) {
92
+ process.stderr.write(`Invalid bug file ${abs}: ${err instanceof Error ? err.message : String(err)}\n`);
93
+ process.exit(1);
94
+ return;
95
+ }
96
+ const result = (0, bug_registry_1.validateBugCloseReadiness)(record);
97
+ if (result.ok) {
98
+ process.stdout.write(`OK: ${abs} satisfies close rules for status=${record.status}.\n`);
99
+ return;
100
+ }
101
+ process.stderr.write(`Bug close validation failed for ${abs}:\n`);
102
+ for (const err of result.errors) {
103
+ process.stderr.write(` - ${err}\n`);
104
+ }
105
+ process.exit(1);
106
+ }