@cleocode/core 2026.4.29 → 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
|
@@ -1603,6 +1603,174 @@ export async function memorySearchHybrid(
|
|
|
1603
1603
|
}
|
|
1604
1604
|
}
|
|
1605
1605
|
|
|
1606
|
+
// ============================================================================
|
|
1607
|
+
// Brain Graph Traversal Operations (T535)
|
|
1608
|
+
// ============================================================================
|
|
1609
|
+
|
|
1610
|
+
/**
|
|
1611
|
+
* BFS traversal of the brain knowledge graph from a seed node.
|
|
1612
|
+
*
|
|
1613
|
+
* @param params - Traversal parameters: nodeId and optional maxDepth (default 3)
|
|
1614
|
+
* @param projectRoot - Optional project root path; defaults to resolved root
|
|
1615
|
+
* @returns EngineResult with traversal nodes annotated with depth
|
|
1616
|
+
*
|
|
1617
|
+
* @remarks
|
|
1618
|
+
* Uses a recursive CTE against brain_page_nodes / brain_page_edges.
|
|
1619
|
+
* Follows edges bidirectionally. Returns the seed node at depth 0.
|
|
1620
|
+
*
|
|
1621
|
+
* @example
|
|
1622
|
+
* ```typescript
|
|
1623
|
+
* const result = await memoryGraphTrace({ nodeId: 'decision:D-abc123', maxDepth: 2 }, '/project');
|
|
1624
|
+
* ```
|
|
1625
|
+
*/
|
|
1626
|
+
export async function memoryGraphTrace(
|
|
1627
|
+
params: { nodeId: string; maxDepth?: number },
|
|
1628
|
+
projectRoot?: string,
|
|
1629
|
+
): Promise<EngineResult> {
|
|
1630
|
+
if (!params.nodeId) {
|
|
1631
|
+
return { success: false, error: { code: 'E_INVALID_INPUT', message: 'nodeId is required' } };
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
try {
|
|
1635
|
+
const root = resolveRoot(projectRoot);
|
|
1636
|
+
const { traceBrainGraph } = await import('./graph-queries.js');
|
|
1637
|
+
const nodes = await traceBrainGraph(root, params.nodeId, params.maxDepth ?? 3);
|
|
1638
|
+
|
|
1639
|
+
if (nodes.length === 0) {
|
|
1640
|
+
return {
|
|
1641
|
+
success: false,
|
|
1642
|
+
error: { code: 'E_NOT_FOUND', message: `Node '${params.nodeId}' not found in brain graph` },
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
return { success: true, data: { nodes, total: nodes.length, seed: params.nodeId } };
|
|
1647
|
+
} catch (error) {
|
|
1648
|
+
return {
|
|
1649
|
+
success: false,
|
|
1650
|
+
error: {
|
|
1651
|
+
code: 'E_GRAPH_TRACE',
|
|
1652
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1653
|
+
},
|
|
1654
|
+
};
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
/**
|
|
1659
|
+
* Return the immediate (1-hop) neighbours of a brain graph node.
|
|
1660
|
+
*
|
|
1661
|
+
* @param params - Parameters: nodeId, optional edgeType filter
|
|
1662
|
+
* @param projectRoot - Optional project root path; defaults to resolved root
|
|
1663
|
+
* @returns EngineResult with neighbour nodes and edge metadata
|
|
1664
|
+
*
|
|
1665
|
+
* @remarks
|
|
1666
|
+
* Follows edges in both directions. Results include direction ('in'/'out'),
|
|
1667
|
+
* edge type, and weight.
|
|
1668
|
+
*
|
|
1669
|
+
* @example
|
|
1670
|
+
* ```typescript
|
|
1671
|
+
* const result = await memoryGraphRelated({ nodeId: 'decision:D-abc123', edgeType: 'applies_to' }, '/project');
|
|
1672
|
+
* ```
|
|
1673
|
+
*/
|
|
1674
|
+
export async function memoryGraphRelated(
|
|
1675
|
+
params: { nodeId: string; edgeType?: string },
|
|
1676
|
+
projectRoot?: string,
|
|
1677
|
+
): Promise<EngineResult> {
|
|
1678
|
+
if (!params.nodeId) {
|
|
1679
|
+
return { success: false, error: { code: 'E_INVALID_INPUT', message: 'nodeId is required' } };
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
try {
|
|
1683
|
+
const root = resolveRoot(projectRoot);
|
|
1684
|
+
const { relatedBrainNodes } = await import('./graph-queries.js');
|
|
1685
|
+
const related = await relatedBrainNodes(root, params.nodeId, params.edgeType);
|
|
1686
|
+
return { success: true, data: { related, total: related.length, seed: params.nodeId } };
|
|
1687
|
+
} catch (error) {
|
|
1688
|
+
return {
|
|
1689
|
+
success: false,
|
|
1690
|
+
error: {
|
|
1691
|
+
code: 'E_GRAPH_RELATED',
|
|
1692
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1693
|
+
},
|
|
1694
|
+
};
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
/**
|
|
1699
|
+
* Return a 360-degree context view of a single brain graph node.
|
|
1700
|
+
*
|
|
1701
|
+
* @param params - Parameters: nodeId
|
|
1702
|
+
* @param projectRoot - Optional project root path; defaults to resolved root
|
|
1703
|
+
* @returns EngineResult with the node, all edges, and neighbouring nodes
|
|
1704
|
+
*
|
|
1705
|
+
* @remarks
|
|
1706
|
+
* Includes the node itself, in-edges, out-edges, and all immediately
|
|
1707
|
+
* reachable neighbour nodes with their edge relationships.
|
|
1708
|
+
*
|
|
1709
|
+
* @example
|
|
1710
|
+
* ```typescript
|
|
1711
|
+
* const result = await memoryGraphContext({ nodeId: 'decision:D-abc123' }, '/project');
|
|
1712
|
+
* ```
|
|
1713
|
+
*/
|
|
1714
|
+
export async function memoryGraphContext(
|
|
1715
|
+
params: { nodeId: string },
|
|
1716
|
+
projectRoot?: string,
|
|
1717
|
+
): Promise<EngineResult> {
|
|
1718
|
+
if (!params.nodeId) {
|
|
1719
|
+
return { success: false, error: { code: 'E_INVALID_INPUT', message: 'nodeId is required' } };
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
try {
|
|
1723
|
+
const root = resolveRoot(projectRoot);
|
|
1724
|
+
const { contextBrainNode } = await import('./graph-queries.js');
|
|
1725
|
+
const context = await contextBrainNode(root, params.nodeId);
|
|
1726
|
+
|
|
1727
|
+
if (!context) {
|
|
1728
|
+
return {
|
|
1729
|
+
success: false,
|
|
1730
|
+
error: { code: 'E_NOT_FOUND', message: `Node '${params.nodeId}' not found in brain graph` },
|
|
1731
|
+
};
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
return { success: true, data: context };
|
|
1735
|
+
} catch (error) {
|
|
1736
|
+
return {
|
|
1737
|
+
success: false,
|
|
1738
|
+
error: {
|
|
1739
|
+
code: 'E_GRAPH_CONTEXT',
|
|
1740
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1741
|
+
},
|
|
1742
|
+
};
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
/**
|
|
1747
|
+
* Return aggregate statistics for the brain knowledge graph.
|
|
1748
|
+
*
|
|
1749
|
+
* @param projectRoot - Optional project root path; defaults to resolved root
|
|
1750
|
+
* @returns EngineResult with node counts by type, edge counts by type, and totals
|
|
1751
|
+
*
|
|
1752
|
+
* @example
|
|
1753
|
+
* ```typescript
|
|
1754
|
+
* const result = await memoryGraphStatsFull('/project');
|
|
1755
|
+
* ```
|
|
1756
|
+
*/
|
|
1757
|
+
export async function memoryGraphStatsFull(projectRoot?: string): Promise<EngineResult> {
|
|
1758
|
+
try {
|
|
1759
|
+
const root = resolveRoot(projectRoot);
|
|
1760
|
+
const { graphStats } = await import('./graph-queries.js');
|
|
1761
|
+
const stats = await graphStats(root);
|
|
1762
|
+
return { success: true, data: stats };
|
|
1763
|
+
} catch (error) {
|
|
1764
|
+
return {
|
|
1765
|
+
success: false,
|
|
1766
|
+
error: {
|
|
1767
|
+
code: 'E_GRAPH_STATS',
|
|
1768
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1769
|
+
},
|
|
1770
|
+
};
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1606
1774
|
/**
|
|
1607
1775
|
* Remove a node or edge from the PageIndex graph.
|
|
1608
1776
|
*
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Graph auto-population helpers for CLEO BRAIN.
|
|
3
|
+
*
|
|
4
|
+
* Provides upsertGraphNode and addGraphEdge helpers that write to the
|
|
5
|
+
* brain_page_nodes and brain_page_edges tables whenever memory entries
|
|
6
|
+
* are created via the legitimate write paths (storeDecision, observeBrain,
|
|
7
|
+
* storePattern, storeLearning, and task completion).
|
|
8
|
+
*
|
|
9
|
+
* Design constraints:
|
|
10
|
+
* - All writes are BEST-EFFORT — never block or fail the primary operation.
|
|
11
|
+
* - All writes are gated on brain.autoCapture via isAutoCaptureEnabled.
|
|
12
|
+
* - Uses INSERT OR REPLACE (onConflictDoUpdate) for upsert semantics.
|
|
13
|
+
* - Edge inserts are idempotent via the composite PK (fromId, toId, edgeType).
|
|
14
|
+
*
|
|
15
|
+
* @task T537
|
|
16
|
+
* @epic T523
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { createHash } from 'node:crypto';
|
|
20
|
+
import type { BrainEdgeType, BrainNodeType } from '../store/brain-schema.js';
|
|
21
|
+
import { brainPageEdges, brainPageNodes } from '../store/brain-schema.js';
|
|
22
|
+
import { getBrainDb } from '../store/brain-sqlite.js';
|
|
23
|
+
|
|
24
|
+
// Re-export types so callers can import them from this module without
|
|
25
|
+
// reaching into brain-schema directly.
|
|
26
|
+
export type { BrainEdgeType, BrainNodeType };
|
|
27
|
+
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Internal helpers
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Return true when brain.autoCapture is enabled for the project.
|
|
34
|
+
* Delegates to the shared isAutoCaptureEnabled helper in handler-helpers.ts.
|
|
35
|
+
* Returns false on any error to keep graph writes safely disabled when the
|
|
36
|
+
* config or brain.db is unavailable.
|
|
37
|
+
*/
|
|
38
|
+
async function shouldAutoPopulateGraph(projectRoot: string): Promise<boolean> {
|
|
39
|
+
try {
|
|
40
|
+
const { isAutoCaptureEnabled } = await import('../hooks/handlers/handler-helpers.js');
|
|
41
|
+
return isAutoCaptureEnabled(projectRoot);
|
|
42
|
+
} catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// Public API
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Upsert a graph node for a typed table entry.
|
|
53
|
+
*
|
|
54
|
+
* Uses INSERT OR REPLACE to handle both new and existing entries. If the
|
|
55
|
+
* node already exists (same id), its label, qualityScore, lastActivityAt,
|
|
56
|
+
* updatedAt, and metadataJson are refreshed while createdAt is preserved.
|
|
57
|
+
*
|
|
58
|
+
* The contentHash is derived from a SHA-256 prefix of the canonical content.
|
|
59
|
+
* External-reference nodes (task, session, epic) may pass an empty string for
|
|
60
|
+
* content; their hash will be null so duplicates are not rejected.
|
|
61
|
+
*
|
|
62
|
+
* This function is gated on brain.autoCapture. If the gate is disabled or any
|
|
63
|
+
* error occurs, it returns silently without throwing.
|
|
64
|
+
*
|
|
65
|
+
* @param projectRoot - Absolute path to the project root directory.
|
|
66
|
+
* @param nodeId - Stable composite ID in the form '<type>:<source-id>'.
|
|
67
|
+
* @param nodeType - Discriminated type from BRAIN_NODE_TYPES.
|
|
68
|
+
* @param label - Human-readable label (title, task ID, etc.).
|
|
69
|
+
* @param qualityScore - 0.0 (noise) to 1.0 (canonical).
|
|
70
|
+
* @param content - Canonical text used to derive the content hash.
|
|
71
|
+
* @param metadata - Optional type-specific metadata blob.
|
|
72
|
+
*
|
|
73
|
+
* @task T537
|
|
74
|
+
*/
|
|
75
|
+
export async function upsertGraphNode(
|
|
76
|
+
projectRoot: string,
|
|
77
|
+
nodeId: string,
|
|
78
|
+
nodeType: BrainNodeType,
|
|
79
|
+
label: string,
|
|
80
|
+
qualityScore: number,
|
|
81
|
+
content: string,
|
|
82
|
+
metadata?: Record<string, unknown>,
|
|
83
|
+
): Promise<void> {
|
|
84
|
+
try {
|
|
85
|
+
if (!(await shouldAutoPopulateGraph(projectRoot))) return;
|
|
86
|
+
|
|
87
|
+
const db = await getBrainDb(projectRoot);
|
|
88
|
+
const now = new Date().toISOString().replace('T', ' ').slice(0, 19);
|
|
89
|
+
|
|
90
|
+
// Only compute a content hash for non-trivial content (external reference
|
|
91
|
+
// nodes like task/session/epic may have empty content).
|
|
92
|
+
const trimmed = content.trim().toLowerCase();
|
|
93
|
+
const contentHash = trimmed
|
|
94
|
+
? createHash('sha256').update(trimmed).digest('hex').substring(0, 16)
|
|
95
|
+
: null;
|
|
96
|
+
|
|
97
|
+
await db
|
|
98
|
+
.insert(brainPageNodes)
|
|
99
|
+
.values({
|
|
100
|
+
id: nodeId,
|
|
101
|
+
nodeType,
|
|
102
|
+
label: label.substring(0, 200),
|
|
103
|
+
qualityScore,
|
|
104
|
+
contentHash,
|
|
105
|
+
metadataJson: metadata ? JSON.stringify(metadata) : null,
|
|
106
|
+
lastActivityAt: now,
|
|
107
|
+
createdAt: now,
|
|
108
|
+
updatedAt: now,
|
|
109
|
+
})
|
|
110
|
+
.onConflictDoUpdate({
|
|
111
|
+
target: brainPageNodes.id,
|
|
112
|
+
set: {
|
|
113
|
+
label: label.substring(0, 200),
|
|
114
|
+
qualityScore,
|
|
115
|
+
lastActivityAt: now,
|
|
116
|
+
updatedAt: now,
|
|
117
|
+
metadataJson: metadata ? JSON.stringify(metadata) : null,
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
} catch (err) {
|
|
121
|
+
// Log but never surface — this is a best-effort side effect.
|
|
122
|
+
console.warn('[brain-graph] upsertGraphNode failed:', err);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Add a directed, typed edge between two graph nodes (idempotent).
|
|
128
|
+
*
|
|
129
|
+
* The composite primary key (fromId, toId, edgeType) prevents duplicate edges
|
|
130
|
+
* of the same type. Conflicting rows are ignored so this is safe to call
|
|
131
|
+
* multiple times with the same arguments.
|
|
132
|
+
*
|
|
133
|
+
* This function is gated on brain.autoCapture. If the gate is disabled or any
|
|
134
|
+
* error occurs, it returns silently without throwing.
|
|
135
|
+
*
|
|
136
|
+
* @param projectRoot - Absolute path to the project root directory.
|
|
137
|
+
* @param fromId - Source node ID (brain_page_nodes.id).
|
|
138
|
+
* @param toId - Target node ID (brain_page_nodes.id or external nexus ID).
|
|
139
|
+
* @param edgeType - Typed relationship from BRAIN_EDGE_TYPES.
|
|
140
|
+
* @param weight - Edge confidence/weight (0.0–1.0). Defaults to 1.0.
|
|
141
|
+
* @param provenance - Human-readable note on why this edge was emitted.
|
|
142
|
+
*
|
|
143
|
+
* @task T537
|
|
144
|
+
*/
|
|
145
|
+
export async function addGraphEdge(
|
|
146
|
+
projectRoot: string,
|
|
147
|
+
fromId: string,
|
|
148
|
+
toId: string,
|
|
149
|
+
edgeType: BrainEdgeType,
|
|
150
|
+
weight = 1.0,
|
|
151
|
+
provenance?: string,
|
|
152
|
+
): Promise<void> {
|
|
153
|
+
try {
|
|
154
|
+
if (!(await shouldAutoPopulateGraph(projectRoot))) return;
|
|
155
|
+
|
|
156
|
+
const db = await getBrainDb(projectRoot);
|
|
157
|
+
const now = new Date().toISOString().replace('T', ' ').slice(0, 19);
|
|
158
|
+
|
|
159
|
+
await db
|
|
160
|
+
.insert(brainPageEdges)
|
|
161
|
+
.values({
|
|
162
|
+
fromId,
|
|
163
|
+
toId,
|
|
164
|
+
edgeType,
|
|
165
|
+
weight,
|
|
166
|
+
provenance: provenance ?? null,
|
|
167
|
+
createdAt: now,
|
|
168
|
+
})
|
|
169
|
+
.onConflictDoNothing();
|
|
170
|
+
} catch (err) {
|
|
171
|
+
console.warn('[brain-graph] addGraphEdge failed:', err);
|
|
172
|
+
}
|
|
173
|
+
}
|