ghagga-core 2.0.0

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 (82) hide show
  1. package/README.md +51 -0
  2. package/dist/agents/consensus.d.ts +68 -0
  3. package/dist/agents/consensus.d.ts.map +1 -0
  4. package/dist/agents/consensus.js +216 -0
  5. package/dist/agents/consensus.js.map +1 -0
  6. package/dist/agents/prompts.d.ts +18 -0
  7. package/dist/agents/prompts.d.ts.map +1 -0
  8. package/dist/agents/prompts.js +194 -0
  9. package/dist/agents/prompts.js.map +1 -0
  10. package/dist/agents/simple.d.ts +49 -0
  11. package/dist/agents/simple.d.ts.map +1 -0
  12. package/dist/agents/simple.js +135 -0
  13. package/dist/agents/simple.js.map +1 -0
  14. package/dist/agents/workflow.d.ts +40 -0
  15. package/dist/agents/workflow.d.ts.map +1 -0
  16. package/dist/agents/workflow.js +127 -0
  17. package/dist/agents/workflow.js.map +1 -0
  18. package/dist/index.d.ts +19 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +21 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/memory/context.d.ts +23 -0
  23. package/dist/memory/context.d.ts.map +1 -0
  24. package/dist/memory/context.js +36 -0
  25. package/dist/memory/context.js.map +1 -0
  26. package/dist/memory/persist.d.ts +22 -0
  27. package/dist/memory/persist.d.ts.map +1 -0
  28. package/dist/memory/persist.js +103 -0
  29. package/dist/memory/persist.js.map +1 -0
  30. package/dist/memory/privacy.d.ts +19 -0
  31. package/dist/memory/privacy.d.ts.map +1 -0
  32. package/dist/memory/privacy.js +77 -0
  33. package/dist/memory/privacy.js.map +1 -0
  34. package/dist/memory/search.d.ts +20 -0
  35. package/dist/memory/search.d.ts.map +1 -0
  36. package/dist/memory/search.js +76 -0
  37. package/dist/memory/search.js.map +1 -0
  38. package/dist/pipeline.d.ts +30 -0
  39. package/dist/pipeline.d.ts.map +1 -0
  40. package/dist/pipeline.js +267 -0
  41. package/dist/pipeline.js.map +1 -0
  42. package/dist/providers/fallback.d.ts +46 -0
  43. package/dist/providers/fallback.d.ts.map +1 -0
  44. package/dist/providers/fallback.js +84 -0
  45. package/dist/providers/fallback.js.map +1 -0
  46. package/dist/providers/index.d.ts +40 -0
  47. package/dist/providers/index.d.ts.map +1 -0
  48. package/dist/providers/index.js +76 -0
  49. package/dist/providers/index.js.map +1 -0
  50. package/dist/tools/cpd.d.ts +24 -0
  51. package/dist/tools/cpd.d.ts.map +1 -0
  52. package/dist/tools/cpd.js +130 -0
  53. package/dist/tools/cpd.js.map +1 -0
  54. package/dist/tools/runner.d.ts +19 -0
  55. package/dist/tools/runner.d.ts.map +1 -0
  56. package/dist/tools/runner.js +61 -0
  57. package/dist/tools/runner.js.map +1 -0
  58. package/dist/tools/semgrep.d.ts +12 -0
  59. package/dist/tools/semgrep.d.ts.map +1 -0
  60. package/dist/tools/semgrep.js +97 -0
  61. package/dist/tools/semgrep.js.map +1 -0
  62. package/dist/tools/trivy.d.ts +11 -0
  63. package/dist/tools/trivy.d.ts.map +1 -0
  64. package/dist/tools/trivy.js +74 -0
  65. package/dist/tools/trivy.js.map +1 -0
  66. package/dist/types.d.ts +168 -0
  67. package/dist/types.d.ts.map +1 -0
  68. package/dist/types.js +24 -0
  69. package/dist/types.js.map +1 -0
  70. package/dist/utils/diff.d.ts +53 -0
  71. package/dist/utils/diff.d.ts.map +1 -0
  72. package/dist/utils/diff.js +103 -0
  73. package/dist/utils/diff.js.map +1 -0
  74. package/dist/utils/stack-detect.d.ts +15 -0
  75. package/dist/utils/stack-detect.d.ts.map +1 -0
  76. package/dist/utils/stack-detect.js +54 -0
  77. package/dist/utils/stack-detect.js.map +1 -0
  78. package/dist/utils/token-budget.d.ts +31 -0
  79. package/dist/utils/token-budget.d.ts.map +1 -0
  80. package/dist/utils/token-budget.js +62 -0
  81. package/dist/utils/token-budget.js.map +1 -0
  82. package/package.json +68 -0
