tryassay 0.28.2 → 0.30.1

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 (93) hide show
  1. package/dist/cli.js +10 -0
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/assess.js +4 -2
  4. package/dist/commands/assess.js.map +1 -1
  5. package/dist/commands/generate.js +1 -0
  6. package/dist/commands/generate.js.map +1 -1
  7. package/dist/commands/harvest.d.ts +9 -0
  8. package/dist/commands/harvest.js +76 -0
  9. package/dist/commands/harvest.js.map +1 -0
  10. package/dist/lib/__tests__/learned-rules.test.d.ts +1 -0
  11. package/dist/lib/__tests__/learned-rules.test.js +260 -0
  12. package/dist/lib/__tests__/learned-rules.test.js.map +1 -0
  13. package/dist/lib/__tests__/pr-harvester-types.test.d.ts +1 -0
  14. package/dist/lib/__tests__/pr-harvester-types.test.js +43 -0
  15. package/dist/lib/__tests__/pr-harvester-types.test.js.map +1 -0
  16. package/dist/lib/__tests__/pr-harvester.test.d.ts +1 -0
  17. package/dist/lib/__tests__/pr-harvester.test.js +341 -0
  18. package/dist/lib/__tests__/pr-harvester.test.js.map +1 -0
  19. package/dist/lib/__tests__/rule-harvester.test.d.ts +1 -0
  20. package/dist/lib/__tests__/rule-harvester.test.js +526 -0
  21. package/dist/lib/__tests__/rule-harvester.test.js.map +1 -0
  22. package/dist/lib/claim-extractor.d.ts +1 -0
  23. package/dist/lib/claim-extractor.js +116 -15
  24. package/dist/lib/claim-extractor.js.map +1 -1
  25. package/dist/lib/code-verifier.d.ts +12 -1
  26. package/dist/lib/code-verifier.js +155 -12
  27. package/dist/lib/code-verifier.js.map +1 -1
  28. package/dist/lib/learned-rules/category-map.d.ts +28 -0
  29. package/dist/lib/learned-rules/category-map.js +110 -0
  30. package/dist/lib/learned-rules/category-map.js.map +1 -0
  31. package/dist/lib/learned-rules/index.d.ts +105 -0
  32. package/dist/lib/learned-rules/index.js +198 -0
  33. package/dist/lib/learned-rules/index.js.map +1 -0
  34. package/dist/lib/learned-rules/learned-catalog.d.ts +62 -0
  35. package/dist/lib/learned-rules/learned-catalog.js +161 -0
  36. package/dist/lib/learned-rules/learned-catalog.js.map +1 -0
  37. package/dist/lib/learned-rules/pattern-extractor.d.ts +25 -0
  38. package/dist/lib/learned-rules/pattern-extractor.js +351 -0
  39. package/dist/lib/learned-rules/pattern-extractor.js.map +1 -0
  40. package/dist/lib/learned-rules/rule-codifier.d.ts +41 -0
  41. package/dist/lib/learned-rules/rule-codifier.js +138 -0
  42. package/dist/lib/learned-rules/rule-codifier.js.map +1 -0
  43. package/dist/lib/learned-rules/starter-catalog.d.ts +16 -0
  44. package/dist/lib/learned-rules/starter-catalog.js +402 -0
  45. package/dist/lib/learned-rules/starter-catalog.js.map +1 -0
  46. package/dist/lib/learned-rules/types.d.ts +196 -0
  47. package/dist/lib/learned-rules/types.js +9 -0
  48. package/dist/lib/learned-rules/types.js.map +1 -0
  49. package/dist/lib/learned-rules/validation-harness.d.ts +26 -0
  50. package/dist/lib/learned-rules/validation-harness.js +260 -0
  51. package/dist/lib/learned-rules/validation-harness.js.map +1 -0
  52. package/dist/lib/llm-provider.d.ts +7 -0
  53. package/dist/lib/llm-provider.js +99 -6
  54. package/dist/lib/llm-provider.js.map +1 -1
  55. package/dist/lib/rule-harvester/diff-parser.d.ts +9 -0
  56. package/dist/lib/rule-harvester/diff-parser.js +77 -0
  57. package/dist/lib/rule-harvester/diff-parser.js.map +1 -0
  58. package/dist/lib/rule-harvester/file-selector.d.ts +10 -0
  59. package/dist/lib/rule-harvester/file-selector.js +59 -0
  60. package/dist/lib/rule-harvester/file-selector.js.map +1 -0
  61. package/dist/lib/rule-harvester/ground-truth.d.ts +19 -0
  62. package/dist/lib/rule-harvester/ground-truth.js +156 -0
  63. package/dist/lib/rule-harvester/ground-truth.js.map +1 -0
  64. package/dist/lib/rule-harvester/harvest.d.ts +26 -0
  65. package/dist/lib/rule-harvester/harvest.js +307 -0
  66. package/dist/lib/rule-harvester/harvest.js.map +1 -0
  67. package/dist/lib/rule-harvester/pr-discovery.d.ts +49 -0
  68. package/dist/lib/rule-harvester/pr-discovery.js +168 -0
  69. package/dist/lib/rule-harvester/pr-discovery.js.map +1 -0
  70. package/dist/lib/rule-harvester/pr-harvest.d.ts +53 -0
  71. package/dist/lib/rule-harvester/pr-harvest.js +326 -0
  72. package/dist/lib/rule-harvester/pr-harvest.js.map +1 -0
  73. package/dist/lib/rule-harvester/progress.d.ts +13 -0
  74. package/dist/lib/rule-harvester/progress.js +50 -0
  75. package/dist/lib/rule-harvester/progress.js.map +1 -0
  76. package/dist/lib/rule-harvester/reporter.d.ts +35 -0
  77. package/dist/lib/rule-harvester/reporter.js +46 -0
  78. package/dist/lib/rule-harvester/reporter.js.map +1 -0
  79. package/dist/lib/rule-harvester/rule-generalizer.d.ts +25 -0
  80. package/dist/lib/rule-harvester/rule-generalizer.js +135 -0
  81. package/dist/lib/rule-harvester/rule-generalizer.js.map +1 -0
  82. package/dist/lib/rule-harvester/scanner.d.ts +20 -0
  83. package/dist/lib/rule-harvester/scanner.js +37 -0
  84. package/dist/lib/rule-harvester/scanner.js.map +1 -0
  85. package/dist/runtime/types.d.ts +1 -1
  86. package/dist/sdk/forward-verify.d.ts +3 -1
  87. package/dist/sdk/forward-verify.js +68 -5
  88. package/dist/sdk/forward-verify.js.map +1 -1
  89. package/dist/sdk/index.d.ts +1 -1
  90. package/dist/sdk/index.js +7 -5
  91. package/dist/sdk/index.js.map +1 -1
  92. package/dist/sdk/types.d.ts +21 -0
  93. package/package.json +1 -1
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Validation Harness — tests learned rules before promotion to the formal catalog.
3
+ *
4
+ * Every auto-generated rule must pass validation:
5
+ * 1. Fires correctly on the ORIGINAL code that triggered it
6
+ * 2. Fires correctly on synthetic "known-bad" variations
7
+ * 3. Does NOT fire on synthetic "known-good" variations
8
+ *
9
+ * Only rules with precision >= 0.8 and recall >= 0.5 are promoted.
10
+ */
11
+ import { executeRule } from './rule-codifier.js';
12
+ // ── Thresholds ───────────────────────────────────────────────
13
+ const MIN_PRECISION = 0.8; // At most 20% false positive rate
14
+ const MIN_RECALL = 0.5; // Catch at least half the real bugs
15
+ const MIN_TEST_CASES = 3; // At least 3 test cases required
16
+ // ── Synthetic Test Case Generation ───────────────────────────
17
+ /**
18
+ * Generate synthetic test cases from the original source finding.
19
+ * Creates variations of the original code that should and shouldn't
20
+ * trigger the rule.
21
+ */
22
+ function generateTestCases(rule) {
23
+ const cases = [];
24
+ const pattern = rule.pattern;
25
+ // Test 1: Original code should trigger the rule
26
+ for (const finding of rule.sourceFindings) {
27
+ cases.push({
28
+ description: `Original finding: ${finding.claimDescription}`,
29
+ code: finding.codeSnippet,
30
+ shouldMatch: pattern.matchBehavior === 'presence_is_bad',
31
+ didMatch: false, // Will be filled during validation
32
+ });
33
+ }
34
+ // Test 2: Generate "fixed" version that should NOT trigger
35
+ for (const finding of rule.sourceFindings) {
36
+ const fixedCode = generateFixedCode(finding.codeSnippet, pattern, finding.language);
37
+ if (fixedCode) {
38
+ cases.push({
39
+ description: `Fixed version of: ${finding.claimDescription}`,
40
+ code: fixedCode,
41
+ shouldMatch: false,
42
+ didMatch: false,
43
+ });
44
+ }
45
+ }
46
+ // Test 3: Generate additional "bad" variations
47
+ for (const finding of rule.sourceFindings) {
48
+ const variation = generateBadVariation(finding.codeSnippet, pattern, finding.language);
49
+ if (variation) {
50
+ cases.push({
51
+ description: `Bad variation of: ${finding.claimDescription}`,
52
+ code: variation,
53
+ shouldMatch: pattern.matchBehavior === 'presence_is_bad',
54
+ didMatch: false,
55
+ });
56
+ }
57
+ }
58
+ // Test 4: Known-good code that should never trigger
59
+ const safeCode = generateSafeCode(pattern, rule.sourceFindings[0]?.language ?? 'typescript');
60
+ if (safeCode) {
61
+ cases.push({
62
+ description: 'Known-good code (should not trigger)',
63
+ code: safeCode,
64
+ shouldMatch: false,
65
+ didMatch: false,
66
+ });
67
+ }
68
+ return cases;
69
+ }
70
+ /**
71
+ * Generate a "fixed" version of buggy code.
72
+ * Applies the obvious fix based on the pattern category.
73
+ */
74
+ function generateFixedCode(code, pattern, language) {
75
+ const lang = language.toLowerCase();
76
+ const category = pattern.claimCategory;
77
+ if (category === 'error-handling') {
78
+ // Wrap in try/catch
79
+ if (['ts', 'tsx', 'typescript', 'js', 'jsx', 'javascript'].includes(lang)) {
80
+ return `try {\n${code}\n} catch (error) {\n console.error('Operation failed:', error);\n throw error;\n}`;
81
+ }
82
+ if (['py', 'python'].includes(lang)) {
83
+ const indented = code.split('\n').map(l => ' ' + l).join('\n');
84
+ return `try:\n${indented}\nexcept Exception as e:\n raise`;
85
+ }
86
+ }
87
+ if (category === 'security' && pattern.regexPattern?.includes('SELECT|INSERT|UPDATE|DELETE')) {
88
+ // Replace string interpolation with parameterized query
89
+ if (['ts', 'tsx', 'typescript', 'js', 'jsx', 'javascript'].includes(lang)) {
90
+ return code
91
+ .replace(/`([^`]*)\$\{(\w+)\}([^`]*)`/g, "'$1?' + ',$3'")
92
+ .replace(/(['"])([^'"]*)\1\s*\+\s*(\w+)/g, "'$2?'")
93
+ + '\n// Parameters passed separately: [userId]';
94
+ }
95
+ }
96
+ if (category === 'edge-case' || category === 'correctness') {
97
+ // Add null check before the function body
98
+ if (['ts', 'tsx', 'typescript', 'js', 'jsx', 'javascript'].includes(lang)) {
99
+ const funcMatch = code.match(/(function\s+\w+\s*\([^)]*\)\s*\{)/);
100
+ if (funcMatch) {
101
+ return code.replace(funcMatch[1], funcMatch[1] + '\n if (arguments[0] === null || arguments[0] === undefined) throw new Error("Invalid input");');
102
+ }
103
+ }
104
+ }
105
+ return null;
106
+ }
107
+ /**
108
+ * Generate a "bad" variation of buggy code.
109
+ * Creates a similar but different instance of the same bug class.
110
+ */
111
+ function generateBadVariation(code, pattern, language) {
112
+ const lang = language.toLowerCase();
113
+ if (pattern.claimCategory === 'error-handling') {
114
+ if (['ts', 'tsx', 'typescript', 'js', 'jsx', 'javascript'].includes(lang)) {
115
+ // Generate another async call without error handling
116
+ return `async function processData(input: unknown) {\n const result = await fetch('/api/data');\n return result.json();\n}`;
117
+ }
118
+ }
119
+ if (pattern.claimCategory === 'security') {
120
+ if (['ts', 'tsx', 'typescript', 'js', 'jsx', 'javascript'].includes(lang)) {
121
+ return 'const query = `SELECT * FROM users WHERE id = ${userId}`;';
122
+ }
123
+ }
124
+ return null;
125
+ }
126
+ /**
127
+ * Generate known-good code that should never trigger the rule.
128
+ */
129
+ function generateSafeCode(pattern, language) {
130
+ const lang = language.toLowerCase();
131
+ if (pattern.claimCategory === 'error-handling') {
132
+ if (['ts', 'tsx', 'typescript', 'js', 'jsx', 'javascript'].includes(lang)) {
133
+ return `async function safeOp() {\n try {\n const result = await fetch('/api/data');\n return await result.json();\n } catch (error) {\n console.error('Failed:', error);\n throw error;\n }\n}`;
134
+ }
135
+ }
136
+ if (pattern.claimCategory === 'security') {
137
+ if (['ts', 'tsx', 'typescript', 'js', 'jsx', 'javascript'].includes(lang)) {
138
+ return "const result = await db.query('SELECT * FROM users WHERE id = ?', [userId]);";
139
+ }
140
+ }
141
+ if (pattern.claimCategory === 'edge-case' || pattern.claimCategory === 'correctness') {
142
+ if (['ts', 'tsx', 'typescript', 'js', 'jsx', 'javascript'].includes(lang)) {
143
+ return `function safeProcess(input: string | null) {\n if (input === null || input === undefined) {\n throw new Error('Input required');\n }\n return input.trim();\n}`;
144
+ }
145
+ }
146
+ return null;
147
+ }
148
+ // ── Validation Execution ─────────────────────────────────────
149
+ /**
150
+ * Validate a learned rule against synthetic test cases.
151
+ *
152
+ * @param rule - The candidate rule to validate.
153
+ * @returns The validation result with precision/recall metrics.
154
+ */
155
+ export function validateRule(rule) {
156
+ const testCases = generateTestCases(rule);
157
+ // PR-sourced rules: if we have fewer synthetic cases but the regex fires on
158
+ // the original buggy code, accept it. The merged PR is the human confirmation.
159
+ if (testCases.length < MIN_TEST_CASES && rule.source === 'pr') {
160
+ return validatePRRule(rule, testCases);
161
+ }
162
+ if (testCases.length < MIN_TEST_CASES) {
163
+ return {
164
+ passed: false,
165
+ truePositives: 0,
166
+ falsePositives: 0,
167
+ trueNegatives: 0,
168
+ falseNegatives: 0,
169
+ precision: 0,
170
+ recall: 0,
171
+ validatedAt: new Date().toISOString(),
172
+ testCases,
173
+ };
174
+ }
175
+ // Run each test case
176
+ const language = rule.sourceFindings[0]?.language ?? 'typescript';
177
+ const executedCases = [];
178
+ let tp = 0, fp = 0, tn = 0, fn = 0;
179
+ for (const testCase of testCases) {
180
+ const { fires } = executeRule(rule, testCase.code, language);
181
+ const executed = {
182
+ ...testCase,
183
+ didMatch: fires,
184
+ };
185
+ executedCases.push(executed);
186
+ if (testCase.shouldMatch && fires)
187
+ tp++;
188
+ else if (testCase.shouldMatch && !fires)
189
+ fn++;
190
+ else if (!testCase.shouldMatch && fires)
191
+ fp++;
192
+ else
193
+ tn++;
194
+ }
195
+ const precision = tp + fp > 0 ? tp / (tp + fp) : 0;
196
+ const recall = tp + fn > 0 ? tp / (tp + fn) : 0;
197
+ const passed = precision >= MIN_PRECISION && recall >= MIN_RECALL;
198
+ return {
199
+ passed,
200
+ truePositives: tp,
201
+ falsePositives: fp,
202
+ trueNegatives: tn,
203
+ falseNegatives: fn,
204
+ precision,
205
+ recall,
206
+ validatedAt: new Date().toISOString(),
207
+ testCases: executedCases,
208
+ };
209
+ }
210
+ /**
211
+ * Lightweight validation for PR-sourced rules.
212
+ * The merged PR is the human confirmation — we just need to verify
213
+ * the regex actually fires on the buggy code from the diff.
214
+ */
215
+ function validatePRRule(rule, testCases) {
216
+ const language = rule.sourceFindings[0]?.language ?? 'typescript';
217
+ let firesOnOriginal = false;
218
+ // Test: does the regex fire on any source finding's code snippet?
219
+ for (const finding of rule.sourceFindings) {
220
+ if (!finding.codeSnippet || finding.codeSnippet.startsWith('PR #'))
221
+ continue;
222
+ const { fires } = executeRule(rule, finding.codeSnippet, language);
223
+ if (fires) {
224
+ firesOnOriginal = true;
225
+ break;
226
+ }
227
+ }
228
+ // Also check: does it compile as valid regex?
229
+ let regexValid = false;
230
+ try {
231
+ if (rule.pattern.regexPattern) {
232
+ new RegExp(rule.pattern.regexPattern, 'gi');
233
+ regexValid = true;
234
+ }
235
+ }
236
+ catch { /* invalid */ }
237
+ const passed = firesOnOriginal && regexValid;
238
+ return {
239
+ passed,
240
+ truePositives: firesOnOriginal ? 1 : 0,
241
+ falsePositives: 0,
242
+ trueNegatives: 0,
243
+ falseNegatives: firesOnOriginal ? 0 : 1,
244
+ precision: firesOnOriginal ? 1.0 : 0,
245
+ recall: firesOnOriginal ? 1.0 : 0,
246
+ validatedAt: new Date().toISOString(),
247
+ testCases,
248
+ };
249
+ }
250
+ /**
251
+ * Get the validation thresholds.
252
+ */
253
+ export function getValidationThresholds() {
254
+ return {
255
+ minPrecision: MIN_PRECISION,
256
+ minRecall: MIN_RECALL,
257
+ minTestCases: MIN_TEST_CASES,
258
+ };
259
+ }
260
+ //# sourceMappingURL=validation-harness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation-harness.js","sourceRoot":"","sources":["../../../src/lib/learned-rules/validation-harness.ts"],"names":[],"mappings":"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"}
@@ -10,6 +10,13 @@ export declare class ClaudeCliProvider implements LLMProvider {
10
10
  readonly type: LLMProviderType;
11
11
  complete(opts: LLMCompletionOptions): Promise<LLMResult>;
12
12
  }
13
+ export declare class OllamaProvider implements LLMProvider {
14
+ readonly type: LLMProviderType;
15
+ private readonly baseUrl;
16
+ private readonly model;
17
+ constructor(baseUrl?: string, model?: string);
18
+ complete(opts: LLMCompletionOptions): Promise<LLMResult>;
19
+ }
13
20
  /**
14
21
  * Get the configured LLM provider.
15
22
  *
@@ -1,6 +1,6 @@
1
1
  // ============================================================
2
- // LLM Provider — Abstraction over Anthropic API and Claude CLI
3
- // Toggle via ASSAY_LLM_PROVIDER env var (api|cli|auto).
2
+ // LLM Provider — Abstraction over Anthropic API, Claude CLI, and Ollama
3
+ // Toggle via ASSAY_LLM_PROVIDER env var (api|cli|ollama|auto).
4
4
  // ============================================================
5
5
  import Anthropic from '@anthropic-ai/sdk';
6
6
  import { safeExecWithStdin } from '../runtime/safe-executor.js';
@@ -131,6 +131,92 @@ export class ClaudeCliProvider {
131
131
  };
132
132
  }
133
133
  }
134
+ // ── Ollama Provider (OpenAI-compatible) ──────────────────────
135
+ export class OllamaProvider {
136
+ type = 'ollama';
137
+ baseUrl;
138
+ model;
139
+ constructor(baseUrl, model) {
140
+ this.baseUrl = baseUrl ?? process.env.ASSAY_OLLAMA_BASE_URL ?? 'http://localhost:11434';
141
+ this.model = model ?? process.env.ASSAY_OLLAMA_MODEL ?? 'qwen3.5:9b';
142
+ }
143
+ async complete(opts) {
144
+ const start = Date.now();
145
+ // Use native Ollama API (not OpenAI-compatible) for think:false support
146
+ const url = `${this.baseUrl}/api/chat`;
147
+ // Detect if the prompt expects JSON output (intent extraction, claim verification, etc.)
148
+ // and use Ollama's constrained decoding to force valid JSON.
149
+ const wantsJson = opts.systemPrompt.includes('Return ONLY a JSON')
150
+ || opts.systemPrompt.includes('JSON object')
151
+ || opts.systemPrompt.includes('Respond with JSON')
152
+ || opts.userPrompt.includes('Return ONLY a JSON')
153
+ || opts.userPrompt.includes('respond with a JSON');
154
+ const body = {
155
+ model: this.model,
156
+ messages: [
157
+ { role: 'system', content: opts.systemPrompt },
158
+ { role: 'user', content: opts.userPrompt },
159
+ ],
160
+ stream: false,
161
+ think: false, // Disable thinking mode — Assay needs direct answers
162
+ options: {
163
+ num_predict: opts.maxTokens ?? 16_000,
164
+ ...(opts.temperature !== undefined && { temperature: opts.temperature }),
165
+ },
166
+ };
167
+ // Local models can take 10+ minutes on large prompts.
168
+ // Node's undici has a default headers timeout (~300s) separate from AbortSignal.
169
+ // Use keepalive:false and a generous AbortSignal to avoid premature timeouts.
170
+ const timeoutMs = 30 * 60 * 1000; // 30min for local models
171
+ const controller = new AbortController();
172
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
173
+ let res;
174
+ try {
175
+ res = await fetch(url, {
176
+ method: 'POST',
177
+ headers: {
178
+ 'Content-Type': 'application/json',
179
+ 'Connection': 'keep-alive',
180
+ },
181
+ body: JSON.stringify(body),
182
+ signal: controller.signal,
183
+ // @ts-expect-error -- Node undici dispatcher options
184
+ dispatcher: new (await import('undici')).Agent({
185
+ headersTimeout: timeoutMs,
186
+ bodyTimeout: timeoutMs,
187
+ connectTimeout: 30_000,
188
+ }),
189
+ });
190
+ }
191
+ finally {
192
+ clearTimeout(timer);
193
+ }
194
+ if (!res.ok) {
195
+ const text = await res.text().catch(() => '');
196
+ throw new Error(`Ollama API error ${res.status}: ${text}`);
197
+ }
198
+ const data = await res.json();
199
+ // Native API returns message.content directly
200
+ // Fall back to thinking field if content is empty (model used thinking despite think:false)
201
+ let content = data.message?.content || data.message?.thinking || '';
202
+ // Tuned models may wrap JSON in markdown formatting (bold, code blocks).
203
+ // Strip markdown artifacts so downstream JSON parsers can handle the output.
204
+ content = content
205
+ .replace(/\*\*/g, '') // Remove bold markers
206
+ .replace(/^```(?:json)?\s*\n?/gm, '') // Remove opening code fences
207
+ .replace(/\n?```\s*$/gm, '') // Remove closing code fences
208
+ .trim();
209
+ const promptTokens = data.prompt_eval_count ?? null;
210
+ const completionTokens = data.eval_count ?? null;
211
+ return {
212
+ content,
213
+ inputTokens: promptTokens,
214
+ outputTokens: completionTokens,
215
+ provider: 'ollama',
216
+ durationMs: Date.now() - start,
217
+ };
218
+ }
219
+ }
134
220
  // ── Factory ──────────────────────────────────────────────────
