@agentbridge1/cli 0.0.8 → 0.0.9

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");
@@ -21175,6 +21316,15 @@ function isLocalSessionState(value) {
21175
21316
  if (value.updatedAt !== void 0 && typeof value.updatedAt !== "string") return false;
21176
21317
  if (value.closedAt !== void 0 && typeof value.closedAt !== "string") return false;
21177
21318
  if (value.claimedPaths !== void 0 && !isStringArray(value.claimedPaths)) return false;
21319
+ if (value.createdBy !== void 0 && value.createdBy !== "user_start" && value.createdBy !== "agent_hello") {
21320
+ return false;
21321
+ }
21322
+ if (value.contractProfiles !== void 0 && !isStringArray(value.contractProfiles)) return false;
21323
+ if (value.expectedSurfaces !== void 0 && !isStringArray(value.expectedSurfaces)) return false;
21324
+ if (value.expectedFileAreas !== void 0 && !isStringArray(value.expectedFileAreas)) return false;
21325
+ if (value.completionCriteria !== void 0 && !isStringArray(value.completionCriteria)) return false;
21326
+ if (value.proofNeeded !== void 0 && !isStringArray(value.proofNeeded)) return false;
21327
+ if (value.riskyOmissions !== void 0 && !isStringArray(value.riskyOmissions)) return false;
21178
21328
  if (value.lastLocalVerificationRun !== void 0 && !isLocalVerificationRun(value.lastLocalVerificationRun)) {
21179
21329
  return false;
21180
21330
  }
@@ -21243,6 +21393,296 @@ function writeSessionState(state) {
21243
21393
  }
21244
21394
  (0, import_node_fs2.renameSync)(tempPath, path);
21245
21395
  }
