@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.
- package/CHANGELOG.md +60 -0
- package/LICENSE +661 -0
- package/README.md +235 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +805 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/brainstorm.d.ts +9 -0
- package/dist/commands/brainstorm.js +37 -0
- package/dist/commands/brainstorm.js.map +1 -0
- package/dist/commands/brainstorm.test.d.ts +1 -0
- package/dist/commands/brainstorm.test.js +110 -0
- package/dist/commands/brainstorm.test.js.map +1 -0
- package/dist/commands/context.d.ts +18 -0
- package/dist/commands/context.js +88 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/context.test.d.ts +1 -0
- package/dist/commands/context.test.js +230 -0
- package/dist/commands/context.test.js.map +1 -0
- package/dist/commands/decide.d.ts +12 -0
- package/dist/commands/decide.js +40 -0
- package/dist/commands/decide.js.map +1 -0
- package/dist/commands/decide.test.d.ts +1 -0
- package/dist/commands/decide.test.js +109 -0
- package/dist/commands/decide.test.js.map +1 -0
- package/dist/commands/graph.d.ts +15 -0
- package/dist/commands/graph.js +89 -0
- package/dist/commands/graph.js.map +1 -0
- package/dist/commands/graph.test.d.ts +1 -0
- package/dist/commands/graph.test.js +215 -0
- package/dist/commands/graph.test.js.map +1 -0
- package/dist/commands/init.d.ts +11 -0
- package/dist/commands/init.js +256 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/learn.d.ts +25 -0
- package/dist/commands/learn.js +92 -0
- package/dist/commands/learn.js.map +1 -0
- package/dist/commands/learn.test.d.ts +1 -0
- package/dist/commands/learn.test.js +241 -0
- package/dist/commands/learn.test.js.map +1 -0
- package/dist/commands/prune.d.ts +58 -0
- package/dist/commands/prune.js +246 -0
- package/dist/commands/prune.js.map +1 -0
- package/dist/commands/read.d.ts +8 -0
- package/dist/commands/read.js +7 -0
- package/dist/commands/read.js.map +1 -0
- package/dist/commands/read.test.d.ts +1 -0
- package/dist/commands/read.test.js +48 -0
- package/dist/commands/read.test.js.map +1 -0
- package/dist/commands/resume.d.ts +20 -0
- package/dist/commands/resume.js +138 -0
- package/dist/commands/resume.js.map +1 -0
- package/dist/commands/resume.test.d.ts +1 -0
- package/dist/commands/resume.test.js +243 -0
- package/dist/commands/resume.test.js.map +1 -0
- package/dist/commands/search.d.ts +8 -0
- package/dist/commands/search.js +25 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/search.test.d.ts +1 -0
- package/dist/commands/search.test.js +61 -0
- package/dist/commands/search.test.js.map +1 -0
- package/dist/commands/session.d.ts +22 -0
- package/dist/commands/session.js +79 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/commands/session.test.d.ts +1 -0
- package/dist/commands/session.test.js +185 -0
- package/dist/commands/session.test.js.map +1 -0
- package/dist/commands/skill/index.d.ts +30 -0
- package/dist/commands/skill/index.js +61 -0
- package/dist/commands/skill/index.js.map +1 -0
- package/dist/commands/skill/index.test.d.ts +1 -0
- package/dist/commands/skill/index.test.js +137 -0
- package/dist/commands/skill/index.test.js.map +1 -0
- package/dist/commands/skill/install.d.ts +14 -0
- package/dist/commands/skill/install.js +107 -0
- package/dist/commands/skill/install.js.map +1 -0
- package/dist/commands/skill/install.test.d.ts +1 -0
- package/dist/commands/skill/install.test.js +225 -0
- package/dist/commands/skill/install.test.js.map +1 -0
- package/dist/commands/skill/list.d.ts +6 -0
- package/dist/commands/skill/list.js +5 -0
- package/dist/commands/skill/list.js.map +1 -0
- package/dist/commands/skill/list.test.d.ts +1 -0
- package/dist/commands/skill/list.test.js +101 -0
- package/dist/commands/skill/list.test.js.map +1 -0
- package/dist/commands/skill/schema.d.ts +27 -0
- package/dist/commands/skill/schema.js +55 -0
- package/dist/commands/skill/schema.js.map +1 -0
- package/dist/commands/skill/schema.test.d.ts +1 -0
- package/dist/commands/skill/schema.test.js +142 -0
- package/dist/commands/skill/schema.test.js.map +1 -0
- package/dist/commands/skill/validate.d.ts +10 -0
- package/dist/commands/skill/validate.js +39 -0
- package/dist/commands/skill/validate.js.map +1 -0
- package/dist/commands/skill/validate.test.d.ts +1 -0
- package/dist/commands/skill/validate.test.js +171 -0
- package/dist/commands/skill/validate.test.js.map +1 -0
- package/dist/commands/task.d.ts +34 -0
- package/dist/commands/task.js +160 -0
- package/dist/commands/task.js.map +1 -0
- package/dist/commands/task.test.d.ts +1 -0
- package/dist/commands/task.test.js +395 -0
- package/dist/commands/task.test.js.map +1 -0
- package/dist/commands/todo.d.ts +15 -0
- package/dist/commands/todo.js +99 -0
- package/dist/commands/todo.js.map +1 -0
- package/dist/commands/todo.test.d.ts +1 -0
- package/dist/commands/todo.test.js +324 -0
- package/dist/commands/todo.test.js.map +1 -0
- package/dist/commands/write.d.ts +12 -0
- package/dist/commands/write.js +40 -0
- package/dist/commands/write.js.map +1 -0
- package/dist/commands/write.test.d.ts +1 -0
- package/dist/commands/write.test.js +79 -0
- package/dist/commands/write.test.js.map +1 -0
- package/dist/config.d.ts +15 -0
- package/dist/config.js +48 -0
- package/dist/config.js.map +1 -0
- package/dist/config.test.d.ts +1 -0
- package/dist/config.test.js +147 -0
- package/dist/config.test.js.map +1 -0
- package/dist/core/executor.d.ts +11 -0
- package/dist/core/executor.js +42 -0
- package/dist/core/executor.js.map +1 -0
- package/dist/core/executor.test.d.ts +1 -0
- package/dist/core/executor.test.js +206 -0
- package/dist/core/executor.test.js.map +1 -0
- package/dist/core/middleware/error-handler.d.ts +2 -0
- package/dist/core/middleware/error-handler.js +29 -0
- package/dist/core/middleware/error-handler.js.map +1 -0
- package/dist/core/middleware/index.d.ts +2 -0
- package/dist/core/middleware/index.js +3 -0
- package/dist/core/middleware/index.js.map +1 -0
- package/dist/core/middleware/logging.d.ts +2 -0
- package/dist/core/middleware/logging.js +18 -0
- package/dist/core/middleware/logging.js.map +1 -0
- package/dist/core/registry.d.ts +12 -0
- package/dist/core/registry.js +407 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/registry.test.d.ts +1 -0
- package/dist/core/registry.test.js +162 -0
- package/dist/core/registry.test.js.map +1 -0
- package/dist/core/types.d.ts +38 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/integration/coordination.test.d.ts +1 -0
- package/dist/integration/coordination.test.js +286 -0
- package/dist/integration/coordination.test.js.map +1 -0
- package/dist/integration/project-artifacts.test.d.ts +1 -0
- package/dist/integration/project-artifacts.test.js +275 -0
- package/dist/integration/project-artifacts.test.js.map +1 -0
- package/dist/integration/registry-graph.test.d.ts +1 -0
- package/dist/integration/registry-graph.test.js +157 -0
- package/dist/integration/registry-graph.test.js.map +1 -0
- package/dist/integration/vault-lifecycle.test.d.ts +1 -0
- package/dist/integration/vault-lifecycle.test.js +183 -0
- package/dist/integration/vault-lifecycle.test.js.map +1 -0
- package/dist/lib/auto-number.d.ts +10 -0
- package/dist/lib/auto-number.js +33 -0
- package/dist/lib/auto-number.js.map +1 -0
- package/dist/lib/auto-number.test.d.ts +1 -0
- package/dist/lib/auto-number.test.js +88 -0
- package/dist/lib/auto-number.test.js.map +1 -0
- package/dist/lib/escape-regex.d.ts +4 -0
- package/dist/lib/escape-regex.js +7 -0
- package/dist/lib/escape-regex.js.map +1 -0
- package/dist/lib/escape-regex.test.d.ts +1 -0
- package/dist/lib/escape-regex.test.js +27 -0
- package/dist/lib/escape-regex.test.js.map +1 -0
- package/dist/lib/frontmatter.d.ts +34 -0
- package/dist/lib/frontmatter.js +74 -0
- package/dist/lib/frontmatter.js.map +1 -0
- package/dist/lib/frontmatter.test.d.ts +1 -0
- package/dist/lib/frontmatter.test.js +192 -0
- package/dist/lib/frontmatter.test.js.map +1 -0
- package/dist/lib/project-detector.d.ts +12 -0
- package/dist/lib/project-detector.js +123 -0
- package/dist/lib/project-detector.js.map +1 -0
- package/dist/lib/project-detector.test.d.ts +1 -0
- package/dist/lib/project-detector.test.js +117 -0
- package/dist/lib/project-detector.test.js.map +1 -0
- package/dist/lib/safe-external-path.d.ts +10 -0
- package/dist/lib/safe-external-path.js +46 -0
- package/dist/lib/safe-external-path.js.map +1 -0
- package/dist/lib/safe-external-path.test.d.ts +1 -0
- package/dist/lib/safe-external-path.test.js +99 -0
- package/dist/lib/safe-external-path.test.js.map +1 -0
- package/dist/lib/search-engine.d.ts +19 -0
- package/dist/lib/search-engine.js +157 -0
- package/dist/lib/search-engine.js.map +1 -0
- package/dist/lib/search-engine.test.d.ts +1 -0
- package/dist/lib/search-engine.test.js +120 -0
- package/dist/lib/search-engine.test.js.map +1 -0
- package/dist/lib/session-registry.d.ts +59 -0
- package/dist/lib/session-registry.js +230 -0
- package/dist/lib/session-registry.js.map +1 -0
- package/dist/lib/session-registry.test.d.ts +1 -0
- package/dist/lib/session-registry.test.js +199 -0
- package/dist/lib/session-registry.test.js.map +1 -0
- package/dist/lib/skill-registry.d.ts +13 -0
- package/dist/lib/skill-registry.js +76 -0
- package/dist/lib/skill-registry.js.map +1 -0
- package/dist/lib/token-estimator.d.ts +19 -0
- package/dist/lib/token-estimator.js +58 -0
- package/dist/lib/token-estimator.js.map +1 -0
- package/dist/lib/token-estimator.test.d.ts +1 -0
- package/dist/lib/token-estimator.test.js +65 -0
- package/dist/lib/token-estimator.test.js.map +1 -0
- package/dist/lib/vault-fs.d.ts +39 -0
- package/dist/lib/vault-fs.js +183 -0
- package/dist/lib/vault-fs.js.map +1 -0
- package/dist/lib/vault-fs.test.d.ts +1 -0
- package/dist/lib/vault-fs.test.js +210 -0
- package/dist/lib/vault-fs.test.js.map +1 -0
- package/dist/mcp-server.d.ts +2 -0
- package/dist/mcp-server.js +385 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/test-helpers.d.ts +20 -0
- package/dist/test-helpers.js +46 -0
- package/dist/test-helpers.js.map +1 -0
- package/dist/test-helpers.test.d.ts +1 -0
- package/dist/test-helpers.test.js +90 -0
- package/dist/test-helpers.test.js.map +1 -0
- 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
|