@gopherine/obsidian-mcp 0.1.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 (223) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/LICENSE +661 -0
  3. package/README.md +235 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +805 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/commands/brainstorm.d.ts +9 -0
  8. package/dist/commands/brainstorm.js +37 -0
  9. package/dist/commands/brainstorm.js.map +1 -0
  10. package/dist/commands/brainstorm.test.d.ts +1 -0
  11. package/dist/commands/brainstorm.test.js +110 -0
  12. package/dist/commands/brainstorm.test.js.map +1 -0
  13. package/dist/commands/context.d.ts +18 -0
  14. package/dist/commands/context.js +88 -0
  15. package/dist/commands/context.js.map +1 -0
  16. package/dist/commands/context.test.d.ts +1 -0
  17. package/dist/commands/context.test.js +230 -0
  18. package/dist/commands/context.test.js.map +1 -0
  19. package/dist/commands/decide.d.ts +12 -0
  20. package/dist/commands/decide.js +40 -0
  21. package/dist/commands/decide.js.map +1 -0
  22. package/dist/commands/decide.test.d.ts +1 -0
  23. package/dist/commands/decide.test.js +109 -0
  24. package/dist/commands/decide.test.js.map +1 -0
  25. package/dist/commands/graph.d.ts +15 -0
  26. package/dist/commands/graph.js +89 -0
  27. package/dist/commands/graph.js.map +1 -0
  28. package/dist/commands/graph.test.d.ts +1 -0
  29. package/dist/commands/graph.test.js +215 -0
  30. package/dist/commands/graph.test.js.map +1 -0
  31. package/dist/commands/init.d.ts +11 -0
  32. package/dist/commands/init.js +256 -0
  33. package/dist/commands/init.js.map +1 -0
  34. package/dist/commands/learn.d.ts +25 -0
  35. package/dist/commands/learn.js +92 -0
  36. package/dist/commands/learn.js.map +1 -0
  37. package/dist/commands/learn.test.d.ts +1 -0
  38. package/dist/commands/learn.test.js +241 -0
  39. package/dist/commands/learn.test.js.map +1 -0
  40. package/dist/commands/prune.d.ts +58 -0
  41. package/dist/commands/prune.js +246 -0
  42. package/dist/commands/prune.js.map +1 -0
  43. package/dist/commands/read.d.ts +8 -0
  44. package/dist/commands/read.js +7 -0
  45. package/dist/commands/read.js.map +1 -0
  46. package/dist/commands/read.test.d.ts +1 -0
  47. package/dist/commands/read.test.js +48 -0
  48. package/dist/commands/read.test.js.map +1 -0
  49. package/dist/commands/resume.d.ts +20 -0
  50. package/dist/commands/resume.js +138 -0
  51. package/dist/commands/resume.js.map +1 -0
  52. package/dist/commands/resume.test.d.ts +1 -0
  53. package/dist/commands/resume.test.js +243 -0
  54. package/dist/commands/resume.test.js.map +1 -0
  55. package/dist/commands/search.d.ts +8 -0
  56. package/dist/commands/search.js +25 -0
  57. package/dist/commands/search.js.map +1 -0
  58. package/dist/commands/search.test.d.ts +1 -0
  59. package/dist/commands/search.test.js +61 -0
  60. package/dist/commands/search.test.js.map +1 -0
  61. package/dist/commands/session.d.ts +22 -0
  62. package/dist/commands/session.js +79 -0
  63. package/dist/commands/session.js.map +1 -0
  64. package/dist/commands/session.test.d.ts +1 -0
  65. package/dist/commands/session.test.js +185 -0
  66. package/dist/commands/session.test.js.map +1 -0
  67. package/dist/commands/skill/index.d.ts +30 -0
  68. package/dist/commands/skill/index.js +61 -0
  69. package/dist/commands/skill/index.js.map +1 -0
  70. package/dist/commands/skill/index.test.d.ts +1 -0
  71. package/dist/commands/skill/index.test.js +137 -0
  72. package/dist/commands/skill/index.test.js.map +1 -0
  73. package/dist/commands/skill/install.d.ts +14 -0
  74. package/dist/commands/skill/install.js +107 -0
  75. package/dist/commands/skill/install.js.map +1 -0
  76. package/dist/commands/skill/install.test.d.ts +1 -0
  77. package/dist/commands/skill/install.test.js +225 -0
  78. package/dist/commands/skill/install.test.js.map +1 -0
  79. package/dist/commands/skill/list.d.ts +6 -0
  80. package/dist/commands/skill/list.js +5 -0
  81. package/dist/commands/skill/list.js.map +1 -0
  82. package/dist/commands/skill/list.test.d.ts +1 -0
  83. package/dist/commands/skill/list.test.js +101 -0
  84. package/dist/commands/skill/list.test.js.map +1 -0
  85. package/dist/commands/skill/schema.d.ts +27 -0
  86. package/dist/commands/skill/schema.js +55 -0
  87. package/dist/commands/skill/schema.js.map +1 -0
  88. package/dist/commands/skill/schema.test.d.ts +1 -0
  89. package/dist/commands/skill/schema.test.js +142 -0
  90. package/dist/commands/skill/schema.test.js.map +1 -0
  91. package/dist/commands/skill/validate.d.ts +10 -0
  92. package/dist/commands/skill/validate.js +39 -0
  93. package/dist/commands/skill/validate.js.map +1 -0
  94. package/dist/commands/skill/validate.test.d.ts +1 -0
  95. package/dist/commands/skill/validate.test.js +171 -0
  96. package/dist/commands/skill/validate.test.js.map +1 -0
  97. package/dist/commands/task.d.ts +34 -0
  98. package/dist/commands/task.js +160 -0
  99. package/dist/commands/task.js.map +1 -0
  100. package/dist/commands/task.test.d.ts +1 -0
  101. package/dist/commands/task.test.js +395 -0
  102. package/dist/commands/task.test.js.map +1 -0
  103. package/dist/commands/todo.d.ts +15 -0
  104. package/dist/commands/todo.js +99 -0
  105. package/dist/commands/todo.js.map +1 -0
  106. package/dist/commands/todo.test.d.ts +1 -0
  107. package/dist/commands/todo.test.js +324 -0
  108. package/dist/commands/todo.test.js.map +1 -0
  109. package/dist/commands/write.d.ts +12 -0
  110. package/dist/commands/write.js +40 -0
  111. package/dist/commands/write.js.map +1 -0
  112. package/dist/commands/write.test.d.ts +1 -0
  113. package/dist/commands/write.test.js +79 -0
  114. package/dist/commands/write.test.js.map +1 -0
  115. package/dist/config.d.ts +15 -0
  116. package/dist/config.js +48 -0
  117. package/dist/config.js.map +1 -0
  118. package/dist/config.test.d.ts +1 -0
  119. package/dist/config.test.js +147 -0
  120. package/dist/config.test.js.map +1 -0
  121. package/dist/core/executor.d.ts +11 -0
  122. package/dist/core/executor.js +42 -0
  123. package/dist/core/executor.js.map +1 -0
  124. package/dist/core/executor.test.d.ts +1 -0
  125. package/dist/core/executor.test.js +206 -0
  126. package/dist/core/executor.test.js.map +1 -0
  127. package/dist/core/middleware/error-handler.d.ts +2 -0
  128. package/dist/core/middleware/error-handler.js +29 -0
  129. package/dist/core/middleware/error-handler.js.map +1 -0
  130. package/dist/core/middleware/index.d.ts +2 -0
  131. package/dist/core/middleware/index.js +3 -0
  132. package/dist/core/middleware/index.js.map +1 -0
  133. package/dist/core/middleware/logging.d.ts +2 -0
  134. package/dist/core/middleware/logging.js +18 -0
  135. package/dist/core/middleware/logging.js.map +1 -0
  136. package/dist/core/registry.d.ts +12 -0
  137. package/dist/core/registry.js +407 -0
  138. package/dist/core/registry.js.map +1 -0
  139. package/dist/core/registry.test.d.ts +1 -0
  140. package/dist/core/registry.test.js +162 -0
  141. package/dist/core/registry.test.js.map +1 -0
  142. package/dist/core/types.d.ts +38 -0
  143. package/dist/core/types.js +2 -0
  144. package/dist/core/types.js.map +1 -0
  145. package/dist/integration/coordination.test.d.ts +1 -0
  146. package/dist/integration/coordination.test.js +286 -0
  147. package/dist/integration/coordination.test.js.map +1 -0
  148. package/dist/integration/project-artifacts.test.d.ts +1 -0
  149. package/dist/integration/project-artifacts.test.js +275 -0
  150. package/dist/integration/project-artifacts.test.js.map +1 -0
  151. package/dist/integration/registry-graph.test.d.ts +1 -0
  152. package/dist/integration/registry-graph.test.js +157 -0
  153. package/dist/integration/registry-graph.test.js.map +1 -0
  154. package/dist/integration/vault-lifecycle.test.d.ts +1 -0
  155. package/dist/integration/vault-lifecycle.test.js +183 -0
  156. package/dist/integration/vault-lifecycle.test.js.map +1 -0
  157. package/dist/lib/auto-number.d.ts +10 -0
  158. package/dist/lib/auto-number.js +33 -0
  159. package/dist/lib/auto-number.js.map +1 -0
  160. package/dist/lib/auto-number.test.d.ts +1 -0
  161. package/dist/lib/auto-number.test.js +88 -0
  162. package/dist/lib/auto-number.test.js.map +1 -0
  163. package/dist/lib/escape-regex.d.ts +4 -0
  164. package/dist/lib/escape-regex.js +7 -0
  165. package/dist/lib/escape-regex.js.map +1 -0
  166. package/dist/lib/escape-regex.test.d.ts +1 -0
  167. package/dist/lib/escape-regex.test.js +27 -0
  168. package/dist/lib/escape-regex.test.js.map +1 -0
  169. package/dist/lib/frontmatter.d.ts +34 -0
  170. package/dist/lib/frontmatter.js +74 -0
  171. package/dist/lib/frontmatter.js.map +1 -0
  172. package/dist/lib/frontmatter.test.d.ts +1 -0
  173. package/dist/lib/frontmatter.test.js +192 -0
  174. package/dist/lib/frontmatter.test.js.map +1 -0
  175. package/dist/lib/project-detector.d.ts +12 -0
  176. package/dist/lib/project-detector.js +123 -0
  177. package/dist/lib/project-detector.js.map +1 -0
  178. package/dist/lib/project-detector.test.d.ts +1 -0
  179. package/dist/lib/project-detector.test.js +117 -0
  180. package/dist/lib/project-detector.test.js.map +1 -0
  181. package/dist/lib/safe-external-path.d.ts +10 -0
  182. package/dist/lib/safe-external-path.js +46 -0
  183. package/dist/lib/safe-external-path.js.map +1 -0
  184. package/dist/lib/safe-external-path.test.d.ts +1 -0
  185. package/dist/lib/safe-external-path.test.js +99 -0
  186. package/dist/lib/safe-external-path.test.js.map +1 -0
  187. package/dist/lib/search-engine.d.ts +19 -0
  188. package/dist/lib/search-engine.js +157 -0
  189. package/dist/lib/search-engine.js.map +1 -0
  190. package/dist/lib/search-engine.test.d.ts +1 -0
  191. package/dist/lib/search-engine.test.js +120 -0
  192. package/dist/lib/search-engine.test.js.map +1 -0
  193. package/dist/lib/session-registry.d.ts +59 -0
  194. package/dist/lib/session-registry.js +230 -0
  195. package/dist/lib/session-registry.js.map +1 -0
  196. package/dist/lib/session-registry.test.d.ts +1 -0
  197. package/dist/lib/session-registry.test.js +199 -0
  198. package/dist/lib/session-registry.test.js.map +1 -0
  199. package/dist/lib/skill-registry.d.ts +13 -0
  200. package/dist/lib/skill-registry.js +76 -0
  201. package/dist/lib/skill-registry.js.map +1 -0
  202. package/dist/lib/token-estimator.d.ts +19 -0
  203. package/dist/lib/token-estimator.js +58 -0
  204. package/dist/lib/token-estimator.js.map +1 -0
  205. package/dist/lib/token-estimator.test.d.ts +1 -0
  206. package/dist/lib/token-estimator.test.js +65 -0
  207. package/dist/lib/token-estimator.test.js.map +1 -0
  208. package/dist/lib/vault-fs.d.ts +39 -0
  209. package/dist/lib/vault-fs.js +183 -0
  210. package/dist/lib/vault-fs.js.map +1 -0
  211. package/dist/lib/vault-fs.test.d.ts +1 -0
  212. package/dist/lib/vault-fs.test.js +210 -0
  213. package/dist/lib/vault-fs.test.js.map +1 -0
  214. package/dist/mcp-server.d.ts +2 -0
  215. package/dist/mcp-server.js +385 -0
  216. package/dist/mcp-server.js.map +1 -0
  217. package/dist/test-helpers.d.ts +20 -0
  218. package/dist/test-helpers.js +46 -0
  219. package/dist/test-helpers.js.map +1 -0
  220. package/dist/test-helpers.test.d.ts +1 -0
  221. package/dist/test-helpers.test.js +90 -0
  222. package/dist/test-helpers.test.js.map +1 -0
  223. package/package.json +72 -0
