@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
package/dist/cli.js ADDED
@@ -0,0 +1,805 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { loadConfig } from "./config.js";
4
+ import { VaultFS } from "./lib/vault-fs.js";
5
+ import { SessionRegistryManager } from "./lib/session-registry.js";
6
+ import { readCommand, listCommand } from "./commands/read.js";
7
+ import { writeCommand } from "./commands/write.js";
8
+ import { searchCommand } from "./commands/search.js";
9
+ import { contextCommand } from "./commands/context.js";
10
+ import { decideCommand } from "./commands/decide.js";
11
+ import { todoCommand } from "./commands/todo.js";
12
+ import { brainstormCommand } from "./commands/brainstorm.js";
13
+ import { sessionCommand } from "./commands/session.js";
14
+ import { graphRelatedCommand, graphCrossProjectCommand } from "./commands/graph.js";
15
+ import { initCommand } from "./commands/init.js";
16
+ import { taskCommand } from "./commands/task.js";
17
+ import { learnCommand } from "./commands/learn.js";
18
+ import { pruneCommand, statsCommand, deprecateCommand } from "./commands/prune.js";
19
+ import { resumeCommand, formatResumeContext } from "./commands/resume.js";
20
+ let _config = null;
21
+ let _vaultFs = null;
22
+ let _sessionRegistry = null;
23
+ function getConfig() {
24
+ if (!_config)
25
+ _config = loadConfig();
26
+ return _config;
27
+ }
28
+ function getVaultFs() {
29
+ if (!_vaultFs)
30
+ _vaultFs = new VaultFS(getConfig().vaultPath);
31
+ return _vaultFs;
32
+ }
33
+ function getSessionRegistry() {
34
+ if (!_sessionRegistry)
35
+ _sessionRegistry = new SessionRegistryManager(getConfig().vaultPath, getConfig().sessionTtlHours);
36
+ return _sessionRegistry;
37
+ }
38
+ const noopLog = {
39
+ debug() { },
40
+ info() { },
41
+ warn() { },
42
+ error() { },
43
+ };
44
+ function createCtx() {
45
+ return {
46
+ vaultFs: getVaultFs(),
47
+ vaultPath: getConfig().vaultPath,
48
+ sessionRegistry: getSessionRegistry(),
49
+ config: getConfig(),
50
+ log: noopLog,
51
+ };
52
+ }
53
+ const program = new Command();
54
+ program
55
+ .name("obsidian-mcp")
56
+ .description("Universal agentic knowledge base — CLI backed by Obsidian vault")
57
+ .version("0.1.1");
58
+ // ── read ──────────────────────────────────────────────
59
+ program
60
+ .command("read <path>")
61
+ .description("Read a vault note")
62
+ .action(async (path) => {
63
+ try {
64
+ const content = await readCommand({ path }, createCtx());
65
+ process.stdout.write(content);
66
+ }
67
+ catch (e) {
68
+ const msg = e instanceof Error ? e.message : String(e);
69
+ console.error(`Error: ${msg}`);
70
+ process.exit(1);
71
+ }
72
+ });
73
+ // ── list ──────────────────────────────────────────────
74
+ program
75
+ .command("list <path>")
76
+ .description("List vault directory")
77
+ .option("-d, --depth <number>", "Listing depth", "1")
78
+ .action(async (path, opts) => {
79
+ try {
80
+ const depth = parseInt(opts.depth, 10);
81
+ if (Number.isNaN(depth) || depth < 1) {
82
+ throw new Error("--depth must be a positive integer");
83
+ }
84
+ const entries = await listCommand({ path, depth }, createCtx());
85
+ for (const entry of entries) {
86
+ console.log(entry);
87
+ }
88
+ }
89
+ catch (e) {
90
+ const msg = e instanceof Error ? e.message : String(e);
91
+ console.error(`Error: ${msg}`);
92
+ process.exit(1);
93
+ }
94
+ });
95
+ // ── write ─────────────────────────────────────────────
96
+ program
97
+ .command("write <path>")
98
+ .description("Write/create a vault note")
99
+ .requiredOption("-c, --content <text>", "Note content")
100
+ .option("-m, --mode <mode>", "Write mode: overwrite|append|prepend", "append")
101
+ .option("-f, --frontmatter <json>", "Frontmatter as JSON")
102
+ .action(async (path, opts) => {
103
+ try {
104
+ const validModes = ["overwrite", "append", "prepend"];
105
+ if (!validModes.includes(opts.mode)) {
106
+ throw new Error(`--mode must be one of: ${validModes.join(", ")}`);
107
+ }
108
+ let fm;
109
+ if (opts.frontmatter) {
110
+ try {
111
+ fm = JSON.parse(opts.frontmatter);
112
+ }
113
+ catch {
114
+ throw new Error(`Invalid JSON in --frontmatter: ${opts.frontmatter}`);
115
+ }
116
+ }
117
+ const result = await writeCommand({
118
+ path,
119
+ content: opts.content,
120
+ mode: opts.mode,
121
+ frontmatter: fm,
122
+ }, createCtx());
123
+ console.log(JSON.stringify(result));
124
+ }
125
+ catch (e) {
126
+ const msg = e instanceof Error ? e.message : String(e);
127
+ console.error(`Error: ${msg}`);
128
+ process.exit(1);
129
+ }
130
+ });
131
+ // ── append ────────────────────────────────────────────
132
+ program
133
+ .command("append <path>")
134
+ .description("Append to an existing vault note")
135
+ .requiredOption("-c, --content <text>", "Content to append")
136
+ .action(async (path, opts) => {
137
+ try {
138
+ const result = await writeCommand({ path, content: opts.content, mode: "append" }, createCtx());
139
+ console.log(JSON.stringify(result));
140
+ }
141
+ catch (e) {
142
+ const msg = e instanceof Error ? e.message : String(e);
143
+ console.error(`Error: ${msg}`);
144
+ process.exit(1);
145
+ }
146
+ });
147
+ // ── search ────────────────────────────────────────────
148
+ program
149
+ .command("search <query>")
150
+ .description("Search the vault")
151
+ .option("-p, --project <slug>", "Restrict to project")
152
+ .option("-l, --limit <number>", "Max results", "10")
153
+ .option("-s, --structured", "Structured frontmatter search")
154
+ .action(async (query, opts) => {
155
+ try {
156
+ const limit = parseInt(opts.limit, 10);
157
+ if (Number.isNaN(limit) || limit < 1) {
158
+ throw new Error("--limit must be a positive integer");
159
+ }
160
+ const results = await searchCommand({
161
+ query,
162
+ project: opts.project,
163
+ limit,
164
+ structured: opts.structured,
165
+ }, createCtx());
166
+ console.log(JSON.stringify(results, null, 2));
167
+ }
168
+ catch (e) {
169
+ const msg = e instanceof Error ? e.message : String(e);
170
+ console.error(`Error: ${msg}`);
171
+ process.exit(1);
172
+ }
173
+ });
174
+ // ── context ───────────────────────────────────────────
175
+ program
176
+ .command("context")
177
+ .description("Get project context (auto-detects from cwd)")
178
+ .option("-p, --project <slug>", "Project slug")
179
+ .option("-d, --detail <level>", "Detail level: summary|full", "summary")
180
+ .action(async (opts) => {
181
+ try {
182
+ const validDetails = ["summary", "full"];
183
+ if (!validDetails.includes(opts.detail)) {
184
+ throw new Error(`--detail must be one of: ${validDetails.join(", ")}`);
185
+ }
186
+ const result = await contextCommand({
187
+ project: opts.project,
188
+ detailLevel: opts.detail,
189
+ }, createCtx());
190
+ console.log(result.context_md);
191
+ }
192
+ catch (e) {
193
+ const msg = e instanceof Error ? e.message : String(e);
194
+ console.error(`Error: ${msg}`);
195
+ process.exit(1);
196
+ }
197
+ });
198
+ // ── init ──────────────────────────────────────────────
199
+ program
200
+ .command("init <project-path>")
201
+ .description("Scan a git repo and generate draft context.md")
202
+ .option("-s, --slug <name>", "Project slug (default: directory name)")
203
+ .action(async (projectPath, opts) => {
204
+ try {
205
+ const result = await initCommand(projectPath, opts.slug);
206
+ process.stdout.write(result.draft_context_md);
207
+ }
208
+ catch (e) {
209
+ const msg = e instanceof Error ? e.message : String(e);
210
+ console.error(`Error: ${msg}`);
211
+ process.exit(1);
212
+ }
213
+ });
214
+ // ── decide ────────────────────────────────────────────
215
+ program
216
+ .command("decide")
217
+ .description("Log an architecture decision record")
218
+ .requiredOption("-t, --title <text>", "Decision title")
219
+ .requiredOption("--decision <text>", "What was decided")
220
+ .option("--context <text>", "Why this decision was needed", "")
221
+ .option("--alternatives <text>", "Alternatives considered")
222
+ .option("--consequences <text>", "Known trade-offs")
223
+ .option("-p, --project <slug>", "Project slug")
224
+ .action(async (opts) => {
225
+ try {
226
+ const result = await decideCommand({
227
+ title: opts.title,
228
+ context: opts.context,
229
+ decision: opts.decision,
230
+ alternatives: opts.alternatives,
231
+ consequences: opts.consequences,
232
+ project: opts.project,
233
+ }, createCtx());
234
+ console.log(JSON.stringify(result));
235
+ }
236
+ catch (e) {
237
+ const msg = e instanceof Error ? e.message : String(e);
238
+ console.error(`Error: ${msg}`);
239
+ process.exit(1);
240
+ }
241
+ });
242
+ // ── todo (deprecated — use task) ─────────────────────
243
+ const todoCmd = program
244
+ .command("todo")
245
+ .description("[Deprecated — use 'task' instead] Manage project todos");
246
+ todoCmd
247
+ .command("list")
248
+ .description("List active todos")
249
+ .option("-p, --project <slug>", "Project slug")
250
+ .option("-b, --blockers-only", "Only show high-priority blockers")
251
+ .action(async (opts) => {
252
+ try {
253
+ const result = await todoCommand({
254
+ action: "list",
255
+ project: opts.project,
256
+ blockersOnly: opts.blockersOnly,
257
+ }, createCtx());
258
+ for (const todo of result.todos) {
259
+ const marker = todo.completed ? "[x]" : "[ ]";
260
+ const priority = todo.priority === "high" ? "P0" : todo.priority === "low" ? "P2" : "P1";
261
+ console.log(`${marker} [${priority}] ${todo.text}`);
262
+ }
263
+ }
264
+ catch (e) {
265
+ const msg = e instanceof Error ? e.message : String(e);
266
+ console.error(`Error: ${msg}`);
267
+ process.exit(1);
268
+ }
269
+ });
270
+ todoCmd
271
+ .command("add <text>")
272
+ .description("Add a todo")
273
+ .option("-p, --project <slug>", "Project slug")
274
+ .option("--priority <level>", "Priority: high|medium|low", "medium")
275
+ .action(async (text, opts) => {
276
+ try {
277
+ const validTodoPriorities = ["high", "medium", "low"];
278
+ if (!validTodoPriorities.includes(opts.priority)) {
279
+ throw new Error(`--priority must be one of: ${validTodoPriorities.join(", ")}`);
280
+ }
281
+ await todoCommand({
282
+ action: "add",
283
+ item: text,
284
+ priority: opts.priority,
285
+ project: opts.project,
286
+ }, createCtx());
287
+ console.log(`Added: ${text}`);
288
+ }
289
+ catch (e) {
290
+ const msg = e instanceof Error ? e.message : String(e);
291
+ console.error(`Error: ${msg}`);
292
+ process.exit(1);
293
+ }
294
+ });
295
+ todoCmd
296
+ .command("complete <text>")
297
+ .description("Complete a todo")
298
+ .option("-p, --project <slug>", "Project slug")
299
+ .action(async (text, opts) => {
300
+ try {
301
+ await todoCommand({
302
+ action: "complete",
303
+ item: text,
304
+ project: opts.project,
305
+ }, createCtx());
306
+ console.log(`Completed: ${text}`);
307
+ }
308
+ catch (e) {
309
+ const msg = e instanceof Error ? e.message : String(e);
310
+ console.error(`Error: ${msg}`);
311
+ process.exit(1);
312
+ }
313
+ });
314
+ // ── task ──────────────────────────────────────────────
315
+ const taskCmd = program
316
+ .command("task")
317
+ .description("Manage project tasks (kanban board)");
318
+ taskCmd
319
+ .command("list")
320
+ .description("List tasks")
321
+ .option("-p, --project <slug>", "Project slug")
322
+ .option("-s, --status <status>", "Filter by status")
323
+ .option("--priority <level>", "Filter by priority")
324
+ .option("--assigned-to <tool>", "Filter by assignee")
325
+ .action(async (opts) => {
326
+ try {
327
+ const validPriorities = ["p0", "p1", "p2"];
328
+ const validStatuses = ["backlog", "in-progress", "blocked", "done", "cancelled"];
329
+ if (opts.priority && !validPriorities.includes(opts.priority)) {
330
+ throw new Error(`--priority must be one of: ${validPriorities.join(", ")}`);
331
+ }
332
+ if (opts.status && !validStatuses.includes(opts.status)) {
333
+ throw new Error(`--status must be one of: ${validStatuses.join(", ")}`);
334
+ }
335
+ const result = await taskCommand({
336
+ action: "list",
337
+ project: opts.project,
338
+ status: opts.status,
339
+ priority: opts.priority,
340
+ assignedTo: opts.assignedTo,
341
+ }, createCtx());
342
+ if (!result.tasks?.length) {
343
+ console.log("No tasks found.");
344
+ return;
345
+ }
346
+ for (const t of result.tasks) {
347
+ const blocked = t.blocked_by.length > 0 ? " [BLOCKED]" : "";
348
+ console.log(`[${t.id}] [${t.priority}] [${t.status}]${blocked} ${t.title}`);
349
+ }
350
+ }
351
+ catch (e) {
352
+ const msg = e instanceof Error ? e.message : String(e);
353
+ console.error(`Error: ${msg}`);
354
+ process.exit(1);
355
+ }
356
+ });
357
+ taskCmd
358
+ .command("add <title>")
359
+ .description("Add a task")
360
+ .option("-p, --project <slug>", "Project slug")
361
+ .option("--priority <level>", "Priority: p0|p1|p2", "p1")
362
+ .option("--blocked-by <ids...>", "Task IDs that block this task")
363
+ .option("--assigned-to <tool>", "Assignee: claude-code|opencode|codex|human")
364
+ .option("--tags <tags>", "Comma-separated tags")
365
+ .action(async (title, opts) => {
366
+ try {
367
+ const validPriorities = ["p0", "p1", "p2"];
368
+ if (!validPriorities.includes(opts.priority)) {
369
+ throw new Error(`--priority must be one of: ${validPriorities.join(", ")}`);
370
+ }
371
+ const tags = opts.tags ? opts.tags.split(",").map((t) => t.trim()) : undefined;
372
+ const result = await taskCommand({
373
+ action: "add",
374
+ title,
375
+ project: opts.project,
376
+ priority: opts.priority,
377
+ blockedBy: opts.blockedBy,
378
+ assignedTo: opts.assignedTo,
379
+ tags,
380
+ }, createCtx());
381
+ console.log(JSON.stringify({ task_id: result.task_id, path: result.path }));
382
+ }
383
+ catch (e) {
384
+ const msg = e instanceof Error ? e.message : String(e);
385
+ console.error(`Error: ${msg}`);
386
+ process.exit(1);
387
+ }
388
+ });
389
+ taskCmd
390
+ .command("update <task-id>")
391
+ .description("Update a task")
392
+ .option("-p, --project <slug>", "Project slug")
393
+ .option("-s, --status <status>", "New status")
394
+ .option("--priority <level>", "New priority")
395
+ .option("--blocked-by <ids...>", "New blocked-by list")
396
+ .option("--assigned-to <tool>", "New assignee")
397
+ .option("-t, --title <text>", "New title")
398
+ .action(async (taskId, opts) => {
399
+ try {
400
+ const validPriorities = ["p0", "p1", "p2"];
401
+ const validStatuses = ["backlog", "in-progress", "blocked", "done", "cancelled"];
402
+ if (opts.priority && !validPriorities.includes(opts.priority)) {
403
+ throw new Error(`--priority must be one of: ${validPriorities.join(", ")}`);
404
+ }
405
+ if (opts.status && !validStatuses.includes(opts.status)) {
406
+ throw new Error(`--status must be one of: ${validStatuses.join(", ")}`);
407
+ }
408
+ const result = await taskCommand({
409
+ action: "update",
410
+ taskId,
411
+ project: opts.project,
412
+ status: opts.status,
413
+ priority: opts.priority,
414
+ blockedBy: opts.blockedBy,
415
+ assignedTo: opts.assignedTo,
416
+ title: opts.title,
417
+ }, createCtx());
418
+ console.log(JSON.stringify({ task_id: result.task_id, updated_fields: result.updated_fields }));
419
+ }
420
+ catch (e) {
421
+ const msg = e instanceof Error ? e.message : String(e);
422
+ console.error(`Error: ${msg}`);
423
+ process.exit(1);
424
+ }
425
+ });
426
+ taskCmd
427
+ .command("board")
428
+ .description("Show task board (kanban view)")
429
+ .option("-p, --project <slug>", "Project slug")
430
+ .action(async (opts) => {
431
+ try {
432
+ const result = await taskCommand({
433
+ action: "board",
434
+ project: opts.project,
435
+ }, createCtx());
436
+ if (!result.board)
437
+ return;
438
+ for (const [status, tasks] of Object.entries(result.board)) {
439
+ if (tasks.length === 0)
440
+ continue;
441
+ console.log(`\n=== ${status.toUpperCase()} (${tasks.length}) ===`);
442
+ for (const t of tasks) {
443
+ const blocked = t.blocked_by.length > 0 ? " [BLOCKED]" : "";
444
+ const assignee = t.assigned_to ? ` @${t.assigned_to}` : "";
445
+ console.log(` [${t.id}] [${t.priority}]${blocked}${assignee} ${t.title}`);
446
+ }
447
+ }
448
+ }
449
+ catch (e) {
450
+ const msg = e instanceof Error ? e.message : String(e);
451
+ console.error(`Error: ${msg}`);
452
+ process.exit(1);
453
+ }
454
+ });
455
+ // ── learn ─────────────────────────────────────────────
456
+ const learnCmd = program
457
+ .command("learn")
458
+ .description("Capture and query learnings");
459
+ learnCmd
460
+ .command("add")
461
+ .description("Capture a learning")
462
+ .requiredOption("-t, --title <text>", "Learning title")
463
+ .requiredOption("-d, --discovery <text>", "What was discovered")
464
+ .option("-p, --project <slug>", "Project slug")
465
+ .option("--tags <tags>", "Comma-separated tags")
466
+ .option("--confidence <level>", "Confidence: high|medium|low", "medium")
467
+ .option("--source <tool>", "Source tool")
468
+ .action(async (opts) => {
469
+ try {
470
+ const validConfidences = ["high", "medium", "low"];
471
+ if (!validConfidences.includes(opts.confidence)) {
472
+ throw new Error(`--confidence must be one of: ${validConfidences.join(", ")}`);
473
+ }
474
+ const tags = opts.tags ? opts.tags.split(",").map((t) => t.trim()) : undefined;
475
+ const result = await learnCommand({
476
+ action: "add",
477
+ title: opts.title,
478
+ discovery: opts.discovery,
479
+ project: opts.project,
480
+ tags,
481
+ confidence: opts.confidence,
482
+ source: opts.source,
483
+ }, createCtx());
484
+ console.log(JSON.stringify({ learning_id: result.learning_id, path: result.path }));
485
+ }
486
+ catch (e) {
487
+ const msg = e instanceof Error ? e.message : String(e);
488
+ console.error(`Error: ${msg}`);
489
+ process.exit(1);
490
+ }
491
+ });
492
+ learnCmd
493
+ .command("list")
494
+ .description("List learnings")
495
+ .option("-p, --project <slug>", "Project slug")
496
+ .option("--tag <tag>", "Filter by tag")
497
+ .action(async (opts) => {
498
+ try {
499
+ const result = await learnCommand({
500
+ action: "list",
501
+ project: opts.project,
502
+ tag: opts.tag,
503
+ }, createCtx());
504
+ if (!result.learnings?.length) {
505
+ console.log(JSON.stringify({ learnings: [] }));
506
+ return;
507
+ }
508
+ if (process.stdout.isTTY) {
509
+ for (const l of result.learnings) {
510
+ const tagStr = l.tags.length > 0 ? ` (${l.tags.join(", ")})` : "";
511
+ console.log(`[${l.id}] [${l.confidence}] ${l.title}${tagStr} — ${l.created}`);
512
+ }
513
+ }
514
+ else {
515
+ console.log(JSON.stringify({ learnings: result.learnings }));
516
+ }
517
+ }
518
+ catch (e) {
519
+ const msg = e instanceof Error ? e.message : String(e);
520
+ console.error(`Error: ${msg}`);
521
+ process.exit(1);
522
+ }
523
+ });
524
+ // ── brainstorm ────────────────────────────────────────
525
+ program
526
+ .command("brainstorm <topic>")
527
+ .description("Start or continue a brainstorm")
528
+ .requiredOption("-c, --content <text>", "Brainstorm content to add")
529
+ .option("-p, --project <slug>", "Project slug")
530
+ .action(async (topic, opts) => {
531
+ try {
532
+ const result = await brainstormCommand({
533
+ topic,
534
+ content: opts.content,
535
+ project: opts.project,
536
+ }, createCtx());
537
+ console.log(JSON.stringify(result));
538
+ }
539
+ catch (e) {
540
+ const msg = e instanceof Error ? e.message : String(e);
541
+ console.error(`Error: ${msg}`);
542
+ process.exit(1);
543
+ }
544
+ });
545
+ // ── session ───────────────────────────────────────────
546
+ const sessionCmd = program
547
+ .command("session")
548
+ .description("Manage agent sessions (swarming coordination)");
549
+ sessionCmd
550
+ .command("register")
551
+ .description("Register a new agent session")
552
+ .requiredOption("--tool <name>", "Tool name: claude-code|opencode|codex")
553
+ .option("-p, --project <slug>", "Project being worked on")
554
+ .option("--task <summary>", "Task summary")
555
+ .option("--files <paths...>", "Files being touched")
556
+ .action(async (opts) => {
557
+ try {
558
+ const result = await sessionCommand({
559
+ action: "register",
560
+ tool: opts.tool,
561
+ project: opts.project,
562
+ taskSummary: opts.task,
563
+ filesTouched: opts.files,
564
+ }, createCtx());
565
+ console.log(JSON.stringify(result, null, 2));
566
+ }
567
+ catch (e) {
568
+ const msg = e instanceof Error ? e.message : String(e);
569
+ console.error(`Error: ${msg}`);
570
+ process.exit(1);
571
+ }
572
+ });
573
+ sessionCmd
574
+ .command("heartbeat <session-id>")
575
+ .description("Update session heartbeat")
576
+ .action(async (sessionId) => {
577
+ try {
578
+ await sessionCommand({ action: "heartbeat", sessionId }, createCtx());
579
+ console.log("Heartbeat updated");
580
+ }
581
+ catch (e) {
582
+ const msg = e instanceof Error ? e.message : String(e);
583
+ console.error(`Error: ${msg}`);
584
+ process.exit(1);
585
+ }
586
+ });
587
+ sessionCmd
588
+ .command("complete <session-id>")
589
+ .description("Mark session as completed")
590
+ .option("--summary <text>", "Session summary")
591
+ .option("--outcome <text>", "Session outcome")
592
+ .option("--files <paths...>", "Files touched during session")
593
+ .option("--tasks <ids...>", "Task IDs completed")
594
+ .option("-p, --project <slug>", "Project slug")
595
+ .action(async (sessionId, opts) => {
596
+ try {
597
+ const result = await sessionCommand({
598
+ action: "complete",
599
+ sessionId,
600
+ taskSummary: opts.summary,
601
+ outcome: opts.outcome,
602
+ filesTouched: opts.files,
603
+ tasksCompleted: opts.tasks,
604
+ project: opts.project,
605
+ }, createCtx());
606
+ console.log("Session completed");
607
+ if (result.session_note_path) {
608
+ console.log(`Session note: ${result.session_note_path}`);
609
+ }
610
+ }
611
+ catch (e) {
612
+ const msg = e instanceof Error ? e.message : String(e);
613
+ console.error(`Error: ${msg}`);
614
+ process.exit(1);
615
+ }
616
+ });
617
+ sessionCmd
618
+ .command("list")
619
+ .description("List active sessions")
620
+ .action(async () => {
621
+ try {
622
+ const result = await sessionCommand({ action: "list_active" }, createCtx());
623
+ if (!result.active_sessions?.length) {
624
+ console.log("No active sessions");
625
+ return;
626
+ }
627
+ for (const s of result.active_sessions) {
628
+ console.log(`[${s.tool}] ${s.project ?? "unknown"}: ${s.task_summary ?? "no task"} (${s.id})`);
629
+ }
630
+ }
631
+ catch (e) {
632
+ const msg = e instanceof Error ? e.message : String(e);
633
+ console.error(`Error: ${msg}`);
634
+ process.exit(1);
635
+ }
636
+ });
637
+ // ── graph ─────────────────────────────────────────────
638
+ const graphCmd = program
639
+ .command("graph")
640
+ .description("Knowledge graph traversal");
641
+ graphCmd
642
+ .command("related <path>")
643
+ .description("Get backlinks and outgoing links for a note")
644
+ .option("--hops <number>", "Traversal depth", "1")
645
+ .action(async (path, opts) => {
646
+ try {
647
+ const hops = parseInt(opts.hops, 10);
648
+ if (Number.isNaN(hops) || hops < 1) {
649
+ throw new Error("--hops must be a positive integer");
650
+ }
651
+ const result = await graphRelatedCommand({ path, hops }, createCtx());
652
+ console.log(JSON.stringify(result, null, 2));
653
+ }
654
+ catch (e) {
655
+ const msg = e instanceof Error ? e.message : String(e);
656
+ console.error(`Error: ${msg}`);
657
+ process.exit(1);
658
+ }
659
+ });
660
+ graphCmd
661
+ .command("cross-project <query>")
662
+ .description("Search across all projects")
663
+ .option("-l, --limit <number>", "Max results", "20")
664
+ .action(async (query, opts) => {
665
+ try {
666
+ const limit = parseInt(opts.limit, 10);
667
+ if (Number.isNaN(limit) || limit < 1) {
668
+ throw new Error("--limit must be a positive integer");
669
+ }
670
+ const grouped = await graphCrossProjectCommand({ query, limit }, createCtx());
671
+ for (const [project, results] of Object.entries(grouped)) {
672
+ console.log(`\n${project} (${results.length} matches):`);
673
+ for (const r of results) {
674
+ console.log(` ${r.path}: ${r.snippet.slice(0, 100)}`);
675
+ }
676
+ }
677
+ }
678
+ catch (e) {
679
+ const msg = e instanceof Error ? e.message : String(e);
680
+ console.error(`Error: ${msg}`);
681
+ process.exit(1);
682
+ }
683
+ });
684
+ // ── prune ─────────────────────────────────────────────
685
+ program
686
+ .command("prune")
687
+ .description("Archive or delete stale vault content")
688
+ .option("-p, --project <slug>", "Project slug")
689
+ .option("-a, --all", "Prune all projects")
690
+ .option("-m, --mode <mode>", "Mode: dry-run|archive|delete", "dry-run")
691
+ .option("--sessions <days>", "Session retention in days", "30")
692
+ .option("--done-tasks <days>", "Done task retention in days", "30")
693
+ .option("--todos <days>", "Completed todo retention in days (0=keep)", "0")
694
+ .action(async (opts) => {
695
+ try {
696
+ const validModes = ["dry-run", "archive", "delete"];
697
+ if (!validModes.includes(opts.mode)) {
698
+ throw new Error(`--mode must be one of: ${validModes.join(", ")}`);
699
+ }
700
+ const policy = {
701
+ sessions: parseInt(opts.sessions, 10) || 30,
702
+ doneTasks: parseInt(opts.doneTasks, 10) || 30,
703
+ todos: parseInt(opts.todos, 10) || 0,
704
+ };
705
+ const results = await pruneCommand({
706
+ project: opts.project,
707
+ mode: opts.mode,
708
+ policy,
709
+ all: opts.all,
710
+ }, createCtx());
711
+ for (const r of results) {
712
+ console.log(`\n=== ${r.project} ===`);
713
+ console.log(` Scanned: ${r.stats.sessions_scanned} sessions, ${r.stats.tasks_scanned} tasks`);
714
+ if (r.archived.length > 0) {
715
+ console.log(` ${opts.mode === "dry-run" ? "Would archive" : "Archived"}: ${r.archived.length} items`);
716
+ for (const a of r.archived)
717
+ console.log(` ${a.from} → ${a.to}`);
718
+ }
719
+ if (r.deleted.length > 0) {
720
+ console.log(` ${opts.mode === "dry-run" ? "Would delete" : "Deleted"}: ${r.deleted.length} items`);
721
+ for (const d of r.deleted)
722
+ console.log(` ${d}`);
723
+ }
724
+ if (r.archived.length === 0 && r.deleted.length === 0) {
725
+ console.log(" Nothing to prune.");
726
+ }
727
+ }
728
+ }
729
+ catch (e) {
730
+ const msg = e instanceof Error ? e.message : String(e);
731
+ console.error(`Error: ${msg}`);
732
+ process.exit(1);
733
+ }
734
+ });
735
+ // ── stats ─────────────────────────────────────────────
736
+ program
737
+ .command("stats")
738
+ .description("Show vault content statistics for a project")
739
+ .option("-p, --project <slug>", "Project slug")
740
+ .action(async (opts) => {
741
+ try {
742
+ const result = await statsCommand({ project: opts.project }, createCtx());
743
+ console.log(`\n=== ${result.project} ===`);
744
+ console.log(` Sessions: ${result.sessions}`);
745
+ console.log(` Tasks: ${result.tasks.total} (backlog: ${result.tasks.backlog}, in-progress: ${result.tasks.inProgress}, done: ${result.tasks.done}, cancelled: ${result.tasks.cancelled})`);
746
+ console.log(` Learnings: ${result.learnings}`);
747
+ console.log(` ADRs: ${result.adrs}`);
748
+ console.log(` Brainstorms: ${result.brainstorms}`);
749
+ console.log(` Total files: ${result.totalFiles}`);
750
+ }
751
+ catch (e) {
752
+ const msg = e instanceof Error ? e.message : String(e);
753
+ console.error(`Error: ${msg}`);
754
+ process.exit(1);
755
+ }
756
+ });
757
+ // ── deprecate ─────────────────────────────────────────
758
+ program
759
+ .command("deprecate <path>")
760
+ .description("Mark a vault item as deprecated")
761
+ .option("-r, --reason <text>", "Reason for deprecation")
762
+ .action(async (path, opts) => {
763
+ try {
764
+ const result = await deprecateCommand({ path, reason: opts.reason }, createCtx());
765
+ console.log(`Deprecated: ${result.path} (status: ${result.status})`);
766
+ }
767
+ catch (e) {
768
+ const msg = e instanceof Error ? e.message : String(e);
769
+ console.error(`Error: ${msg}`);
770
+ process.exit(1);
771
+ }
772
+ });
773
+ // ── resume ────────────────────────────────────────────
774
+ program
775
+ .command("resume")
776
+ .description("Get resume context for continuing work — shows recent sessions, interrupted work, next steps")
777
+ .option("-p, --project <slug>", "Project slug")
778
+ .option("-l, --limit <number>", "Number of recent sessions to show", "5")
779
+ .option("--json", "Output as JSON instead of markdown")
780
+ .action(async (opts) => {
781
+ try {
782
+ const limit = parseInt(opts.limit, 10);
783
+ if (Number.isNaN(limit) || limit < 1) {
784
+ throw new Error("--limit must be a positive integer");
785
+ }
786
+ const result = await resumeCommand({ project: opts.project, limit }, createCtx());
787
+ if (opts.json) {
788
+ console.log(JSON.stringify(result, null, 2));
789
+ }
790
+ else {
791
+ console.log(formatResumeContext(result));
792
+ }
793
+ }
794
+ catch (e) {
795
+ const msg = e instanceof Error ? e.message : String(e);
796
+ console.error(`Error: ${msg}`);
797
+ process.exit(1);
798
+ }
799
+ });
800
+ program.parse();
801
+ process.on("unhandledRejection", (reason) => {
802
+ console.error("Unhandled rejection:", reason);
803
+ process.exit(1);
804
+ });
805
+ //# sourceMappingURL=cli.js.map