pi-para 0.5.2 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/ARCHITECTURE.md +160 -0
  2. package/CHANGELOG.md +29 -0
  3. package/CONFIGURATION.md +243 -0
  4. package/README.md +140 -237
  5. package/SETUP.md +196 -112
  6. package/STRUCTURE.md +140 -0
  7. package/dist/atomic-write.d.ts +2 -0
  8. package/dist/atomic-write.d.ts.map +1 -0
  9. package/dist/atomic-write.js +18 -0
  10. package/dist/atomic-write.js.map +1 -0
  11. package/dist/capture.d.ts +4 -4
  12. package/dist/capture.d.ts.map +1 -1
  13. package/dist/capture.js +1 -1
  14. package/dist/capture.js.map +1 -1
  15. package/dist/cli-legacy.d.ts +4 -0
  16. package/dist/cli-legacy.d.ts.map +1 -0
  17. package/dist/cli-legacy.js +41 -0
  18. package/dist/cli-legacy.js.map +1 -0
  19. package/dist/cli.d.ts +2 -9
  20. package/dist/cli.d.ts.map +1 -1
  21. package/dist/cli.js +135 -29
  22. package/dist/cli.js.map +1 -1
  23. package/dist/commands.d.ts +3 -3
  24. package/dist/commands.d.ts.map +1 -1
  25. package/dist/commands.js +189 -256
  26. package/dist/commands.js.map +1 -1
  27. package/dist/config.d.ts +104 -0
  28. package/dist/config.d.ts.map +1 -0
  29. package/dist/config.js +242 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/context.d.ts +4 -2
  32. package/dist/context.d.ts.map +1 -1
  33. package/dist/context.js +29 -7
  34. package/dist/context.js.map +1 -1
  35. package/dist/credentials.d.ts +27 -0
  36. package/dist/credentials.d.ts.map +1 -0
  37. package/dist/credentials.js +85 -0
  38. package/dist/credentials.js.map +1 -0
  39. package/dist/daemon.d.ts +1 -1
  40. package/dist/daemon.d.ts.map +1 -1
  41. package/dist/doctor.d.ts +20 -0
  42. package/dist/doctor.d.ts.map +1 -0
  43. package/dist/doctor.js +171 -0
  44. package/dist/doctor.js.map +1 -0
  45. package/dist/gepa/index.js +1 -1
  46. package/dist/gepa/index.js.map +1 -1
  47. package/dist/index.d.ts +3 -3
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +102 -37
  50. package/dist/index.js.map +1 -1
  51. package/dist/maintainer.d.ts +2 -2
  52. package/dist/maintainer.d.ts.map +1 -1
  53. package/dist/maintainer.js +2 -2
  54. package/dist/maintainer.js.map +1 -1
  55. package/dist/model-resolver.d.ts +30 -0
  56. package/dist/model-resolver.d.ts.map +1 -0
  57. package/dist/model-resolver.js +71 -0
  58. package/dist/model-resolver.js.map +1 -0
  59. package/dist/paths.d.ts +20 -0
  60. package/dist/paths.d.ts.map +1 -0
  61. package/dist/paths.js +33 -0
  62. package/dist/paths.js.map +1 -0
  63. package/dist/processor.d.ts +2 -2
  64. package/dist/processor.d.ts.map +1 -1
  65. package/dist/processor.js +2 -2
  66. package/dist/processor.js.map +1 -1
  67. package/dist/qmd-providers.d.ts +12 -0
  68. package/dist/qmd-providers.d.ts.map +1 -0
  69. package/dist/qmd-providers.js +45 -0
  70. package/dist/qmd-providers.js.map +1 -0
  71. package/dist/query.d.ts +1 -1
  72. package/dist/query.d.ts.map +1 -1
  73. package/dist/query.js +14 -1
  74. package/dist/query.js.map +1 -1
  75. package/dist/repair.d.ts +11 -0
  76. package/dist/repair.d.ts.map +1 -0
  77. package/dist/repair.js +41 -0
  78. package/dist/repair.js.map +1 -0
  79. package/dist/scheduler/controls.d.ts +35 -0
  80. package/dist/scheduler/controls.d.ts.map +1 -0
  81. package/dist/scheduler/controls.js +98 -0
  82. package/dist/scheduler/controls.js.map +1 -0
  83. package/dist/scheduler/index.d.ts +48 -0
  84. package/dist/scheduler/index.d.ts.map +1 -0
  85. package/dist/scheduler/index.js +193 -0
  86. package/dist/scheduler/index.js.map +1 -0
  87. package/dist/scheduler/leases.d.ts +22 -0
  88. package/dist/scheduler/leases.d.ts.map +1 -0
  89. package/dist/scheduler/leases.js +74 -0
  90. package/dist/scheduler/leases.js.map +1 -0
  91. package/dist/scheduler/session-capture.d.ts +33 -0
  92. package/dist/scheduler/session-capture.d.ts.map +1 -0
  93. package/dist/scheduler/session-capture.js +108 -0
  94. package/dist/scheduler/session-capture.js.map +1 -0
  95. package/dist/scheduler/state.d.ts +56 -0
  96. package/dist/scheduler/state.d.ts.map +1 -0
  97. package/dist/scheduler/state.js +210 -0
  98. package/dist/scheduler/state.js.map +1 -0
  99. package/dist/session-tools.d.ts +1 -1
  100. package/dist/session-tools.d.ts.map +1 -1
  101. package/dist/settings.d.ts +19 -0
  102. package/dist/settings.d.ts.map +1 -0
  103. package/dist/settings.js +106 -0
  104. package/dist/settings.js.map +1 -0
  105. package/dist/setup.d.ts +29 -0
  106. package/dist/setup.d.ts.map +1 -0
  107. package/dist/setup.js +90 -0
  108. package/dist/setup.js.map +1 -0
  109. package/dist/status.d.ts +22 -0
  110. package/dist/status.d.ts.map +1 -0
  111. package/dist/status.js +93 -0
  112. package/dist/status.js.map +1 -0
  113. package/dist/store.d.ts +23 -8
  114. package/dist/store.d.ts.map +1 -1
  115. package/dist/store.js +89 -34
  116. package/dist/store.js.map +1 -1
  117. package/dist/summarize.d.ts +6 -2
  118. package/dist/summarize.d.ts.map +1 -1
  119. package/dist/summarize.js +60 -25
  120. package/dist/summarize.js.map +1 -1
  121. package/dist/templates/prompts.d.ts +1 -1
  122. package/dist/templates/prompts.d.ts.map +1 -1
  123. package/dist/templates/prompts.js +1 -1
  124. package/dist/tools.d.ts +5 -5
  125. package/dist/tools.d.ts.map +1 -1
  126. package/dist/tools.js +243 -78
  127. package/dist/tools.js.map +1 -1
  128. package/dist/wiki-tool-guidance.d.ts +6 -0
  129. package/dist/wiki-tool-guidance.d.ts.map +1 -0
  130. package/dist/wiki-tool-guidance.js +70 -0
  131. package/dist/wiki-tool-guidance.js.map +1 -0
  132. package/dist/wiki.d.ts +6 -1
  133. package/dist/wiki.d.ts.map +1 -1
  134. package/dist/wiki.js +88 -19
  135. package/dist/wiki.js.map +1 -1
  136. package/package.json +80 -15
  137. package/scripts/install.ps1 +44 -0
  138. package/scripts/install.sh +62 -0
  139. package/scripts/smoke-install.mjs +61 -0
  140. package/skills/para/SKILL.md +1 -1
  141. package/skills/setup/SKILL.md +1 -1
  142. package/src/atomic-write.ts +17 -0
  143. package/src/capture.ts +4 -4
  144. package/src/cli-legacy.ts +43 -0
  145. package/src/cli.ts +133 -28
  146. package/src/commands.ts +203 -243
  147. package/src/config.ts +355 -0
  148. package/src/context.ts +29 -14
  149. package/src/credentials.ts +100 -0
  150. package/src/daemon.ts +1 -1
  151. package/src/doctor.ts +204 -0
  152. package/src/gepa/index.ts +1 -1
  153. package/src/index.ts +119 -40
  154. package/src/maintainer.ts +4 -4
  155. package/src/model-resolver.ts +96 -0
  156. package/src/paths.ts +48 -0
  157. package/src/processor.ts +4 -4
  158. package/src/qmd-providers.ts +63 -0
  159. package/src/query.ts +15 -1
  160. package/src/repair.ts +49 -0
  161. package/src/scheduler/controls.ts +125 -0
  162. package/src/scheduler/index.ts +210 -0
  163. package/src/scheduler/leases.ts +98 -0
  164. package/src/scheduler/session-capture.ts +146 -0
  165. package/src/scheduler/state.ts +304 -0
  166. package/src/session-tools.ts +1 -1
  167. package/src/settings.ts +126 -0
  168. package/src/setup.ts +116 -0
  169. package/src/status.ts +115 -0
  170. package/src/store.ts +116 -31
  171. package/src/summarize.ts +72 -16
  172. package/src/templates/prompts.ts +1 -1
  173. package/src/tools.ts +280 -90
  174. package/src/wiki-tool-guidance.ts +84 -0
  175. package/src/wiki.ts +98 -26
