clawvault 3.1.0 → 3.2.1

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 (273) hide show
  1. package/README.md +422 -141
  2. package/bin/clawvault.js +10 -2
  3. package/bin/command-registration.test.js +3 -1
  4. package/bin/command-runtime.js +9 -1
  5. package/bin/register-core-commands.js +23 -28
  6. package/bin/register-maintenance-commands.js +39 -3
  7. package/bin/register-query-commands.js +58 -29
  8. package/bin/register-tailscale-commands.js +106 -0
  9. package/bin/register-task-commands.js +18 -1
  10. package/bin/register-task-commands.test.js +16 -0
  11. package/bin/register-vault-operations-commands.js +29 -1
  12. package/bin/register-workgraph-commands.js +451 -0
  13. package/dashboard/lib/graph-diff.js +104 -0
  14. package/dashboard/lib/graph-diff.test.js +75 -0
  15. package/dashboard/lib/vault-parser.js +556 -0
  16. package/dashboard/lib/vault-parser.test.js +254 -0
  17. package/dashboard/public/app.js +796 -0
  18. package/dashboard/public/index.html +52 -0
  19. package/dashboard/public/styles.css +221 -0
  20. package/dashboard/server.js +374 -0
  21. package/dist/{chunk-C7OK5WKP.js → chunk-2JQ3O2YL.js} +4 -4
  22. package/dist/{chunk-VR5NE7PZ.js → chunk-2RAZ4ZFE.js} +1 -1
  23. package/dist/{chunk-F2JEUD4J.js → chunk-4ITRXIVT.js} +5 -7
  24. package/dist/{chunk-GUKMRGM7.js → chunk-4OXMU5S2.js} +1 -1
  25. package/dist/chunk-5PJ4STIC.js +465 -0
  26. package/dist/{chunk-62YTUT6J.js → chunk-AZYOKJYC.js} +2 -2
  27. package/dist/chunk-BSJ6RIT7.js +447 -0
  28. package/dist/chunk-ECRZL5XR.js +50 -0
  29. package/dist/chunk-ERNE2FZ5.js +189 -0
  30. package/dist/{chunk-WAZ3NLWL.js → chunk-F55HGNU4.js} +0 -47
  31. package/dist/{chunk-VGLOTGAS.js → chunk-FAKNOB7Y.js} +2 -2
  32. package/dist/{chunk-QK3UCXWL.js → chunk-FHFUXL6G.js} +2 -2
  33. package/dist/chunk-GNJL4YGR.js +79 -0
  34. package/dist/chunk-HR4KN6S2.js +152 -0
  35. package/dist/{chunk-OZ7RIXTO.js → chunk-IIOU45CK.js} +1 -1
  36. package/dist/chunk-IJBFGPCS.js +33 -0
  37. package/dist/chunk-IVRIKYFE.js +520 -0
  38. package/dist/chunk-K7PNYS45.js +93 -0
  39. package/dist/chunk-MDIH26GC.js +183 -0
  40. package/dist/{chunk-LYHGEHXG.js → chunk-MFAWT5O5.js} +0 -1
  41. package/dist/{chunk-H34S76MB.js → chunk-MNPUYCHQ.js} +6 -6
  42. package/dist/chunk-NTOPJI7W.js +207 -0
  43. package/dist/{chunk-QBLMXKF2.js → chunk-OIWVQYQF.js} +1 -1
  44. package/dist/chunk-PG56HX5T.js +154 -0
  45. package/dist/{chunk-LNJA2UGL.js → chunk-PI4WMLMG.js} +7 -84
  46. package/dist/chunk-QMHPQYUV.js +363 -0
  47. package/dist/{chunk-H62BP7RI.js → chunk-QPDDIHXE.js} +209 -43
  48. package/dist/{chunk-N2AXRYLC.js → chunk-QWQ3TIKS.js} +1 -1
  49. package/dist/{chunk-3DHXQHYG.js → chunk-R2MIW5G7.js} +1 -1
  50. package/dist/{chunk-SJSFRIYS.js → chunk-S5OJEGFG.js} +2 -2
  51. package/dist/chunk-SS4B7P7V.js +99 -0
  52. package/dist/chunk-TIGW564L.js +628 -0
  53. package/dist/chunk-U67V476Y.js +35 -0
  54. package/dist/{chunk-JY6FYXIT.js → chunk-UCQAOZHW.js} +6 -11
  55. package/dist/{chunk-ITPEXLHA.js → chunk-URXDAUVH.js} +24 -5
  56. package/dist/chunk-WIOLLGAD.js +190 -0
  57. package/dist/{chunk-3WRJEKN4.js → chunk-WJVWINEM.js} +72 -8
  58. package/dist/chunk-WMGIIABP.js +15 -0
  59. package/dist/{chunk-33UGEQRT.js → chunk-X3SPPUFG.js} +151 -64
  60. package/dist/{chunk-3NSBOUT3.js → chunk-Y3TIJEBP.js} +314 -79
  61. package/dist/chunk-Y6VJKXGL.js +373 -0
  62. package/dist/{chunk-LI4O6NVK.js → chunk-YDWHS4LJ.js} +49 -9
  63. package/dist/{chunk-U55BGUAU.js → chunk-YNIPYN4F.js} +5 -5
  64. package/dist/chunk-YXQCA6B7.js +226 -0
  65. package/dist/cli/index.js +26 -22
  66. package/dist/commands/archive.js +3 -3
  67. package/dist/commands/backlog.js +3 -3
  68. package/dist/commands/blocked.js +3 -3
  69. package/dist/commands/canvas.d.ts +15 -0
  70. package/dist/commands/canvas.js +200 -0
  71. package/dist/commands/checkpoint.js +2 -2
  72. package/dist/commands/compat.js +2 -2
  73. package/dist/commands/context.js +7 -5
  74. package/dist/commands/doctor.d.ts +11 -7
  75. package/dist/commands/doctor.js +16 -14
  76. package/dist/commands/embed.js +5 -6
  77. package/dist/commands/entities.js +2 -2
  78. package/dist/commands/graph.js +3 -3
  79. package/dist/commands/inject.d.ts +1 -1
  80. package/dist/commands/inject.js +4 -5
  81. package/dist/commands/kanban.js +4 -4
  82. package/dist/commands/link.js +2 -2
  83. package/dist/commands/migrate-observations.js +4 -4
  84. package/dist/commands/observe.d.ts +0 -1
  85. package/dist/commands/observe.js +13 -12
  86. package/dist/commands/project.js +5 -5
  87. package/dist/commands/rebuild-embeddings.d.ts +21 -0
  88. package/dist/commands/rebuild-embeddings.js +91 -0
  89. package/dist/commands/rebuild.js +12 -11
  90. package/dist/commands/recover.js +3 -3
  91. package/dist/commands/reflect.js +6 -7
  92. package/dist/commands/repair-session.js +1 -1
  93. package/dist/commands/replay.js +14 -14
  94. package/dist/commands/session-recap.js +1 -1
  95. package/dist/commands/setup.d.ts +2 -89
  96. package/dist/commands/setup.js +3 -21
  97. package/dist/commands/shell-init.js +1 -1
  98. package/dist/commands/sleep.d.ts +1 -1
  99. package/dist/commands/sleep.js +18 -17
  100. package/dist/commands/status.d.ts +2 -0
  101. package/dist/commands/status.js +40 -30
  102. package/dist/commands/sync-bd.d.ts +10 -0
  103. package/dist/commands/sync-bd.js +10 -0
  104. package/dist/commands/tailscale.d.ts +52 -0
  105. package/dist/commands/tailscale.js +26 -0
  106. package/dist/commands/task.js +4 -4
  107. package/dist/commands/template.js +2 -2
  108. package/dist/commands/wake.d.ts +1 -1
  109. package/dist/commands/wake.js +11 -10
  110. package/dist/index.d.ts +334 -191
  111. package/dist/index.js +432 -108
  112. package/dist/{inject-Bzi5E-By.d.ts → inject-DYUrDqQO.d.ts} +3 -3
  113. package/dist/ledger-B7g7jhqG.d.ts +44 -0
  114. package/dist/lib/auto-linker.js +1 -1
  115. package/dist/lib/canvas-layout.d.ts +115 -0
  116. package/dist/lib/canvas-layout.js +35 -0
  117. package/dist/lib/config.d.ts +27 -3
  118. package/dist/lib/config.js +4 -2
  119. package/dist/lib/entity-index.js +1 -1
  120. package/dist/lib/project-utils.js +4 -4
  121. package/dist/lib/session-repair.js +1 -1
  122. package/dist/lib/session-utils.js +1 -1
  123. package/dist/lib/tailscale.d.ts +225 -0
  124. package/dist/lib/tailscale.js +50 -0
  125. package/dist/lib/task-utils.js +3 -3
  126. package/dist/lib/template-engine.js +1 -1
  127. package/dist/lib/webdav.d.ts +109 -0
  128. package/dist/lib/webdav.js +35 -0
  129. package/dist/plugin/index.d.ts +344 -28
  130. package/dist/plugin/index.js +3919 -227
  131. package/dist/registry-BR4326o0.d.ts +30 -0
  132. package/dist/store-CA-6sKCJ.d.ts +34 -0
  133. package/dist/thread-B9LhXNU0.d.ts +41 -0
  134. package/dist/{types-Y2_Um2Ls.d.ts → types-BbWJoC1c.d.ts} +1 -44
  135. package/dist/workgraph/index.d.ts +5 -0
  136. package/dist/workgraph/index.js +23 -0
  137. package/dist/workgraph/ledger.d.ts +2 -0
  138. package/dist/workgraph/ledger.js +25 -0
  139. package/dist/workgraph/registry.d.ts +2 -0
  140. package/dist/workgraph/registry.js +19 -0
  141. package/dist/workgraph/store.d.ts +2 -0
  142. package/dist/workgraph/store.js +25 -0
  143. package/dist/workgraph/thread.d.ts +2 -0
  144. package/dist/workgraph/thread.js +25 -0
  145. package/dist/workgraph/types.d.ts +54 -0
  146. package/dist/workgraph/types.js +7 -0
  147. package/hooks/clawvault/HOOK.md +113 -0
  148. package/hooks/clawvault/handler.js +1559 -0
  149. package/hooks/clawvault/handler.test.js +510 -0
  150. package/hooks/clawvault/openclaw.plugin.json +72 -0
  151. package/openclaw.plugin.json +235 -30
  152. package/package.json +20 -20
  153. package/dist/chunk-3RG5ZIWI.js +0 -10
  154. package/dist/chunk-3ZIH425O.js +0 -871
  155. package/dist/chunk-6U6MK36V.js +0 -205
  156. package/dist/chunk-CMB7UL7C.js +0 -327
  157. package/dist/chunk-D2H45LON.js +0 -1074
  158. package/dist/chunk-E7MFQB6D.js +0 -163
  159. package/dist/chunk-GQSLDZTS.js +0 -560
  160. package/dist/chunk-MFM6K7PU.js +0 -374
  161. package/dist/chunk-MXSSG3QU.js +0 -42
  162. package/dist/chunk-OCGVIN3L.js +0 -88
  163. package/dist/chunk-PAH27GSN.js +0 -108
  164. package/dist/chunk-YCUNCH2I.js +0 -78
  165. package/dist/cli/index.cjs +0 -8584
  166. package/dist/cli/index.d.cts +0 -5
  167. package/dist/commands/archive.cjs +0 -287
  168. package/dist/commands/archive.d.cts +0 -11
  169. package/dist/commands/backlog.cjs +0 -721
  170. package/dist/commands/backlog.d.cts +0 -53
  171. package/dist/commands/blocked.cjs +0 -204
  172. package/dist/commands/blocked.d.cts +0 -26
  173. package/dist/commands/checkpoint.cjs +0 -244
  174. package/dist/commands/checkpoint.d.cts +0 -41
  175. package/dist/commands/compat.cjs +0 -294
  176. package/dist/commands/compat.d.cts +0 -28
  177. package/dist/commands/context.cjs +0 -2990
  178. package/dist/commands/context.d.cts +0 -2
  179. package/dist/commands/doctor.cjs +0 -2986
  180. package/dist/commands/doctor.d.cts +0 -21
  181. package/dist/commands/embed.cjs +0 -232
  182. package/dist/commands/embed.d.cts +0 -17
  183. package/dist/commands/entities.cjs +0 -141
  184. package/dist/commands/entities.d.cts +0 -7
  185. package/dist/commands/graph.cjs +0 -501
  186. package/dist/commands/graph.d.cts +0 -21
  187. package/dist/commands/inject.cjs +0 -1636
  188. package/dist/commands/inject.d.cts +0 -2
  189. package/dist/commands/kanban.cjs +0 -884
  190. package/dist/commands/kanban.d.cts +0 -63
  191. package/dist/commands/link.cjs +0 -965
  192. package/dist/commands/link.d.cts +0 -11
  193. package/dist/commands/migrate-observations.cjs +0 -362
  194. package/dist/commands/migrate-observations.d.cts +0 -19
  195. package/dist/commands/observe.cjs +0 -4099
  196. package/dist/commands/observe.d.cts +0 -23
  197. package/dist/commands/project.cjs +0 -1341
  198. package/dist/commands/project.d.cts +0 -85
  199. package/dist/commands/rebuild.cjs +0 -3136
  200. package/dist/commands/rebuild.d.cts +0 -11
  201. package/dist/commands/recover.cjs +0 -361
  202. package/dist/commands/recover.d.cts +0 -38
  203. package/dist/commands/reflect.cjs +0 -1008
  204. package/dist/commands/reflect.d.cts +0 -11
  205. package/dist/commands/repair-session.cjs +0 -457
  206. package/dist/commands/repair-session.d.cts +0 -38
  207. package/dist/commands/replay.cjs +0 -4103
  208. package/dist/commands/replay.d.cts +0 -16
  209. package/dist/commands/session-recap.cjs +0 -353
  210. package/dist/commands/session-recap.d.cts +0 -27
  211. package/dist/commands/setup.cjs +0 -1278
  212. package/dist/commands/setup.d.cts +0 -99
  213. package/dist/commands/shell-init.cjs +0 -75
  214. package/dist/commands/shell-init.d.cts +0 -7
  215. package/dist/commands/sleep.cjs +0 -6029
  216. package/dist/commands/sleep.d.cts +0 -36
  217. package/dist/commands/status.cjs +0 -2737
  218. package/dist/commands/status.d.cts +0 -52
  219. package/dist/commands/task.cjs +0 -1236
  220. package/dist/commands/task.d.cts +0 -97
  221. package/dist/commands/template.cjs +0 -457
  222. package/dist/commands/template.d.cts +0 -36
  223. package/dist/commands/wake.cjs +0 -2627
  224. package/dist/commands/wake.d.cts +0 -22
  225. package/dist/context-BUGaWpyL.d.cts +0 -46
  226. package/dist/index.cjs +0 -12373
  227. package/dist/index.d.cts +0 -854
  228. package/dist/inject-Bzi5E-By.d.cts +0 -137
  229. package/dist/lib/auto-linker.cjs +0 -176
  230. package/dist/lib/auto-linker.d.cts +0 -26
  231. package/dist/lib/config.cjs +0 -78
  232. package/dist/lib/config.d.cts +0 -11
  233. package/dist/lib/entity-index.cjs +0 -84
  234. package/dist/lib/entity-index.d.cts +0 -26
  235. package/dist/lib/project-utils.cjs +0 -864
  236. package/dist/lib/project-utils.d.cts +0 -97
  237. package/dist/lib/session-repair.cjs +0 -239
  238. package/dist/lib/session-repair.d.cts +0 -110
  239. package/dist/lib/session-utils.cjs +0 -209
  240. package/dist/lib/session-utils.d.cts +0 -63
  241. package/dist/lib/task-utils.cjs +0 -1137
  242. package/dist/lib/task-utils.d.cts +0 -208
  243. package/dist/lib/template-engine.cjs +0 -47
  244. package/dist/lib/template-engine.d.cts +0 -11
  245. package/dist/plugin/index.cjs +0 -1907
  246. package/dist/plugin/index.d.cts +0 -36
  247. package/dist/plugin/inject.cjs +0 -356
  248. package/dist/plugin/inject.d.cts +0 -54
  249. package/dist/plugin/inject.d.ts +0 -54
  250. package/dist/plugin/inject.js +0 -17
  251. package/dist/plugin/observe.cjs +0 -631
  252. package/dist/plugin/observe.d.cts +0 -39
  253. package/dist/plugin/observe.d.ts +0 -39
  254. package/dist/plugin/observe.js +0 -18
  255. package/dist/plugin/templates.cjs +0 -593
  256. package/dist/plugin/templates.d.cts +0 -52
  257. package/dist/plugin/templates.d.ts +0 -52
  258. package/dist/plugin/templates.js +0 -25
  259. package/dist/plugin/types.cjs +0 -18
  260. package/dist/plugin/types.d.cts +0 -209
  261. package/dist/plugin/types.d.ts +0 -209
  262. package/dist/plugin/types.js +0 -0
  263. package/dist/plugin/vault.cjs +0 -927
  264. package/dist/plugin/vault.d.cts +0 -68
  265. package/dist/plugin/vault.d.ts +0 -68
  266. package/dist/plugin/vault.js +0 -22
  267. package/dist/types-Y2_Um2Ls.d.cts +0 -205
  268. package/templates/memory-event.md +0 -67
  269. package/templates/party.md +0 -63
  270. package/templates/primitive-registry.yaml +0 -551
  271. package/templates/run.md +0 -68
  272. package/templates/trigger.md +0 -68
  273. package/templates/workspace.md +0 -50
