@codraoss/core 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/LICENSE +625 -0
  2. package/README.md +15 -0
  3. package/dist/chunk-FGXA7JNR.js +57 -0
  4. package/dist/chunk-FGXA7JNR.js.map +1 -0
  5. package/dist/chunk-ILZUCIZH.js +117 -0
  6. package/dist/chunk-ILZUCIZH.js.map +1 -0
  7. package/dist/chunk-JC74XH7Q.js +67 -0
  8. package/dist/chunk-JC74XH7Q.js.map +1 -0
  9. package/dist/chunk-MMVQPGK3.js +341 -0
  10. package/dist/chunk-MMVQPGK3.js.map +1 -0
  11. package/dist/chunk-NSTV4TRP.js +39 -0
  12. package/dist/chunk-NSTV4TRP.js.map +1 -0
  13. package/dist/chunk-OIQRTDYR.js +337 -0
  14. package/dist/chunk-OIQRTDYR.js.map +1 -0
  15. package/dist/chunk-PVXFURDR.js +90 -0
  16. package/dist/chunk-PVXFURDR.js.map +1 -0
  17. package/dist/chunk-R2HRWFRH.js +108 -0
  18. package/dist/chunk-R2HRWFRH.js.map +1 -0
  19. package/dist/chunk-RQ5S67XB.js +82 -0
  20. package/dist/chunk-RQ5S67XB.js.map +1 -0
  21. package/dist/chunk-U3VKVDZ7.js +266 -0
  22. package/dist/chunk-U3VKVDZ7.js.map +1 -0
  23. package/dist/chunk-V3RF6GQW.js +137 -0
  24. package/dist/chunk-V3RF6GQW.js.map +1 -0
  25. package/dist/chunk-WSK45HW4.js +79 -0
  26. package/dist/chunk-WSK45HW4.js.map +1 -0
  27. package/dist/chunk-XD6CGYHO.js +893 -0
  28. package/dist/chunk-XD6CGYHO.js.map +1 -0
  29. package/dist/claim-checks.d.ts +54 -0
  30. package/dist/claim-checks.js +20 -0
  31. package/dist/claim-checks.js.map +1 -0
  32. package/dist/diff/index.d.ts +22 -0
  33. package/dist/diff/index.js +25 -0
  34. package/dist/diff/index.js.map +1 -0
  35. package/dist/fingerprint.d.ts +9 -0
  36. package/dist/fingerprint.js +19 -0
  37. package/dist/fingerprint.js.map +1 -0
  38. package/dist/index-BTDWXDck.d.ts +93 -0
  39. package/dist/index.d.ts +138 -0
  40. package/dist/index.js +1755 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/logger.d.ts +32 -0
  43. package/dist/logger.js +17 -0
  44. package/dist/logger.js.map +1 -0
  45. package/dist/model-gRDTk8yk.d.ts +117 -0
  46. package/dist/model-output/index.d.ts +4 -0
  47. package/dist/model-output/index.js +32 -0
  48. package/dist/model-output/index.js.map +1 -0
  49. package/dist/ports/index.d.ts +512 -0
  50. package/dist/ports/index.js +13 -0
  51. package/dist/ports/index.js.map +1 -0
  52. package/dist/position-h_jdn6ZH.d.ts +30 -0
  53. package/dist/prompts/file-review.d.ts +6 -0
  54. package/dist/prompts/file-review.js +25 -0
  55. package/dist/prompts/file-review.js.map +1 -0
  56. package/dist/prompts/languages.d.ts +9 -0
  57. package/dist/prompts/languages.js +7 -0
  58. package/dist/prompts/languages.js.map +1 -0
  59. package/dist/prompts/summary.d.ts +12 -0
  60. package/dist/prompts/summary.js +50 -0
  61. package/dist/prompts/summary.js.map +1 -0
  62. package/dist/prompts/verify.d.ts +71 -0
  63. package/dist/prompts/verify.js +15 -0
  64. package/dist/prompts/verify.js.map +1 -0
  65. package/dist/rules/detect.d.ts +32 -0
  66. package/dist/rules/detect.js +13 -0
  67. package/dist/rules/detect.js.map +1 -0
  68. package/dist/rules/table.d.ts +18 -0
  69. package/dist/rules/table.js +7 -0
  70. package/dist/rules/table.js.map +1 -0
  71. package/dist/timeout.d.ts +6 -0
  72. package/dist/timeout.js +26 -0
  73. package/dist/timeout.js.map +1 -0
  74. package/dist/token-tracker.d.ts +38 -0
  75. package/dist/token-tracker.js +8 -0
  76. package/dist/token-tracker.js.map +1 -0
  77. package/dist/verify.d.ts +3 -0
  78. package/dist/verify.js +21 -0
  79. package/dist/verify.js.map +1 -0
  80. package/package.json +181 -0
