ghagga-core 2.1.0 → 2.3.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.
- package/dist/agents/consensus.d.ts +1 -1
- package/dist/agents/consensus.d.ts.map +1 -1
- package/dist/agents/consensus.js +7 -5
- package/dist/agents/consensus.js.map +1 -1
- package/dist/agents/prompts.d.ts.map +1 -1
- package/dist/agents/prompts.js +1 -3
- package/dist/agents/prompts.js.map +1 -1
- package/dist/agents/simple.d.ts +1 -1
- package/dist/agents/simple.d.ts.map +1 -1
- package/dist/agents/simple.js +12 -17
- package/dist/agents/simple.js.map +1 -1
- package/dist/agents/workflow.d.ts +1 -1
- package/dist/agents/workflow.d.ts.map +1 -1
- package/dist/agents/workflow.js +5 -4
- package/dist/agents/workflow.js.map +1 -1
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +2 -4
- package/dist/format.js.map +1 -1
- package/dist/index.d.ts +10 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -7
- package/dist/index.js.map +1 -1
- package/dist/memory/context.d.ts.map +1 -1
- package/dist/memory/context.js.map +1 -1
- package/dist/memory/engram-client.d.ts +38 -0
- package/dist/memory/engram-client.d.ts.map +1 -0
- package/dist/memory/engram-client.js +156 -0
- package/dist/memory/engram-client.js.map +1 -0
- package/dist/memory/engram-mapping.d.ts +83 -0
- package/dist/memory/engram-mapping.d.ts.map +1 -0
- package/dist/memory/engram-mapping.js +159 -0
- package/dist/memory/engram-mapping.js.map +1 -0
- package/dist/memory/engram-types.d.ts +51 -0
- package/dist/memory/engram-types.d.ts.map +1 -0
- package/dist/memory/engram-types.js +12 -0
- package/dist/memory/engram-types.js.map +1 -0
- package/dist/memory/engram.d.ts +75 -0
- package/dist/memory/engram.d.ts.map +1 -0
- package/dist/memory/engram.js +193 -0
- package/dist/memory/engram.js.map +1 -0
- package/dist/memory/persist.d.ts.map +1 -1
- package/dist/memory/persist.js +1 -1
- package/dist/memory/persist.js.map +1 -1
- package/dist/memory/privacy.d.ts.map +1 -1
- package/dist/memory/privacy.js +4 -1
- package/dist/memory/privacy.js.map +1 -1
- package/dist/memory/search.d.ts +26 -0
- package/dist/memory/search.d.ts.map +1 -1
- package/dist/memory/search.js +55 -8
- package/dist/memory/search.js.map +1 -1
- package/dist/memory/sqlite.d.ts +7 -2
- package/dist/memory/sqlite.d.ts.map +1 -1
- package/dist/memory/sqlite.js +42 -34
- package/dist/memory/sqlite.js.map +1 -1
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +39 -14
- package/dist/pipeline.js.map +1 -1
- package/dist/providers/fallback.d.ts.map +1 -1
- package/dist/providers/fallback.js +5 -3
- package/dist/providers/fallback.js.map +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +1 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/tools/cpd.d.ts +1 -1
- package/dist/tools/cpd.d.ts.map +1 -1
- package/dist/tools/cpd.js +20 -15
- package/dist/tools/cpd.js.map +1 -1
- package/dist/tools/runner.d.ts +1 -1
- package/dist/tools/runner.d.ts.map +1 -1
- package/dist/tools/runner.js +2 -4
- package/dist/tools/runner.js.map +1 -1
- package/dist/tools/semgrep.d.ts +1 -1
- package/dist/tools/semgrep.d.ts.map +1 -1
- package/dist/tools/semgrep.js +20 -14
- package/dist/tools/semgrep.js.map +1 -1
- package/dist/tools/trivy.d.ts +1 -1
- package/dist/tools/trivy.d.ts.map +1 -1
- package/dist/tools/trivy.js +3 -1
- package/dist/tools/trivy.js.map +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/diff.js +1 -1
- package/dist/utils/diff.js.map +1 -1
- package/package.json +7 -7
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Engram-backed memory storage.
|
|
3
|
+
*
|
|
4
|
+
* Implements the MemoryStorage interface by mapping each method to HTTP
|
|
5
|
+
* calls against Engram's REST API (localhost:7437). Uses native fetch()
|
|
6
|
+
* with no external dependencies.
|
|
7
|
+
*
|
|
8
|
+
* Graceful degradation: the async factory performs a health check. If
|
|
9
|
+
* Engram is unreachable it returns null so the caller can fall back to
|
|
10
|
+
* SQLite. During operation, individual method calls catch HTTP errors
|
|
11
|
+
* and return safe defaults — the review never fails because of Engram.
|
|
12
|
+
*/
|
|
13
|
+
import { EngramClient } from './engram-client.js';
|
|
14
|
+
import { toEngramSaveData, toMemoryObservationDetail, toMemoryObservationRow, } from './engram-mapping.js';
|
|
15
|
+
export class EngramMemoryStorage {
|
|
16
|
+
client;
|
|
17
|
+
/**
|
|
18
|
+
* Maps GHAGGA numeric IDs → Engram original IDs (string | number).
|
|
19
|
+
* Populated by searchObservations(), listObservations(), and saveObservation().
|
|
20
|
+
* Used by getObservation() and deleteObservation() to resolve the real Engram ID.
|
|
21
|
+
*/
|
|
22
|
+
idMap = new Map();
|
|
23
|
+
constructor(client) {
|
|
24
|
+
this.client = client;
|
|
25
|
+
}
|
|
26
|
+
// ─── Factory ────────────────────────────────────────────────────
|
|
27
|
+
/**
|
|
28
|
+
* Async factory — creates an instance after verifying Engram is reachable.
|
|
29
|
+
*
|
|
30
|
+
* Config resolution order:
|
|
31
|
+
* 1. Explicit `config` parameter
|
|
32
|
+
* 2. Environment variables (GHAGGA_ENGRAM_HOST, GHAGGA_ENGRAM_TIMEOUT)
|
|
33
|
+
* 3. Defaults (http://localhost:7437, 5000ms)
|
|
34
|
+
*
|
|
35
|
+
* Returns null if Engram is not reachable (caller should fall back to SQLite).
|
|
36
|
+
*/
|
|
37
|
+
static async create(config) {
|
|
38
|
+
const fullConfig = {
|
|
39
|
+
host: config?.host ?? process.env.GHAGGA_ENGRAM_HOST ?? 'http://localhost:7437',
|
|
40
|
+
timeout: config?.timeout ?? Number(process.env.GHAGGA_ENGRAM_TIMEOUT ?? '5') * 1000,
|
|
41
|
+
};
|
|
42
|
+
const client = new EngramClient(fullConfig);
|
|
43
|
+
const healthy = await client.healthCheck();
|
|
44
|
+
if (!healthy) {
|
|
45
|
+
console.warn('[ghagga:engram] Engram server not reachable at %s — falling back to SQLite', fullConfig.host);
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return new EngramMemoryStorage(client);
|
|
49
|
+
}
|
|
50
|
+
// ─── Hot-path methods ───────────────────────────────────────────
|
|
51
|
+
async searchObservations(project, query, options = {}) {
|
|
52
|
+
const { limit = 10, type } = options;
|
|
53
|
+
const results = await this.client.search(query, project, limit);
|
|
54
|
+
let mapped = results.map((obs) => {
|
|
55
|
+
const row = toMemoryObservationRow(obs);
|
|
56
|
+
this.trackId(row.id, obs.id);
|
|
57
|
+
return row;
|
|
58
|
+
});
|
|
59
|
+
// Client-side type filter (Engram may not support it natively)
|
|
60
|
+
if (type) {
|
|
61
|
+
mapped = mapped.filter((r) => r.type === type);
|
|
62
|
+
}
|
|
63
|
+
return mapped;
|
|
64
|
+
}
|
|
65
|
+
async saveObservation(data) {
|
|
66
|
+
const payload = toEngramSaveData(data);
|
|
67
|
+
const result = await this.client.save(payload);
|
|
68
|
+
if (!result) {
|
|
69
|
+
// Graceful degradation: return a synthetic row so the pipeline continues
|
|
70
|
+
console.warn('[ghagga:engram] saveObservation: Engram save failed, returning synthetic row');
|
|
71
|
+
return {
|
|
72
|
+
id: -1,
|
|
73
|
+
type: data.type,
|
|
74
|
+
title: data.title,
|
|
75
|
+
content: data.content,
|
|
76
|
+
filePaths: data.filePaths ?? null,
|
|
77
|
+
severity: data.severity ?? null,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const row = toMemoryObservationRow(result);
|
|
81
|
+
this.trackId(row.id, result.id);
|
|
82
|
+
return row;
|
|
83
|
+
}
|
|
84
|
+
// ─── Session methods ────────────────────────────────────────────
|
|
85
|
+
async createSession(data) {
|
|
86
|
+
const sessionId = await this.client.createSession(data.project);
|
|
87
|
+
if (sessionId == null) {
|
|
88
|
+
console.warn('[ghagga:engram] createSession failed, returning synthetic id');
|
|
89
|
+
return { id: -1 };
|
|
90
|
+
}
|
|
91
|
+
return { id: sessionId };
|
|
92
|
+
}
|
|
93
|
+
async endSession(sessionId, summary) {
|
|
94
|
+
// Skip if session was never properly created
|
|
95
|
+
if (sessionId === -1)
|
|
96
|
+
return;
|
|
97
|
+
await this.client.endSession(sessionId, summary);
|
|
98
|
+
}
|
|
99
|
+
/** No-op — HTTP connections don't need explicit cleanup. */
|
|
100
|
+
async close() {
|
|
101
|
+
// Nothing to clean up for HTTP-based storage
|
|
102
|
+
}
|
|
103
|
+
// ─── Management methods ─────────────────────────────────────────
|
|
104
|
+
async listObservations(options = {}) {
|
|
105
|
+
const { project, type, limit = 20 } = options;
|
|
106
|
+
// Use search with empty query to list all observations
|
|
107
|
+
const results = await this.client.search('', project, limit);
|
|
108
|
+
let mapped = results.map((obs) => {
|
|
109
|
+
const detail = toMemoryObservationDetail(obs);
|
|
110
|
+
this.trackId(detail.id, obs.id);
|
|
111
|
+
return detail;
|
|
112
|
+
});
|
|
113
|
+
// Client-side type filter
|
|
114
|
+
if (type) {
|
|
115
|
+
mapped = mapped.filter((r) => r.type === type);
|
|
116
|
+
}
|
|
117
|
+
// Client-side offset (Engram may not support it natively)
|
|
118
|
+
if (options.offset && options.offset > 0) {
|
|
119
|
+
mapped = mapped.slice(options.offset);
|
|
120
|
+
}
|
|
121
|
+
return mapped;
|
|
122
|
+
}
|
|
123
|
+
async getObservation(id) {
|
|
124
|
+
const realId = this.idMap.get(id);
|
|
125
|
+
if (realId == null) {
|
|
126
|
+
console.warn('[ghagga:engram] getObservation: ID %d not found in local map. ' +
|
|
127
|
+
'Run searchObservations or listObservations first.', id);
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
const obs = await this.client.getObservation(realId);
|
|
131
|
+
if (!obs)
|
|
132
|
+
return null;
|
|
133
|
+
return toMemoryObservationDetail(obs);
|
|
134
|
+
}
|
|
135
|
+
async deleteObservation(id) {
|
|
136
|
+
const realId = this.idMap.get(id);
|
|
137
|
+
if (realId == null) {
|
|
138
|
+
console.warn('[ghagga:engram] deleteObservation: ID %d not found in local map.', id);
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
const deleted = await this.client.deleteObservation(realId);
|
|
142
|
+
if (deleted) {
|
|
143
|
+
this.idMap.delete(id);
|
|
144
|
+
}
|
|
145
|
+
return deleted;
|
|
146
|
+
}
|
|
147
|
+
async getStats() {
|
|
148
|
+
const stats = await this.client.getStats();
|
|
149
|
+
if (!stats) {
|
|
150
|
+
return {
|
|
151
|
+
totalObservations: 0,
|
|
152
|
+
byType: {},
|
|
153
|
+
byProject: {},
|
|
154
|
+
oldestObservation: null,
|
|
155
|
+
newestObservation: null,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
// Map Engram stats to GHAGGA's MemoryStats shape
|
|
159
|
+
const byProject = {};
|
|
160
|
+
if (stats.projects) {
|
|
161
|
+
for (const p of stats.projects) {
|
|
162
|
+
byProject[p] = 0; // Engram doesn't provide per-project counts in stats
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
totalObservations: stats.total_observations,
|
|
167
|
+
byType: {}, // Engram stats endpoint doesn't break down by type
|
|
168
|
+
byProject,
|
|
169
|
+
oldestObservation: null, // Not provided by Engram stats
|
|
170
|
+
newestObservation: null, // Not provided by Engram stats
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Not supported by the Engram HTTP API.
|
|
175
|
+
* Use the `engram` CLI directly for bulk operations.
|
|
176
|
+
*/
|
|
177
|
+
async clearObservations(_options) {
|
|
178
|
+
console.warn('[ghagga:engram] clearObservations is not supported with the Engram backend. ' +
|
|
179
|
+
'Use the "engram" CLI directly for bulk deletion.');
|
|
180
|
+
return 0;
|
|
181
|
+
}
|
|
182
|
+
// ─── Internal helpers ───────────────────────────────────────────
|
|
183
|
+
/**
|
|
184
|
+
* Track the mapping between a GHAGGA numeric ID and the original Engram ID.
|
|
185
|
+
* This allows getObservation() and deleteObservation() to resolve the real ID.
|
|
186
|
+
*/
|
|
187
|
+
trackId(numericId, engramId) {
|
|
188
|
+
if (!this.idMap.has(numericId)) {
|
|
189
|
+
this.idMap.set(numericId, engramId);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=engram.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engram.js","sourceRoot":"","sources":["../../src/memory/engram.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../src/memory/persist.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../src/memory/persist.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAkC,YAAY,EAAE,MAAM,aAAa,CAAC;AAuC/F;;;;;;;;;;;GAWG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,IAAI,CAAC,CA4Df"}
|
package/dist/memory/persist.js
CHANGED
|
@@ -33,7 +33,7 @@ function findingToObservationType(finding) {
|
|
|
33
33
|
* We only save high and critical findings to avoid noise.
|
|
34
34
|
*/
|
|
35
35
|
function isSignificantFinding(finding) {
|
|
36
|
-
return finding.severity === 'critical' || finding.severity === 'high' || finding.severity === 'medium';
|
|
36
|
+
return (finding.severity === 'critical' || finding.severity === 'high' || finding.severity === 'medium');
|
|
37
37
|
}
|
|
38
38
|
// ─── Main Function ──────────────────────────────────────────────
|
|
39
39
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persist.js","sourceRoot":"","sources":["../../src/memory/persist.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"persist.js","sourceRoot":"","sources":["../../src/memory/persist.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"privacy.d.ts","sourceRoot":"","sources":["../../src/memory/privacy.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"privacy.d.ts","sourceRoot":"","sources":["../../src/memory/privacy.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAuEH;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUrD"}
|
package/dist/memory/privacy.js
CHANGED
|
@@ -18,7 +18,10 @@ const SENSITIVE_PATTERNS = [
|
|
|
18
18
|
// AWS Access Key IDs
|
|
19
19
|
{ pattern: /AKIA[0-9A-Z]{16}/g, replacement: '[REDACTED_AWS_KEY]' },
|
|
20
20
|
// AWS Secret Access Keys (typically 40 chars, base64-ish)
|
|
21
|
-
{
|
|
21
|
+
{
|
|
22
|
+
pattern: /(?<=AWS_SECRET_ACCESS_KEY\s*=\s*)[A-Za-z0-9/+=]{40}/g,
|
|
23
|
+
replacement: '[REDACTED_AWS_SECRET]',
|
|
24
|
+
},
|
|
22
25
|
// GitHub tokens (classic and fine-grained)
|
|
23
26
|
{ pattern: /ghp_[a-zA-Z0-9]{36,}/g, replacement: '[REDACTED_GITHUB_PAT]' },
|
|
24
27
|
{ pattern: /gho_[a-zA-Z0-9]{36,}/g, replacement: '[REDACTED_GITHUB_OAUTH]' },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"privacy.js","sourceRoot":"","sources":["../../src/memory/privacy.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,mEAAmE;AAEnE;;;GAGG;AACH,MAAM,kBAAkB,GAAoD;IAC1E,qBAAqB;IACrB,EAAE,OAAO,EAAE,4BAA4B,EAAE,WAAW,EAAE,0BAA0B,EAAE;IAElF,+EAA+E;IAC/E,EAAE,OAAO,EAAE,wBAAwB,EAAE,WAAW,EAAE,uBAAuB,EAAE;IAE3E,qBAAqB;IACrB,EAAE,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,oBAAoB,EAAE;IAEnE,0DAA0D;IAC1D,
|
|
1
|
+
{"version":3,"file":"privacy.js","sourceRoot":"","sources":["../../src/memory/privacy.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,mEAAmE;AAEnE;;;GAGG;AACH,MAAM,kBAAkB,GAAoD;IAC1E,qBAAqB;IACrB,EAAE,OAAO,EAAE,4BAA4B,EAAE,WAAW,EAAE,0BAA0B,EAAE;IAElF,+EAA+E;IAC/E,EAAE,OAAO,EAAE,wBAAwB,EAAE,WAAW,EAAE,uBAAuB,EAAE;IAE3E,qBAAqB;IACrB,EAAE,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,oBAAoB,EAAE;IAEnE,0DAA0D;IAC1D;QACE,OAAO,EAAE,sDAAsD;QAC/D,WAAW,EAAE,uBAAuB;KACrC;IAED,2CAA2C;IAC3C,EAAE,OAAO,EAAE,uBAAuB,EAAE,WAAW,EAAE,uBAAuB,EAAE;IAC1E,EAAE,OAAO,EAAE,uBAAuB,EAAE,WAAW,EAAE,yBAAyB,EAAE;IAC5E,EAAE,OAAO,EAAE,uBAAuB,EAAE,WAAW,EAAE,uBAAuB,EAAE;IAC1E,EAAE,OAAO,EAAE,uBAAuB,EAAE,WAAW,EAAE,2BAA2B,EAAE;IAC9E,EAAE,OAAO,EAAE,+BAA+B,EAAE,WAAW,EAAE,4BAA4B,EAAE;IAEvF,kBAAkB;IAClB,EAAE,OAAO,EAAE,wBAAwB,EAAE,WAAW,EAAE,uBAAuB,EAAE;IAE3E,eAAe;IACf,EAAE,OAAO,EAAE,+BAA+B,EAAE,WAAW,EAAE,wBAAwB,EAAE;IAEnF,mCAAmC;IACnC,EAAE,OAAO,EAAE,gCAAgC,EAAE,WAAW,EAAE,yBAAyB,EAAE;IAErF,sDAAsD;IACtD,yEAAyE;IACzE;QACE,OAAO,EAAE,sFAAsF;QAC/F,WAAW,EAAE,YAAY;KAC1B;IAED,0EAA0E;IAC1E,mEAAmE;IACnE;QACE,OAAO,EACL,+FAA+F;QACjG,WAAW,EAAE,mBAAmB;KACjC;IAED,4BAA4B;IAC5B;QACE,OAAO,EACL,mGAAmG;QACrG,WAAW,EAAE,wBAAwB;KACtC;IAED,0DAA0D;IAC1D;QACE,OAAO,EAAE,mEAAmE;QAC5E,WAAW,EAAE,gBAAgB;KAC9B;CACF,CAAC;AAEF,mEAAmE;AAEnE;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,IAAI,SAAS,GAAG,IAAI,CAAC;IAErB,KAAK,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,kBAAkB,EAAE,CAAC;QAC1D,oEAAoE;QACpE,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC;QACtB,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
package/dist/memory/search.d.ts
CHANGED
|
@@ -6,6 +6,32 @@
|
|
|
6
6
|
* past reviews of the same project.
|
|
7
7
|
*/
|
|
8
8
|
import type { MemoryStorage } from '../types.js';
|
|
9
|
+
/** Maximum number of search terms to include in a query. */
|
|
10
|
+
export declare const MAX_SEARCH_TERMS = 10;
|
|
11
|
+
/**
|
|
12
|
+
* Default set of path segments to ignore when building search queries.
|
|
13
|
+
* These are common directory names that carry no semantic value.
|
|
14
|
+
*/
|
|
15
|
+
export declare const DEFAULT_IGNORED_SEGMENTS: Set<string>;
|
|
16
|
+
/**
|
|
17
|
+
* Build a search query from file paths.
|
|
18
|
+
*
|
|
19
|
+
* Extracts meaningful segments from file paths (directory names,
|
|
20
|
+
* file names without extensions) to use as search terms.
|
|
21
|
+
*
|
|
22
|
+
* @param fileList - List of file paths to extract terms from.
|
|
23
|
+
* @param ignoredSegments - Optional set of path segments to ignore.
|
|
24
|
+
* Defaults to {@link DEFAULT_IGNORED_SEGMENTS}.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ["src/auth/login.ts", "lib/db/pool.ts"]
|
|
28
|
+
* → "auth login pool"
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ["src/auth/login.test.ts"]
|
|
32
|
+
* → "auth login" // multi-part extension fully stripped
|
|
33
|
+
*/
|
|
34
|
+
export declare function buildSearchQuery(fileList: string[], ignoredSegments?: Set<string>): string;
|
|
9
35
|
/**
|
|
10
36
|
* Search past review observations for context relevant to the current diff.
|
|
11
37
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/memory/search.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/memory/search.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAKjD,4DAA4D;AAC5D,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAKnC;;;GAGG;AACH,eAAO,MAAM,wBAAwB,aAqBnC,CAAC;AAaH;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAAE,EAClB,eAAe,GAAE,GAAG,CAAC,MAAM,CAA4B,GACtD,MAAM,CAsBR;AAID;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA4BxB"}
|
package/dist/memory/search.js
CHANGED
|
@@ -6,6 +6,45 @@
|
|
|
6
6
|
* past reviews of the same project.
|
|
7
7
|
*/
|
|
8
8
|
import { formatMemoryContext } from './context.js';
|
|
9
|
+
// ─── Constants ──────────────────────────────────────────────────
|
|
10
|
+
/** Maximum number of search terms to include in a query. */
|
|
11
|
+
export const MAX_SEARCH_TERMS = 10;
|
|
12
|
+
/** Minimum length for a path segment to be considered a useful search term. */
|
|
13
|
+
const MIN_TERM_LENGTH = 3;
|
|
14
|
+
/**
|
|
15
|
+
* Default set of path segments to ignore when building search queries.
|
|
16
|
+
* These are common directory names that carry no semantic value.
|
|
17
|
+
*/
|
|
18
|
+
export const DEFAULT_IGNORED_SEGMENTS = new Set([
|
|
19
|
+
'src',
|
|
20
|
+
'lib',
|
|
21
|
+
'dist',
|
|
22
|
+
'build',
|
|
23
|
+
'out',
|
|
24
|
+
'output',
|
|
25
|
+
'node_modules',
|
|
26
|
+
'vendor',
|
|
27
|
+
'test',
|
|
28
|
+
'tests',
|
|
29
|
+
'__tests__',
|
|
30
|
+
'__mocks__',
|
|
31
|
+
'__fixtures__',
|
|
32
|
+
'__snapshots__',
|
|
33
|
+
'.git',
|
|
34
|
+
'.github',
|
|
35
|
+
'.vscode',
|
|
36
|
+
'coverage',
|
|
37
|
+
'tmp',
|
|
38
|
+
'temp',
|
|
39
|
+
]);
|
|
40
|
+
/**
|
|
41
|
+
* Regex that strips all file extensions from a filename,
|
|
42
|
+
* including multi-part extensions like `.test.ts`, `.spec.tsx`, `.d.ts`.
|
|
43
|
+
*
|
|
44
|
+
* Matches one or more consecutive `.ext` groups at the end of the string
|
|
45
|
+
* where each extension part is alphanumeric (1-10 chars).
|
|
46
|
+
*/
|
|
47
|
+
const EXTENSIONS_RE = /(?:\.[a-zA-Z0-9]{1,10})+$/;
|
|
9
48
|
// ─── Helpers ────────────────────────────────────────────────────
|
|
10
49
|
/**
|
|
11
50
|
* Build a search query from file paths.
|
|
@@ -13,28 +52,36 @@ import { formatMemoryContext } from './context.js';
|
|
|
13
52
|
* Extracts meaningful segments from file paths (directory names,
|
|
14
53
|
* file names without extensions) to use as search terms.
|
|
15
54
|
*
|
|
55
|
+
* @param fileList - List of file paths to extract terms from.
|
|
56
|
+
* @param ignoredSegments - Optional set of path segments to ignore.
|
|
57
|
+
* Defaults to {@link DEFAULT_IGNORED_SEGMENTS}.
|
|
58
|
+
*
|
|
16
59
|
* @example
|
|
17
60
|
* ["src/auth/login.ts", "lib/db/pool.ts"]
|
|
18
|
-
* → "auth login
|
|
61
|
+
* → "auth login pool"
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ["src/auth/login.test.ts"]
|
|
65
|
+
* → "auth login" // multi-part extension fully stripped
|
|
19
66
|
*/
|
|
20
|
-
function buildSearchQuery(fileList) {
|
|
67
|
+
export function buildSearchQuery(fileList, ignoredSegments = DEFAULT_IGNORED_SEGMENTS) {
|
|
21
68
|
const terms = new Set();
|
|
22
69
|
for (const filePath of fileList) {
|
|
23
70
|
// Split path into segments
|
|
24
71
|
const segments = filePath.split('/').filter(Boolean);
|
|
25
72
|
for (const segment of segments) {
|
|
26
|
-
// Skip
|
|
27
|
-
if (
|
|
73
|
+
// Skip uninformative directories
|
|
74
|
+
if (ignoredSegments.has(segment)) {
|
|
28
75
|
continue;
|
|
29
76
|
}
|
|
30
|
-
// Remove file
|
|
31
|
-
const name = segment.replace(
|
|
32
|
-
if (name.length
|
|
77
|
+
// Remove all file extensions (handles .test.ts, .spec.tsx, .d.ts, etc.)
|
|
78
|
+
const name = segment.replace(EXTENSIONS_RE, '');
|
|
79
|
+
if (name.length >= MIN_TERM_LENGTH) {
|
|
33
80
|
terms.add(name);
|
|
34
81
|
}
|
|
35
82
|
}
|
|
36
83
|
}
|
|
37
|
-
return [...terms].slice(0,
|
|
84
|
+
return [...terms].slice(0, MAX_SEARCH_TERMS).join(' ');
|
|
38
85
|
}
|
|
39
86
|
// ─── Main Function ──────────────────────────────────────────────
|
|
40
87
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/memory/search.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/memory/search.ts"],"names":[],"mappings":"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"}
|
package/dist/memory/sqlite.d.ts
CHANGED
|
@@ -5,16 +5,21 @@
|
|
|
5
5
|
* database with manual file persistence via close(). Designed for single-process
|
|
6
6
|
* environments (CLI, GitHub Action).
|
|
7
7
|
*/
|
|
8
|
-
import type {
|
|
8
|
+
import type { ListObservationsOptions, MemoryObservationDetail, MemoryObservationRow, MemoryStats, MemoryStorage } from '../types.js';
|
|
9
|
+
export interface SqliteMemoryStorageOptions {
|
|
10
|
+
/** Dedup window in minutes. Observations with the same content hash within this window are deduplicated. Defaults to 15. */
|
|
11
|
+
dedupWindowMinutes?: number;
|
|
12
|
+
}
|
|
9
13
|
export declare class SqliteMemoryStorage implements MemoryStorage {
|
|
10
14
|
private db;
|
|
11
15
|
private filePath;
|
|
16
|
+
private dedupWindowMinutes;
|
|
12
17
|
private constructor();
|
|
13
18
|
/**
|
|
14
19
|
* Async factory — handles WASM initialization and file loading.
|
|
15
20
|
* If filePath exists, loads the existing DB. Otherwise, creates a fresh one.
|
|
16
21
|
*/
|
|
17
|
-
static create(filePath: string): Promise<SqliteMemoryStorage>;
|
|
22
|
+
static create(filePath: string, options?: SqliteMemoryStorageOptions): Promise<SqliteMemoryStorage>;
|
|
18
23
|
searchObservations(project: string, query: string, options?: {
|
|
19
24
|
limit?: number;
|
|
20
25
|
type?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/memory/sqlite.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/memory/sqlite.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EACV,uBAAuB,EACvB,uBAAuB,EACvB,oBAAoB,EACpB,WAAW,EACX,aAAa,EACd,MAAM,aAAa,CAAC;AAkErB,MAAM,WAAW,0BAA0B;IACzC,4HAA4H;IAC5H,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,qBAAa,mBAAoB,YAAW,aAAa;IAIrD,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,QAAQ;IAJlB,OAAO,CAAC,kBAAkB,CAAS;IAEnC,OAAO;IAQP;;;OAGG;WACU,MAAM,CACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,0BAA0B,GACnC,OAAO,CAAC,mBAAmB,CAAC;IAuBzB,kBAAkB,CACtB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAO,GAC9C,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAkD5B,eAAe,CAAC,IAAI,EAAE;QAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAyH3B,aAAa,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAapF,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAanE,OAAO,CAAC,WAAW;IAgBb,gBAAgB,CACpB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,uBAAuB,EAAE,CAAC;IAmC/B,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC;IAmBnE,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK/C,QAAQ,IAAI,OAAO,CAAC,WAAW,CAAC;IAmChC,iBAAiB,CAAC,OAAO,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAStE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAU7B"}
|
package/dist/memory/sqlite.js
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* database with manual file persistence via close(). Designed for single-process
|
|
6
6
|
* environments (CLI, GitHub Action).
|
|
7
7
|
*/
|
|
8
|
-
import initSqlJs from 'fts5-sql-bundle';
|
|
9
8
|
import { createHash } from 'node:crypto';
|
|
10
|
-
import {
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
11
10
|
import { dirname } from 'node:path';
|
|
11
|
+
import initSqlJs from 'fts5-sql-bundle';
|
|
12
12
|
const SCHEMA_SQL = `
|
|
13
13
|
CREATE TABLE IF NOT EXISTS memory_sessions (
|
|
14
14
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -59,19 +59,21 @@ const SCHEMA_SQL = `
|
|
|
59
59
|
VALUES ('delete', old.id, old.title, old.content);
|
|
60
60
|
END;
|
|
61
61
|
`;
|
|
62
|
-
const
|
|
62
|
+
const DEFAULT_DEDUP_WINDOW_MINUTES = 15;
|
|
63
63
|
export class SqliteMemoryStorage {
|
|
64
64
|
db;
|
|
65
65
|
filePath;
|
|
66
|
-
|
|
66
|
+
dedupWindowMinutes;
|
|
67
|
+
constructor(db, filePath, options = {}) {
|
|
67
68
|
this.db = db;
|
|
68
69
|
this.filePath = filePath;
|
|
70
|
+
this.dedupWindowMinutes = options.dedupWindowMinutes ?? DEFAULT_DEDUP_WINDOW_MINUTES;
|
|
69
71
|
}
|
|
70
72
|
/**
|
|
71
73
|
* Async factory — handles WASM initialization and file loading.
|
|
72
74
|
* If filePath exists, loads the existing DB. Otherwise, creates a fresh one.
|
|
73
75
|
*/
|
|
74
|
-
static async create(filePath) {
|
|
76
|
+
static async create(filePath, options) {
|
|
75
77
|
const SQL = await initSqlJs();
|
|
76
78
|
let db;
|
|
77
79
|
if (existsSync(filePath)) {
|
|
@@ -89,7 +91,7 @@ export class SqliteMemoryStorage {
|
|
|
89
91
|
catch {
|
|
90
92
|
// Column already exists — idempotent migration
|
|
91
93
|
}
|
|
92
|
-
return new SqliteMemoryStorage(db, filePath);
|
|
94
|
+
return new SqliteMemoryStorage(db, filePath, options);
|
|
93
95
|
}
|
|
94
96
|
async searchObservations(project, query, options = {}) {
|
|
95
97
|
const { limit = 10, type } = options;
|
|
@@ -122,12 +124,12 @@ export class SqliteMemoryStorage {
|
|
|
122
124
|
while (stmt.step()) {
|
|
123
125
|
const row = stmt.getAsObject();
|
|
124
126
|
rows.push({
|
|
125
|
-
id: row
|
|
126
|
-
type: row
|
|
127
|
-
title: row
|
|
128
|
-
content: row
|
|
129
|
-
filePaths: row
|
|
130
|
-
severity: row
|
|
127
|
+
id: row.id,
|
|
128
|
+
type: row.type,
|
|
129
|
+
title: row.title,
|
|
130
|
+
content: row.content,
|
|
131
|
+
filePaths: row.file_paths ? JSON.parse(row.file_paths) : null,
|
|
132
|
+
severity: row.severity ?? null,
|
|
131
133
|
});
|
|
132
134
|
}
|
|
133
135
|
stmt.free();
|
|
@@ -137,15 +139,17 @@ export class SqliteMemoryStorage {
|
|
|
137
139
|
const contentHash = createHash('sha256')
|
|
138
140
|
.update(`${data.type}:${data.title}:${data.content}`)
|
|
139
141
|
.digest('hex');
|
|
140
|
-
// Dedup: check for same content hash within
|
|
142
|
+
// Dedup: check for same content hash within the configured dedup window
|
|
141
143
|
const dedupRows = this.db.exec(`
|
|
142
144
|
SELECT id, type, title, content, file_paths FROM memory_observations
|
|
143
145
|
WHERE content_hash = ? AND project = ?
|
|
144
|
-
AND created_at > datetime('now', '-${
|
|
146
|
+
AND created_at > datetime('now', '-${this.dedupWindowMinutes} minutes')
|
|
145
147
|
LIMIT 1
|
|
146
148
|
`, [contentHash, data.project]);
|
|
147
|
-
if (dedupRows.length > 0 && dedupRows[0]
|
|
148
|
-
const row = dedupRows[0]
|
|
149
|
+
if (dedupRows.length > 0 && dedupRows[0]?.values.length > 0) {
|
|
150
|
+
const row = dedupRows[0]?.values[0];
|
|
151
|
+
if (!row)
|
|
152
|
+
throw new Error('Unexpected empty row after dedup check');
|
|
149
153
|
const existingId = row[0];
|
|
150
154
|
// If the existing observation is from a different session, reassign it
|
|
151
155
|
if (data.sessionId != null) {
|
|
@@ -171,8 +175,8 @@ export class SqliteMemoryStorage {
|
|
|
171
175
|
WHERE topic_key = ? AND project = ?
|
|
172
176
|
LIMIT 1
|
|
173
177
|
`, [data.topicKey, data.project]);
|
|
174
|
-
if (existingByTopic.length > 0 && existingByTopic[0]
|
|
175
|
-
const existingId = existingByTopic[0]
|
|
178
|
+
if (existingByTopic.length > 0 && existingByTopic[0]?.values.length > 0) {
|
|
179
|
+
const existingId = existingByTopic[0]?.values[0]?.[0];
|
|
176
180
|
const filePathsJson = JSON.stringify(data.filePaths ?? []);
|
|
177
181
|
const updated = this.db.exec(`
|
|
178
182
|
UPDATE memory_observations
|
|
@@ -183,7 +187,9 @@ export class SqliteMemoryStorage {
|
|
|
183
187
|
WHERE id = ?
|
|
184
188
|
RETURNING id, type, title, content, file_paths, severity
|
|
185
189
|
`, [data.content, data.title, contentHash, filePathsJson, data.severity ?? null, existingId]);
|
|
186
|
-
const row = updated[0]
|
|
190
|
+
const row = updated[0]?.values[0];
|
|
191
|
+
if (!row)
|
|
192
|
+
throw new Error('Unexpected empty row after topic upsert');
|
|
187
193
|
return {
|
|
188
194
|
id: row[0],
|
|
189
195
|
type: row[1],
|
|
@@ -212,7 +218,9 @@ export class SqliteMemoryStorage {
|
|
|
212
218
|
filePathsJson,
|
|
213
219
|
contentHash,
|
|
214
220
|
]);
|
|
215
|
-
const row = inserted[0]
|
|
221
|
+
const row = inserted[0]?.values[0];
|
|
222
|
+
if (!row)
|
|
223
|
+
throw new Error('Unexpected empty row after insert');
|
|
216
224
|
return {
|
|
217
225
|
id: row[0],
|
|
218
226
|
type: row[1],
|
|
@@ -228,7 +236,7 @@ export class SqliteMemoryStorage {
|
|
|
228
236
|
VALUES (?, ?)
|
|
229
237
|
RETURNING id
|
|
230
238
|
`, [data.project, data.prNumber ?? null]);
|
|
231
|
-
return { id: result[0]
|
|
239
|
+
return { id: result[0]?.values[0]?.[0] };
|
|
232
240
|
}
|
|
233
241
|
async endSession(sessionId, summary) {
|
|
234
242
|
this.db.run(`
|
|
@@ -240,17 +248,17 @@ export class SqliteMemoryStorage {
|
|
|
240
248
|
// ── Management methods ──────────────────────────────────────────
|
|
241
249
|
mapToDetail(row) {
|
|
242
250
|
return {
|
|
243
|
-
id: row
|
|
244
|
-
type: row
|
|
245
|
-
title: row
|
|
246
|
-
content: row
|
|
247
|
-
filePaths: row
|
|
248
|
-
severity: row
|
|
249
|
-
project: row
|
|
250
|
-
topicKey: row
|
|
251
|
-
revisionCount: row
|
|
252
|
-
createdAt: row
|
|
253
|
-
updatedAt: row
|
|
251
|
+
id: row.id,
|
|
252
|
+
type: row.type,
|
|
253
|
+
title: row.title,
|
|
254
|
+
content: row.content,
|
|
255
|
+
filePaths: row.file_paths ? JSON.parse(row.file_paths) : null,
|
|
256
|
+
severity: row.severity ?? null,
|
|
257
|
+
project: row.project,
|
|
258
|
+
topicKey: row.topic_key ?? null,
|
|
259
|
+
revisionCount: row.revision_count,
|
|
260
|
+
createdAt: row.created_at,
|
|
261
|
+
updatedAt: row.updated_at,
|
|
254
262
|
};
|
|
255
263
|
}
|
|
256
264
|
async listObservations(options = {}) {
|
|
@@ -313,7 +321,7 @@ export class SqliteMemoryStorage {
|
|
|
313
321
|
const byTypeResult = this.db.exec('SELECT type, COUNT(*) AS count FROM memory_observations GROUP BY type ORDER BY count DESC');
|
|
314
322
|
const byType = {};
|
|
315
323
|
if (byTypeResult.length > 0) {
|
|
316
|
-
for (const row of byTypeResult[0]
|
|
324
|
+
for (const row of byTypeResult[0]?.values) {
|
|
317
325
|
byType[row[0]] = row[1];
|
|
318
326
|
}
|
|
319
327
|
}
|
|
@@ -321,7 +329,7 @@ export class SqliteMemoryStorage {
|
|
|
321
329
|
const byProjectResult = this.db.exec('SELECT project, COUNT(*) AS count FROM memory_observations GROUP BY project ORDER BY count DESC');
|
|
322
330
|
const byProject = {};
|
|
323
331
|
if (byProjectResult.length > 0) {
|
|
324
|
-
for (const row of byProjectResult[0]
|
|
332
|
+
for (const row of byProjectResult[0]?.values) {
|
|
325
333
|
byProject[row[0]] = row[1];
|
|
326
334
|
}
|
|
327
335
|
}
|