@@ -0,0 +1,254 @@
1
+ import * as fs from 'node:fs';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
4
+ import { describe, expect, it } from 'vitest';
5
+ import { buildVaultGraph } from './vault-parser.js';
6
+
7
+ function makeTempVault() {
8
+ return fs.mkdtempSync(path.join(os.tmpdir(), 'clawvault-dashboard-'));
9
+ }
10
+
11
+ function writeVaultFile(root, relativePath, content) {
12
+ const fullPath = path.join(root, relativePath);
13
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
14
+ fs.writeFileSync(fullPath, content, 'utf8');
15
+ }
16
+
17
+ describe('buildVaultGraph', () => {
18
+ it('builds nodes and edges from markdown wiki-links', async () => {
19
+ const vaultPath = makeTempVault();
20
+ try {
21
+ writeVaultFile(
22
+ vaultPath,
23
+ 'decisions/use-clawvault.md',
24
+ `---
25
+ title: Use ClawVault
26
+ tags: [architecture, memory]
27
+ ---
28
+ Linked to [[projects/clawvault|ClawVault Project]] and [[missing-note]].
29
+ `
30
+ );
31
+ writeVaultFile(vaultPath, 'projects/clawvault.md', '# ClawVault');
32
+
33
+ const graph = await buildVaultGraph(vaultPath);
34
+ const decisionNode = graph.nodes.find((node) => node.id === 'decisions/use-clawvault');
35
+ const unresolvedNode = graph.nodes.find((node) => node.id === 'missing-note');
36
+
37
+ expect(decisionNode).toMatchObject({
38
+ title: 'Use ClawVault',
39
+ category: 'decisions',
40
+ tags: ['architecture', 'memory'],
41
+ type: 'decision'
42
+ });
43
+ expect(graph.edges).toEqual(expect.arrayContaining([
44
+ expect.objectContaining({
45
+ source: 'decisions/use-clawvault',
46
+ target: 'projects/clawvault',
47
+ type: 'wiki_link'
48
+ }),
49
+ expect.objectContaining({
50
+ source: 'decisions/use-clawvault',
51
+ target: 'missing-note',
52
+ type: 'wiki_link'
53
+ }),
54
+ expect.objectContaining({
55
+ source: 'decisions/use-clawvault',
56
+ target: 'tag:architecture',
57
+ type: 'tag'
58
+ })
59
+ ]));
60
+ expect(unresolvedNode).toMatchObject({
61
+ missing: true,
62
+ category: 'unresolved'
63
+ });
64
+ expect(graph.stats.edgeTypeCounts.wiki_link).toBeGreaterThanOrEqual(2);
65
+ expect(graph.stats.edgeTypeCounts.tag).toBeGreaterThanOrEqual(1);
66
+ } finally {
67
+ fs.rmSync(vaultPath, { recursive: true, force: true });
68
+ }
69
+ });
70
+
71
+ it('resolves basename links when there is a unique match', async () => {
72
+ const vaultPath = makeTempVault();
73
+ try {
74
+ writeVaultFile(vaultPath, 'research/notes.md', 'See [[clawvault]].');
75
+ writeVaultFile(vaultPath, 'projects/clawvault.md', '# ClawVault');
76
+
77
+ const graph = await buildVaultGraph(vaultPath);
78
+
79
+ expect(graph.edges).toEqual(expect.arrayContaining([
80
+ expect.objectContaining({
81
+ source: 'research/notes',
82
+ target: 'projects/clawvault',
83
+ type: 'wiki_link'
84
+ })
85
+ ]));
86
+ } finally {
87
+ fs.rmSync(vaultPath, { recursive: true, force: true });
88
+ }
89
+ });
90
+
91
+ it('emits frontmatter relation edges with labels', async () => {
92
+ const vaultPath = makeTempVault();
93
+ try {
94
+ writeVaultFile(
95
+ vaultPath,
96
+ 'decisions/db.md',
97
+ `---
98
+ related:
99
+ - projects/clawvault
100
+ owner: people/alice
101
+ ---
102
+ Decision details`
103
+ );
104
+ writeVaultFile(vaultPath, 'projects/clawvault.md', '# ClawVault');
105
+ writeVaultFile(vaultPath, 'people/alice.md', '# Alice');
106
+
107
+ const graph = await buildVaultGraph(vaultPath);
108
+ expect(graph.edges).toEqual(expect.arrayContaining([
109
+ expect.objectContaining({
110
+ source: 'decisions/db',
111
+ target: 'projects/clawvault',
112
+ type: 'frontmatter_relation',
113
+ label: 'related'
114
+ }),
115
+ expect.objectContaining({
116
+ source: 'decisions/db',
117
+ target: 'people/alice',
118
+ type: 'frontmatter_relation',
119
+ label: 'owner'
120
+ })
121
+ ]));
122
+ } finally {
123
+ fs.rmSync(vaultPath, { recursive: true, force: true });
124
+ }
125
+ });
126
+
127
+ it('loads graph data from memory graph index when present', async () => {
128
+ const vaultPath = makeTempVault();
129
+ try {
130
+ writeVaultFile(vaultPath, 'decisions/use-clawvault.md', '# Placeholder');
131
+ writeVaultFile(vaultPath, 'projects/clawvault.md', '# Placeholder project');
132
+ const decisionMtime = fs.statSync(path.join(vaultPath, 'decisions/use-clawvault.md')).mtimeMs;
133
+ const projectMtime = fs.statSync(path.join(vaultPath, 'projects/clawvault.md')).mtimeMs;
134
+ const indexPath = path.join(vaultPath, '.clawvault', 'graph-index.json');
135
+ fs.mkdirSync(path.dirname(indexPath), { recursive: true });
136
+ fs.writeFileSync(
137
+ indexPath,
138
+ JSON.stringify({
139
+ schemaVersion: 1,
140
+ files: {
141
+ 'decisions/use-clawvault.md': {
142
+ relativePath: 'decisions/use-clawvault.md',
143
+ mtimeMs: decisionMtime
144
+ },
145
+ 'projects/clawvault.md': {
146
+ relativePath: 'projects/clawvault.md',
147
+ mtimeMs: projectMtime
148
+ }
149
+ },
150
+ graph: {
151
+ nodes: [
152
+ {
153
+ id: 'note:decisions/use-clawvault',
154
+ title: 'Use ClawVault',
155
+ type: 'decision',
156
+ category: 'decisions',
157
+ tags: ['architecture'],
158
+ path: 'decisions/use-clawvault.md',
159
+ missing: false,
160
+ degree: 1
161
+ },
162
+ {
163
+ id: 'note:projects/clawvault',
164
+ title: 'ClawVault Project',
165
+ type: 'project',
166
+ category: 'projects',
167
+ tags: [],
168
+ path: 'projects/clawvault.md',
169
+ missing: false,
170
+ degree: 1
171
+ }
172
+ ],
173
+ edges: [
174
+ {
175
+ source: 'note:decisions/use-clawvault',
176
+ target: 'note:projects/clawvault',
177
+ type: 'frontmatter_relation',
178
+ label: 'related'
179
+ }
180
+ ],
181
+ stats: { generatedAt: '2026-02-13T00:00:00.000Z' }
182
+ }
183
+ }),
184
+ 'utf8'
185
+ );
186
+
187
+ const graph = await buildVaultGraph(vaultPath);
188
+ expect(graph.nodes.find((node) => node.id === 'decisions/use-clawvault')).toBeTruthy();
189
+ expect(graph.edges).toEqual([
190
+ {
191
+ source: 'decisions/use-clawvault',
192
+ target: 'projects/clawvault',
193
+ type: 'frontmatter_relation',
194
+ label: 'related'
195
+ }
196
+ ]);
197
+ expect(graph.stats.fileCount).toBe(2);
198
+ } finally {
199
+ fs.rmSync(vaultPath, { recursive: true, force: true });
200
+ }
201
+ });
202
+
203
+ it('falls back to markdown parsing when memory graph index is stale', async () => {
204
+ const vaultPath = makeTempVault();
205
+ try {
206
+ writeVaultFile(vaultPath, 'projects/clawvault.md', '# ClawVault');
207
+ writeVaultFile(vaultPath, 'decisions/use-clawvault.md', 'See [[projects/clawvault]].');
208
+
209
+ const indexPath = path.join(vaultPath, '.clawvault', 'graph-index.json');
210
+ fs.mkdirSync(path.dirname(indexPath), { recursive: true });
211
+ fs.writeFileSync(
212
+ indexPath,
213
+ JSON.stringify({
214
+ schemaVersion: 1,
215
+ generatedAt: '2026-02-13T00:00:00.000Z',
216
+ files: {
217
+ 'decisions/use-clawvault.md': { relativePath: 'decisions/use-clawvault.md', mtimeMs: 1 },
218
+ 'projects/clawvault.md': { relativePath: 'projects/clawvault.md', mtimeMs: 1 }
219
+ },
220
+ graph: {
221
+ nodes: [
222
+ {
223
+ id: 'note:decisions/use-clawvault',
224
+ title: 'Old node',
225
+ type: 'decision',
226
+ category: 'decisions',
227
+ tags: [],
228
+ path: 'decisions/use-clawvault.md',
229
+ missing: false,
230
+ degree: 0
231
+ }
232
+ ],
233
+ edges: [],
234
+ stats: { generatedAt: '2026-02-13T00:00:00.000Z' }
235
+ }
236
+ }),
237
+ 'utf8'
238
+ );
239
+
240
+ const graph = await buildVaultGraph(vaultPath);
241
+ const node = graph.nodes.find((candidate) => candidate.id === 'decisions/use-clawvault');
242
+ expect(node?.title).not.toBe('Old node');
243
+ expect(graph.edges).toEqual(expect.arrayContaining([
244
+ expect.objectContaining({
245
+ source: 'decisions/use-clawvault',
246
+ target: 'projects/clawvault',
247
+ type: 'wiki_link'
248
+ })
249
+ ]));
250
+ } finally {
251
+ fs.rmSync(vaultPath, { recursive: true, force: true });
252
+ }
253
+ });
254
+ });