@agentbridge1/cli 0.0.8 → 0.0.10

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.
@@ -2980,7 +2980,7 @@ var require_compile = __commonJS({
2980
2980
  const schOrFunc = root.refs[ref];
2981
2981
  if (schOrFunc)
2982
2982
  return schOrFunc;
2983
- let _sch = resolve2.call(this, root, ref);
2983
+ let _sch = resolve3.call(this, root, ref);
2984
2984
  if (_sch === void 0) {
2985
2985
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
2986
2986
  const { schemaId } = this.opts;
@@ -3007,7 +3007,7 @@ var require_compile = __commonJS({
3007
3007
  function sameSchemaEnv(s1, s2) {
3008
3008
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3009
3009
  }
3010
- function resolve2(root, ref) {
3010
+ function resolve3(root, ref) {
3011
3011
  let sch;
3012
3012
  while (typeof (sch = this.refs[ref]) == "string")
3013
3013
  ref = sch;
@@ -3582,7 +3582,7 @@ var require_fast_uri = __commonJS({
3582
3582
  }
3583
3583
  return uri;
3584
3584
  }
3585
- function resolve2(baseURI, relativeURI, options) {
3585
+ function resolve3(baseURI, relativeURI, options) {
3586
3586
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3587
3587
  const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
3588
3588
  schemelessOptions.skipEscape = true;
@@ -3809,7 +3809,7 @@ var require_fast_uri = __commonJS({
3809
3809
  var fastUri = {
3810
3810
  SCHEMES,
3811
3811
  normalize,
3812
- resolve: resolve2,
3812
+ resolve: resolve3,
3813
3813
  resolveComponent,
3814
3814
  equal,
3815
3815
  serialize,
@@ -18892,7 +18892,7 @@ var Protocol = class {
18892
18892
  return;
18893
18893
  }
18894
18894
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
18895
- await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
18895
+ await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
18896
18896
  options?.signal?.throwIfAborted();
18897
18897
  }
18898
18898
  } catch (error2) {
@@ -18909,7 +18909,7 @@ var Protocol = class {
18909
18909
  */
18910
18910
  request(request, resultSchema, options) {
18911
18911
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
18912
- return new Promise((resolve2, reject) => {
18912
+ return new Promise((resolve3, reject) => {
18913
18913
  const earlyReject = (error2) => {
18914
18914
  reject(error2);
18915
18915
  };
@@ -18987,7 +18987,7 @@ var Protocol = class {
18987
18987
  if (!parseResult.success) {
18988
18988
  reject(parseResult.error);
18989
18989
  } else {
18990
- resolve2(parseResult.data);
18990
+ resolve3(parseResult.data);
18991
18991
  }
18992
18992
  } catch (error2) {
18993
18993
  reject(error2);
@@ -19248,12 +19248,12 @@ var Protocol = class {
19248
19248
  }
19249
19249
  } catch {
19250
19250
  }
19251
- return new Promise((resolve2, reject) => {
19251
+ return new Promise((resolve3, reject) => {
19252
19252
  if (signal.aborted) {
19253
19253
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
19254
19254
  return;
19255
19255
  }
19256
- const timeoutId = setTimeout(resolve2, interval);
19256
+ const timeoutId = setTimeout(resolve3, interval);
19257
19257
  signal.addEventListener("abort", () => {
19258
19258
  clearTimeout(timeoutId);
19259
19259
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -20353,7 +20353,7 @@ var McpServer = class {
20353
20353
  let task = createTaskResult.task;
20354
20354
  const pollInterval = task.pollInterval ?? 5e3;
20355
20355
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
20356
- await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
20356
+ await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
20357
20357
  const updatedTask = await extra.taskStore.getTask(taskId);
20358
20358
  if (!updatedTask) {
20359
20359
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -21002,20 +21002,161 @@ var StdioServerTransport = class {
21002
21002
  this.onclose?.();
21003
21003
  }
21004
21004
  send(message) {
21005
- return new Promise((resolve2) => {
21005
+ return new Promise((resolve3) => {
21006
21006
  const json = serializeMessage(message);
21007
21007
  if (this._stdout.write(json)) {
21008
- resolve2();
21008
+ resolve3();
21009
21009
  } else {
21010
- this._stdout.once("drain", resolve2);
21010
+ this._stdout.once("drain", resolve3);
21011
21011
  }
21012
21012
  });
21013
21013
  }
21014
21014
  };
21015
21015
 
21016
+ // ../mcp/agentbridge-mcp.ts
21017
+ var import_node_fs3 = require("node:fs");
21018
+ var import_node_path2 = require("node:path");
21019
+
21016
21020
  // src/session.ts
21017
21021
  var import_node_child_process2 = require("node:child_process");
21018
21022
 
21023
+ // src/intent-parser.ts
21024
+ var ACTIONS = ["fix", "add", "update", "refactor", "remove", "migrate", "rename", "revert", "delete", "implement", "improve", "patch"];
21025
+ var DOMAIN_KEYWORD_MAP = {
21026
+ auth: ["auth", "login", "logout", "session", "token", "oauth", "jwt", "password", "signin", "signup", "sign-in", "sign-up", "register", "credential", "permission", "role"],
21027
+ database: ["database", "db", "schema", "migration", "migrate", "prisma", "sql", "query", "table", "column", "index", "model", "seed", "orm"],
21028
+ payments: ["payment", "payments", "billing", "stripe", "invoice", "subscription", "checkout", "webhook"],
21029
+ api: ["api", "endpoint", "route", "handler", "controller", "request", "response", "rest", "graphql"],
21030
+ ui: ["ui", "style", "css", "component", "page", "layout", "copy", "design", "render", "display", "view", "frontend", "html", "template"],
21031
+ mcp: ["mcp", "agentbridge", "cursor", "rules"],
21032
+ tests: ["test", "tests", "spec", "vitest", "jest", "coverage", "proof"]
21033
+ };
21034
+ var SYMPTOMS = {
21035
+ "401": ["401", "unauthorized", "unauthenticated"],
21036
+ "403": ["403", "forbidden"],
21037
+ "404": ["404", "not found", "notfound"],
21038
+ "500": ["500", "internal server error", "server error"],
21039
+ "null": ["null", "undefined", "nan", "nil"],
21040
+ "crash": ["crash", "crashes", "crashed", "exception", "throws", "throw"],
21041
+ "loop": ["loop", "infinite loop", "recursion"],
21042
+ "hang": ["hang", "hangs", "timeout", "deadlock"],
21043
+ "slow": ["slow", "performance", "latency", "memory leak", "memory"],
21044
+ "duplicate": ["duplicate", "duplicated", "twice"],
21045
+ "missing": ["missing", "not showing", "not found"]
21046
+ };
21047
+ var ROUTE_RE = /(?<!\w)(\/[a-zA-Z0-9_-]+(?:\/[a-zA-Z0-9_:_-]+)*)/g;
21048
+ var FILE_RE = /\b([A-Za-z][A-Za-z0-9_-]*\.[a-z]{2,4})\b/g;
21049
+ var PASCAL_RE = /\b([A-Z][a-z]+(?:[A-Z][a-z]*)+)\b/g;
21050
+ var CAMEL_FN_RE = /\b([a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)+)\b/g;
21051
+ var QUOTED_RE = /"([^"]+)"|'([^']+)'/g;
21052
+ function detectDomain(lower) {
21053
+ for (const [domain, keywords] of Object.entries(DOMAIN_KEYWORD_MAP)) {
21054
+ if (keywords.some((kw) => lower.includes(kw))) return domain;
21055
+ }
21056
+ return null;
21057
+ }
21058
+ function detectAction(lower) {
21059
+ for (const action of ACTIONS) {
21060
+ if (lower.startsWith(action) || lower.includes(` ${action} `)) return action;
21061
+ }
21062
+ return null;
21063
+ }
21064
+ function detectSymptom(lower) {
21065
+ for (const [symptom, patterns] of Object.entries(SYMPTOMS)) {
21066
+ if (patterns.some((p) => lower.includes(p))) return symptom;
21067
+ }
21068
+ return null;
21069
+ }
21070
+ function extractTargets(raw) {
21071
+ const targets = /* @__PURE__ */ new Set();
21072
+ for (const m of raw.matchAll(QUOTED_RE)) {
21073
+ const phrase = m[1] ?? m[2];
21074
+ if (phrase) targets.add(phrase.trim());
21075
+ }
21076
+ for (const m of raw.matchAll(ROUTE_RE)) {
21077
+ targets.add(m[1]);
21078
+ }
21079
+ for (const m of raw.matchAll(FILE_RE)) {
21080
+ targets.add(m[1]);
21081
+ }
21082
+ for (const m of raw.matchAll(PASCAL_RE)) {
21083
+ targets.add(m[1]);
21084
+ }
21085
+ for (const m of raw.matchAll(CAMEL_FN_RE)) {
21086
+ if (m[1].length <= 40) targets.add(m[1]);
21087
+ }
21088
+ return [...targets];
21089
+ }
21090
+ function inferLayers(domain, targets, symptom, action, rawLower) {
21091
+ const layers = [];
21092
+ const hasRoute = targets.some((t) => t.startsWith("/"));
21093
+ const hasMigration = action === "migrate" || targets.some((t) => t.toLowerCase().includes("migration"));
21094
+ if (domain === "auth") {
21095
+ if (symptom === "401" || symptom === "403") layers.push("route_handler", "auth_middleware");
21096
+ else if (hasRoute) layers.push("route_handler", "auth_middleware");
21097
+ else layers.push("auth_middleware");
21098
+ }
21099
+ if (domain === "api") {
21100
+ if (hasRoute) layers.push("route_handler");
21101
+ else layers.push("route_handler");
21102
+ }
21103
+ if (domain === "database") {
21104
+ if (hasMigration) layers.push("schema", "migration_file");
21105
+ else layers.push("model", "query");
21106
+ }
21107
+ if (domain === "ui") {
21108
+ layers.push("component", "view");
21109
+ }
21110
+ if (domain === "payments") {
21111
+ if (targets.some((t) => t.toLowerCase().includes("webhook")) || rawLower.includes("webhook")) {
21112
+ layers.push("webhook_handler");
21113
+ } else {
21114
+ layers.push("billing_handler");
21115
+ }
21116
+ }
21117
+ if (domain === "mcp") {
21118
+ layers.push("mcp_server", "config");
21119
+ }
21120
+ return layers;
21121
+ }
21122
+ function parseIntent(raw) {
21123
+ if (!raw?.trim()) {
21124
+ return { raw: raw ?? "", domain: null, targets: [], symptom: null, action: null, affected_layer: [] };
21125
+ }
21126
+ const lower = raw.toLowerCase();
21127
+ const domain = detectDomain(lower);
21128
+ const action = detectAction(lower);
21129
+ const symptom = detectSymptom(lower);
21130
+ const targets = extractTargets(raw);
21131
+ const affected_layer = inferLayers(domain, targets, symptom, action, lower);
21132
+ return { raw, domain, targets, symptom, action, affected_layer };
21133
+ }
21134
+ function buildTaskSpecificCriteria(parsed) {
21135
+ const extra = [];
21136
+ const routeTargets = parsed.targets.filter((t) => t.startsWith("/"));
21137
+ const fileTargets = parsed.targets.filter((t) => t.includes("."));
21138
+ const namedTargets = parsed.targets.filter((t) => !t.startsWith("/") && !t.includes("."));
21139
+ if (routeTargets.length > 0 && parsed.domain === "auth" && parsed.symptom) {
21140
+ extra.push(`The ${routeTargets[0]} route handler or its auth middleware chain is updated to address the ${parsed.symptom}.`);
21141
+ } else if (routeTargets.length > 0 && parsed.domain === "api") {
21142
+ extra.push(`The ${routeTargets[0]} endpoint handler is updated as intended.`);
21143
+ } else if (routeTargets.length > 0) {
21144
+ extra.push(`Changes are focused on the ${routeTargets[0]} route.`);
21145
+ } else if (fileTargets.length > 0) {
21146
+ extra.push(`Changes are focused on ${fileTargets[0]}.`);
21147
+ } else if (namedTargets.length > 0) {
21148
+ extra.push(`Changes are focused on ${namedTargets[0]}.`);
21149
+ }
21150
+ if (parsed.symptom && routeTargets.length > 0) {
21151
+ extra.push(`Proof exists for the ${parsed.symptom} behavior on ${routeTargets[0]}.`);
21152
+ } else if (parsed.symptom) {
21153
+ extra.push(`Proof exists that the ${parsed.symptom} condition is resolved.`);
21154
+ } else if (parsed.action && parsed.domain) {
21155
+ extra.push(`Proof exists that the ${parsed.action} to the ${parsed.domain} layer is correct.`);
21156
+ }
21157
+ return extra;
21158
+ }
21159
+
21019
21160
  // src/session-state.ts
21020
21161
  var import_node_fs2 = require("node:fs");
21021
21162
  var import_node_path = require("node:path");
@@ -21064,6 +21205,9 @@ function normalizePath2(path) {
21064
21205
  function isProofNoiseFile(file) {
21065
21206
  const normalized = normalizePath2(file);
21066
21207
  const lower = normalized.toLowerCase();
21208
+ if (lower === ".agentbridge" || lower.endsWith("/.agentbridge") || lower.includes(".agentbridge/")) {
21209
+ return true;
21210
+ }
21067
21211
  if (lower === "agentbridge.md" || lower === ".cursor" || lower.startsWith(".cursor/")) {
21068
21212
  return true;
21069
21213
  }
@@ -21175,6 +21319,15 @@ function isLocalSessionState(value) {
21175
21319
  if (value.updatedAt !== void 0 && typeof value.updatedAt !== "string") return false;
21176
21320
  if (value.closedAt !== void 0 && typeof value.closedAt !== "string") return false;
21177
21321
  if (value.claimedPaths !== void 0 && !isStringArray(value.claimedPaths)) return false;
21322
+ if (value.createdBy !== void 0 && value.createdBy !== "user_start" && value.createdBy !== "agent_hello") {
21323
+ return false;
21324
+ }
21325
+ if (value.contractProfiles !== void 0 && !isStringArray(value.contractProfiles)) return false;
21326
+ if (value.expectedSurfaces !== void 0 && !isStringArray(value.expectedSurfaces)) return false;
21327
+ if (value.expectedFileAreas !== void 0 && !isStringArray(value.expectedFileAreas)) return false;
21328
+ if (value.completionCriteria !== void 0 && !isStringArray(value.completionCriteria)) return false;
21329
+ if (value.proofNeeded !== void 0 && !isStringArray(value.proofNeeded)) return false;
21330
+ if (value.riskyOmissions !== void 0 && !isStringArray(value.riskyOmissions)) return false;
21178
21331
  if (value.lastLocalVerificationRun !== void 0 && !isLocalVerificationRun(value.lastLocalVerificationRun)) {
21179
21332
  return false;
21180
21333
  }
@@ -21243,6 +21396,296 @@ function writeSessionState(state) {
21243
21396
  }
21244
21397
  (0, import_node_fs2.renameSync)(tempPath, path);
21245
21398
  }
21399
+ function clearSessionState() {
21400
+ writeSessionState({
21401
+ id: "none",
21402
+ agentId: "none",
21403
+ laneDomain: null,
21404
+ status: "closed",
21405
+ closeReason: "completed",
21406
+ changedFiles: [],
21407
+ crossings: [],
21408
+ approvals: [],
21409
+ domains: [],
21410
+ createdAt: (/* @__PURE__ */ new Date(0)).toISOString()
21411
+ });
21412
+ }
21413
+
21414
+ // src/contract-intelligence.ts
21415
+ var MCP_RULE_PROFILE = "mcp_rules_config_install";
21416
+ var AUTH_PROFILE = "auth_login_session";
21417
+ var DB_PROFILE = "database_schema_migration";
21418
+ var UI_PROFILE = "ui_style_copy";
21419
+ var API_PROFILE = "api_endpoint";
21420
+ var TEST_PROFILE = "tests";
21421
+ var PAYMENTS_PROFILE = "payments_webhooks";
21422
+ var GENERIC_PROFILE = "generic";
21423
+ function unique(values) {
21424
+ return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
21425
+ }
21426
+ function matchIntent(intent, keywords) {
21427
+ const lower = intent.toLowerCase();
21428
+ return keywords.some((keyword) => lower.includes(keyword));
21429
+ }
21430
+ function detectProfiles(intent) {
21431
+ const profiles = [];
21432
+ if (matchIntent(intent, ["mcp"]) && matchIntent(intent, ["rule", "rules", "install", "setup", "config", "configuration"])) {
21433
+ profiles.push(MCP_RULE_PROFILE);
21434
+ }
21435
+ if (matchIntent(intent, ["auth", "login", "session", "token", "oauth"])) {
21436
+ profiles.push(AUTH_PROFILE);
21437
+ }
21438
+ if (matchIntent(intent, ["database", "db", "schema", "migration", "prisma", "sql"])) {
21439
+ profiles.push(DB_PROFILE);
21440
+ }
21441
+ if (matchIntent(intent, ["ui", "style", "css", "copy", "layout", "component"])) {
21442
+ profiles.push(UI_PROFILE);
21443
+ }
21444
+ if (matchIntent(intent, ["api", "endpoint", "route", "handler", "controller"])) {
21445
+ profiles.push(API_PROFILE);
21446
+ }
21447
+ if (matchIntent(intent, ["test", "tests", "vitest", "jest", "proof"])) {
21448
+ profiles.push(TEST_PROFILE);
21449
+ }
21450
+ if (matchIntent(intent, ["payment", "payments", "billing", "stripe", "webhook"])) {
21451
+ profiles.push(PAYMENTS_PROFILE);
21452
+ }
21453
+ return profiles.length > 0 ? profiles : [GENERIC_PROFILE];
21454
+ }
21455
+ function profileBlueprint(profile) {
21456
+ switch (profile) {
21457
+ case MCP_RULE_PROFILE:
21458
+ return {
21459
+ expectedSurfaces: ["mcp", "rules_installation", "configuration_flow", "tests_or_manual_proof"],
21460
+ expectedFileAreas: [
21461
+ "mcp/**",
21462
+ "mcp/agentbridge-mcp.ts",
21463
+ "AGENTBRIDGE.md",
21464
+ ".cursor/rules/**",
21465
+ "cli/src/mcp-config.ts",
21466
+ "cli/src/commands/setup-mcp.ts"
21467
+ ],
21468
+ completionCriteria: [
21469
+ "MCP configuration path triggers AgentBridge rule installation.",
21470
+ "Rules are created or updated in the expected Cursor rules location.",
21471
+ "Existing rules/config are preserved or safely updated.",
21472
+ "Re-running MCP configuration is idempotent and does not duplicate rules.",
21473
+ "Proof exists that MCP configuration causes rules to be installed."
21474
+ ],
21475
+ proofNeeded: [
21476
+ "Run MCP setup/configuration flow in a clean repo.",
21477
+ "Confirm the AgentBridge Cursor rules file is created.",
21478
+ "Run setup/configuration again to confirm idempotency.",
21479
+ "Record test/manual proof output."
21480
+ ],
21481
+ riskyOmissions: [
21482
+ "MCP server file changed but no install trigger wired to the configuration path.",
21483
+ "Rules are installed only in local mode or only in server mode.",
21484
+ "Existing Cursor rules are overwritten.",
21485
+ "No idempotency check.",
21486
+ "No proof that the auto-install behavior actually runs."
21487
+ ],
21488
+ contractProfiles: [MCP_RULE_PROFILE]
21489
+ };
21490
+ case AUTH_PROFILE:
21491
+ return {
21492
+ expectedSurfaces: ["auth", "session", "tests_or_manual_proof"],
21493
+ expectedFileAreas: ["auth/**", "src/**/auth/**", "src/**/session/**", "middleware/**"],
21494
+ completionCriteria: [
21495
+ "Authentication/session flow handling is updated where intended.",
21496
+ "Authorization boundaries remain explicit and unchanged outside scope.",
21497
+ "Proof exists for the auth/session behavior change."
21498
+ ],
21499
+ proofNeeded: ["Run auth/session flow tests or a reproducible manual proof."],
21500
+ riskyOmissions: [
21501
+ "Auth logic changed without session or middleware checks.",
21502
+ "No proof of login/session behavior."
21503
+ ],
21504
+ contractProfiles: [AUTH_PROFILE]
21505
+ };
21506
+ case DB_PROFILE:
21507
+ return {
21508
+ expectedSurfaces: ["database", "schema_migration", "tests_or_manual_proof"],
21509
+ expectedFileAreas: ["prisma/**", "migrations/**", "src/**/db/**", "src/**/database/**"],
21510
+ completionCriteria: [
21511
+ "Schema/data-layer changes are applied in intended files.",
21512
+ "Migration or compatibility impact is accounted for.",
21513
+ "Proof exists for migration/query behavior."
21514
+ ],
21515
+ proofNeeded: ["Run migration/database verification command and capture output."],
21516
+ riskyOmissions: [
21517
+ "Schema changed without migration/compatibility proof.",
21518
+ "No evidence of migration/query validation."
21519
+ ],
21520
+ contractProfiles: [DB_PROFILE]
21521
+ };
21522
+ case UI_PROFILE:
21523
+ return {
21524
+ expectedSurfaces: ["ui", "copy_or_style", "tests_or_manual_proof"],
21525
+ expectedFileAreas: ["src/**/components/**", "src/**/pages/**", "src/**/*.css", "src/**/*.html"],
21526
+ completionCriteria: [
21527
+ "UI/copy/style surface for the contract is updated.",
21528
+ "Unrelated UI areas are not changed unintentionally.",
21529
+ "Proof exists for the visible behavior change."
21530
+ ],
21531
+ proofNeeded: ["Record manual verification steps (or visual/tests) for UI outcome."],
21532
+ riskyOmissions: [
21533
+ "UI files changed without a clear visible proof.",
21534
+ "Broad style changes without scope evidence."
21535
+ ],
21536
+ contractProfiles: [UI_PROFILE]
21537
+ };
21538
+ case API_PROFILE:
21539
+ return {
21540
+ expectedSurfaces: ["api", "endpoint_logic", "tests_or_manual_proof"],
21541
+ expectedFileAreas: ["src/**/routes/**", "src/**/api/**", "src/**/controllers/**"],
21542
+ completionCriteria: [
21543
+ "Target API/endpoint behavior is changed in the correct surface.",
21544
+ "Request/response behavior is evidenced.",
21545
+ "Proof exists for endpoint behavior."
21546
+ ],
21547
+ proofNeeded: ["Run endpoint tests or reproducible request/response proof."],
21548
+ riskyOmissions: [
21549
+ "Endpoint files changed without request/response evidence.",
21550
+ "No proof for API behavior change."
21551
+ ],
21552
+ contractProfiles: [API_PROFILE]
21553
+ };
21554
+ case TEST_PROFILE:
21555
+ return {
21556
+ expectedSurfaces: ["tests", "proof"],
21557
+ expectedFileAreas: ["**/*.test.ts", "**/*.spec.ts", "tests/**"],
21558
+ completionCriteria: [
21559
+ "Test/proof artifacts relevant to the contract are updated.",
21560
+ "Test execution evidence is recorded."
21561
+ ],
21562
+ proofNeeded: ["Run tests and record command + result output."],
21563
+ riskyOmissions: ["Tests changed but no execution proof recorded."],
21564
+ contractProfiles: [TEST_PROFILE]
21565
+ };
21566
+ case PAYMENTS_PROFILE:
21567
+ return {
21568
+ expectedSurfaces: ["payments", "webhooks", "tests_or_manual_proof"],
21569
+ expectedFileAreas: ["src/**/billing/**", "src/**/payments/**", "src/**/webhooks/**"],
21570
+ completionCriteria: [
21571
+ "Payments/webhook handling logic is updated in scope.",
21572
+ "Critical payment side effects are evidenced.",
21573
+ "Proof exists for payment/webhook behavior."
21574
+ ],
21575
+ proofNeeded: ["Run payment/webhook verification flow and capture output."],
21576
+ riskyOmissions: [
21577
+ "Webhook/payment logic changed without behavior proof.",
21578
+ "No idempotency/effect validation for financial flows."
21579
+ ],
21580
+ contractProfiles: [PAYMENTS_PROFILE]
21581
+ };
21582
+ default:
21583
+ return {
21584
+ expectedSurfaces: ["implementation_surface", "tests_or_manual_proof"],
21585
+ expectedFileAreas: ["**"],
21586
+ completionCriteria: [
21587
+ "Files changed align with the declared contract intent.",
21588
+ "Proof exists for the intended behavior change."
21589
+ ],
21590
+ proofNeeded: ["Run relevant tests or provide manual proof output."],
21591
+ riskyOmissions: ["Code changed without proof linked to the declared intent."],
21592
+ contractProfiles: [GENERIC_PROFILE]
21593
+ };
21594
+ }
21595
+ }
21596
+ function buildContractIntelligence(intent, createdBy) {
21597
+ const normalizedIntent = intent?.trim() ?? "";
21598
+ const profiles = detectProfiles(normalizedIntent);
21599
+ const combined = profiles.map((profile) => profileBlueprint(profile));
21600
+ const baseCompletionCriteria = unique(combined.flatMap((item) => item.completionCriteria));
21601
+ const parsed = parseIntent(normalizedIntent);
21602
+ const taskCriteria = buildTaskSpecificCriteria(parsed);
21603
+ const firstTask = taskCriteria[0];
21604
+ const lastTask = taskCriteria[1];
21605
+ const completionCriteria = unique([
21606
+ ...firstTask ? [firstTask] : [],
21607
+ ...baseCompletionCriteria.filter((c) => !c.toLowerCase().startsWith("proof")),
21608
+ ...lastTask ? [lastTask] : [],
21609
+ ...baseCompletionCriteria.filter((c) => c.toLowerCase().startsWith("proof"))
21610
+ ]);
21611
+ return {
21612
+ expectedSurfaces: unique(combined.flatMap((item) => item.expectedSurfaces)),
21613
+ expectedFileAreas: unique(combined.flatMap((item) => item.expectedFileAreas)),
21614
+ completionCriteria,
21615
+ proofNeeded: unique(combined.flatMap((item) => item.proofNeeded)),
21616
+ riskyOmissions: unique(combined.flatMap((item) => item.riskyOmissions)),
21617
+ contractProfiles: unique(combined.flatMap((item) => item.contractProfiles)),
21618
+ createdBy
21619
+ };
21620
+ }
21621
+ function localSessionToContractIntelligence(session) {
21622
+ if (session.completionCriteria?.length && session.contractProfiles?.length) {
21623
+ return {
21624
+ contractProfiles: session.contractProfiles,
21625
+ completionCriteria: session.completionCriteria,
21626
+ proofNeeded: session.proofNeeded ?? [],
21627
+ riskyOmissions: session.riskyOmissions ?? [],
21628
+ expectedSurfaces: session.expectedSurfaces ?? [],
21629
+ expectedFileAreas: session.expectedFileAreas ?? [],
21630
+ createdBy: session.createdBy ?? "agent_hello"
21631
+ };
21632
+ }
21633
+ return buildContractIntelligence(session.intent, session.createdBy ?? "agent_hello");
21634
+ }
21635
+ function sessionNeedsContractBackfill(session) {
21636
+ return !session.completionCriteria?.length || !session.contractProfiles?.length;
21637
+ }
21638
+ function ensureSessionContractIntelligence(session) {
21639
+ if (!sessionNeedsContractBackfill(session)) {
21640
+ return session;
21641
+ }
21642
+ const createdBy = session.createdBy ?? (session.agentId?.trim() === "local" ? "user_start" : "agent_hello");
21643
+ const contract = buildContractIntelligence(session.intent?.trim(), createdBy);
21644
+ const updated = {
21645
+ ...session,
21646
+ createdBy,
21647
+ contractProfiles: contract.contractProfiles,
21648
+ expectedSurfaces: contract.expectedSurfaces,
21649
+ expectedFileAreas: contract.expectedFileAreas,
21650
+ completionCriteria: contract.completionCriteria,
21651
+ proofNeeded: contract.proofNeeded,
21652
+ riskyOmissions: contract.riskyOmissions
21653
+ };
21654
+ writeSessionState(updated);
21655
+ return updated;
21656
+ }
21657
+ function buildProofRequiredCommands(contract) {
21658
+ if (contract.contractProfiles.includes(MCP_RULE_PROFILE)) {
21659
+ return [
21660
+ "agentbridge verify npm run setup-mcp -- --local",
21661
+ "agentbridge verify npm run setup-mcp -- --local (run again to confirm idempotency)"
21662
+ ];
21663
+ }
21664
+ if (contract.contractProfiles.includes(DB_PROFILE)) {
21665
+ return [
21666
+ "agentbridge verify npx prisma migrate status",
21667
+ "agentbridge verify npm test"
21668
+ ];
21669
+ }
21670
+ if (contract.contractProfiles.includes(AUTH_PROFILE) || contract.contractProfiles.includes(API_PROFILE) || contract.contractProfiles.includes(UI_PROFILE) || contract.contractProfiles.includes(TEST_PROFILE) || contract.contractProfiles.includes(PAYMENTS_PROFILE)) {
21671
+ return ["agentbridge verify npm test"];
21672
+ }
21673
+ return ["agentbridge verify npm test"];
21674
+ }
21675
+ function buildLocalHelloPayload(session) {
21676
+ const contract = localSessionToContractIntelligence(session);
21677
+ const scopeSource = session.createdBy ?? (session.agentId?.trim() === "local" ? "user_start" : "agent_hello");
21678
+ return {
21679
+ contract: {
21680
+ id: session.id,
21681
+ intent: session.intent?.trim() ?? null,
21682
+ created_by: scopeSource
21683
+ },
21684
+ done_checklist: contract.completionCriteria.slice(0, 4),
21685
+ proof_required: buildProofRequiredCommands(contract),
21686
+ warnings: contract.riskyOmissions.slice(0, 2)
21687
+ };
21688
+ }
21246
21689
 
21247
21690
  // src/git-status.ts
21248
21691
  var import_node_child_process = require("node:child_process");
@@ -21251,7 +21694,9 @@ function normalizePath3(path) {
21251
21694
  }
21252
21695
  function isAgentbridgeLocalPath(path) {
21253
21696
  const normalized = normalizePath3(path);
21254
- return normalized === ".agentbridge" || normalized.startsWith(".agentbridge/");
21697
+ const lower = normalized.toLowerCase();
21698
+ if (lower === ".agentbridge" || lower.endsWith("/.agentbridge")) return true;
21699
+ return lower.includes(".agentbridge/");
21255
21700
  }
21256
21701
  function isRenameOrCopy(xy) {
21257
21702
  return xy.includes("R") || xy.includes("C");
@@ -21340,11 +21785,20 @@ function openLocalSession(input) {
21340
21785
  const claimedPaths = [...new Set((input.claimedPaths ?? []).map((path) => path.trim()).filter(Boolean))];
21341
21786
  const baseline = captureLocalSessionBaseline();
21342
21787
  const now = baseline.startedAt;
21788
+ const createdBy = input.createdBy ?? (input.agentId?.trim() === "local" ? "user_start" : "agent_hello");
21789
+ const contract = buildContractIntelligence(input.intent?.trim(), createdBy);
21343
21790
  const state = {
21344
21791
  id: `local_${Date.now().toString(36)}`,
21345
21792
  agentId: input.agentId?.trim() || "local",
21346
21793
  laneDomain: input.laneDomain,
21347
21794
  intent: input.intent?.trim() || void 0,
21795
+ createdBy,
21796
+ contractProfiles: contract.contractProfiles,
21797
+ expectedSurfaces: contract.expectedSurfaces,
21798
+ expectedFileAreas: contract.expectedFileAreas,
21799
+ completionCriteria: contract.completionCriteria,
21800
+ proofNeeded: contract.proofNeeded,
21801
+ riskyOmissions: contract.riskyOmissions,
21348
21802
  mode: input.mode,
21349
21803
  changeRequestId: input.changeRequestId,
21350
21804
  claimedPaths,
@@ -22135,6 +22589,84 @@ function toolJson(data) {
22135
22589
  ]
22136
22590
  };
22137
22591
  }
22592
+ function isRecord2(value) {
22593
+ return typeof value === "object" && value !== null && !Array.isArray(value);
22594
+ }
22595
+ function needsRulesAutoInstall(hello) {
22596
+ return isRecord2(hello) && hello.rules_status === "not_installed";
22597
+ }
22598
+ function formatUnknownError(err) {
22599
+ if (err instanceof Error) return err.message;
22600
+ return String(err);
22601
+ }
22602
+ async function autoInstallRulesForProject(cfg, projectId) {
22603
+ const installPath = `/v1/dev/projects/${encodeURIComponent(projectId)}/rules`;
22604
+ const installRes = await bridgeJson(cfg, "GET", installPath);
22605
+ if (!installRes.ok) {
22606
+ return {
22607
+ status: "failed",
22608
+ step: "install_agentbridge_rules",
22609
+ error: formatHttpFailure(installPath, installRes.status, installRes.text, installRes.json)
22610
+ };
22611
+ }
22612
+ if (!isRecord2(installRes.json) || !Array.isArray(installRes.json.files)) {
22613
+ return {
22614
+ status: "failed",
22615
+ step: "install_agentbridge_rules",
22616
+ error: "Rules payload is missing a valid files[] list."
22617
+ };
22618
+ }
22619
+ const repoRoot = process.env.AGENTBRIDGE_REPO_ROOT?.trim() || process.cwd();
22620
+ const writtenFiles = [];
22621
+ try {
22622
+ for (const file of installRes.json.files) {
22623
+ if (!isRecord2(file) || typeof file.path !== "string" || typeof file.content !== "string") {
22624
+ throw new Error("Invalid rules file entry in server response.");
22625
+ }
22626
+ const absolutePath = (0, import_node_path2.resolve)(repoRoot, file.path);
22627
+ (0, import_node_fs3.mkdirSync)((0, import_node_path2.dirname)(absolutePath), { recursive: true });
22628
+ (0, import_node_fs3.writeFileSync)(absolutePath, `${file.content}
22629
+ `, "utf8");
22630
+ writtenFiles.push(file.path);
22631
+ }
22632
+ } catch (err) {
22633
+ return {
22634
+ status: "failed",
22635
+ step: "write_rules_files",
22636
+ error: formatUnknownError(err)
22637
+ };
22638
+ }
22639
+ const markPath = `/v1/dev/projects/${encodeURIComponent(projectId)}/rules/mark-installed`;
22640
+ const markRes = await bridgeJson(cfg, "POST", markPath, {});
22641
+ if (!markRes.ok) {
22642
+ return {
22643
+ status: "failed",
22644
+ step: "mark_rules_installed",
22645
+ error: formatHttpFailure(markPath, markRes.status, markRes.text, markRes.json)
22646
+ };
22647
+ }
22648
+ return {
22649
+ status: "installed",
22650
+ repo_root: repoRoot,
22651
+ written_files: writtenFiles
22652
+ };
22653
+ }
22654
+ function localHelloResult(action, session, extras) {
22655
+ const prepared = ensureSessionContractIntelligence(session);
22656
+ const scopeSource = prepared.agentId === "local" ? "user_start" : "agent_hello";
22657
+ const checklist = buildLocalHelloPayload(prepared);
22658
+ return toolJson({
22659
+ mode: "local",
22660
+ action,
22661
+ contract_id: prepared.id,
22662
+ intent: prepared.intent ?? null,
22663
+ scope_source: scopeSource,
22664
+ status: prepared.status,
22665
+ next_action: "Contract declared. Start implementation now; agentbridge watch will track this work contract.",
22666
+ ...checklist,
22667
+ ...extras ?? {}
22668
+ });
22669
+ }
22138
22670
  function formatHttpFailure(path, status, text, json) {
22139
22671
  if (json && typeof json === "object" && "error" in json) {
22140
22672
  return `AgentBridge ${path} returned ${status}: ${JSON.stringify(json.error)}`;
@@ -22227,18 +22759,41 @@ async function main() {
22227
22759
  async (args) => {
22228
22760
  if (cfg.localOnly) {
22229
22761
  const existing = readSessionState();
22762
+ const declaredIntent = args.intent?.trim() || args.note?.trim();
22230
22763
  if (isActiveLocalSession(existing)) {
22231
- return toolJson({
22232
- mode: "local",
22233
- action: "reuse",
22234
- contract: existing,
22235
- message: "Reusing active local work contract."
22764
+ if (existing.agentId === "local") {
22765
+ return localHelloResult("reuse", existing, {
22766
+ scope_locked: true
22767
+ });
22768
+ }
22769
+ const currentIntent = existing.intent?.trim();
22770
+ if (!declaredIntent || declaredIntent === currentIntent) {
22771
+ return localHelloResult("reuse", existing);
22772
+ }
22773
+ const canReplace = existing.changedFiles.length === 0 && existing.crossings.length === 0 && existing.approvals.length === 0;
22774
+ if (!canReplace) {
22775
+ return toolError(
22776
+ `A different AgentBridge contract is already active.
22777
+
22778
+ Current contract: ${currentIntent ?? "(unknown intent)"}
22779
+ Close the current contract in watch, then call agent_hello again with your new intent.`
22780
+ );
22781
+ }
22782
+ clearSessionState();
22783
+ const replaced = openLocalSession({
22784
+ agentId: "mcp",
22785
+ laneDomain: null,
22786
+ intent: declaredIntent,
22787
+ domains: [],
22788
+ mode: "local_supervision"
22789
+ });
22790
+ return localHelloResult("replaced", replaced, {
22791
+ replaced_contract_id: existing.id
22236
22792
  });
22237
22793
  }
22238
- const declaredIntent = args.intent?.trim() || args.note?.trim();
22239
22794
  if (!declaredIntent || isVagueIntent(declaredIntent)) {
22240
22795
  return toolError(
22241
- 'No active AgentBridge contract.\n\nDeclare what you are about to do before editing files:\n agent_hello({ intent: "Fix auth 401 on /rooms endpoint" })\n\nOr run in a terminal: agentbridge start "Fix auth 401 on /rooms endpoint"'
22796
+ 'No active AgentBridge contract.\n\nAgent must declare a concrete scope before editing files:\n agent_hello({ intent: "Fix auth 401 on /rooms endpoint" })\n\nOr run in a terminal: agentbridge start "Fix auth 401 on /rooms endpoint"'
22242
22797
  );
22243
22798
  }
22244
22799
  const session = openLocalSession({
@@ -22248,12 +22803,7 @@ async function main() {
22248
22803
  domains: [],
22249
22804
  mode: "local_supervision"
22250
22805
  });
22251
- return toolJson({
22252
- mode: "local",
22253
- action: "created",
22254
- contract: session,
22255
- message: "Local work contract created."
22256
- });
22806
+ return localHelloResult("created", session);
22257
22807
  }
22258
22808
  const resolvedProjectId = args.project_id ?? cfg.projectId;
22259
22809
  if (!resolvedProjectId) {
@@ -22271,7 +22821,20 @@ async function main() {
22271
22821
  const res = await bridgeJson(cfg, "POST", path, body);
22272
22822
  if (!res.ok)
22273
22823
  return toolError(formatHttpFailure(path, res.status, res.text, res.json));
22274
- return toolJson(res.json);
22824
+ if (!needsRulesAutoInstall(res.json)) {
22825
+ return toolJson(res.json);
22826
+ }
22827
+ const autoRulesInstall = await autoInstallRulesForProject(cfg, resolvedProjectId);
22828
+ if (isRecord2(res.json)) {
22829
+ return toolJson({
22830
+ ...res.json,
22831
+ auto_rules_install: autoRulesInstall
22832
+ });
22833
+ }
22834
+ return toolJson({
22835
+ hello: res.json,
22836
+ auto_rules_install: autoRulesInstall
22837
+ });
22275
22838
  }
22276
22839
  );
22277
22840
  server.registerTool(