@tekmidian/pai 0.17.0 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/dist/auto-route-tE6ymgYb.mjs +86 -0
  2. package/dist/auto-route-tE6ymgYb.mjs.map +1 -0
  3. package/dist/checkpoint-block-CkvwYA5y.mjs +1062 -0
  4. package/dist/checkpoint-block-CkvwYA5y.mjs.map +1 -0
  5. package/dist/cli/index.mjs +6 -5
  6. package/dist/cli/index.mjs.map +1 -1
  7. package/dist/cli/program.d.mts.map +1 -1
  8. package/dist/cli/program.mjs +7 -5
  9. package/dist/cli/program.mjs.map +1 -1
  10. package/dist/clusters-CRXU2T6Z.mjs +201 -0
  11. package/dist/clusters-CRXU2T6Z.mjs.map +1 -0
  12. package/dist/config-7UiKVmbE.mjs +203 -0
  13. package/dist/config-7UiKVmbE.mjs.map +1 -0
  14. package/dist/config-CcdkNSWa.mjs +204 -0
  15. package/dist/config-CcdkNSWa.mjs.map +1 -0
  16. package/dist/daemon/index.mjs +9 -9
  17. package/dist/daemon-DPHtKQB2.mjs +1576 -0
  18. package/dist/daemon-DPHtKQB2.mjs.map +1 -0
  19. package/dist/daemon-Pm6crudb.mjs +1576 -0
  20. package/dist/daemon-Pm6crudb.mjs.map +1 -0
  21. package/dist/daemon-mcp/index.mjs +2 -2
  22. package/dist/detector-BGw8SNWe.mjs +74 -0
  23. package/dist/detector-BGw8SNWe.mjs.map +1 -0
  24. package/dist/factory-1UI19STL.mjs +72 -0
  25. package/dist/factory-1UI19STL.mjs.map +1 -0
  26. package/dist/indexer-backend-B93q9ICi.mjs +299 -0
  27. package/dist/indexer-backend-B93q9ICi.mjs.map +1 -0
  28. package/dist/ipc-client-aVKVERjJ.mjs +152 -0
  29. package/dist/ipc-client-aVKVERjJ.mjs.map +1 -0
  30. package/dist/kg-entity-r8duqhi9.mjs +176 -0
  31. package/dist/kg-entity-r8duqhi9.mjs.map +1 -0
  32. package/dist/latent-ideas-CHrgdkSW.mjs +191 -0
  33. package/dist/latent-ideas-CHrgdkSW.mjs.map +1 -0
  34. package/dist/main-resolver-BozXCqpl.mjs +1340 -0
  35. package/dist/main-resolver-BozXCqpl.mjs.map +1 -0
  36. package/dist/neighborhood-jPQBkbB-.mjs +135 -0
  37. package/dist/neighborhood-jPQBkbB-.mjs.map +1 -0
  38. package/dist/note-context-D2S2sR38.mjs +126 -0
  39. package/dist/note-context-D2S2sR38.mjs.map +1 -0
  40. package/dist/pick-44g9gnCi.mjs +12329 -0
  41. package/dist/pick-44g9gnCi.mjs.map +1 -0
  42. package/dist/pick-BLU3NUzF.mjs +12602 -0
  43. package/dist/pick-BLU3NUzF.mjs.map +1 -0
  44. package/dist/pick-BROEyWxI.mjs +12665 -0
  45. package/dist/pick-BROEyWxI.mjs.map +1 -0
  46. package/dist/pick-BRdJ4gpT.mjs +12666 -0
  47. package/dist/pick-BRdJ4gpT.mjs.map +1 -0
  48. package/dist/pick-B_ZSZxR4.mjs +12301 -0
  49. package/dist/pick-B_ZSZxR4.mjs.map +1 -0
  50. package/dist/pick-Bl-g6BzM.mjs +12300 -0
  51. package/dist/pick-Bl-g6BzM.mjs.map +1 -0
  52. package/dist/pick-C1Gfz3nN.mjs +12587 -0
  53. package/dist/pick-C1Gfz3nN.mjs.map +1 -0
  54. package/dist/pick-Cj2seRfQ.mjs +12531 -0
  55. package/dist/pick-Cj2seRfQ.mjs.map +1 -0
  56. package/dist/pick-CpZSdQcU.mjs +12587 -0
  57. package/dist/pick-CpZSdQcU.mjs.map +1 -0
  58. package/dist/pick-DAHBqsS_.mjs +12665 -0
  59. package/dist/pick-DAHBqsS_.mjs.map +1 -0
  60. package/dist/pick-HsJkYu5D.mjs +12500 -0
  61. package/dist/pick-HsJkYu5D.mjs.map +1 -0
  62. package/dist/pick-SxeegWzK.mjs +12126 -0
  63. package/dist/pick-SxeegWzK.mjs.map +1 -0
  64. package/dist/pick-Y-Bnmlqx.mjs +12571 -0
  65. package/dist/pick-Y-Bnmlqx.mjs.map +1 -0
  66. package/dist/pick-sXVKiZhC.mjs +12498 -0
  67. package/dist/pick-sXVKiZhC.mjs.map +1 -0
  68. package/dist/postgres-D96XWfwI.mjs +891 -0
  69. package/dist/postgres-D96XWfwI.mjs.map +1 -0
  70. package/dist/query-feedback-CdBJ2icj.mjs +76 -0
  71. package/dist/query-feedback-CdBJ2icj.mjs.map +1 -0
  72. package/dist/runtime-paths-B0P1TvUr.mjs +50 -0
  73. package/dist/runtime-paths-B0P1TvUr.mjs.map +1 -0
  74. package/dist/sqlite-CR9aMImG.mjs +271 -0
  75. package/dist/sqlite-CR9aMImG.mjs.map +1 -0
  76. package/dist/state-DTvy-jRB.mjs +102 -0
  77. package/dist/state-DTvy-jRB.mjs.map +1 -0
  78. package/dist/themes-HzMKPTas.mjs +148 -0
  79. package/dist/themes-HzMKPTas.mjs.map +1 -0
  80. package/dist/tools-DuorP_SS.mjs +1939 -0
  81. package/dist/tools-DuorP_SS.mjs.map +1 -0
  82. package/dist/trace-Bf7OprC5.mjs +137 -0
  83. package/dist/trace-Bf7OprC5.mjs.map +1 -0
  84. package/dist/vault-indexer-qAfX09RY.mjs +536 -0
  85. package/dist/vault-indexer-qAfX09RY.mjs.map +1 -0
  86. package/dist/work-queue-worker-Bov9eWMC.mjs +1856 -0
  87. package/dist/work-queue-worker-Bov9eWMC.mjs.map +1 -0
  88. package/dist/zettelkasten-DcZk-XSm.mjs +1063 -0
  89. package/dist/zettelkasten-DcZk-XSm.mjs.map +1 -0
  90. package/docs/commands/README.md +7 -0
  91. package/docs/commands/backup.md +1 -1
  92. package/docs/commands/clear-names.md +1 -1
  93. package/docs/commands/daemon.md +1 -1
  94. package/docs/commands/db.md +1 -1
  95. package/docs/commands/end.md +1 -1
  96. package/docs/commands/help.md +1 -1
  97. package/docs/commands/identity.md +65 -0
  98. package/docs/commands/kg.md +1 -1
  99. package/docs/commands/mcp.md +1 -1
  100. package/docs/commands/memory.md +1 -1
  101. package/docs/commands/notify.md +1 -1
  102. package/docs/commands/observation.md +1 -1
  103. package/docs/commands/obsidian.md +1 -1
  104. package/docs/commands/pause.md +1 -1
  105. package/docs/commands/project.md +1 -1
  106. package/docs/commands/projects.md +1 -1
  107. package/docs/commands/registry.md +1 -1
  108. package/docs/commands/restore.md +1 -1
  109. package/docs/commands/session.md +1 -1
  110. package/docs/commands/sessions.md +1 -1
  111. package/docs/commands/setup.md +1 -1
  112. package/docs/commands/shell-init.md +1 -1
  113. package/docs/commands/skill.md +1 -1
  114. package/docs/commands/task.md +15 -2
  115. package/docs/commands/topic.md +1 -1
  116. package/docs/commands/update.md +1 -1
  117. package/docs/commands/zettel.md +1 -1
  118. package/docs/task-bus.md +185 -0
  119. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zettelkasten-DcZk-XSm.mjs","names":["MAX_CHUNKS"],"sources":["../src/zettelkasten/explore.ts","../src/zettelkasten/surprise.ts","../src/zettelkasten/converse.ts","../src/zettelkasten/health.ts","../src/zettelkasten/suggest.ts","../src/zettelkasten/god-notes.ts","../src/zettelkasten/communities.ts"],"sourcesContent":["import type { StorageBackend } from \"../storage/interface.js\";\nimport { dirname } from \"node:path\";\n\nexport interface ExploreOptions {\n startNote: string;\n depth?: number;\n direction?: \"forward\" | \"backward\" | \"both\";\n mode?: \"sequential\" | \"associative\" | \"all\";\n}\n\nexport interface ExploreNode {\n path: string;\n title: string | null;\n depth: number;\n linkType: \"sequential\" | \"associative\";\n inbound: number;\n outbound: number;\n}\n\nexport interface ExploreResult {\n root: string;\n nodes: ExploreNode[];\n edges: Array<{ from: string; to: string; type: \"sequential\" | \"associative\" }>;\n branchingPoints: string[];\n maxDepthReached: boolean;\n}\n\nfunction classifyEdge(source: string, target: string): \"sequential\" | \"associative\" {\n return dirname(source) === dirname(target) ? \"sequential\" : \"associative\";\n}\n\nasync function resolveStart(backend: StorageBackend, startNote: string): Promise<string | null> {\n // Try direct lookup first\n const files = await backend.getVaultFilesByPaths([startNote]);\n if (files.length > 0) return files[0].vaultPath;\n\n // Try alias lookup\n const alias = await backend.getVaultAlias(startNote);\n if (!alias) return null;\n\n const canonical = await backend.getVaultFilesByPaths([alias.canonicalPath]);\n return canonical.length > 0 ? canonical[0].vaultPath : null;\n}\n\nasync function getForwardNeighbors(backend: StorageBackend, path: string): Promise<string[]> {\n const links = await backend.getLinksFromSource(path);\n return links.filter(l => l.targetPath !== null).map(l => l.targetPath as string);\n}\n\nasync function getBackwardNeighbors(backend: StorageBackend, path: string): Promise<string[]> {\n const links = await backend.getLinksToTarget(path);\n return links.map(l => l.sourcePath);\n}\n\nasync function getFileInfo(\n backend: StorageBackend,\n path: string,\n): Promise<{ title: string | null; inbound: number; outbound: number }> {\n const [files, health] = await Promise.all([\n backend.getVaultFilesByPaths([path]),\n backend.getVaultHealth(path),\n ]);\n\n return {\n title: files[0]?.title ?? null,\n inbound: health?.inboundCount ?? 0,\n outbound: health?.outboundCount ?? 0,\n };\n}\n\n/**\n * Traverse the Zettelkasten link graph using BFS, following chains of thought\n * from a starting note up to a configurable depth.\n */\nexport async function zettelExplore(backend: StorageBackend, opts: ExploreOptions): Promise<ExploreResult> {\n const depth = Math.min(Math.max(opts.depth ?? 3, 1), 10);\n const direction = opts.direction ?? \"both\";\n const mode = opts.mode ?? \"all\";\n\n const root = await resolveStart(backend, opts.startNote);\n if (!root) {\n return {\n root: opts.startNote,\n nodes: [],\n edges: [],\n branchingPoints: [],\n maxDepthReached: false,\n };\n }\n\n const visited = new Set<string>([root]);\n const nodes: ExploreNode[] = [];\n const edges: Array<{ from: string; to: string; type: \"sequential\" | \"associative\" }> = [];\n let maxDepthReached = false;\n\n const queue: Array<{ path: string; depth: number }> = [{ path: root, depth: 0 }];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n\n if (current.depth >= depth) {\n maxDepthReached = true;\n continue;\n }\n\n const neighbors: Array<{ neighbor: string; from: string; to: string }> = [];\n\n if (direction === \"forward\" || direction === \"both\") {\n for (const n of await getForwardNeighbors(backend, current.path)) {\n neighbors.push({ neighbor: n, from: current.path, to: n });\n }\n }\n\n if (direction === \"backward\" || direction === \"both\") {\n for (const n of await getBackwardNeighbors(backend, current.path)) {\n neighbors.push({ neighbor: n, from: n, to: current.path });\n }\n }\n\n for (const { neighbor, from, to } of neighbors) {\n const edgeType = classifyEdge(from, to);\n\n if (mode !== \"all\" && edgeType !== mode) {\n continue;\n }\n\n const alreadyHasEdge = edges.some((e) => e.from === from && e.to === to);\n if (!alreadyHasEdge) {\n edges.push({ from, to, type: edgeType });\n }\n\n if (!visited.has(neighbor)) {\n visited.add(neighbor);\n\n const info = await getFileInfo(backend, neighbor);\n nodes.push({\n path: neighbor,\n title: info.title,\n depth: current.depth + 1,\n linkType: edgeType,\n inbound: info.inbound,\n outbound: info.outbound,\n });\n\n queue.push({ path: neighbor, depth: current.depth + 1 });\n }\n }\n }\n\n const branchingPoints = nodes\n .filter((n) => n.outbound > 2)\n .map((n) => n.path);\n\n const rootInfo = await getFileInfo(backend, root);\n if (rootInfo.outbound > 2) {\n branchingPoints.unshift(root);\n }\n\n return { root, nodes, edges, branchingPoints, maxDepthReached };\n}\n","import type { StorageBackend } from \"../storage/interface.js\";\nimport {\n deserializeEmbedding,\n generateEmbedding,\n cosineSimilarity,\n} from \"../memory/embeddings.js\";\n\nexport interface SurpriseOptions {\n referencePath: string;\n vaultProjectId: number;\n limit?: number;\n minSimilarity?: number;\n minGraphDistance?: number;\n}\n\nexport interface SurpriseResult {\n path: string;\n title: string | null;\n cosineSimilarity: number;\n graphDistance: number;\n surpriseScore: number;\n sharedSnippet: string;\n}\n\nconst MAX_CHUNKS = 5000;\nconst BFS_HOP_CAP = 20;\n\nasync function getFileEmbeddings(\n backend: StorageBackend,\n projectId: number,\n): Promise<Map<string, { embedding: Float32Array; text: string }>> {\n const rows = await backend.getChunksWithEmbeddings(projectId, MAX_CHUNKS);\n\n const byPath = new Map<string, { sum: Float32Array; count: number; text: string }>();\n for (const row of rows) {\n const vec = deserializeEmbedding(row.embedding);\n const entry = byPath.get(row.path);\n if (!entry) {\n byPath.set(row.path, { sum: new Float32Array(vec), count: 1, text: row.text });\n } else {\n for (let i = 0; i < vec.length; i++) {\n entry.sum[i] += vec[i];\n }\n entry.count++;\n }\n }\n\n const result = new Map<string, { embedding: Float32Array; text: string }>();\n for (const [path, { sum, count, text }] of byPath) {\n const avg = new Float32Array(sum.length);\n for (let i = 0; i < sum.length; i++) {\n avg[i] = sum[i] / count;\n }\n result.set(path, { embedding: avg, text });\n }\n return result;\n}\n\nasync function getReferenceEmbedding(\n backend: StorageBackend,\n projectId: number,\n path: string,\n): Promise<{ embedding: Float32Array; found: boolean }> {\n const rows = await backend.getChunksForPath(projectId, path);\n\n if (rows.length === 0) {\n return { embedding: new Float32Array(0), found: false };\n }\n\n const embRows = rows.filter(r => r.embedding !== null) as Array<{ text: string; embedding: Buffer }>;\n if (embRows.length === 0) {\n return { embedding: new Float32Array(0), found: false };\n }\n\n const dim = deserializeEmbedding(embRows[0].embedding).length;\n const sum = new Float32Array(dim);\n for (const row of embRows) {\n const vec = deserializeEmbedding(row.embedding);\n for (let i = 0; i < dim; i++) {\n sum[i] += vec[i];\n }\n }\n const avg = new Float32Array(dim);\n for (let i = 0; i < dim; i++) {\n avg[i] = sum[i] / embRows.length;\n }\n return { embedding: avg, found: true };\n}\n\nasync function bfsGraphDistance(backend: StorageBackend, source: string, target: string): Promise<number> {\n if (source === target) return 0;\n\n const visited = new Set<string>([source]);\n const queue: Array<{ path: string; hops: number }> = [{ path: source, hops: 0 }];\n\n while (queue.length > 0) {\n const { path, hops } = queue.shift()!;\n if (hops >= BFS_HOP_CAP) continue;\n\n const [forwardLinks, backwardLinks] = await Promise.all([\n backend.getLinksFromSource(path),\n backend.getLinksToTarget(path),\n ]);\n\n const neighbors: string[] = [\n ...forwardLinks.filter(l => l.targetPath !== null).map(l => l.targetPath as string),\n ...backwardLinks.map(l => l.sourcePath),\n ];\n\n for (const neighbor of neighbors) {\n if (neighbor === target) return hops + 1;\n if (!visited.has(neighbor)) {\n visited.add(neighbor);\n queue.push({ path: neighbor, hops: hops + 1 });\n }\n }\n }\n\n return Infinity;\n}\n\nfunction getBestChunkText(\n chunkRows: Array<{ text: string; embedding: Buffer | null }>,\n refEmbedding: Float32Array,\n): string {\n const rows = chunkRows.filter(r => r.embedding !== null) as Array<{ text: string; embedding: Buffer }>;\n if (rows.length === 0) return \"\";\n\n let bestText = rows[0].text;\n let bestSim = -Infinity;\n\n for (const row of rows) {\n const vec = deserializeEmbedding(row.embedding);\n const sim = cosineSimilarity(refEmbedding, vec);\n if (sim > bestSim) {\n bestSim = sim;\n bestText = row.text;\n }\n }\n\n return bestText.trim().slice(0, 200);\n}\n\n/**\n * Find notes that are semantically similar to a reference note but graph-distant —\n * revealing surprising conceptual connections across unrelated areas of the Zettelkasten.\n */\nexport async function zettelSurprise(\n backend: StorageBackend,\n opts: SurpriseOptions,\n): Promise<SurpriseResult[]> {\n const limit = opts.limit ?? 10;\n const minSimilarity = opts.minSimilarity ?? 0.3;\n const minGraphDistance = opts.minGraphDistance ?? 3;\n\n let { embedding: refEmbedding, found } = await getReferenceEmbedding(\n backend,\n opts.vaultProjectId,\n opts.referencePath,\n );\n\n // Fall back to generating an embedding from the file title if no chunks exist\n if (!found) {\n const files = await backend.getVaultFilesByPaths([opts.referencePath]);\n const text = files[0]?.title ?? opts.referencePath;\n refEmbedding = await generateEmbedding(text, true);\n }\n\n const allFileEmbeddings = await getFileEmbeddings(backend, opts.vaultProjectId);\n\n // Remove the reference note itself from candidates\n allFileEmbeddings.delete(opts.referencePath);\n\n // First pass: filter by semantic similarity to avoid BFS on all nodes\n const semanticCandidates: Array<{ path: string; sim: number }> = [];\n for (const [path, { embedding }] of allFileEmbeddings) {\n const sim = cosineSimilarity(refEmbedding, embedding);\n if (sim >= minSimilarity) {\n semanticCandidates.push({ path, sim });\n }\n }\n\n // Compute graph distances for semantic candidates\n const results: SurpriseResult[] = [];\n\n for (const { path, sim } of semanticCandidates) {\n const graphDistance = await bfsGraphDistance(backend, opts.referencePath, path);\n\n const effectiveDistance = isFinite(graphDistance) ? graphDistance : BFS_HOP_CAP;\n if (effectiveDistance < minGraphDistance) continue;\n\n const files = await backend.getVaultFilesByPaths([path]);\n const chunkRows = await backend.getChunksForPath(opts.vaultProjectId, path, 20);\n\n const surpriseScore = sim * Math.log2(effectiveDistance + 1);\n const sharedSnippet = getBestChunkText(chunkRows, refEmbedding);\n\n results.push({\n path,\n title: files[0]?.title ?? null,\n cosineSimilarity: sim,\n graphDistance: isFinite(graphDistance) ? graphDistance : Infinity,\n surpriseScore,\n sharedSnippet,\n });\n }\n\n results.sort((a, b) => b.surpriseScore - a.surpriseScore);\n return results.slice(0, limit);\n}\n","import type { StorageBackend } from \"../storage/interface.js\";\nimport type { SearchResult } from \"../memory/search.js\";\nimport { generateEmbedding } from \"../memory/embeddings.js\";\n\nexport interface ConverseOptions {\n /** The user's question or topic to explore. */\n question: string;\n /** project_id for vault chunks in memory_chunks. */\n vaultProjectId: number;\n /** Graph expansion depth. Default 2. */\n depth?: number;\n /** Maximum number of relevant notes to return. Default 15. */\n limit?: number;\n}\n\nexport interface ConverseConnection {\n fromPath: string;\n toPath: string;\n /** Top-level folder of fromPath. */\n fromDomain: string;\n /** Top-level folder of toPath. */\n toDomain: string;\n /** Link count between these two notes (can be > 1). */\n strength: number;\n}\n\nexport interface ConverseResult {\n relevantNotes: Array<{\n path: string;\n title: string | null;\n snippet: string;\n score: number;\n domain: string;\n }>;\n /** Cross-domain connections found among the selected notes. */\n connections: ConverseConnection[];\n /** Unique domains involved across all selected notes. */\n domains: string[];\n /** AI-ready prompt combining notes + connections for insight generation. */\n synthesisPrompt: string;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Extract the top-level folder from a vault path (first path segment). */\nfunction extractDomain(vaultPath: string): string {\n const slash = vaultPath.indexOf(\"/\");\n return slash === -1 ? vaultPath : vaultPath.slice(0, slash);\n}\n\n/**\n * Expand one level of graph neighbors for a set of paths.\n * Returns all outbound and inbound neighbor paths (excluding already-visited).\n */\nasync function expandNeighbors(backend: StorageBackend, paths: Set<string>): Promise<string[]> {\n if (paths.size === 0) return [];\n const pathList = Array.from(paths);\n\n const [forwardLinks, backwardLinks] = await Promise.all([\n backend.getVaultLinksFromPaths(pathList),\n Promise.all(pathList.map(p => backend.getLinksToTarget(p))),\n ]);\n\n const neighbors: string[] = [];\n for (const link of forwardLinks) {\n if (link.targetPath) neighbors.push(link.targetPath);\n }\n for (const linkList of backwardLinks) {\n for (const link of linkList) {\n neighbors.push(link.sourcePath);\n }\n }\n return neighbors;\n}\n\n/**\n * Hybrid search combining keyword + semantic results using the StorageBackend.\n */\nasync function hybridSearch(\n backend: StorageBackend,\n query: string,\n queryEmbedding: Float32Array,\n opts: { projectIds?: number[]; maxResults?: number },\n): Promise<SearchResult[]> {\n const maxResults = opts.maxResults ?? 10;\n const kw = 0.5;\n const sw = 0.5;\n\n const [keywordResults, semanticResults] = await Promise.all([\n backend.searchKeyword(query, { ...opts, maxResults: 50 }),\n backend.searchSemantic(queryEmbedding, { ...opts, maxResults: 50 }),\n ]);\n\n if (keywordResults.length === 0 && semanticResults.length === 0) return [];\n\n const keyFor = (r: SearchResult) =>\n `${r.projectId}:${r.path}:${r.startLine}:${r.endLine}`;\n\n function minMaxNormalize(scores: number[]): number[] {\n const min = Math.min(...scores);\n const max = Math.max(...scores);\n const range = max - min;\n if (range === 0) return scores.map(() => 1.0);\n return scores.map(s => (s - min) / range);\n }\n\n const kwNorm = minMaxNormalize(keywordResults.map(r => r.score));\n const semNorm = minMaxNormalize(semanticResults.map(r => r.score));\n\n const combined = new Map<string, SearchResult & { combinedScore: number }>();\n\n for (let i = 0; i < keywordResults.length; i++) {\n const r = keywordResults[i];\n const k = keyFor(r);\n combined.set(k, { ...r, combinedScore: kw * kwNorm[i] });\n }\n\n for (let i = 0; i < semanticResults.length; i++) {\n const r = semanticResults[i];\n const k = keyFor(r);\n const existing = combined.get(k);\n if (existing) {\n existing.combinedScore += sw * semNorm[i];\n } else {\n combined.set(k, { ...r, combinedScore: sw * semNorm[i] });\n }\n }\n\n const sorted = Array.from(combined.values())\n .sort((a, b) => b.combinedScore - a.combinedScore)\n .slice(0, maxResults);\n\n return sorted.map(r => ({ ...r, score: r.combinedScore }));\n}\n\n// ---------------------------------------------------------------------------\n// Main export\n// ---------------------------------------------------------------------------\n\n/**\n * Let the vault \"talk back\" — find notes relevant to a question, expand\n * through the link graph, identify cross-domain connections, and return a\n * structured result including a synthesis prompt for an AI to generate insights.\n */\nexport async function zettelConverse(\n backend: StorageBackend,\n opts: ConverseOptions,\n): Promise<ConverseResult> {\n const depth = Math.max(opts.depth ?? 2, 0);\n const limit = Math.max(opts.limit ?? 15, 1);\n const candidateLimit = 20;\n\n // ------------------------------------------------------------------\n // 1. Hybrid search: find top candidates via BM25 + semantic similarity\n // ------------------------------------------------------------------\n const queryEmbedding = await generateEmbedding(opts.question, true);\n\n const searchResults = await hybridSearch(\n backend,\n opts.question,\n queryEmbedding,\n {\n projectIds: [opts.vaultProjectId],\n maxResults: candidateLimit,\n },\n );\n\n // Map of path -> best score + snippet from search results\n const searchHits = new Map<string, { score: number; snippet: string }>();\n for (const r of searchResults) {\n const existing = searchHits.get(r.path);\n if (!existing || r.score > existing.score) {\n searchHits.set(r.path, { score: r.score, snippet: r.snippet });\n }\n }\n\n // ------------------------------------------------------------------\n // 2. Graph expansion: BFS from each search result up to `depth` levels\n // ------------------------------------------------------------------\n const allPaths = new Set<string>(searchHits.keys());\n let frontier = new Set<string>(searchHits.keys());\n\n for (let d = 0; d < depth; d++) {\n const neighbors = await expandNeighbors(backend, frontier);\n const newFrontier = new Set<string>();\n for (const n of neighbors) {\n if (!allPaths.has(n)) {\n allPaths.add(n);\n newFrontier.add(n);\n }\n }\n if (newFrontier.size === 0) break;\n frontier = newFrontier;\n }\n\n // ------------------------------------------------------------------\n // 3. Deduplicate + trim to limit\n // ------------------------------------------------------------------\n const searchRanked = Array.from(searchHits.entries())\n .sort((a, b) => b[1].score - a[1].score)\n .map(([path, info]) => ({ path, ...info, isSearchResult: true }));\n\n const neighborPaths = Array.from(allPaths).filter((p) => !searchHits.has(p));\n\n // Fetch health data for neighbor ranking\n const neighborHealthRows = await Promise.all(\n neighborPaths.map(p => backend.getVaultHealth(p))\n );\n const neighborRanked = neighborPaths\n .map((path, idx) => ({\n path,\n score: 0,\n snippet: \"\",\n inbound: neighborHealthRows[idx]?.inboundCount ?? 0,\n isSearchResult: false,\n }))\n .sort((a, b) => b.inbound - a.inbound);\n\n const budgetForNeighbors = Math.max(limit - searchRanked.length, 0);\n const selectedNeighbors = neighborRanked.slice(0, budgetForNeighbors);\n\n const selectedSearchPaths = searchRanked.slice(0, limit);\n const selectedPaths = new Set<string>([\n ...selectedSearchPaths.map((r) => r.path),\n ...selectedNeighbors.map((r) => r.path),\n ]);\n\n // ------------------------------------------------------------------\n // 4. Build relevantNotes with titles + domains\n // ------------------------------------------------------------------\n\n // Fetch titles in bulk\n const allSelectedPaths = Array.from(selectedPaths);\n const fileRows = await backend.getVaultFilesByPaths(allSelectedPaths);\n const titleMap = new Map<string, string | null>(fileRows.map(f => [f.vaultPath, f.title]));\n\n const relevantNotes: ConverseResult[\"relevantNotes\"] = [];\n\n for (const r of selectedSearchPaths) {\n if (!selectedPaths.has(r.path)) continue;\n relevantNotes.push({\n path: r.path,\n title: titleMap.get(r.path) ?? null,\n snippet: r.snippet,\n score: r.score,\n domain: extractDomain(r.path),\n });\n }\n\n for (const r of selectedNeighbors) {\n relevantNotes.push({\n path: r.path,\n title: titleMap.get(r.path) ?? null,\n snippet: r.snippet,\n score: 0,\n domain: extractDomain(r.path),\n });\n }\n\n // ------------------------------------------------------------------\n // 5. Find connections between the selected notes\n // ------------------------------------------------------------------\n let connections: ConverseConnection[] = [];\n\n if (selectedPaths.size > 0) {\n const pathList = Array.from(selectedPaths);\n const pathSet = new Set(pathList);\n\n // Get all outbound links from the selected paths\n const linkRows = await backend.getVaultLinksFromPaths(pathList);\n\n // Count links between selected paths\n const edgeCounts = new Map<string, number>();\n for (const link of linkRows) {\n if (link.targetPath && pathSet.has(link.targetPath)) {\n const key = `${link.sourcePath}|||${link.targetPath}`;\n edgeCounts.set(key, (edgeCounts.get(key) ?? 0) + 1);\n }\n }\n\n for (const [key, cnt] of edgeCounts) {\n const [sourcePath, targetPath] = key.split(\"|||\");\n connections.push({\n fromPath: sourcePath,\n toPath: targetPath,\n fromDomain: extractDomain(sourcePath),\n toDomain: extractDomain(targetPath),\n strength: cnt,\n });\n }\n }\n\n // ------------------------------------------------------------------\n // 6. Domains + cross-domain filter\n // ------------------------------------------------------------------\n const domainSet = new Set<string>(relevantNotes.map((n) => n.domain));\n const domains = Array.from(domainSet).sort();\n\n const crossDomainConnections = connections.filter(\n (c) => c.fromDomain !== c.toDomain,\n );\n\n // ------------------------------------------------------------------\n // 7. Build synthesis prompt\n // ------------------------------------------------------------------\n const notesSummary = relevantNotes\n .map((n, i) => {\n const title = n.title ? `\"${n.title}\"` : \"(untitled)\";\n const domain = n.domain;\n const scoreLabel = n.score > 0 ? ` [relevance: ${n.score.toFixed(3)}]` : \" [context]\";\n const snippet = n.snippet.trim().slice(0, 300);\n return `${i + 1}. [${domain}] ${title}${scoreLabel}\\n Path: ${n.path}\\n \"${snippet}\"`;\n })\n .join(\"\\n\\n\");\n\n const connectionSummary =\n crossDomainConnections.length > 0\n ? crossDomainConnections\n .map(\n (c) =>\n `- \"${c.fromPath}\" (${c.fromDomain}) → \"${c.toPath}\" (${c.toDomain}) [strength: ${c.strength}]`,\n )\n .join(\"\\n\")\n : \"(no cross-domain connections found)\";\n\n const domainList = domains.join(\", \");\n\n const synthesisPrompt = `You are a Zettelkasten research assistant. The vault has surfaced the following notes in response to this question:\n\nQUESTION: ${opts.question}\n\n---\n\nRELEVANT NOTES (${relevantNotes.length} notes across ${domains.length} domain(s): ${domainList}):\n\n${notesSummary}\n\n---\n\nCROSS-DOMAIN CONNECTIONS (links bridging different knowledge areas):\n\n${connectionSummary}\n\n---\n\nSYNTHESIS TASK:\n\nBased on these notes and the connections between them, please:\n\n1. Identify the key insights that emerge in direct response to the question.\n2. Highlight any unexpected connections between notes from different domains (${domainList}).\n3. Point out tensions, contradictions, or open questions the vault raises but does not resolve.\n4. Suggest what is notably absent — what the vault does NOT yet contain that would strengthen the understanding of this topic.\n5. Propose 2-3 new notes that would meaningfully extend this knowledge cluster.\n\nThink like a scholar who has deeply internalized these ideas and is now synthesizing them for the first time.`;\n\n return {\n relevantNotes,\n connections: crossDomainConnections,\n domains,\n synthesisPrompt,\n };\n}\n","import type { StorageBackend } from \"../storage/interface.js\";\n\nexport interface HealthOptions {\n scope?: \"full\" | \"recent\" | \"project\";\n projectPath?: string;\n recentDays?: number;\n include?: Array<\"dead_links\" | \"orphans\" | \"disconnected\" | \"low_connectivity\">;\n}\n\nexport interface DeadLink {\n sourcePath: string;\n targetRaw: string;\n lineNumber: number;\n}\n\nexport interface HealthResult {\n totalFiles: number;\n totalLinks: number;\n deadLinks: DeadLink[];\n orphans: string[];\n disconnectedClusters: number;\n lowConnectivity: string[];\n healthScore: number;\n computedAt: number;\n /** Breakdown of links by confidence level (if confidence tagging is active). */\n linkConfidence?: {\n extracted: number;\n inferred: number;\n ambiguous: number;\n };\n}\n\nfunction countComponents(nodes: string[], edges: Array<{ source: string; target: string }>): number {\n if (nodes.length === 0) return 0;\n\n const parent = new Map<string, string>();\n const rank = new Map<string, number>();\n\n for (const n of nodes) {\n parent.set(n, n);\n rank.set(n, 0);\n }\n\n function find(x: string): string {\n let root = x;\n while (parent.get(root) !== root) {\n root = parent.get(root)!;\n }\n let current = x;\n while (current !== root) {\n const next = parent.get(current)!;\n parent.set(current, root);\n current = next;\n }\n return root;\n }\n\n function union(a: string, b: string): void {\n const ra = find(a);\n const rb = find(b);\n if (ra === rb) return;\n const rankA = rank.get(ra) ?? 0;\n const rankB = rank.get(rb) ?? 0;\n if (rankA < rankB) {\n parent.set(ra, rb);\n } else if (rankA > rankB) {\n parent.set(rb, ra);\n } else {\n parent.set(rb, ra);\n rank.set(ra, rankA + 1);\n }\n }\n\n for (const { source, target } of edges) {\n if (parent.has(source) && parent.has(target)) {\n union(source, target);\n }\n }\n\n const roots = new Set<string>();\n for (const n of nodes) {\n roots.add(find(n));\n }\n return roots.size;\n}\n\n/**\n * Audit the structural health of the Zettelkasten vault using graph metrics.\n */\nexport async function zettelHealth(backend: StorageBackend, opts?: HealthOptions): Promise<HealthResult> {\n const options = opts ?? {};\n const scope = options.scope ?? \"full\";\n const include = options.include ?? [\"dead_links\", \"orphans\", \"disconnected\", \"low_connectivity\"];\n\n const computedAt = Date.now();\n\n // --- totalFiles ---\n let totalFiles = 0;\n if (scope === \"full\") {\n totalFiles = await backend.countVaultFiles();\n } else if (scope === \"project\") {\n const prefix = options.projectPath ?? \"\";\n totalFiles = await backend.countVaultFilesWithPrefix(prefix);\n } else {\n const days = options.recentDays ?? 30;\n const cutoff = computedAt - days * 86400000;\n totalFiles = await backend.countVaultFilesAfter(cutoff);\n }\n\n // --- totalLinks ---\n let totalLinks = 0;\n if (scope === \"full\") {\n // Count total links via link graph length\n const graph = await backend.getVaultLinkGraph();\n totalLinks = graph.length;\n } else if (scope === \"project\") {\n const prefix = options.projectPath ?? \"\";\n totalLinks = await backend.countVaultLinksWithPrefix(prefix);\n } else {\n const days = options.recentDays ?? 30;\n const cutoff = computedAt - days * 86400000;\n totalLinks = await backend.countVaultLinksAfter(cutoff);\n }\n\n // --- deadLinks ---\n let deadLinks: DeadLink[] = [];\n if (include.includes(\"dead_links\")) {\n if (scope === \"full\") {\n deadLinks = await backend.getDeadLinksWithLineNumbers();\n } else if (scope === \"project\") {\n const prefix = options.projectPath ?? \"\";\n deadLinks = await backend.getDeadLinksWithPrefix(prefix);\n } else {\n const days = options.recentDays ?? 30;\n const cutoff = computedAt - days * 86400000;\n deadLinks = await backend.getDeadLinksAfter(cutoff);\n }\n }\n\n // --- orphans ---\n let orphans: string[] = [];\n if (include.includes(\"orphans\")) {\n if (scope === \"full\") {\n const orphanRows = await backend.getOrphans();\n orphans = orphanRows.map(r => r.vaultPath);\n } else if (scope === \"project\") {\n const prefix = options.projectPath ?? \"\";\n orphans = await backend.getOrphansWithPrefix(prefix);\n } else {\n const days = options.recentDays ?? 30;\n const cutoff = computedAt - days * 86400000;\n orphans = await backend.getOrphansAfter(cutoff);\n }\n }\n\n // --- disconnectedClusters (union-find) ---\n let disconnectedClusters = 1;\n if (include.includes(\"disconnected\")) {\n let allNodes: string[];\n let allEdges: Array<{ source: string; target: string }>;\n\n if (scope === \"full\") {\n [allNodes, allEdges] = await Promise.all([\n backend.getAllVaultFilePaths(),\n backend.getVaultLinkEdges(),\n ]);\n } else if (scope === \"project\") {\n const prefix = options.projectPath ?? \"\";\n [allNodes, allEdges] = await Promise.all([\n backend.getVaultFilePathsWithPrefix(prefix),\n backend.getVaultLinkEdgesWithPrefix(prefix),\n ]);\n } else {\n const days = options.recentDays ?? 30;\n const cutoff = computedAt - days * 86400000;\n [allNodes, allEdges] = await Promise.all([\n backend.getVaultFilePathsAfter(cutoff),\n backend.getVaultLinkEdgesAfter(cutoff),\n ]);\n }\n\n disconnectedClusters = countComponents(allNodes, allEdges);\n }\n\n // --- lowConnectivity ---\n let lowConnectivity: string[] = [];\n if (include.includes(\"low_connectivity\")) {\n if (scope === \"full\") {\n lowConnectivity = await backend.getLowConnectivity();\n } else if (scope === \"project\") {\n const prefix = options.projectPath ?? \"\";\n lowConnectivity = await backend.getLowConnectivityWithPrefix(prefix);\n } else {\n const days = options.recentDays ?? 30;\n const cutoff = computedAt - days * 86400000;\n lowConnectivity = await backend.getLowConnectivityAfter(cutoff);\n }\n }\n\n // --- linkConfidence ---\n // Count links by confidence level using a sampling of all vault file paths.\n // We reuse the link graph data we already fetched for totalLinks when scope=full.\n let linkConfidence: HealthResult[\"linkConfidence\"] | undefined;\n if (scope === \"full\") {\n try {\n // Get all file paths to iterate their outbound links with confidence\n const samplePaths = await backend.getAllVaultFilePaths();\n const sampleSize = Math.min(samplePaths.length, 200); // limit for performance\n const sampled = samplePaths.slice(0, sampleSize);\n let extracted = 0;\n let inferred = 0;\n let ambiguous = 0;\n for (const path of sampled) {\n const links = await backend.getLinksFromSource(path);\n for (const link of links) {\n const c = link.confidence ?? \"EXTRACTED\";\n if (c === \"EXTRACTED\") extracted++;\n else if (c === \"INFERRED\") inferred++;\n else ambiguous++;\n }\n }\n // Extrapolate if we sampled\n const factor = samplePaths.length > 0 ? samplePaths.length / sampleSize : 1;\n linkConfidence = {\n extracted: Math.round(extracted * factor),\n inferred: Math.round(inferred * factor),\n ambiguous: Math.round(ambiguous * factor),\n };\n } catch {\n // Backend may not support confidence field yet — ignore\n }\n }\n\n // --- healthScore ---\n const deadRatio = totalLinks > 0 ? deadLinks.length / totalLinks : 0;\n const orphanRatio = totalFiles > 0 ? orphans.length / totalFiles : 0;\n const lowConnRatio = totalFiles > 0 ? lowConnectivity.length / totalFiles : 0;\n const healthScore = Math.round(\n 100 * (1 - deadRatio) * (1 - orphanRatio * 0.5) * (1 - lowConnRatio * 0.3),\n );\n\n return {\n totalFiles,\n totalLinks,\n deadLinks,\n orphans,\n disconnectedClusters,\n lowConnectivity,\n healthScore,\n computedAt,\n linkConfidence,\n };\n}\n","import type { StorageBackend } from \"../storage/interface.js\";\nimport { deserializeEmbedding, cosineSimilarity } from \"../memory/embeddings.js\";\nimport { basename } from \"node:path\";\nimport { STOP_WORDS } from \"../utils/stop-words.js\";\n\nexport interface SuggestOptions {\n notePath: string;\n vaultProjectId: number;\n limit?: number;\n excludeLinked?: boolean;\n}\n\nexport interface Suggestion {\n path: string;\n title: string | null;\n score: number;\n semanticScore: number;\n tagScore: number;\n neighborScore: number;\n reason: string;\n suggestedWikilink: string;\n}\n\nconst MAX_CHUNKS = 5000;\nconst SEMANTIC_WEIGHT = 0.5;\nconst TAG_WEIGHT = 0.2;\nconst NEIGHBOR_WEIGHT = 0.3;\n\n// STOP_WORDS imported from utils/stop-words.ts\n\nfunction extractTagsFromChunkTexts(texts: string[]): Set<string> {\n const tags = new Set<string>();\n for (const text of texts) {\n // Match YAML frontmatter tags block: \"tags:\\n - tag1\\n - tag2\"\n const match = text.match(/^tags:\\s*\\n((?:[ \\t]*-[ \\t]*.+\\n?)*)/m);\n if (!match) continue;\n const block = match[1];\n const lines = block.split(\"\\n\");\n for (const line of lines) {\n const tagMatch = line.match(/^[ \\t]*-[ \\t]*(.+)/);\n if (tagMatch) {\n const tag = tagMatch[1].trim().toLowerCase();\n if (tag) tags.add(tag);\n }\n }\n }\n return tags;\n}\n\nfunction jaccardSimilarity(a: Set<string>, b: Set<string>): number {\n if (a.size === 0 && b.size === 0) return 0;\n let intersection = 0;\n for (const tag of a) {\n if (b.has(tag)) intersection++;\n }\n const union = a.size + b.size - intersection;\n return union === 0 ? 0 : intersection / union;\n}\n\nfunction buildReason(\n semanticScore: number,\n tagScore: number,\n neighborScore: number,\n neighborCount: number,\n): string {\n const signals: Array<{ label: string; value: number }> = [\n { label: `Semantically similar (${semanticScore.toFixed(2)})`, value: semanticScore * SEMANTIC_WEIGHT },\n { label: `Shared tags (${tagScore.toFixed(2)} Jaccard)`, value: tagScore * TAG_WEIGHT },\n { label: `Linked by ${neighborCount} mutual connection${neighborCount !== 1 ? \"s\" : \"\"}`, value: neighborScore * NEIGHBOR_WEIGHT },\n ];\n signals.sort((a, b) => b.value - a.value);\n return signals[0].label;\n}\n\nfunction suggestedWikilink(vaultPath: string): string {\n const base = basename(vaultPath);\n const name = base.endsWith(\".md\") ? base.slice(0, -3) : base;\n return `[[${name}]]`;\n}\n\n/**\n * Proactively find notes worth linking to a given note, combining semantic similarity,\n * shared tags, and graph-neighborhood signals into a ranked list of suggestions.\n */\nexport async function zettelSuggest(\n backend: StorageBackend,\n opts: SuggestOptions,\n): Promise<Suggestion[]> {\n const limit = opts.limit ?? 5;\n const excludeLinked = opts.excludeLinked ?? true;\n\n // Step 1: get current outbound links\n const outboundLinks = await backend.getLinksFromSource(opts.notePath);\n const linkedPaths = new Set(outboundLinks.filter(l => l.targetPath !== null).map(l => l.targetPath as string));\n\n // Step 2a: get all file-level embeddings for semantic scoring\n const chunkRows = await backend.getChunksWithEmbeddings(opts.vaultProjectId, MAX_CHUNKS);\n\n const byPath = new Map<string, { sum: Float32Array; count: number }>();\n for (const row of chunkRows) {\n const vec = deserializeEmbedding(row.embedding);\n const entry = byPath.get(row.path);\n if (!entry) {\n byPath.set(row.path, { sum: new Float32Array(vec), count: 1 });\n } else {\n for (let i = 0; i < vec.length; i++) {\n entry.sum[i] += vec[i];\n }\n entry.count++;\n }\n }\n\n const allEmbeddings = new Map<string, Float32Array>();\n for (const [path, { sum, count }] of byPath) {\n const avg = new Float32Array(sum.length);\n for (let i = 0; i < sum.length; i++) {\n avg[i] = sum[i] / count;\n }\n allEmbeddings.set(path, avg);\n }\n allEmbeddings.delete(opts.notePath);\n\n // Step 2b: get source embedding\n const sourceEmbedding = allEmbeddings.get(opts.notePath) ?? null;\n\n // Step 2c: get source tags\n const sourceChunkTexts = await backend.getChunksForPath(opts.vaultProjectId, opts.notePath, 5);\n const sourceTags = extractTagsFromChunkTexts(sourceChunkTexts.map(r => r.text));\n\n // Step 2d: compute graph neighborhood (friends-of-friends)\n const directLinks = await backend.getLinksFromSource(opts.notePath);\n const directTargets = directLinks.filter(l => l.targetPath !== null).map(l => l.targetPath as string);\n\n const friendLinkCounts = new Map<string, number>();\n for (const target of directTargets) {\n const friendLinks = await backend.getLinksFromSource(target);\n for (const link of friendLinks) {\n if (link.targetPath && link.targetPath !== opts.notePath) {\n friendLinkCounts.set(link.targetPath, (friendLinkCounts.get(link.targetPath) ?? 0) + 1);\n }\n }\n }\n const maxFriendLinks = Math.max(1, ...friendLinkCounts.values());\n\n // Get all vault files to enumerate candidates\n const allFiles = await backend.getAllVaultFiles();\n\n const suggestions: Suggestion[] = [];\n\n for (const fileRow of allFiles) {\n const vault_path = fileRow.vaultPath;\n const title = fileRow.title;\n\n if (vault_path === opts.notePath) continue;\n if (excludeLinked && linkedPaths.has(vault_path)) continue;\n\n // Semantic score\n let semanticScore = 0;\n if (sourceEmbedding) {\n const candidateEmbedding = allEmbeddings.get(vault_path);\n if (candidateEmbedding) {\n semanticScore = Math.max(0, cosineSimilarity(sourceEmbedding, candidateEmbedding));\n }\n }\n\n // Tag score (only compute if candidate might have chunks)\n let tagScore = 0;\n if (allEmbeddings.has(vault_path)) {\n const candidateChunkTexts = await backend.getChunksForPath(opts.vaultProjectId, vault_path, 5);\n const candidateTags = extractTagsFromChunkTexts(candidateChunkTexts.map(r => r.text));\n tagScore = jaccardSimilarity(sourceTags, candidateTags);\n }\n\n // Neighbor score\n const friendCount = friendLinkCounts.get(vault_path) ?? 0;\n const neighborScore = friendCount / maxFriendLinks;\n\n const score =\n SEMANTIC_WEIGHT * semanticScore +\n TAG_WEIGHT * tagScore +\n NEIGHBOR_WEIGHT * neighborScore;\n\n // Only include if there is at least some signal\n if (score <= 0) continue;\n\n const reason = buildReason(semanticScore, tagScore, neighborScore, friendCount);\n\n suggestions.push({\n path: vault_path,\n title,\n score,\n semanticScore,\n tagScore,\n neighborScore,\n reason,\n suggestedWikilink: suggestedWikilink(vault_path),\n });\n }\n\n suggestions.sort((a, b) => b.score - a.score);\n return suggestions.slice(0, limit);\n}\n","/**\n * God-note detection — find hub notes via degree centrality on the vault link graph.\n *\n * Hub notes (or \"god notes\") are notes with unusually high inbound link counts.\n * They represent core concepts that the rest of the vault frequently references.\n * Structural pages (index, MOC, master notes, tag pages) are filtered out.\n */\n\nimport type { StorageBackend } from \"../storage/interface.js\";\n\nexport interface GodNoteOptions {\n /** Maximum number of hub notes to return. Default: 20. */\n limit?: number;\n /** Minimum inbound link count to qualify as a hub. Default: 3. */\n minInbound?: number;\n /** Include outbound counts in the result. Default: true. */\n includeOutbound?: boolean;\n}\n\nexport interface GodNote {\n path: string;\n title: string | null;\n inboundCount: number;\n outboundCount: number;\n /** Ratio of inbound to total degree — higher means more \"sink\"-like. */\n inboundRatio: number;\n}\n\nexport interface GodNoteResult {\n godNotes: GodNote[];\n totalVaultFiles: number;\n /** Median inbound count across all vault files (for context). */\n medianInbound: number;\n}\n\n/** Patterns that identify structural/meta pages rather than concept notes. */\nconst STRUCTURAL_PATTERNS = [\n /\\bindex\\b/i,\n /\\bMOC\\b/,\n /\\bmaster\\b/i,\n /\\btag\\s*page/i,\n /\\bhome\\b/i,\n /\\bdashboard\\b/i,\n /\\btemplate/i,\n /^_/,\n];\n\nfunction isStructuralNote(title: string | null, path: string): boolean {\n const text = title ?? path;\n return STRUCTURAL_PATTERNS.some((pattern) => pattern.test(text));\n}\n\n/**\n * Find hub/\"god\" notes in the vault — notes with the highest inbound link counts,\n * excluding structural pages.\n */\nexport async function zettelGodNotes(\n backend: StorageBackend,\n opts?: GodNoteOptions,\n): Promise<GodNoteResult> {\n const limit = opts?.limit ?? 20;\n const minInbound = opts?.minInbound ?? 3;\n\n // Get the full link graph\n const linkGraph = await backend.getVaultLinkGraph();\n\n // Count inbound and outbound per path\n const inboundCounts = new Map<string, number>();\n const outboundCounts = new Map<string, number>();\n\n for (const { source_path, target_path } of linkGraph) {\n inboundCounts.set(target_path, (inboundCounts.get(target_path) ?? 0) + 1);\n outboundCounts.set(source_path, (outboundCounts.get(source_path) ?? 0) + 1);\n }\n\n // Get all vault files for title lookup and total count\n const allFiles = await backend.getAllVaultFiles();\n const titleMap = new Map<string, string | null>();\n for (const f of allFiles) {\n titleMap.set(f.vaultPath, f.title);\n }\n\n const totalVaultFiles = allFiles.length;\n\n // Compute median inbound for context\n const allInbounds = allFiles.map((f) => inboundCounts.get(f.vaultPath) ?? 0);\n allInbounds.sort((a, b) => a - b);\n const medianInbound =\n allInbounds.length > 0\n ? allInbounds[Math.floor(allInbounds.length / 2)]\n : 0;\n\n // Build candidate list: all paths with inbound >= minInbound, excluding structural\n const candidates: GodNote[] = [];\n\n for (const [path, inbound] of inboundCounts) {\n if (inbound < minInbound) continue;\n\n const title = titleMap.get(path) ?? null;\n if (isStructuralNote(title, path)) continue;\n\n const outbound = outboundCounts.get(path) ?? 0;\n const totalDegree = inbound + outbound;\n const inboundRatio = totalDegree > 0 ? inbound / totalDegree : 0;\n\n candidates.push({\n path,\n title,\n inboundCount: inbound,\n outboundCount: outbound,\n inboundRatio: Math.round(inboundRatio * 1000) / 1000,\n });\n }\n\n // Sort by inbound count descending\n candidates.sort((a, b) => b.inboundCount - a.inboundCount);\n\n return {\n godNotes: candidates.slice(0, limit),\n totalVaultFiles,\n medianInbound,\n };\n}\n","/**\n * Community detection for the Zettelkasten vault link graph.\n *\n * Implements the Louvain algorithm in pure TypeScript for discovering\n * densely connected clusters of notes. This reveals emergent knowledge\n * communities that may not be obvious from folder structure alone.\n *\n * The Louvain method optimizes modularity in two phases:\n * 1. Local: each node greedily moves to the community that maximizes modularity gain.\n * 2. Aggregation: communities are collapsed into super-nodes, forming a new graph.\n * Repeat until no further improvement.\n */\n\nimport type { StorageBackend } from \"../storage/interface.js\";\n\nexport interface CommunityOptions {\n /** Minimum community size to include in results. Default: 3. */\n minSize?: number;\n /** Maximum number of communities to return. Default: 20. */\n maxCommunities?: number;\n /** Resolution parameter for Louvain (higher = more communities). Default: 1.0. */\n resolution?: number;\n}\n\nexport interface CommunityNode {\n path: string;\n title: string | null;\n /** Degree (inbound + outbound) within the community. */\n internalDegree: number;\n}\n\nexport interface Community {\n id: number;\n /** Label derived from most common words in note titles. */\n label: string;\n nodes: CommunityNode[];\n size: number;\n /** Cohesion: ratio of internal edges to total possible internal edges. */\n cohesion: number;\n /** Top folders represented in this community. */\n topFolders: string[];\n}\n\nexport interface CommunityResult {\n communities: Community[];\n totalNodes: number;\n totalEdges: number;\n /** Global modularity score of the partition. */\n modularity: number;\n}\n\n// ---------------------------------------------------------------------------\n// Louvain algorithm implementation\n// ---------------------------------------------------------------------------\n\ninterface Graph {\n /** All node IDs */\n nodes: string[];\n /** Adjacency list: node -> Map<neighbor, weight> */\n adj: Map<string, Map<string, number>>;\n /** Total edge weight (sum of all edge weights, counting undirected edges once) */\n totalWeight: number;\n /** Weighted degree per node */\n degree: Map<string, number>;\n}\n\nfunction buildUndirectedGraph(\n edges: Array<{ source_path: string; target_path: string }>\n): Graph {\n const adj = new Map<string, Map<string, number>>();\n const nodeSet = new Set<string>();\n\n function getOrCreate(node: string): Map<string, number> {\n let m = adj.get(node);\n if (!m) {\n m = new Map();\n adj.set(node, m);\n }\n nodeSet.add(node);\n return m;\n }\n\n let totalWeight = 0;\n\n for (const { source_path, target_path } of edges) {\n if (source_path === target_path) continue; // skip self-loops\n\n const aMap = getOrCreate(source_path);\n const bMap = getOrCreate(target_path);\n\n // Undirected: add weight in both directions\n aMap.set(target_path, (aMap.get(target_path) ?? 0) + 1);\n bMap.set(source_path, (bMap.get(source_path) ?? 0) + 1);\n totalWeight += 1; // each undirected edge counted once\n }\n\n // Compute weighted degree\n const degree = new Map<string, number>();\n for (const [node, neighbors] of adj) {\n let d = 0;\n for (const w of neighbors.values()) d += w;\n degree.set(node, d);\n }\n\n return {\n nodes: Array.from(nodeSet),\n adj,\n totalWeight,\n degree,\n };\n}\n\n/**\n * Run one pass of Phase 1: local node movement.\n * Returns true if any node changed community.\n */\nfunction louvainPhase1(\n graph: Graph,\n community: Map<string, number>,\n resolution: number,\n): boolean {\n const m2 = 2 * graph.totalWeight;\n if (m2 === 0) return false;\n\n // Community -> sum of degrees of nodes in community\n const communityDegreeSum = new Map<number, number>();\n // Community -> sum of internal edge weights\n const communityInternalWeight = new Map<number, number>();\n\n for (const node of graph.nodes) {\n const c = community.get(node)!;\n communityDegreeSum.set(c, (communityDegreeSum.get(c) ?? 0) + (graph.degree.get(node) ?? 0));\n }\n\n // Compute internal weights\n for (const [node, neighbors] of graph.adj) {\n const nc = community.get(node)!;\n for (const [neighbor, weight] of neighbors) {\n if (community.get(neighbor) === nc) {\n communityInternalWeight.set(nc, (communityInternalWeight.get(nc) ?? 0) + weight);\n }\n }\n }\n // Each internal edge is counted twice (both endpoints), so divide\n for (const [c, w] of communityInternalWeight) {\n communityInternalWeight.set(c, w / 2);\n }\n\n let improved = false;\n // Shuffle node order for better convergence\n const shuffled = [...graph.nodes];\n for (let i = shuffled.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];\n }\n\n for (const node of shuffled) {\n const currentComm = community.get(node)!;\n const ki = graph.degree.get(node) ?? 0;\n const neighbors = graph.adj.get(node) ?? new Map();\n\n // Compute weight to each neighboring community\n const weightToComm = new Map<number, number>();\n for (const [neighbor, weight] of neighbors) {\n const nc = community.get(neighbor)!;\n weightToComm.set(nc, (weightToComm.get(nc) ?? 0) + weight);\n }\n\n // Remove node from its current community\n communityDegreeSum.set(currentComm, (communityDegreeSum.get(currentComm) ?? 0) - ki);\n const weightToCurrent = weightToComm.get(currentComm) ?? 0;\n communityInternalWeight.set(\n currentComm,\n (communityInternalWeight.get(currentComm) ?? 0) - weightToCurrent,\n );\n\n // Find best community\n let bestComm = currentComm;\n let bestDelta = 0;\n\n for (const [candidateComm, weightToCandidate] of weightToComm) {\n const sigmaTot = communityDegreeSum.get(candidateComm) ?? 0;\n // Modularity gain of moving node to candidateComm\n const delta = weightToCandidate - resolution * (ki * sigmaTot) / m2;\n if (delta > bestDelta) {\n bestDelta = delta;\n bestComm = candidateComm;\n }\n }\n\n // Also consider staying (delta = 0, but need to re-add)\n // Move node to best community\n community.set(node, bestComm);\n communityDegreeSum.set(bestComm, (communityDegreeSum.get(bestComm) ?? 0) + ki);\n const weightToBest = weightToComm.get(bestComm) ?? 0;\n communityInternalWeight.set(\n bestComm,\n (communityInternalWeight.get(bestComm) ?? 0) + weightToBest,\n );\n\n if (bestComm !== currentComm) {\n improved = true;\n }\n }\n\n return improved;\n}\n\n/**\n * Compute modularity for a given partition.\n */\nfunction computeModularity(\n graph: Graph,\n community: Map<string, number>,\n resolution: number,\n): number {\n const m2 = 2 * graph.totalWeight;\n if (m2 === 0) return 0;\n\n let q = 0;\n for (const [node, neighbors] of graph.adj) {\n const ci = community.get(node)!;\n const ki = graph.degree.get(node) ?? 0;\n\n for (const [neighbor, weight] of neighbors) {\n if (community.get(neighbor) === ci) {\n q += weight - resolution * (ki * (graph.degree.get(neighbor) ?? 0)) / m2;\n }\n }\n }\n return q / m2;\n}\n\n/**\n * Run Louvain community detection on the vault link graph.\n */\nfunction runLouvain(\n graph: Graph,\n resolution: number,\n): Map<string, number> {\n // Initialize: each node in its own community\n const community = new Map<string, number>();\n let nextComm = 0;\n for (const node of graph.nodes) {\n community.set(node, nextComm++);\n }\n\n // Phase 1: iteratively move nodes until no improvement\n const MAX_ITERATIONS = 20;\n for (let iter = 0; iter < MAX_ITERATIONS; iter++) {\n const improved = louvainPhase1(graph, community, resolution);\n if (!improved) break;\n }\n\n // Compact community IDs\n const commRemap = new Map<number, number>();\n let remapIdx = 0;\n for (const [, c] of community) {\n if (!commRemap.has(c)) {\n commRemap.set(c, remapIdx++);\n }\n }\n for (const [node, c] of community) {\n community.set(node, commRemap.get(c)!);\n }\n\n return community;\n}\n\n// ---------------------------------------------------------------------------\n// Label generation\n// ---------------------------------------------------------------------------\n\nconst STOP_WORDS = new Set([\n \"the\", \"and\", \"for\", \"are\", \"but\", \"not\", \"you\", \"all\", \"can\", \"her\",\n \"was\", \"one\", \"our\", \"out\", \"has\", \"had\", \"how\", \"its\", \"may\", \"new\",\n \"now\", \"old\", \"see\", \"way\", \"who\", \"did\", \"get\", \"let\", \"say\", \"she\",\n \"too\", \"use\", \"from\", \"with\", \"this\", \"that\", \"will\", \"been\", \"have\",\n \"each\", \"make\", \"like\", \"long\", \"look\", \"many\", \"them\", \"then\", \"what\",\n \"when\", \"some\", \"time\", \"very\", \"your\", \"about\", \"could\", \"into\", \"just\",\n \"more\", \"note\", \"notes\", \"than\", \"over\",\n]);\n\nfunction generateCommunityLabel(titles: Array<string | null>): string {\n const wordCounts = new Map<string, number>();\n for (const title of titles) {\n if (!title) continue;\n const words = title\n .toLowerCase()\n .replace(/[^a-z0-9\\s]/g, \" \")\n .split(/\\s+/)\n .filter((w) => w.length > 2 && !STOP_WORDS.has(w));\n for (const word of words) {\n wordCounts.set(word, (wordCounts.get(word) ?? 0) + 1);\n }\n }\n const sorted = [...wordCounts.entries()].sort((a, b) => b[1] - a[1]);\n return sorted\n .slice(0, 3)\n .map(([w]) => w)\n .join(\" / \") || \"unnamed\";\n}\n\nfunction getTopFolder(path: string): string {\n const slash = path.indexOf(\"/\");\n return slash === -1 ? path : path.slice(0, slash);\n}\n\n// ---------------------------------------------------------------------------\n// Main export\n// ---------------------------------------------------------------------------\n\n/**\n * Detect communities in the vault link graph using the Louvain algorithm.\n * Returns clusters of densely connected notes with labels and cohesion scores.\n */\nexport async function zettelCommunities(\n backend: StorageBackend,\n opts?: CommunityOptions,\n): Promise<CommunityResult> {\n const minSize = opts?.minSize ?? 3;\n const maxCommunities = opts?.maxCommunities ?? 20;\n const resolution = opts?.resolution ?? 1.0;\n\n // Get the full link graph\n const linkGraph = await backend.getVaultLinkGraph();\n const graph = buildUndirectedGraph(linkGraph);\n\n if (graph.nodes.length === 0) {\n return {\n communities: [],\n totalNodes: 0,\n totalEdges: 0,\n modularity: 0,\n };\n }\n\n // Run Louvain\n const communityMap = runLouvain(graph, resolution);\n\n // Compute modularity\n const modularity = computeModularity(graph, communityMap, resolution);\n\n // Group nodes by community\n const groups = new Map<number, string[]>();\n for (const [node, comm] of communityMap) {\n const arr = groups.get(comm);\n if (arr) arr.push(node);\n else groups.set(comm, [node]);\n }\n\n // Get all vault files for title lookup\n const allFiles = await backend.getAllVaultFiles();\n const titleMap = new Map<string, string | null>();\n for (const f of allFiles) {\n titleMap.set(f.vaultPath, f.title);\n }\n\n // Build community results\n const communities: Community[] = [];\n let commId = 0;\n\n for (const [, members] of groups) {\n if (members.length < minSize) continue;\n\n const memberSet = new Set(members);\n const titles = members.map((p) => titleMap.get(p) ?? null);\n const label = generateCommunityLabel(titles);\n\n // Compute internal degree per node\n const nodes: CommunityNode[] = members.map((path) => {\n const neighbors = graph.adj.get(path) ?? new Map();\n let internalDeg = 0;\n for (const [neighbor, weight] of neighbors) {\n if (memberSet.has(neighbor)) internalDeg += weight;\n }\n return {\n path,\n title: titleMap.get(path) ?? null,\n internalDegree: internalDeg,\n };\n });\n\n // Sort nodes by internal degree descending\n nodes.sort((a, b) => b.internalDegree - a.internalDegree);\n\n // Compute cohesion: ratio of actual internal edges to possible\n let internalEdges = 0;\n for (const node of nodes) {\n internalEdges += node.internalDegree;\n }\n internalEdges /= 2; // each edge counted twice\n const possibleEdges = (members.length * (members.length - 1)) / 2;\n const cohesion = possibleEdges > 0 ? Math.round((internalEdges / possibleEdges) * 1000) / 1000 : 0;\n\n // Top folders\n const folderCounts = new Map<string, number>();\n for (const path of members) {\n const folder = getTopFolder(path);\n folderCounts.set(folder, (folderCounts.get(folder) ?? 0) + 1);\n }\n const topFolders = [...folderCounts.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, 5)\n .map(([f]) => f);\n\n communities.push({\n id: commId++,\n label,\n nodes,\n size: members.length,\n cohesion,\n topFolders,\n });\n }\n\n // Sort by size descending\n communities.sort((a, b) => b.size - a.size);\n\n return {\n communities: communities.slice(0, maxCommunities),\n totalNodes: graph.nodes.length,\n totalEdges: graph.totalWeight,\n modularity: Math.round(modularity * 10000) / 10000,\n };\n}\n"],"mappings":"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"}