@@ -0,0 +1,40 @@
1
+ /**
2
+ * LLM Provider factory using the Vercel AI SDK.
3
+ *
4
+ * Wraps @ai-sdk/anthropic, @ai-sdk/openai, @ai-sdk/google,
5
+ * GitHub Models, and Ollama behind a unified factory so the rest
6
+ * of the codebase doesn't need to know which provider is being used.
7
+ *
8
+ * GitHub Models uses the OpenAI-compatible endpoint at
9
+ * https://models.inference.ai.azure.com and authenticates with a
10
+ * GitHub Personal Access Token (PAT) with `models:read` scope.
11
+ *
12
+ * Ollama runs locally and exposes an OpenAI-compatible endpoint at
13
+ * http://localhost:11434/v1. No API key required.
14
+ */
15
+ import type { LanguageModel } from 'ai';
16
+ import type { LLMProvider } from '../types.js';
17
+ /**
18
+ * Create a provider instance configured with the given API key.
19
+ *
20
+ * Returns the provider's model creator function, which can be called
21
+ * with a model ID to get a LanguageModel instance.
22
+ *
23
+ * @param provider - Provider name ('anthropic' | 'openai' | 'google' | 'github' | 'ollama')
24
+ * @param apiKey - Decrypted API key for the provider
25
+ * @returns The provider's model creator function
26
+ */
27
+ export declare function createProvider(provider: LLMProvider, apiKey: string): import("@ai-sdk/anthropic").AnthropicProvider | import("@ai-sdk/openai").OpenAIProvider | import("@ai-sdk/google").GoogleGenerativeAIProvider;
28
+ /**
29
+ * Create a LanguageModel instance for the given provider + model combo.
30
+ *
31
+ * This is the primary entry point for the rest of the codebase.
32
+ * It handles provider initialization and model creation in one step.
33
+ *
34
+ * @param provider - Provider name
35
+ * @param model - Model identifier (e.g., "claude-sonnet-4-20250514")
36
+ * @param apiKey - Decrypted API key
37
+ * @returns A LanguageModel ready for use with AI SDK's generateText/streamText
38
+ */
39
+ export declare function createModel(provider: LLMProvider, model: string, apiKey: string): LanguageModel;
40
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AACxC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAU/C;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,WAAW,EACrB,MAAM,EAAE,MAAM,iJA2Bf;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,WAAW,EACrB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,aAAa,CAGf"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * LLM Provider factory using the Vercel AI SDK.
3
+ *
4
+ * Wraps @ai-sdk/anthropic, @ai-sdk/openai, @ai-sdk/google,
5
+ * GitHub Models, and Ollama behind a unified factory so the rest
6
+ * of the codebase doesn't need to know which provider is being used.
7
+ *
8
+ * GitHub Models uses the OpenAI-compatible endpoint at
9
+ * https://models.inference.ai.azure.com and authenticates with a
10
+ * GitHub Personal Access Token (PAT) with `models:read` scope.
11
+ *
12
+ * Ollama runs locally and exposes an OpenAI-compatible endpoint at
13
+ * http://localhost:11434/v1. No API key required.
14
+ */
15
+ import { createAnthropic } from '@ai-sdk/anthropic';
16
+ import { createOpenAI } from '@ai-sdk/openai';
17
+ import { createGoogleGenerativeAI } from '@ai-sdk/google';
18
+ /** GitHub Models inference endpoint (OpenAI-compatible) */
19
+ const GITHUB_MODELS_BASE_URL = 'https://models.inference.ai.azure.com';
20
+ /** Ollama local inference endpoint (OpenAI-compatible) */
21
+ const OLLAMA_BASE_URL = 'http://localhost:11434/v1';
22
+ // ─── Provider Factory ───────────────────────────────────────────
23
+ /**
24
+ * Create a provider instance configured with the given API key.
25
+ *
26
+ * Returns the provider's model creator function, which can be called
27
+ * with a model ID to get a LanguageModel instance.
28
+ *
29
+ * @param provider - Provider name ('anthropic' | 'openai' | 'google' | 'github' | 'ollama')
30
+ * @param apiKey - Decrypted API key for the provider
31
+ * @returns The provider's model creator function
32
+ */
33
+ export function createProvider(provider, apiKey) {
34
+ switch (provider) {
35
+ case 'anthropic':
36
+ return createAnthropic({ apiKey });
37
+ case 'openai':
38
+ return createOpenAI({ apiKey });
39
+ case 'google':
40
+ return createGoogleGenerativeAI({ apiKey });
41
+ case 'github':
42
+ return createOpenAI({
43
+ apiKey,
44
+ baseURL: GITHUB_MODELS_BASE_URL,
45
+ name: 'github-models',
46
+ });
47
+ case 'ollama':
48
+ return createOpenAI({
49
+ apiKey: apiKey || 'ollama',
50
+ baseURL: OLLAMA_BASE_URL,
51
+ name: 'ollama',
52
+ });
53
+ default: {
54
+ // Exhaustive check — TypeScript will error if a provider is missing
55
+ const _exhaustive = provider;
56
+ throw new Error(`Unknown provider: ${_exhaustive}`);
57
+ }
58
+ }
59
+ }
60
+ // ─── Model Factory ──────────────────────────────────────────────
61
+ /**
62
+ * Create a LanguageModel instance for the given provider + model combo.
63
+ *
64
+ * This is the primary entry point for the rest of the codebase.
65
+ * It handles provider initialization and model creation in one step.
66
+ *
67
+ * @param provider - Provider name
68
+ * @param model - Model identifier (e.g., "claude-sonnet-4-20250514")
69
+ * @param apiKey - Decrypted API key
70
+ * @returns A LanguageModel ready for use with AI SDK's generateText/streamText
71
+ */
72
+ export function createModel(provider, model, apiKey) {
73
+ const providerInstance = createProvider(provider, apiKey);
74
+ return providerInstance(model);
75
+ }
76
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAI1D,2DAA2D;AAC3D,MAAM,sBAAsB,GAAG,uCAAuC,CAAC;AAEvE,0DAA0D;AAC1D,MAAM,eAAe,GAAG,2BAA2B,CAAC;AAEpD,mEAAmE;AAEnE;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAC5B,QAAqB,EACrB,MAAc;IAEd,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,WAAW;YACd,OAAO,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QACrC,KAAK,QAAQ;YACX,OAAO,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QAClC,KAAK,QAAQ;YACX,OAAO,wBAAwB,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QAC9C,KAAK,QAAQ;YACX,OAAO,YAAY,CAAC;gBAClB,MAAM;gBACN,OAAO,EAAE,sBAAsB;gBAC/B,IAAI,EAAE,eAAe;aACtB,CAAC,CAAC;QACL,KAAK,QAAQ;YACX,OAAO,YAAY,CAAC;gBAClB,MAAM,EAAE,MAAM,IAAI,QAAQ;gBAC1B,OAAO,EAAE,eAAe;gBACxB,IAAI,EAAE,QAAQ;aACf,CAAC,CAAC;QACL,OAAO,CAAC,CAAC,CAAC;YACR,oEAAoE;YACpE,MAAM,WAAW,GAAU,QAAQ,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,qBAAqB,WAAW,EAAE,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;AACH,CAAC;AAED,mEAAmE;AAEnE;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CACzB,QAAqB,EACrB,KAAa,EACb,MAAc;IAEd,MAAM,gBAAgB,GAAG,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC1D,OAAO,gBAAgB,CAAC,KAAK,CAAkB,CAAC;AAClD,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * PMD/CPD (Copy-Paste Detector) for duplicate code detection.
3
+ * Executes cpd as a child process and parses XML output.
4
+ */
5
+ import type { ToolResult, ReviewFinding } from '../types.js';
6
+ /**
7
+ * Simple XML parser for CPD output.
8
+ * CPD XML format:
9
+ * <pmd-cpd>
10
+ * <duplication lines="N" tokens="N">
11
+ * <file path="..." line="N" endline="N" />
12
+ * <file path="..." line="N" endline="N" />
13
+ * <codefragment>...</codefragment>
14
+ * </duplication>
15
+ * </pmd-cpd>
16
+ */
17
+ export declare function parseCpdXml(xml: string, basePath: string): ReviewFinding[];
18
+ /**
19
+ * Run CPD against a directory to find duplicated code.
20
+ */
21
+ export declare function runCpd(scanPath: string, options?: {
22
+ minimumTokens?: number;
23
+ }): Promise<ToolResult>;
24
+ //# sourceMappingURL=cpd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cpd.d.ts","sourceRoot":"","sources":["../../src/tools/cpd.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAM7D;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,EAAE,CAoC1E;AAsBD;;GAEG;AACH,wBAAsB,MAAM,CAC1B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAO,GACvC,OAAO,CAAC,UAAU,CAAC,CAwDrB"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * PMD/CPD (Copy-Paste Detector) for duplicate code detection.
3
+ * Executes cpd as a child process and parses XML output.
4
+ */
5
+ import { execFile } from 'node:child_process';
6
+ import { promisify } from 'node:util';
7
+ const execFileAsync = promisify(execFile);
8
+ const TIMEOUT_MS = 60_000;
9
+ const DEFAULT_MIN_TOKENS = 100;
10
+ /**
11
+ * Simple XML parser for CPD output.
12
+ * CPD XML format:
13
+ * <pmd-cpd>
14
+ * <duplication lines="N" tokens="N">
15
+ * <file path="..." line="N" endline="N" />
16
+ * <file path="..." line="N" endline="N" />
17
+ * <codefragment>...</codefragment>
18
+ * </duplication>
19
+ * </pmd-cpd>
20
+ */
21
+ export function parseCpdXml(xml, basePath) {
22
+ const findings = [];
23
+ const dupRegex = /<duplication lines="(\d+)" tokens="(\d+)">([\s\S]*?)<\/duplication>/g;
24
+ const fileRegex = /<file\s+path="([^"]+)"\s+line="(\d+)"/g;
25
+ let dupMatch;
26
+ while ((dupMatch = dupRegex.exec(xml)) !== null) {
27
+ const lines = parseInt(dupMatch[1], 10);
28
+ const tokens = parseInt(dupMatch[2], 10);
29
+ const inner = dupMatch[3];
30
+ const files = [];
31
+ let fileMatch;
32
+ fileRegex.lastIndex = 0;
33
+ while ((fileMatch = fileRegex.exec(inner)) !== null) {
34
+ files.push({
35
+ path: fileMatch[1].replace(basePath + '/', ''),
36
+ line: parseInt(fileMatch[2], 10),
37
+ });
38
+ }
39
+ if (files.length >= 2) {
40
+ const locations = files.map((f) => `${f.path}:${f.line}`).join(', ');
41
+ findings.push({
42
+ severity: 'medium',
43
+ category: 'duplication',
44
+ file: files[0].path,
45
+ line: files[0].line,
46
+ message: `Duplicated code block (${lines} lines, ${tokens} tokens) found in: ${locations}`,
47
+ suggestion: 'Extract the duplicated code into a shared function or module.',
48
+ source: 'cpd',
49
+ });
50
+ }
51
+ }
52
+ return findings;
53
+ }
54
+ /**
55
+ * Detect the CPD binary name. Supports both `cpd` (standalone)
56
+ * and `pmd cpd` (PMD 7+ CLI).
57
+ */
58
+ async function findCpdBinary() {
59
+ // Try standalone cpd first
60
+ try {
61
+ await execFileAsync('cpd', ['--help'], { timeout: 5_000 });
62
+ return { cmd: 'cpd', args: [] };
63
+ }
64
+ catch {
65
+ // Try pmd CLI (PMD 7+)
66
+ try {
67
+ await execFileAsync('pmd', ['cpd', '--help'], { timeout: 5_000 });
68
+ return { cmd: 'pmd', args: ['cpd'] };
69
+ }
70
+ catch {
71
+ return null;
72
+ }
73
+ }
74
+ }
75
+ /**
76
+ * Run CPD against a directory to find duplicated code.
77
+ */
78
+ export async function runCpd(scanPath, options = {}) {
79
+ const start = Date.now();
80
+ const minimumTokens = options.minimumTokens ?? DEFAULT_MIN_TOKENS;
81
+ const binary = await findCpdBinary();
82
+ if (!binary) {
83
+ return {
84
+ status: 'skipped',
85
+ findings: [],
86
+ error: 'CPD/PMD not available. Install from: https://pmd.github.io',
87
+ executionTimeMs: Date.now() - start,
88
+ };
89
+ }
90
+ try {
91
+ const args = [
92
+ ...binary.args,
93
+ '--format', 'xml',
94
+ '--minimum-tokens', String(minimumTokens),
95
+ '--dir', scanPath,
96
+ '--skip-lexical-errors',
97
+ ];
98
+ const { stdout } = await execFileAsync(binary.cmd, args, {
99
+ timeout: TIMEOUT_MS,
100
+ maxBuffer: 10 * 1024 * 1024,
101
+ });
102
+ const findings = parseCpdXml(stdout, scanPath);
103
+ return {
104
+ status: 'success',
105
+ findings,
106
+ executionTimeMs: Date.now() - start,
107
+ };
108
+ }
109
+ catch (error) {
110
+ // CPD exits with code 4 when duplications are found (not an error)
111
+ if (error && typeof error === 'object' && 'stdout' in error) {
112
+ const stdout = error.stdout;
113
+ if (stdout && stdout.includes('<pmd-cpd')) {
114
+ const findings = parseCpdXml(stdout, scanPath);
115
+ return {
116
+ status: 'success',
117
+ findings,
118
+ executionTimeMs: Date.now() - start,
119
+ };
120
+ }
121
+ }
122
+ return {
123
+ status: 'error',
124
+ findings: [],
125
+ error: `CPD failed: ${error instanceof Error ? error.message : String(error)}`,
126
+ executionTimeMs: Date.now() - start,
127
+ };
128
+ }
129
+ }
130
+ //# sourceMappingURL=cpd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cpd.js","sourceRoot":"","sources":["../../src/tools/cpd.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Static analysis tool runner.
3
+ * Runs Semgrep, Trivy, and CPD in parallel and merges results.
4
+ */
5
+ import type { StaticAnalysisResult, ReviewSettings } from '../types.js';
6
+ /**
7
+ * Run all enabled static analysis tools in parallel.
8
+ *
9
+ * @param files - Map of file paths to file contents (for Semgrep)
10
+ * @param scanPath - Directory path on disk (for Trivy and CPD)
11
+ * @param settings - Which tools are enabled
12
+ */
13
+ export declare function runStaticAnalysis(files: Map<string, string>, scanPath: string, settings: Pick<ReviewSettings, 'enableSemgrep' | 'enableTrivy' | 'enableCpd' | 'customRules'>): Promise<StaticAnalysisResult>;
14
+ /**
15
+ * Format static analysis findings into a prompt context block.
16
+ * This is injected into LLM prompts so agents don't repeat findings.
17
+ */
18
+ export declare function formatStaticAnalysisContext(result: StaticAnalysisResult): string;
19
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/tools/runner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,oBAAoB,EAAE,cAAc,EAAc,MAAM,aAAa,CAAC;AASpF;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC1B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,eAAe,GAAG,aAAa,GAAG,WAAW,GAAG,aAAa,CAAC,GAC5F,OAAO,CAAC,oBAAoB,CAAC,CAkB/B;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAqBhF"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Static analysis tool runner.
3
+ * Runs Semgrep, Trivy, and CPD in parallel and merges results.
4
+ */
5
+ import { runSemgrep } from './semgrep.js';
6
+ import { runTrivy } from './trivy.js';
7
+ import { runCpd } from './cpd.js';
8
+ const SKIPPED_RESULT = {
9
+ status: 'skipped',
10
+ findings: [],
11
+ error: 'Disabled in settings',
12
+ executionTimeMs: 0,
13
+ };
14
+ /**
15
+ * Run all enabled static analysis tools in parallel.
16
+ *
17
+ * @param files - Map of file paths to file contents (for Semgrep)
18
+ * @param scanPath - Directory path on disk (for Trivy and CPD)
19
+ * @param settings - Which tools are enabled
20
+ */
21
+ export async function runStaticAnalysis(files, scanPath, settings) {
22
+ const [semgrepResult, trivyResult, cpdResult] = await Promise.allSettled([
23
+ settings.enableSemgrep ? runSemgrep(files) : Promise.resolve(SKIPPED_RESULT),
24
+ settings.enableTrivy ? runTrivy(scanPath) : Promise.resolve(SKIPPED_RESULT),
25
+ settings.enableCpd ? runCpd(scanPath) : Promise.resolve(SKIPPED_RESULT),
26
+ ]);
27
+ return {
28
+ semgrep: semgrepResult.status === 'fulfilled'
29
+ ? semgrepResult.value
30
+ : { status: 'error', findings: [], error: String(semgrepResult.reason), executionTimeMs: 0 },
31
+ trivy: trivyResult.status === 'fulfilled'
32
+ ? trivyResult.value
33
+ : { status: 'error', findings: [], error: String(trivyResult.reason), executionTimeMs: 0 },
34
+ cpd: cpdResult.status === 'fulfilled'
35
+ ? cpdResult.value
36
+ : { status: 'error', findings: [], error: String(cpdResult.reason), executionTimeMs: 0 },
37
+ };
38
+ }
39
+ /**
40
+ * Format static analysis findings into a prompt context block.
41
+ * This is injected into LLM prompts so agents don't repeat findings.
42
+ */
43
+ export function formatStaticAnalysisContext(result) {
44
+ const allFindings = [
45
+ ...result.semgrep.findings,
46
+ ...result.trivy.findings,
47
+ ...result.cpd.findings,
48
+ ];
49
+ if (allFindings.length === 0)
50
+ return '';
51
+ const lines = ['## Pre-Review Static Analysis (confirmed issues - do NOT repeat these)', ''];
52
+ for (const finding of allFindings) {
53
+ const location = finding.line ? `${finding.file}:${finding.line}` : finding.file;
54
+ lines.push(`- **[${finding.source.toUpperCase()}]** [${finding.severity}] ${location}: ${finding.message}`);
55
+ }
56
+ lines.push('');
57
+ lines.push('> These issues were detected by automated tools. Do NOT repeat them in your review.');
58
+ lines.push('> Focus on logic, architecture, and issues that static analysis cannot detect.');
59
+ return lines.join('\n');
60
+ }
61
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/tools/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Semgrep static analysis tool runner.
3
+ * Executes semgrep as a child process and parses JSON output.
4
+ */
5
+ import type { ToolResult, FindingSeverity } from '../types.js';
6
+ export declare function mapSeverity(semgrepSeverity: string): FindingSeverity;
7
+ /**
8
+ * Run Semgrep against file contents.
9
+ * Files are written to a temp directory, scanned, then cleaned up.
10
+ */
11
+ export declare function runSemgrep(files: Map<string, string>, customRulesPath?: string): Promise<ToolResult>;
12
+ //# sourceMappingURL=semgrep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semgrep.d.ts","sourceRoot":"","sources":["../../src/tools/semgrep.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH,OAAO,KAAK,EAAE,UAAU,EAAiB,eAAe,EAAE,MAAM,aAAa,CAAC;AAsB9E,wBAAgB,WAAW,CAAC,eAAe,EAAE,MAAM,GAAG,eAAe,CAWpE;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC1B,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,UAAU,CAAC,CAwErB"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Semgrep static analysis tool runner.
3
+ * Executes semgrep as a child process and parses JSON output.
4
+ */
5
+ import { execFile } from 'node:child_process';
6
+ import { writeFile, mkdtemp, rm } from 'node:fs/promises';
7
+ import { join } from 'node:path';
8
+ import { tmpdir } from 'node:os';
9
+ import { promisify } from 'node:util';
10
+ import { fileURLToPath } from 'node:url';
11
+ import { dirname } from 'node:path';
12
+ const execFileAsync = promisify(execFile);
13
+ const __dirname = dirname(fileURLToPath(import.meta.url));
14
+ const RULES_PATH = join(__dirname, 'semgrep-rules.yml');
15
+ const TIMEOUT_MS = 60_000;
16
+ export function mapSeverity(semgrepSeverity) {
17
+ switch (semgrepSeverity.toUpperCase()) {
18
+ case 'ERROR':
19
+ return 'high';
20
+ case 'WARNING':
21
+ return 'medium';
22
+ case 'INFO':
23
+ return 'info';
24
+ default:
25
+ return 'low';
26
+ }
27
+ }
28
+ /**
29
+ * Run Semgrep against file contents.
30
+ * Files are written to a temp directory, scanned, then cleaned up.
31
+ */
32
+ export async function runSemgrep(files, customRulesPath) {
33
+ const start = Date.now();
34
+ try {
35
+ // Check if semgrep is available
36
+ await execFileAsync('semgrep', ['--version'], { timeout: 5_000 });
37
+ }
38
+ catch {
39
+ return {
40
+ status: 'skipped',
41
+ findings: [],
42
+ error: 'Semgrep not available. Install with: pip install semgrep',
43
+ executionTimeMs: Date.now() - start,
44
+ };
45
+ }
46
+ let tempDir;
47
+ try {
48
+ // Write files to temp directory
49
+ tempDir = await mkdtemp(join(tmpdir(), 'ghagga-semgrep-'));
50
+ for (const [filePath, content] of files) {
51
+ const fullPath = join(tempDir, filePath);
52
+ const dir = dirname(fullPath);
53
+ await mkdtemp(dir).catch(() => { }); // ignore if exists
54
+ await writeFile(fullPath, content, 'utf8').catch(async () => {
55
+ // Create parent dirs recursively
56
+ const { mkdir } = await import('node:fs/promises');
57
+ await mkdir(dirname(fullPath), { recursive: true });
58
+ await writeFile(fullPath, content, 'utf8');
59
+ });
60
+ }
61
+ // Run semgrep
62
+ const configArgs = ['--config', RULES_PATH];
63
+ if (customRulesPath) {
64
+ configArgs.push('--config', customRulesPath);
65
+ }
66
+ const { stdout } = await execFileAsync('semgrep', ['--json', ...configArgs, tempDir], { timeout: TIMEOUT_MS, maxBuffer: 10 * 1024 * 1024 });
67
+ const result = JSON.parse(stdout);
68
+ const findings = result.results.map((r) => ({
69
+ severity: mapSeverity(r.extra.severity),
70
+ category: 'security',
71
+ file: r.path.replace(tempDir + '/', ''),
72
+ line: r.start.line,
73
+ message: r.extra.message,
74
+ suggestion: undefined,
75
+ source: 'semgrep',
76
+ }));
77
+ return {
78
+ status: 'success',
79
+ findings,
80
+ executionTimeMs: Date.now() - start,
81
+ };
82
+ }
83
+ catch (error) {
84
+ return {
85
+ status: 'error',
86
+ findings: [],
87
+ error: `Semgrep failed: ${error instanceof Error ? error.message : String(error)}`,
88
+ executionTimeMs: Date.now() - start,
89
+ };
90
+ }
91
+ finally {
92
+ if (tempDir) {
93
+ await rm(tempDir, { recursive: true, force: true }).catch(() => { });
94
+ }
95
+ }
96
+ }
97
+ //# sourceMappingURL=semgrep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semgrep.js","sourceRoot":"","sources":["../../src/tools/semgrep.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Trivy dependency vulnerability scanner.
3
+ * Executes trivy as a child process and parses JSON output.
4
+ */
5
+ import type { ToolResult, FindingSeverity } from '../types.js';
6
+ export declare function mapSeverity(trivySeverity: string): FindingSeverity;
7
+ /**
8
+ * Run Trivy against a directory to scan for dependency vulnerabilities.
9
+ */
10
+ export declare function runTrivy(scanPath: string): Promise<ToolResult>;
11
+ //# sourceMappingURL=trivy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trivy.d.ts","sourceRoot":"","sources":["../../src/tools/trivy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAiB,eAAe,EAAE,MAAM,aAAa,CAAC;AAqB9E,wBAAgB,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,eAAe,CAalE;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAuDpE"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Trivy dependency vulnerability scanner.
3
+ * Executes trivy as a child process and parses JSON output.
4
+ */
5
+ import { execFile } from 'node:child_process';
6
+ import { promisify } from 'node:util';
7
+ const execFileAsync = promisify(execFile);
8
+ const TIMEOUT_MS = 120_000; // Trivy can be slow on first run (downloading DB)
9
+ export function mapSeverity(trivySeverity) {
10
+ switch (trivySeverity.toUpperCase()) {
11
+ case 'CRITICAL':
12
+ return 'critical';
13
+ case 'HIGH':
14
+ return 'high';
15
+ case 'MEDIUM':
16
+ return 'medium';
17
+ case 'LOW':
18
+ return 'low';
19
+ default:
20
+ return 'info';
21
+ }
22
+ }
23
+ /**
24
+ * Run Trivy against a directory to scan for dependency vulnerabilities.
25
+ */
26
+ export async function runTrivy(scanPath) {
27
+ const start = Date.now();
28
+ try {
29
+ // Check if trivy is available
30
+ await execFileAsync('trivy', ['--version'], { timeout: 5_000 });
31
+ }
32
+ catch {
33
+ return {
34
+ status: 'skipped',
35
+ findings: [],
36
+ error: 'Trivy not available. Install from: https://trivy.dev',
37
+ executionTimeMs: Date.now() - start,
38
+ };
39
+ }
40
+ try {
41
+ const { stdout } = await execFileAsync('trivy', ['fs', '--format', 'json', '--scanners', 'vuln', '--quiet', scanPath], { timeout: TIMEOUT_MS, maxBuffer: 10 * 1024 * 1024 });
42
+ const result = JSON.parse(stdout);
43
+ const findings = [];
44
+ for (const target of result.Results ?? []) {
45
+ for (const vuln of target.Vulnerabilities ?? []) {
46
+ const fixInfo = vuln.FixedVersion ? ` (fix: upgrade to ${vuln.FixedVersion})` : ' (no fix available)';
47
+ findings.push({
48
+ severity: mapSeverity(vuln.Severity),
49
+ category: 'dependency-vulnerability',
50
+ file: target.Target,
51
+ message: `${vuln.VulnerabilityID}: ${vuln.PkgName}@${vuln.InstalledVersion} - ${vuln.Title ?? vuln.Description ?? 'Known vulnerability'}${fixInfo}`,
52
+ suggestion: vuln.FixedVersion
53
+ ? `Upgrade ${vuln.PkgName} to ${vuln.FixedVersion}`
54
+ : undefined,
55
+ source: 'trivy',
56
+ });
57
+ }
58
+ }
59
+ return {
60
+ status: 'success',
61
+ findings,
62
+ executionTimeMs: Date.now() - start,
63
+ };
64
+ }
65
+ catch (error) {
66
+ return {
67
+ status: 'error',
68
+ findings: [],
69
+ error: `Trivy failed: ${error instanceof Error ? error.message : String(error)}`,
70
+ executionTimeMs: Date.now() - start,
71
+ };
72
+ }
73
+ }
74
+ //# sourceMappingURL=trivy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trivy.js","sourceRoot":"","sources":["../../src/tools/trivy.ts"],"names":[],"mappings":"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"}