@@ -0,0 +1,84 @@
1
+ export type WikiToolName =
2
+ | "wiki_ingest"
3
+ | "wiki_query"
4
+ | "wiki_edit"
5
+ | "wiki_write"
6
+ | "wiki_read"
7
+ | "wiki_move"
8
+ | "wiki_lint"
9
+ | "wiki_migrate"
10
+ | "wiki_summarize";
11
+
12
+ export const WIKI_TOOL_DESCRIPTIONS: Record<WikiToolName, string> = {
13
+ wiki_ingest: "Ingest a URL, file path, or raw text into the PARA wiki and save a raw source copy.",
14
+ wiki_query: "Search the PARA wiki with scope/category filters and freshness metadata.",
15
+ wiki_edit: "Atomically edit an existing wiki page with exact oldText→newText replacements.",
16
+ wiki_write: "Create new pages, append sections, or intentionally replace complete wiki pages.",
17
+ wiki_read: "Read index.md or a wiki page by path/title, including frontmatter and freshness metadata.",
18
+ wiki_move: "Move a wiki page between PARA categories and update frontmatter.",
19
+ wiki_lint: "Run wiki health checks and optionally auto-fix safe issues.",
20
+ wiki_migrate: "Batch-migrate all wiki pages to the current schema version.",
21
+ wiki_summarize: "Collect page/category/wiki content for an LLM-written summary.",
22
+ };
23
+
24
+ export const WIKI_TOOL_SNIPPETS: Record<WikiToolName, string> = {
25
+ wiki_ingest: "Ingest a source into the PARA wiki.",
26
+ wiki_query: "Search the PARA wiki for relevant knowledge.",
27
+ wiki_edit: "Surgically edit an existing wiki page; every oldText must match exactly once.",
28
+ wiki_write: "Create/append/replace PARA wiki pages; prefer wiki_edit for surgical updates.",
29
+ wiki_read: "Read index.md or a wiki page and check freshness for page results.",
30
+ wiki_move: "Move a wiki page between PARA categories.",
31
+ wiki_lint: "Run wiki health checks and safe autofixes.",
32
+ wiki_migrate: "Batch-migrate wiki pages to the current schema.",
33
+ wiki_summarize: "Summarize wiki pages, categories, or the entire wiki.",
34
+ };
35
+
36
+ const TOOL_SPECIFIC_GUIDELINES: Record<WikiToolName, readonly string[]> = {
37
+ wiki_ingest: [
38
+ "Use wiki_ingest when the user provides a URL, file path, or text to add to the knowledge base.",
39
+ "After ingesting, synthesize durable knowledge with wiki_write/wiki_edit when the source contains reusable facts.",
40
+ ],
41
+ wiki_query: [
42
+ "Use wiki_query before answering questions that may depend on past architecture decisions, debugging history, conventions, or domain knowledge.",
43
+ "Use global=true only when the answer should search across project scopes.",
44
+ ],
45
+ wiki_edit: [
46
+ "Use wiki_edit after wiki_read when fixing stale, incorrect, or incomplete content.",
47
+ "Keep oldText as small as possible while still unique in the page body.",
48
+ ],
49
+ wiki_write: [
50
+ "Use wiki_write for new pages, appending new sections, or intentional whole-page replacement.",
51
+ "mode=create is safe: if a page exists it is skipped, not overwritten.",
52
+ "Use mode=replace only when deliberately rewriting the entire page.",
53
+ ],
54
+ wiki_read: [
55
+ "When wiki_read returns AGING/STALE/VERY STALE, verify code/config/API claims against source before trusting them.",
56
+ ],
57
+ wiki_move: [
58
+ "Use wiki_move when a page's PARA lifecycle changes, such as archiving a completed project.",
59
+ ],
60
+ wiki_lint: [
61
+ "Use wiki_lint to check orphan pages, broken links, stale pages, scope drift, frontmatter, index drift, and duplicate slugs.",
62
+ ],
63
+ wiki_migrate: [
64
+ "Use wiki_migrate when schemaVersion changes or doctor/lint reports stale page schema.",
65
+ ],
66
+ wiki_summarize: [
67
+ "Use wiki_summarize to gather source material for a synthesized summary; write a durable page only if the summary is worth preserving.",
68
+ ],
69
+ };
70
+
71
+ export function getWikiToolGuidelines(name: WikiToolName): string[] {
72
+ return [...TOOL_SPECIFIC_GUIDELINES[name]];
73
+ }
74
+
75
+ export function buildWikiToolGuidanceSection(): string {
76
+ return [
77
+ "## pi-para Wiki Tool Guidance",
78
+ "",
79
+ "- Search with wiki_query when past architecture, debugging, conventions, domain facts, or prior discussion may matter.",
80
+ "- Treat AGING/STALE/VERY STALE code/config/API claims as untrusted until verified against source; fix wrong pages with wiki_edit.",
81
+ "- Persist major decisions/debugging/conventions without being asked. Use wiki_edit for surgical fixes; wiki_write create/append for new/additive knowledge; replace only for deliberate full rewrites.",
82
+ "- Prefer resources/, kebab-case scope/tags, [[wikilinks]], and no secrets.",
83
+ ].join("\n");
84
+ }
package/src/wiki.ts CHANGED
@@ -53,7 +53,7 @@ export interface PageRef {
53
53
 
54
54
  export interface LogEntry {
55
55
  date: string;
56
- operation: "ingest" | "query" | "lint" | "capture" | "move" | "archive";
56
+ operation: "ingest" | "query" | "lint" | "capture" | "move" | "archive" | "edit";
57
57
  summary: string;
58
58
  pages: string[];
59
59
  }
@@ -176,6 +176,17 @@ const DEFAULT_LOG = "# Activity Log\n";
176
176
 
177
177
  const DEFAULT_SESSIONS = "# Session Digests\n";
178
178
 
179
+ const DEFAULT_GITIGNORE = `# pi-para generated state (do not version)
180
+ .qmd.sqlite*
181
+ .daemon.sqlite*
182
+ .pi-para.sqlite*
183
+ gepa/input/
184
+ gepa/output/
185
+
186
+ # local caches/logs
187
+ *.tmp
188
+ `;
189
+
179
190
  // -- Helpers -----------------------------------------------------------------
180
191
 
181
192
  async function fileExists(path: string): Promise<boolean> {
@@ -216,6 +227,7 @@ export async function initWiki(wikiDir: string): Promise<void> {
216
227
  await seedFile(join(wikiDir, "index.md"), DEFAULT_INDEX);
217
228
  await seedFile(join(wikiDir, "log.md"), DEFAULT_LOG);
218
229
  await seedFile(join(wikiDir, "sessions.md"), DEFAULT_SESSIONS);
230
+ await seedFile(join(wikiDir, ".gitignore"), DEFAULT_GITIGNORE);
219
231
  }
220
232
 
221
233
  /**
@@ -339,19 +351,22 @@ export async function listPages(
339
351
  continue; // directory doesn't exist yet
340
352
  }
341
353
 
342
- for (const entry of entries) {
343
- if (!entry.endsWith(".md")) continue;
344
- const slug = entry.slice(0, -3);
345
- const filePath = join(dirPath, entry);
346
- const content = await readFile(filePath, "utf-8");
347
- const { frontmatter } = parseFrontmatter(content);
348
- refs.push({
349
- category: cat,
350
- slug,
351
- title: frontmatter.title,
352
- path: `${cat}/${entry}`,
353
- });
354
- }
354
+ const catRefs = await Promise.all(
355
+ entries
356
+ .filter((entry) => entry.endsWith(".md"))
357
+ .map(async (entry): Promise<PageRef> => {
358
+ const slug = entry.slice(0, -3);
359
+ const content = await readFile(join(dirPath, entry), "utf-8");
360
+ const { frontmatter } = parseFrontmatter(content);
361
+ return {
362
+ category: cat,
363
+ slug,
364
+ title: frontmatter.title,
365
+ path: `${cat}/${entry}`,
366
+ };
367
+ }),
368
+ );
369
+ refs.push(...catRefs);
355
370
  }
356
371
 
357
372
  // Sort by category order, then slug alphabetically
@@ -366,6 +381,26 @@ export async function listPages(
366
381
  return refs;
367
382
  }
368
383
 
384
+ /**
385
+ * List page slugs across all PARA categories without reading file contents.
386
+ * Much cheaper than listPages() when only slugs are needed (e.g. auto-linking).
387
+ */
388
+ export async function listPageSlugs(wikiDir: string): Promise<Set<string>> {
389
+ const slugs = new Set<string>();
390
+ for (const cat of PARA_CATEGORIES) {
391
+ let entries: string[];
392
+ try {
393
+ entries = await readdir(join(wikiDir, cat));
394
+ } catch {
395
+ continue;
396
+ }
397
+ for (const entry of entries) {
398
+ if (entry.endsWith(".md")) slugs.add(entry.slice(0, -3));
399
+ }
400
+ }
401
+ return slugs;
402
+ }
403
+
369
404
  /**
370
405
  * Read the wiki index (index.md).
371
406
  */
@@ -427,8 +462,6 @@ function git(wikiDir: string, args: string[]): Promise<string | null> {
427
462
  * each category, and writes a standardized index.md with one-line summaries.
428
463
  */
429
464
  export async function rebuildIndex(wikiDir: string): Promise<void> {
430
- const allRefs = await listPages(wikiDir);
431
-
432
465
  const sections: Record<ParaCategory, string[]> = {
433
466
  projects: [],
434
467
  areas: [],
@@ -436,14 +469,36 @@ export async function rebuildIndex(wikiDir: string): Promise<void> {
436
469
  archives: [],
437
470
  };
438
471
 
439
- for (const ref of allRefs) {
440
- const page = await readPage(wikiDir, ref.category, ref.slug);
441
- const title = page?.frontmatter.title ?? ref.title;
442
- const summary = page ? extractFirstParagraphFromBody(page.body) : "";
443
- const desc = summary && summary !== "(no summary)"
444
- ? ": " + (summary.length > 120 ? summary.slice(0, 117) + "..." : summary)
445
- : "";
446
- sections[ref.category].push(`- [[${ref.slug}]] \u2014 ${title}${desc}`);
472
+ // Single pass: read each page file exactly once, in parallel per category.
473
+ // (Previously this called listPages() and then readPage() per page, reading
474
+ // every file twice sequentially — the dominant cost of wiki_write at scale.)
475
+ for (const cat of PARA_CATEGORIES) {
476
+ const dirPath = join(wikiDir, cat);
477
+ let entries: string[];
478
+ try {
479
+ entries = await readdir(dirPath);
480
+ } catch {
481
+ continue; // directory doesn't exist yet
482
+ }
483
+
484
+ const lines = await Promise.all(
485
+ entries
486
+ .filter((entry) => entry.endsWith(".md"))
487
+ .map((entry) => entry.slice(0, -3))
488
+ .sort((a, b) => a.localeCompare(b))
489
+ .map(async (slug) => {
490
+ const entry = `${slug}.md`;
491
+ const content = await readFile(join(dirPath, entry), "utf-8");
492
+ const { frontmatter, body } = parseFrontmatter(content);
493
+ const title = frontmatter.title;
494
+ const summary = extractFirstParagraphFromBody(body);
495
+ const desc = summary && summary !== "(no summary)"
496
+ ? ": " + (summary.length > 120 ? summary.slice(0, 117) + "..." : summary)
497
+ : "";
498
+ return `- [[${slug}]] \u2014 ${title}${desc}`;
499
+ }),
500
+ );
501
+ sections[cat].push(...lines);
447
502
  }
448
503
 
449
504
  const categoryLabels: Record<ParaCategory, string> = {
@@ -534,8 +589,25 @@ export async function gitCommit(
534
589
  if (!initResult) return false;
535
590
  }
536
591
 
537
- // Stage all changes
538
- await git(wikiDir, ["add", "-A"]);
592
+ // Stage durable wiki content only. Generated SQLite/search state can be large
593
+ // and made wiki edits slow when `git add -A` tried to version it.
594
+ const stageCandidates = [
595
+ ".gitignore",
596
+ "schema.md",
597
+ "index.md",
598
+ "log.md",
599
+ "sessions.md",
600
+ "config.json",
601
+ ".completed-sessions",
602
+ ...PARA_CATEGORIES,
603
+ ];
604
+ const stagePaths: string[] = [];
605
+ for (const p of stageCandidates) {
606
+ if (await fileExists(join(wikiDir, p))) stagePaths.push(p);
607
+ }
608
+ if (stagePaths.length > 0) {
609
+ await git(wikiDir, ["add", "--", ...stagePaths]);
610
+ }
539
611
 
540
612
  // Check if there's anything to commit
541
613
  const status = await git(wikiDir, ["status", "--porcelain"]);