@@ -14,6 +14,7 @@ Generated man pages for every `pai` command area. Read any page in the terminal
14
14
  | [`pai db`](db.md) | Database inspection: query, tables, schema (sqlite or postgres) |
15
15
  | [`pai end`](end.md) | Finalize a session: save state, mark note Completed, display safe-exit instructions. |
16
16
  | [`pai help`](help.md) | Show the man page for a command area (pai help <area>), or list all areas |
17
+ | [`pai identity`](identity.md) | Declare who you are: self addresses and where mail is delivered |
17
18
  | [`pai kg`](kg.md) | Temporal knowledge graph: backfill, query, list, stats |
18
19
  | [`pai mcp`](mcp.md) | MCP server management: install and status |
19
20
  | [`pai memory`](memory.md) | Memory engine: index, search, and status |
@@ -55,6 +56,11 @@ Generated man pages for every `pai` command area. Read any page in the terminal
55
56
  | `pai db tables` | List all tables in the selected database |
56
57
  | `pai end` | Finalize a session: save state, mark note Completed, display safe-exit instructions. |
57
58
  | `pai help` | Show the man page for a command area (pai help <area>), or list all areas |
59
+ | `pai identity` | Declare who you are: self addresses and where mail is delivered |
60
+ | `pai identity add <email>` | Declare an address as your own (widens what can be sent unreviewed) |
61
+ | `pai identity remove <email>` | Stop treating an address as your own |
62
+ | `pai identity set-delivery <email>` | Set where digests and 'mail me X' are delivered |
63
+ | `pai identity show` | Show the configured identity |
58
64
  | `pai kg` | Temporal knowledge graph: backfill, query, list, stats |
