@agentbridge1/cli 0.0.10 → 0.0.11

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,6 +1,6 @@
1
1
  {
2
- "builtAt": "2026-06-20T13:18:31.649Z",
3
- "gitHead": "40b250d",
4
- "sourceLatestMtime": "2026-06-20T13:01:45.098Z",
2
+ "builtAt": "2026-06-21T02:05:47.707Z",
3
+ "gitHead": "09300d7",
4
+ "sourceLatestMtime": "2026-06-21T01:45:03.790Z",
5
5
  "sourceLatestFile": "src/commands/watch.ts"
6
6
  }
@@ -648,9 +648,12 @@ async function buildAutoVerifyIssueForFiles(files, options) {
648
648
  ? `\nNote: ${coherence.suspiciousFiles.length} file(s) may not match your declared intent ("${options.intent}"). Check: ${coherence.suspiciousFiles.slice(0, 3).join(", ")}`
649
649
  : "";
650
650
  if (testResult.passed) {
651
+ const fallbackNote = testResult.fallbackUsed
652
+ ? "\nScoped test selection could not run cleanly; AgentBridge fell back to the full verify command."
653
+ : "";
651
654
  return {
652
655
  errorCode: "AUTO_VERIFIED",
653
- whatHappened: `AgentBridge ran tests and they passed (${testResult.command}).` + driftWarning,
656
+ whatHappened: `AgentBridge ran tests and they passed (${testResult.command}).` + fallbackNote + driftWarning,
654
657
  whyItMatters: coherence.verdict === "drift"
655
658
  ? "Tests passed, but some changed files look unrelated to your stated intent — review the scope."
656
659
  : "Automated verification confirms the changes are safe.",
@@ -664,18 +667,94 @@ async function buildAutoVerifyIssueForFiles(files, options) {
664
667
  coherence,
665
668
  };
666
669
  }
670
+ const failureType = testResult.failureType ?? "test_failures";
667
671
  const failedNames = (0, test_runner_1.extractFailedTestNames)(testResult.stdout + "\n" + testResult.stderr);
668
672
  const failureSummary = failedNames.length > 0
669
673
  ? `Failing: ${failedNames.slice(0, 3).join(", ")}${failedNames.length > 3 ? ` (+${failedNames.length - 3} more)` : ""}`
670
- : "Check test output for details.";
674
+ : "Check verification output for details.";
675
+ const firstErrorLine = (testResult.stderr.split("\n").find((line) => line.trim().length > 0) ??
676
+ testResult.stdout.split("\n").find((line) => line.trim().length > 0) ??
677
+ "No diagnostic output captured.").trim();
678
+ const fallbackHint = testResult.fallbackUsed
679
+ ? "AgentBridge retried with the broader verify command after scoped verification failed."
680
+ : "";
681
+ if (failureType === "command_invalid") {
682
+ return {
683
+ errorCode: "TEST_COMMAND_INVALID",
684
+ whatHappened: `AgentBridge could not run verification command due to an invalid flag/argument (${testResult.command}).` +
685
+ driftWarning,
686
+ whyItMatters: "This is a verification command configuration issue, so test pass/fail status is unknown.",
687
+ files: uniqueFiles,
688
+ suggestedPrompt: [
689
+ "AgentBridge could not execute verification because the command arguments are invalid.",
690
+ `Diagnostic: ${firstErrorLine}`,
691
+ fallbackHint,
692
+ options.intent ? `Your declared intent: "${options.intent}"` : "",
693
+ "Fix the verification command/config and rerun AgentBridge watch.",
694
+ `Files changed: ${summarizeWatchPromptFiles(uniqueFiles)}`,
695
+ ]
696
+ .filter(Boolean)
697
+ .join("\n"),
698
+ nextAction: "Fix verification command flags/options, then rerun: agentbridge watch",
699
+ testResult,
700
+ intent: options.intent,
701
+ coherence,
702
+ };
703
+ }
704
+ if (failureType === "tooling_missing") {
705
+ return {
706
+ errorCode: "TEST_TOOLING_MISSING",
707
+ whatHappened: `AgentBridge could not run verification because required test tooling is missing (${testResult.command}).` +
708
+ driftWarning,
709
+ whyItMatters: "Without test tooling, AgentBridge cannot produce reliable verification evidence for this change.",
710
+ files: uniqueFiles,
711
+ suggestedPrompt: [
712
+ "AgentBridge could not execute verification because test tooling is missing.",
713
+ `Diagnostic: ${firstErrorLine}`,
714
+ fallbackHint,
715
+ options.intent ? `Your declared intent: "${options.intent}"` : "",
716
+ "Install/restore required tooling and rerun AgentBridge watch.",
717
+ `Files changed: ${summarizeWatchPromptFiles(uniqueFiles)}`,
718
+ ]
719
+ .filter(Boolean)
720
+ .join("\n"),
721
+ nextAction: "Install missing test tooling/dependencies, then rerun: agentbridge watch",
722
+ testResult,
723
+ intent: options.intent,
724
+ coherence,
725
+ };
726
+ }
727
+ if (failureType === "timeout") {
728
+ return {
729
+ errorCode: "TEST_TIMEOUT",
730
+ whatHappened: `AgentBridge verification timed out (${testResult.command}).` + driftWarning,
731
+ whyItMatters: "Timed-out verification provides no reliable proof for these changes.",
732
+ files: uniqueFiles,
733
+ suggestedPrompt: [
734
+ "AgentBridge verification timed out before tests completed.",
735
+ `Diagnostic: ${firstErrorLine}`,
736
+ fallbackHint,
737
+ options.intent ? `Your declared intent: "${options.intent}"` : "",
738
+ "Narrow the test scope or increase verify timeout, then rerun AgentBridge watch.",
739
+ `Files changed: ${summarizeWatchPromptFiles(uniqueFiles)}`,
740
+ ]
741
+ .filter(Boolean)
742
+ .join("\n"),
743
+ nextAction: "Reduce scope or increase verify timeout, then rerun: agentbridge watch",
744
+ testResult,
745
+ intent: options.intent,
746
+ coherence,
747
+ };
748
+ }
671
749
  return {
672
750
  errorCode: "TEST_FAILED",
673
- whatHappened: `AgentBridge ran \`${testResult.command}\` and ${testResult.timedOut ? "it timed out" : "tests failed"}.` +
751
+ whatHappened: `AgentBridge ran \`${testResult.command}\` and tests failed.` +
674
752
  driftWarning,
675
753
  whyItMatters: "These test failures must be fixed before the changes can be trusted.",
676
754
  files: uniqueFiles,
677
755
  suggestedPrompt: [
678
756
  "AgentBridge ran tests and found failures.",
757
+ fallbackHint,
679
758
  failureSummary,
680
759
  options.intent ? `Your declared intent: "${options.intent}"` : "",
681
760
  "Fix the failing tests, then rerun AgentBridge watch.",
@@ -875,19 +954,44 @@ function renderWatchBlockingIssue(issue) {
875
954
  ].join("\n");
876
955
  }
877
956
  // ── Test run failed ───────────────────────────────────────────────────────
878
- if (issue.errorCode === "TEST_FAILED" && issue.testResult) {
957
+ if ((issue.errorCode === "TEST_FAILED" ||
958
+ issue.errorCode === "TEST_TIMEOUT" ||
959
+ issue.errorCode === "TEST_COMMAND_INVALID" ||
960
+ issue.errorCode === "TEST_TOOLING_MISSING") &&
961
+ issue.testResult) {
879
962
  const dur = (issue.testResult.durationMs / 1000).toFixed(1);
880
963
  const failedNames = (0, test_runner_1.extractFailedTestNames)(issue.testResult.stdout + "\n" + issue.testResult.stderr);
964
+ const firstDiagnosticLine = (issue.testResult.stderr.split("\n").find((line) => line.trim().length > 0) ??
965
+ issue.testResult.stdout.split("\n").find((line) => line.trim().length > 0) ??
966
+ "No diagnostic output captured.").trim();
881
967
  const failureLines = failedNames.length > 0
882
968
  ? failedNames.slice(0, 5).map((n) => ` ✗ ${n}`).join("\n")
883
- : " (check test output above for details)";
969
+ : issue.errorCode === "TEST_COMMAND_INVALID" ||
970
+ issue.errorCode === "TEST_TOOLING_MISSING" ||
971
+ issue.errorCode === "TEST_TIMEOUT"
972
+ ? ` ${firstDiagnosticLine}`
973
+ : " (check test output above for details)";
884
974
  const moreFailures = failedNames.length > 5 ? `\n (+${failedNames.length - 5} more)` : "";
885
975
  const hasDrift = issue.coherence?.verdict === "drift";
886
976
  const fileList = issue.files.slice(0, 8).join("\n ");
887
977
  const moreFiles = issue.files.length > 8 ? `\n (+${issue.files.length - 8} more)` : "";
888
- const timedOutNote = issue.testResult.timedOut
978
+ const timedOutNote = issue.testResult.failureType === "timeout"
889
979
  ? `\n ⚠ Timed out after ${(issue.testResult.durationMs / 1000).toFixed(0)}s — increase verify.timeoutMs in .agentbridge.json`
890
980
  : "";
981
+ const outcomeLabel = issue.errorCode === "TEST_COMMAND_INVALID"
982
+ ? "⚠ Verification command invalid"
983
+ : issue.errorCode === "TEST_TOOLING_MISSING"
984
+ ? "⚠ Verification tooling missing"
985
+ : issue.errorCode === "TEST_TIMEOUT"
986
+ ? "✗ Verification timed out"
987
+ : "✗ Tests failed";
988
+ const stepOne = issue.errorCode === "TEST_COMMAND_INVALID"
989
+ ? " 1. Fix the verification command flags/options shown above."
990
+ : issue.errorCode === "TEST_TOOLING_MISSING"
991
+ ? " 1. Install/fix required test tooling for the verification command."
992
+ : issue.errorCode === "TEST_TIMEOUT"
993
+ ? " 1. Narrow test scope or increase verification timeout."
994
+ : " 1. Fix the failing tests listed above.";
891
995
  return [
892
996
  sep,
893
997
  " AgentBridge Verification Report",
@@ -908,14 +1012,14 @@ function renderWatchBlockingIssue(issue) {
908
1012
  "🔬 WHAT WE RAN",
909
1013
  ` Command : ${issue.testResult.command}`,
910
1014
  ` Duration: ${dur}s${timedOutNote}`,
911
- ` Outcome : ✗ Tests failed`,
1015
+ ` Outcome : ${outcomeLabel}`,
912
1016
  "",
913
1017
  "❌ WHAT WE FOUND",
914
1018
  failureLines + moreFailures,
915
1019
  ...(hasDrift ? ["", ` Scope note: ${issue.whyItMatters}`] : []),
916
1020
  "",
917
1021
  "➡ NEXT STEPS",
918
- " 1. Fix the failing tests listed above.",
1022
+ stepOne,
919
1023
  ...(hasDrift ? [" 2. Review the out-of-scope files — are they intentional?"] : []),
920
1024
  ` ${hasDrift ? "3" : "2"}. Re-run: agentbridge watch`,
921
1025
  "",
@@ -2552,7 +2656,10 @@ async function runWatch(options = {}) {
2552
2656
  nextAction: onceModeVerificationIssue.nextAction,
2553
2657
  };
2554
2658
  }
2555
- else if (onceModeVerificationIssue?.errorCode === "TEST_FAILED") {
2659
+ else if (onceModeVerificationIssue?.errorCode === "TEST_FAILED" ||
2660
+ onceModeVerificationIssue?.errorCode === "TEST_TIMEOUT" ||
2661
+ onceModeVerificationIssue?.errorCode === "TEST_COMMAND_INVALID" ||
2662
+ onceModeVerificationIssue?.errorCode === "TEST_TOOLING_MISSING") {
2556
2663
  supervision = {
2557
2664
  ...supervision,
2558
2665
  decision: "failed",
@@ -15,6 +15,7 @@ const intent_parser_1 = require("./intent-parser");
15
15
  const proof_parser_1 = require("./proof-parser");
16
16
  const diff_reader_1 = require("./diff-reader");
17
17
  const session_state_1 = require("./session-state");
18
+ const domain_keywords_1 = require("./domain-keywords");
18
19
  const MCP_RULE_PROFILE = "mcp_rules_config_install";
19
20
  const AUTH_PROFILE = "auth_login_session";
20
21
  const DB_PROFILE = "database_schema_migration";
@@ -26,32 +27,30 @@ const GENERIC_PROFILE = "generic";
26
27
  function unique(values) {
27
28
  return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
28
29
  }
29
- function matchIntent(intent, keywords) {
30
- const lower = intent.toLowerCase();
31
- return keywords.some((keyword) => lower.includes(keyword));
32
- }
30
+ const MCP_ENTITY_KEYWORDS = ["mcp", "agentbridge", "cursor"];
31
+ const MCP_ACTION_KEYWORDS = ["rule", "rules", "install", "setup", "config", "configuration"];
33
32
  function detectProfiles(intent) {
34
33
  const profiles = [];
35
- if (matchIntent(intent, ["mcp"]) &&
36
- matchIntent(intent, ["rule", "rules", "install", "setup", "config", "configuration"])) {
34
+ if ((0, domain_keywords_1.matchesAnyKeywordBoundary)(intent, MCP_ENTITY_KEYWORDS) &&
35
+ (0, domain_keywords_1.matchesAnyKeywordBoundary)(intent, MCP_ACTION_KEYWORDS)) {
37
36
  profiles.push(MCP_RULE_PROFILE);
38
37
  }
39
- if (matchIntent(intent, ["auth", "login", "session", "token", "oauth"])) {
38
+ if ((0, domain_keywords_1.matchesDomainKeywords)(intent, "auth")) {
40
39
  profiles.push(AUTH_PROFILE);
41
40
  }
42
- if (matchIntent(intent, ["database", "db", "schema", "migration", "prisma", "sql"])) {
41
+ if ((0, domain_keywords_1.matchesDomainKeywords)(intent, "database")) {
43
42
  profiles.push(DB_PROFILE);
44
43
  }
45
- if (matchIntent(intent, ["ui", "style", "css", "copy", "layout", "component"])) {
44
+ if ((0, domain_keywords_1.matchesDomainKeywords)(intent, "ui")) {
46
45
  profiles.push(UI_PROFILE);
47
46
  }
48
- if (matchIntent(intent, ["api", "endpoint", "route", "handler", "controller"])) {
47
+ if ((0, domain_keywords_1.matchesDomainKeywords)(intent, "api")) {
49
48
  profiles.push(API_PROFILE);
50
49
  }
51
- if (matchIntent(intent, ["test", "tests", "vitest", "jest", "proof"])) {
50
+ if ((0, domain_keywords_1.matchesDomainKeywords)(intent, "tests")) {
52
51
  profiles.push(TEST_PROFILE);
53
52
  }
54
- if (matchIntent(intent, ["payment", "payments", "billing", "stripe", "webhook"])) {
53
+ if ((0, domain_keywords_1.matchesDomainKeywords)(intent, "payments")) {
55
54
  profiles.push(PAYMENTS_PROFILE);
56
55
  }
57
56
  return profiles.length > 0 ? profiles : [GENERIC_PROFILE];
@@ -229,6 +228,31 @@ function fileMatchesArea(file, area) {
229
228
  const normalizedArea = area.replaceAll("\\", "/").trim();
230
229
  if (!normalizedArea || normalizedArea === "**")
231
230
  return true;
231
+ // Infix glob: src/**/auth/** — prefix path + directory segment(s), not substring hacks
232
+ if (normalizedArea.includes("**/")) {
233
+ const segments = normalizedArea.split("/**/");
234
+ if (segments.length >= 2) {
235
+ const prefix = segments[0].replace(/\/$/, "");
236
+ if (prefix && normalizedFile !== prefix && !normalizedFile.startsWith(`${prefix}/`)) {
237
+ return false;
238
+ }
239
+ const infixPattern = segments
240
+ .slice(1)
241
+ .join("/")
242
+ .replace(/\/\*\*$/, "")
243
+ .replace(/^\*\//, "");
244
+ if (!infixPattern || infixPattern === "*")
245
+ return true;
246
+ const infixParts = infixPattern.split("/").filter((part) => part && part !== "*");
247
+ for (const part of infixParts) {
248
+ const asDir = `/${part}/`;
249
+ const atEnd = normalizedFile.endsWith(`/${part}`);
250
+ if (!normalizedFile.includes(asDir) && !atEnd)
251
+ return false;
252
+ }
253
+ return true;
254
+ }
255
+ }
232
256
  if (normalizedArea.endsWith("/**")) {
233
257
  const prefix = normalizedArea.slice(0, -3);
234
258
  return normalizedFile === prefix || normalizedFile.startsWith(`${prefix}/`);
@@ -495,20 +519,23 @@ function evaluateCompletionCriteria(params) {
495
519
  });
496
520
  }
497
521
  function nextStepFromCriteria(evaluations, proofNeeded) {
498
- const missing = evaluations.filter((item) => item.status === "no_evidence");
499
- if (missing.length === 0) {
522
+ const unproven = evaluations.filter((item) => item.status === "no_evidence" ||
523
+ item.status === "partial_evidence" ||
524
+ item.status === "cannot_verify");
525
+ if (unproven.length === 0) {
500
526
  return "All tracked criteria are evidenced. Confirm final proof and commit when ready.";
501
527
  }
502
528
  // Prioritise idempotency if it is anywhere in the unproven list
503
- const idempotency = missing.find((item) => item.criterion.toLowerCase().includes("idempotent"));
529
+ const idempotency = unproven.find((item) => item.criterion.toLowerCase().includes("idempotent"));
504
530
  if (idempotency) {
505
531
  return "Run setup/configuration twice in a clean repo and record output proving idempotency.";
506
532
  }
507
- const firstMissing = missing[0];
508
- if (firstMissing && firstMissing.criterion.toLowerCase().includes("proof")) {
533
+ const proofGap = unproven.find((item) => item.criterion.toLowerCase().includes("proof"));
534
+ if (proofGap) {
509
535
  return proofNeeded[0] ?? "Run a relevant test/manual proof command and record output.";
510
536
  }
511
- return `${firstMissing?.criterion ?? "Missing evidence"} — capture concrete proof before closing this contract.`;
537
+ const firstUnproven = unproven[0];
538
+ return `${firstUnproven?.criterion ?? "Missing evidence"} — capture concrete proof before closing this contract.`;
512
539
  }
513
540
  function renderLiveExpectationSummary(contract) {
514
541
  const lines = ["AgentBridge expects evidence for:"];
@@ -207,7 +207,9 @@ function renderContractVerdict(session, changedFiles, proofRun, domains = []) {
207
207
  }
208
208
  }
209
209
  const stillUnproven = criteriaEvals
210
- ? criteriaEvals.filter((e) => e.status === "no_evidence")
210
+ ? criteriaEvals.filter((e) => e.status === "no_evidence" ||
211
+ e.status === "partial_evidence" ||
212
+ e.status === "cannot_verify")
211
213
  : [];
212
214
  lines.push("", "Here's what is still unproven:");
213
215
  if (stillUnproven.length === 0 && (!criteriaEvals || criteriaEvals.length === 0)) {
@@ -218,7 +220,12 @@ function renderContractVerdict(session, changedFiles, proofRun, domains = []) {
218
220
  }
219
221
  else {
220
222
  for (const e of stillUnproven) {
221
- lines.push(` - ${e.criterion}`);
223
+ const prefix = e.status === "partial_evidence"
224
+ ? "~"
225
+ : e.status === "cannot_verify"
226
+ ? "?"
227
+ : "✗";
228
+ lines.push(` ${prefix} [${e.status}] ${e.criterion}`);
222
229
  lines.push(` ${e.evidence}`);
223
230
  }
224
231
  }
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DOMAIN_KEYWORDS = void 0;
4
+ exports.matchesKeywordBoundary = matchesKeywordBoundary;
5
+ exports.matchesAnyKeywordBoundary = matchesAnyKeywordBoundary;
6
+ exports.matchesDomainKeywords = matchesDomainKeywords;
7
+ exports.detectDomainFromKeywords = detectDomainFromKeywords;
8
+ exports.DOMAIN_KEYWORDS = {
9
+ auth: [
10
+ "auth",
11
+ "login",
12
+ "logout",
13
+ "session",
14
+ "token",
15
+ "oauth",
16
+ "jwt",
17
+ "password",
18
+ "signin",
19
+ "sign-in",
20
+ "signup",
21
+ "sign-up",
22
+ "register",
23
+ "registration",
24
+ "credential",
25
+ "credentials",
26
+ "permission",
27
+ "permissions",
28
+ "role",
29
+ "roles",
30
+ "unauthorized",
31
+ "unauthenticated",
32
+ "authenticate",
33
+ "authentication",
34
+ "authorize",
35
+ "authorization",
36
+ ],
37
+ database: [
38
+ "database",
39
+ "db",
40
+ "schema",
41
+ "migration",
42
+ "migrate",
43
+ "prisma",
44
+ "sql",
45
+ "query",
46
+ "table",
47
+ "column",
48
+ "index",
49
+ "model",
50
+ "entity",
51
+ "seed",
52
+ "orm",
53
+ ],
54
+ payments: [
55
+ "payment",
56
+ "payments",
57
+ "billing",
58
+ "stripe",
59
+ "invoice",
60
+ "subscription",
61
+ "checkout",
62
+ "webhook",
63
+ "charge",
64
+ ],
65
+ api: ["api", "endpoint", "route", "handler", "controller", "request", "response", "rest", "graphql"],
66
+ ui: [
67
+ "ui",
68
+ "style",
69
+ "css",
70
+ "component",
71
+ "page",
72
+ "layout",
73
+ "copy",
74
+ "design",
75
+ "render",
76
+ "display",
77
+ "view",
78
+ "frontend",
79
+ "html",
80
+ "template",
81
+ "dashboard",
82
+ "button",
83
+ "theme",
84
+ ],
85
+ mcp: ["mcp", "agentbridge", "cursor", "rules", "configuration", "config", "setup", "install"],
86
+ tests: ["test", "tests", "spec", "vitest", "jest", "coverage", "proof"],
87
+ };
88
+ const BOUNDARY_ALNUM = "a-z0-9";
89
+ const REGEX_CACHE = new Map();
90
+ function escapeRegex(value) {
91
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
92
+ }
93
+ function keywordRegex(keyword) {
94
+ const normalized = keyword.trim().toLowerCase();
95
+ const cached = REGEX_CACHE.get(normalized);
96
+ if (cached)
97
+ return cached;
98
+ const pattern = `(^|[^${BOUNDARY_ALNUM}])${escapeRegex(normalized)}([^${BOUNDARY_ALNUM}]|$)`;
99
+ const compiled = new RegExp(pattern);
100
+ REGEX_CACHE.set(normalized, compiled);
101
+ return compiled;
102
+ }
103
+ function matchesKeywordBoundary(text, keyword) {
104
+ const normalizedText = text.toLowerCase();
105
+ return keywordRegex(keyword).test(normalizedText);
106
+ }
107
+ function matchesAnyKeywordBoundary(text, keywords) {
108
+ return keywords.some((keyword) => matchesKeywordBoundary(text, keyword));
109
+ }
110
+ function matchesDomainKeywords(text, domain) {
111
+ return matchesAnyKeywordBoundary(text, exports.DOMAIN_KEYWORDS[domain]);
112
+ }
113
+ function detectDomainFromKeywords(text, domainOrder = ["auth", "database", "payments", "api", "ui", "mcp", "tests"]) {
114
+ for (const domain of domainOrder) {
115
+ if (matchesDomainKeywords(text, domain))
116
+ return domain;
117
+ }
118
+ return null;
119
+ }
package/dist/index.js CHANGED
@@ -262,6 +262,10 @@ async function main() {
262
262
  if (command === "--help" || command === "help") {
263
263
  usage(undefined, { exitCode: 0, stream: "stdout" });
264
264
  }
265
+ if (command === "--version" || command === "-V" || command === "-v") {
266
+ (0, version_1.runVersion)();
267
+ return;
268
+ }
265
269
  // --help on any subcommand
266
270
  if (flags["--help"] === "true") {
267
271
  usage(command, { exitCode: 0, stream: "stdout" });
@@ -1,28 +1,20 @@
1
1
  "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseIntent = parseIntent;
4
+ exports.buildTaskSpecificCriteria = buildTaskSpecificCriteria;
2
5
  /**
3
6
  * Intent parser — extracts structured task specification from a raw intent string.
4
7
  * All logic is deterministic: no LLM, no external calls.
5
8
  */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.parseIntent = parseIntent;
8
- exports.buildTaskSpecificCriteria = buildTaskSpecificCriteria;
9
+ const domain_keywords_1 = require("./domain-keywords");
9
10
  const ACTIONS = ["fix", "add", "update", "refactor", "remove", "migrate", "rename", "revert", "delete", "implement", "improve", "patch"];
10
- const DOMAIN_KEYWORD_MAP = {
11
- auth: ["auth", "login", "logout", "session", "token", "oauth", "jwt", "password", "signin", "signup", "sign-in", "sign-up", "register", "credential", "permission", "role"],
12
- database: ["database", "db", "schema", "migration", "migrate", "prisma", "sql", "query", "table", "column", "index", "model", "seed", "orm"],
13
- payments: ["payment", "payments", "billing", "stripe", "invoice", "subscription", "checkout", "webhook"],
14
- api: ["api", "endpoint", "route", "handler", "controller", "request", "response", "rest", "graphql"],
15
- ui: ["ui", "style", "css", "component", "page", "layout", "copy", "design", "render", "display", "view", "frontend", "html", "template"],
16
- mcp: ["mcp", "agentbridge", "cursor", "rules"],
17
- tests: ["test", "tests", "spec", "vitest", "jest", "coverage", "proof"],
18
- };
19
11
  const SYMPTOMS = {
20
12
  "401": ["401", "unauthorized", "unauthenticated"],
21
13
  "403": ["403", "forbidden"],
22
14
  "404": ["404", "not found", "notfound"],
23
15
  "500": ["500", "internal server error", "server error"],
24
16
  "null": ["null", "undefined", "nan", "nil"],
25
- "crash": ["crash", "crashes", "crashed", "exception", "throws", "throw"],
17
+ "crash": ["crash", "crashes", "crashed", "crashing", "exception", "throws", "throw"],
26
18
  "loop": ["loop", "infinite loop", "recursion"],
27
19
  "hang": ["hang", "hangs", "timeout", "deadlock"],
28
20
  "slow": ["slow", "performance", "latency", "memory leak", "memory"],
@@ -40,22 +32,18 @@ const CAMEL_FN_RE = /\b([a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)+)\b/g;
40
32
  // Quoted phrase
41
33
  const QUOTED_RE = /"([^"]+)"|'([^']+)'/g;
42
34
  function detectDomain(lower) {
43
- for (const [domain, keywords] of Object.entries(DOMAIN_KEYWORD_MAP)) {
44
- if (keywords.some((kw) => lower.includes(kw)))
45
- return domain;
46
- }
47
- return null;
35
+ return (0, domain_keywords_1.detectDomainFromKeywords)(lower);
48
36
  }
49
37
  function detectAction(lower) {
50
38
  for (const action of ACTIONS) {
51
- if (lower.startsWith(action) || lower.includes(` ${action} `))
39
+ if ((0, domain_keywords_1.matchesKeywordBoundary)(lower, action))
52
40
  return action;
53
41
  }
54
42
  return null;
55
43
  }
56
44
  function detectSymptom(lower) {
57
45
  for (const [symptom, patterns] of Object.entries(SYMPTOMS)) {
58
- if (patterns.some((p) => lower.includes(p)))
46
+ if ((0, domain_keywords_1.matchesAnyKeywordBoundary)(lower, patterns))
59
47
  return symptom;
60
48
  }
61
49
  return null;