@unpolarize/knowledge-planning 0.1.0 → 0.2.0

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/dist/cli.js ADDED
@@ -0,0 +1,385 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ PlanningIndex,
4
+ Store,
5
+ cmdBlocked,
6
+ cmdCalendar,
7
+ cmdCapture,
8
+ cmdCreate,
9
+ cmdDaily,
10
+ cmdDelete,
11
+ cmdDeriveProjects,
12
+ cmdDeriveSessions,
13
+ cmdDoctor,
14
+ cmdEdit,
15
+ cmdExport,
16
+ cmdGraph,
17
+ cmdIngestGdoc,
18
+ cmdIngestThoughts,
19
+ cmdInsightIngest,
20
+ cmdLabelSessions,
21
+ cmdLinkSession,
22
+ cmdList,
23
+ cmdLookback,
24
+ cmdPromote,
25
+ cmdRecategorize,
26
+ cmdReindex,
27
+ cmdResearch,
28
+ cmdSetDue,
29
+ cmdSetPriority,
30
+ cmdSetProject,
31
+ cmdSetStatus,
32
+ cmdShow,
33
+ today
34
+ } from "./chunk-6GL2IYUE.js";
35
+
36
+ // src/cli/index.ts
37
+ import { homedir } from "os";
38
+ import { join, resolve, basename, dirname } from "path";
39
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
40
+ var HELP = `KP (knowledge-planning) \u2014 durable, git-backed life-wide planning over a markdown SSOT
41
+ Prefer "KP" in product language; this CLI bin is \`kp\`; package/repo slug is knowledge-planning.
42
+
43
+ Usage: kp <command> [options]
44
+
45
+ reindex Rebuild the .index/planning.db projection
46
+ list [--type T] [--status S] [--project P] List objects (idea/plan/task/...)
47
+ daily [--date today|YYYY-MM-DD] [--carry-over] Render the daily board
48
+ board Alias for 'daily'
49
+ blocked Show objects blocked_by unresolved knowledge
50
+ graph [--json] Graph data (nodes/edges) for the CSV webview
51
+ capture "<text>" [--domain D] Quick-capture an idea (status=capture)
52
+ promote <ideaId> [--to planId] Promote an idea to a plan
53
+ link-session <objectId> <uuid> Bidirectional session link (+planning_refs)
54
+ insight <file> --source S Ingest digest/brief bullets into an Insight
55
+ ingest-gdoc [<file>] [--domain D] [--max N] [--source-url U] Diff-ingest NEW Google-Doc bullets \u2192
56
+ capture ideas/inbox tasks (baselines on first run, caps per run)
57
+ ingest-thoughts [<file>] [--max N] [--source S] [--source-url U] [--all] Diff-ingest NEW prose
58
+ paragraphs \u2192 thoughts/ (status new; --all backfills the whole doc)
59
+ set-status <objectId> <status> [--note N] Change status; --note appends a '## Resolution' section
60
+ set-due <objectId> <date|-> Set/clear a task's due date (due==today \u21D2 must-have)
61
+ set-priority <objectId> <p0-p3|-> Set/clear priority (p0 highest)
62
+ set-project <objectId> <project|-> Tag with KP project (projects/slug, CS/CSV/CB/KP, or '-' clear)
63
+ create "<title>" [--type T --status S --domain D --project P --priority P --due DATE]
64
+ calendar [--from D] [--to D] Tasks by due date (default: today \u2192 +14d)
65
+ serve [--port 4622] Standalone web dashboard: board \xB7 calendar (month/week/day) \xB7 topics \xB7 lookback
66
+ lookback [<date>|today] What was I up to that day: sessions + tasks touched + plan
67
+ derive-sessions Re-analyze ALL captured sessions \u2192 advisory insight (topics/tasks/ideas)
68
+ derive-projects [--dry-run] [--force] Heuristic project labels on tasks/ideas/thoughts/plans
69
+ label-sessions [--dry-run] [--force] Tag session envelopes with project:<slug> from cwd
70
+ export [--date today|DATE] One-shot JSON snapshot (for the CSV planning mode)
71
+ show <objectId> Rich JSON detail (body + resolved refs + children) for the dashboard
72
+ doctor Config, counts, fts, blocked, warnings
73
+
74
+ Options:
75
+ --root <path> Store root (default: package root or $KP_ROOT)
76
+ --help This help
77
+ `;
78
+ function addDays(date, n) {
79
+ const d = /* @__PURE__ */ new Date(`${date}T00:00:00Z`);
80
+ d.setUTCDate(d.getUTCDate() + n);
81
+ return d.toISOString().slice(0, 10);
82
+ }
83
+ function getFlag(argv, name) {
84
+ const i = argv.indexOf(name);
85
+ if (i >= 0 && i + 1 < argv.length && !argv[i + 1].startsWith("--")) return argv[i + 1];
86
+ const eq = argv.find((a) => a.startsWith(`${name}=`));
87
+ return eq ? eq.slice(name.length + 1) : void 0;
88
+ }
89
+ var hasFlag = (argv, name) => argv.includes(name);
90
+ function defaultStoreRoot() {
91
+ return process.env.KP_ROOT ?? join(homedir(), ".knowledge-planning");
92
+ }
93
+ function resolveRoot(argv) {
94
+ const flag = getFlag(argv, "--root");
95
+ if (flag) return resolve(flag);
96
+ if (process.env.KP_ROOT) return resolve(process.env.KP_ROOT);
97
+ return defaultStoreRoot();
98
+ }
99
+ function positionals(argv) {
100
+ const out = [];
101
+ for (let i = 0; i < argv.length; i++) {
102
+ const a = argv[i];
103
+ if (a.startsWith("--")) {
104
+ if (i + 1 < argv.length && !argv[i + 1].startsWith("--") && !a.includes("=")) i++;
105
+ continue;
106
+ }
107
+ out.push(a);
108
+ }
109
+ return out;
110
+ }
111
+ function main() {
112
+ const argv = process.argv.slice(2);
113
+ const command = argv[0];
114
+ if (!command || command === "help" || hasFlag(argv, "--help")) {
115
+ console.log(HELP);
116
+ return;
117
+ }
118
+ const root = resolveRoot(argv);
119
+ const store = new Store(root);
120
+ const config = store.config();
121
+ const rest = positionals(argv).slice(1);
122
+ const idx = () => new PlanningIndex(store);
123
+ switch (command) {
124
+ case "reindex":
125
+ console.log(cmdReindex(store, root));
126
+ break;
127
+ case "list":
128
+ console.log(
129
+ cmdList(store, {
130
+ type: getFlag(argv, "--type"),
131
+ status: getFlag(argv, "--status"),
132
+ project: getFlag(argv, "--project")
133
+ })
134
+ );
135
+ break;
136
+ case "daily":
137
+ case "board": {
138
+ const date = getFlag(argv, "--date");
139
+ console.log(cmdDaily(store, idx(), !date || date === "today" ? today() : date, hasFlag(argv, "--carry-over")));
140
+ break;
141
+ }
142
+ case "blocked":
143
+ console.log(cmdBlocked(idx(), config.kb_root));
144
+ break;
145
+ case "graph":
146
+ console.log(cmdGraph(store, config.kb_root, hasFlag(argv, "--json")));
147
+ break;
148
+ case "capture": {
149
+ if (!rest[0]) throw new Error('usage: kp capture "<text>" [--domain D]');
150
+ const res = cmdCapture(store, rest[0], getFlag(argv, "--domain"));
151
+ console.log(`captured ${res.id}`);
152
+ break;
153
+ }
154
+ case "promote": {
155
+ if (!rest[0]) throw new Error("usage: kp promote <ideaId> [--to planId]");
156
+ const res = cmdPromote(store, rest[0], getFlag(argv, "--to"));
157
+ console.log(`promoted ${res.ideaId} \u2192 ${res.planId}`);
158
+ break;
159
+ }
160
+ case "link-session": {
161
+ if (!rest[0] || !rest[1]) throw new Error("usage: kp link-session <objectId> <session-uuid>");
162
+ const res = cmdLinkSession(store, config.sessions_root, rest[0], rest[1]);
163
+ console.log(
164
+ `linked ${res.objectId} \u2194 session ${res.uuid}
165
+ envelope: ${res.envelopePath ? `${res.envelopeUpdated ? "updated" : "already linked"} (${res.envelopePath})` : "not found in " + config.sessions_root}`
166
+ );
167
+ break;
168
+ }
169
+ case "insight": {
170
+ if (!rest[0]) throw new Error("usage: kp insight <file> --source S [--ref R]");
171
+ const text = readFileSync(resolve(rest[0]), "utf8");
172
+ const res = cmdInsightIngest(store, text, getFlag(argv, "--source") ?? "manual", getFlag(argv, "--ref"));
173
+ console.log(`created ${res.id} with ${res.suggested} suggested item(s)`);
174
+ break;
175
+ }
176
+ case "export": {
177
+ const date = getFlag(argv, "--date");
178
+ console.log(JSON.stringify(cmdExport(store, idx(), config, !date || date === "today" ? today() : date)));
179
+ break;
180
+ }
181
+ case "show": {
182
+ if (!rest[0]) throw new Error("usage: kp show <objectId>");
183
+ console.log(JSON.stringify(cmdShow(store, config.kb_root, rest[0])));
184
+ break;
185
+ }
186
+ case "create": {
187
+ if (!rest[0]) throw new Error('usage: kp create "<title>" [--type task|idea|plan|thought] [--status S] [--domain D] [--project P] [--priority p0-p3] [--due YYYY-MM-DD]');
188
+ const r = cmdCreate(store, {
189
+ type: getFlag(argv, "--type"),
190
+ title: rest[0],
191
+ status: getFlag(argv, "--status"),
192
+ domain: getFlag(argv, "--domain"),
193
+ project: getFlag(argv, "--project"),
194
+ priority: getFlag(argv, "--priority"),
195
+ due: getFlag(argv, "--due")
196
+ });
197
+ console.log(`created ${r.id}`);
198
+ break;
199
+ }
200
+ case "edit": {
201
+ if (!rest[0]) throw new Error("usage: kp edit <id> [--title T] [--status S] [--domain D] [--lane L] [--project P] [--body -]");
202
+ const bodyFlag = getFlag(argv, "--body");
203
+ cmdEdit(store, rest[0], {
204
+ title: getFlag(argv, "--title"),
205
+ status: getFlag(argv, "--status"),
206
+ domain: getFlag(argv, "--domain"),
207
+ lane: getFlag(argv, "--lane"),
208
+ project: getFlag(argv, "--project"),
209
+ body: bodyFlag === "-" ? readFileSync(0, "utf8") : bodyFlag
210
+ });
211
+ console.log(`edited ${rest[0]}`);
212
+ break;
213
+ }
214
+ case "recategorize": {
215
+ if (!rest[0]) throw new Error("usage: kp recategorize <id> [--to-type task|idea|plan] [--domain D] [--lane L]");
216
+ const res = cmdRecategorize(store, rest[0], {
217
+ toType: getFlag(argv, "--to-type"),
218
+ domain: getFlag(argv, "--domain"),
219
+ lane: getFlag(argv, "--lane")
220
+ });
221
+ console.log(`recategorized ${rest[0]} \u2192 ${res.newId}`);
222
+ break;
223
+ }
224
+ case "delete": {
225
+ if (!rest[0]) throw new Error("usage: kp delete <id>");
226
+ cmdDelete(store, rest[0]);
227
+ console.log(`deleted ${rest[0]}`);
228
+ break;
229
+ }
230
+ case "research": {
231
+ if (!rest[0]) throw new Error("usage: kp research <id>");
232
+ console.log(cmdResearch(store, config.kb_root, rest[0]));
233
+ break;
234
+ }
235
+ case "ingest-gdoc": {
236
+ const src = rest[0] || getFlag(argv, "--file") || process.env.KP_GDOC_MIRROR;
237
+ if (!src) throw new Error("ingest needs a source file: kp <cmd> <file> (or $KP_GDOC_MIRROR)");
238
+ const file = resolve(src);
239
+ const text = readFileSync(file, "utf8");
240
+ const snapPath = join(root, ".ingest-state", `${basename(file)}.snapshot`);
241
+ const prevText = existsSync(snapPath) ? readFileSync(snapPath, "utf8") : null;
242
+ const max = getFlag(argv, "--max");
243
+ const res = cmdIngestGdoc(store, text, {
244
+ domain: getFlag(argv, "--domain"),
245
+ sourceUrl: getFlag(argv, "--source-url"),
246
+ prevText,
247
+ max: max ? Number(max) : void 0
248
+ });
249
+ mkdirSync(dirname(snapPath), { recursive: true });
250
+ writeFileSync(snapPath, text, "utf8");
251
+ if (res.baselined) {
252
+ console.log(`baselined ${file} (first run \u2014 no ingest). New bullets will be ingested on the next change.`);
253
+ } else {
254
+ console.log(
255
+ `ingested ${file}
256
+ ${res.newBullets} new bullet(s) \u2192 created ${res.created.length}, skipped ${res.skipped}` + (res.capped ? `, capped ${res.capped} (raise with --max)` : "")
257
+ );
258
+ if (res.created.length) console.log(" " + res.created.join("\n "));
259
+ }
260
+ break;
261
+ }
262
+ case "ingest-thoughts": {
263
+ const src = rest[0] || getFlag(argv, "--file") || process.env.KP_GDOC_MIRROR;
264
+ if (!src) throw new Error("ingest needs a source file: kp <cmd> <file> (or $KP_GDOC_MIRROR)");
265
+ const file = resolve(src);
266
+ const text = readFileSync(file, "utf8");
267
+ const snapPath = join(root, ".ingest-state", `${basename(file)}.thoughts.snapshot`);
268
+ const backfill = argv.includes("--all");
269
+ const prevText = backfill ? "" : existsSync(snapPath) ? readFileSync(snapPath, "utf8") : null;
270
+ const max = getFlag(argv, "--max");
271
+ const res = cmdIngestThoughts(store, text, {
272
+ source: getFlag(argv, "--source"),
273
+ sourceUrl: getFlag(argv, "--source-url"),
274
+ prevText,
275
+ max: max ? Number(max) : void 0
276
+ });
277
+ mkdirSync(dirname(snapPath), { recursive: true });
278
+ writeFileSync(snapPath, text, "utf8");
279
+ if (res.baselined) {
280
+ console.log(`baselined ${file} (first run \u2014 no thoughts ingested). Use --all to backfill.`);
281
+ } else {
282
+ console.log(
283
+ `ingested thoughts from ${file}
284
+ ${res.newBlocks} new block(s) \u2192 created ${res.created.length}, skipped ${res.skipped}` + (res.capped ? `, capped ${res.capped} (raise with --max)` : "")
285
+ );
286
+ if (res.created.length) console.log(" " + res.created.join("\n "));
287
+ }
288
+ break;
289
+ }
290
+ case "set-status": {
291
+ if (!rest[0] || !rest[1]) throw new Error('usage: kp set-status <objectId> <status> [--note "resolution"]');
292
+ cmdSetStatus(store, rest[0], rest[1], getFlag(argv, "--note"));
293
+ console.log(`set ${rest[0]} \u2192 ${rest[1]}`);
294
+ break;
295
+ }
296
+ case "set-due": {
297
+ if (!rest[0] || !rest[1]) throw new Error("usage: kp set-due <objectId> <YYYY-MM-DD|->");
298
+ cmdSetDue(store, rest[0], rest[1]);
299
+ console.log(`set ${rest[0]} due \u2192 ${rest[1]}`);
300
+ break;
301
+ }
302
+ case "set-priority": {
303
+ if (!rest[0] || !rest[1]) throw new Error("usage: kp set-priority <objectId> <p0..p3|->");
304
+ cmdSetPriority(store, rest[0], rest[1]);
305
+ console.log(`set ${rest[0]} priority \u2192 ${rest[1]}`);
306
+ break;
307
+ }
308
+ case "set-project": {
309
+ if (!rest[0] || !rest[1]) throw new Error("usage: kp set-project <objectId> <projects/slug|alias|->");
310
+ cmdSetProject(store, rest[0], rest[1]);
311
+ console.log(`set ${rest[0]} project \u2192 ${rest[1]}`);
312
+ break;
313
+ }
314
+ case "calendar": {
315
+ const from = getFlag(argv, "--from") ?? today();
316
+ const toFlag = getFlag(argv, "--to");
317
+ const to = toFlag ?? addDays(from, 14);
318
+ console.log(cmdCalendar(idx(), getFlag(argv, "--all") ? "0000-01-01" : from, to));
319
+ break;
320
+ }
321
+ case "lookback": {
322
+ const date = getFlag(argv, "--date") ?? rest[0] ?? today();
323
+ console.log(cmdLookback(store, date === "today" ? today() : date));
324
+ break;
325
+ }
326
+ case "derive-sessions": {
327
+ const res = cmdDeriveSessions(store, config);
328
+ console.log(`analyzed ${res.sessions} sessions \u2192 ${res.clusters} project clusters \u2192 ${res.id}`);
329
+ break;
330
+ }
331
+ case "derive-projects": {
332
+ const res = cmdDeriveProjects(store, {
333
+ dryRun: hasFlag(argv, "--dry-run"),
334
+ force: hasFlag(argv, "--force")
335
+ });
336
+ console.log(
337
+ `derive-projects${hasFlag(argv, "--dry-run") ? " (dry-run)" : ""}: examined ${res.examined}, labeled ${res.labeled}, already-set ${res.skippedHasProject}, no-match ${res.skippedNoMatch}`
338
+ );
339
+ for (const c of res.changes) {
340
+ console.log(` ${c.id} \u2192 ${c.project}${c.previous ? ` (was ${c.previous})` : ""}`);
341
+ }
342
+ break;
343
+ }
344
+ case "label-sessions": {
345
+ const res = cmdLabelSessions(store, {
346
+ dryRun: hasFlag(argv, "--dry-run"),
347
+ force: hasFlag(argv, "--force"),
348
+ sessionsRoot: config.sessions_root
349
+ });
350
+ console.log(
351
+ `label-sessions${hasFlag(argv, "--dry-run") ? " (dry-run)" : ""}: examined ${res.examined}, labeled ${res.labeled}, skipped ${res.skipped}`
352
+ );
353
+ for (const c of res.changes.slice(0, 15)) {
354
+ console.log(` ${c.uuid.slice(0, 8)}\u2026 \u2192 ${c.label}`);
355
+ }
356
+ if (res.changes.length > 15) console.log(` \u2026 +${res.changes.length - 15} more`);
357
+ break;
358
+ }
359
+ case "serve": {
360
+ const port = Number(getFlag(argv, "--port") ?? 4622);
361
+ void import("./server-NXAKSD5Z.js").then(async ({ serve }) => {
362
+ const { port: actual } = await serve({ root, config, port });
363
+ console.log(`kp dashboard: http://127.0.0.1:${actual} (root: ${root}; Ctrl-C to stop)`);
364
+ });
365
+ return;
366
+ }
367
+ case "doctor":
368
+ console.log(cmdDoctor(store, idx(), config));
369
+ break;
370
+ default:
371
+ console.error(`unknown command: ${command}
372
+ `);
373
+ console.log(HELP);
374
+ process.exitCode = 1;
375
+ }
376
+ }
377
+ try {
378
+ main();
379
+ } catch (err) {
380
+ console.error(`error: ${err.message}`);
381
+ process.exitCode = 1;
382
+ }
383
+ export {
384
+ defaultStoreRoot
385
+ };