59
65
  | `pai kg backfill` | Populate the KG from existing session notes (idempotent) |
60
66
  | `pai kg list` | List currently-valid triples |
@@ -164,6 +170,7 @@ Generated man pages for every `pai` command area. Read any page in the terminal
164
170
  | `pai task done <id>` | Mark a task complete on the tracker |
165
171
  | `pai task list` | List open tasks on the bus |
166
172
  | `pai task poll` | One scheduler tick: dispatch what is due, check what is running, report |
173
+ | `pai task projects` | Show which sessions can be given work from the tracker, and create the missing projects |
167
174
  | `pai task schedule` | Install, remove or inspect the launchd agent that ticks the scheduler |
168
175
  | `pai task schedule install` | Install the scheduler tick (default: every 15 minutes) |
169
176
  | `pai task schedule status` | Show whether the scheduler agent is installed and loaded |
@@ -18,7 +18,7 @@ pai backup [options]
18
18
 
19
19
  ## See also
20
20
 
21
- [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
21
+ [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
22
22
 
23
23
  Run `pai help <area>` to read any of these in the terminal.
24
24
 
@@ -21,7 +21,7 @@ pai clear-names [options]
21
21
 
22
22
  ## See also
23
23
 
24
- [`pai backup`](backup.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
24
+ [`pai backup`](backup.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
25
25
 
26
26
  Run `pai help <area>` to read any of these in the terminal.
27
27
 
@@ -72,7 +72,7 @@ Tail the daemon log (/tmp/pai-daemon.log)
72
72
 
73
73
  ## See also
74
74
 
75
- [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
75
+ [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
76
76
 
77
77
  Run `pai help <area>` to read any of these in the terminal.
78
78
 
@@ -68,7 +68,7 @@ Show column schema for a table
68
68
 
69
69
  ## See also
70
70
 
71
- [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
71
+ [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
72
72
 
73
73
  Run `pai help <area>` to read any of these in the terminal.
74
74
 
@@ -21,7 +21,7 @@ pai end [options]
21
21
 
22
22
  ## See also
23
23
 
24
- [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai help`](help.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
24
+ [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
25
25
 
26
26
  Run `pai help <area>` to read any of these in the terminal.
27
27
 
@@ -24,7 +24,7 @@ pai help [area] [options]
24
24
 
25
25
  ## See also
26
26
 
27
- [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
27
+ [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
28
28
 
29
29
  Run `pai help <area>` to read any of these in the terminal.
30
30
 
@@ -0,0 +1,65 @@
1
+ <!-- GENERATED by scripts/build-docs.mjs from the live Commander tree (src/cli/program.ts). Do not edit by hand. -->
2
+
3
+ # pai identity
4
+
5
+ > Declare who you are: self addresses and where mail is delivered
6
+
7
+ ## Synopsis
8
+
9
+ ```
10
+ pai identity <subcommand> [options]
11
+ ```
12
+
13
+ ## Subcommands
14
+
15
+ | Command | Description |
16
+ |---------|-------------|
17
+ | [`pai identity show`](#pai-identity-show) | Show the configured identity |
18
+ | [`pai identity add <email>`](#pai-identity-add-email) | Declare an address as your own (widens what can be sent unreviewed) |
19
+ | [`pai identity remove <email>`](#pai-identity-remove-email) | Stop treating an address as your own |
20
+ | [`pai identity set-delivery <email>`](#pai-identity-set-delivery-email) | Set where digests and 'mail me X' are delivered |
21
+
22
+ ### pai identity show
23
+
24
+ Show the configured identity
25
+
26
+
27
+ ### pai identity add <email>
28
+
29
+ Declare an address as your own (widens what can be sent unreviewed)
30
+
31
+ **Arguments**
32
+
33
+ | Argument | Kind |
34
+ |----------|------|
35
+ | `<email>` | required |
36
+
37
+
38
+ ### pai identity remove <email>
39
+
40
+ Stop treating an address as your own
41
+
42
+ **Arguments**
43
+
44
+ | Argument | Kind |
45
+ |----------|------|
46
+ | `<email>` | required |
47
+
48
+
49
+ ### pai identity set-delivery <email>
50
+
51
+ Set where digests and 'mail me X' are delivered
52
+
53
+ **Arguments**
54
+
55
+ | Argument | Kind |
56
+ |----------|------|
57
+ | `<email>` | required |
58
+
59
+
60
+ ## See also
61
+
62
+ [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
63
+
64
+ Run `pai help <area>` to read any of these in the terminal.
65
+
@@ -83,7 +83,7 @@ pai kg stats
83
83
 
84
84
  ## See also
85
85
 
86
- [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
86
+ [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
87
87
 
88
88
  Run `pai help <area>` to read any of these in the terminal.
89
89
 
@@ -29,7 +29,7 @@ Show whether the PAI MCP server is registered and the binary exists
29
29
 
30
30
  ## See also
31
31
 
32
- [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai kg`](kg.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
32
+ [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
33
33
 
34
34
  Run `pai help <area>` to read any of these in the terminal.
35
35
 
@@ -119,7 +119,7 @@ pai memory status
119
119
 
120
120
  ## See also
121
121
 
122
- [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
122
+ [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
123
123
 
124
124
  Run `pai help <area>` to read any of these in the terminal.
125
125
 
@@ -77,7 +77,7 @@ Send a notification with an explicit event type and message
77
77
 
78
78
  ## See also
79
79
 
80
- [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
80
+ [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
81
81
 
82
82
  Run `pai help <area>` to read any of these in the terminal.
83
83
 
@@ -74,7 +74,7 @@ pai observation stats
74
74
 
75
75
  ## See also
76
76
 
77
- [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
77
+ [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
78
78
 
79
79
  Run `pai help <area>` to read any of these in the terminal.
80
80
 
@@ -54,7 +54,7 @@ Open the vault in Obsidian (macOS)
54
54
 
55
55
  ## See also
56
56
 
57
- [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
57
+ [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
58
58
 
59
59
  Run `pai help <area>` to read any of these in the terminal.
60
60
 
@@ -32,7 +32,7 @@ pai pause [target] [options]
32
32
 
33
33
  ## See also
34
34
 
35
- [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
35
+ [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
36
36
 
37
37
  Run `pai help <area>` to read any of these in the terminal.
38
38
 
@@ -340,7 +340,7 @@ Use --options to discover available keys and presets.
340
340
 
341
341
  ## See also
342
342
 
343
- [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
343
+ [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
344
344
 
345
345
  Run `pai help <area>` to read any of these in the terminal.
346
346
 
@@ -359,7 +359,7 @@ pai projects health --fix
359
359
 
360
360
  ## See also
361
361
 
362
- [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
362
+ [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
363
363
 
364
364
  Run `pai help <area>` to read any of these in the terminal.
365
365
 
@@ -79,7 +79,7 @@ Find the project slug for a filesystem path (for use in scripts)
79
79
 
80
80
  ## See also
81
81
 
82
- [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
82
+ [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
83
83
 
84
84
  Run `pai help <area>` to read any of these in the terminal.
85
85
 
@@ -25,7 +25,7 @@ pai restore [backup-path] [options]
25
25
 
26
26
  ## See also
27
27
 
28
- [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
28
+ [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
29
29
 
30
30
  Run `pai help <area>` to read any of these in the terminal.
31
31
 
@@ -289,7 +289,7 @@ Clean up session notes: delete empties, auto-name unnamed, move into YYYY/MM/ hi
289
289
 
290
290
  ## See also
291
291
 
292
- [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
292
+ [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
293
293
 
294
294
  Run `pai help <area>` to read any of these in the terminal.
295
295
 
@@ -19,7 +19,7 @@ pai sessions [options]
19
19
 
20
20
  ## See also
21
21
 
22
- [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
22
+ [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
23
23
 
24
24
  Run `pai help <area>` to read any of these in the terminal.
25
25
 
@@ -14,7 +14,7 @@ pai setup
14
14
 
15
15
  ## See also
16
16
 
17
- [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
17
+ [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
18
18
 
19
19
  Run `pai help <area>` to read any of these in the terminal.
20
20
 
@@ -12,7 +12,7 @@ pai shell-init
12
12
 
13
13
  ## See also
14
14
 
15
- [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
15
+ [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
16
16
 
17
17
  Run `pai help <area>` to read any of these in the terminal.
18
18
 
@@ -48,7 +48,7 @@ See `Notes/swarm/skills-self-educating.md` for the full self-educating loop
48
48
 
49
49
  ## See also
50
50
 
51
- [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
51
+ [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)
52
52
 
53
53
  Run `pai help <area>` to read any of these in the terminal.
54
54