@@ -0,0 +1,108 @@
1
+ // src/rules/table.ts
2
+ var ts = ["ts", "tsx", "js", "jsx", "mjs", "cjs"];
3
+ var RULES = [
4
+ {
5
+ id: "empty-catch",
6
+ claimType: "swallowed_error",
7
+ severity: "P2",
8
+ title: "Empty catch block swallows the error",
9
+ body: "This `catch` has no body, so the error is discarded with no log, no rethrow and no recovery. A failure here becomes silent. If the error is genuinely expected, say so in a comment inside the block.",
10
+ triggers: ["catch"],
11
+ pattern: /\bcatch\s*(\([^)]*\))?\s*\{\s*\}/,
12
+ rejectRaw: /\bcatch\s*(\([^)]*\))?\s*\{\s*(?:\/\/|\/\*)/,
13
+ extensions: ts,
14
+ enabled: true
15
+ },
16
+ {
17
+ id: "debugger-statement",
18
+ claimType: "other",
19
+ severity: "P1",
20
+ title: "`debugger` statement left in the diff",
21
+ body: "A `debugger` statement halts execution whenever devtools are open. This is almost always a leftover from local debugging.",
22
+ triggers: ["debugger"],
23
+ pattern: /^\s*debugger\s*;?\s*$/,
24
+ extensions: ts,
25
+ enabled: true
26
+ },
27
+ {
28
+ id: "focused-test",
29
+ claimType: "other",
30
+ severity: "P1",
31
+ title: "Focused test will skip the rest of the suite",
32
+ body: "A focused test (`.only`) silently prevents every other test in the file from running, so CI stays green while covering almost nothing.",
33
+ triggers: [".only"],
34
+ pattern: /\b(?:describe|it|test|context|suite)\s*\.\s*only\s*\(/,
35
+ extensions: ts,
36
+ enabled: true
37
+ },
38
+ {
39
+ id: "dynamic-code-exec",
40
+ claimType: "unsafe_dynamic_code",
41
+ severity: "P1",
42
+ title: "Dynamic code execution",
43
+ body: "`eval` and the `Function` constructor execute arbitrary strings as code. If any part of that string can be influenced by input, this is remote code execution.",
44
+ triggers: ["eval(", "Function("],
45
+ pattern: /(?:^|[^.\w])eval\s*\(|new\s+Function\s*\(/,
46
+ extensions: ts,
47
+ enabled: true
48
+ },
49
+ {
50
+ id: "dynamic-html-sink",
51
+ claimType: "unsafe_dom_sink",
52
+ severity: "P1",
53
+ title: "Unsanitized value assigned to an HTML sink",
54
+ body: "Assigning a non-literal to `innerHTML`/`outerHTML` (or passing one to `insertAdjacentHTML`) executes any markup it contains. If the value can carry user input this is XSS.",
55
+ triggers: ["innerHTML", "outerHTML", "insertAdjacentHTML"],
56
+ pattern: /\.(?:inner|outer)HTML\s*=\s*[A-Za-z_$][\w$.[\]()]*|insertAdjacentHTML\s*\([^)]*,\s*[A-Za-z_$]/,
57
+ extensions: ts,
58
+ enabled: true
59
+ },
60
+ {
61
+ id: "mutable-default-arg",
62
+ claimType: "mutable_default_arg",
63
+ severity: "P2",
64
+ title: "Mutable default argument",
65
+ body: "Python evaluates a default argument once, at definition time, so this list/dict/set is shared by every call. Mutating it leaks state between invocations. Use `None` and build the value inside the function.",
66
+ triggers: ["def "],
67
+ pattern: /\bdef\s+\w+\s*\([^)]*=\s*(?:\[\s*\]|\{\s*\}|set\s*\(\s*\)|dict\s*\(\s*\)|list\s*\(\s*\))/,
68
+ extensions: ["py"],
69
+ enabled: true
70
+ },
71
+ {
72
+ id: "destructive-migration",
73
+ claimType: "destructive_migration",
74
+ severity: "P1",
75
+ title: "Destructive migration statement",
76
+ body: "This statement discards data irreversibly. On a forward-only migration chain there is no rollback: confirm the column/table is genuinely unused and that a backup exists.",
77
+ triggers: ["DROP", "TRUNCATE", "drop", "truncate"],
78
+ pattern: /\b(?:drop\s+(?:column|table)|truncate\s+table|truncate\s+\w)/i,
79
+ extensions: ["sql"],
80
+ enabled: true
81
+ },
82
+ {
83
+ id: "hardcoded-secret",
84
+ claimType: "hardcoded_secret",
85
+ severity: "P0",
86
+ title: "Possible hardcoded credential",
87
+ body: "This looks like a literal credential committed to the repository. If it is real, rotate it and move it to a secret binding.",
88
+ triggers: ["sk-", "AIza", "ghp_", "AKIA"],
89
+ pattern: /\b(?:sk-[A-Za-z0-9]{20,}|AIza[0-9A-Za-z_-]{30,}|gh[pousr]_[A-Za-z0-9]{30,}|AKIA[0-9A-Z]{16})\b/,
90
+ enabled: false
91
+ },
92
+ {
93
+ id: "insecure-random",
94
+ claimType: "insecure_randomness",
95
+ severity: "P2",
96
+ title: "`Math.random()` used for a security-sensitive value",
97
+ body: "`Math.random()` is not cryptographically secure and its output is predictable. Use `crypto.getRandomValues()` for tokens, ids or anything an attacker should not guess.",
98
+ triggers: ["Math.random"],
99
+ pattern: /\b(?:token|secret|key|nonce|salt|password|session|id)\w*\s*=[^=]*Math\.random\s*\(/i,
100
+ extensions: ts,
101
+ enabled: false
102
+ }
103
+ ];
104
+
105
+ export {
106
+ RULES
107
+ };
108
+ //# sourceMappingURL=chunk-R2HRWFRH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rules/table.ts"],"sourcesContent":["import type { ClaimType, reviewSeverities } from '@codraoss/schema';\r\n\r\ntype ReviewSeverity = typeof reviewSeverities[number];\r\n\r\nexport type Rule = {\r\n id: string;\r\n claimType: ClaimType;\r\n severity: ReviewSeverity;\r\n title: string;\r\n body: string;\r\n triggers: readonly string[];\r\n pattern: RegExp;\r\n rejectRaw?: RegExp;\r\n extensions?: readonly string[];\r\n enabled: boolean;\r\n};\r\n\r\nconst ts = ['ts', 'tsx', 'js', 'jsx', 'mjs', 'cjs'] as const;\r\n\r\nexport const RULES: readonly Rule[] = [\r\n {\r\n id: 'empty-catch',\r\n claimType: 'swallowed_error',\r\n severity: 'P2',\r\n title: 'Empty catch block swallows the error',\r\n body: 'This `catch` has no body, so the error is discarded with no log, no rethrow and no recovery. '\r\n + 'A failure here becomes silent. If the error is genuinely expected, say so in a comment inside the block.',\r\n triggers: ['catch'],\r\n pattern: /\\bcatch\\s*(\\([^)]*\\))?\\s*\\{\\s*\\}/,\r\n rejectRaw: /\\bcatch\\s*(\\([^)]*\\))?\\s*\\{\\s*(?:\\/\\/|\\/\\*)/,\r\n extensions: ts,\r\n enabled: true,\r\n },\r\n {\r\n id: 'debugger-statement',\r\n claimType: 'other',\r\n severity: 'P1',\r\n title: '`debugger` statement left in the diff',\r\n body: 'A `debugger` statement halts execution whenever devtools are open. This is almost always '\r\n + 'a leftover from local debugging.',\r\n triggers: ['debugger'],\r\n pattern: /^\\s*debugger\\s*;?\\s*$/,\r\n extensions: ts,\r\n enabled: true,\r\n },\r\n {\r\n id: 'focused-test',\r\n claimType: 'other',\r\n severity: 'P1',\r\n title: 'Focused test will skip the rest of the suite',\r\n body: 'A focused test (`.only`) silently prevents every other test in the file from running, so '\r\n + 'CI stays green while covering almost nothing.',\r\n triggers: ['.only'],\r\n pattern: /\\b(?:describe|it|test|context|suite)\\s*\\.\\s*only\\s*\\(/,\r\n extensions: ts,\r\n enabled: true,\r\n },\r\n {\r\n id: 'dynamic-code-exec',\r\n claimType: 'unsafe_dynamic_code',\r\n severity: 'P1',\r\n title: 'Dynamic code execution',\r\n body: '`eval` and the `Function` constructor execute arbitrary strings as code. If any part of '\r\n + 'that string can be influenced by input, this is remote code execution.',\r\n triggers: ['eval(', 'Function('],\r\n pattern: /(?:^|[^.\\w])eval\\s*\\(|new\\s+Function\\s*\\(/,\r\n extensions: ts,\r\n enabled: true,\r\n },\r\n {\r\n id: 'dynamic-html-sink',\r\n claimType: 'unsafe_dom_sink',\r\n severity: 'P1',\r\n title: 'Unsanitized value assigned to an HTML sink',\r\n body: 'Assigning a non-literal to `innerHTML`/`outerHTML` (or passing one to `insertAdjacentHTML`) '\r\n + 'executes any markup it contains. If the value can carry user input this is XSS.',\r\n triggers: ['innerHTML', 'outerHTML', 'insertAdjacentHTML'],\r\n pattern: /\\.(?:inner|outer)HTML\\s*=\\s*[A-Za-z_$][\\w$.[\\]()]*|insertAdjacentHTML\\s*\\([^)]*,\\s*[A-Za-z_$]/,\r\n extensions: ts,\r\n enabled: true,\r\n },\r\n {\r\n id: 'mutable-default-arg',\r\n claimType: 'mutable_default_arg',\r\n severity: 'P2',\r\n title: 'Mutable default argument',\r\n body: 'Python evaluates a default argument once, at definition time, so this list/dict/set is '\r\n + 'shared by every call. Mutating it leaks state between invocations. Use `None` and build the '\r\n + 'value inside the function.',\r\n triggers: ['def '],\r\n pattern: /\\bdef\\s+\\w+\\s*\\([^)]*=\\s*(?:\\[\\s*\\]|\\{\\s*\\}|set\\s*\\(\\s*\\)|dict\\s*\\(\\s*\\)|list\\s*\\(\\s*\\))/,\r\n extensions: ['py'],\r\n enabled: true,\r\n },\r\n {\r\n id: 'destructive-migration',\r\n claimType: 'destructive_migration',\r\n severity: 'P1',\r\n title: 'Destructive migration statement',\r\n body: 'This statement discards data irreversibly. On a forward-only migration chain there is no '\r\n + 'rollback: confirm the column/table is genuinely unused and that a backup exists.',\r\n triggers: ['DROP', 'TRUNCATE', 'drop', 'truncate'],\r\n pattern: /\\b(?:drop\\s+(?:column|table)|truncate\\s+table|truncate\\s+\\w)/i,\r\n extensions: ['sql'],\r\n enabled: true,\r\n },\r\n\r\n\r\n {\r\n id: 'hardcoded-secret',\r\n claimType: 'hardcoded_secret',\r\n severity: 'P0',\r\n title: 'Possible hardcoded credential',\r\n body: 'This looks like a literal credential committed to the repository. If it is real, rotate it '\r\n + 'and move it to a secret binding.',\r\n triggers: ['sk-', 'AIza', 'ghp_', 'AKIA'],\r\n pattern: /\\b(?:sk-[A-Za-z0-9]{20,}|AIza[0-9A-Za-z_-]{30,}|gh[pousr]_[A-Za-z0-9]{30,}|AKIA[0-9A-Z]{16})\\b/,\r\n enabled: false,\r\n },\r\n {\r\n id: 'insecure-random',\r\n claimType: 'insecure_randomness',\r\n severity: 'P2',\r\n title: '`Math.random()` used for a security-sensitive value',\r\n body: '`Math.random()` is not cryptographically secure and its output is predictable. Use '\r\n + '`crypto.getRandomValues()` for tokens, ids or anything an attacker should not guess.',\r\n triggers: ['Math.random'],\r\n pattern: /\\b(?:token|secret|key|nonce|salt|password|session|id)\\w*\\s*=[^=]*Math\\.random\\s*\\(/i,\r\n extensions: ts,\r\n enabled: false,\r\n },\r\n];\r\n\r\n"],"mappings":";AAiBA,IAAM,KAAK,CAAC,MAAM,OAAO,MAAM,OAAO,OAAO,KAAK;AAE3C,IAAM,QAAyB;AAAA,EACpC;AAAA,IACE,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,IACP,MAAM;AAAA,IAEN,UAAU,CAAC,OAAO;AAAA,IAClB,SAAS;AAAA,IACT,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,IACP,MAAM;AAAA,IAEN,UAAU,CAAC,UAAU;AAAA,IACrB,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,IACP,MAAM;AAAA,IAEN,UAAU,CAAC,OAAO;AAAA,IAClB,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,IACP,MAAM;AAAA,IAEN,UAAU,CAAC,SAAS,WAAW;AAAA,IAC/B,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,IACP,MAAM;AAAA,IAEN,UAAU,CAAC,aAAa,aAAa,oBAAoB;AAAA,IACzD,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,IACP,MAAM;AAAA,IAGN,UAAU,CAAC,MAAM;AAAA,IACjB,SAAS;AAAA,IACT,YAAY,CAAC,IAAI;AAAA,IACjB,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,IACP,MAAM;AAAA,IAEN,UAAU,CAAC,QAAQ,YAAY,QAAQ,UAAU;AAAA,IACjD,SAAS;AAAA,IACT,YAAY,CAAC,KAAK;AAAA,IAClB,SAAS;AAAA,EACX;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,IACP,MAAM;AAAA,IAEN,UAAU,CAAC,OAAO,QAAQ,QAAQ,MAAM;AAAA,IACxC,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,IACP,MAAM;AAAA,IAEN,UAAU,CAAC,aAAa;AAAA,IACxB,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SAAS;AAAA,EACX;AACF;","names":[]}
@@ -0,0 +1,82 @@
1
+ // src/prompts/languages.ts
2
+ var LANGUAGE_GUIDELINES = [
3
+ {
4
+ language: "TypeScript/JavaScript",
5
+ persona: "an expert TypeScript engineer focused on correctness and safe async code",
6
+ extensions: ["ts", "tsx", "js", "jsx", "mjs", "cjs"],
7
+ guidelines: [
8
+ "Flag unhandled promise rejections, missing await, or async errors that can crash or silently drop work.",
9
+ "Flag resource leaks that cause real bugs (uncleared timers/intervals/listeners on a path that runs repeatedly).",
10
+ "Flag security pitfalls such as eval() on untrusted input or ReDoS-prone regexes.",
11
+ "Flag runtime-breaking null/undefined access introduced by the diff."
12
+ ]
13
+ },
14
+ {
15
+ language: "Python",
16
+ persona: "a Python engineer focused on correctness",
17
+ extensions: ["py"],
18
+ guidelines: [
19
+ "Flag mutable default arguments that cause shared-state bugs.",
20
+ 'Flag bare "except:" that swallows errors and hides failures.',
21
+ "Flag incorrect exception handling or resource handling (files/sockets not closed)."
22
+ ]
23
+ },
24
+ {
25
+ language: "CSS/SCSS/Less",
26
+ persona: "a frontend engineer",
27
+ extensions: ["css", "scss", "sass", "less"],
28
+ guidelines: [
29
+ "Flag only rules that break layout or rendering; do not report stylistic preferences."
30
+ ]
31
+ },
32
+ {
33
+ language: "SQL",
34
+ persona: "a database engineer focused on query safety and correctness",
35
+ extensions: ["sql"],
36
+ guidelines: [
37
+ "Flag SQL injection risks (unparameterized/interpolated user input).",
38
+ "Flag destructive or non-atomic migrations that risk data loss."
39
+ ]
40
+ },
41
+ {
42
+ language: "Markdown",
43
+ persona: "a technical writer",
44
+ extensions: ["md", "mdx"],
45
+ guidelines: [
46
+ "Flag only broken links/images or factually incorrect content; do not report style or grammar nits."
47
+ ]
48
+ },
49
+ {
50
+ language: "HTML",
51
+ persona: "a web engineer",
52
+ extensions: ["html", "htm"],
53
+ guidelines: [
54
+ "Flag only markup that is broken or functionally inaccessible; do not report SEO or style preferences."
55
+ ]
56
+ },
57
+ {
58
+ language: "JSON/Config",
59
+ persona: "a DevOps engineer",
60
+ extensions: ["json", "jsonc", "yaml", "yml", "toml"],
61
+ guidelines: [
62
+ "Flag invalid syntax/schema or hardcoded secrets; do not report naming-convention preferences."
63
+ ]
64
+ }
65
+ ];
66
+ function getLanguageForFile(path) {
67
+ const ext = path.split(".").pop()?.toLowerCase();
68
+ if (!ext) return void 0;
69
+ const matches = LANGUAGE_GUIDELINES.filter((g) => g.extensions.includes(ext));
70
+ if (matches.length === 0) return void 0;
71
+ if (matches.length > 1) {
72
+ return matches.reduce(
73
+ (best, candidate) => candidate.extensions.length < best.extensions.length ? candidate : best
74
+ );
75
+ }
76
+ return matches[0];
77
+ }
78
+
79
+ export {
80
+ getLanguageForFile
81
+ };
82
+ //# sourceMappingURL=chunk-RQ5S67XB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/prompts/languages.ts"],"sourcesContent":["export type LanguageGuideline = {\r\n language: string;\r\n extensions: string[];\r\n guidelines: string[];\r\n persona?: string;\r\n};\r\n\r\nconst LANGUAGE_GUIDELINES: LanguageGuideline[] = [\r\n {\r\n language: 'TypeScript/JavaScript',\r\n persona: 'an expert TypeScript engineer focused on correctness and safe async code',\r\n extensions: ['ts', 'tsx', 'js', 'jsx', 'mjs', 'cjs'],\r\n guidelines: [\r\n 'Flag unhandled promise rejections, missing await, or async errors that can crash or silently drop work.',\r\n 'Flag resource leaks that cause real bugs (uncleared timers/intervals/listeners on a path that runs repeatedly).',\r\n 'Flag security pitfalls such as eval() on untrusted input or ReDoS-prone regexes.',\r\n 'Flag runtime-breaking null/undefined access introduced by the diff.',\r\n ],\r\n },\r\n {\r\n language: 'Python',\r\n persona: 'a Python engineer focused on correctness',\r\n extensions: ['py'],\r\n guidelines: [\r\n 'Flag mutable default arguments that cause shared-state bugs.',\r\n 'Flag bare \"except:\" that swallows errors and hides failures.',\r\n 'Flag incorrect exception handling or resource handling (files/sockets not closed).',\r\n ],\r\n },\r\n {\r\n language: 'CSS/SCSS/Less',\r\n persona: 'a frontend engineer',\r\n extensions: ['css', 'scss', 'sass', 'less'],\r\n guidelines: [\r\n 'Flag only rules that break layout or rendering; do not report stylistic preferences.',\r\n ],\r\n },\r\n {\r\n language: 'SQL',\r\n persona: 'a database engineer focused on query safety and correctness',\r\n extensions: ['sql'],\r\n guidelines: [\r\n 'Flag SQL injection risks (unparameterized/interpolated user input).',\r\n 'Flag destructive or non-atomic migrations that risk data loss.',\r\n ],\r\n },\r\n {\r\n language: 'Markdown',\r\n persona: 'a technical writer',\r\n extensions: ['md', 'mdx'],\r\n guidelines: [\r\n 'Flag only broken links/images or factually incorrect content; do not report style or grammar nits.',\r\n ],\r\n },\r\n {\r\n language: 'HTML',\r\n persona: 'a web engineer',\r\n extensions: ['html', 'htm'],\r\n guidelines: [\r\n 'Flag only markup that is broken or functionally inaccessible; do not report SEO or style preferences.',\r\n ],\r\n },\r\n {\r\n language: 'JSON/Config',\r\n persona: 'a DevOps engineer',\r\n extensions: ['json', 'jsonc', 'yaml', 'yml', 'toml'],\r\n guidelines: [\r\n 'Flag invalid syntax/schema or hardcoded secrets; do not report naming-convention preferences.',\r\n ],\r\n },\r\n];\r\n\r\nexport function getLanguageForFile(path: string): LanguageGuideline | undefined {\r\n const ext = path.split('.').pop()?.toLowerCase();\r\n if (!ext) return undefined;\r\n\r\n const matches = LANGUAGE_GUIDELINES.filter((g) => g.extensions.includes(ext));\r\n \r\n if (matches.length === 0) return undefined;\r\n\r\n if (matches.length > 1) {\r\n return matches.reduce((best, candidate) =>\r\n candidate.extensions.length < best.extensions.length ? candidate : best,\r\n );\r\n }\r\n\r\n return matches[0];\r\n}\r\n"],"mappings":";AAOA,IAAM,sBAA2C;AAAA,EAC/C;AAAA,IACE,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY,CAAC,MAAM,OAAO,MAAM,OAAO,OAAO,KAAK;AAAA,IACnD,YAAY;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY,CAAC,IAAI;AAAA,IACjB,YAAY;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY,CAAC,OAAO,QAAQ,QAAQ,MAAM;AAAA,IAC1C,YAAY;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY,CAAC,KAAK;AAAA,IAClB,YAAY;AAAA,MACV;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY,CAAC,MAAM,KAAK;AAAA,IACxB,YAAY;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY,CAAC,QAAQ,KAAK;AAAA,IAC1B,YAAY;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY,CAAC,QAAQ,SAAS,QAAQ,OAAO,MAAM;AAAA,IACnD,YAAY;AAAA,MACV;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,mBAAmB,MAA6C;AAC9E,QAAM,MAAM,KAAK,MAAM,GAAG,EAAE,IAAI,GAAG,YAAY;AAC/C,MAAI,CAAC,IAAK,QAAO;AAEjB,QAAM,UAAU,oBAAoB,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,GAAG,CAAC;AAE5E,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,MAAI,QAAQ,SAAS,GAAG;AACtB,WAAO,QAAQ;AAAA,MAAO,CAAC,MAAM,cAC3B,UAAU,WAAW,SAAS,KAAK,WAAW,SAAS,YAAY;AAAA,IACrE;AAAA,EACF;AAEA,SAAO,QAAQ,CAAC;AAClB;","names":[]}
@@ -0,0 +1,266 @@
1
+ import {
2
+ normalizeDiffText
3
+ } from "./chunk-NSTV4TRP.js";
4
+
5
+ // src/claim-checks.ts
6
+ var PROXIMITY_WINDOW_LINES = 25;
7
+ var MIN_IDENTIFIER_LENGTH = 3;
8
+ var ABSENCE_PATTERNS = [
9
+ /\b(?:never|not|no longer)\s+(?:being\s+)?(?:passed|provided|supplied|forwarded|included|used|called|invoked|awaited|checked|set|declared|defined|imported)\b/i,
10
+ /\bdoes not\s+(?:pass|include|call|use|await|check|set|import)\b/i,
11
+ /\bfails to\s+(?:pass|include|call|await|check|import)\b/i,
12
+ /\bwithout\s+(?:passing|including|calling|awaiting|checking|importing)\b/i,
13
+ /\b(?:missing|omitted|absent)\b/i,
14
+ /\bis not defined\b/i
15
+ ];
16
+ var IDENTIFIER_STOPLIST = /* @__PURE__ */ new Set([
17
+ "await",
18
+ "async",
19
+ "if",
20
+ "else",
21
+ "try",
22
+ "catch",
23
+ "finally",
24
+ "return",
25
+ "throw",
26
+ "new",
27
+ "const",
28
+ "let",
29
+ "var",
30
+ "function",
31
+ "class",
32
+ "this",
33
+ "super",
34
+ "import",
35
+ "export",
36
+ "from",
37
+ "default",
38
+ "null",
39
+ "undefined",
40
+ "true",
41
+ "false",
42
+ "void",
43
+ "typeof",
44
+ "instanceof",
45
+ "delete",
46
+ "yield",
47
+ "props",
48
+ "state",
49
+ "error",
50
+ "err",
51
+ "data",
52
+ "value",
53
+ "key",
54
+ "id",
55
+ "type",
56
+ "name",
57
+ "index",
58
+ "result",
59
+ "response",
60
+ "request",
61
+ "req",
62
+ "res",
63
+ "params",
64
+ "options",
65
+ "config",
66
+ "args"
67
+ ]);
68
+ var VERSION_CLAIM_PATTERNS = [
69
+ /\b(?:does not|doesn't|do not|don't)\s+exist\b/i,
70
+ /\b(?:non-?existent|nonexistent)\b/i,
71
+ /\bis not a valid\b/i,
72
+ /\blatest (?:major )?version\b/i,
73
+ /\bno such (?:version|tag|release)\b/i,
74
+ /\bnot a valid (?:configuration )?(?:option|key|property)\b/i,
75
+ /\b(?:does not|doesn't|do not|don't)\s+(?:expose|provide|have|support|include|offer)\b/i,
76
+ /\bno such (?:function|method|export|property|api|field)\b/i,
77
+ /\bis not (?:exposed|exported|available) (?:by|from|in)\b/i
78
+ ];
79
+ var CROSS_FILE_SUBJECT = /\b(?:other|another|external|downstream|consuming|importing|dependent|calling)\s+(?:module|file|component|caller|package|consumer|import)s?\b/i;
80
+ var CROSS_FILE_CONSEQUENCE = /\b(?:break|breaks|breaking|broken|fail|fails|failing|error|errors|cannot import|can't import|unable to|compilation|compile|prevent|prevents|preventing|block|blocks|blocking)\b/i;
81
+ var ENVIRONMENT_HEDGE = /\b(?:depending on|might not|may not|could be undefined|if (?:this|the|it)\b[^.]{0,60}\b(?:is )?(?:rendered|run|executed|used)\b)/i;
82
+ var ENVIRONMENT_SUBJECT = /\b(?:older|legacy|earlier|some)\s+(?:node(?:\.js)?|browsers?|runtimes?|environments?|engines?|versions?)\b|\bserver[- ]side\b|\bSSR\b|\bhydration\b|\bpolyfill\b|\bis not defined on the server\b/i;
83
+ var CALLEE_FAILURE_CONDITION = /\b(?:if|when|should|were)\b(?:(?!\.\s)[^;!?]){0,62}\b(?:fails?|failing|rejects?|rejecting|throws?|throwing|errors? out)\b/i;
84
+ var CALLEE_CALL_SHAPE = /[\w.$]{1,50}\s*\(\s*\)|`[\w.$]{1,50}\(/;
85
+ var CALLEE_UNHANDLED_OUTCOME = /\bunhandled\b|\bunhandled promise\b|\bnot (?:caught|handled)\b|\bno (?:\.)?catch\b|\bwithout (?:a )?(?:try|catch)\b|\bcrash\b/i;
86
+ function refuteUndecidableClaim(input) {
87
+ const text = `${input.title}
88
+ ${input.body}`;
89
+ if (CROSS_FILE_SUBJECT.test(text) && CROSS_FILE_CONSEQUENCE.test(text)) return "cross-file";
90
+ if (ENVIRONMENT_HEDGE.test(text) && ENVIRONMENT_SUBJECT.test(text)) return "environment";
91
+ if (CALLEE_FAILURE_CONDITION.test(text) && CALLEE_CALL_SHAPE.test(text) && CALLEE_UNHANDLED_OUTCOME.test(text)) {
92
+ return "callee-errors";
93
+ }
94
+ return null;
95
+ }
96
+ var FULL_SHA_PATTERN = /\b[0-9a-f]{40}\b/;
97
+ function looksLikeExternalVersionClaim(title, body) {
98
+ const text = `${title}
99
+ ${body}`;
100
+ return VERSION_CLAIM_PATTERNS.some((pattern) => pattern.test(text));
101
+ }
102
+ function isVersionClaimRefutedByPin(input) {
103
+ if (!looksLikeExternalVersionClaim(input.title, input.body)) return false;
104
+ return FULL_SHA_PATTERN.test(input.anchorContent);
105
+ }
106
+ function commentSyntaxFor(path) {
107
+ const ext = path.toLowerCase().split(".").pop() ?? "";
108
+ if (ext === "py" || ext === "rb" || ext === "sh" || ext === "yaml" || ext === "yml" || ext === "toml") {
109
+ return { line: ["#"], block: false };
110
+ }
111
+ if (ext === "sql") return { line: ["--"], block: true };
112
+ return { line: ["//"], block: true };
113
+ }
114
+ function stripCommentsAndStrings(input, syntax) {
115
+ let out = "";
116
+ let i = 0;
117
+ while (i < input.length) {
118
+ const rest = input.slice(i);
119
+ if (syntax.line.some((token) => rest.startsWith(token))) break;
120
+ if (syntax.block && rest.startsWith("/*")) {
121
+ const end = input.indexOf("*/", i + 2);
122
+ if (end === -1) return null;
123
+ out += " ";
124
+ i = end + 2;
125
+ continue;
126
+ }
127
+ const char = input[i];
128
+ if (char === "'" || char === '"') {
129
+ const close = findStringEnd(input, i + 1, char);
130
+ if (close === -1) return null;
131
+ out += " ";
132
+ i = close + 1;
133
+ continue;
134
+ }
135
+ if (char === "`") {
136
+ const scanned = scanTemplateLiteral(input, i);
137
+ if (!scanned) return null;
138
+ out += scanned.code;
139
+ i = scanned.next;
140
+ continue;
141
+ }
142
+ out += char;
143
+ i += 1;
144
+ }
145
+ return out;
146
+ }
147
+ function findStringEnd(input, start, quote) {
148
+ for (let i = start; i < input.length; i++) {
149
+ if (input[i] === "\\") {
150
+ i += 1;
151
+ continue;
152
+ }
153
+ if (input[i] === quote) return i;
154
+ }
155
+ return -1;
156
+ }
157
+ function scanTemplateLiteral(input, start) {
158
+ let code = " ";
159
+ let i = start + 1;
160
+ while (i < input.length) {
161
+ if (input[i] === "\\") {
162
+ i += 2;
163
+ continue;
164
+ }
165
+ if (input[i] === "`") return { code, next: i + 1 };
166
+ if (input[i] === "$" && input[i + 1] === "{") {
167
+ let depth = 1;
168
+ let j = i + 2;
169
+ while (j < input.length && depth > 0) {
170
+ if (input[j] === "{") depth += 1;
171
+ else if (input[j] === "}") depth -= 1;
172
+ j += 1;
173
+ }
174
+ if (depth !== 0) return null;
175
+ code += ` ${input.slice(i + 2, j - 1)} `;
176
+ i = j;
177
+ continue;
178
+ }
179
+ i += 1;
180
+ }
181
+ return null;
182
+ }
183
+ var TOKEN_PATTERN = /[A-Za-z_$][\w$]*/g;
184
+ function buildPresenceIndex(file) {
185
+ const syntax = commentSyntaxFor(file.path);
186
+ const byToken = /* @__PURE__ */ new Map();
187
+ const entries = [];
188
+ const hunkByLine = /* @__PURE__ */ new Map();
189
+ file.hunks.forEach((hunk, hunkIndex) => {
190
+ for (const line of hunk.lines) {
191
+ if (line.newLineNumber !== void 0) hunkByLine.set(line.newLineNumber, hunkIndex);
192
+ if (line.kind === "del") continue;
193
+ const code = stripCommentsAndStrings(normalizeDiffText(line.content), syntax);
194
+ if (code === null) continue;
195
+ const entry = { line, hunkIndex, code };
196
+ entries.push(entry);
197
+ for (const match of code.matchAll(TOKEN_PATTERN)) {
198
+ const token = match[0];
199
+ const existing = byToken.get(token);
200
+ if (existing) existing.push(entry);
201
+ else byToken.set(token, [entry]);
202
+ }
203
+ }
204
+ });
205
+ return { byToken, entries, hunkByLine };
206
+ }
207
+ var SIMPLE_IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
208
+ var DOTTED_IDENTIFIER = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/;
209
+ function extractIdentifier(sentence) {
210
+ const spans = [
211
+ ...sentence.matchAll(/`([^`]+)`/g),
212
+ ...sentence.matchAll(/'([^']+)'/g),
213
+ ...sentence.matchAll(/"([^"]+)"/g)
214
+ ].map((match) => match[1].trim());
215
+ const candidates = new Set(
216
+ spans.filter((span) => SIMPLE_IDENTIFIER.test(span) || DOTTED_IDENTIFIER.test(span))
217
+ );
218
+ if (candidates.size === 0) return "none";
219
+ if (candidates.size > 1) return "ambiguous";
220
+ return { identifier: [...candidates][0] };
221
+ }
222
+ function absenceSentences(text) {
223
+ return text.split(/[.;\n]/).filter((sentence) => ABSENCE_PATTERNS.some((pattern) => pattern.test(sentence)));
224
+ }
225
+ function checkAbsenceClaim(input) {
226
+ const text = `${input.title}
227
+ ${input.body.slice(0, 600)}`;
228
+ const sentences = absenceSentences(text);
229
+ if (sentences.length === 0) return { status: "unknown", reason: "not_absence_shaped" };
230
+ let identifier;
231
+ for (const sentence of sentences) {
232
+ const extracted = extractIdentifier(sentence);
233
+ if (extracted === "ambiguous") return { status: "unknown", reason: "ambiguous_identifier" };
234
+ if (extracted !== "none") {
235
+ identifier = extracted.identifier;
236
+ break;
237
+ }
238
+ }
239
+ if (!identifier) return { status: "unknown", reason: "no_identifier" };
240
+ const head = identifier.split(".")[0];
241
+ if (identifier.length < MIN_IDENTIFIER_LENGTH) return { status: "unknown", reason: "stoplisted" };
242
+ if (IDENTIFIER_STOPLIST.has(identifier.toLowerCase()) || IDENTIFIER_STOPLIST.has(head.toLowerCase())) {
243
+ return { status: "unknown", reason: "stoplisted" };
244
+ }
245
+ const occurrences = identifier.includes(".") ? input.index.entries.filter((entry) => entry.code.replace(/\s*\.\s*/g, ".").includes(identifier)) : input.index.byToken.get(identifier) ?? [];
246
+ if (occurrences.length === 0) return { status: "unknown", reason: "not_present" };
247
+ const anchorHunk = input.anchorLine !== void 0 ? input.index.hunkByLine.get(input.anchorLine) : void 0;
248
+ const nearby = occurrences.find((entry) => {
249
+ if (anchorHunk !== void 0 && entry.hunkIndex === anchorHunk) return true;
250
+ if (input.anchorLine === void 0 || entry.line.newLineNumber === void 0) return false;
251
+ return Math.abs(entry.line.newLineNumber - input.anchorLine) <= PROXIMITY_WINDOW_LINES;
252
+ });
253
+ if (!nearby) return { status: "unknown", reason: "out_of_window" };
254
+ return { status: "refuted", identifier, line: nearby.line };
255
+ }
256
+
257
+ export {
258
+ refuteUndecidableClaim,
259
+ looksLikeExternalVersionClaim,
260
+ isVersionClaimRefutedByPin,
261
+ commentSyntaxFor,
262
+ stripCommentsAndStrings,
263
+ buildPresenceIndex,
264
+ checkAbsenceClaim
265
+ };
266
+ //# sourceMappingURL=chunk-U3VKVDZ7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/claim-checks.ts"],"sourcesContent":["// SOUNDNESS, binding on every change: `refuted` asserts only that \"X does not appear\" is FALSE. There is no `confirmed` verdict, since a check that can confirm findings manufactures them. Losing a refutation is free; a wrong one silences a real defect.\r\nimport type { DiffLine, FileDiff } from './diff';\r\nimport { normalizeDiffText } from './fingerprint';\r\n\r\nconst PROXIMITY_WINDOW_LINES = 25;\r\n\r\nconst MIN_IDENTIFIER_LENGTH = 3;\r\n\r\nconst ABSENCE_PATTERNS: readonly RegExp[] = [\r\n /\\b(?:never|not|no longer)\\s+(?:being\\s+)?(?:passed|provided|supplied|forwarded|included|used|called|invoked|awaited|checked|set|declared|defined|imported)\\b/i,\r\n /\\bdoes not\\s+(?:pass|include|call|use|await|check|set|import)\\b/i,\r\n /\\bfails to\\s+(?:pass|include|call|await|check|import)\\b/i,\r\n /\\bwithout\\s+(?:passing|including|calling|awaiting|checking|importing)\\b/i,\r\n /\\b(?:missing|omitted|absent)\\b/i,\r\n /\\bis not defined\\b/i,\r\n];\r\n\r\nconst IDENTIFIER_STOPLIST = new Set([\r\n 'await', 'async', 'if', 'else', 'try', 'catch', 'finally', 'return', 'throw', 'new', 'const',\r\n 'let', 'var', 'function', 'class', 'this', 'super', 'import', 'export', 'from', 'default',\r\n 'null', 'undefined', 'true', 'false', 'void', 'typeof', 'instanceof', 'delete', 'yield',\r\n 'props', 'state', 'error', 'err', 'data', 'value', 'key', 'id', 'type', 'name', 'index',\r\n 'result', 'response', 'request', 'req', 'res', 'params', 'options', 'config', 'args',\r\n]);\r\n\r\nconst VERSION_CLAIM_PATTERNS: readonly RegExp[] = [\r\n /\\b(?:does not|doesn't|do not|don't)\\s+exist\\b/i,\r\n /\\b(?:non-?existent|nonexistent)\\b/i,\r\n /\\bis not a valid\\b/i,\r\n /\\blatest (?:major )?version\\b/i,\r\n /\\bno such (?:version|tag|release)\\b/i,\r\n /\\bnot a valid (?:configuration )?(?:option|key|property)\\b/i,\r\n /\\b(?:does not|doesn't|do not|don't)\\s+(?:expose|provide|have|support|include|offer)\\b/i,\r\n /\\bno such (?:function|method|export|property|api|field)\\b/i,\r\n /\\bis not (?:exposed|exported|available) (?:by|from|in)\\b/i,\r\n];\r\n\r\n// Same soundness rule as the absence checker above: a refutation asserts only that the claim cannot be\r\n\r\nconst CROSS_FILE_SUBJECT = /\\b(?:other|another|external|downstream|consuming|importing|dependent|calling)\\s+(?:module|file|component|caller|package|consumer|import)s?\\b/i;\r\nconst CROSS_FILE_CONSEQUENCE = /\\b(?:break|breaks|breaking|broken|fail|fails|failing|error|errors|cannot import|can't import|unable to|compilation|compile|prevent|prevents|preventing|block|blocks|blocking)\\b/i;\r\n\r\nconst ENVIRONMENT_HEDGE = /\\b(?:depending on|might not|may not|could be undefined|if (?:this|the|it)\\b[^.]{0,60}\\b(?:is )?(?:rendered|run|executed|used)\\b)/i;\r\nconst ENVIRONMENT_SUBJECT = /\\b(?:older|legacy|earlier|some)\\s+(?:node(?:\\.js)?|browsers?|runtimes?|environments?|engines?|versions?)\\b|\\bserver[- ]side\\b|\\bSSR\\b|\\bhydration\\b|\\bpolyfill\\b|\\bis not defined on the server\\b/i;\r\n\r\nconst CALLEE_FAILURE_CONDITION = /\\b(?:if|when|should|were)\\b(?:(?!\\.\\s)[^;!?]){0,62}\\b(?:fails?|failing|rejects?|rejecting|throws?|throwing|errors? out)\\b/i;\r\nconst CALLEE_CALL_SHAPE = /[\\w.$]{1,50}\\s*\\(\\s*\\)|`[\\w.$]{1,50}\\(/;\r\nconst CALLEE_UNHANDLED_OUTCOME = /\\bunhandled\\b|\\bunhandled promise\\b|\\bnot (?:caught|handled)\\b|\\bno (?:\\.)?catch\\b|\\bwithout (?:a )?(?:try|catch)\\b|\\bcrash\\b/i;\r\n\r\nexport type UndecidableClaimReason = 'cross-file' | 'environment' | 'callee-errors';\r\n\r\n/**\r\n * Refutes a claim whose truth lives outside the diff, returning the family it belongs to or null.\r\n *\r\n * Deliberately requires TWO independent signals per family -- a subject and a consequence -- because\r\n * either alone is ordinary review language. \"This breaks the build\" is a normal thing to say about\r\n * code in the diff; \"other modules import this\" is a normal aside. Only together do they describe a\r\n * consequence in a file nobody showed the model.\r\n */\r\nexport function refuteUndecidableClaim(input: { title: string; body: string }): UndecidableClaimReason | null {\r\n const text = `${input.title}\\n${input.body}`;\r\n\r\n if (CROSS_FILE_SUBJECT.test(text) && CROSS_FILE_CONSEQUENCE.test(text)) return 'cross-file';\r\n if (ENVIRONMENT_HEDGE.test(text) && ENVIRONMENT_SUBJECT.test(text)) return 'environment';\r\n if (CALLEE_FAILURE_CONDITION.test(text) && CALLEE_CALL_SHAPE.test(text) && CALLEE_UNHANDLED_OUTCOME.test(text)) {\r\n return 'callee-errors';\r\n }\r\n\r\n return null;\r\n}\r\n\r\nconst FULL_SHA_PATTERN = /\\b[0-9a-f]{40}\\b/;\r\n\r\nexport function looksLikeExternalVersionClaim(title: string, body: string): boolean {\r\n const text = `${title}\\n${body}`;\r\n return VERSION_CLAIM_PATTERNS.some((pattern) => pattern.test(text));\r\n}\r\n\r\nexport function isVersionClaimRefutedByPin(input: { title: string; body: string; anchorContent: string }): boolean {\r\n if (!looksLikeExternalVersionClaim(input.title, input.body)) return false;\r\n return FULL_SHA_PATTERN.test(input.anchorContent);\r\n}\r\n\r\ntype PresenceEntry = { line: DiffLine; hunkIndex: number; code: string };\r\n\r\nexport type PresenceIndex = {\r\n byToken: Map<string, PresenceEntry[]>;\r\n entries: PresenceEntry[];\r\n hunkByLine: Map<number, number>;\r\n};\r\n\r\nexport type AbsenceClaimVerdict =\r\n | {\r\n status: 'unknown';\r\n reason:\r\n | 'not_absence_shaped'\r\n | 'no_identifier'\r\n | 'ambiguous_identifier'\r\n | 'stoplisted'\r\n | 'not_present'\r\n | 'out_of_window';\r\n }\r\n | { status: 'refuted'; identifier: string; line: DiffLine };\r\n\r\ntype CommentSyntax = { line: readonly string[]; block: boolean };\r\n\r\nexport function commentSyntaxFor(path: string): CommentSyntax {\r\n const ext = path.toLowerCase().split('.').pop() ?? '';\r\n if (ext === 'py' || ext === 'rb' || ext === 'sh' || ext === 'yaml' || ext === 'yml' || ext === 'toml') {\r\n return { line: ['#'], block: false };\r\n }\r\n if (ext === 'sql') return { line: ['--'], block: true };\r\n return { line: ['//'], block: true };\r\n}\r\n\r\nexport function stripCommentsAndStrings(input: string, syntax: CommentSyntax): string | null {\r\n let out = '';\r\n let i = 0;\r\n\r\n while (i < input.length) {\r\n const rest = input.slice(i);\r\n\r\n if (syntax.line.some((token) => rest.startsWith(token))) break;\r\n\r\n if (syntax.block && rest.startsWith('/*')) {\r\n const end = input.indexOf('*/', i + 2);\r\n if (end === -1) return null;\r\n out += ' ';\r\n i = end + 2;\r\n continue;\r\n }\r\n\r\n const char = input[i];\r\n\r\n if (char === \"'\" || char === '\"') {\r\n const close = findStringEnd(input, i + 1, char);\r\n if (close === -1) return null;\r\n out += ' ';\r\n i = close + 1;\r\n continue;\r\n }\r\n\r\n if (char === '`') {\r\n const scanned = scanTemplateLiteral(input, i);\r\n if (!scanned) return null;\r\n out += scanned.code;\r\n i = scanned.next;\r\n continue;\r\n }\r\n\r\n out += char;\r\n i += 1;\r\n }\r\n\r\n return out;\r\n}\r\n\r\nfunction findStringEnd(input: string, start: number, quote: string): number {\r\n for (let i = start; i < input.length; i++) {\r\n if (input[i] === '\\\\') {\r\n i += 1;\r\n continue;\r\n }\r\n if (input[i] === quote) return i;\r\n }\r\n return -1;\r\n}\r\n\r\nfunction scanTemplateLiteral(input: string, start: number): { code: string; next: number } | null {\r\n let code = ' ';\r\n let i = start + 1;\r\n\r\n while (i < input.length) {\r\n if (input[i] === '\\\\') {\r\n i += 2;\r\n continue;\r\n }\r\n if (input[i] === '`') return { code, next: i + 1 };\r\n if (input[i] === '$' && input[i + 1] === '{') {\r\n let depth = 1;\r\n let j = i + 2;\r\n while (j < input.length && depth > 0) {\r\n if (input[j] === '{') depth += 1;\r\n else if (input[j] === '}') depth -= 1;\r\n j += 1;\r\n }\r\n if (depth !== 0) return null;\r\n code += ` ${input.slice(i + 2, j - 1)} `;\r\n i = j;\r\n continue;\r\n }\r\n i += 1;\r\n }\r\n\r\n return null;\r\n}\r\n\r\nconst TOKEN_PATTERN = /[A-Za-z_$][\\w$]*/g;\r\n\r\nexport function buildPresenceIndex(file: FileDiff): PresenceIndex {\r\n const syntax = commentSyntaxFor(file.path);\r\n const byToken = new Map<string, PresenceEntry[]>();\r\n const entries: PresenceEntry[] = [];\r\n const hunkByLine = new Map<number, number>();\r\n\r\n file.hunks.forEach((hunk, hunkIndex) => {\r\n for (const line of hunk.lines) {\r\n if (line.newLineNumber !== undefined) hunkByLine.set(line.newLineNumber, hunkIndex);\r\n\r\n if (line.kind === 'del') continue;\r\n\r\n const code = stripCommentsAndStrings(normalizeDiffText(line.content), syntax);\r\n if (code === null) continue;\r\n\r\n const entry: PresenceEntry = { line, hunkIndex, code };\r\n entries.push(entry);\r\n\r\n for (const match of code.matchAll(TOKEN_PATTERN)) {\r\n const token = match[0];\r\n const existing = byToken.get(token);\r\n if (existing) existing.push(entry);\r\n else byToken.set(token, [entry]);\r\n }\r\n }\r\n });\r\n\r\n return { byToken, entries, hunkByLine };\r\n}\r\n\r\nconst SIMPLE_IDENTIFIER = /^[A-Za-z_$][\\w$]*$/;\r\nconst DOTTED_IDENTIFIER = /^[A-Za-z_$][\\w$]*(?:\\.[A-Za-z_$][\\w$]*)+$/;\r\n\r\nfunction extractIdentifier(sentence: string): { identifier: string } | 'none' | 'ambiguous' {\r\n const spans = [\r\n ...sentence.matchAll(/`([^`]+)`/g),\r\n ...sentence.matchAll(/'([^']+)'/g),\r\n ...sentence.matchAll(/\"([^\"]+)\"/g),\r\n ].map((match) => match[1].trim());\r\n\r\n const candidates = new Set(\r\n spans.filter((span) => SIMPLE_IDENTIFIER.test(span) || DOTTED_IDENTIFIER.test(span)),\r\n );\r\n\r\n if (candidates.size === 0) return 'none';\r\n if (candidates.size > 1) return 'ambiguous';\r\n return { identifier: [...candidates][0] };\r\n}\r\n\r\nfunction absenceSentences(text: string): string[] {\r\n return text.split(/[.;\\n]/).filter((sentence) => ABSENCE_PATTERNS.some((pattern) => pattern.test(sentence)));\r\n}\r\n\r\nexport function checkAbsenceClaim(input: {\r\n title: string;\r\n body: string;\r\n anchorLine: number | undefined;\r\n index: PresenceIndex;\r\n}): AbsenceClaimVerdict {\r\n const text = `${input.title}\\n${input.body.slice(0, 600)}`;\r\n\r\n const sentences = absenceSentences(text);\r\n if (sentences.length === 0) return { status: 'unknown', reason: 'not_absence_shaped' };\r\n\r\n let identifier: string | undefined;\r\n for (const sentence of sentences) {\r\n const extracted = extractIdentifier(sentence);\r\n if (extracted === 'ambiguous') return { status: 'unknown', reason: 'ambiguous_identifier' };\r\n if (extracted !== 'none') {\r\n identifier = extracted.identifier;\r\n break;\r\n }\r\n }\r\n if (!identifier) return { status: 'unknown', reason: 'no_identifier' };\r\n\r\n const head = identifier.split('.')[0];\r\n if (identifier.length < MIN_IDENTIFIER_LENGTH) return { status: 'unknown', reason: 'stoplisted' };\r\n if (IDENTIFIER_STOPLIST.has(identifier.toLowerCase()) || IDENTIFIER_STOPLIST.has(head.toLowerCase())) {\r\n return { status: 'unknown', reason: 'stoplisted' };\r\n }\r\n\r\n const occurrences = identifier.includes('.')\r\n ? input.index.entries.filter((entry) => entry.code.replace(/\\s*\\.\\s*/g, '.').includes(identifier))\r\n : (input.index.byToken.get(identifier) ?? []);\r\n\r\n if (occurrences.length === 0) return { status: 'unknown', reason: 'not_present' };\r\n\r\n const anchorHunk = input.anchorLine !== undefined ? input.index.hunkByLine.get(input.anchorLine) : undefined;\r\n const nearby = occurrences.find((entry) => {\r\n if (anchorHunk !== undefined && entry.hunkIndex === anchorHunk) return true;\r\n if (input.anchorLine === undefined || entry.line.newLineNumber === undefined) return false;\r\n return Math.abs(entry.line.newLineNumber - input.anchorLine) <= PROXIMITY_WINDOW_LINES;\r\n });\r\n\r\n if (!nearby) return { status: 'unknown', reason: 'out_of_window' };\r\n return { status: 'refuted', identifier, line: nearby.line };\r\n}\r\n"],"mappings":"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","names":[]}