triage-ai 1.0.2

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 (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +209 -0
  3. package/dist/cli.d.ts +9 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +633 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/mcp-server.d.ts +24 -0
  8. package/dist/mcp-server.d.ts.map +1 -0
  9. package/dist/mcp-server.js +411 -0
  10. package/dist/mcp-server.js.map +1 -0
  11. package/dist/memory.d.ts +40 -0
  12. package/dist/memory.d.ts.map +1 -0
  13. package/dist/memory.js +241 -0
  14. package/dist/memory.js.map +1 -0
  15. package/dist/merge.d.ts +32 -0
  16. package/dist/merge.d.ts.map +1 -0
  17. package/dist/merge.js +251 -0
  18. package/dist/merge.js.map +1 -0
  19. package/dist/models/base.d.ts +72 -0
  20. package/dist/models/base.d.ts.map +1 -0
  21. package/dist/models/base.js +342 -0
  22. package/dist/models/base.js.map +1 -0
  23. package/dist/models/claude.d.ts +23 -0
  24. package/dist/models/claude.d.ts.map +1 -0
  25. package/dist/models/claude.js +30 -0
  26. package/dist/models/claude.js.map +1 -0
  27. package/dist/models/codex.d.ts +25 -0
  28. package/dist/models/codex.d.ts.map +1 -0
  29. package/dist/models/codex.js +34 -0
  30. package/dist/models/codex.js.map +1 -0
  31. package/dist/models/gemini.d.ts +23 -0
  32. package/dist/models/gemini.d.ts.map +1 -0
  33. package/dist/models/gemini.js +32 -0
  34. package/dist/models/gemini.js.map +1 -0
  35. package/dist/patch.d.ts +40 -0
  36. package/dist/patch.d.ts.map +1 -0
  37. package/dist/patch.js +183 -0
  38. package/dist/patch.js.map +1 -0
  39. package/dist/progress.d.ts +71 -0
  40. package/dist/progress.d.ts.map +1 -0
  41. package/dist/progress.js +268 -0
  42. package/dist/progress.js.map +1 -0
  43. package/dist/report.d.ts +19 -0
  44. package/dist/report.d.ts.map +1 -0
  45. package/dist/report.js +245 -0
  46. package/dist/report.js.map +1 -0
  47. package/dist/scanner.d.ts +64 -0
  48. package/dist/scanner.d.ts.map +1 -0
  49. package/dist/scanner.js +645 -0
  50. package/dist/scanner.js.map +1 -0
  51. package/dist/setup.d.ts +52 -0
  52. package/dist/setup.d.ts.map +1 -0
  53. package/dist/setup.js +252 -0
  54. package/dist/setup.js.map +1 -0
  55. package/dist/types.d.ts +153 -0
  56. package/dist/types.d.ts.map +1 -0
  57. package/dist/types.js +203 -0
  58. package/dist/types.js.map +1 -0
  59. package/examples/claude-code-skill.md +22 -0
  60. package/examples/mcp-config.json +9 -0
  61. package/package.json +77 -0
package/dist/memory.js ADDED
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Memory writer — saves triage findings to AI model memory files.
3
+ *
4
+ * After triage runs, findings are written to model-specific memory files
5
+ * so that Claude, Gemini and Codex remember issues in future sessions.
6
+ *
7
+ * Supported memory files:
8
+ * - CLAUDE.md — Claude Code project instructions
9
+ * - GEMINI.md — Gemini CLI project context
10
+ * - AGENTS.md — Codex/OpenAI agent instructions
11
+ */
12
+ import { readFile, writeFile } from 'node:fs/promises';
13
+ import { existsSync } from 'node:fs';
14
+ import { join } from 'node:path';
15
+ import { clusterRepresentative, isConsensus } from './types.js';
16
+ // ---------------------------------------------------------------------------
17
+ // Constants
18
+ // ---------------------------------------------------------------------------
19
+ export const MEMORY_START = '<!-- triage:start -->';
20
+ export const MEMORY_END = '<!-- triage:end -->';
21
+ export const MEMORY_FILES = {
22
+ claude: 'CLAUDE.md',
23
+ gemini: 'GEMINI.md',
24
+ codex: 'AGENTS.md',
25
+ };
26
+ // ---------------------------------------------------------------------------
27
+ // Public API
28
+ // ---------------------------------------------------------------------------
29
+ /**
30
+ * Write triage findings to AI model memory files.
31
+ *
32
+ * Replaces any existing triage section (between markers) or appends
33
+ * a new one. This keeps memory current — old findings are replaced by
34
+ * the latest run, not accumulated forever.
35
+ */
36
+ export async function writeMemory(merged, prompt, root, models) {
37
+ const rootDir = root ?? process.cwd();
38
+ const targetModels = models ?? Object.keys(MEMORY_FILES);
39
+ const content = _buildMemoryContent(merged, prompt);
40
+ if (!content) {
41
+ // No findings — clear any stale triage blocks
42
+ return clearMemory(rootDir, targetModels);
43
+ }
44
+ const results = {};
45
+ for (const model of targetModels) {
46
+ const filename = MEMORY_FILES[model];
47
+ if (!filename)
48
+ continue;
49
+ const filepath = join(rootDir, filename);
50
+ const success = await _updateMemoryFile(filepath, content);
51
+ results[filename] = success;
52
+ if (success) {
53
+ console.log(` Updated ${filename} with triage findings`);
54
+ }
55
+ }
56
+ return results;
57
+ }
58
+ /**
59
+ * Remove triage sections from AI model memory files.
60
+ */
61
+ export async function clearMemory(root, models) {
62
+ const rootDir = root ?? process.cwd();
63
+ const targetModels = models ?? Object.keys(MEMORY_FILES);
64
+ const results = {};
65
+ for (const model of targetModels) {
66
+ const filename = MEMORY_FILES[model];
67
+ if (!filename)
68
+ continue;
69
+ const filepath = join(rootDir, filename);
70
+ if (!existsSync(filepath))
71
+ continue;
72
+ try {
73
+ const existing = await readFile(filepath, 'utf-8');
74
+ if (!existing.includes(MEMORY_START))
75
+ continue;
76
+ if (!existing.includes(MEMORY_END)) {
77
+ console.log(` Warning: ${filename} has start marker but no end marker — skipping`);
78
+ continue;
79
+ }
80
+ // Remove the triage section
81
+ const before = existing.slice(0, existing.indexOf(MEMORY_START)).trimEnd();
82
+ const afterIdx = existing.indexOf(MEMORY_END) + MEMORY_END.length;
83
+ const after = existing.slice(afterIdx).replace(/^\n+/, '');
84
+ let newContent = before;
85
+ if (after) {
86
+ newContent += '\n\n' + after;
87
+ }
88
+ newContent = newContent.trimEnd() + '\n';
89
+ await writeFile(filepath, newContent, 'utf-8');
90
+ results[filename] = true;
91
+ console.log(` Cleared triage section from ${filename}`);
92
+ }
93
+ catch (e) {
94
+ console.log(` Failed to clear ${filename}: ${e}`);
95
+ results[filename] = false;
96
+ }
97
+ }
98
+ return results;
99
+ }
100
+ // ---------------------------------------------------------------------------
101
+ // Internal helpers
102
+ // ---------------------------------------------------------------------------
103
+ /** Build the memory content block from merged results. */
104
+ export function _buildMemoryContent(merged, prompt) {
105
+ const total = merged.blockers.length +
106
+ merged.high.length +
107
+ merged.medium.length +
108
+ merged.low.length;
109
+ if (total === 0)
110
+ return '';
111
+ const now = new Date();
112
+ const timestamp = now.getFullYear() +
113
+ '-' +
114
+ String(now.getMonth() + 1).padStart(2, '0') +
115
+ '-' +
116
+ String(now.getDate()).padStart(2, '0') +
117
+ ' ' +
118
+ String(now.getHours()).padStart(2, '0') +
119
+ ':' +
120
+ String(now.getMinutes()).padStart(2, '0');
121
+ const lines = [];
122
+ lines.push(MEMORY_START);
123
+ lines.push('');
124
+ lines.push('## Triage Findings');
125
+ lines.push('');
126
+ lines.push(`*Last run: ${timestamp} — ${total} issues found, ${merged.consensus.length} consensus*`);
127
+ lines.push('');
128
+ if (prompt) {
129
+ lines.push(`> **Scope:** ${prompt}`);
130
+ lines.push('');
131
+ }
132
+ // Blockers — full detail
133
+ if (merged.blockers.length > 0) {
134
+ lines.push('### Blockers (must fix)');
135
+ lines.push('');
136
+ for (const cluster of merged.blockers) {
137
+ lines.push(..._formatCluster(cluster));
138
+ }
139
+ lines.push('');
140
+ }
141
+ // High priority — full detail
142
+ if (merged.high.length > 0) {
143
+ lines.push('### High Priority');
144
+ lines.push('');
145
+ for (const cluster of merged.high) {
146
+ lines.push(..._formatCluster(cluster));
147
+ }
148
+ lines.push('');
149
+ }
150
+ // Medium — summary only to keep memory concise
151
+ if (merged.medium.length > 0) {
152
+ lines.push('### Medium Priority');
153
+ lines.push('');
154
+ for (const cluster of merged.medium) {
155
+ const f = clusterRepresentative(cluster);
156
+ const consensusTag = isConsensus(cluster) ? ' **(consensus)**' : '';
157
+ lines.push(`- [${f.severity}] ${f.title} — \`${f.location.path}:${f.location.start_line}\`${consensusTag}`);
158
+ }
159
+ lines.push('');
160
+ }
161
+ // Low — just a count
162
+ if (merged.low.length > 0) {
163
+ lines.push(`*Plus ${merged.low.length} low-priority items (S3).*`);
164
+ lines.push('');
165
+ }
166
+ // Patterns to watch from consensus
167
+ if (merged.consensus.length > 0) {
168
+ lines.push('### Patterns to Watch');
169
+ lines.push('');
170
+ lines.push('These issues were flagged by multiple models — avoid introducing similar patterns:');
171
+ lines.push('');
172
+ for (const cluster of merged.consensus) {
173
+ const f = clusterRepresentative(cluster);
174
+ lines.push(`- **${f.title}**: ${f.recommendation}`);
175
+ }
176
+ lines.push('');
177
+ }
178
+ lines.push(MEMORY_END);
179
+ return lines.join('\n');
180
+ }
181
+ /** Format a finding cluster for memory output. */
182
+ export function _formatCluster(cluster) {
183
+ const lines = [];
184
+ const f = clusterRepresentative(cluster);
185
+ const consensusTag = isConsensus(cluster) ? ' **(consensus)**' : '';
186
+ const models = [...cluster.models].sort().join(', ');
187
+ lines.push(`- **[${f.severity}] ${f.title}**${consensusTag}`);
188
+ lines.push(` - Location: \`${f.location.path}:${f.location.start_line}-${f.location.end_line}\``);
189
+ lines.push(` - Models: ${models}`);
190
+ if (f.recommendation) {
191
+ let rec = f.recommendation.slice(0, 200);
192
+ if (f.recommendation.length > 200)
193
+ rec += '...';
194
+ lines.push(` - Fix: ${rec}`);
195
+ }
196
+ return lines;
197
+ }
198
+ /**
199
+ * Update a memory file with triage content.
200
+ *
201
+ * - If the file has an existing triage section, replace it.
202
+ * - If the file exists without a triage section, append.
203
+ * - If the file does not exist, create it with a minimal header.
204
+ */
205
+ export async function _updateMemoryFile(filepath, content) {
206
+ try {
207
+ if (existsSync(filepath)) {
208
+ const existing = await readFile(filepath, 'utf-8');
209
+ let newContent;
210
+ if (existing.includes(MEMORY_START) && existing.includes(MEMORY_END)) {
211
+ // Replace existing triage section
212
+ const before = existing.slice(0, existing.indexOf(MEMORY_START)).trimEnd();
213
+ const afterIdx = existing.indexOf(MEMORY_END) + MEMORY_END.length;
214
+ const after = existing.slice(afterIdx).replace(/^\n+/, '');
215
+ newContent = before + '\n\n' + content;
216
+ if (after) {
217
+ newContent += '\n\n' + after;
218
+ }
219
+ newContent = newContent.trimEnd() + '\n';
220
+ }
221
+ else {
222
+ // Append triage section
223
+ newContent = existing.trimEnd() + '\n\n' + content + '\n';
224
+ }
225
+ await writeFile(filepath, newContent, 'utf-8');
226
+ }
227
+ else {
228
+ // Create new file with minimal header
229
+ const filename = filepath.split('/').pop()?.replace('.md', '') ?? 'Memory';
230
+ const newContent = `# ${filename}\n\n${content}\n`;
231
+ await writeFile(filepath, newContent, 'utf-8');
232
+ }
233
+ return true;
234
+ }
235
+ catch (e) {
236
+ const filename = filepath.split('/').pop() ?? filepath;
237
+ console.log(` Failed to write ${filename}: ${e}`);
238
+ return false;
239
+ }
240
+ }
241
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../src/memory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Merge and deduplicate findings from multiple models.
3
+ *
4
+ * Handles:
5
+ * - Clustering similar findings
6
+ * - Identifying consensus (2+ models agree)
7
+ * - Detecting conflicts/disagreements
8
+ * - Aggregating patches
9
+ */
10
+ import { type Finding, type Patch, type ModelResult, type FindingCluster, type Conflict, type MergedResult } from './types.js';
11
+ export declare class MergeEngine {
12
+ private similarityThreshold;
13
+ constructor(similarityThreshold?: number);
14
+ /**
15
+ * Merge results from multiple models.
16
+ */
17
+ merge(results: ModelResult[]): MergedResult;
18
+ /** Cluster similar findings together using greedy linear clustering. */
19
+ _clusterFindings(findings: Finding[]): FindingCluster[];
20
+ /** Check if a finding should be added to a cluster. */
21
+ _shouldCluster(finding: Finding, cluster: FindingCluster): boolean;
22
+ /** Detect disagreements between models (severity disagreements >= 2 levels). */
23
+ _detectConflicts(clusters: FindingCluster[]): Conflict[];
24
+ /** Aggregate unique patches from all clusters, prioritizing consensus. */
25
+ _aggregatePatches(clusters: FindingCluster[]): Patch[];
26
+ }
27
+ /**
28
+ * Convert a MergedResult to a plain JSON-serializable object.
29
+ * Sets are converted to arrays.
30
+ */
31
+ export declare function mergedResultToDict(merged: MergedResult): Record<string, unknown>;
32
+ //# sourceMappingURL=merge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,KAAK,EACV,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,YAAY,EAMlB,MAAM,YAAY,CAAC;AAMpB,qBAAa,WAAW;IACtB,OAAO,CAAC,mBAAmB,CAAS;gBAExB,mBAAmB,SAAM;IAIrC;;OAEG;IACH,KAAK,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,YAAY;IA0E3C,wEAAwE;IACxE,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE;IA2BvD,uDAAuD;IACvD,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO;IASlE,gFAAgF;IAChF,gBAAgB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,QAAQ,EAAE;IAyBxD,0EAA0E;IAC1E,iBAAiB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,KAAK,EAAE;CAmBvD;AA4CD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgDhF"}
package/dist/merge.js ADDED
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Merge and deduplicate findings from multiple models.
3
+ *
4
+ * Handles:
5
+ * - Clustering similar findings
6
+ * - Identifying consensus (2+ models agree)
7
+ * - Detecting conflicts/disagreements
8
+ * - Aggregating patches
9
+ */
10
+ import { findingsMatch, clusterSeverity, clusterRepresentative, clusterPatches, isConsensus, } from './types.js';
11
+ // ---------------------------------------------------------------------------
12
+ // MergeEngine
13
+ // ---------------------------------------------------------------------------
14
+ export class MergeEngine {
15
+ similarityThreshold;
16
+ constructor(similarityThreshold = 0.6) {
17
+ this.similarityThreshold = similarityThreshold;
18
+ }
19
+ /**
20
+ * Merge results from multiple models.
21
+ */
22
+ merge(results) {
23
+ const merged = {
24
+ blockers: [],
25
+ high: [],
26
+ medium: [],
27
+ low: [],
28
+ consensus: [],
29
+ unique_by_model: {},
30
+ conflicts: [],
31
+ patches: [],
32
+ questions: [],
33
+ summaries: {},
34
+ };
35
+ // Collect all findings
36
+ const allFindings = [];
37
+ for (const result of results) {
38
+ merged.summaries[result.model] = result.summary;
39
+ merged.questions.push(...result.questions);
40
+ for (const finding of result.findings) {
41
+ // Ensure model is set on each finding
42
+ const f = { ...finding, model: result.model };
43
+ allFindings.push(f);
44
+ }
45
+ }
46
+ // Cluster similar findings
47
+ const clusters = this._clusterFindings(allFindings);
48
+ // Categorize by severity and consensus
49
+ for (const cluster of clusters) {
50
+ const severity = clusterSeverity(cluster);
51
+ if (severity === 'S0') {
52
+ merged.blockers.push(cluster);
53
+ }
54
+ else if (severity === 'S1') {
55
+ merged.high.push(cluster);
56
+ }
57
+ else if (severity === 'S2') {
58
+ merged.medium.push(cluster);
59
+ }
60
+ else {
61
+ merged.low.push(cluster);
62
+ }
63
+ if (isConsensus(cluster)) {
64
+ merged.consensus.push(cluster);
65
+ }
66
+ else {
67
+ // Single model finding
68
+ const model = [...cluster.models][0];
69
+ if (!merged.unique_by_model[model]) {
70
+ merged.unique_by_model[model] = [];
71
+ }
72
+ merged.unique_by_model[model].push(...cluster.findings);
73
+ }
74
+ }
75
+ // Detect conflicts
76
+ merged.conflicts = this._detectConflicts(clusters);
77
+ // Aggregate patches
78
+ merged.patches = this._aggregatePatches(clusters);
79
+ // Sort by model count (descending) within each category
80
+ const byModelCount = (a, b) => b.models.size - a.models.size;
81
+ merged.blockers.sort(byModelCount);
82
+ merged.high.sort(byModelCount);
83
+ merged.medium.sort(byModelCount);
84
+ merged.low.sort(byModelCount);
85
+ return merged;
86
+ }
87
+ /** Cluster similar findings together using greedy linear clustering. */
88
+ _clusterFindings(findings) {
89
+ const clusters = [];
90
+ for (const finding of findings) {
91
+ let added = false;
92
+ for (const cluster of clusters) {
93
+ if (this._shouldCluster(finding, cluster)) {
94
+ cluster.findings.push(finding);
95
+ cluster.models.add(finding.model);
96
+ added = true;
97
+ break;
98
+ }
99
+ }
100
+ if (!added) {
101
+ const cluster = {
102
+ findings: [finding],
103
+ models: new Set([finding.model]),
104
+ };
105
+ clusters.push(cluster);
106
+ }
107
+ }
108
+ return clusters;
109
+ }
110
+ /** Check if a finding should be added to a cluster. */
111
+ _shouldCluster(finding, cluster) {
112
+ for (const existing of cluster.findings) {
113
+ if (findingsMatch(finding, existing, this.similarityThreshold)) {
114
+ return true;
115
+ }
116
+ }
117
+ return false;
118
+ }
119
+ /** Detect disagreements between models (severity disagreements >= 2 levels). */
120
+ _detectConflicts(clusters) {
121
+ const conflicts = [];
122
+ const sevValues = { S0: 0, S1: 1, S2: 2, S3: 3 };
123
+ for (const cluster of clusters) {
124
+ if (cluster.findings.length < 2)
125
+ continue;
126
+ const severities = new Set(cluster.findings.map((f) => f.severity));
127
+ if (severities.size > 1) {
128
+ const sevNums = [...severities].map((s) => sevValues[s] ?? 3);
129
+ if (Math.max(...sevNums) - Math.min(...sevNums) >= 2) {
130
+ const rep = clusterRepresentative(cluster);
131
+ conflicts.push({
132
+ title: rep.title,
133
+ findings: cluster.findings,
134
+ conflict_type: 'severity',
135
+ details: `Models disagree on severity: ${JSON.stringify([...severities])}`,
136
+ });
137
+ }
138
+ }
139
+ }
140
+ return conflicts;
141
+ }
142
+ /** Aggregate unique patches from all clusters, prioritizing consensus. */
143
+ _aggregatePatches(clusters) {
144
+ const patches = [];
145
+ const seen = new Set();
146
+ // Sort clusters by model count descending (consensus first)
147
+ const sorted = [...clusters].sort((a, b) => b.models.size - a.models.size);
148
+ for (const cluster of sorted) {
149
+ for (const patch of clusterPatches(cluster)) {
150
+ const key = `${patch.path}|${patch.diff.slice(0, 100)}`;
151
+ if (!seen.has(key)) {
152
+ seen.add(key);
153
+ patches.push(patch);
154
+ }
155
+ }
156
+ }
157
+ return patches;
158
+ }
159
+ }
160
+ // ---------------------------------------------------------------------------
161
+ // Serialisation helper
162
+ // ---------------------------------------------------------------------------
163
+ /** Serialize a FindingCluster to a plain JSON-safe object. */
164
+ function clusterToDict(cluster) {
165
+ const rep = clusterRepresentative(cluster);
166
+ return {
167
+ is_consensus: isConsensus(cluster),
168
+ models: [...cluster.models],
169
+ severity: clusterSeverity(cluster),
170
+ representative: {
171
+ title: rep.title,
172
+ severity: rep.severity,
173
+ confidence: rep.confidence,
174
+ category: rep.category,
175
+ location: rep.location,
176
+ evidence: rep.evidence,
177
+ recommendation: rep.recommendation,
178
+ model: rep.model,
179
+ patch: rep.patch ?? null,
180
+ },
181
+ all_findings: cluster.findings.map((f) => ({
182
+ title: f.title,
183
+ severity: f.severity,
184
+ confidence: f.confidence,
185
+ category: f.category,
186
+ location: f.location,
187
+ evidence: f.evidence,
188
+ recommendation: f.recommendation,
189
+ model: f.model,
190
+ patch: f.patch ?? null,
191
+ })),
192
+ patches: clusterPatches(cluster).map((p) => ({
193
+ path: p.path,
194
+ diff: p.diff,
195
+ description: p.description,
196
+ model: p.model,
197
+ })),
198
+ };
199
+ }
200
+ /**
201
+ * Convert a MergedResult to a plain JSON-serializable object.
202
+ * Sets are converted to arrays.
203
+ */
204
+ export function mergedResultToDict(merged) {
205
+ return {
206
+ blockers: merged.blockers.map(clusterToDict),
207
+ high: merged.high.map(clusterToDict),
208
+ medium: merged.medium.map(clusterToDict),
209
+ low: merged.low.map(clusterToDict),
210
+ consensus: merged.consensus.map(clusterToDict),
211
+ unique_by_model: Object.fromEntries(Object.entries(merged.unique_by_model).map(([model, findings]) => [
212
+ model,
213
+ findings.map((f) => ({
214
+ title: f.title,
215
+ severity: f.severity,
216
+ confidence: f.confidence,
217
+ category: f.category,
218
+ location: f.location,
219
+ evidence: f.evidence,
220
+ recommendation: f.recommendation,
221
+ model: f.model,
222
+ patch: f.patch ?? null,
223
+ })),
224
+ ])),
225
+ conflicts: merged.conflicts.map((c) => ({
226
+ title: c.title,
227
+ type: c.conflict_type,
228
+ details: c.details,
229
+ findings: c.findings.map((f) => ({
230
+ title: f.title,
231
+ severity: f.severity,
232
+ confidence: f.confidence,
233
+ category: f.category,
234
+ location: f.location,
235
+ evidence: f.evidence,
236
+ recommendation: f.recommendation,
237
+ model: f.model,
238
+ patch: f.patch ?? null,
239
+ })),
240
+ })),
241
+ patches: merged.patches.map((p) => ({
242
+ path: p.path,
243
+ diff: p.diff,
244
+ description: p.description,
245
+ model: p.model,
246
+ })),
247
+ questions: merged.questions,
248
+ summaries: merged.summaries,
249
+ };
250
+ }
251
+ //# sourceMappingURL=merge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.js","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Base model interface and subprocess runner.
3
+ *
4
+ * Ported from triage_cli/models/base.py
5
+ */
6
+ import { type ModelResult, type ScanContext } from '../types.js';
7
+ export declare abstract class BaseModel {
8
+ name: string;
9
+ cmdEnvVar: string;
10
+ defaultCmd: string[];
11
+ constructor();
12
+ /** Get the command to use for this model (env override > default). */
13
+ get command(): string[];
14
+ /**
15
+ * Run analysis using this model.
16
+ *
17
+ * Builds prompt, saves it for debugging, calls _runModel, parses the
18
+ * result, saves parsed JSON to resultsDir, returns ModelResult.
19
+ */
20
+ analyze(prompt: string, context: ScanContext, resultsDir: string, timeout?: number, nice?: number): Promise<ModelResult>;
21
+ /** Build the full prompt string from the template and scan context. */
22
+ buildPrompt(prompt: string, context: ScanContext): string;
23
+ /**
24
+ * Parse raw model output into a ModelResult.
25
+ *
26
+ * Tries in order:
27
+ * 1. JSON inside ```json ... ``` blocks
28
+ * 2. JSON inside ``` ... ``` blocks
29
+ * 3. Raw JSON parse of entire output
30
+ * 4. First `{...}` match in the string
31
+ */
32
+ parseOutput(output: string): ModelResult;
33
+ /** Serialize a ModelResult to a plain object for JSON saving. */
34
+ private _resultToDict;
35
+ /** Run the model and return raw string output. Subclasses implement this. */
36
+ abstract _runModel(prompt: string, timeout: number, nice: number): Promise<string>;
37
+ }
38
+ export declare abstract class SubprocessModel extends BaseModel {
39
+ /**
40
+ * Run model via subprocess.
41
+ *
42
+ * - Writes prompt to a temp file (for debugging / codex adapter)
43
+ * - Builds command via _buildCommand()
44
+ * - Applies env overrides (undefined value = delete key)
45
+ * - Spawns with detached:true so we can kill the whole process group
46
+ * - Passes prompt via stdin
47
+ * - Detects auth/quota errors in stderr
48
+ * - Kills process group on error or cancellation
49
+ * - Cleans up temp file in finally
50
+ */
51
+ _runModel(prompt: string, timeout: number, nice: number): Promise<string>;
52
+ /**
53
+ * Safely kill the process group that was created with detached:true.
54
+ * Because detached:true guarantees PGID == proc.pid, we negate the PID.
55
+ * Never targets PID <= 1 (init / our own group).
56
+ */
57
+ private _killProcessGroup;
58
+ /**
59
+ * Build the command and env overrides for this model.
60
+ *
61
+ * @param promptFile Path to temp file containing the prompt text.
62
+ * Most adapters ignore this (they use stdin).
63
+ * The Codex adapter reads it and passes the text as an arg.
64
+ * @returns `cmd` — argument list (no shell, no bash -c);
65
+ * `env` — overrides (undefined value = delete from env)
66
+ */
67
+ abstract _buildCommand(promptFile: string): {
68
+ cmd: string[];
69
+ env: Record<string, string | undefined>;
70
+ };
71
+ }
72
+ //# sourceMappingURL=base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/models/base.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,WAAW,EAGjB,MAAM,aAAa,CAAC;AA6CrB,8BAAsB,SAAS;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,EAAE,CAAC;;IAQrB,sEAAsE;IACtE,IAAI,OAAO,IAAI,MAAM,EAAE,CAItB;IAED;;;;;OAKG;IACG,OAAO,CACX,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,WAAW,EACpB,UAAU,EAAE,MAAM,EAClB,OAAO,SAAM,EACb,IAAI,SAAK,GACR,OAAO,CAAC,WAAW,CAAC;IAsCvB,uEAAuE;IACvE,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,MAAM;IAqDzD;;;;;;;;OAQG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW;IAwCxC,iEAAiE;IACjE,OAAO,CAAC,aAAa;IAWrB,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CACnF;AAMD,8BAAsB,eAAgB,SAAQ,SAAS;IACrD;;;;;;;;;;;OAWG;IACY,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA2GxF;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAczB;;;;;;;;OAQG;IACH,QAAQ,CAAC,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG;QAC1C,GAAG,EAAE,MAAM,EAAE,CAAC;QACd,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;KACzC;CACF"}