@cleocode/core 2026.4.30 → 2026.4.31
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/bootstrap.d.ts +35 -0
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/code/index.d.ts +8 -4
- package/dist/code/index.d.ts.map +1 -1
- package/dist/code/parser.d.ts +22 -9
- package/dist/code/parser.d.ts.map +1 -1
- package/dist/hooks/handlers/session-hooks.d.ts +11 -4
- package/dist/hooks/handlers/session-hooks.d.ts.map +1 -1
- package/dist/hooks/payload-schemas.d.ts +6 -6
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3859 -3008
- package/dist/index.js.map +4 -4
- package/dist/internal.d.ts +10 -7
- package/dist/internal.d.ts.map +1 -1
- package/dist/lib/tree-sitter-languages.d.ts +11 -7
- package/dist/lib/tree-sitter-languages.d.ts.map +1 -1
- package/dist/memory/auto-extract.d.ts +27 -15
- package/dist/memory/auto-extract.d.ts.map +1 -1
- package/dist/memory/brain-backfill.d.ts +59 -0
- package/dist/memory/brain-backfill.d.ts.map +1 -0
- package/dist/memory/brain-purge.d.ts +51 -0
- package/dist/memory/brain-purge.d.ts.map +1 -0
- package/dist/memory/brain-retrieval.d.ts.map +1 -1
- package/dist/memory/brain-search.d.ts.map +1 -1
- package/dist/memory/decisions.d.ts.map +1 -1
- package/dist/memory/engine-compat.d.ts +71 -0
- package/dist/memory/engine-compat.d.ts.map +1 -1
- package/dist/memory/graph-auto-populate.d.ts +65 -0
- package/dist/memory/graph-auto-populate.d.ts.map +1 -0
- package/dist/memory/graph-queries.d.ts +127 -0
- package/dist/memory/graph-queries.d.ts.map +1 -0
- package/dist/memory/learnings.d.ts +2 -0
- package/dist/memory/learnings.d.ts.map +1 -1
- package/dist/memory/patterns.d.ts +2 -0
- package/dist/memory/patterns.d.ts.map +1 -1
- package/dist/memory/quality-scoring.d.ts +90 -0
- package/dist/memory/quality-scoring.d.ts.map +1 -0
- package/dist/sessions/session-memory-bridge.d.ts +16 -10
- package/dist/sessions/session-memory-bridge.d.ts.map +1 -1
- package/dist/store/brain-accessor.d.ts +7 -0
- package/dist/store/brain-accessor.d.ts.map +1 -1
- package/dist/store/brain-schema.d.ts +185 -11
- package/dist/store/brain-schema.d.ts.map +1 -1
- package/dist/store/brain-sqlite.d.ts.map +1 -1
- package/dist/store/nexus-schema.d.ts +480 -2
- package/dist/store/nexus-schema.d.ts.map +1 -1
- package/dist/store/tasks-schema.d.ts +9 -9
- package/dist/store/validation-schemas.d.ts +44 -28
- package/dist/store/validation-schemas.d.ts.map +1 -1
- package/dist/system/dependencies.d.ts +43 -0
- package/dist/system/dependencies.d.ts.map +1 -0
- package/dist/system/health.d.ts +3 -0
- package/dist/system/health.d.ts.map +1 -1
- package/dist/tasks/complete.d.ts.map +1 -1
- package/package.json +19 -19
- package/src/bootstrap.ts +124 -0
- package/src/code/index.ts +20 -4
- package/src/code/parser.ts +310 -110
- package/src/hooks/handlers/__tests__/hook-automation-e2e.test.ts +19 -45
- package/src/hooks/handlers/__tests__/session-hooks.test.ts +42 -54
- package/src/hooks/handlers/session-hooks.ts +11 -33
- package/src/index.ts +14 -0
- package/src/internal.ts +37 -7
- package/src/lib/tree-sitter-languages.ts +11 -7
- package/src/memory/__tests__/auto-extract.test.ts +20 -82
- package/src/memory/__tests__/embedding-pipeline.test.ts +389 -0
- package/src/memory/auto-extract.ts +34 -120
- package/src/memory/brain-backfill.ts +471 -0
- package/src/memory/brain-purge.ts +315 -0
- package/src/memory/brain-retrieval.ts +43 -2
- package/src/memory/brain-search.ts +23 -6
- package/src/memory/decisions.ts +76 -3
- package/src/memory/engine-compat.ts +168 -0
- package/src/memory/graph-auto-populate.ts +173 -0
- package/src/memory/graph-queries.ts +424 -0
- package/src/memory/learnings.ts +55 -7
- package/src/memory/patterns.ts +66 -13
- package/src/memory/quality-scoring.ts +173 -0
- package/src/sessions/__tests__/session-memory-bridge.test.ts +27 -49
- package/src/sessions/session-memory-bridge.ts +19 -47
- package/src/store/__tests__/brain-accessor-pageindex.test.ts +93 -22
- package/src/store/brain-accessor.ts +48 -2
- package/src/store/brain-schema.ts +165 -13
- package/src/store/brain-sqlite.ts +35 -0
- package/src/store/nexus-schema.ts +257 -3
- package/src/system/dependencies.ts +534 -0
- package/src/system/health.ts +126 -22
- package/src/tasks/complete.ts +40 -0
|
@@ -94,6 +94,12 @@ export const brainDecisions = sqliteTable(
|
|
|
94
94
|
contextEpicId: text('context_epic_id'), // soft FK to tasks.id in tasks.db
|
|
95
95
|
contextTaskId: text('context_task_id'), // soft FK to tasks.id in tasks.db
|
|
96
96
|
contextPhase: text('context_phase'),
|
|
97
|
+
/**
|
|
98
|
+
* Quality score: 0.0 (noise) – 1.0 (canonical). Null for legacy entries.
|
|
99
|
+
* Computed at insert time from confidence, content richness, and context.
|
|
100
|
+
* Entries below 0.3 are excluded from search results (T531).
|
|
101
|
+
*/
|
|
102
|
+
qualityScore: real('quality_score'),
|
|
97
103
|
createdAt: text('created_at').notNull().default(sql`(datetime('now'))`),
|
|
98
104
|
updatedAt: text('updated_at'),
|
|
99
105
|
},
|
|
@@ -103,6 +109,7 @@ export const brainDecisions = sqliteTable(
|
|
|
103
109
|
index('idx_brain_decisions_outcome').on(table.outcome),
|
|
104
110
|
index('idx_brain_decisions_context_epic').on(table.contextEpicId),
|
|
105
111
|
index('idx_brain_decisions_context_task').on(table.contextTaskId),
|
|
112
|
+
index('idx_brain_decisions_quality').on(table.qualityScore),
|
|
106
113
|
],
|
|
107
114
|
);
|
|
108
115
|
|
|
@@ -123,11 +130,18 @@ export const brainPatterns = sqliteTable(
|
|
|
123
130
|
examplesJson: text('examples_json').default('[]'),
|
|
124
131
|
extractedAt: text('extracted_at').notNull().default(sql`(datetime('now'))`),
|
|
125
132
|
updatedAt: text('updated_at'),
|
|
133
|
+
/**
|
|
134
|
+
* Quality score: 0.0 (noise) – 1.0 (canonical). Null for legacy entries.
|
|
135
|
+
* Computed at insert time from type, content richness, and examples.
|
|
136
|
+
* Entries below 0.3 are excluded from search results (T531).
|
|
137
|
+
*/
|
|
138
|
+
qualityScore: real('quality_score'),
|
|
126
139
|
},
|
|
127
140
|
(table) => [
|
|
128
141
|
index('idx_brain_patterns_type').on(table.type),
|
|
129
142
|
index('idx_brain_patterns_impact').on(table.impact),
|
|
130
143
|
index('idx_brain_patterns_frequency').on(table.frequency),
|
|
144
|
+
index('idx_brain_patterns_quality').on(table.qualityScore),
|
|
131
145
|
],
|
|
132
146
|
);
|
|
133
147
|
|
|
@@ -145,10 +159,17 @@ export const brainLearnings = sqliteTable(
|
|
|
145
159
|
applicableTypesJson: text('applicable_types_json'),
|
|
146
160
|
createdAt: text('created_at').notNull().default(sql`(datetime('now'))`),
|
|
147
161
|
updatedAt: text('updated_at'),
|
|
162
|
+
/**
|
|
163
|
+
* Quality score: 0.0 (noise) – 1.0 (canonical). Null for legacy entries.
|
|
164
|
+
* Computed at insert time from confidence, actionability, and content richness.
|
|
165
|
+
* Entries below 0.3 are excluded from search results (T531).
|
|
166
|
+
*/
|
|
167
|
+
qualityScore: real('quality_score'),
|
|
148
168
|
},
|
|
149
169
|
(table) => [
|
|
150
170
|
index('idx_brain_learnings_confidence').on(table.confidence),
|
|
151
171
|
index('idx_brain_learnings_actionable').on(table.actionable),
|
|
172
|
+
index('idx_brain_learnings_quality').on(table.qualityScore),
|
|
152
173
|
],
|
|
153
174
|
);
|
|
154
175
|
|
|
@@ -176,6 +197,12 @@ export const brainObservations = sqliteTable(
|
|
|
176
197
|
agent: text('agent'), // nullable — null for legacy observations
|
|
177
198
|
contentHash: text('content_hash'), // SHA-256 prefix for dedup
|
|
178
199
|
discoveryTokens: integer('discovery_tokens'), // cost to produce this observation
|
|
200
|
+
/**
|
|
201
|
+
* Quality score: 0.0 (noise) – 1.0 (canonical). Null for legacy entries.
|
|
202
|
+
* Computed at insert time from content richness and title length.
|
|
203
|
+
* Entries below 0.3 are excluded from search results (T531).
|
|
204
|
+
*/
|
|
205
|
+
qualityScore: real('quality_score'),
|
|
179
206
|
createdAt: text('created_at').notNull().default(sql`(datetime('now'))`),
|
|
180
207
|
updatedAt: text('updated_at'),
|
|
181
208
|
},
|
|
@@ -191,6 +218,8 @@ export const brainObservations = sqliteTable(
|
|
|
191
218
|
index('idx_brain_observations_type_project').on(table.type, table.project),
|
|
192
219
|
// T417: agent provenance index for memory.find --agent filter
|
|
193
220
|
index('idx_brain_observations_agent').on(table.agent),
|
|
221
|
+
// T531: quality score filter index
|
|
222
|
+
index('idx_brain_observations_quality').on(table.qualityScore),
|
|
194
223
|
],
|
|
195
224
|
);
|
|
196
225
|
|
|
@@ -244,41 +273,163 @@ export const brainSchemaMeta = sqliteTable('brain_schema_meta', {
|
|
|
244
273
|
value: text('value').notNull(),
|
|
245
274
|
});
|
|
246
275
|
|
|
247
|
-
// === PAGEINDEX GRAPH TABLES (T5160) ===
|
|
276
|
+
// === PAGEINDEX GRAPH TABLES (T5160, expanded T528) ===
|
|
248
277
|
|
|
249
|
-
/**
|
|
250
|
-
|
|
278
|
+
/**
|
|
279
|
+
* Node types for the graph-native memory model.
|
|
280
|
+
* Mirrors typed tables (decision, pattern, learning, observation, sticky),
|
|
281
|
+
* adds task provenance (task, session, epic), codebase bridging (file, symbol),
|
|
282
|
+
* and abstract/synthesized types (concept, summary).
|
|
283
|
+
*/
|
|
284
|
+
export const BRAIN_NODE_TYPES = [
|
|
285
|
+
// Memory entity types (mirror typed tables)
|
|
286
|
+
'decision',
|
|
287
|
+
'pattern',
|
|
288
|
+
'learning',
|
|
289
|
+
'observation',
|
|
290
|
+
'sticky',
|
|
291
|
+
// Task provenance (soft FK into tasks.db)
|
|
292
|
+
'task',
|
|
293
|
+
'session',
|
|
294
|
+
'epic',
|
|
295
|
+
// Codebase integration (bridge to nexus.db code_index)
|
|
296
|
+
'file',
|
|
297
|
+
'symbol',
|
|
298
|
+
// Abstract / synthesized
|
|
299
|
+
'concept',
|
|
300
|
+
'summary',
|
|
301
|
+
] as const;
|
|
251
302
|
|
|
252
|
-
/**
|
|
253
|
-
export
|
|
303
|
+
/** Discriminated union of all supported brain graph node types. */
|
|
304
|
+
export type BrainNodeType = (typeof BRAIN_NODE_TYPES)[number];
|
|
254
305
|
|
|
255
|
-
/**
|
|
306
|
+
/**
|
|
307
|
+
* Edge types for the graph-native memory model.
|
|
308
|
+
* Covers provenance/derivation, semantic relationships, structural links,
|
|
309
|
+
* and graph bridging between memory entities and codebase nodes.
|
|
310
|
+
*/
|
|
311
|
+
export const BRAIN_EDGE_TYPES = [
|
|
312
|
+
// Provenance / derivation
|
|
313
|
+
'derived_from', // learning ← derived_from ← observation
|
|
314
|
+
'produced_by', // observation ← produced_by ← session
|
|
315
|
+
'informed_by', // decision ← informed_by ← pattern
|
|
316
|
+
// Semantic relationship
|
|
317
|
+
'supports', // observation → supports → decision
|
|
318
|
+
'contradicts', // observation → contradicts → decision
|
|
319
|
+
'supersedes', // decision → supersedes → decision (older)
|
|
320
|
+
'applies_to', // decision/pattern → applies_to → task/file/symbol
|
|
321
|
+
// Structural
|
|
322
|
+
'documents', // observation → documents → symbol/file
|
|
323
|
+
'summarizes', // summary → summarizes → observation (consolidation)
|
|
324
|
+
'part_of', // task → part_of → epic
|
|
325
|
+
// Graph bridging (memory ↔ code)
|
|
326
|
+
'references', // observation → references → symbol
|
|
327
|
+
'modified_by', // file → modified_by → session
|
|
328
|
+
] as const;
|
|
329
|
+
|
|
330
|
+
/** Discriminated union of all supported brain graph edge types. */
|
|
331
|
+
export type BrainEdgeType = (typeof BRAIN_EDGE_TYPES)[number];
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Graph nodes table — the traversable knowledge graph layer.
|
|
335
|
+
*
|
|
336
|
+
* Every entity row in a typed table (decisions, patterns, learnings,
|
|
337
|
+
* observations) gets a corresponding node here. The typed table row is
|
|
338
|
+
* the source of truth; the graph node is the index entry for traversal
|
|
339
|
+
* and cross-entity reasoning.
|
|
340
|
+
*
|
|
341
|
+
* Node ID convention: '<type>:<source-id>'
|
|
342
|
+
* Examples: 'decision:D-abc123', 'observation:O-mntphoj6-0',
|
|
343
|
+
* 'task:T523', 'symbol:src/store/brain-schema.ts::brainPageNodes'
|
|
344
|
+
*/
|
|
256
345
|
export const brainPageNodes = sqliteTable(
|
|
257
346
|
'brain_page_nodes',
|
|
258
347
|
{
|
|
259
|
-
|
|
348
|
+
/** Stable composite ID: '<type>:<source-id>' */
|
|
349
|
+
id: text('id').primaryKey(),
|
|
350
|
+
|
|
351
|
+
/** Discriminated type from BRAIN_NODE_TYPES. */
|
|
260
352
|
nodeType: text('node_type', { enum: BRAIN_NODE_TYPES }).notNull(),
|
|
353
|
+
|
|
354
|
+
/** Human-readable label (title, name, or generated summary). */
|
|
261
355
|
label: text('label').notNull(),
|
|
262
|
-
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Quality score: 0.0 (noise) – 1.0 (canonical).
|
|
359
|
+
* Derived from: source confidence, edge density, age decay, agent provenance.
|
|
360
|
+
* Default 0.5 for unknown provenance; 0.0 triggers exclusion from traversal.
|
|
361
|
+
*/
|
|
362
|
+
qualityScore: real('quality_score').notNull().default(0.5),
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* SHA-256 prefix (first 16 hex chars) of the canonical content.
|
|
366
|
+
* Computed at insert time; duplicate hashes are rejected.
|
|
367
|
+
* Null for external references (task, session, symbol nodes).
|
|
368
|
+
*/
|
|
369
|
+
contentHash: text('content_hash'),
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* ISO 8601 timestamp of last activity on this node.
|
|
373
|
+
* Updated when new edges are added, quality changes, or content is revised.
|
|
374
|
+
*/
|
|
375
|
+
lastActivityAt: text('last_activity_at').notNull().default(sql`(datetime('now'))`),
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Extensible JSON metadata blob — type-specific payload.
|
|
379
|
+
* decision: { type, confidence, outcome }
|
|
380
|
+
* observation: { sourceType, agent, sessionId }
|
|
381
|
+
* symbol: { filePath, kind, startLine, endLine, language }
|
|
382
|
+
* task: { status, priority, epicId }
|
|
383
|
+
*/
|
|
384
|
+
metadataJson: text('metadata_json'),
|
|
385
|
+
|
|
263
386
|
createdAt: text('created_at').notNull().default(sql`(datetime('now'))`),
|
|
387
|
+
updatedAt: text('updated_at'),
|
|
264
388
|
},
|
|
265
|
-
(table) => [
|
|
389
|
+
(table) => [
|
|
390
|
+
index('idx_brain_nodes_type').on(table.nodeType),
|
|
391
|
+
index('idx_brain_nodes_quality').on(table.qualityScore),
|
|
392
|
+
index('idx_brain_nodes_content_hash').on(table.contentHash),
|
|
393
|
+
index('idx_brain_nodes_last_activity').on(table.lastActivityAt),
|
|
394
|
+
],
|
|
266
395
|
);
|
|
267
396
|
|
|
268
|
-
/**
|
|
397
|
+
/**
|
|
398
|
+
* Graph edges table — directed, typed, weighted, provenance-aware links
|
|
399
|
+
* between brain_page_nodes entries (or external nexus node IDs).
|
|
400
|
+
*
|
|
401
|
+
* The composite primary key (fromId, toId, edgeType) prevents duplicate
|
|
402
|
+
* edges of the same type between the same pair of nodes.
|
|
403
|
+
*/
|
|
269
404
|
export const brainPageEdges = sqliteTable(
|
|
270
405
|
'brain_page_edges',
|
|
271
406
|
{
|
|
272
|
-
fromId: text('from_id').notNull(),
|
|
273
|
-
toId: text('to_id').notNull(),
|
|
407
|
+
fromId: text('from_id').notNull(), // brain_page_nodes.id
|
|
408
|
+
toId: text('to_id').notNull(), // brain_page_nodes.id or nexus node id
|
|
274
409
|
edgeType: text('edge_type', { enum: BRAIN_EDGE_TYPES }).notNull(),
|
|
275
|
-
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Edge weight / confidence: 0.0 – 1.0.
|
|
413
|
+
* Semantic edges use extractor confidence (similarity score).
|
|
414
|
+
* Structural edges use 1.0 (deterministic).
|
|
415
|
+
* Contradiction edges store the overlap score that triggered detection.
|
|
416
|
+
*/
|
|
417
|
+
weight: real('weight').notNull().default(1.0),
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Human-readable note on why this edge was emitted.
|
|
421
|
+
* Examples: 'auto:task-complete' | 'auto:session-end' |
|
|
422
|
+
* 'auto:contradiction-detected' | 'auto:consolidation' | 'manual'
|
|
423
|
+
*/
|
|
424
|
+
provenance: text('provenance'),
|
|
425
|
+
|
|
276
426
|
createdAt: text('created_at').notNull().default(sql`(datetime('now'))`),
|
|
277
427
|
},
|
|
278
428
|
(table) => [
|
|
279
429
|
primaryKey({ columns: [table.fromId, table.toId, table.edgeType] }),
|
|
280
430
|
index('idx_brain_edges_from').on(table.fromId),
|
|
281
431
|
index('idx_brain_edges_to').on(table.toId),
|
|
432
|
+
index('idx_brain_edges_type').on(table.edgeType),
|
|
282
433
|
],
|
|
283
434
|
);
|
|
284
435
|
|
|
@@ -300,3 +451,4 @@ export type BrainPageEdgeRow = typeof brainPageEdges.$inferSelect;
|
|
|
300
451
|
export type NewBrainPageEdgeRow = typeof brainPageEdges.$inferInsert;
|
|
301
452
|
export type BrainStickyNoteRow = typeof brainStickyNotes.$inferSelect;
|
|
302
453
|
export type NewBrainStickyNoteRow = typeof brainStickyNotes.$inferInsert;
|
|
454
|
+
// BrainNodeType and BrainEdgeType are declared alongside their enum arrays above.
|
|
@@ -142,6 +142,30 @@ export function isBrainVecLoaded(): boolean {
|
|
|
142
142
|
return _vecLoaded;
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Initialize the default embedding provider when brain.embedding.enabled is true.
|
|
147
|
+
*
|
|
148
|
+
* Called asynchronously after getBrainDb() completes its synchronous setup.
|
|
149
|
+
* Uses dynamic import to avoid circular dependencies and keep the heavy
|
|
150
|
+
* @huggingface/transformers bundle out of the critical startup path.
|
|
151
|
+
*
|
|
152
|
+
* Best-effort: errors are swallowed by the caller so DB access is never blocked.
|
|
153
|
+
*
|
|
154
|
+
* @task T539
|
|
155
|
+
*/
|
|
156
|
+
async function initEmbeddingProvider(cwd?: string): Promise<void> {
|
|
157
|
+
try {
|
|
158
|
+
const { loadConfig } = await import('../config.js');
|
|
159
|
+
const config = await loadConfig(cwd);
|
|
160
|
+
if (config.brain?.embedding?.enabled) {
|
|
161
|
+
const { initDefaultProvider } = await import('../memory/brain-embedding.js');
|
|
162
|
+
await initDefaultProvider();
|
|
163
|
+
}
|
|
164
|
+
} catch {
|
|
165
|
+
// Config load or provider init failed — non-fatal, embedding stays unavailable
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
145
169
|
/**
|
|
146
170
|
* Initialize the brain.db SQLite database (lazy, singleton).
|
|
147
171
|
* Creates the database file and tables if they don't exist.
|
|
@@ -200,6 +224,17 @@ export async function getBrainDb(cwd?: string): Promise<NodeSQLiteDatabase<typeo
|
|
|
200
224
|
|
|
201
225
|
// Set singleton only after migrations complete
|
|
202
226
|
_db = db;
|
|
227
|
+
|
|
228
|
+
// Wire the default embedding provider when vec is loaded and embedding is enabled.
|
|
229
|
+
// Best-effort, async, never blocks DB access. (T539)
|
|
230
|
+
if (_vecLoaded) {
|
|
231
|
+
setImmediate(() => {
|
|
232
|
+
initEmbeddingProvider(cwd).catch(() => {
|
|
233
|
+
// Non-fatal — embedding will be unavailable until next startup
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
203
238
|
return db;
|
|
204
239
|
})();
|
|
205
240
|
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Drizzle ORM schema for CLEO nexus.db (SQLite via node:sqlite + sqlite-proxy).
|
|
3
3
|
*
|
|
4
|
-
* Tables: project_registry, nexus_audit_log, nexus_schema_meta
|
|
5
|
-
*
|
|
4
|
+
* Tables: project_registry, nexus_audit_log, nexus_schema_meta,
|
|
5
|
+
* nexus_nodes, nexus_relations
|
|
6
|
+
* Stores cross-project registry and audit infrastructure for the Nexus domain,
|
|
7
|
+
* plus the code intelligence graph layer (nodes + directed edges).
|
|
6
8
|
*
|
|
7
9
|
* @task T5365
|
|
10
|
+
* @task T529
|
|
8
11
|
*/
|
|
9
12
|
|
|
10
13
|
import { sql } from 'drizzle-orm';
|
|
11
|
-
import { index, integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
|
|
14
|
+
import { index, integer, real, sqliteTable, text } from 'drizzle-orm/sqlite-core';
|
|
12
15
|
|
|
13
16
|
// === PROJECT_REGISTRY TABLE ===
|
|
14
17
|
|
|
@@ -75,6 +78,253 @@ export const nexusSchemaMeta = sqliteTable('nexus_schema_meta', {
|
|
|
75
78
|
value: text('value').notNull(),
|
|
76
79
|
});
|
|
77
80
|
|
|
81
|
+
// === NEXUS_NODES TABLE ===
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* All node kind values — matches GraphNodeKind in @cleocode/contracts.
|
|
85
|
+
*
|
|
86
|
+
* Kept as a const tuple for use in Drizzle enum column definitions.
|
|
87
|
+
* The ordering is intentional: structural → module → callable → type →
|
|
88
|
+
* value-level → language-specific → graph-level → legacy.
|
|
89
|
+
*/
|
|
90
|
+
export const NEXUS_NODE_KINDS = [
|
|
91
|
+
// Structural
|
|
92
|
+
'file',
|
|
93
|
+
'folder',
|
|
94
|
+
// Module-level
|
|
95
|
+
'module',
|
|
96
|
+
'namespace',
|
|
97
|
+
// Callable
|
|
98
|
+
'function',
|
|
99
|
+
'method',
|
|
100
|
+
'constructor',
|
|
101
|
+
// Type hierarchy
|
|
102
|
+
'class',
|
|
103
|
+
'interface',
|
|
104
|
+
'struct',
|
|
105
|
+
'trait',
|
|
106
|
+
'impl',
|
|
107
|
+
'type_alias',
|
|
108
|
+
'enum',
|
|
109
|
+
// Value-level
|
|
110
|
+
'property',
|
|
111
|
+
'constant',
|
|
112
|
+
'variable',
|
|
113
|
+
'static',
|
|
114
|
+
'record',
|
|
115
|
+
'delegate',
|
|
116
|
+
// Language-specific constructs
|
|
117
|
+
'macro',
|
|
118
|
+
'union',
|
|
119
|
+
'typedef',
|
|
120
|
+
'annotation',
|
|
121
|
+
'template',
|
|
122
|
+
// Graph-level (synthetic nodes from analysis phases)
|
|
123
|
+
'community',
|
|
124
|
+
'process',
|
|
125
|
+
'route',
|
|
126
|
+
// External references
|
|
127
|
+
'tool',
|
|
128
|
+
'section',
|
|
129
|
+
// Legacy (kept for T506 compatibility)
|
|
130
|
+
'import',
|
|
131
|
+
'export',
|
|
132
|
+
'type',
|
|
133
|
+
] as const;
|
|
134
|
+
|
|
135
|
+
/** TypeScript type derived from NEXUS_NODE_KINDS. */
|
|
136
|
+
export type NexusNodeKind = (typeof NEXUS_NODE_KINDS)[number];
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Graph nodes table — one row per symbol or structural element.
|
|
140
|
+
*
|
|
141
|
+
* Stores all code intelligence graph nodes indexed per project.
|
|
142
|
+
* Synthetic nodes (community, process) share this table with
|
|
143
|
+
* source-derived nodes (function, class, file).
|
|
144
|
+
*
|
|
145
|
+
* Both this table and `code_index` are populated from the same parse pass.
|
|
146
|
+
* They serve complementary roles — do NOT merge them.
|
|
147
|
+
*
|
|
148
|
+
* @task T529
|
|
149
|
+
*/
|
|
150
|
+
export const nexusNodes = sqliteTable(
|
|
151
|
+
'nexus_nodes',
|
|
152
|
+
{
|
|
153
|
+
/** Stable node ID. Format: `<filePath>::<name>` for symbols,
|
|
154
|
+
* `<filePath>` for file nodes, `community:<n>` for community nodes,
|
|
155
|
+
* `process:<slug>` for execution flow nodes. */
|
|
156
|
+
id: text('id').primaryKey(),
|
|
157
|
+
|
|
158
|
+
/** Foreign key to project_registry.project_id. Scopes the node. */
|
|
159
|
+
projectId: text('project_id').notNull(),
|
|
160
|
+
|
|
161
|
+
/** Node kind from GraphNodeKind union. */
|
|
162
|
+
kind: text('kind', { enum: NEXUS_NODE_KINDS }).notNull(),
|
|
163
|
+
|
|
164
|
+
/** Human-readable label for display. For symbols, same as name.
|
|
165
|
+
* For communities, the inferred folder label. For processes, the
|
|
166
|
+
* entry point function name. */
|
|
167
|
+
label: text('label').notNull(),
|
|
168
|
+
|
|
169
|
+
/** Symbol name as it appears in source code. Null for file/folder nodes. */
|
|
170
|
+
name: text('name'),
|
|
171
|
+
|
|
172
|
+
/** File path relative to project root. Null for community/process nodes. */
|
|
173
|
+
filePath: text('file_path'),
|
|
174
|
+
|
|
175
|
+
/** Start line in source file (1-based). Null for structural nodes. */
|
|
176
|
+
startLine: integer('start_line'),
|
|
177
|
+
|
|
178
|
+
/** End line in source file (1-based). Null for structural nodes. */
|
|
179
|
+
endLine: integer('end_line'),
|
|
180
|
+
|
|
181
|
+
/** Source language (typescript, python, go, rust, etc.). */
|
|
182
|
+
language: text('language'),
|
|
183
|
+
|
|
184
|
+
/** Whether the symbol is publicly exported from its module. */
|
|
185
|
+
isExported: integer('is_exported', { mode: 'boolean' }).notNull().default(false),
|
|
186
|
+
|
|
187
|
+
/** Parent node ID for nested symbols (e.g., method inside class).
|
|
188
|
+
* References nexus_nodes.id in the same project. Soft FK. */
|
|
189
|
+
parentId: text('parent_id'),
|
|
190
|
+
|
|
191
|
+
/** JSON array of parameter name strings for functions/methods.
|
|
192
|
+
* Stored as `["param1","param2"]`. Null if not applicable. */
|
|
193
|
+
parametersJson: text('parameters_json'),
|
|
194
|
+
|
|
195
|
+
/** Return type annotation text (e.g., "Promise<void>"). */
|
|
196
|
+
returnType: text('return_type'),
|
|
197
|
+
|
|
198
|
+
/** First line of the TSDoc/JSDoc comment for this symbol. */
|
|
199
|
+
docSummary: text('doc_summary'),
|
|
200
|
+
|
|
201
|
+
/** Community membership ID — references the community node's id.
|
|
202
|
+
* Set during Phase 4.5 community detection. Null until then. */
|
|
203
|
+
communityId: text('community_id'),
|
|
204
|
+
|
|
205
|
+
/** JSON blob for kind-specific metadata.
|
|
206
|
+
* For `process` nodes: `{"stepCount": 7, "entryScore": 0.92}`.
|
|
207
|
+
* For `community` nodes: `{"memberCount": 14, "topFolders": ["src/core"]}`.
|
|
208
|
+
* For `route` nodes: `{"method": "GET", "path": "/api/v1/tasks"}`.
|
|
209
|
+
* For all others: null. */
|
|
210
|
+
metaJson: text('meta_json'),
|
|
211
|
+
|
|
212
|
+
/** ISO 8601 timestamp when this node was last indexed. */
|
|
213
|
+
indexedAt: text('indexed_at').notNull().default(sql`(datetime('now'))`),
|
|
214
|
+
},
|
|
215
|
+
(table) => [
|
|
216
|
+
index('idx_nexus_nodes_project').on(table.projectId),
|
|
217
|
+
index('idx_nexus_nodes_kind').on(table.kind),
|
|
218
|
+
index('idx_nexus_nodes_file').on(table.filePath),
|
|
219
|
+
index('idx_nexus_nodes_name').on(table.name),
|
|
220
|
+
index('idx_nexus_nodes_project_kind').on(table.projectId, table.kind),
|
|
221
|
+
index('idx_nexus_nodes_project_file').on(table.projectId, table.filePath),
|
|
222
|
+
index('idx_nexus_nodes_community').on(table.communityId),
|
|
223
|
+
index('idx_nexus_nodes_parent').on(table.parentId),
|
|
224
|
+
index('idx_nexus_nodes_exported').on(table.isExported),
|
|
225
|
+
],
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
// === NEXUS_RELATIONS TABLE ===
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* All relation type values — matches GraphRelationType in @cleocode/contracts.
|
|
232
|
+
*
|
|
233
|
+
* Kept as a const tuple for use in Drizzle enum column definitions.
|
|
234
|
+
*/
|
|
235
|
+
export const NEXUS_RELATION_TYPES = [
|
|
236
|
+
// Structural
|
|
237
|
+
'contains',
|
|
238
|
+
// Definition / usage
|
|
239
|
+
'defines',
|
|
240
|
+
'imports',
|
|
241
|
+
'accesses',
|
|
242
|
+
// Callable
|
|
243
|
+
'calls',
|
|
244
|
+
// Type hierarchy
|
|
245
|
+
'extends',
|
|
246
|
+
'implements',
|
|
247
|
+
'method_overrides',
|
|
248
|
+
'method_implements',
|
|
249
|
+
// Class structure
|
|
250
|
+
'has_method',
|
|
251
|
+
'has_property',
|
|
252
|
+
// Graph-level (synthetic, from analysis phases)
|
|
253
|
+
'member_of', // symbol → community
|
|
254
|
+
'step_in_process', // symbol → process
|
|
255
|
+
// Web / API
|
|
256
|
+
'handles_route', // function → route node
|
|
257
|
+
'fetches', // function → external API
|
|
258
|
+
// Tool / agent
|
|
259
|
+
'handles_tool',
|
|
260
|
+
'entry_point_of', // function → process
|
|
261
|
+
// Wrapping / delegation
|
|
262
|
+
'wraps',
|
|
263
|
+
// Data access
|
|
264
|
+
'queries',
|
|
265
|
+
// Cross-graph (brain link)
|
|
266
|
+
'documents', // brain_page_node → nexus_nodes
|
|
267
|
+
'applies_to', // brain_page_node → nexus_nodes
|
|
268
|
+
] as const;
|
|
269
|
+
|
|
270
|
+
/** TypeScript type derived from NEXUS_RELATION_TYPES. */
|
|
271
|
+
export type NexusRelationType = (typeof NEXUS_RELATION_TYPES)[number];
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Graph relations table — one row per directed edge.
|
|
275
|
+
*
|
|
276
|
+
* All graph traversal (impact, context, process detection) reads from
|
|
277
|
+
* this table after ingestion completes.
|
|
278
|
+
*
|
|
279
|
+
* Source and target reference nexus_nodes.id. They are soft FKs —
|
|
280
|
+
* unresolved targets (e.g., external packages) are stored as raw specifiers.
|
|
281
|
+
*
|
|
282
|
+
* @task T529
|
|
283
|
+
*/
|
|
284
|
+
export const nexusRelations = sqliteTable(
|
|
285
|
+
'nexus_relations',
|
|
286
|
+
{
|
|
287
|
+
/** UUID v4 row identifier. */
|
|
288
|
+
id: text('id').primaryKey(),
|
|
289
|
+
|
|
290
|
+
/** Foreign key to project_registry.project_id. */
|
|
291
|
+
projectId: text('project_id').notNull(),
|
|
292
|
+
|
|
293
|
+
/** Source node ID (nexus_nodes.id). */
|
|
294
|
+
sourceId: text('source_id').notNull(),
|
|
295
|
+
|
|
296
|
+
/** Target node ID (nexus_nodes.id) or raw module specifier for
|
|
297
|
+
* unresolved imports. Example: `@cleocode/contracts` or
|
|
298
|
+
* `src/core/parser.ts::parseFile`. */
|
|
299
|
+
targetId: text('target_id').notNull(),
|
|
300
|
+
|
|
301
|
+
/** Semantic relation type. */
|
|
302
|
+
type: text('type', { enum: NEXUS_RELATION_TYPES }).notNull(),
|
|
303
|
+
|
|
304
|
+
/** Extractor confidence (0.0 to 1.0). */
|
|
305
|
+
confidence: real('confidence').notNull(),
|
|
306
|
+
|
|
307
|
+
/** Human-readable note explaining why this relation was emitted. */
|
|
308
|
+
reason: text('reason'),
|
|
309
|
+
|
|
310
|
+
/** Step index within an execution flow (for step_in_process relations). */
|
|
311
|
+
step: integer('step'),
|
|
312
|
+
|
|
313
|
+
/** ISO 8601 timestamp when this relation was last indexed. */
|
|
314
|
+
indexedAt: text('indexed_at').notNull().default(sql`(datetime('now'))`),
|
|
315
|
+
},
|
|
316
|
+
(table) => [
|
|
317
|
+
index('idx_nexus_relations_project').on(table.projectId),
|
|
318
|
+
index('idx_nexus_relations_source').on(table.sourceId),
|
|
319
|
+
index('idx_nexus_relations_target').on(table.targetId),
|
|
320
|
+
index('idx_nexus_relations_type').on(table.type),
|
|
321
|
+
index('idx_nexus_relations_project_type').on(table.projectId, table.type),
|
|
322
|
+
index('idx_nexus_relations_source_type').on(table.sourceId, table.type),
|
|
323
|
+
index('idx_nexus_relations_target_type').on(table.targetId, table.type),
|
|
324
|
+
index('idx_nexus_relations_confidence').on(table.confidence),
|
|
325
|
+
],
|
|
326
|
+
);
|
|
327
|
+
|
|
78
328
|
// === TYPE EXPORTS ===
|
|
79
329
|
|
|
80
330
|
export type ProjectRegistryRow = typeof projectRegistry.$inferSelect;
|
|
@@ -83,3 +333,7 @@ export type NexusAuditLogRow = typeof nexusAuditLog.$inferSelect;
|
|
|
83
333
|
export type NewNexusAuditLogRow = typeof nexusAuditLog.$inferInsert;
|
|
84
334
|
export type NexusSchemaMetaRow = typeof nexusSchemaMeta.$inferSelect;
|
|
85
335
|
export type NewNexusSchemaMetaRow = typeof nexusSchemaMeta.$inferInsert;
|
|
336
|
+
export type NexusNodeRow = typeof nexusNodes.$inferSelect;
|
|
337
|
+
export type NewNexusNodeRow = typeof nexusNodes.$inferInsert;
|
|
338
|
+
export type NexusRelationRow = typeof nexusRelations.$inferSelect;
|
|
339
|
+
export type NewNexusRelationRow = typeof nexusRelations.$inferInsert;
|