21396
+ function clearSessionState() {
21397
+ writeSessionState({
21398
+ id: "none",
21399
+ agentId: "none",
21400
+ laneDomain: null,
21401
+ status: "closed",
21402
+ closeReason: "completed",
21403
+ changedFiles: [],
21404
+ crossings: [],
21405
+ approvals: [],
21406
+ domains: [],
21407
+ createdAt: (/* @__PURE__ */ new Date(0)).toISOString()
21408
+ });
21409
+ }
21410
+
21411
+ // src/contract-intelligence.ts
21412
+ var MCP_RULE_PROFILE = "mcp_rules_config_install";
21413
+ var AUTH_PROFILE = "auth_login_session";
21414
+ var DB_PROFILE = "database_schema_migration";
21415
+ var UI_PROFILE = "ui_style_copy";
21416
+ var API_PROFILE = "api_endpoint";
21417
+ var TEST_PROFILE = "tests";
21418
+ var PAYMENTS_PROFILE = "payments_webhooks";
21419
+ var GENERIC_PROFILE = "generic";
21420
+ function unique(values) {
21421
+ return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
21422
+ }
21423
+ function matchIntent(intent, keywords) {
21424
+ const lower = intent.toLowerCase();
21425
+ return keywords.some((keyword) => lower.includes(keyword));
21426
+ }
21427
+ function detectProfiles(intent) {
21428
+ const profiles = [];
21429
+ if (matchIntent(intent, ["mcp"]) && matchIntent(intent, ["rule", "rules", "install", "setup", "config", "configuration"])) {
21430
+ profiles.push(MCP_RULE_PROFILE);
21431
+ }
21432
+ if (matchIntent(intent, ["auth", "login", "session", "token", "oauth"])) {
21433
+ profiles.push(AUTH_PROFILE);
21434
+ }
21435
+ if (matchIntent(intent, ["database", "db", "schema", "migration", "prisma", "sql"])) {
21436
+ profiles.push(DB_PROFILE);
21437
+ }
21438
+ if (matchIntent(intent, ["ui", "style", "css", "copy", "layout", "component"])) {
21439
+ profiles.push(UI_PROFILE);
21440
+ }
21441
+ if (matchIntent(intent, ["api", "endpoint", "route", "handler", "controller"])) {
21442
+ profiles.push(API_PROFILE);
21443
+ }
21444
+ if (matchIntent(intent, ["test", "tests", "vitest", "jest", "proof"])) {
21445
+ profiles.push(TEST_PROFILE);
21446
+ }
21447
+ if (matchIntent(intent, ["payment", "payments", "billing", "stripe", "webhook"])) {
21448
+ profiles.push(PAYMENTS_PROFILE);
21449
+ }
21450
+ return profiles.length > 0 ? profiles : [GENERIC_PROFILE];
21451
+ }
21452
+ function profileBlueprint(profile) {
21453
+ switch (profile) {
21454
+ case MCP_RULE_PROFILE:
21455
+ return {
21456
+ expectedSurfaces: ["mcp", "rules_installation", "configuration_flow", "tests_or_manual_proof"],
21457
+ expectedFileAreas: [
21458
+ "mcp/**",
21459
+ "mcp/agentbridge-mcp.ts",
21460
+ "AGENTBRIDGE.md",
21461
+ ".cursor/rules/**",
21462
+ "cli/src/mcp-config.ts",
21463
+ "cli/src/commands/setup-mcp.ts"
21464
+ ],
21465
+ completionCriteria: [
21466
+ "MCP configuration path triggers AgentBridge rule installation.",
21467
+ "Rules are created or updated in the expected Cursor rules location.",
21468
+ "Existing rules/config are preserved or safely updated.",
21469
+ "Re-running MCP configuration is idempotent and does not duplicate rules.",
21470
+ "Proof exists that MCP configuration causes rules to be installed."
21471
+ ],
21472
+ proofNeeded: [
21473
+ "Run MCP setup/configuration flow in a clean repo.",
21474
+ "Confirm the AgentBridge Cursor rules file is created.",
21475
+ "Run setup/configuration again to confirm idempotency.",
21476
+ "Record test/manual proof output."
21477
+ ],
21478
+ riskyOmissions: [
21479
+ "MCP server file changed but no install trigger wired to the configuration path.",
21480
+ "Rules are installed only in local mode or only in server mode.",
21481
+ "Existing Cursor rules are overwritten.",
21482
+ "No idempotency check.",
21483
+ "No proof that the auto-install behavior actually runs."
21484
+ ],
21485
+ contractProfiles: [MCP_RULE_PROFILE]
21486
+ };
21487
+ case AUTH_PROFILE:
21488
+ return {
21489
+ expectedSurfaces: ["auth", "session", "tests_or_manual_proof"],
21490
+ expectedFileAreas: ["auth/**", "src/**/auth/**", "src/**/session/**", "middleware/**"],
21491
+ completionCriteria: [
21492
+ "Authentication/session flow handling is updated where intended.",
21493
+ "Authorization boundaries remain explicit and unchanged outside scope.",
21494
+ "Proof exists for the auth/session behavior change."
21495
+ ],
21496
+ proofNeeded: ["Run auth/session flow tests or a reproducible manual proof."],
21497
+ riskyOmissions: [
21498
+ "Auth logic changed without session or middleware checks.",
21499
+ "No proof of login/session behavior."
21500
+ ],
21501
+ contractProfiles: [AUTH_PROFILE]
21502
+ };
21503
+ case DB_PROFILE:
21504
+ return {
21505
+ expectedSurfaces: ["database", "schema_migration", "tests_or_manual_proof"],
21506
+ expectedFileAreas: ["prisma/**", "migrations/**", "src/**/db/**", "src/**/database/**"],
21507
+ completionCriteria: [
21508
+ "Schema/data-layer changes are applied in intended files.",
21509
+ "Migration or compatibility impact is accounted for.",
21510
+ "Proof exists for migration/query behavior."
21511
+ ],
21512
+ proofNeeded: ["Run migration/database verification command and capture output."],
21513
+ riskyOmissions: [
21514
+ "Schema changed without migration/compatibility proof.",
21515
+ "No evidence of migration/query validation."
21516
+ ],
21517
+ contractProfiles: [DB_PROFILE]
21518
+ };
21519
+ case UI_PROFILE:
21520
+ return {
21521
+ expectedSurfaces: ["ui", "copy_or_style", "tests_or_manual_proof"],
21522
+ expectedFileAreas: ["src/**/components/**", "src/**/pages/**", "src/**/*.css", "src/**/*.html"],
21523
+ completionCriteria: [
21524
+ "UI/copy/style surface for the contract is updated.",
21525
+ "Unrelated UI areas are not changed unintentionally.",
21526
+ "Proof exists for the visible behavior change."
21527
+ ],
21528
+ proofNeeded: ["Record manual verification steps (or visual/tests) for UI outcome."],
21529
+ riskyOmissions: [
21530
+ "UI files changed without a clear visible proof.",
21531
+ "Broad style changes without scope evidence."
21532
+ ],
21533
+ contractProfiles: [UI_PROFILE]
21534
+ };
21535
+ case API_PROFILE:
21536
+ return {
21537
+ expectedSurfaces: ["api", "endpoint_logic", "tests_or_manual_proof"],
21538
+ expectedFileAreas: ["src/**/routes/**", "src/**/api/**", "src/**/controllers/**"],
21539
+ completionCriteria: [
21540
+ "Target API/endpoint behavior is changed in the correct surface.",
21541
+ "Request/response behavior is evidenced.",
21542
+ "Proof exists for endpoint behavior."
21543
+ ],
21544
+ proofNeeded: ["Run endpoint tests or reproducible request/response proof."],
21545
+ riskyOmissions: [
21546
+ "Endpoint files changed without request/response evidence.",
21547
+ "No proof for API behavior change."
21548
+ ],
21549
+ contractProfiles: [API_PROFILE]
21550
+ };
21551
+ case TEST_PROFILE:
21552
+ return {
21553
+ expectedSurfaces: ["tests", "proof"],
21554
+ expectedFileAreas: ["**/*.test.ts", "**/*.spec.ts", "tests/**"],
21555
+ completionCriteria: [
21556
+ "Test/proof artifacts relevant to the contract are updated.",
21557
+ "Test execution evidence is recorded."
21558
+ ],
21559
+ proofNeeded: ["Run tests and record command + result output."],
21560
+ riskyOmissions: ["Tests changed but no execution proof recorded."],
21561
+ contractProfiles: [TEST_PROFILE]
21562
+ };
21563
+ case PAYMENTS_PROFILE:
21564
+ return {
21565
+ expectedSurfaces: ["payments", "webhooks", "tests_or_manual_proof"],
21566
+ expectedFileAreas: ["src/**/billing/**", "src/**/payments/**", "src/**/webhooks/**"],
21567
+ completionCriteria: [
21568
+ "Payments/webhook handling logic is updated in scope.",
21569
+ "Critical payment side effects are evidenced.",
21570
+ "Proof exists for payment/webhook behavior."
21571
+ ],
21572
+ proofNeeded: ["Run payment/webhook verification flow and capture output."],
21573
+ riskyOmissions: [
21574
+ "Webhook/payment logic changed without behavior proof.",
21575
+ "No idempotency/effect validation for financial flows."
21576
+ ],
21577
+ contractProfiles: [PAYMENTS_PROFILE]
21578
+ };
21579
+ default:
21580
+ return {
21581
+ expectedSurfaces: ["implementation_surface", "tests_or_manual_proof"],
21582
+ expectedFileAreas: ["**"],
21583
+ completionCriteria: [
21584
+ "Files changed align with the declared contract intent.",
21585
+ "Proof exists for the intended behavior change."
21586
+ ],
21587
+ proofNeeded: ["Run relevant tests or provide manual proof output."],
21588
+ riskyOmissions: ["Code changed without proof linked to the declared intent."],
21589
+ contractProfiles: [GENERIC_PROFILE]
21590
+ };
21591
+ }
21592
+ }
21593
+ function buildContractIntelligence(intent, createdBy) {
21594
+ const normalizedIntent = intent?.trim() ?? "";
21595
+ const profiles = detectProfiles(normalizedIntent);
21596
+ const combined = profiles.map((profile) => profileBlueprint(profile));
21597
+ const baseCompletionCriteria = unique(combined.flatMap((item) => item.completionCriteria));
21598
+ const parsed = parseIntent(normalizedIntent);
21599
+ const taskCriteria = buildTaskSpecificCriteria(parsed);
21600
+ const firstTask = taskCriteria[0];
21601
+ const lastTask = taskCriteria[1];
21602
+ const completionCriteria = unique([
21603
+ ...firstTask ? [firstTask] : [],
21604
+ ...baseCompletionCriteria.filter((c) => !c.toLowerCase().startsWith("proof")),
21605
+ ...lastTask ? [lastTask] : [],
21606
+ ...baseCompletionCriteria.filter((c) => c.toLowerCase().startsWith("proof"))
21607
+ ]);
21608
+ return {
21609
+ expectedSurfaces: unique(combined.flatMap((item) => item.expectedSurfaces)),
21610
+ expectedFileAreas: unique(combined.flatMap((item) => item.expectedFileAreas)),
21611
+ completionCriteria,
21612
+ proofNeeded: unique(combined.flatMap((item) => item.proofNeeded)),
21613
+ riskyOmissions: unique(combined.flatMap((item) => item.riskyOmissions)),
21614
+ contractProfiles: unique(combined.flatMap((item) => item.contractProfiles)),
21615
+ createdBy
21616
+ };
21617
+ }
21618
+ function localSessionToContractIntelligence(session) {
21619
+ if (session.completionCriteria?.length && session.contractProfiles?.length) {
21620
+ return {
21621
+ contractProfiles: session.contractProfiles,
21622
+ completionCriteria: session.completionCriteria,
21623
+ proofNeeded: session.proofNeeded ?? [],
21624
+ riskyOmissions: session.riskyOmissions ?? [],
21625
+ expectedSurfaces: session.expectedSurfaces ?? [],
21626
+ expectedFileAreas: session.expectedFileAreas ?? [],
21627
+ createdBy: session.createdBy ?? "agent_hello"
21628
+ };
21629
+ }
21630
+ return buildContractIntelligence(session.intent, session.createdBy ?? "agent_hello");
21631
+ }
21632
+ function sessionNeedsContractBackfill(session) {
21633
+ return !session.completionCriteria?.length || !session.contractProfiles?.length;
21634
+ }
21635
+ function ensureSessionContractIntelligence(session) {
21636
+ if (!sessionNeedsContractBackfill(session)) {
21637
+ return session;
21638
+ }
21639
+ const createdBy = session.createdBy ?? (session.agentId?.trim() === "local" ? "user_start" : "agent_hello");
21640
+ const contract = buildContractIntelligence(session.intent?.trim(), createdBy);
21641
+ const updated = {
21642
+ ...session,
21643
+ createdBy,
21644
+ contractProfiles: contract.contractProfiles,
21645
+ expectedSurfaces: contract.expectedSurfaces,
21646
+ expectedFileAreas: contract.expectedFileAreas,
21647
+ completionCriteria: contract.completionCriteria,
21648
+ proofNeeded: contract.proofNeeded,
21649
+ riskyOmissions: contract.riskyOmissions
21650
+ };
21651
+ writeSessionState(updated);
21652
+ return updated;
21653
+ }
21654
+ function buildProofRequiredCommands(contract) {
21655
+ if (contract.contractProfiles.includes(MCP_RULE_PROFILE)) {
21656
+ return [
21657
+ "agentbridge verify npm run setup-mcp -- --local",
21658
+ "agentbridge verify npm run setup-mcp -- --local (run again to confirm idempotency)"
21659
+ ];
21660
+ }
21661
+ if (contract.contractProfiles.includes(DB_PROFILE)) {
21662
+ return [
21663
+ "agentbridge verify npx prisma migrate status",
21664
+ "agentbridge verify npm test"
21665
+ ];
21666
+ }
21667
+ 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)) {
21668
+ return ["agentbridge verify npm test"];
21669
+ }
21670
+ return ["agentbridge verify npm test"];
21671
+ }
21672
+ function buildLocalHelloPayload(session) {
21673
+ const contract = localSessionToContractIntelligence(session);
21674
+ const scopeSource = session.createdBy ?? (session.agentId?.trim() === "local" ? "user_start" : "agent_hello");
21675
+ return {
21676
+ contract: {
21677
+ id: session.id,
21678
+ intent: session.intent?.trim() ?? null,
21679
+ created_by: scopeSource
21680
+ },
21681
+ done_checklist: contract.completionCriteria.slice(0, 4),
21682
+ proof_required: buildProofRequiredCommands(contract),
21683
+ warnings: contract.riskyOmissions.slice(0, 2)
21684
+ };
21685
+ }
21246
21686
 
