@agentbridge1/cli 0.0.4 → 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.
@@ -1,12 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runRecover = runRecover;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const node_process_1 = require("node:process");
4
7
  const config_1 = require("../config");
5
8
  const errors_1 = require("../errors");
6
- const server_sync_1 = require("../server-sync");
7
- const node_process_1 = require("node:process");
8
9
  const gates_1 = require("../gates");
10
+ const http_1 = require("../http");
9
11
  const init_1 = require("../init");
12
+ const recovery_reconcile_1 = require("../recovery-reconcile");
13
+ const server_sync_1 = require("../server-sync");
10
14
  function resolveNetworkContext() {
11
15
  const cfg = (0, config_1.readConfig)();
12
16
  const projectId = process.env.AGENTBRIDGE_PROJECT_ID ?? cfg.projectId ?? "";
@@ -19,9 +23,14 @@ function resolveNetworkContext() {
19
23
  }
20
24
  return { projectId, apiKey, apiBaseUrl };
21
25
  }
26
+ function rulesInstalled(repoRoot) {
27
+ return ((0, node_fs_1.existsSync)((0, node_path_1.resolve)(repoRoot, "AGENTBRIDGE.md")) &&
28
+ (0, node_fs_1.existsSync)((0, node_path_1.resolve)(repoRoot, ".cursor", "rules", "agentbridge.mdc")));
29
+ }
22
30
  function renderRecoverOutput(packet) {
23
31
  const lines = [];
24
- const domainCount = packet.domains_summary.length;
32
+ const activeDomains = (packet.domains_summary ?? []).filter((d) => d.state !== "retired");
33
+ const domainCount = activeDomains.length;
25
34
  const ruleCount = packet.global_rules?.length ?? 0;
26
35
  const hasCharterContext = Boolean(packet.charter_summary?.purpose);
27
36
  lines.push("Project recovered.");
@@ -35,7 +44,7 @@ function renderRecoverOutput(packet) {
35
44
  if (domainCount > 0) {
36
45
  lines.push("");
37
46
  lines.push("Domains found:");
38
- for (const domain of packet.domains_summary) {
47
+ for (const domain of activeDomains) {
39
48
  const pathNote = domain.owned_path_count > 0 ? `${domain.owned_path_count} path(s)` : "paths pending";
40
49
  lines.push(`- ${domain.domain_name} — ${pathNote}`);
41
50
  }
@@ -54,18 +63,56 @@ function renderRecoverOutput(packet) {
54
63
  }
55
64
  lines.push("");
56
65
  lines.push("Next:");
57
- lines.push(" agentbridge start");
66
+ lines.push(" agentbridge watch");
58
67
  lines.push("");
59
68
  return lines.join("\n");
60
69
  }
61
70
  function recoveryStatusLabel(packet) {
62
71
  return packet.recovery_status ?? "unknown";
63
72
  }
64
- function baselineRequired(packet) {
65
- const status = packet.recovery_status ?? null;
66
- if (packet.project_mode !== "recovery")
67
- return false;
68
- return status === "baseline_required" || status === "pending" || status === null;
73
+ function formatDomainLines(refs) {
74
+ if (refs.length === 0)
75
+ return " (none)";
76
+ return refs.map((d) => `- ${d.name}`).join("\n");
77
+ }
78
+ function renderReconcileReport(input) {
79
+ const { plan, applied, qualityAfter } = input;
80
+ const lines = [""];
81
+ if (applied) {
82
+ lines.push("AgentBridge checked recovery.");
83
+ lines.push(`Recovery quality: ${qualityAfter}`);
84
+ if (plan.toAdd.length > 0) {
85
+ lines.push("");
86
+ lines.push("Added:");
87
+ lines.push(formatDomainLines(plan.toAdd));
88
+ }
89
+ if (plan.toPreserve.length > 0) {
90
+ lines.push("");
91
+ lines.push("Existing domains preserved:");
92
+ lines.push(formatDomainLines(plan.toPreserve));
93
+ }
94
+ if (plan.toRetire.length > 0) {
95
+ lines.push("");
96
+ lines.push("Retired:");
97
+ lines.push(formatDomainLines(plan.toRetire));
98
+ }
99
+ }
100
+ else {
101
+ lines.push("AgentBridge checked recovery.");
102
+ lines.push("No changes needed.");
103
+ lines.push("Recovery is up to date.");
104
+ lines.push(`Recovery quality: ${qualityAfter}`);
105
+ }
106
+ return lines.join("\n") + "\n";
107
+ }
108
+ function renderBasicHint() {
109
+ return [
110
+ "",
111
+ "Recovery: basic",
112
+ "AgentBridge found only a generic project area.",
113
+ "Run `agent recover --force` to rebuild the domain map.",
114
+ "",
115
+ ].join("\n");
69
116
  }
70
117
  function renderRecoveryStatusBlock(input) {
71
118
  const lines = [];
@@ -78,6 +125,60 @@ function renderRecoveryStatusBlock(input) {
78
125
  lines.push("");
79
126
  return lines.join("\n");
80
127
  }
128
+ function normalizeBootstrapError(error) {
129
+ if ((0, http_1.isCliHttpError)(error) && error.status === 422) {
130
+ const parsed = (0, http_1.parseCliHttpErrorBody)(error);
131
+ const details = [];
132
+ const nestedError = parsed && typeof parsed.error === "object" && parsed.error !== null
133
+ ? parsed.error
134
+ : null;
135
+ const fieldErrors = nestedError && typeof nestedError.fieldErrors === "object" && nestedError.fieldErrors !== null
136
+ ? nestedError.fieldErrors
137
+ : null;
138
+ if (fieldErrors) {
139
+ for (const [field, value] of Object.entries(fieldErrors)) {
140
+ if (Array.isArray(value) && value.length > 0) {
141
+ details.push(`${field}: ${String(value[0])}`);
142
+ }
143
+ }
144
+ }
145
+ const detailText = details.length > 0 ? ` (${details.join("; ")})` : "";
146
+ return (0, errors_1.catalogCliError)("CONFIG_INCOMPLETE", {
147
+ what: `Recovery bootstrap payload was rejected by server validation${detailText}.`,
148
+ why: "The server cannot activate recovery until required bootstrap fields pass validation.",
149
+ next: "Retry `agentbridge recover`. If it fails again, run with AGENTBRIDGE_DEBUG=1 and share output.",
150
+ });
151
+ }
152
+ if (error instanceof Error) {
153
+ const message = error.message;
154
+ if (/does not have any commits yet/i.test(message) ||
155
+ /bad revision ['"]head['"]/i.test(message) ||
156
+ /not a git repository/i.test(message)) {
157
+ return new errors_1.SafeCliError({
158
+ code: "CONFIG_INCOMPLETE",
159
+ category: "CONFIG_ERROR",
160
+ what: "Repository has no usable git history for recovery mapping.",
161
+ why: "Recover infers baseline context from commit history and cannot proceed without it.",
162
+ next: [
163
+ "Create an initial commit, then rerun recover:",
164
+ "1) git add .",
165
+ "2) git commit -m \"Initial commit\"",
166
+ "3) agentbridge recover",
167
+ ].join("\n"),
168
+ });
169
+ }
170
+ if (/payload too large after compaction/i.test(message)) {
171
+ return new errors_1.SafeCliError({
172
+ code: "CONFIG_INCOMPLETE",
173
+ category: "CONFIG_ERROR",
174
+ what: "Repository evidence exceeded bootstrap payload limits.",
175
+ why: "The recovery evidence payload could not be compacted enough for server ingestion.",
176
+ next: "Run `agentbridge init` for an interactive baseline, or retry recover after reducing repository noise.",
177
+ });
178
+ }
179
+ }
180
+ return error;
181
+ }
81
182
  async function runRecover(options = {}) {
82
183
  process.exitCode = 0;
83
184
  process.stdout.write([
@@ -120,18 +221,64 @@ async function runRecover(options = {}) {
120
221
  return;
121
222
  }
122
223
  const beforePacket = packet;
123
- const needsRecovery = baselineRequired(beforePacket);
124
- let actionTaken = "baseline already exists; no rebuild needed";
125
- if (needsRecovery || options.force) {
126
- actionTaken = needsRecovery
224
+ const cfg = (0, config_1.readConfig)();
225
+ const repoRoot = (0, node_process_1.cwd)();
226
+ const rulesOk = rulesInstalled(repoRoot);
227
+ let scan;
228
+ try {
229
+ process.stdout.write("Scanning repository layout...\n");
230
+ scan = await (0, init_1.scanRecoveryFromRepo)(ctx);
231
+ }
232
+ catch (error) {
233
+ process.stderr.write(`${(0, errors_1.renderCliError)(normalizeBootstrapError(error))}\n`);
234
+ process.exitCode = 1;
235
+ return;
236
+ }
237
+ const existingActive = (0, recovery_reconcile_1.packetDomainsToRefs)(beforePacket, cfg.domains ?? []);
238
+ const plan = (0, recovery_reconcile_1.buildReconcilePlan)({
239
+ existingActive,
240
+ candidates: scan.candidateRefs,
241
+ mode: options.force ? "force" : "safe",
242
+ packet: beforePacket,
243
+ rulesInstalled: rulesOk,
244
+ });
245
+ const needsBaseline = (0, recovery_reconcile_1.recoveryBaselineRequired)(beforePacket);
246
+ const shouldApply = needsBaseline || options.force || plan.hasChanges;
247
+ let actionTaken = "reconciliation check only; no changes needed";
248
+ if (shouldApply) {
249
+ actionTaken = needsBaseline
127
250
  ? "build recovery baseline from repository evidence"
128
- : "force-refresh recovery baseline from repository evidence";
129
- process.stdout.write("Building recovery baseline now...\n");
130
- await (0, init_1.runBootstrapRecovery)(ctx);
131
- packet = await (0, server_sync_1.fetchProjectPacket)(ctx);
251
+ : options.force
252
+ ? "force-refresh recovery baseline from repository evidence"
253
+ : "reconcile recovery with repository evidence";
254
+ process.stdout.write("Applying recovery changes...\n");
255
+ try {
256
+ await (0, init_1.runBootstrapRecovery)(ctx, {
257
+ reconcilePlan: plan,
258
+ scan,
259
+ });
260
+ packet = await (0, server_sync_1.fetchProjectPacket)(ctx);
261
+ }
262
+ catch (error) {
263
+ process.stderr.write(`${(0, errors_1.renderCliError)(normalizeBootstrapError(error))}\n`);
264
+ process.exitCode = 1;
265
+ return;
266
+ }
132
267
  }
133
268
  const afterPacket = packet;
134
- if (baselineRequired(afterPacket)) {
269
+ const qualityAfter = (0, recovery_reconcile_1.recoveryQualityLabel)((0, recovery_reconcile_1.buildReconcilePlan)({
270
+ existingActive: (0, recovery_reconcile_1.packetDomainsToRefs)(afterPacket, cfg.domains ?? []),
271
+ candidates: scan.candidateRefs,
272
+ mode: options.force ? "force" : "safe",
273
+ packet: afterPacket,
274
+ rulesInstalled: rulesOk,
275
+ }).quality);
276
+ process.stdout.write(renderReconcileReport({
277
+ plan,
278
+ applied: shouldApply,
279
+ qualityAfter,
280
+ }));
281
+ if ((0, recovery_reconcile_1.recoveryBaselineRequired)(afterPacket)) {
135
282
  process.stderr.write([
136
283
  "Recovery could not complete: baseline still required.",
137
284
  "",
@@ -146,7 +293,6 @@ async function runRecover(options = {}) {
146
293
  process.exitCode = 1;
147
294
  return;
148
295
  }
149
- const repoRoot = (0, node_process_1.cwd)();
150
296
  (0, gates_1.ensureGitignoreSessionEntry)(repoRoot);
151
297
  (0, gates_1.ensureGatesFile)(repoRoot);
152
298
  process.stdout.write(renderRecoveryStatusBlock({
@@ -154,7 +300,10 @@ async function runRecover(options = {}) {
154
300
  before: beforePacket,
155
301
  after: afterPacket,
156
302
  action: actionTaken,
157
- next: "agentbridge start",
303
+ next: "agentbridge watch",
158
304
  }));
159
- process.stdout.write(renderRecoverOutput(packet));
305
+ process.stdout.write(renderRecoverOutput(afterPacket));
306
+ if ((0, recovery_reconcile_1.recoveryIsBasicPacket)(afterPacket)) {
307
+ process.stdout.write(renderBasicHint());
308
+ }
160
309
  }
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runSetupMcp = runSetupMcp;
4
+ const node_path_1 = require("node:path");
4
5
  const config_1 = require("../config");
6
+ const mcp_runtime_1 = require("../mcp-runtime");
5
7
  const MCP_SERVER_NAME = "agentbridge";
6
8
  function standardSnippet(projectId, apiKey, apiBaseUrl) {
7
9
  const serverConfig = {
@@ -47,8 +49,27 @@ async function runSetupMcp(options = {}) {
47
49
  else {
48
50
  process.stdout.write("\n✓ Credentials filled from .agentbridge/config.json\n");
49
51
  }
52
+ const cliDistDir = (0, node_path_1.resolve)(__dirname, "..");
53
+ if (!(0, mcp_runtime_1.isMcpRuntimeAvailable)(cliDistDir)) {
54
+ process.stdout.write([
55
+ "",
56
+ "⚠ MCP runtime is missing from this CLI install.",
57
+ " `agentbridge mcp` will exit until you reinstall:",
58
+ " npm install -g @agentbridge1/cli@latest",
59
+ "",
60
+ " Or point mcp.json at a repo build:",
61
+ " node /path/to/AuthAgent/cli/dist/mcp/agentbridge-mcp.js",
62
+ "",
63
+ ].join("\n"));
64
+ }
65
+ else {
66
+ process.stdout.write([
67
+ "",
68
+ "✓ MCP runtime is bundled with this CLI (`agentbridge mcp` is ready).",
69
+ "",
70
+ ].join("\n"));
71
+ }
50
72
  process.stdout.write([
51
- "",
52
73
  "After saving the config, restart your editor to activate the MCP server.",
53
74
  "The MCP server lets your AI assistant call tools like agent_hello, check AgentBridge status, and trigger approvals.",
54
75
  "",
@@ -39,9 +39,8 @@ const domain_resolution_1 = require("../domain-resolution");
39
39
  const errors_1 = require("../errors");
40
40
  const error_catalog_1 = require("../error-catalog");
41
41
  const http_1 = require("../http");
42
- const git_status_1 = require("../git-status");
43
42
  const session_1 = require("../session");
44
- const work_contract_1 = require("../work-contract");
43
+ const session_state_1 = require("../session-state");
45
44
  const server_sync_1 = require("../server-sync");
46
45
  const work_context_resolver_1 = require("../work-context-resolver");
47
46
  const PROMOTION_PLAN = {
@@ -395,6 +394,29 @@ function renderOtherSessions(lines, sessionIds) {
395
394
  ...sessionIds.map((id) => `- ${id}`),
396
395
  ];
397
396
  }
397
+ async function resolveExecutionSurfaceIdForStart(ctx, opts, cfgExecutionSurfaceId) {
398
+ const explicitExecutionSurfaceId = opts.executionSurfaceId?.trim();
399
+ if (explicitExecutionSurfaceId) {
400
+ (0, config_1.updateConfig)({ executionSurfaceId: explicitExecutionSurfaceId });
401
+ return explicitExecutionSurfaceId;
402
+ }
403
+ const configuredExecutionSurfaceId = cfgExecutionSurfaceId?.trim();
404
+ if (configuredExecutionSurfaceId) {
405
+ return configuredExecutionSurfaceId;
406
+ }
407
+ try {
408
+ const callerPacket = await (0, server_sync_1.fetchCallerIdentityPacket)(ctx);
409
+ const discoveredExecutionSurfaceId = callerPacket?.execution_surface?.id?.trim() || null;
410
+ if (discoveredExecutionSurfaceId) {
411
+ (0, config_1.updateConfig)({ executionSurfaceId: discoveredExecutionSurfaceId });
412
+ return discoveredExecutionSurfaceId;
413
+ }
414
+ }
415
+ catch {
416
+ // Keep the existing catalog error path if caller identity discovery fails.
417
+ }
418
+ return null;
419
+ }
398
420
  async function executeStartWorkSession(opts) {
399
421
  const summary = trimRequired(opts.summary ?? "", "--summary");
400
422
  const scope = trimRequired(opts.scope ?? "", "--scope");
@@ -406,7 +428,7 @@ async function executeStartWorkSession(opts) {
406
428
  if (!explicitAgentId && !configuredActiveAgentId) {
407
429
  throw (0, errors_1.catalogCliError)("START_MISSING_ACTIVE_AGENT");
408
430
  }
409
- const executionSurfaceId = cfg.executionSurfaceId?.trim();
431
+ const executionSurfaceId = await resolveExecutionSurfaceIdForStart(ctx, opts, cfg.executionSurfaceId);
410
432
  if (!executionSurfaceId) {
411
433
  throw (0, errors_1.catalogCliError)("START_EXECUTION_SURFACE_REQUIRED");
412
434
  }
@@ -544,83 +566,55 @@ async function executeStartWorkSession(opts) {
544
566
  otherSessionIds: [...new Set(otherSessionIds)],
545
567
  };
546
568
  }
547
- async function runSmartStart(opts = {}) {
548
- const cfg = (0, config_1.readConfig)();
549
- await ensureProjectAccess();
550
- const explicitAgentId = opts.agentId?.trim();
551
- const configuredActiveAgentId = cfg.activeAgentId?.trim();
552
- if (!explicitAgentId && !configuredActiveAgentId) {
553
- throw (0, errors_1.catalogCliError)("START_MISSING_ACTIVE_AGENT");
554
- }
555
- if (!cfg.executionSurfaceId?.trim()) {
556
- throw (0, errors_1.catalogCliError)("START_EXECUTION_SURFACE_REQUIRED");
557
- }
558
- const ctx = (0, config_1.contextFromConfig)();
559
- const resolution = await (0, work_context_resolver_1.resolveWorkContext)(ctx, {
560
- allowServerCurrentForCr: true,
561
- includeOtherActiveSessions: true,
562
- });
563
- if (resolution.state === "current_session_resolved") {
564
- process.stdout.write("Active work session found - entering watch mode.\n");
565
- if (!opts.skipWatch) {
566
- const { runWatch } = await Promise.resolve().then(() => __importStar(require("./watch")));
567
- await runWatch({ allowDirty: true });
568
- }
569
- return;
570
- }
571
- if (resolution.state === "ambiguous_active_sessions") {
572
- process.stdout.write(`${(0, work_context_resolver_1.renderWorkContextLines)(resolution).join("\n")}\n`);
573
- throw (0, errors_1.catalogCliError)((0, error_catalog_1.workContextStateToCode)(resolution.state));
574
- }
575
- if (resolution.state === "mismatch" || resolution.state === "stale_orphan_session_detected") {
576
- process.stdout.write(`${(0, work_context_resolver_1.renderWorkContextLines)(resolution).join("\n")}\n`);
577
- throw (0, errors_1.catalogCliError)((0, error_catalog_1.workContextStateToCode)(resolution.state));
578
- }
579
- const inferred = (0, work_contract_1.inferWorkContract)({
569
+ function hasActiveLocalRun() {
570
+ const local = (0, session_state_1.readSessionState)();
571
+ if (!local)
572
+ return false;
573
+ if (local.status !== "active" && local.status !== "blocked")
574
+ return false;
575
+ if (!local.id || local.id === "none")
576
+ return false;
577
+ return true;
578
+ }
579
+ async function runDefaultStart(opts = {}) {
580
+ const { runLocalSupervision } = await Promise.resolve().then(() => __importStar(require("../local-supervision")));
581
+ await runLocalSupervision({
580
582
  prompt: opts.prompt,
581
- branchName: (0, git_status_1.getBranchName)(),
582
- dirtyFiles: (0, git_status_1.getDirtyWorkingTreeFiles)(),
583
- activeChangeRequestId: cfg.activeChangeRequestId ?? null,
584
- });
585
- const draft = {
586
- summary: inferred.summary,
587
- scope: opts.scope?.trim() || inferred.scope,
588
- };
589
- const confirmed = await (0, work_contract_1.confirmWorkContract)(draft, {
590
- nonInteractive: !process.stdin.isTTY,
591
- });
592
- if (!confirmed) {
593
- process.stdout.write("Cancelled.\n");
594
- return;
595
- }
596
- await executeStartWorkSession({
597
- ...opts,
598
- summary: confirmed.summary,
599
- scope: confirmed.scope,
583
+ details: opts.details,
584
+ confirmClose: opts.confirmClose,
600
585
  });
601
- process.stdout.write("Work session started. Entering watch mode...\n");
602
- if (!opts.skipWatch) {
603
- const { runWatch } = await Promise.resolve().then(() => __importStar(require("./watch")));
604
- await runWatch({ allowDirty: true });
605
- }
586
+ }
587
+ function isStrictTrackedStartRequest(opts) {
588
+ if (opts.resume === true)
589
+ return true;
590
+ if (opts.changeRequestId?.trim())
591
+ return true;
592
+ const explicitSummaryFlag = opts.summaryFlagProvided === true;
593
+ const explicitScopeFlag = opts.scopeFlagProvided === true;
594
+ const explicitInputProvided = opts.summary !== undefined || opts.scope !== undefined;
595
+ return explicitSummaryFlag || explicitScopeFlag || explicitInputProvided;
606
596
  }
607
597
  async function runStart(opts = {}) {
598
+ const strictTrackedStart = isStrictTrackedStartRequest(opts);
608
599
  const explicitSummaryFlag = opts.summaryFlagProvided === true;
609
600
  const explicitScopeFlag = opts.scopeFlagProvided === true;
610
601
  const explicitInputProvided = opts.summary !== undefined || opts.scope !== undefined;
611
602
  const explicitModeRequested = explicitSummaryFlag || explicitScopeFlag || explicitInputProvided;
612
603
  const explicitSummary = opts.summary?.trim();
613
604
  const explicitScope = opts.scope?.trim();
614
- if (explicitModeRequested && explicitSummary && explicitScope) {
605
+ if (strictTrackedStart && explicitSummary && explicitScope) {
615
606
  await executeStartWorkSession({ ...opts, summary: explicitSummary, scope: explicitScope });
616
607
  if (!opts.skipWatch) {
617
608
  const { runWatch } = await Promise.resolve().then(() => __importStar(require("./watch")));
618
- await runWatch({ allowDirty: true });
609
+ await runWatch({ allowDirty: true, details: opts.details });
619
610
  }
620
611
  return;
621
612
  }
613
+ if (strictTrackedStart) {
614
+ throw (0, errors_1.catalogCliError)("START_EXPLICIT_PAIR_REQUIRED");
615
+ }
622
616
  if (explicitModeRequested) {
623
617
  throw (0, errors_1.catalogCliError)("START_EXPLICIT_PAIR_REQUIRED");
624
618
  }
625
- await runSmartStart(opts);
619
+ await runDefaultStart(opts);
626
620
  }