135
221
  let cachedProvider = null;
136
222
  /**
@@ -143,7 +229,10 @@ export function getProvider() {
143
229
  if (cachedProvider)
144
230
  return cachedProvider;
145
231
  const explicit = process.env.ASSAY_LLM_PROVIDER;
146
- if (explicit === 'cli') {
232
+ if (explicit === 'ollama') {
233
+ cachedProvider = new OllamaProvider();
234
+ }
235
+ else if (explicit === 'cli') {
147
236
  if (isInsideClaudeCode()) {
148
237
  throw new Error('ASSAY_LLM_PROVIDER=cli but running inside Claude Code (deadlock).\n' +
149
238
  'Set ANTHROPIC_API_KEY and use ASSAY_LLM_PROVIDER=api, or run from a regular terminal.');
@@ -154,15 +243,19 @@ export function getProvider() {
154
243
  cachedProvider = new AnthropicApiProvider();
155
244
  }
156
245
  else {
157
- // Auto-detect: use API if key is available, otherwise CLI
246
+ // Auto-detect: Ollama if configured, API if key available, else CLI
158
247
  // Never auto-select CLI when inside Claude Code (causes deadlock)
159
- if (process.env.ANTHROPIC_API_KEY) {
248
+ if (process.env.ASSAY_OLLAMA_MODEL) {
249
+ cachedProvider = new OllamaProvider();
250
+ }
251
+ else if (process.env.ANTHROPIC_API_KEY) {
160
252
  cachedProvider = new AnthropicApiProvider();
161
253
  }
162
254
  else if (isInsideClaudeCode()) {
163
255
  throw new Error('No ANTHROPIC_API_KEY set and running inside Claude Code.\n' +
164
256
  'The CLI provider deadlocks when spawned from Claude Code.\n' +
165
- 'Export ANTHROPIC_API_KEY or run Assay from a regular terminal.');
257
+ 'Export ANTHROPIC_API_KEY or run Assay from a regular terminal.\n' +
258
+ 'Or set ASSAY_LLM_PROVIDER=ollama to use a local model.');
166
259
  }
167
260
  else {
168
261
  cachedProvider = new ClaudeCliProvider();
@@ -1 +1 @@
1
- {"version":3,"file":"llm-provider.js","sourceRoot":"","sources":["../../src/lib/llm-provider.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"llm-provider.js","sourceRoot":"","sources":["../../src/lib/llm-provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { DiffHunk } from '../learned-rules/types.js';
2
+ /**
3
+ * Parse a GitHub PR file `patch` string (unified diff format) into typed hunks.
4
+ */
5
+ export declare function parsePatch(filePath: string, patch: string): DiffHunk[];
6
+ /**
7
+ * Filter out noise hunks: import-only, comment-only, whitespace-only, <2 meaningful lines.
8
+ */
9
+ export declare function filterHunks(hunks: DiffHunk[]): DiffHunk[];
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Parse a GitHub PR file `patch` string (unified diff format) into typed hunks.
3
+ */
4
+ export function parsePatch(filePath, patch) {
5
+ if (!patch)
6
+ return [];
7
+ const hunks = [];
8
+ const lines = patch.split('\n');
9
+ let currentHunk = null;
10
+ for (const line of lines) {
11
+ const headerMatch = line.match(/^@@ -(\d+)/);
12
+ if (headerMatch) {
13
+ if (currentHunk) {
14
+ hunks.push(makeHunk(filePath, currentHunk));
15
+ }
16
+ currentHunk = {
17
+ removed: [],
18
+ added: [],
19
+ context: [],
20
+ startLine: parseInt(headerMatch[1], 10),
21
+ };
22
+ continue;
23
+ }
24
+ if (!currentHunk)
25
+ continue;
26
+ if (line.startsWith('-')) {
27
+ currentHunk.removed.push(line.slice(1));
28
+ }
29
+ else if (line.startsWith('+')) {
30
+ currentHunk.added.push(line.slice(1));
31
+ }
32
+ else if (line.startsWith(' ')) {
33
+ currentHunk.context.push(line);
34
+ }
35
+ }
36
+ if (currentHunk) {
37
+ hunks.push(makeHunk(filePath, currentHunk));
38
+ }
39
+ return hunks;
40
+ }
41
+ function makeHunk(file, raw) {
42
+ return {
43
+ file,
44
+ removedLines: raw.removed,
45
+ addedLines: raw.added,
46
+ context: raw.context,
47
+ startLine: raw.startLine,
48
+ };
49
+ }
50
+ /**
51
+ * Filter out noise hunks: import-only, comment-only, whitespace-only, <2 meaningful lines.
52
+ */
53
+ export function filterHunks(hunks) {
54
+ return hunks.filter((hunk) => {
55
+ const removed = hunk.removedLines;
56
+ const added = hunk.addedLines;
57
+ if (Math.max(removed.length, added.length) < 2)
58
+ return false;
59
+ const allImports = [...removed, ...added].every((l) => l.trim().startsWith('import ') || (l.trim().startsWith('export ') && l.includes('from')));
60
+ if (allImports)
61
+ return false;
62
+ const allComments = [...removed, ...added].every((l) => {
63
+ const trimmed = l.trim();
64
+ return trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*') || trimmed === '';
65
+ });
66
+ if (allComments)
67
+ return false;
68
+ const removedNormalized = removed.map((l) => l.replace(/\s+/g, ' ').trim());
69
+ const addedNormalized = added.map((l) => l.replace(/\s+/g, ' ').trim());
70
+ if (removedNormalized.length === addedNormalized.length &&
71
+ removedNormalized.every((l, i) => l === addedNormalized[i])) {
72
+ return false;
73
+ }
74
+ return true;
75
+ });
76
+ }
77
+ //# sourceMappingURL=diff-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-parser.js","sourceRoot":"","sources":["../../../src/lib/rule-harvester/diff-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export type FileCandidate = {
2
+ path: string;
3
+ lineCount: number;
4
+ };
5
+ export type SelectedFile = {
6
+ path: string;
7
+ reason: string;
8
+ priority: number;
9
+ };
10
+ export declare function selectFiles(candidates: FileCandidate[]): SelectedFile[];
@@ -0,0 +1,59 @@
1
+ const MAX_FILES = 20;
2
+ const MAX_LINE_COUNT = 800;
3
+ const SKIP_PATTERNS = [
4
+ /\.test\./,
5
+ /\.spec\./,
6
+ /__tests__\//,
7
+ /\.d\.ts$/,
8
+ /\.config\.(js|ts)$/,
9
+ /tsconfig/,
10
+ /package\.json$/,
11
+ /\/generated\//,
12
+ /node_modules/,
13
+ /tailwind\.config/,
14
+ /next\.config/,
15
+ /vite\.config/,
16
+ /jest\.config/,
17
+ /vitest\.config/,
18
+ /eslint\.config/,
19
+ /prettier\.config/,
20
+ /\.eslintrc/,
21
+ /\.prettierrc/,
22
+ ];
23
+ function shouldSkip(path) {
24
+ return SKIP_PATTERNS.some((pattern) => pattern.test(path));
25
+ }
26
+ function assignPriority(path) {
27
+ if (/\/api\/|\/routes\/|\/middleware\//.test(path)) {
28
+ return { priority: 1, reason: 'API/route/middleware file' };
29
+ }
30
+ if (/\/auth/.test(path)) {
31
+ return { priority: 2, reason: 'Auth-related file' };
32
+ }
33
+ if (/\/db\/|\/database\/|\/models\/|\/entities\//.test(path)) {
34
+ return { priority: 3, reason: 'Database/model file' };
35
+ }
36
+ if (/\/services\/|\/handlers\/|\/controllers\//.test(path)) {
37
+ return { priority: 4, reason: 'Service/handler/controller file' };
38
+ }
39
+ if (/\/lib\/|\/utils\/|\/helpers\//.test(path)) {
40
+ return { priority: 5, reason: 'Library/utility/helper file' };
41
+ }
42
+ if (/\.ts$/.test(path)) {
43
+ return { priority: 6, reason: 'TypeScript file' };
44
+ }
45
+ if (/\.tsx$/.test(path)) {
46
+ return { priority: 7, reason: 'TypeScript React file' };
47
+ }
48
+ return { priority: 99, reason: 'Other file' };
49
+ }
50
+ export function selectFiles(candidates) {
51
+ const filtered = candidates.filter((c) => !shouldSkip(c.path) && c.lineCount <= MAX_LINE_COUNT);
52
+ const scored = filtered.map((c) => {
53
+ const { priority, reason } = assignPriority(c.path);
54
+ return { path: c.path, reason, priority };
55
+ });
56
+ scored.sort((a, b) => a.priority - b.priority);
57
+ return scored.slice(0, MAX_FILES);
58
+ }
59
+ //# sourceMappingURL=file-selector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-selector.js","sourceRoot":"","sources":["../../../src/lib/rule-harvester/file-selector.ts"],"names":[],"mappings":"AAWA,MAAM,SAAS,GAAG,EAAE,CAAC;AACrB,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B,MAAM,aAAa,GAAa;IAC9B,UAAU;IACV,UAAU;IACV,aAAa;IACb,UAAU;IACV,oBAAoB;IACpB,UAAU;IACV,gBAAgB;IAChB,eAAe;IACf,cAAc;IACd,kBAAkB;IAClB,cAAc;IACd,cAAc;IACd,cAAc;IACd,gBAAgB;IAChB,gBAAgB;IAChB,kBAAkB;IAClB,YAAY;IACZ,cAAc;CACf,CAAC;AAEF,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,cAAc,CAAC,IAAY;IAClC,IAAI,mCAAmC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,2BAA2B,EAAE,CAAC;IAC9D,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;IACtD,CAAC;IACD,IAAI,6CAA6C,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7D,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC;IACxD,CAAC;IACD,IAAI,2CAA2C,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3D,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,iCAAiC,EAAE,CAAC;IACpE,CAAC;IACD,IAAI,+BAA+B,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/C,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,6BAA6B,EAAE,CAAC;IAChE,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC;IACpD,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC;IAC1D,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,UAA2B;IACrD,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,CAChC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,SAAS,IAAI,cAAc,CAC5D,CAAC;IAEF,MAAM,MAAM,GAAmB,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAChD,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACpD,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;IAE/C,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AACpC,CAAC"}
@@ -0,0 +1,19 @@
1
+ export interface ClaimForConfirmation {
2
+ claimId: string;
3
+ category: string;
4
+ description: string;
5
+ verdict: string;
6
+ }
7
+ export interface GroundTruthResult {
8
+ claimId: string;
9
+ confirmed: boolean;
10
+ method: 'compiler' | 'test' | 'grep';
11
+ evidence: string;
12
+ }
13
+ export declare function runTscCheck(repoDir: string): {
14
+ errors: string;
15
+ exitCode: number;
16
+ } | null;
17
+ export declare function tscReportsErrorInFile(tscOutput: string, filePath: string): boolean;
18
+ export declare function confirmByGrep(code: string, claim: ClaimForConfirmation): GroundTruthResult;
19
+ export declare function parseConfirmation(code: string, claim: ClaimForConfirmation, tscOutput: string | null, filePath: string): GroundTruthResult;