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.
Files changed (84) hide show
  1. package/dist/agents/consensus.d.ts +1 -1
  2. package/dist/agents/consensus.d.ts.map +1 -1
  3. package/dist/agents/consensus.js +7 -5
  4. package/dist/agents/consensus.js.map +1 -1
  5. package/dist/agents/prompts.d.ts.map +1 -1
  6. package/dist/agents/prompts.js +1 -3
  7. package/dist/agents/prompts.js.map +1 -1
  8. package/dist/agents/simple.d.ts +1 -1
  9. package/dist/agents/simple.d.ts.map +1 -1
  10. package/dist/agents/simple.js +12 -17
  11. package/dist/agents/simple.js.map +1 -1
  12. package/dist/agents/workflow.d.ts +1 -1
  13. package/dist/agents/workflow.d.ts.map +1 -1
  14. package/dist/agents/workflow.js +5 -4
  15. package/dist/agents/workflow.js.map +1 -1
  16. package/dist/format.d.ts.map +1 -1
  17. package/dist/format.js +2 -4
  18. package/dist/format.js.map +1 -1
  19. package/dist/index.d.ts +10 -9
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +6 -7
  22. package/dist/index.js.map +1 -1
  23. package/dist/memory/context.d.ts.map +1 -1
  24. package/dist/memory/context.js.map +1 -1
  25. package/dist/memory/engram-client.d.ts +38 -0
  26. package/dist/memory/engram-client.d.ts.map +1 -0
  27. package/dist/memory/engram-client.js +156 -0
  28. package/dist/memory/engram-client.js.map +1 -0
  29. package/dist/memory/engram-mapping.d.ts +83 -0
  30. package/dist/memory/engram-mapping.d.ts.map +1 -0
  31. package/dist/memory/engram-mapping.js +159 -0
  32. package/dist/memory/engram-mapping.js.map +1 -0
  33. package/dist/memory/engram-types.d.ts +51 -0
  34. package/dist/memory/engram-types.d.ts.map +1 -0
  35. package/dist/memory/engram-types.js +12 -0
  36. package/dist/memory/engram-types.js.map +1 -0
  37. package/dist/memory/engram.d.ts +75 -0
  38. package/dist/memory/engram.d.ts.map +1 -0
  39. package/dist/memory/engram.js +193 -0
  40. package/dist/memory/engram.js.map +1 -0
  41. package/dist/memory/persist.d.ts.map +1 -1
  42. package/dist/memory/persist.js +1 -1
  43. package/dist/memory/persist.js.map +1 -1
  44. package/dist/memory/privacy.d.ts.map +1 -1
  45. package/dist/memory/privacy.js +4 -1
  46. package/dist/memory/privacy.js.map +1 -1
  47. package/dist/memory/search.d.ts +26 -0
  48. package/dist/memory/search.d.ts.map +1 -1
  49. package/dist/memory/search.js +55 -8
  50. package/dist/memory/search.js.map +1 -1
  51. package/dist/memory/sqlite.d.ts +7 -2
  52. package/dist/memory/sqlite.d.ts.map +1 -1
  53. package/dist/memory/sqlite.js +42 -34
  54. package/dist/memory/sqlite.js.map +1 -1
  55. package/dist/pipeline.d.ts.map +1 -1
  56. package/dist/pipeline.js +39 -14
  57. package/dist/pipeline.js.map +1 -1
  58. package/dist/providers/fallback.d.ts.map +1 -1
  59. package/dist/providers/fallback.js +5 -3
  60. package/dist/providers/fallback.js.map +1 -1
  61. package/dist/providers/index.d.ts.map +1 -1
  62. package/dist/providers/index.js +1 -1
  63. package/dist/providers/index.js.map +1 -1
  64. package/dist/tools/cpd.d.ts +1 -1
  65. package/dist/tools/cpd.d.ts.map +1 -1
  66. package/dist/tools/cpd.js +20 -15
  67. package/dist/tools/cpd.js.map +1 -1
  68. package/dist/tools/runner.d.ts +1 -1
  69. package/dist/tools/runner.d.ts.map +1 -1
  70. package/dist/tools/runner.js +2 -4
  71. package/dist/tools/runner.js.map +1 -1
  72. package/dist/tools/semgrep.d.ts +1 -1
  73. package/dist/tools/semgrep.d.ts.map +1 -1
  74. package/dist/tools/semgrep.js +20 -14
  75. package/dist/tools/semgrep.js.map +1 -1
  76. package/dist/tools/trivy.d.ts +1 -1
  77. package/dist/tools/trivy.d.ts.map +1 -1
  78. package/dist/tools/trivy.js +3 -1
  79. package/dist/tools/trivy.js.map +1 -1
  80. package/dist/types.d.ts.map +1 -1
  81. package/dist/types.js.map +1 -1
  82. package/dist/utils/diff.js +1 -1
  83. package/dist/utils/diff.js.map +1 -1
  84. 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;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAkC,MAAM,aAAa,CAAC;AAoC/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"}
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"}
@@ -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":"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"}
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;AAkEH;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUrD"}
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"}
@@ -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
- { pattern: /(?<=AWS_SECRET_ACCESS_KEY\s*=\s*)[A-Za-z0-9/+=]{40}/g, replacement: '[REDACTED_AWS_SECRET]' },
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,EAAE,OAAO,EAAE,sDAAsD,EAAE,WAAW,EAAE,uBAAuB,EAAE;IAEzG,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,EAAE,+FAA+F;QACxG,WAAW,EAAE,mBAAmB;KACjC;IAED,4BAA4B;IAC5B;QACE,OAAO,EAAE,mGAAmG;QAC5G,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"}
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"}
@@ -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;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAwCjD;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgCxB"}
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"}
@@ -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 db pool"
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 common uninformative directories
27
- if (['src', 'lib', 'dist', 'build', 'node_modules', 'test', 'tests'].includes(segment)) {
73
+ // Skip uninformative directories
74
+ if (ignoredSegments.has(segment)) {
28
75
  continue;
29
76
  }
30
- // Remove file extension and add as a search term
31
- const name = segment.replace(/\.[^.]+$/, '');
32
- if (name.length > 2) {
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, 10).join(' ');
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;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAGnD,mEAAmE;AAEnE;;;;;;;;;GASG;AACH,SAAS,gBAAgB,CAAC,QAAkB;IAC1C,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAEhC,KAAK,MAAM,QAAQ,IAAI,QAAQ,EAAE,CAAC;QAChC,2BAA2B;QAC3B,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAErD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,wCAAwC;YACxC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBACvF,SAAS;YACX,CAAC;YAED,iDAAiD;YACjD,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;YAC7C,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACpB,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3C,CAAC;AAED,mEAAmE;AAEnE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,OAAsB,EACtB,OAAe,EACf,QAAkB;IAElB,IAAI,CAAC;QACH,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAE1B,MAAM,KAAK,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QACzC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAExB,qEAAqE;QACrE,MAAM,YAAY,GAAG,MAAM,OAAO,CAAC,kBAAkB,CACnD,OAAO,EACP,KAAK,EACL,EAAE,KAAK,EAAE,CAAC,EAAE,CACb,CAAC;QAEF,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAE5D,2CAA2C;QAC3C,OAAO,mBAAmB,CACxB,YAAY,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YACzB,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,OAAO,EAAE,GAAG,CAAC,OAAO;SACrB,CAAC,CAAC,CACJ,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,8DAA8D;QAC9D,OAAO,CAAC,IAAI,CACV,uDAAuD,EACvD,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACvD,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/memory/search.ts"],"names":[],"mappings":"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"}
@@ -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 { MemoryStorage, MemoryObservationRow, MemoryObservationDetail, MemoryStats, ListObservationsOptions } from '../types.js';
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,EAAE,aAAa,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AA+DtI,qBAAa,mBAAoB,YAAW,aAAa;IAErD,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,QAAQ;IAFlB,OAAO;IAKP;;;OAGG;WACU,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAuB7D,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;IA0G3B,aAAa,CAAC,IAAI,EAAE;QACxB,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAUrB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUnE,OAAO,CAAC,WAAW;IAgBb,gBAAgB,CAAC,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,uBAAuB,EAAE,CAAC;IAmC3F,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"}
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"}
@@ -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 { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
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 DEDUP_WINDOW_MINUTES = 15;
62
+ const DEFAULT_DEDUP_WINDOW_MINUTES = 15;
63
63
  export class SqliteMemoryStorage {
64
64
  db;
65
65
  filePath;
66
- constructor(db, filePath) {
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['id'],
126
- type: row['type'],
127
- title: row['title'],
128
- content: row['content'],
129
- filePaths: row['file_paths'] ? JSON.parse(row['file_paths']) : null,
130
- severity: row['severity'] ?? null,
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 15-minute window
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', '-${DEDUP_WINDOW_MINUTES} minutes')
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].values.length > 0) {
148
- const row = dedupRows[0].values[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].values.length > 0) {
175
- const existingId = existingByTopic[0].values[0][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].values[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].values[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].values[0][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['id'],
244
- type: row['type'],
245
- title: row['title'],
246
- content: row['content'],
247
- filePaths: row['file_paths'] ? JSON.parse(row['file_paths']) : null,
248
- severity: row['severity'] ?? null,
249
- project: row['project'],
250
- topicKey: row['topic_key'] ?? null,
251
- revisionCount: row['revision_count'],
252
- createdAt: row['created_at'],
253
- updatedAt: row['updated_at'],
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].values) {
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].values) {
332
+ for (const row of byProjectResult[0]?.values) {
325
333
  byProject[row[0]] = row[1];
326
334
  }
327
335
  }