@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,256 @@
1
+ import { readFile, readdir } from "fs/promises";
2
+ import { join, basename } from "path";
3
+ import { execFile } from "child_process";
4
+ import { promisify } from "util";
5
+ import { safeExternalPath } from "../lib/safe-external-path.js";
6
+ const execFileAsync = promisify(execFile);
7
+ const MANIFEST_FILES = [
8
+ "README.md", "AGENTS.md", "CLAUDE.md",
9
+ "go.mod", "Cargo.toml", "package.json", "pyproject.toml",
10
+ "requirements.txt", "Makefile", "Dockerfile", "docker-compose.yml",
11
+ ];
12
+ /**
13
+ * Scan a git repo and generate a draft context.md.
14
+ * Does NOT write to vault — returns the draft as a string.
15
+ */
16
+ export async function initCommand(projectPath, slug) {
17
+ const resolved = await safeExternalPath(projectPath);
18
+ const projectSlug = slug ?? basename(resolved).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
19
+ // Probe manifest files
20
+ const probes = [];
21
+ for (const name of MANIFEST_FILES) {
22
+ try {
23
+ const content = await readFile(join(resolved, name), "utf-8");
24
+ probes.push({ name, exists: true, content });
25
+ }
26
+ catch {
27
+ probes.push({ name, exists: false });
28
+ }
29
+ }
30
+ // Detect tech stack
31
+ const techStack = detectTechStack(probes);
32
+ // Extract description from README
33
+ const readme = probes.find((p) => p.name === "README.md" && p.exists);
34
+ const description = readme ? extractDescription(readme.content) : "No README found.";
35
+ // Check for AI instructions
36
+ const hasAgentsMd = probes.some((p) => p.name === "AGENTS.md" && p.exists);
37
+ const hasClaudeMd = probes.some((p) => p.name === "CLAUDE.md" && p.exists);
38
+ // Git log (5s timeout)
39
+ let recentActivity = "No git history available.";
40
+ try {
41
+ const { stdout } = await execFileAsync("git", ["log", "--oneline", "-20"], {
42
+ cwd: resolved,
43
+ timeout: 5000,
44
+ });
45
+ recentActivity = stdout.trim() || "No commits yet.";
46
+ }
47
+ catch {
48
+ // git log failed, use default
49
+ }
50
+ // Git remote
51
+ let repoUrl = "";
52
+ try {
53
+ const { stdout } = await execFileAsync("git", ["remote", "-v"], {
54
+ cwd: resolved,
55
+ timeout: 3000,
56
+ });
57
+ const match = stdout.match(/origin\s+(\S+)\s+\(fetch\)/);
58
+ if (match)
59
+ repoUrl = match[1];
60
+ }
61
+ catch {
62
+ // no remote
63
+ }
64
+ // Detect project structure
65
+ const structure = await detectStructure(resolved);
66
+ // Detect key files
67
+ const keyFiles = probes
68
+ .filter((p) => p.exists)
69
+ .map((p) => p.name);
70
+ // AI instructions note
71
+ const aiInstructions = [];
72
+ if (hasAgentsMd)
73
+ aiInstructions.push("AGENTS.md present");
74
+ if (hasClaudeMd)
75
+ aiInstructions.push("CLAUDE.md present");
76
+ // Build draft
77
+ const today = new Date().toISOString().slice(0, 10);
78
+ const draft = `---
79
+ type: context
80
+ project: ${projectSlug}
81
+ status: active
82
+ created: ${today}
83
+ updated: ${today}
84
+ tags: []
85
+ ---
86
+
87
+ # ${projectSlug}
88
+
89
+ ## What It Is
90
+
91
+ ${description}
92
+
93
+ ## Current State
94
+
95
+ ${structure}${repoUrl ? `\nRepository: ${repoUrl}` : ""}${aiInstructions.length > 0 ? `\nAI Instructions: ${aiInstructions.join(", ")}` : ""}
96
+
97
+ ## Tech Stack
98
+
99
+ ${techStack.map((t) => `- ${t}`).join("\n")}
100
+
101
+ ## Key Files
102
+
103
+ ${keyFiles.map((f) => `- \`${f}\``).join("\n")}
104
+
105
+ ## Recent Activity
106
+
107
+ \`\`\`
108
+ ${recentActivity}
109
+ \`\`\`
110
+ `;
111
+ return { draft_context_md: draft, detected_tech_stack: techStack, slug: projectSlug };
112
+ }
113
+ function detectTechStack(probes) {
114
+ const stack = [];
115
+ const goMod = probes.find((p) => p.name === "go.mod" && p.exists);
116
+ if (goMod) {
117
+ stack.push("Go");
118
+ const content = goMod.content;
119
+ if (content.includes("gin-gonic"))
120
+ stack.push("Gin");
121
+ if (content.includes("fiber"))
122
+ stack.push("Fiber");
123
+ if (content.includes("echo"))
124
+ stack.push("Echo");
125
+ if (content.includes("sqlc"))
126
+ stack.push("sqlc");
127
+ if (content.includes("pgx") || content.includes("lib/pq"))
128
+ stack.push("PostgreSQL");
129
+ }
130
+ const cargo = probes.find((p) => p.name === "Cargo.toml" && p.exists);
131
+ if (cargo) {
132
+ stack.push("Rust");
133
+ const content = cargo.content;
134
+ if (content.includes("actix"))
135
+ stack.push("Actix");
136
+ if (content.includes("axum"))
137
+ stack.push("Axum");
138
+ if (content.includes("tokio"))
139
+ stack.push("Tokio");
140
+ if (content.includes("serde"))
141
+ stack.push("Serde");
142
+ }
143
+ const packageJson = probes.find((p) => p.name === "package.json" && p.exists);
144
+ if (packageJson) {
145
+ try {
146
+ const pkg = JSON.parse(packageJson.content);
147
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
148
+ if (allDeps["next"])
149
+ stack.push("Next.js");
150
+ else if (allDeps["react"])
151
+ stack.push("React");
152
+ if (allDeps["vue"])
153
+ stack.push("Vue");
154
+ if (allDeps["svelte"] || allDeps["@sveltejs/kit"])
155
+ stack.push("Svelte");
156
+ if (allDeps["express"])
157
+ stack.push("Express");
158
+ if (allDeps["typescript"])
159
+ stack.push("TypeScript");
160
+ if (allDeps["tailwindcss"])
161
+ stack.push("Tailwind CSS");
162
+ if (allDeps["prisma"] || allDeps["@prisma/client"])
163
+ stack.push("Prisma");
164
+ if (allDeps["drizzle-orm"])
165
+ stack.push("Drizzle");
166
+ if (!stack.some((s) => ["Next.js", "React", "Vue", "Svelte", "Express"].includes(s))) {
167
+ stack.push("Node.js");
168
+ }
169
+ }
170
+ catch {
171
+ stack.push("Node.js");
172
+ }
173
+ }
174
+ const pyproject = probes.find((p) => p.name === "pyproject.toml" && p.exists);
175
+ const requirements = probes.find((p) => p.name === "requirements.txt" && p.exists);
176
+ if (pyproject || requirements) {
177
+ stack.push("Python");
178
+ const content = (pyproject?.content ?? "") + (requirements?.content ?? "");
179
+ if (content.includes("fastapi"))
180
+ stack.push("FastAPI");
181
+ if (content.includes("django"))
182
+ stack.push("Django");
183
+ if (content.includes("flask"))
184
+ stack.push("Flask");
185
+ if (content.includes("groq"))
186
+ stack.push("Groq");
187
+ if (content.includes("openai"))
188
+ stack.push("OpenAI");
189
+ if (content.includes("graphql") || content.includes("strawberry") || content.includes("ariadne"))
190
+ stack.push("GraphQL");
191
+ }
192
+ const dockerfile = probes.find((p) => p.name === "Dockerfile" && p.exists);
193
+ if (dockerfile)
194
+ stack.push("Docker");
195
+ const dockerCompose = probes.find((p) => p.name === "docker-compose.yml" && p.exists);
196
+ if (dockerCompose) {
197
+ stack.push("Docker Compose");
198
+ const content = dockerCompose.content;
199
+ if (content.includes("postgres"))
200
+ stack.push("PostgreSQL");
201
+ if (content.includes("redis"))
202
+ stack.push("Redis");
203
+ if (content.includes("mongo"))
204
+ stack.push("MongoDB");
205
+ }
206
+ const makefile = probes.find((p) => p.name === "Makefile" && p.exists);
207
+ if (makefile)
208
+ stack.push("Make");
209
+ return [...new Set(stack)];
210
+ }
211
+ function extractDescription(readme) {
212
+ const lines = readme.split("\n");
213
+ let foundHeading = false;
214
+ const paragraphLines = [];
215
+ for (const line of lines) {
216
+ if (line.startsWith("# ")) {
217
+ foundHeading = true;
218
+ continue;
219
+ }
220
+ if (foundHeading) {
221
+ if (line.trim() === "" && paragraphLines.length === 0)
222
+ continue;
223
+ if (line.startsWith("## ") || line.startsWith("# "))
224
+ break;
225
+ if (line.trim() === "" && paragraphLines.length > 0)
226
+ break;
227
+ paragraphLines.push(line);
228
+ }
229
+ }
230
+ const desc = paragraphLines.join(" ").trim();
231
+ if (desc.length > 500)
232
+ return desc.slice(0, 500) + "...";
233
+ return desc || "No description available.";
234
+ }
235
+ async function detectStructure(projectPath) {
236
+ try {
237
+ const entries = await readdir(projectPath, { withFileTypes: true });
238
+ const dirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
239
+ const hasFrontend = dirs.some((d) => ["frontend", "web", "client", "app"].includes(d));
240
+ const hasBackend = dirs.some((d) => ["backend", "server", "api", "cmd", "internal"].includes(d));
241
+ const hasPackages = dirs.some((d) => ["packages", "libs", "crates", "modules"].includes(d));
242
+ if (hasPackages)
243
+ return "Monorepo structure";
244
+ if (hasFrontend && hasBackend)
245
+ return "Full-stack (frontend + backend)";
246
+ if (dirs.includes("cmd") && dirs.includes("internal"))
247
+ return "Go project structure (cmd + internal)";
248
+ if (dirs.length > 10)
249
+ return `Large project (${dirs.length} top-level directories)`;
250
+ return `Single project (top-level dirs: ${dirs.slice(0, 8).join(", ")})`;
251
+ }
252
+ catch {
253
+ return "Structure unknown";
254
+ }
255
+ }
256
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { CommandContext } from "../core/types.js";
2
+ export type Confidence = "high" | "medium" | "low";
3
+ export interface LearningItem {
4
+ id: string;
5
+ title: string;
6
+ confidence: Confidence;
7
+ tags: string[];
8
+ created: string;
9
+ path: string;
10
+ }
11
+ export declare function learnCommand(args: {
12
+ action: "add" | "list";
13
+ title?: string;
14
+ discovery?: string;
15
+ project?: string;
16
+ tags?: string[];
17
+ confidence?: Confidence;
18
+ source?: string;
19
+ sessionId?: string;
20
+ tag?: string;
21
+ }, ctx: CommandContext): Promise<{
22
+ learning_id?: string;
23
+ path?: string;
24
+ learnings?: LearningItem[];
25
+ }>;
@@ -0,0 +1,92 @@
1
+ import { parseFrontmatter, serializeFrontmatter, createFrontmatter } from "../lib/frontmatter.js";
2
+ import { resolveProject } from "../config.js";
3
+ import { getNextNumber, slugify } from "../lib/auto-number.js";
4
+ const VALID_CONFIDENCE = ["high", "medium", "low"];
5
+ export async function learnCommand(args, ctx) {
6
+ const projectSlug = await resolveProject(ctx.vaultPath, args.project);
7
+ const vaultFs = ctx.vaultFs;
8
+ const learningsDir = `projects/${projectSlug}/learnings`;
9
+ switch (args.action) {
10
+ case "add": {
11
+ if (!args.title)
12
+ throw new Error("Title required for add");
13
+ if (!args.discovery)
14
+ throw new Error("Discovery required for add");
15
+ const confidence = args.confidence ?? "medium";
16
+ if (!VALID_CONFIDENCE.includes(confidence)) {
17
+ throw new Error(`Invalid confidence "${confidence}". Must be one of: ${VALID_CONFIDENCE.join(", ")}`);
18
+ }
19
+ const nextNum = await getNextNumber(vaultFs, learningsDir);
20
+ const padded = String(nextNum).padStart(3, "0");
21
+ const titleSlug = slugify(args.title);
22
+ const filename = `${padded}-${titleSlug}.md`;
23
+ const filePath = `${learningsDir}/${filename}`;
24
+ const learningId = padded;
25
+ const fm = createFrontmatter({
26
+ type: "learning",
27
+ project: projectSlug,
28
+ status: "active",
29
+ confidence,
30
+ source: args.source ?? "",
31
+ session_id: args.sessionId ?? "",
32
+ tags: args.tags ?? [],
33
+ });
34
+ const body = `\n# ${args.title}\n\n${args.discovery}\n`;
35
+ await vaultFs.write(filePath, serializeFrontmatter(fm, body));
36
+ return { learning_id: learningId, path: filePath };
37
+ }
38
+ case "list": {
39
+ const learnings = await listLearnings(vaultFs, learningsDir);
40
+ let filtered = learnings;
41
+ if (args.tag) {
42
+ filtered = filtered.filter((l) => l.tags.includes(args.tag));
43
+ }
44
+ return { learnings: filtered };
45
+ }
46
+ default:
47
+ throw new Error(`Unknown action: ${args.action}`);
48
+ }
49
+ }
50
+ async function listLearnings(vaultFs, learningsDir) {
51
+ const learnings = [];
52
+ let files;
53
+ try {
54
+ files = await vaultFs.list(learningsDir, 1);
55
+ }
56
+ catch {
57
+ return [];
58
+ }
59
+ for (const file of files) {
60
+ if (!file.endsWith(".md"))
61
+ continue;
62
+ const filePath = file;
63
+ try {
64
+ const content = await vaultFs.read(filePath);
65
+ const { data, content: body } = parseFrontmatter(content);
66
+ if (data.type !== "learning")
67
+ continue;
68
+ const basename = file.split("/").pop() ?? file;
69
+ const idMatch = basename.match(/^(\d+)-/);
70
+ if (!idMatch)
71
+ continue;
72
+ const titleMatch = body.match(/^# (.+)$/m);
73
+ const title = titleMatch ? titleMatch[1].trim() : file;
74
+ learnings.push({
75
+ id: idMatch[1],
76
+ title,
77
+ confidence: data.confidence ?? "medium",
78
+ tags: Array.isArray(data.tags) ? data.tags : [],
79
+ created: data.created ?? "",
80
+ path: filePath,
81
+ });
82
+ }
83
+ catch (e) {
84
+ if (e instanceof Error && "code" in e && e.code !== "ENOENT") {
85
+ console.error("[learn] Skipping unreadable learning file:", e instanceof Error ? e.message : e);
86
+ }
87
+ }
88
+ }
89
+ learnings.sort((a, b) => a.id.localeCompare(b.id));
90
+ return learnings;
91
+ }
92
+ //# sourceMappingURL=learn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"learn.js","sourceRoot":"","sources":["../../src/commands/learn.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,241 @@
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 { learnCommand } from "./learn.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("learnCommand", () => {
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("add learning", () => {
31
+ it("creates new learning with auto-numbering", async () => {
32
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
33
+ await mkdir(join(vaultRoot, "projects/test-project/learnings"), { recursive: true });
34
+ const result = await learnCommand({
35
+ action: "add",
36
+ title: "API Rate Limiting",
37
+ discovery: "Rate limits reset at midnight UTC",
38
+ project: "test-project",
39
+ }, ctx);
40
+ expect(result.learning_id).toBe("001");
41
+ expect(result.path).toBe("projects/test-project/learnings/001-api-rate-limiting.md");
42
+ const content = await vaultFs.read(result.path);
43
+ expect(content).toContain("type: learning");
44
+ expect(content).toContain("# API Rate Limiting");
45
+ expect(content).toContain("Rate limits reset at midnight UTC");
46
+ });
47
+ it("increments number for subsequent learnings", async () => {
48
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
49
+ await mkdir(join(vaultRoot, "projects/test-project/learnings"), { recursive: true });
50
+ const result1 = await learnCommand({
51
+ action: "add",
52
+ title: "First Learning",
53
+ discovery: "Discovery 1",
54
+ project: "test-project",
55
+ }, ctx);
56
+ const result2 = await learnCommand({
57
+ action: "add",
58
+ title: "Second Learning",
59
+ discovery: "Discovery 2",
60
+ project: "test-project",
61
+ }, ctx);
62
+ expect(result1.learning_id).toBe("001");
63
+ expect(result2.learning_id).toBe("002");
64
+ });
65
+ it("sets default confidence to medium", async () => {
66
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
67
+ await mkdir(join(vaultRoot, "projects/test-project/learnings"), { recursive: true });
68
+ await learnCommand({
69
+ action: "add",
70
+ title: "Test",
71
+ discovery: "Discovery",
72
+ project: "test-project",
73
+ }, ctx);
74
+ const content = await vaultFs.read("projects/test-project/learnings/001-test.md");
75
+ expect(content).toContain("confidence: medium");
76
+ });
77
+ it("accepts high confidence", async () => {
78
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
79
+ await mkdir(join(vaultRoot, "projects/test-project/learnings"), { recursive: true });
80
+ await learnCommand({
81
+ action: "add",
82
+ title: "High Confidence",
83
+ discovery: "Verified",
84
+ confidence: "high",
85
+ project: "test-project",
86
+ }, ctx);
87
+ const content = await vaultFs.read("projects/test-project/learnings/001-high-confidence.md");
88
+ expect(content).toContain("confidence: high");
89
+ });
90
+ it("accepts low confidence", async () => {
91
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
92
+ await mkdir(join(vaultRoot, "projects/test-project/learnings"), { recursive: true });
93
+ await learnCommand({
94
+ action: "add",
95
+ title: "Low Confidence",
96
+ discovery: "Unverified",
97
+ confidence: "low",
98
+ project: "test-project",
99
+ }, ctx);
100
+ const content = await vaultFs.read("projects/test-project/learnings/001-low-confidence.md");
101
+ expect(content).toContain("confidence: low");
102
+ });
103
+ it("rejects invalid confidence", async () => {
104
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
105
+ await mkdir(join(vaultRoot, "projects/test-project/learnings"), { recursive: true });
106
+ await expect(learnCommand({
107
+ action: "add",
108
+ title: "Test",
109
+ discovery: "Test",
110
+ confidence: "invalid",
111
+ project: "test-project",
112
+ }, ctx)).rejects.toThrow('Invalid confidence "invalid"');
113
+ });
114
+ it("requires title for add", async () => {
115
+ await expect(learnCommand({
116
+ action: "add",
117
+ discovery: "Test",
118
+ project: "test-project",
119
+ }, ctx)).rejects.toThrow("Title required for add");
120
+ });
121
+ it("requires discovery for add", async () => {
122
+ await expect(learnCommand({
123
+ action: "add",
124
+ title: "Test",
125
+ project: "test-project",
126
+ }, ctx)).rejects.toThrow("Discovery required for add");
127
+ });
128
+ it("stores tags in frontmatter", async () => {
129
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
130
+ await mkdir(join(vaultRoot, "projects/test-project/learnings"), { recursive: true });
131
+ await learnCommand({
132
+ action: "add",
133
+ title: "Tagged Learning",
134
+ discovery: "Content",
135
+ tags: ["api", "performance"],
136
+ project: "test-project",
137
+ }, ctx);
138
+ const content = await vaultFs.read("projects/test-project/learnings/001-tagged-learning.md");
139
+ expect(content).toContain("tags:");
140
+ expect(content).toContain("- api");
141
+ expect(content).toContain("- performance");
142
+ });
143
+ it("stores source and session_id", async () => {
144
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
145
+ await mkdir(join(vaultRoot, "projects/test-project/learnings"), { recursive: true });
146
+ await learnCommand({
147
+ action: "add",
148
+ title: "Sourced Learning",
149
+ discovery: "Content",
150
+ source: "PR #123",
151
+ sessionId: "session-abc123",
152
+ project: "test-project",
153
+ }, ctx);
154
+ const content = await vaultFs.read("projects/test-project/learnings/001-sourced-learning.md");
155
+ expect(content).toMatch(/source:\s*'?PR #123'?/);
156
+ expect(content).toContain("session_id: session-abc123");
157
+ });
158
+ });
159
+ describe("list learnings", () => {
160
+ it("returns empty array when no learnings", async () => {
161
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
162
+ await mkdir(join(vaultRoot, "projects/test-project/learnings"), { recursive: true });
163
+ const result = await learnCommand({
164
+ action: "list",
165
+ project: "test-project",
166
+ }, ctx);
167
+ expect(result.learnings).toEqual([]);
168
+ });
169
+ it("lists all learnings", async () => {
170
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
171
+ await mkdir(join(vaultRoot, "projects/test-project/learnings"), { recursive: true });
172
+ await learnCommand({
173
+ action: "add",
174
+ title: "Learning 1",
175
+ discovery: "Discovery 1",
176
+ tags: ["api"],
177
+ project: "test-project",
178
+ }, ctx);
179
+ await learnCommand({
180
+ action: "add",
181
+ title: "Learning 2",
182
+ discovery: "Discovery 2",
183
+ tags: ["database"],
184
+ confidence: "high",
185
+ project: "test-project",
186
+ }, ctx);
187
+ const result = await learnCommand({
188
+ action: "list",
189
+ project: "test-project",
190
+ }, ctx);
191
+ expect(result.learnings).toHaveLength(2);
192
+ expect(result.learnings[0].id).toBe("001");
193
+ expect(result.learnings[0].title).toBe("Learning 1");
194
+ expect(result.learnings[0].tags).toEqual(["api"]);
195
+ expect(result.learnings[1].id).toBe("002");
196
+ expect(result.learnings[1].confidence).toBe("high");
197
+ });
198
+ it("filters by tag", async () => {
199
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
200
+ await mkdir(join(vaultRoot, "projects/test-project/learnings"), { recursive: true });
201
+ await learnCommand({
202
+ action: "add",
203
+ title: "API Learning",
204
+ discovery: "Discovery",
205
+ tags: ["api"],
206
+ project: "test-project",
207
+ }, ctx);
208
+ await learnCommand({
209
+ action: "add",
210
+ title: "DB Learning",
211
+ discovery: "Discovery",
212
+ tags: ["database"],
213
+ project: "test-project",
214
+ }, ctx);
215
+ const result = await learnCommand({
216
+ action: "list",
217
+ tag: "api",
218
+ project: "test-project",
219
+ }, ctx);
220
+ expect(result.learnings).toHaveLength(1);
221
+ expect(result.learnings[0].title).toBe("API Learning");
222
+ });
223
+ it("returns empty when learnings directory does not exist", async () => {
224
+ await vaultFs.write("project-map.json", JSON.stringify({ projects: { "test-project": "/tmp/test" } }));
225
+ const result = await learnCommand({
226
+ action: "list",
227
+ project: "test-project",
228
+ }, ctx);
229
+ expect(result.learnings).toEqual([]);
230
+ });
231
+ });
232
+ describe("unknown action", () => {
233
+ it("throws error for unknown action", async () => {
234
+ await expect(learnCommand({
235
+ action: "unknown",
236
+ project: "test-project",
237
+ }, ctx)).rejects.toThrow("Unknown action: unknown");
238
+ });
239
+ });
240
+ });
241
+ //# sourceMappingURL=learn.test.js.map