@@ -0,0 +1,324 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
+ import { mkdir, rm } from "fs/promises";
3
+ import { homedir } from "os";
4
+ import { join } from "path";
5
+ import { todoCommand } from "./todo.js";
6
+ import { VaultFS } from "../lib/vault-fs.js";
7
+ function createCommandContext(vaultFs, overrides) {
8
+ return {
9
+ vaultFs,
10
+ vaultPath: vaultFs.root,
11
+ sessionRegistry: {},
12
+ config: {},
13
+ log: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },
14
+ ...overrides,
15
+ };
16
+ }
17
+ describe("todoCommand", () => {
18
+ let vaultRoot;
19
+ let vaultFs;
20
+ let ctx;
21
+ beforeEach(async () => {
22
+ vaultRoot = join(homedir(), `.vault-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
23
+ await mkdir(vaultRoot, { recursive: true });
24
+ vaultFs = new VaultFS(vaultRoot);
25
+ ctx = createCommandContext(vaultFs);
26
+ });
27
+ afterEach(async () => {
28
+ await rm(vaultRoot, { recursive: true, force: true });
29
+ });
30
+ describe("list todos", () => {
31
+ it("returns empty array when no todos file", async () => {
32
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
33
+ const result = await todoCommand({
34
+ action: "list",
35
+ project: "test-project",
36
+ }, ctx);
37
+ expect(result.todos).toEqual([]);
38
+ });
39
+ it("lists pending todos", async () => {
40
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
41
+ await mkdir(join(vaultRoot, "projects/test-project"), { recursive: true });
42
+ await vaultFs.write("projects/test-project/todos.md", `---
43
+ type: todo
44
+ project: test-project
45
+ ---
46
+
47
+ # Todos
48
+
49
+ - [ ] First task
50
+ - [x] Completed task
51
+ - [ ] 🔴 High priority task
52
+ `);
53
+ const result = await todoCommand({
54
+ action: "list",
55
+ project: "test-project",
56
+ }, ctx);
57
+ expect(result.todos).toHaveLength(2);
58
+ expect(result.todos[0].text).toBe("First task");
59
+ expect(result.todos[1].text).toBe("High priority task");
60
+ });
61
+ it("filters to blockers only when requested", async () => {
62
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
63
+ await mkdir(join(vaultRoot, "projects/test-project"), { recursive: true });
64
+ await vaultFs.write("projects/test-project/todos.md", `---
65
+ type: todo
66
+ project: test-project
67
+ ---
68
+
69
+ # Todos
70
+
71
+ - [ ] 🟡 Medium priority
72
+ - [ ] 🔴 High priority blocker
73
+ - [ ] 🟢 Low priority
74
+ `);
75
+ const result = await todoCommand({
76
+ action: "list",
77
+ blockersOnly: true,
78
+ project: "test-project",
79
+ }, ctx);
80
+ expect(result.todos).toHaveLength(1);
81
+ expect(result.todos[0].text).toBe("High priority blocker");
82
+ expect(result.todos[0].priority).toBe("high");
83
+ });
84
+ it("parses priority from emojis", async () => {
85
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
86
+ await mkdir(join(vaultRoot, "projects/test-project"), { recursive: true });
87
+ await vaultFs.write("projects/test-project/todos.md", `---
88
+ type: todo
89
+ ---
90
+
91
+ # Todos
92
+
93
+ - [ ] 🔴 High
94
+ - [ ] 🟡 Medium
95
+ - [ ] 🟢 Low
96
+ - [ ] No emoji (medium default)
97
+ `);
98
+ const result = await todoCommand({
99
+ action: "list",
100
+ project: "test-project",
101
+ }, ctx);
102
+ expect(result.todos[0].priority).toBe("high");
103
+ expect(result.todos[1].priority).toBe("medium");
104
+ expect(result.todos[2].priority).toBe("low");
105
+ expect(result.todos[3].priority).toBe("medium");
106
+ });
107
+ });
108
+ describe("add todo", () => {
109
+ it("creates todos file if it does not exist", async () => {
110
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
111
+ const result = await todoCommand({
112
+ action: "add",
113
+ item: "New task",
114
+ project: "test-project",
115
+ }, ctx);
116
+ expect(result.todos).toHaveLength(1);
117
+ expect(result.todos[0].text).toBe("New task");
118
+ const content = await vaultFs.read("projects/test-project/todos.md");
119
+ expect(content).toContain("type: todo");
120
+ expect(content).toContain("# Todos");
121
+ });
122
+ it("appends to existing todos file", async () => {
123
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
124
+ await mkdir(join(vaultRoot, "projects/test-project"), { recursive: true });
125
+ await vaultFs.write("projects/test-project/todos.md", `---
126
+ type: todo
127
+ ---
128
+
129
+ # Todos
130
+
131
+ - [ ] Existing task
132
+ `);
133
+ const result = await todoCommand({
134
+ action: "add",
135
+ item: "New task",
136
+ project: "test-project",
137
+ }, ctx);
138
+ expect(result.todos).toHaveLength(2);
139
+ const content = await vaultFs.read("projects/test-project/todos.md");
140
+ expect(content).toContain("Existing task");
141
+ expect(content).toContain("New task");
142
+ });
143
+ it("adds with high priority emoji", async () => {
144
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
145
+ await todoCommand({
146
+ action: "add",
147
+ item: "Urgent task",
148
+ priority: "high",
149
+ project: "test-project",
150
+ }, ctx);
151
+ const content = await vaultFs.read("projects/test-project/todos.md");
152
+ expect(content).toContain("🔴 Urgent task");
153
+ });
154
+ it("adds with low priority emoji", async () => {
155
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
156
+ await todoCommand({
157
+ action: "add",
158
+ item: "Low priority task",
159
+ priority: "low",
160
+ project: "test-project",
161
+ }, ctx);
162
+ const content = await vaultFs.read("projects/test-project/todos.md");
163
+ expect(content).toContain("🟢 Low priority task");
164
+ });
165
+ it("adds with medium priority emoji by default", async () => {
166
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
167
+ await todoCommand({
168
+ action: "add",
169
+ item: "Normal task",
170
+ project: "test-project",
171
+ }, ctx);
172
+ const content = await vaultFs.read("projects/test-project/todos.md");
173
+ expect(content).toContain("🟡 Normal task");
174
+ });
175
+ it("requires item text for add", async () => {
176
+ await expect(todoCommand({
177
+ action: "add",
178
+ project: "test-project",
179
+ }, ctx)).rejects.toThrow("Item text required for add");
180
+ });
181
+ });
182
+ describe("complete todo", () => {
183
+ it("marks todo as completed", async () => {
184
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
185
+ await mkdir(join(vaultRoot, "projects/test-project"), { recursive: true });
186
+ await vaultFs.write("projects/test-project/todos.md", `---
187
+ type: todo
188
+ ---
189
+
190
+ # Todos
191
+
192
+ - [ ] Task to complete
193
+ - [ ] Another task
194
+ `);
195
+ const result = await todoCommand({
196
+ action: "complete",
197
+ item: "Task to complete",
198
+ project: "test-project",
199
+ }, ctx);
200
+ const content = await vaultFs.read("projects/test-project/todos.md");
201
+ expect(content).toContain("- [x] Task to complete");
202
+ expect(content).toContain("- [ ] Another task");
203
+ });
204
+ it("works with priority emoji", async () => {
205
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
206
+ await mkdir(join(vaultRoot, "projects/test-project"), { recursive: true });
207
+ await vaultFs.write("projects/test-project/todos.md", `---
208
+ type: todo
209
+ ---
210
+
211
+ # Todos
212
+
213
+ - [ ] 🔴 Priority task
214
+ `);
215
+ await todoCommand({
216
+ action: "complete",
217
+ item: "Priority task",
218
+ project: "test-project",
219
+ }, ctx);
220
+ const content = await vaultFs.read("projects/test-project/todos.md");
221
+ expect(content).toContain("- [x] 🔴 Priority task");
222
+ });
223
+ it("requires item text for complete", async () => {
224
+ await expect(todoCommand({
225
+ action: "complete",
226
+ project: "test-project",
227
+ }, ctx)).rejects.toThrow("Item text required for complete");
228
+ });
229
+ it("throws if todos file does not exist", async () => {
230
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
231
+ await expect(todoCommand({
232
+ action: "complete",
233
+ item: "Task",
234
+ project: "test-project",
235
+ }, ctx)).rejects.toThrow("No todos file found");
236
+ });
237
+ });
238
+ describe("remove todo", () => {
239
+ it("removes a pending todo", async () => {
240
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
241
+ await mkdir(join(vaultRoot, "projects/test-project"), { recursive: true });
242
+ await vaultFs.write("projects/test-project/todos.md", `---
243
+ type: todo
244
+ ---
245
+
246
+ # Todos
247
+
248
+ - [ ] Task to remove
249
+ - [ ] Task to keep
250
+ `);
251
+ const result = await todoCommand({
252
+ action: "remove",
253
+ item: "Task to remove",
254
+ project: "test-project",
255
+ }, ctx);
256
+ const content = await vaultFs.read("projects/test-project/todos.md");
257
+ expect(content).not.toContain("Task to remove");
258
+ expect(content).toContain("Task to keep");
259
+ });
260
+ it("removes a completed todo", async () => {
261
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
262
+ await mkdir(join(vaultRoot, "projects/test-project"), { recursive: true });
263
+ await vaultFs.write("projects/test-project/todos.md", `---
264
+ type: todo
265
+ ---
266
+
267
+ # Todos
268
+
269
+ - [x] Completed task to remove
270
+ - [ ] Pending task
271
+ `);
272
+ await todoCommand({
273
+ action: "remove",
274
+ item: "Completed task to remove",
275
+ project: "test-project",
276
+ }, ctx);
277
+ const content = await vaultFs.read("projects/test-project/todos.md");
278
+ expect(content).not.toContain("Completed task to remove");
279
+ expect(content).toContain("Pending task");
280
+ });
281
+ it("removes todo with priority emoji", async () => {
282
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
283
+ await mkdir(join(vaultRoot, "projects/test-project"), { recursive: true });
284
+ await vaultFs.write("projects/test-project/todos.md", `---
285
+ type: todo
286
+ ---
287
+
288
+ # Todos
289
+
290
+ - [ ] 🔴 Priority task to remove
291
+ `);
292
+ await todoCommand({
293
+ action: "remove",
294
+ item: "Priority task to remove",
295
+ project: "test-project",
296
+ }, ctx);
297
+ const content = await vaultFs.read("projects/test-project/todos.md");
298
+ expect(content).not.toContain("Priority task to remove");
299
+ });
300
+ it("requires item text for remove", async () => {
301
+ await expect(todoCommand({
302
+ action: "remove",
303
+ project: "test-project",
304
+ }, ctx)).rejects.toThrow("Item text required for remove");
305
+ });
306
+ it("throws if todos file does not exist for remove", async () => {
307
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
308
+ await expect(todoCommand({
309
+ action: "remove",
310
+ item: "Task",
311
+ project: "test-project",
312
+ }, ctx)).rejects.toThrow("No todos file found");
313
+ });
314
+ });
315
+ describe("unknown action", () => {
316
+ it("throws error for unknown action", async () => {
317
+ await expect(todoCommand({
318
+ action: "unknown",
319
+ project: "test-project",
320
+ }, ctx)).rejects.toThrow("Unknown action: unknown");
321
+ });
322
+ });
323
+ });
324
+ //# sourceMappingURL=todo.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"todo.test.js","sourceRoot":"","sources":["../../src/commands/todo.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { CommandContext } from "../core/types.js";
2
+ import { type Frontmatter } from "../lib/frontmatter.js";
3
+ export declare function writeCommand(args: {
4
+ path: string;
5
+ content: string;
6
+ mode?: "overwrite" | "append" | "prepend";
7
+ frontmatter?: Partial<Frontmatter>;
8
+ }, ctx: CommandContext): Promise<{
9
+ written: boolean;
10
+ path: string;
11
+ bytes: number;
12
+ }>;
@@ -0,0 +1,40 @@
1
+ import { parseFrontmatter, serializeFrontmatter, createFrontmatter, mergeFrontmatter, validateFrontmatter, } from "../lib/frontmatter.js";
2
+ export async function writeCommand(args, ctx) {
3
+ const { path, content, mode = "append", frontmatter: fmOverrides } = args;
4
+ const vaultFs = ctx.vaultFs;
5
+ await vaultFs.verifyNoSymlinkEscape(path);
6
+ if (mode === "append" || mode === "prepend") {
7
+ const fileExists = await vaultFs.exists(path);
8
+ if (!fileExists) {
9
+ return createNewFile(vaultFs, path, content, fmOverrides);
10
+ }
11
+ const existing = await vaultFs.read(path);
12
+ const { data, content: body } = parseFrontmatter(existing);
13
+ const updatedFm = mergeFrontmatter(data, fmOverrides ?? {});
14
+ const newBody = mode === "append"
15
+ ? body.trimEnd() + "\n" + content
16
+ : content + "\n" + body;
17
+ const result = await vaultFs.write(path, serializeFrontmatter(updatedFm, newBody));
18
+ return { written: true, ...result };
19
+ }
20
+ const fm = fmOverrides
21
+ ? createFrontmatter(fmOverrides)
22
+ : createFrontmatter({});
23
+ const errors = validateFrontmatter(fm);
24
+ if (errors.length > 0) {
25
+ throw new Error(`Invalid frontmatter: ${errors.join("; ")}`);
26
+ }
27
+ const fullContent = serializeFrontmatter(fm, content);
28
+ const result = await vaultFs.write(path, fullContent);
29
+ return { written: true, ...result };
30
+ }
31
+ async function createNewFile(vaultFs, path, content, fmOverrides) {
32
+ const fm = fmOverrides ? createFrontmatter(fmOverrides) : createFrontmatter({});
33
+ const errors = validateFrontmatter(fm);
34
+ if (errors.length > 0) {
35
+ throw new Error(`Invalid frontmatter: ${errors.join("; ")}`);
36
+ }
37
+ const result = await vaultFs.write(path, serializeFrontmatter(fm, content));
38
+ return { written: true, ...result };
39
+ }
40
+ //# sourceMappingURL=write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.js","sourceRoot":"","sources":["../../src/commands/write.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,79 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
+ import { mkdir, rm } from "fs/promises";
3
+ import { homedir } from "os";
4
+ import { join } from "path";
5
+ import { writeCommand } from "./write.js";
6
+ import { VaultFS } from "../lib/vault-fs.js";
7
+ function createCommandContext(vaultFs, overrides) {
8
+ return {
9
+ vaultFs,
10
+ vaultPath: vaultFs.root,
11
+ sessionRegistry: {},
12
+ config: {},
13
+ log: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },
14
+ ...overrides,
15
+ };
16
+ }
17
+ describe("writeCommand", () => {
18
+ let vaultRoot;
19
+ let vaultFs;
20
+ let ctx;
21
+ beforeEach(async () => {
22
+ vaultRoot = join(homedir(), `.vault-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
23
+ await mkdir(vaultRoot, { recursive: true });
24
+ vaultFs = new VaultFS(vaultRoot);
25
+ ctx = createCommandContext(vaultFs);
26
+ });
27
+ afterEach(async () => {
28
+ await rm(vaultRoot, { recursive: true, force: true });
29
+ });
30
+ describe("append mode (default)", () => {
31
+ it("creates new file with frontmatter", async () => {
32
+ const result = await writeCommand({ path: "new.md", content: "content", mode: "append" }, ctx);
33
+ expect(result.written).toBe(true);
34
+ expect(result.path).toBe("new.md");
35
+ const content = await vaultFs.read("new.md");
36
+ expect(content).toContain("content");
37
+ });
38
+ it("appends to existing file", async () => {
39
+ await vaultFs.write("existing.md", "---\ncreated: 2024-01-01\n---\n\nInitial content");
40
+ const result = await writeCommand({ path: "existing.md", content: "appended", mode: "append" }, ctx);
41
+ expect(result.written).toBe(true);
42
+ const content = await vaultFs.read("existing.md");
43
+ expect(content).toContain("Initial content");
44
+ expect(content).toContain("appended");
45
+ });
46
+ it("merges frontmatter on append", async () => {
47
+ await vaultFs.write("fm.md", "---\ntags: [a]\n---\n\nBody");
48
+ const result = await writeCommand({ path: "fm.md", content: "new content", mode: "append", frontmatter: { status: "new-status" } }, ctx);
49
+ const content = await vaultFs.read("fm.md");
50
+ expect(content).toContain("status: new-status");
51
+ });
52
+ });
53
+ describe("prepend mode", () => {
54
+ it("prepends content to existing file", async () => {
55
+ await vaultFs.write("prepend.md", "---\ntitle: Test\n---\n\nOriginal body");
56
+ const result = await writeCommand({ path: "prepend.md", content: "New top content", mode: "prepend" }, ctx);
57
+ expect(result.written).toBe(true);
58
+ const content = await vaultFs.read("prepend.md");
59
+ expect(content).toContain("New top content");
60
+ expect(content).toContain("Original body");
61
+ });
62
+ });
63
+ describe("overwrite mode", () => {
64
+ it("creates file with frontmatter", async () => {
65
+ const result = await writeCommand({ path: "overwrite.md", content: "content", mode: "overwrite" }, ctx);
66
+ expect(result.written).toBe(true);
67
+ const content = await vaultFs.read("overwrite.md");
68
+ expect(content).toContain("content");
69
+ });
70
+ it("creates file with custom frontmatter", async () => {
71
+ const result = await writeCommand({ path: "custom.md", content: "content", mode: "overwrite", frontmatter: { priority: "high", tags: ["test"] } }, ctx);
72
+ expect(result.written).toBe(true);
73
+ const content = await vaultFs.read("custom.md");
74
+ expect(content).toContain("priority: high");
75
+ expect(content).toContain("tags:");
76
+ });
77
+ });
78
+ });
79
+ //# sourceMappingURL=write.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.test.js","sourceRoot":"","sources":["../../src/commands/write.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ export interface Config {
2
+ vaultPath: string;
3
+ maxInjectTokens: number;
4
+ sessionTtlHours: number;
5
+ }
6
+ /**
7
+ * Validate that a project slug contains only safe characters.
8
+ * Prevents path manipulation via project names.
9
+ */
10
+ export declare function validateProjectSlug(slug: string): string;
11
+ /**
12
+ * Resolve a project slug: use explicit value, auto-detect from CWD, or throw.
13
+ */
14
+ export declare function resolveProject(vaultPath: string, explicitSlug?: string | null): Promise<string>;
15
+ export declare function loadConfig(): Config;
package/dist/config.js ADDED
@@ -0,0 +1,48 @@
1
+ import { homedir } from "os";
2
+ import { resolve } from "path";
3
+ import { detectProject } from "./lib/project-detector.js";
4
+ /**
5
+ * Validate that a project slug contains only safe characters.
6
+ * Prevents path manipulation via project names.
7
+ */
8
+ export function validateProjectSlug(slug) {
9
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(slug)) {
10
+ throw new Error(`Invalid project slug "${slug}". Must be alphanumeric with optional dots, hyphens, underscores.`);
11
+ }
12
+ if (slug.includes("..") || slug.includes("/") || slug.includes("\\")) {
13
+ throw new Error(`Invalid project slug "${slug}". Must not contain path separators or traversal.`);
14
+ }
15
+ return slug;
16
+ }
17
+ /**
18
+ * Resolve a project slug: use explicit value, auto-detect from CWD, or throw.
19
+ */
20
+ export async function resolveProject(vaultPath, explicitSlug) {
21
+ let slug = explicitSlug ?? null;
22
+ if (!slug) {
23
+ slug = await detectProject(process.cwd(), vaultPath);
24
+ }
25
+ if (!slug) {
26
+ throw new Error("Could not detect project. Use --project <slug> to specify.");
27
+ }
28
+ return validateProjectSlug(slug);
29
+ }
30
+ export function loadConfig() {
31
+ const raw = process.env.VAULT_PATH ?? "~/Vaults/ai";
32
+ const vaultPath = raw.startsWith("~")
33
+ ? resolve(homedir(), raw.slice(2))
34
+ : resolve(raw);
35
+ // Bound VAULT_PATH: must be under home directory (exact prefix match)
36
+ const home = homedir();
37
+ if (vaultPath !== home && !vaultPath.startsWith(home + "/")) {
38
+ throw new Error(`VAULT_PATH must be under home directory (${home}). Got: ${vaultPath}`);
39
+ }
40
+ const maxInjectTokens = parseInt(process.env.MAX_INJECT_TOKENS ?? "1500", 10);
41
+ const sessionTtlHours = parseInt(process.env.SESSION_TTL_HOURS ?? "2", 10);
42
+ return {
43
+ vaultPath,
44
+ maxInjectTokens: Math.min(50000, Math.max(100, Number.isNaN(maxInjectTokens) ? 1500 : maxInjectTokens)),
45
+ sessionTtlHours: Math.min(168, Math.max(1, Number.isNaN(sessionTtlHours) ? 2 : sessionTtlHours)),
46
+ };
47
+ }
48
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAQ1D;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,IAAI,CAAC,8BAA8B,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CACb,yBAAyB,IAAI,mEAAmE,CACjG,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACrE,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,mDAAmD,CAAC,CAAC;IACpG,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,SAAiB,EACjB,YAA4B;IAE5B,IAAI,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;IAChC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC;IACvD,CAAC;IACD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;IAChF,CAAC;IACD,OAAO,mBAAmB,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,aAAa,CAAC;IACpD,MAAM,SAAS,GAAG,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC;QACnC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAClC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAEjB,sEAAsE;IACtE,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,IAAI,SAAS,KAAK,IAAI,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CACb,4CAA4C,IAAI,WAAW,SAAS,EAAE,CACvE,CAAC;IACJ,CAAC;IAED,MAAM,eAAe,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;IAC9E,MAAM,eAAe,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;IAE3E,OAAO;QACL,SAAS;QACT,eAAe,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;QACvG,eAAe,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;KACjG,CAAC;AACJ,CAAC"}
@@ -0,0 +1 @@
1
+ export {};