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,135 @@
1
+ /**
2
+ * Simple review agent.
3
+ *
4
+ * Runs a single LLM call with the full diff and context.
5
+ * Best for small-to-medium PRs where parallel specialists
6
+ * would be overkill.
7
+ */
8
+ import { generateText } from 'ai';
9
+ import { createModel } from '../providers/index.js';
10
+ import { SIMPLE_REVIEW_SYSTEM, buildMemoryContext, } from './prompts.js';
11
+ // ─── Response Parsing ───────────────────────────────────────────
12
+ /** Valid severity values for type-safe parsing */
13
+ const VALID_SEVERITIES = new Set([
14
+ 'critical',
15
+ 'high',
16
+ 'medium',
17
+ 'low',
18
+ 'info',
19
+ ]);
20
+ /**
21
+ * Parse the structured LLM response into a ReviewResult.
22
+ *
23
+ * Extracts STATUS, SUMMARY, and FINDINGS sections using regex
24
+ * patterns that match the format defined in SIMPLE_REVIEW_SYSTEM.
25
+ */
26
+ function parseReviewResponse(text, provider, model, tokensUsed, executionTimeMs, memoryContext) {
27
+ // Extract STATUS
28
+ const statusMatch = /STATUS:\s*(PASSED|FAILED|NEEDS_HUMAN_REVIEW|SKIPPED)/i.exec(text);
29
+ const status = statusMatch?.[1]?.toUpperCase() ?? 'NEEDS_HUMAN_REVIEW';
30
+ // Extract SUMMARY
31
+ const summaryMatch = /SUMMARY:\s*(.+?)(?:\n(?:FINDINGS:|$))/is.exec(text);
32
+ const summary = summaryMatch?.[1]?.trim() ?? 'Review completed but summary could not be parsed.';
33
+ // Extract FINDINGS
34
+ const findings = parseFindingsBlock(text);
35
+ return {
36
+ status,
37
+ summary,
38
+ findings,
39
+ staticAnalysis: {
40
+ semgrep: { status: 'skipped', findings: [], executionTimeMs: 0 },
41
+ trivy: { status: 'skipped', findings: [], executionTimeMs: 0 },
42
+ cpd: { status: 'skipped', findings: [], executionTimeMs: 0 },
43
+ },
44
+ memoryContext,
45
+ metadata: {
46
+ mode: 'simple',
47
+ provider,
48
+ model,
49
+ tokensUsed,
50
+ executionTimeMs,
51
+ toolsRun: [],
52
+ toolsSkipped: [],
53
+ },
54
+ };
55
+ }
56
+ /**
57
+ * Parse the FINDINGS block from the LLM response.
58
+ *
59
+ * Each finding follows this format:
60
+ * - SEVERITY: critical
61
+ * CATEGORY: security
62
+ * FILE: src/auth.ts
63
+ * LINE: 42
64
+ * MESSAGE: SQL injection vulnerability
65
+ * SUGGESTION: Use parameterized queries
66
+ */
67
+ function parseFindingsBlock(text) {
68
+ const findings = [];
69
+ // Match each finding block
70
+ const findingPattern = /- SEVERITY:\s*(\S+)\s*\n\s*CATEGORY:\s*(\S+)\s*\n\s*FILE:\s*(.+?)\s*\n\s*LINE:\s*(.+?)\s*\n\s*MESSAGE:\s*(.+?)\s*\n\s*SUGGESTION:\s*(.+?)(?=\n\s*- SEVERITY:|\n*$)/gis;
71
+ let match;
72
+ while ((match = findingPattern.exec(text)) !== null) {
73
+ const rawSeverity = match[1].toLowerCase();
74
+ const severity = VALID_SEVERITIES.has(rawSeverity) ? rawSeverity : 'info';
75
+ const lineStr = match[4].trim();
76
+ const line = lineStr === 'N/A' ? undefined : parseInt(lineStr, 10) || undefined;
77
+ findings.push({
78
+ severity,
79
+ category: match[2].trim().toLowerCase(),
80
+ file: match[3].trim(),
81
+ line,
82
+ message: match[5].trim(),
83
+ suggestion: match[6].trim(),
84
+ source: 'ai',
85
+ });
86
+ }
87
+ return findings;
88
+ }
89
+ // ─── Main Function ──────────────────────────────────────────────
90
+ /**
91
+ * Run a simple (single-pass) code review.
92
+ *
93
+ * Combines the system prompt with all context layers (static analysis,
94
+ * memory, stack hints) and the diff into a single LLM call.
95
+ *
96
+ * @param input - Review input with diff, provider config, and context
97
+ * @returns Parsed ReviewResult
98
+ */
99
+ export async function runSimpleReview(input) {
100
+ const { diff, provider, model, apiKey, staticContext, memoryContext, stackHints } = input;
101
+ const emit = input.onProgress ?? (() => { });
102
+ const startTime = Date.now();
103
+ // Build the full system prompt with all context layers
104
+ const system = [
105
+ SIMPLE_REVIEW_SYSTEM,
106
+ staticContext,
107
+ buildMemoryContext(memoryContext),
108
+ stackHints,
109
+ ]
110
+ .filter(Boolean)
111
+ .join('\n');
112
+ // Build the user prompt with the diff
113
+ const prompt = `Please review the following code changes:\n\n\`\`\`diff\n${diff}\n\`\`\``;
114
+ const languageModel = createModel(provider, model, apiKey);
115
+ emit({
116
+ step: 'simple-call',
117
+ message: `Calling ${provider}/${model} for single-pass review...`,
118
+ });
119
+ const result = await generateText({
120
+ model: languageModel,
121
+ system,
122
+ prompt,
123
+ temperature: 0.3,
124
+ });
125
+ const executionTimeMs = Date.now() - startTime;
126
+ const tokensUsed = (result.usage?.promptTokens ?? 0) + (result.usage?.completionTokens ?? 0);
127
+ emit({
128
+ step: 'simple-done',
129
+ message: `Review complete — ${tokensUsed} tokens, ${(executionTimeMs / 1000).toFixed(1)}s`,
130
+ });
131
+ return parseReviewResponse(result.text, provider, model, tokensUsed, executionTimeMs, memoryContext);
132
+ }
133
+ // Re-export the parser for use in workflow and consensus modes
134
+ export { parseReviewResponse, parseFindingsBlock };
135
+ //# sourceMappingURL=simple.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simple.js","sourceRoot":"","sources":["../../src/agents/simple.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Workflow review agent (multi-specialist).
3
+ *
4
+ * Runs 5 specialist reviewers in parallel, then synthesizes their
5
+ * findings into a single unified review. Best for medium-to-large PRs
6
+ * where different aspects need focused attention.
7
+ *
8
+ * Specialists:
9
+ * 1. Scope Analysis — what changed, what's affected
10
+ * 2. Coding Standards — naming, formatting, DRY
11
+ * 3. Error Handling — null safety, edge cases, exceptions
12
+ * 4. Security Audit — injection, XSS, auth, data exposure
13
+ * 5. Performance — complexity, N+1, memory, resources
14
+ *
15
+ * After all specialists complete, a synthesis step merges and
16
+ * deduplicates findings into the final STATUS/SUMMARY/FINDINGS.
17
+ */
18
+ import type { LLMProvider, ProgressCallback, ReviewResult } from '../types.js';
19
+ export interface WorkflowReviewInput {
20
+ diff: string;
21
+ provider: LLMProvider;
22
+ model: string;
23
+ apiKey: string;
24
+ staticContext: string;
25
+ memoryContext: string | null;
26
+ stackHints: string;
27
+ onProgress?: ProgressCallback;
28
+ }
29
+ /**
30
+ * Run a workflow (multi-specialist) code review.
31
+ *
32
+ * 1. Launch 5 specialist reviews in parallel with Promise.allSettled
33
+ * 2. Collect all specialist outputs (including failures)
34
+ * 3. Run a synthesis step to merge findings into a unified review
35
+ *
36
+ * @param input - Review input with diff, provider config, and context
37
+ * @returns Parsed ReviewResult from the synthesis step
38
+ */
39
+ export declare function runWorkflowReview(input: WorkflowReviewInput): Promise<ReviewResult>;
40
+ //# sourceMappingURL=workflow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/agents/workflow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAcH,OAAO,KAAK,EACV,WAAW,EACX,gBAAgB,EAChB,YAAY,EAEb,MAAM,aAAa,CAAC;AAIrB,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,WAAW,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B;AAoBD;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,CAkHzF"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Workflow review agent (multi-specialist).
3
+ *
4
+ * Runs 5 specialist reviewers in parallel, then synthesizes their
5
+ * findings into a single unified review. Best for medium-to-large PRs
6
+ * where different aspects need focused attention.
7
+ *
8
+ * Specialists:
9
+ * 1. Scope Analysis — what changed, what's affected
10
+ * 2. Coding Standards — naming, formatting, DRY
11
+ * 3. Error Handling — null safety, edge cases, exceptions
12
+ * 4. Security Audit — injection, XSS, auth, data exposure
13
+ * 5. Performance — complexity, N+1, memory, resources
14
+ *
15
+ * After all specialists complete, a synthesis step merges and
16
+ * deduplicates findings into the final STATUS/SUMMARY/FINDINGS.
17
+ */
18
+ import { generateText } from 'ai';
19
+ import { createModel } from '../providers/index.js';
20
+ import { WORKFLOW_SCOPE_SYSTEM, WORKFLOW_STANDARDS_SYSTEM, WORKFLOW_ERRORS_SYSTEM, WORKFLOW_SECURITY_SYSTEM, WORKFLOW_PERFORMANCE_SYSTEM, WORKFLOW_SYNTHESIS_SYSTEM, buildMemoryContext, } from './prompts.js';
21
+ import { parseReviewResponse } from './simple.js';
22
+ // ─── Specialist Configuration ───────────────────────────────────
23
+ const SPECIALISTS = [
24
+ { name: 'scope-analysis', label: 'Scope Analysis', system: WORKFLOW_SCOPE_SYSTEM },
25
+ { name: 'coding-standards', label: 'Coding Standards', system: WORKFLOW_STANDARDS_SYSTEM },
26
+ { name: 'error-handling', label: 'Error Handling', system: WORKFLOW_ERRORS_SYSTEM },
27
+ { name: 'security-audit', label: 'Security Audit', system: WORKFLOW_SECURITY_SYSTEM },
28
+ { name: 'performance-review', label: 'Performance', system: WORKFLOW_PERFORMANCE_SYSTEM },
29
+ ];
30
+ // ─── Main Function ──────────────────────────────────────────────
31
+ /**
32
+ * Run a workflow (multi-specialist) code review.
33
+ *
34
+ * 1. Launch 5 specialist reviews in parallel with Promise.allSettled
35
+ * 2. Collect all specialist outputs (including failures)
36
+ * 3. Run a synthesis step to merge findings into a unified review
37
+ *
38
+ * @param input - Review input with diff, provider config, and context
39
+ * @returns Parsed ReviewResult from the synthesis step
40
+ */
41
+ export async function runWorkflowReview(input) {
42
+ const { diff, provider, model, apiKey, staticContext, memoryContext, stackHints } = input;
43
+ const emit = input.onProgress ?? (() => { });
44
+ const startTime = Date.now();
45
+ const languageModel = createModel(provider, model, apiKey);
46
+ emit({
47
+ step: 'workflow-start',
48
+ message: `Launching ${SPECIALISTS.length} specialist reviewers in parallel`,
49
+ detail: SPECIALISTS.map((s) => ` → ${s.label}`).join('\n'),
50
+ });
51
+ // Build the user prompt (same for all specialists)
52
+ const userPrompt = `Review the following code changes:\n\n\`\`\`diff\n${diff}\n\`\`\``;
53
+ // ── Step 1: Run all specialists in parallel ────────────────
54
+ const specialistPromises = SPECIALISTS.map(async (specialist) => {
55
+ const system = [
56
+ specialist.system,
57
+ staticContext,
58
+ buildMemoryContext(memoryContext),
59
+ stackHints,
60
+ ]
61
+ .filter(Boolean)
62
+ .join('\n');
63
+ const result = await generateText({
64
+ model: languageModel,
65
+ system,
66
+ prompt: userPrompt,
67
+ temperature: 0.3,
68
+ });
69
+ return {
70
+ name: specialist.name,
71
+ label: specialist.label,
72
+ text: result.text,
73
+ tokensUsed: (result.usage?.promptTokens ?? 0) + (result.usage?.completionTokens ?? 0),
74
+ };
75
+ });
76
+ const results = await Promise.allSettled(specialistPromises);
77
+ // ── Step 2: Collect results ────────────────────────────────
78
+ let totalTokens = 0;
79
+ const specialistOutputs = [];
80
+ for (let i = 0; i < results.length; i++) {
81
+ const result = results[i];
82
+ const spec = SPECIALISTS[i];
83
+ if (result.status === 'fulfilled') {
84
+ totalTokens += result.value.tokensUsed;
85
+ specialistOutputs.push(`### ${result.value.label}\n\n${result.value.text}`);
86
+ emit({
87
+ step: `specialist-${spec.name}`,
88
+ message: `✓ ${spec.label} — ${result.value.tokensUsed} tokens`,
89
+ detail: result.value.text,
90
+ });
91
+ }
92
+ else {
93
+ // Include error information in synthesis so it's aware of gaps
94
+ specialistOutputs.push(`### [FAILED] Specialist\n\nThis specialist could not complete: ${String(result.reason)}`);
95
+ emit({
96
+ step: `specialist-${spec.name}`,
97
+ message: `✗ ${spec.label} — FAILED: ${String(result.reason)}`,
98
+ });
99
+ }
100
+ }
101
+ emit({
102
+ step: 'workflow-synthesis',
103
+ message: `Synthesizing ${specialistOutputs.length} specialist outputs...`,
104
+ });
105
+ // ── Step 3: Synthesis ──────────────────────────────────────
106
+ const synthesisPrompt = [
107
+ 'Below are the findings from 5 specialist reviewers. Synthesize them into a final review.\n',
108
+ ...specialistOutputs,
109
+ '\n\n---\n\nNow provide the unified review in the required format.',
110
+ ].join('\n\n');
111
+ const synthesisResult = await generateText({
112
+ model: languageModel,
113
+ system: WORKFLOW_SYNTHESIS_SYSTEM,
114
+ prompt: synthesisPrompt,
115
+ temperature: 0.3,
116
+ });
117
+ const synthesisTokens = (synthesisResult.usage?.promptTokens ?? 0) +
118
+ (synthesisResult.usage?.completionTokens ?? 0);
119
+ totalTokens += synthesisTokens;
120
+ const executionTimeMs = Date.now() - startTime;
121
+ // Parse the synthesis output using the same parser as simple mode
122
+ const reviewResult = parseReviewResponse(synthesisResult.text, provider, model, totalTokens, executionTimeMs, memoryContext);
123
+ // Override mode in metadata
124
+ reviewResult.metadata.mode = 'workflow';
125
+ return reviewResult;
126
+ }
127
+ //# sourceMappingURL=workflow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow.js","sourceRoot":"","sources":["../../src/agents/workflow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @ghagga/core — Public API
3
+ *
4
+ * The core review engine for GHAGGA v2.
5
+ * This module re-exports the pipeline entry point and all public types.
6
+ */
7
+ export { reviewPipeline } from './pipeline.js';
8
+ export type { ReviewInput, ReviewSettings, ReviewContext, ReviewMode, LLMProvider, ReviewLevel, ReviewResult, ReviewFinding, ReviewMetadata, ReviewStatus, FindingSeverity, FindingSource, StaticAnalysisResult, ToolResult, ToolStatus, WorkflowSpecialist, ConsensusStance, ConsensusVote, ObservationType, MemoryObservation, ProgressCallback, ProgressEvent, } from './types.js';
9
+ export { DEFAULT_SETTINGS, DEFAULT_MODELS } from './types.js';
10
+ export { detectStacks } from './utils/stack-detect.js';
11
+ export { parseDiffFiles, filterIgnoredFiles, truncateDiff } from './utils/diff.js';
12
+ export type { DiffFile } from './utils/diff.js';
13
+ export { getContextWindow, calculateTokenBudget } from './utils/token-budget.js';
14
+ export { createProvider, createModel } from './providers/index.js';
15
+ export { generateWithFallback } from './providers/fallback.js';
16
+ export type { FallbackOptions, FallbackResult } from './providers/fallback.js';
17
+ export { formatMemoryContext } from './memory/context.js';
18
+ export { stripPrivateData } from './memory/privacy.js';
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAI/C,YAAY,EAEV,WAAW,EACX,cAAc,EACd,aAAa,EACb,UAAU,EACV,WAAW,EACX,WAAW,EAGX,YAAY,EACZ,aAAa,EACb,cAAc,EACd,YAAY,EACZ,eAAe,EACf,aAAa,EAGb,oBAAoB,EACpB,UAAU,EACV,UAAU,EAGV,kBAAkB,EAClB,eAAe,EACf,aAAa,EAGb,eAAe,EACf,iBAAiB,EAGjB,gBAAgB,EAChB,aAAa,GACd,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAI9D,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACnF,YAAY,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAIjF,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAI/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @ghagga/core — Public API
3
+ *
4
+ * The core review engine for GHAGGA v2.
5
+ * This module re-exports the pipeline entry point and all public types.
6
+ */
7
+ // ─── Pipeline ───────────────────────────────────────────────────
8
+ export { reviewPipeline } from './pipeline.js';
9
+ // ─── Constants ──────────────────────────────────────────────────
10
+ export { DEFAULT_SETTINGS, DEFAULT_MODELS } from './types.js';
11
+ // ─── Utilities (for advanced usage) ─────────────────────────────
12
+ export { detectStacks } from './utils/stack-detect.js';
13
+ export { parseDiffFiles, filterIgnoredFiles, truncateDiff } from './utils/diff.js';
14
+ export { getContextWindow, calculateTokenBudget } from './utils/token-budget.js';
15
+ // ─── Providers (for direct model access) ────────────────────────
16
+ export { createProvider, createModel } from './providers/index.js';
17
+ export { generateWithFallback } from './providers/fallback.js';
18
+ // ─── Memory (for custom memory integrations) ────────────────────
19
+ export { formatMemoryContext } from './memory/context.js';
20
+ export { stripPrivateData } from './memory/privacy.js';
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,mEAAmE;AAEnE,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAwC/C,mEAAmE;AAEnE,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE9D,mEAAmE;AAEnE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEnF,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAEjF,mEAAmE;AAEnE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAG/D,mEAAmE;AAEnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Format memory observations for prompt injection.
3
+ *
4
+ * Takes raw observation objects from the database and formats them
5
+ * into a human-readable context block that can be appended to
6
+ * agent system prompts.
7
+ */
8
+ export interface ObservationForContext {
9
+ type: string;
10
+ title: string;
11
+ content: string;
12
+ }
13
+ /**
14
+ * Format an array of memory observations into a prompt-ready string.
15
+ *
16
+ * The output is structured with headers and bullet points so the LLM
17
+ * can easily parse and reference past knowledge during its review.
18
+ *
19
+ * @param observations - Array of observation objects with type, title, and content
20
+ * @returns Formatted string for prompt injection, or empty string if no observations
21
+ */
22
+ export declare function formatMemoryContext(observations: ObservationForContext[]): string;
23
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/memory/context.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAID;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,qBAAqB,EAAE,GACpC,MAAM,CAuBR"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Format memory observations for prompt injection.
3
+ *
4
+ * Takes raw observation objects from the database and formats them
5
+ * into a human-readable context block that can be appended to
6
+ * agent system prompts.
7
+ */
8
+ // ─── Main Function ──────────────────────────────────────────────
9
+ /**
10
+ * Format an array of memory observations into a prompt-ready string.
11
+ *
12
+ * The output is structured with headers and bullet points so the LLM
13
+ * can easily parse and reference past knowledge during its review.
14
+ *
15
+ * @param observations - Array of observation objects with type, title, and content
16
+ * @returns Formatted string for prompt injection, or empty string if no observations
17
+ */
18
+ export function formatMemoryContext(observations) {
19
+ if (observations.length === 0)
20
+ return '';
21
+ const lines = [
22
+ '## Past Review Memory',
23
+ '',
24
+ 'The following observations were learned from previous reviews of this project:',
25
+ '',
26
+ ];
27
+ for (const obs of observations) {
28
+ lines.push(`### [${obs.type.toUpperCase()}] ${obs.title}`);
29
+ lines.push('');
30
+ lines.push(obs.content);
31
+ lines.push('');
32
+ }
33
+ lines.push('> Use these past observations to give more informed, context-aware reviews.', '> Do not repeat findings that match these known patterns unless the issue persists.');
34
+ return lines.join('\n');
35
+ }
36
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/memory/context.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,mEAAmE;AAEnE;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CACjC,YAAqC;IAErC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEzC,MAAM,KAAK,GAAa;QACtB,uBAAuB;QACvB,EAAE;QACF,gFAAgF;QAChF,EAAE;KACH,CAAC;IAEF,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;QAC3D,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,IAAI,CACR,6EAA6E,EAC7E,qFAAqF,CACtF,CAAC;IAEF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Persist review observations to memory.
3
+ *
4
+ * After a review completes, this module extracts key findings
5
+ * and saves them as memory observations so future reviews of
6
+ * the same project can benefit from past context.
7
+ */
8
+ import type { ReviewResult } from '../types.js';
9
+ /**
10
+ * Persist notable review findings as memory observations.
11
+ *
12
+ * Creates a memory session for the review, extracts significant
13
+ * findings, strips private data, and saves them via ghagga-db.
14
+ * Gracefully handles database errors without propagating them.
15
+ *
16
+ * @param db - Database instance (typed as unknown for loose coupling)
17
+ * @param project - Project identifier (e.g., "owner/repo")
18
+ * @param prNumber - Pull request number
19
+ * @param result - The completed review result
20
+ */
21
+ export declare function persistReviewObservations(db: unknown, project: string, prNumber: number, result: ReviewResult): Promise<void>;
22
+ //# sourceMappingURL=persist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../src/memory/persist.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAkC,MAAM,aAAa,CAAC;AAoChF;;;;;;;;;;;GAWG;AACH,wBAAsB,yBAAyB,CAC7C,EAAE,EAAE,OAAO,EACX,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,IAAI,CAAC,CA+Df"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Persist review observations to memory.
3
+ *
4
+ * After a review completes, this module extracts key findings
5
+ * and saves them as memory observations so future reviews of
6
+ * the same project can benefit from past context.
7
+ */
8
+ import { saveObservation, createMemorySession, endMemorySession } from 'ghagga-db';
9
+ import { stripPrivateData } from './privacy.js';
10
+ // ─── Helpers ────────────────────────────────────────────────────
11
+ /**
12
+ * Map a finding's category to an observation type.
13
+ * This determines how the finding is stored and retrieved.
14
+ */
15
+ function findingToObservationType(finding) {
16
+ switch (finding.category) {
17
+ case 'security':
18
+ return 'discovery';
19
+ case 'bug':
20
+ return 'bugfix';
21
+ case 'performance':
22
+ return 'pattern';
23
+ case 'style':
24
+ case 'maintainability':
25
+ return 'pattern';
26
+ case 'error-handling':
27
+ return 'learning';
28
+ default:
29
+ return 'learning';
30
+ }
31
+ }
32
+ /**
33
+ * Check if a finding is significant enough to persist.
34
+ * We only save high and critical findings to avoid noise.
35
+ */
36
+ function isSignificantFinding(finding) {
37
+ return finding.severity === 'critical' || finding.severity === 'high';
38
+ }
39
+ // ─── Main Function ──────────────────────────────────────────────
40
+ /**
41
+ * Persist notable review findings as memory observations.
42
+ *
43
+ * Creates a memory session for the review, extracts significant
44
+ * findings, strips private data, and saves them via ghagga-db.
45
+ * Gracefully handles database errors without propagating them.
46
+ *
47
+ * @param db - Database instance (typed as unknown for loose coupling)
48
+ * @param project - Project identifier (e.g., "owner/repo")
49
+ * @param prNumber - Pull request number
50
+ * @param result - The completed review result
51
+ */
52
+ export async function persistReviewObservations(db, project, prNumber, result) {
53
+ try {
54
+ if (!db)
55
+ return;
56
+ const typedDb = db;
57
+ // Create a memory session for this review
58
+ const session = await createMemorySession(typedDb, { project, prNumber });
59
+ // Extract significant findings as observations
60
+ const significantFindings = result.findings.filter(isSignificantFinding);
61
+ for (const finding of significantFindings) {
62
+ const sanitizedMessage = stripPrivateData(finding.message);
63
+ const sanitizedSuggestion = finding.suggestion
64
+ ? stripPrivateData(finding.suggestion)
65
+ : undefined;
66
+ const content = [
67
+ `[${finding.severity.toUpperCase()}] ${finding.category}`,
68
+ `File: ${finding.file}${finding.line ? `:${finding.line}` : ''}`,
69
+ `Issue: ${sanitizedMessage}`,
70
+ sanitizedSuggestion ? `Fix: ${sanitizedSuggestion}` : '',
71
+ ]
72
+ .filter(Boolean)
73
+ .join('\n');
74
+ await saveObservation(typedDb, {
75
+ sessionId: session.id,
76
+ project,
77
+ type: findingToObservationType(finding),
78
+ title: `${finding.category}: ${sanitizedMessage.slice(0, 80)}`,
79
+ content,
80
+ filePaths: [finding.file],
81
+ });
82
+ }
83
+ // Save a summary observation for the overall review
84
+ if (significantFindings.length > 0) {
85
+ await saveObservation(typedDb, {
86
+ sessionId: session.id,
87
+ project,
88
+ type: 'decision',
89
+ title: `PR #${prNumber} review: ${result.status}`,
90
+ content: stripPrivateData(result.summary),
91
+ topicKey: `pr-${prNumber}-review`,
92
+ filePaths: significantFindings.map((f) => f.file),
93
+ });
94
+ }
95
+ // End the session with a summary
96
+ await endMemorySession(typedDb, session.id, `Review of PR #${prNumber}: ${result.status} with ${significantFindings.length} significant findings.`);
97
+ }
98
+ catch (error) {
99
+ // Memory persistence is optional — never let it break the pipeline
100
+ console.warn('[ghagga] Failed to persist review observations (non-fatal):', error instanceof Error ? error.message : String(error));
101
+ }
102
+ }
103
+ //# sourceMappingURL=persist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persist.js","sourceRoot":"","sources":["../../src/memory/persist.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Privacy-safe text sanitization.
3
+ *
4
+ * Strips sensitive data (API keys, tokens, passwords) from text
5
+ * before it gets persisted to memory. This ensures that even if
6
+ * a diff contains credentials, they won't be stored in the database.
7
+ */
8
+ /**
9
+ * Strip sensitive data from text before persisting to memory.
10
+ *
11
+ * Applies all known secret patterns and replaces matches with
12
+ * human-readable redaction labels. The patterns are applied in
13
+ * order, so more specific patterns take precedence.
14
+ *
15
+ * @param text - The text to sanitize
16
+ * @returns Sanitized text with secrets replaced by redaction labels
17
+ */
18
+ export declare function stripPrivateData(text: string): string;
19
+ //# sourceMappingURL=privacy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"privacy.d.ts","sourceRoot":"","sources":["../../src/memory/privacy.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAkEH;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUrD"}