21247
21687
  // src/git-status.ts
21248
21688
  var import_node_child_process = require("node:child_process");
@@ -21340,11 +21780,20 @@ function openLocalSession(input) {
21340
21780
  const claimedPaths = [...new Set((input.claimedPaths ?? []).map((path) => path.trim()).filter(Boolean))];
21341
21781
  const baseline = captureLocalSessionBaseline();
21342
21782
  const now = baseline.startedAt;
21783
+ const createdBy = input.createdBy ?? (input.agentId?.trim() === "local" ? "user_start" : "agent_hello");
21784
+ const contract = buildContractIntelligence(input.intent?.trim(), createdBy);
21343
21785
  const state = {
21344
21786
  id: `local_${Date.now().toString(36)}`,
21345
21787
  agentId: input.agentId?.trim() || "local",
21346
21788
  laneDomain: input.laneDomain,
21347
21789
  intent: input.intent?.trim() || void 0,
21790
+ createdBy,
21791
+ contractProfiles: contract.contractProfiles,
21792
+ expectedSurfaces: contract.expectedSurfaces,
21793
+ expectedFileAreas: contract.expectedFileAreas,
21794
+ completionCriteria: contract.completionCriteria,
21795
+ proofNeeded: contract.proofNeeded,
21796
+ riskyOmissions: contract.riskyOmissions,
21348
21797
  mode: input.mode,
21349
21798
  changeRequestId: input.changeRequestId,
21350
21799
  claimedPaths,
@@ -22135,6 +22584,84 @@ function toolJson(data) {
22135
22584
  ]
22136
22585
  };
22137
22586
  }
