@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.
Files changed (89) hide show
  1. package/dist/bootstrap.d.ts +35 -0
  2. package/dist/bootstrap.d.ts.map +1 -1
  3. package/dist/code/index.d.ts +8 -4
  4. package/dist/code/index.d.ts.map +1 -1
  5. package/dist/code/parser.d.ts +22 -9
  6. package/dist/code/parser.d.ts.map +1 -1
  7. package/dist/hooks/handlers/session-hooks.d.ts +11 -4
  8. package/dist/hooks/handlers/session-hooks.d.ts.map +1 -1
  9. package/dist/hooks/payload-schemas.d.ts +6 -6
  10. package/dist/index.d.ts +2 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +3859 -3008
  13. package/dist/index.js.map +4 -4
  14. package/dist/internal.d.ts +10 -7
  15. package/dist/internal.d.ts.map +1 -1
  16. package/dist/lib/tree-sitter-languages.d.ts +11 -7
  17. package/dist/lib/tree-sitter-languages.d.ts.map +1 -1
  18. package/dist/memory/auto-extract.d.ts +27 -15
  19. package/dist/memory/auto-extract.d.ts.map +1 -1
  20. package/dist/memory/brain-backfill.d.ts +59 -0
  21. package/dist/memory/brain-backfill.d.ts.map +1 -0
  22. package/dist/memory/brain-purge.d.ts +51 -0
  23. package/dist/memory/brain-purge.d.ts.map +1 -0
  24. package/dist/memory/brain-retrieval.d.ts.map +1 -1
  25. package/dist/memory/brain-search.d.ts.map +1 -1
  26. package/dist/memory/decisions.d.ts.map +1 -1
  27. package/dist/memory/engine-compat.d.ts +71 -0
  28. package/dist/memory/engine-compat.d.ts.map +1 -1
  29. package/dist/memory/graph-auto-populate.d.ts +65 -0
  30. package/dist/memory/graph-auto-populate.d.ts.map +1 -0
  31. package/dist/memory/graph-queries.d.ts +127 -0
  32. package/dist/memory/graph-queries.d.ts.map +1 -0
  33. package/dist/memory/learnings.d.ts +2 -0
  34. package/dist/memory/learnings.d.ts.map +1 -1
  35. package/dist/memory/patterns.d.ts +2 -0
  36. package/dist/memory/patterns.d.ts.map +1 -1
  37. package/dist/memory/quality-scoring.d.ts +90 -0
  38. package/dist/memory/quality-scoring.d.ts.map +1 -0
  39. package/dist/sessions/session-memory-bridge.d.ts +16 -10
  40. package/dist/sessions/session-memory-bridge.d.ts.map +1 -1
  41. package/dist/store/brain-accessor.d.ts +7 -0
  42. package/dist/store/brain-accessor.d.ts.map +1 -1
  43. package/dist/store/brain-schema.d.ts +185 -11
  44. package/dist/store/brain-schema.d.ts.map +1 -1
  45. package/dist/store/brain-sqlite.d.ts.map +1 -1
  46. package/dist/store/nexus-schema.d.ts +480 -2
  47. package/dist/store/nexus-schema.d.ts.map +1 -1
  48. package/dist/store/tasks-schema.d.ts +9 -9
  49. package/dist/store/validation-schemas.d.ts +44 -28
  50. package/dist/store/validation-schemas.d.ts.map +1 -1
  51. package/dist/system/dependencies.d.ts +43 -0
  52. package/dist/system/dependencies.d.ts.map +1 -0
  53. package/dist/system/health.d.ts +3 -0
  54. package/dist/system/health.d.ts.map +1 -1
  55. package/dist/tasks/complete.d.ts.map +1 -1
  56. package/package.json +19 -19
  57. package/src/bootstrap.ts +124 -0
  58. package/src/code/index.ts +20 -4
  59. package/src/code/parser.ts +310 -110
  60. package/src/hooks/handlers/__tests__/hook-automation-e2e.test.ts +19 -45
  61. package/src/hooks/handlers/__tests__/session-hooks.test.ts +42 -54
  62. package/src/hooks/handlers/session-hooks.ts +11 -33
  63. package/src/index.ts +14 -0
  64. package/src/internal.ts +37 -7
  65. package/src/lib/tree-sitter-languages.ts +11 -7
  66. package/src/memory/__tests__/auto-extract.test.ts +20 -82
  67. package/src/memory/__tests__/embedding-pipeline.test.ts +389 -0
  68. package/src/memory/auto-extract.ts +34 -120
  69. package/src/memory/brain-backfill.ts +471 -0
  70. package/src/memory/brain-purge.ts +315 -0
  71. package/src/memory/brain-retrieval.ts +43 -2
  72. package/src/memory/brain-search.ts +23 -6
  73. package/src/memory/decisions.ts +76 -3
  74. package/src/memory/engine-compat.ts +168 -0
  75. package/src/memory/graph-auto-populate.ts +173 -0
  76. package/src/memory/graph-queries.ts +424 -0
  77. package/src/memory/learnings.ts +55 -7
  78. package/src/memory/patterns.ts +66 -13
  79. package/src/memory/quality-scoring.ts +173 -0
  80. package/src/sessions/__tests__/session-memory-bridge.test.ts +27 -49
  81. package/src/sessions/session-memory-bridge.ts +19 -47
  82. package/src/store/__tests__/brain-accessor-pageindex.test.ts +93 -22
  83. package/src/store/brain-accessor.ts +48 -2
  84. package/src/store/brain-schema.ts +165 -13
  85. package/src/store/brain-sqlite.ts +35 -0
  86. package/src/store/nexus-schema.ts +257 -3
  87. package/src/system/dependencies.ts +534 -0
  88. package/src/system/health.ts +126 -22
  89. 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
+ }