clawvault 2.5.2 → 2.5.4

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 (71) hide show
  1. package/README.md +159 -200
  2. package/bin/clawvault.js +111 -111
  3. package/bin/command-registration.test.js +166 -166
  4. package/bin/command-runtime.js +93 -93
  5. package/bin/command-runtime.test.js +154 -154
  6. package/bin/help-contract.test.js +39 -39
  7. package/bin/register-config-commands.js +153 -153
  8. package/bin/register-config-route-commands.test.js +121 -121
  9. package/bin/register-core-commands.js +237 -237
  10. package/bin/register-kanban-commands.js +56 -56
  11. package/bin/register-kanban-commands.test.js +83 -83
  12. package/bin/register-maintenance-commands.js +282 -282
  13. package/bin/register-project-commands.js +209 -209
  14. package/bin/register-project-commands.test.js +206 -206
  15. package/bin/register-query-commands.js +317 -317
  16. package/bin/register-query-commands.test.js +65 -65
  17. package/bin/register-resilience-commands.js +182 -182
  18. package/bin/register-resilience-commands.test.js +81 -81
  19. package/bin/register-route-commands.js +114 -114
  20. package/bin/register-session-lifecycle-commands.js +206 -206
  21. package/bin/register-tailscale-commands.js +106 -106
  22. package/bin/register-task-commands.js +348 -348
  23. package/bin/register-task-commands.test.js +69 -69
  24. package/bin/register-template-commands.js +72 -72
  25. package/bin/register-vault-operations-commands.js +300 -300
  26. package/bin/test-helpers/cli-command-fixtures.js +119 -119
  27. package/dashboard/lib/graph-diff.js +104 -104
  28. package/dashboard/lib/graph-diff.test.js +75 -75
  29. package/dashboard/lib/vault-parser.js +556 -556
  30. package/dashboard/lib/vault-parser.test.js +254 -254
  31. package/dashboard/public/app.js +796 -796
  32. package/dashboard/public/index.html +52 -52
  33. package/dashboard/public/styles.css +221 -221
  34. package/dashboard/server.js +374 -374
  35. package/dist/{chunk-3FP5BJ42.js → chunk-4QYGFWRM.js} +1 -1
  36. package/dist/{chunk-M25QVSJM.js → chunk-AXKYDCNN.js} +1 -1
  37. package/dist/{chunk-CLE2HHNT.js → chunk-IVRIKYFE.js} +18 -11
  38. package/dist/{chunk-HRTPQQF2.js → chunk-IZEY5S74.js} +1 -1
  39. package/dist/{chunk-HWUNREDJ.js → chunk-JDLOL2PL.js} +4 -4
  40. package/dist/{chunk-AY4PGUVL.js → chunk-KL4NAOMO.js} +1 -1
  41. package/dist/{chunk-O7XHXF7F.js → chunk-MAKNAHAW.js} +4 -4
  42. package/dist/{chunk-PLZKZW4I.js → chunk-OSMS7QIG.js} +1 -1
  43. package/dist/{chunk-NZ4ZZNSR.js → chunk-THRJVD4L.js} +1 -1
  44. package/dist/{chunk-4GBPTBFJ.js → chunk-TIGW564L.js} +1 -1
  45. package/dist/{chunk-BHO7WSAY.js → chunk-W2HNZC22.js} +3 -3
  46. package/dist/{chunk-GFJ3LIIB.js → chunk-XAVB4GB4.js} +1 -1
  47. package/dist/cli/index.js +10 -10
  48. package/dist/commands/context.js +3 -3
  49. package/dist/commands/doctor.js +4 -4
  50. package/dist/commands/embed.js +2 -2
  51. package/dist/commands/observe.js +2 -2
  52. package/dist/commands/setup.js +2 -2
  53. package/dist/commands/sleep.js +2 -2
  54. package/dist/commands/status.js +3 -3
  55. package/dist/commands/tailscale.js +3 -3
  56. package/dist/commands/wake.js +2 -2
  57. package/dist/index.js +12 -12
  58. package/dist/lib/tailscale.js +2 -2
  59. package/dist/lib/webdav.js +1 -1
  60. package/hooks/clawvault/HOOK.md +83 -74
  61. package/hooks/clawvault/handler.js +816 -816
  62. package/hooks/clawvault/handler.test.js +263 -263
  63. package/package.json +94 -125
  64. package/templates/checkpoint.md +19 -19
  65. package/templates/daily-note.md +19 -19
  66. package/templates/daily.md +19 -19
  67. package/templates/decision.md +17 -17
  68. package/templates/handoff.md +19 -19
  69. package/templates/lesson.md +16 -16
  70. package/templates/person.md +19 -19
  71. package/templates/project.md +23 -23
@@ -1,254 +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
- });
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
+ });