22587
+ function isRecord2(value) {
22588
+ return typeof value === "object" && value !== null && !Array.isArray(value);
22589
+ }
22590
+ function needsRulesAutoInstall(hello) {
22591
+ return isRecord2(hello) && hello.rules_status === "not_installed";
22592
+ }
22593
+ function formatUnknownError(err) {
22594
+ if (err instanceof Error) return err.message;
22595
+ return String(err);
22596
+ }
22597
+ async function autoInstallRulesForProject(cfg, projectId) {
22598
+ const installPath = `/v1/dev/projects/${encodeURIComponent(projectId)}/rules`;
22599
+ const installRes = await bridgeJson(cfg, "GET", installPath);
22600
+ if (!installRes.ok) {
22601
+ return {
22602
+ status: "failed",
22603
+ step: "install_agentbridge_rules",
22604
+ error: formatHttpFailure(installPath, installRes.status, installRes.text, installRes.json)
22605
+ };
22606
+ }
22607
+ if (!isRecord2(installRes.json) || !Array.isArray(installRes.json.files)) {
22608
+ return {
22609
+ status: "failed",
22610
+ step: "install_agentbridge_rules",
22611
+ error: "Rules payload is missing a valid files[] list."
22612
+ };
22613
+ }
22614
+ const repoRoot = process.env.AGENTBRIDGE_REPO_ROOT?.trim() || process.cwd();
22615
+ const writtenFiles = [];
22616
+ try {
22617
+ for (const file of installRes.json.files) {
22618
+ if (!isRecord2(file) || typeof file.path !== "string" || typeof file.content !== "string") {
22619
+ throw new Error("Invalid rules file entry in server response.");
22620
+ }
22621
+ const absolutePath = (0, import_node_path2.resolve)(repoRoot, file.path);
22622
+ (0, import_node_fs3.mkdirSync)((0, import_node_path2.dirname)(absolutePath), { recursive: true });
22623
+ (0, import_node_fs3.writeFileSync)(absolutePath, `${file.content}
22624
+ `, "utf8");
22625
+ writtenFiles.push(file.path);
22626
+ }
22627
+ } catch (err) {
22628
+ return {
22629
+ status: "failed",
22630
+ step: "write_rules_files",
22631
+ error: formatUnknownError(err)
22632
+ };
22633
+ }
22634
+ const markPath = `/v1/dev/projects/${encodeURIComponent(projectId)}/rules/mark-installed`;
22635
+ const markRes = await bridgeJson(cfg, "POST", markPath, {});
22636
+ if (!markRes.ok) {
22637
+ return {
22638
+ status: "failed",
22639
+ step: "mark_rules_installed",
22640
+ error: formatHttpFailure(markPath, markRes.status, markRes.text, markRes.json)
22641
+ };
22642
+ }
22643
+ return {
22644
+ status: "installed",
22645
+ repo_root: repoRoot,
22646
+ written_files: writtenFiles
22647
+ };
22648
+ }
22649
+ function localHelloResult(action, session, extras) {
22650
+ const prepared = ensureSessionContractIntelligence(session);
22651
+ const scopeSource = prepared.agentId === "local" ? "user_start" : "agent_hello";
22652
+ const checklist = buildLocalHelloPayload(prepared);
22653
+ return toolJson({
22654
+ mode: "local",
22655
+ action,
22656
+ contract_id: prepared.id,
22657
+ intent: prepared.intent ?? null,
22658
+ scope_source: scopeSource,
22659
+ status: prepared.status,
22660
+ next_action: "Contract declared. Start implementation now; agentbridge watch will track this work contract.",
22661
+ ...checklist,
22662
+ ...extras ?? {}
22663
+ });
22664
+ }
22138
22665
  function formatHttpFailure(path, status, text, json) {
22139
22666
  if (json && typeof json === "object" && "error" in json) {
22140
22667
  return `AgentBridge ${path} returned ${status}: ${JSON.stringify(json.error)}`;
@@ -22227,18 +22754,41 @@ async function main() {
22227
22754
  async (args) => {
22228
22755
  if (cfg.localOnly) {
22229
22756
  const existing = readSessionState();
22757
+ const declaredIntent = args.intent?.trim() || args.note?.trim();
22230
22758
  if (isActiveLocalSession(existing)) {
22231
- return toolJson({
22232
- mode: "local",
22233
- action: "reuse",
22234
- contract: existing,
22235
- message: "Reusing active local work contract."
22759
+ if (existing.agentId === "local") {
22760
+ return localHelloResult("reuse", existing, {
22761
+ scope_locked: true
22762
+ });
22763
+ }
22764
+ const currentIntent = existing.intent?.trim();
22765
+ if (!declaredIntent || declaredIntent === currentIntent) {
22766
+ return localHelloResult("reuse", existing);
22767
+ }
22768
+ const canReplace = existing.changedFiles.length === 0 && existing.crossings.length === 0 && existing.approvals.length === 0;
22769
+ if (!canReplace) {
22770
+ return toolError(
22771
+ `A different AgentBridge contract is already active.
22772
+
22773
+ Current contract: ${currentIntent ?? "(unknown intent)"}
22774
+ Close the current contract in watch, then call agent_hello again with your new intent.`
22775
+ );
22776
+ }
22777
+ clearSessionState();
22778
+ const replaced = openLocalSession({
22779
+ agentId: "mcp",
22780
+ laneDomain: null,
22781
+ intent: declaredIntent,
22782
+ domains: [],
22783
+ mode: "local_supervision"
22784
+ });
22785
+ return localHelloResult("replaced", replaced, {
22786
+ replaced_contract_id: existing.id
22236
22787
  });
22237
22788
  }
22238
- const declaredIntent = args.intent?.trim() || args.note?.trim();
22239
22789
  if (!declaredIntent || isVagueIntent(declaredIntent)) {
22240
22790
  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"'
22791
+ '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
22792
  );
22243
22793
  }
22244
22794
  const session = openLocalSession({
@@ -22248,12 +22798,7 @@ async function main() {
22248
22798
  domains: [],
22249
22799
  mode: "local_supervision"
22250
22800
  });
22251
- return toolJson({
22252
- mode: "local",
22253
- action: "created",
22254
- contract: session,
22255
- message: "Local work contract created."
22256
- });
22801
+ return localHelloResult("created", session);
22257
22802
  }
22258
22803
  const resolvedProjectId = args.project_id ?? cfg.projectId;
22259
22804
  if (!resolvedProjectId) {
@@ -22271,7 +22816,20 @@ async function main() {
22271
22816
  const res = await bridgeJson(cfg, "POST", path, body);
22272
22817
  if (!res.ok)
22273
22818
  return toolError(formatHttpFailure(path, res.status, res.text, res.json));
22274
- return toolJson(res.json);
22819
+ if (!needsRulesAutoInstall(res.json)) {
22820
+ return toolJson(res.json);
22821
+ }
22822
+ const autoRulesInstall = await autoInstallRulesForProject(cfg, resolvedProjectId);
22823
+ if (isRecord2(res.json)) {
22824
+ return toolJson({
22825
+ ...res.json,
22826
+ auto_rules_install: autoRulesInstall
22827
+ });
22828
+ }
22829
+ return toolJson({
22830
+ hello: res.json,
22831
+ auto_rules_install: autoRulesInstall
22832
+ });
22275
22833
  }
22276
22834
  );
22277
22835
  server.registerTool(