@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
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
|