codealmanac 0.2.4 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +9 -9
  2. package/dist/{agents-4Y7X24WW.js → agents-RVTQYE6A.js} +3 -3
  3. package/dist/{chunk-UU6FBRQO.js → chunk-6BJUYZ43.js} +15 -7
  4. package/dist/chunk-6BJUYZ43.js.map +1 -0
  5. package/dist/{chunk-BF2J4XTC.js → chunk-BGUID5BS.js} +2 -2
  6. package/dist/{chunk-H6QKCB7M.js → chunk-DL5BXZCX.js} +53 -3
  7. package/dist/chunk-DL5BXZCX.js.map +1 -0
  8. package/dist/{chunk-QRK3JLFX.js → chunk-GFUB57IT.js} +122 -44
  9. package/dist/chunk-GFUB57IT.js.map +1 -0
  10. package/dist/{chunk-CW4HRLMS.js → chunk-SMIK2YLU.js} +81 -73
  11. package/dist/chunk-SMIK2YLU.js.map +1 -0
  12. package/dist/{cli-MYMZ66EN.js → cli-CL4ID7EO.js} +8 -8
  13. package/dist/codealmanac.js +1 -1
  14. package/dist/{doctor-W5KQQLAX.js → doctor-DOLJRGS4.js} +4 -4
  15. package/dist/{register-commands-XTK2G2FB.js → register-commands-FBJ6XQ3L.js} +10 -10
  16. package/dist/register-commands-FBJ6XQ3L.js.map +1 -0
  17. package/dist/{uninstall-N7JY7ZV2.js → uninstall-DX6LFKMX.js} +4 -4
  18. package/guides/mini.md +3 -3
  19. package/guides/reference.md +7 -7
  20. package/package.json +1 -1
  21. package/dist/chunk-CW4HRLMS.js.map +0 -1
  22. package/dist/chunk-H6QKCB7M.js.map +0 -1
  23. package/dist/chunk-QRK3JLFX.js.map +0 -1
  24. package/dist/chunk-UU6FBRQO.js.map +0 -1
  25. package/dist/register-commands-XTK2G2FB.js.map +0 -1
  26. /package/dist/{agents-4Y7X24WW.js.map → agents-RVTQYE6A.js.map} +0 -0
  27. /package/dist/{chunk-BF2J4XTC.js.map → chunk-BGUID5BS.js.map} +0 -0
  28. /package/dist/{cli-MYMZ66EN.js.map → cli-CL4ID7EO.js.map} +0 -0
  29. /package/dist/{doctor-W5KQQLAX.js.map → doctor-DOLJRGS4.js.map} +0 -0
  30. /package/dist/{uninstall-N7JY7ZV2.js.map → uninstall-DX6LFKMX.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/topics/frontmatter-rewrite.ts","../src/topics/paths.ts","../src/commands/tag.ts","../src/commands/topics/workspace.ts","../src/commands/topics/create.ts","../src/commands/topics/page-rewrite.ts","../src/commands/topics/delete.ts","../src/commands/topics/describe.ts","../src/commands/topics/link.ts","../src/commands/topics/list.ts","../src/commands/topics/rename.ts","../src/commands/topics/read.ts","../src/commands/topics/show.ts","../src/commands/topics/unlink.ts","../src/registry/autoregister.ts","../src/cli/register-edit-commands.ts","../src/commands/list.ts","../src/commands/search.ts","../src/commands/show.ts","../src/cli/register-query-commands.ts","../src/cli/register-setup-commands.ts","../src/commands/bootstrap.ts","../src/agent/prompts.ts","../src/agent/selection.ts","../src/cli/outcome.ts","../src/agent/sdk.ts","../src/commands/init.ts","../src/commands/capture.ts","../src/commands/captureStatus.ts","../src/commands/reindex.ts","../src/cli/register-wiki-lifecycle-commands.ts","../src/cli/register-commands.ts"],"sourcesContent":["import { readFile, rename, writeFile } from \"node:fs/promises\";\n\nimport yaml from \"js-yaml\";\n\n/**\n * Rewrite the `topics:` field in a markdown file's YAML frontmatter.\n *\n * The absolute requirement is **body byte-preservation**. Tag/untag/\n * rename commands touch only the frontmatter; everything after the\n * closing `---` must be byte-identical to the input (same line endings,\n * same trailing whitespace, same final-newline-or-not). We lean on a\n * precise split rather than re-serializing the whole file so any\n * incidental bytes in the body (literal `\\r\\n`, unusual trailing\n * whitespace, triple-hyphen rulers) are left alone.\n *\n * For the frontmatter itself we do a **surgical rewrite of the `topics:`\n * field only** — not a full YAML roundtrip. Reasons:\n * - `js-yaml` normalizes quoting, re-orders keys alphabetically by\n * default, and can drop comments. Any of those would surprise a user\n * who hand-edited their own frontmatter and expects their formatting\n * preserved byte-for-byte.\n * - We only care about one field. Replacing just that field lets the\n * rest of the YAML survive verbatim.\n *\n * The strategy:\n * 1. Find the exact span of the `topics:` key (whether flow\n * `topics: [a, b]` or block `topics:\\n - a\\n - b\\n`) using a\n * small line scanner.\n * 2. Compute the new topics list from the caller's transform.\n * 3. Replace the span with a freshly-emitted `topics: [a, b, c]` line\n * (flow style — compact, readable, and what most authors write).\n * 4. If no `topics:` key exists and the new list is non-empty, append\n * a line to the end of the frontmatter block.\n * 5. If `topics:` exists but the new list is empty, drop the line\n * entirely rather than leaving `topics: []` around.\n *\n * The transform function gets the current deduplicated topic list and\n * returns the new list. Returning an empty array means \"remove the\n * `topics:` key entirely\".\n */\n\nexport interface RewriteResult {\n /** The page's topics before the rewrite (possibly empty). */\n before: string[];\n /** The page's topics after the rewrite (possibly empty). */\n after: string[];\n /** True iff the file content actually changed. */\n changed: boolean;\n}\n\n/**\n * Read `filePath`, compute the new topics via `transform`, and\n * atomically rewrite if the result differs.\n *\n * Atomic per file: write to `<path>.tmp` then rename, same pattern as\n * the registry and topics.yaml writers. A half-written page would\n * corrupt committed user content, so this is non-negotiable.\n */\nexport async function rewritePageTopics(\n filePath: string,\n transform: (current: string[]) => string[],\n): Promise<RewriteResult> {\n const raw = await readFile(filePath, \"utf8\");\n const { before, after, output, changed } = applyTopicsTransform(\n raw,\n transform,\n );\n if (changed) {\n const tmp = `${filePath}.tmp`;\n await writeFile(tmp, output, \"utf8\");\n await rename(tmp, filePath);\n }\n return { before, after, changed };\n}\n\ninterface TransformApplied {\n before: string[];\n after: string[];\n output: string;\n changed: boolean;\n}\n\n/**\n * Pure-string version of `rewritePageTopics`. Useful for tests and for\n * the few places (rename, delete) where we loop many files and want to\n * short-circuit no-op writes cheaply.\n */\nexport function applyTopicsTransform(\n raw: string,\n transform: (current: string[]) => string[],\n): TransformApplied {\n const parsed = splitFrontmatter(raw);\n if (parsed === null) {\n // No frontmatter at all. Tagging a topic on such a page means\n // creating a frontmatter block. We keep the body untouched and\n // prepend `---\\ntopics: [...]\\n---\\n\\n`. If the transform yields\n // an empty list, this is a no-op. Line endings: default to LF for a\n // brand-new frontmatter — we can't infer intent from a file that\n // doesn't have frontmatter yet, and LF is the committed default in\n // most modern repos.\n const next = dedupeSlugs(transform([]));\n if (next.length === 0) {\n return { before: [], after: [], output: raw, changed: false };\n }\n const fm = `---\\ntopics: ${flowList(next)}\\n---\\n\\n`;\n return {\n before: [],\n after: next,\n output: `${fm}${raw}`,\n changed: true,\n };\n }\n\n const { opener, fmLines, closer, body, eol } = parsed;\n const { before, existingRange } = readTopicsFromLines(fmLines);\n const beforeDeduped = dedupeSlugs(before);\n const after = dedupeSlugs(transform(beforeDeduped));\n\n if (arraysEqual(beforeDeduped, after)) {\n return { before: beforeDeduped, after, output: raw, changed: false };\n }\n\n let nextFmLines: string[];\n if (existingRange === null) {\n // No `topics:` key currently present. Add one (only if non-empty).\n if (after.length === 0) {\n return { before: beforeDeduped, after, output: raw, changed: false };\n }\n nextFmLines = [...fmLines, `topics: ${flowList(after)}`];\n } else {\n const replacement =\n after.length === 0 ? null : `topics: ${flowList(after)}`;\n // Interleaved comments/blank lines from a block-style list are\n // re-emitted BELOW the new flow-style `topics:` line so the\n // author's commentary sticks around. Flow/scalar inputs produce an\n // empty `preserved` array, so this collapses to the old behavior\n // for the common case. When we fully delete the key (empty after)\n // the preserved lines go too — without a `topics:` key to anchor\n // them to, trailing \"# below the topics list\" comments become\n // orphans that no longer mean what they said.\n const preservedTail =\n replacement === null ? [] : existingRange.preserved;\n nextFmLines = [\n ...fmLines.slice(0, existingRange.start),\n ...(replacement === null ? [] : [replacement]),\n ...preservedTail,\n ...fmLines.slice(existingRange.end),\n ];\n }\n\n // Rejoin with the same line ending the input frontmatter used so a\n // CRLF-authored file comes out CRLF end-to-end. `splitFrontmatter`\n // sniffed the dominant separator for us.\n const fmBlock =\n nextFmLines.length === 0 ? \"\" : `${nextFmLines.join(eol)}${eol}`;\n const output = `${opener}${fmBlock}${closer}${body}`;\n return {\n before: beforeDeduped,\n after,\n output,\n changed: true,\n };\n}\n\ninterface SplitFrontmatter {\n /** The opening `---\\n` or `---\\r\\n`. */\n opener: string;\n /** Frontmatter lines (no line-ending character included). */\n fmLines: string[];\n /** The closing `---\\n` (or `---\\r\\n`, possibly without trailing newline if EOF). */\n closer: string;\n /** Everything after the closing fence, byte-for-byte. */\n body: string;\n /**\n * Dominant line ending inside the frontmatter block. CRLF-authored\n * files stay CRLF on write; LF stays LF. We sniff once at split time\n * so rewriting doesn't have to re-inspect every line.\n */\n eol: \"\\n\" | \"\\r\\n\";\n}\n\n/**\n * Split a file into (opener, frontmatter lines, closer, body). The\n * regex mirrors `parseFrontmatter` in `indexer/frontmatter.ts` so the\n * indexer and the rewriter agree on what \"has frontmatter\" means.\n *\n * Returns `null` when the file doesn't start with a `---` fence or\n * lacks a matching closer — both cases are legal (a page with only\n * body content) and the caller treats them as \"no frontmatter to\n * rewrite\".\n */\nfunction splitFrontmatter(raw: string): SplitFrontmatter | null {\n if (!raw.startsWith(\"---\")) return null;\n // Match the exact opener (with its line ending) so we can preserve\n // it byte-for-byte.\n const openerMatch = raw.match(/^---(\\r?\\n)/);\n if (openerMatch === null) return null;\n const opener = `---${openerMatch[1] ?? \"\\n\"}`;\n const rest = raw.slice(opener.length);\n // Find the closing `---` that begins at the start of a line. We\n // also handle the edge case where the closer sits at position 0 of\n // `rest` (frontmatter was empty).\n let fenceIdx: number;\n if (rest.startsWith(\"---\")) {\n fenceIdx = 0;\n } else {\n const m = rest.match(/\\r?\\n---(\\r?\\n|$)/);\n if (m === null || m.index === undefined) return null;\n // `m.index` points at the `\\r?\\n` before `---`; skip that newline\n // so fenceIdx lands exactly on the `-`.\n const leadingNewlineLen = (m[0] ?? \"\").startsWith(\"\\r\\n\") ? 2 : 1;\n fenceIdx = m.index + leadingNewlineLen;\n }\n const fmBlock = rest.slice(0, fenceIdx);\n // Determine the closer's full span, including its trailing newline if any.\n const afterDashes = rest.slice(fenceIdx + 3);\n let closerTail = \"\";\n if (afterDashes.startsWith(\"\\r\\n\")) {\n closerTail = \"\\r\\n\";\n } else if (afterDashes.startsWith(\"\\n\")) {\n closerTail = \"\\n\";\n }\n const closer = `---${closerTail}`;\n const body = afterDashes.slice(closerTail.length);\n const fmLines =\n fmBlock.length === 0 ? [] : fmBlock.replace(/\\r?\\n$/, \"\").split(/\\r?\\n/);\n // Sniff the frontmatter's dominant line ending. We look at the\n // opener first (most reliable signal — it's always present and\n // always has an ending). Fall back to checking the fmBlock for any\n // `\\r\\n` runs so a frontmatter with a single-line opener and\n // multi-line body still gets classified right.\n const eol: \"\\n\" | \"\\r\\n\" =\n opener.endsWith(\"\\r\\n\") || /\\r\\n/.test(fmBlock) ? \"\\r\\n\" : \"\\n\";\n return { opener, fmLines, closer, body, eol };\n}\n\ninterface ExistingRange {\n /** Index in `fmLines` of the `topics:` key line (inclusive). */\n start: number;\n /** Index in `fmLines` one past the last line belonging to this key. */\n end: number;\n /**\n * Lines inside `[start+1, end)` that aren't `- entry` lines — i.e.\n * interleaved comments and blank lines a user wrote between entries.\n * We preserve these verbatim when rewriting block-style lists to\n * flow; otherwise a `tag` on a commented list would silently drop\n * the commentary. Empty for flow/scalar shapes.\n */\n preserved: string[];\n}\n\n/**\n * Find `topics:` in a frontmatter-lines array and read the values.\n *\n * Handles three YAML shapes authors commonly write:\n * - `topics: [a, b, c]` (flow sequence, one line)\n * - `topics:` followed by block entries like ` - a` (block sequence)\n * - `topics: a` (a single scalar — treated as one element)\n *\n * Also handles the empty case `topics:` with nothing after it, and the\n * \"no topics key\" case (returns `existingRange: null`).\n *\n * This is NOT a general YAML parser — it's intentionally scoped to the\n * one key we mutate, because using `js-yaml` for a round-trip would\n * lose comments and re-quote strings the user picked a specific way.\n */\nfunction readTopicsFromLines(fmLines: string[]): {\n before: string[];\n existingRange: ExistingRange | null;\n} {\n const keyLineIdx = findTopKey(fmLines, \"topics\");\n if (keyLineIdx === -1) {\n return { before: [], existingRange: null };\n }\n const keyLine = fmLines[keyLineIdx] ?? \"\";\n const colonIdx = keyLine.indexOf(\":\");\n // Everything to the right of the first colon, trimmed.\n const after = keyLine.slice(colonIdx + 1).trim();\n // Strip trailing `# ...` line-comment from a flow value so we don't\n // parse comments as list contents. (A block list's sub-items have\n // their own comments stripped in the block branch below.)\n const afterNoComment = stripTrailingComment(after);\n\n if (afterNoComment.length === 0) {\n // Block sequence style: collect subsequent `- item` lines. Between\n // entries a user may have written:\n // - interleaved `# comment` lines\n // - blank lines\n // We must NOT break the scan on those — doing so would drop every\n // entry after the first comment/blank when we rewrite (silent data\n // loss: the original bug that triggered this fix). We skip them in\n // the scan and stash them in `preserved` so the replacement step\n // can re-emit them verbatim BETWEEN the new flow-style line and\n // the rest of the frontmatter.\n //\n // Edge: comments/blanks that appear BEFORE the first `- entry` or\n // AFTER the last `- entry` count as part of the block too — pulling\n // them out keeps the author's commentary near the list it belongs\n // to. We cap the scan when we hit a real non-entry line (e.g. the\n // next top-level key), leaving everything from that line onward\n // outside the range.\n const values: string[] = [];\n const preserved: string[] = [];\n // Provisional scan cursor. `endIdx` only advances when we've seen\n // something we're sure belongs to this block (an entry line), so\n // trailing whitespace/comments that don't precede another entry\n // stay OUTSIDE the range and aren't shuffled on rewrite.\n let i = keyLineIdx + 1;\n let endIdx = i;\n // `pendingNonEntries` holds comments/blanks we've seen since the\n // last confirmed entry. They're committed to `preserved` only\n // when a subsequent `- entry` proves they live mid-list.\n let pendingNonEntries: string[] = [];\n while (i < fmLines.length) {\n const line = fmLines[i] ?? \"\";\n const trimmed = line.trim();\n if (trimmed.length === 0 || trimmed.startsWith(\"#\")) {\n pendingNonEntries.push(line);\n i += 1;\n continue;\n }\n const m = line.match(/^\\s*-\\s+(.*)$/);\n if (m === null) break;\n // Promote any pending comments/blanks — they're between entries\n // (or before the first entry within the block).\n if (pendingNonEntries.length > 0) {\n preserved.push(...pendingNonEntries);\n pendingNonEntries = [];\n }\n const raw = stripTrailingComment((m[1] ?? \"\").trim());\n const parsed = parseScalar(raw);\n if (parsed.length > 0) values.push(parsed);\n i += 1;\n endIdx = i;\n }\n return {\n before: values,\n existingRange: { start: keyLineIdx, end: endIdx, preserved },\n };\n }\n\n // Flow / scalar shape on one line. Let js-yaml handle the value-parsing\n // (quoting, escapes, etc.) for the RHS only.\n let parsed: unknown;\n try {\n parsed = yaml.load(afterNoComment);\n } catch {\n parsed = null;\n }\n const values: string[] = [];\n if (Array.isArray(parsed)) {\n for (const v of parsed) {\n if (typeof v === \"string\" && v.trim().length > 0) {\n values.push(v.trim());\n }\n }\n } else if (typeof parsed === \"string\" && parsed.trim().length > 0) {\n values.push(parsed.trim());\n }\n return {\n before: values,\n existingRange: { start: keyLineIdx, end: keyLineIdx + 1, preserved: [] },\n };\n}\n\n/**\n * Find a top-level key line. \"Top-level\" means no leading whitespace —\n * we don't walk into nested mappings. The indexer's frontmatter parser\n * only reads top-level keys too, so this matches.\n */\nfunction findTopKey(fmLines: string[], key: string): number {\n const re = new RegExp(`^${escapeRegex(key)}\\\\s*:`);\n for (let i = 0; i < fmLines.length; i += 1) {\n if (re.test(fmLines[i] ?? \"\")) return i;\n }\n return -1;\n}\n\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction stripTrailingComment(s: string): string {\n // Only strip `#` outside of quotes. For the shapes we handle —\n // slug-like kebab-case topics — quoted strings with `#` are rare, but\n // be defensive.\n let inSingle = false;\n let inDouble = false;\n for (let i = 0; i < s.length; i += 1) {\n const ch = s[i];\n if (ch === \"'\" && !inDouble) inSingle = !inSingle;\n else if (ch === '\"' && !inSingle) inDouble = !inDouble;\n else if (ch === \"#\" && !inSingle && !inDouble) {\n return s.slice(0, i).trimEnd();\n }\n }\n return s;\n}\n\n/**\n * Strip YAML quoting from a scalar. Block-sequence items might be\n * written as `- 'foo'` or `- \"foo\"` or bare `- foo`; we accept all\n * three and return the plain string.\n */\nfunction parseScalar(s: string): string {\n if (s.length === 0) return s;\n if (s.length >= 2 && s[0] === '\"' && s[s.length - 1] === '\"') {\n return s.slice(1, -1);\n }\n if (s.length >= 2 && s[0] === \"'\" && s[s.length - 1] === \"'\") {\n return s.slice(1, -1);\n }\n return s;\n}\n\n/**\n * Emit a flow-style YAML sequence like `[auth, jwt, security]`. We use\n * flow because it's the shape most authors write by hand and stays on\n * one line, which keeps diffs tight. Values are quoted only when\n * necessary — plain kebab-case slugs never need quoting.\n */\nfunction flowList(items: string[]): string {\n return `[${items.map((t) => formatScalar(t)).join(\", \")}]`;\n}\n\nfunction formatScalar(s: string): string {\n // If it's a bare kebab/alnum slug, no quotes. Otherwise fall back to\n // js-yaml for correct escaping. We check against a conservative\n // pattern — anything outside it gets YAML-quoted.\n if (/^[a-z0-9][a-z0-9-]*$/.test(s)) return s;\n return yaml\n .dump(s, { flowLevel: 0, lineWidth: Number.MAX_SAFE_INTEGER })\n .trimEnd();\n}\n\nfunction dedupeSlugs(list: string[]): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const raw of list) {\n const s = raw.trim();\n if (s.length === 0) continue;\n if (seen.has(s)) continue;\n seen.add(s);\n out.push(s);\n }\n return out;\n}\n\nfunction arraysEqual(a: string[], b: string[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i += 1) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n","import { join } from \"node:path\";\n\n/**\n * `.almanac/topics.yaml` inside a given repo root. Single helper so no\n * caller has to remember where the file lives.\n */\nexport function topicsYamlPath(repoRoot: string): string {\n return join(repoRoot, \".almanac\", \"topics.yaml\");\n}\n\n/**\n * `.almanac/index.db` inside a given repo root. Mirrors `topicsYamlPath`\n * so the topics commands don't have to import from scattered places.\n */\nexport function indexDbPath(repoRoot: string): string {\n return join(repoRoot, \".almanac\", \"index.db\");\n}\n","import { ensureFreshIndex, runIndexer } from \"../indexer/index.js\";\nimport { resolveWikiRoot } from \"../indexer/resolve-wiki.js\";\nimport { openIndex } from \"../indexer/schema.js\";\nimport { toKebabCase } from \"../slug.js\";\nimport { rewritePageTopics } from \"../topics/frontmatter-rewrite.js\";\nimport { indexDbPath, topicsYamlPath } from \"../topics/paths.js\";\nimport {\n ensureTopic,\n loadTopicsFile,\n writeTopicsFile,\n} from \"../topics/yaml.js\";\n\n/**\n * `almanac tag <page> <topic>...` and `almanac untag <page> <topic>`.\n *\n * These are the page-side of the topics system — `topics ...` manages\n * the DAG and metadata; `tag`/`untag` wires concrete pages into\n * topics. Both commands mutate page frontmatter atomically per file\n * and leave body bytes untouched.\n *\n * Auto-creation policy: if a topic passed to `tag` doesn't yet exist\n * in `topics.yaml`, we create a minimal entry for it (title-cased\n * title, no description, no parents). This matches the spec: \"Ensure\n * topic exists in topics.yaml; if not, create a minimal entry.\" We\n * don't silently create topics on `untag` — you can only untag\n * something that was already a topic.\n */\n\nexport interface TagCommandOutput {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\nexport interface TagOptions {\n cwd: string;\n wiki?: string;\n page?: string;\n topics: string[];\n stdin?: boolean;\n stdinInput?: string;\n}\n\nexport interface UntagOptions {\n cwd: string;\n wiki?: string;\n page: string;\n topic: string;\n}\n\nexport async function runTag(options: TagOptions): Promise<TagCommandOutput> {\n const repoRoot = await resolveWikiRoot({ cwd: options.cwd, wiki: options.wiki });\n\n const topics = options.topics\n .map((t) => toKebabCase(t))\n .filter((t) => t.length > 0);\n if (topics.length === 0) {\n return {\n stdout: \"\",\n stderr: \"almanac: tag requires at least one topic\\n\",\n exitCode: 1,\n };\n }\n\n // Bulk mode reads slugs from stdin; single mode uses the positional.\n const pages: string[] = [];\n if (options.stdin === true) {\n if (options.stdinInput === undefined) {\n return {\n stdout: \"\",\n stderr: \"almanac: tag --stdin called without stdin input\\n\",\n exitCode: 1,\n };\n }\n for (const line of options.stdinInput.split(/\\r?\\n/)) {\n const s = line.trim();\n if (s.length > 0) pages.push(s);\n }\n } else if (options.page !== undefined && options.page.length > 0) {\n pages.push(options.page);\n } else {\n return {\n stdout: \"\",\n stderr: \"almanac: tag requires a page slug (or --stdin)\\n\",\n exitCode: 1,\n };\n }\n\n // Resolve slugs to file paths from the DB. A stale index is fine for\n // `tag` — we just need to find each page's file; `ensureFreshIndex`\n // runs first so the common path is consistent.\n await ensureFreshIndex({ repoRoot });\n const db = openIndex(indexDbPath(repoRoot));\n\n // Validate every requested page exists BEFORE touching topics.yaml.\n // Previously we auto-created topics first, which meant\n // `almanac tag does-not-exist brand-new` left `brand-new` in\n // topics.yaml as a state leak even though the tag itself errored. We\n // resolve rows up front, then short-circuit with an error (and no\n // mutations) if none of the pages are valid.\n const stmt = db.prepare<[string], { file_path: string }>(\n \"SELECT file_path FROM pages WHERE slug = ?\",\n );\n const resolved: { page: string; filePath: string }[] = [];\n const missing: string[] = [];\n try {\n for (const page of pages) {\n const row = stmt.get(toKebabCase(page));\n if (row === undefined) {\n missing.push(page);\n } else {\n resolved.push({ page, filePath: row.file_path });\n }\n }\n } finally {\n db.close();\n }\n\n // Hard-fail when NO page resolved. We deliberately don't mutate\n // topics.yaml on this path — the user's intent (tag page X) is\n // inarguably unsatisfiable, so we shouldn't leave breadcrumbs.\n //\n // In bulk mode (`--stdin`) some pages might resolve and others\n // won't; keeping the original partial-progress behavior for that case\n // (topics get created, resolved pages get tagged, `missing` are\n // reported on stderr with exitCode 1). The state leak only matters\n // when NOTHING succeeds, and that's the case we're fixing.\n if (resolved.length === 0) {\n const stderr = missing.map((p) => `almanac: no such page \"${p}\"\\n`).join(\"\");\n return {\n stdout: \"\",\n stderr,\n exitCode: 1,\n };\n }\n\n // Auto-create missing topics in topics.yaml. Safe to do now — we have\n // at least one page that will actually end up tagged with them.\n const yamlPath = topicsYamlPath(repoRoot);\n const file = await loadTopicsFile(yamlPath);\n let fileChanged = false;\n for (const t of topics) {\n // ensureTopic mutates the file; we check presence beforehand so\n // we only write when something actually changes (skip a redundant\n // atomic rewrite + mtime bump).\n const before = file.topics.length;\n ensureTopic(file, t);\n if (file.topics.length > before) fileChanged = true;\n }\n if (fileChanged) {\n await writeTopicsFile(yamlPath, file);\n }\n\n const summary: string[] = [];\n let taggedPages = 0;\n for (const { page, filePath } of resolved) {\n const result = await rewritePageTopics(filePath, (current) => {\n // Preserve existing order; append new topics in the order\n // the caller supplied them. `applyTopicsTransform` will\n // dedupe for us, but we skip redundant work here too.\n const out = [...current];\n for (const t of topics) if (!current.includes(t)) out.push(t);\n return out;\n });\n if (result.changed) {\n taggedPages += 1;\n // Only surface the NEWLY ADDED topics — not the full request.\n // Reporting every requested topic (including ones the page\n // already had) reads like false positives in commit diffs.\n const added = result.after.filter((t) => !result.before.includes(t));\n summary.push(`tagged ${page}: ${added.join(\", \")}`);\n } else {\n summary.push(\n `no change ${page} (already tagged with ${topics.join(\", \")})`,\n );\n }\n }\n\n if (taggedPages > 0 || fileChanged) {\n // Trigger a reindex so downstream queries see the new rows\n // immediately. Writes to page files bumped their mtimes; writes to\n // topics.yaml are caught by `topicsYamlNewerThan`.\n await runIndexer({ repoRoot });\n }\n\n const stderr = missing.map((p) => `almanac: no such page \"${p}\"\\n`).join(\"\");\n return {\n stdout: summary.length > 0 ? `${summary.join(\"\\n\")}\\n` : \"\",\n stderr,\n exitCode: missing.length > 0 ? 1 : 0,\n };\n}\n\nexport async function runUntag(\n options: UntagOptions,\n): Promise<TagCommandOutput> {\n const repoRoot = await resolveWikiRoot({ cwd: options.cwd, wiki: options.wiki });\n const page = toKebabCase(options.page);\n const topic = toKebabCase(options.topic);\n if (page.length === 0) {\n return {\n stdout: \"\",\n stderr: \"almanac: untag requires a page slug\\n\",\n exitCode: 1,\n };\n }\n if (topic.length === 0) {\n return {\n stdout: \"\",\n stderr: \"almanac: untag requires a topic\\n\",\n exitCode: 1,\n };\n }\n\n await ensureFreshIndex({ repoRoot });\n const db = openIndex(indexDbPath(repoRoot));\n let filePath: string;\n try {\n const row = db\n .prepare<[string], { file_path: string }>(\n \"SELECT file_path FROM pages WHERE slug = ?\",\n )\n .get(page);\n if (row === undefined) {\n return {\n stdout: \"\",\n stderr: `almanac: no such page \"${page}\"\\n`,\n exitCode: 1,\n };\n }\n filePath = row.file_path;\n } finally {\n db.close();\n }\n\n const result = await rewritePageTopics(filePath, (current) =>\n current.filter((t) => t !== topic),\n );\n if (result.changed) {\n await runIndexer({ repoRoot });\n }\n\n return {\n stdout: result.changed\n ? `untagged ${page}: ${topic}\\n`\n : `no change ${page} (not tagged with ${topic})\\n`,\n stderr: \"\",\n exitCode: 0,\n };\n}\n","import type Database from \"better-sqlite3\";\n\nimport { ensureFreshIndex } from \"../../indexer/index.js\";\nimport { resolveWikiRoot } from \"../../indexer/resolve-wiki.js\";\nimport { openIndex } from \"../../indexer/schema.js\";\nimport { indexDbPath, topicsYamlPath } from \"../../topics/paths.js\";\nimport {\n findTopic,\n loadTopicsFile,\n type TopicsFile,\n} from \"../../topics/yaml.js\";\n\ninterface TopicsRepoOptions {\n cwd: string;\n wiki?: string;\n}\n\nexport interface TopicsWorkspace {\n repoRoot: string;\n yamlPath: string;\n file: TopicsFile;\n db: Database.Database;\n}\n\nexport function resolveTopicsRepo(options: TopicsRepoOptions): Promise<string> {\n return resolveWikiRoot({ cwd: options.cwd, wiki: options.wiki });\n}\n\n/**\n * Shared setup path for mutating topic commands. These commands all need\n * a fresh DB view so ad-hoc topics from page frontmatter can be promoted\n * into `topics.yaml` before mutation.\n */\nexport async function openFreshTopicsWorkspace(\n repoRoot: string,\n): Promise<TopicsWorkspace> {\n await ensureFreshIndex({ repoRoot });\n\n const yamlPath = topicsYamlPath(repoRoot);\n const file = await loadTopicsFile(yamlPath);\n const db = openIndex(indexDbPath(repoRoot));\n return { repoRoot, yamlPath, file, db };\n}\n\nexport function closeWorkspace(workspace: TopicsWorkspace): void {\n workspace.db.close();\n}\n\n/**\n * Is `slug` a known topic anywhere — in `topics.yaml`, or as an ad-hoc\n * slug that a page's frontmatter mentioned and the indexer seeded?\n */\nexport function topicExists(\n file: TopicsFile,\n db: Database.Database,\n slug: string,\n): boolean {\n if (findTopic(file, slug) !== null) return true;\n const row = db\n .prepare<[string], { slug: string }>(\n \"SELECT slug FROM topics WHERE slug = ?\",\n )\n .get(slug);\n return row !== undefined;\n}\n","import { runIndexer } from \"../../indexer/index.js\";\nimport { toKebabCase } from \"../../slug.js\";\nimport { ancestorsInFile } from \"../../topics/dag.js\";\nimport {\n ensureTopic,\n findTopic,\n titleCase,\n writeTopicsFile,\n type TopicEntry,\n} from \"../../topics/yaml.js\";\nimport type { TopicsCommandOutput, TopicsCreateOptions } from \"./types.js\";\nimport {\n closeWorkspace,\n openFreshTopicsWorkspace,\n resolveTopicsRepo,\n topicExists,\n} from \"./workspace.js\";\n\n/**\n * `almanac topics create <name> [--parent <slug>]...`.\n *\n * Policy: `--parent <slug>` MUST refer to an existing topic (created\n * earlier in topics.yaml or indexed from page frontmatter). Auto-\n * creating parents silently would let typos cascade — `create JWT\n * --parent secuirty` would quietly spawn a \"secuirty\" topic. Better to\n * refuse and point the user at `almanac topics create <parent>` first.\n *\n * Already-exists is not an error if no new parents are being added —\n * rerunning the same `create` is a no-op. If new parents are introduced\n * we add them (respecting cycle prevention, just like `link`).\n */\nexport async function runTopicsCreate(\n options: TopicsCreateOptions,\n): Promise<TopicsCommandOutput> {\n const repoRoot = await resolveTopicsRepo(options);\n const slug = toKebabCase(options.name);\n if (slug.length === 0) {\n return {\n stdout: \"\",\n stderr: `almanac: topic name \"${options.name}\" has no slug-able characters\\n`,\n exitCode: 1,\n };\n }\n const title = options.name.trim().length > 0 ? options.name.trim() : titleCase(slug);\n\n const workspace = await openFreshTopicsWorkspace(repoRoot);\n try {\n const { repoRoot, yamlPath, file, db } = workspace;\n // Resolve/validate parents BEFORE mutating the file. All-or-nothing.\n const requestedParents = (options.parents ?? [])\n .map((p) => toKebabCase(p))\n .filter((p) => p.length > 0);\n for (const p of requestedParents) {\n if (p === slug) {\n return {\n stdout: \"\",\n stderr: `almanac: topic cannot be its own parent\\n`,\n exitCode: 1,\n };\n }\n if (!topicExists(file, db, p)) {\n return {\n stdout: \"\",\n stderr: `almanac: parent topic \"${p}\" does not exist; create it first with \\`almanac topics create ${p}\\`\\n`,\n exitCode: 1,\n };\n }\n if (findTopic(file, p) === null) {\n // Topic exists only as an ad-hoc DB entry. Promote it into\n // topics.yaml so it has a proper record. `ensureTopic` is\n // idempotent so this is safe even if two loop iterations\n // reference the same ad-hoc parent.\n ensureTopic(file, p);\n }\n }\n\n const existing = findTopic(file, slug);\n if (existing === null) {\n const entry: TopicEntry = {\n slug,\n title,\n description: null,\n parents: requestedParents,\n };\n file.topics.push(entry);\n } else {\n // Add any new parents, skipping ones that already exist or would\n // create a cycle.\n for (const p of requestedParents) {\n if (existing.parents.includes(p)) continue;\n const ancestors = ancestorsInFile(file, p);\n if (ancestors.has(slug) || p === slug) {\n return {\n stdout: \"\",\n stderr: `almanac: adding \"${p}\" as a parent of \"${slug}\" would create a cycle\\n`,\n exitCode: 1,\n };\n }\n existing.parents.push(p);\n }\n // Promote the user-supplied title only if the existing one was a\n // title-cased default (i.e., they didn't describe it yet). Don't\n // clobber a deliberate title silently.\n if (\n existing.title === titleCase(existing.slug) &&\n title !== titleCase(slug) &&\n title !== existing.title\n ) {\n existing.title = title;\n }\n }\n\n await writeTopicsFile(yamlPath, file);\n await runIndexer({ repoRoot });\n return {\n stdout: existing === null\n ? `created topic \"${slug}\"\\n`\n : `updated topic \"${slug}\"\\n`,\n stderr: \"\",\n exitCode: 0,\n };\n } finally {\n closeWorkspace(workspace);\n }\n}\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport fg from \"fast-glob\";\n\nimport {\n applyTopicsTransform,\n rewritePageTopics,\n} from \"../../topics/frontmatter-rewrite.js\";\n\n/**\n * Apply a `topic-list transform` to every `.almanac/pages/*.md` file\n * whose frontmatter contains a relevant topic. Returns the number of\n * files actually changed.\n *\n * We glob page files ourselves (not the DB) so this works even on a\n * stale index — `rename` and `delete` run the indexer AFTER mutation,\n * and we don't want the scan to miss a page that was just modified.\n */\nexport async function rewriteTopicOnPages(\n repoRoot: string,\n transform: (topics: string[]) => string[],\n): Promise<number> {\n const pagesDir = join(repoRoot, \".almanac\", \"pages\");\n const files = await fg(\"**/*.md\", {\n cwd: pagesDir,\n absolute: true,\n onlyFiles: true,\n });\n let changed = 0;\n for (const filePath of files) {\n // Cheap read → in-memory check. Skip files that wouldn't be\n // changed so we don't bump their mtime.\n const raw = await readFile(filePath, \"utf8\");\n const applied = applyTopicsTransform(raw, transform);\n if (!applied.changed) continue;\n await rewritePageTopics(filePath, transform);\n changed += 1;\n }\n return changed;\n}\n","import { runIndexer } from \"../../indexer/index.js\";\nimport { toKebabCase } from \"../../slug.js\";\nimport { writeTopicsFile } from \"../../topics/yaml.js\";\nimport { rewriteTopicOnPages } from \"./page-rewrite.js\";\nimport type { TopicsCommandOutput, TopicsDeleteOptions } from \"./types.js\";\nimport {\n closeWorkspace,\n openFreshTopicsWorkspace,\n resolveTopicsRepo,\n topicExists,\n} from \"./workspace.js\";\n\n/**\n * `almanac topics delete <slug>`. Removes the topic from `topics.yaml`\n * (if present), scrubs any parent edges pointing at it, and untags\n * every page that had it. Pages themselves are left alone — deleting a\n * topic doesn't delete pages, just the relationship.\n */\nexport async function runTopicsDelete(\n options: TopicsDeleteOptions,\n): Promise<TopicsCommandOutput> {\n const repoRoot = await resolveTopicsRepo(options);\n const slug = toKebabCase(options.slug);\n if (slug.length === 0) {\n return { stdout: \"\", stderr: `almanac: empty topic slug\\n`, exitCode: 1 };\n }\n\n const workspace = await openFreshTopicsWorkspace(repoRoot);\n let pagesUpdated: number;\n try {\n const { repoRoot, yamlPath, file, db } = workspace;\n if (!topicExists(file, db, slug)) {\n return {\n stdout: \"\",\n stderr: `almanac: no such topic \"${slug}\"\\n`,\n exitCode: 1,\n };\n }\n\n // Remove the entry and strip it from everyone else's `parents` list.\n file.topics = file.topics.filter((t) => t.slug !== slug);\n for (const t of file.topics) {\n t.parents = t.parents.filter((p) => p !== slug);\n }\n\n // Same write ordering as rename: topics.yaml first (atomic), then\n // pages. A crash between the two leaves topics.yaml already scrubbed\n // and any remaining in-page references become ad-hoc topics — which\n // the reindex will pick up as empty-topics on next health, and the\n // user can re-run to finish untagging.\n await writeTopicsFile(yamlPath, file);\n\n pagesUpdated = await rewriteTopicOnPages(repoRoot, (topics) =>\n topics.filter((t) => t !== slug),\n );\n } finally {\n closeWorkspace(workspace);\n }\n\n await runIndexer({ repoRoot: workspace.repoRoot });\n return {\n stdout: `deleted topic \"${slug}\" (${pagesUpdated} page${pagesUpdated === 1 ? \"\" : \"s\"} untagged)\\n`,\n stderr: \"\",\n exitCode: 0,\n };\n}\n","import { runIndexer } from \"../../indexer/index.js\";\nimport { toKebabCase } from \"../../slug.js\";\nimport { ensureTopic, writeTopicsFile } from \"../../topics/yaml.js\";\nimport type { TopicsCommandOutput, TopicsDescribeOptions } from \"./types.js\";\nimport {\n closeWorkspace,\n openFreshTopicsWorkspace,\n resolveTopicsRepo,\n topicExists,\n} from \"./workspace.js\";\n\n/**\n * `almanac topics describe <slug> \"<text>\"`. Sets or updates the\n * one-liner description. An empty string clears it.\n */\nexport async function runTopicsDescribe(\n options: TopicsDescribeOptions,\n): Promise<TopicsCommandOutput> {\n const repoRoot = await resolveTopicsRepo(options);\n const slug = toKebabCase(options.slug);\n if (slug.length === 0) {\n return { stdout: \"\", stderr: `almanac: empty topic slug\\n`, exitCode: 1 };\n }\n\n const workspace = await openFreshTopicsWorkspace(repoRoot);\n try {\n const { yamlPath, file, db } = workspace;\n if (!topicExists(file, db, slug)) {\n return {\n stdout: \"\",\n stderr: `almanac: no such topic \"${slug}\"\\n`,\n exitCode: 1,\n };\n }\n // `ensureTopic` is idempotent — if the topic was DB-only it\n // promotes into `file`; if already in `file` it returns the\n // existing entry. Either way we get a concrete entry to mutate.\n const entry = ensureTopic(file, slug);\n\n const text = options.description.trim();\n entry.description = text.length === 0 ? null : text;\n\n await writeTopicsFile(yamlPath, file);\n } finally {\n closeWorkspace(workspace);\n }\n\n await runIndexer({ repoRoot: workspace.repoRoot });\n return {\n stdout: `described ${slug}\\n`,\n stderr: \"\",\n exitCode: 0,\n };\n}\n","import { runIndexer } from \"../../indexer/index.js\";\nimport { toKebabCase } from \"../../slug.js\";\nimport { ancestorsInFile } from \"../../topics/dag.js\";\nimport {\n ensureTopic,\n findTopic,\n writeTopicsFile,\n} from \"../../topics/yaml.js\";\nimport type { TopicsCommandOutput, TopicsLinkOptions } from \"./types.js\";\nimport {\n closeWorkspace,\n openFreshTopicsWorkspace,\n resolveTopicsRepo,\n topicExists,\n} from \"./workspace.js\";\n\n/**\n * `almanac topics link <child> <parent>`. Adds a DAG edge after\n * checking that it wouldn't close a cycle. Both topics must exist.\n */\nexport async function runTopicsLink(\n options: TopicsLinkOptions,\n): Promise<TopicsCommandOutput> {\n const repoRoot = await resolveTopicsRepo(options);\n const child = toKebabCase(options.child);\n const parent = toKebabCase(options.parent);\n if (child.length === 0 || parent.length === 0) {\n return { stdout: \"\", stderr: `almanac: empty topic slug\\n`, exitCode: 1 };\n }\n if (child === parent) {\n return {\n stdout: \"\",\n stderr: `almanac: topic cannot be its own parent\\n`,\n exitCode: 1,\n };\n }\n\n const workspace = await openFreshTopicsWorkspace(repoRoot);\n try {\n const { repoRoot, yamlPath, file, db } = workspace;\n for (const slug of [child, parent]) {\n if (!topicExists(file, db, slug)) {\n return {\n stdout: \"\",\n stderr: `almanac: topic \"${slug}\" does not exist\\n`,\n exitCode: 1,\n };\n }\n if (findTopic(file, slug) === null) {\n // DB-only ad-hoc topic → promote it into topics.yaml so the\n // new DAG edge has a concrete home.\n ensureTopic(file, slug);\n }\n }\n\n const childEntry = findTopic(file, child);\n if (childEntry === null) {\n // Shouldn't happen after ensureTopic above — defensive.\n return {\n stdout: \"\",\n stderr: `almanac: topic \"${child}\" not found\\n`,\n exitCode: 1,\n };\n }\n\n if (childEntry.parents.includes(parent)) {\n return {\n stdout: `edge ${child} → ${parent} already exists\\n`,\n stderr: \"\",\n exitCode: 0,\n };\n }\n\n // Cycle check BEFORE mutation. Uses the in-memory file so the check\n // operates on the state we're about to write — no DB round-trip needed.\n const parentAncestors = ancestorsInFile(file, parent);\n if (parentAncestors.has(child) || parent === child) {\n return {\n stdout: \"\",\n stderr: `almanac: adding ${parent} as parent of ${child} would create a cycle\\n`,\n exitCode: 1,\n };\n }\n\n childEntry.parents.push(parent);\n await writeTopicsFile(yamlPath, file);\n await runIndexer({ repoRoot });\n return {\n stdout: `linked ${child} → ${parent}\\n`,\n stderr: \"\",\n exitCode: 0,\n };\n } finally {\n closeWorkspace(workspace);\n }\n}\n","import { BLUE, DIM, RST } from \"../../ansi.js\";\nimport { ensureFreshIndex } from \"../../indexer/index.js\";\nimport { resolveWikiRoot } from \"../../indexer/resolve-wiki.js\";\nimport { openIndex } from \"../../indexer/schema.js\";\nimport { indexDbPath } from \"../../topics/paths.js\";\nimport type { TopicsCommandOutput, TopicsListOptions } from \"./types.js\";\n\n/**\n * `almanac topics` (and `almanac topics list`). Prints one line per\n * known topic — from the DB, which already unions topics.yaml with any\n * ad-hoc slugs found in page frontmatter. Page counts come straight\n * from `page_topics`, which the indexer rebuilt on entry.\n */\nexport async function runTopicsList(\n options: TopicsListOptions,\n): Promise<TopicsCommandOutput> {\n const repoRoot = await resolveWikiRoot({ cwd: options.cwd, wiki: options.wiki });\n await ensureFreshIndex({ repoRoot });\n\n const db = openIndex(indexDbPath(repoRoot));\n try {\n const rows = db\n .prepare<\n [],\n { slug: string; title: string | null; description: string | null; page_count: number }\n >(\n // page_count excludes archived pages — matches the policy used\n // by `topics show` (see `pagesDirectlyTagged`) and by every\n // page-scoped check in `health`. Pick one rule and apply it\n // everywhere; a topic with \"5 pages\" in `topics list` and \"3\n // pages\" in `topics show` is a trust-eroding inconsistency.\n `SELECT t.slug, t.title, t.description,\n (SELECT COUNT(*)\n FROM page_topics pt\n JOIN pages p ON p.slug = pt.page_slug\n WHERE pt.topic_slug = t.slug AND p.archived_at IS NULL\n ) AS page_count\n FROM topics t\n ORDER BY t.slug`,\n )\n .all();\n\n if (options.json === true) {\n return {\n stdout: `${JSON.stringify(rows, null, 2)}\\n`,\n stderr: \"\",\n exitCode: 0,\n };\n }\n\n if (rows.length === 0) {\n return {\n stdout:\n \"no topics. create one with `almanac topics create <name>` or tag a page.\\n\",\n stderr: \"\",\n exitCode: 0,\n };\n }\n\n const slugWidth = rows.reduce((w, r) => Math.max(w, r.slug.length), 0);\n const lines = rows.map((r) => {\n const slug = r.slug.padEnd(slugWidth);\n const count = `(${r.page_count} page${r.page_count === 1 ? \"\" : \"s\"})`;\n return `${BLUE}${slug}${RST} ${DIM}${count}${RST}`;\n });\n return { stdout: `${lines.join(\"\\n\")}\\n`, stderr: \"\", exitCode: 0 };\n } finally {\n db.close();\n }\n}\n","import { runIndexer } from \"../../indexer/index.js\";\nimport { toKebabCase } from \"../../slug.js\";\nimport {\n findTopic,\n titleCase,\n writeTopicsFile,\n} from \"../../topics/yaml.js\";\nimport { rewriteTopicOnPages } from \"./page-rewrite.js\";\nimport type { TopicsCommandOutput, TopicsRenameOptions } from \"./types.js\";\nimport {\n closeWorkspace,\n openFreshTopicsWorkspace,\n resolveTopicsRepo,\n topicExists,\n} from \"./workspace.js\";\n\n/**\n * `almanac topics rename <old> <new>`. Rewrites the slug both in\n * `topics.yaml` (as an entry key and in anyone who declared it as a\n * parent) and in every affected page's frontmatter.\n *\n * Refuses if `<new>` is already a distinct topic — \"merging\" two topics\n * should be explicit, not a silent side effect of a rename.\n */\nexport async function runTopicsRename(\n options: TopicsRenameOptions,\n): Promise<TopicsCommandOutput> {\n const repoRoot = await resolveTopicsRepo(options);\n const oldSlug = toKebabCase(options.oldSlug);\n const newSlug = toKebabCase(options.newSlug);\n if (oldSlug.length === 0 || newSlug.length === 0) {\n return { stdout: \"\", stderr: `almanac: empty topic slug\\n`, exitCode: 1 };\n }\n if (oldSlug === newSlug) {\n return {\n stdout: `topic \"${oldSlug}\" unchanged\\n`,\n stderr: \"\",\n exitCode: 0,\n };\n }\n\n const workspace = await openFreshTopicsWorkspace(repoRoot);\n let pagesUpdated: number;\n try {\n const { repoRoot, yamlPath, file, db } = workspace;\n // Fetch existence info. `oldInYaml` is kept as a direct reference\n // because we mutate the entry; the DB check is only needed when\n // the slug isn't in the file (ad-hoc-only).\n const oldInYaml = findTopic(file, oldSlug);\n if (!topicExists(file, db, oldSlug)) {\n return {\n stdout: \"\",\n stderr: `almanac: no such topic \"${oldSlug}\"\\n`,\n exitCode: 1,\n };\n }\n\n if (topicExists(file, db, newSlug)) {\n return {\n stdout: \"\",\n stderr: `almanac: topic \"${newSlug}\" already exists; delete it first if you intend to merge\\n`,\n exitCode: 1,\n };\n }\n\n // Rewrite `topics.yaml`: the entry itself (if present) plus any\n // parent reference to `oldSlug`.\n if (oldInYaml !== null) {\n oldInYaml.slug = newSlug;\n if (oldInYaml.title === titleCase(oldSlug)) {\n // Title was the auto-generated default — refresh it to the new\n // slug's title-case. A custom title stays as-is.\n oldInYaml.title = titleCase(newSlug);\n }\n }\n for (const t of file.topics) {\n t.parents = t.parents.map((p) => (p === oldSlug ? newSlug : p));\n }\n\n // Write ordering matters: topics.yaml FIRST (atomic tmp+rename), THEN\n // the page rewrites. If topics.yaml write fails, no page was touched.\n // If a page rewrite fails midway, topics.yaml already reflects the\n // rename so the next reindex picks up the ad-hoc state and the user\n // can re-run to finish the remaining pages. The opposite ordering\n // would leave half-rewritten pages referencing a slug that\n // topics.yaml doesn't know about.\n await writeTopicsFile(yamlPath, file);\n\n // Rewrite every page that has `oldSlug` in `topics:` frontmatter.\n pagesUpdated = await rewriteTopicOnPages(repoRoot, (topics) =>\n topics.map((t) => (t === oldSlug ? newSlug : t)),\n );\n } finally {\n closeWorkspace(workspace);\n }\n\n await runIndexer({ repoRoot: workspace.repoRoot });\n return {\n stdout: `renamed ${oldSlug} → ${newSlug} (${pagesUpdated} page${pagesUpdated === 1 ? \"\" : \"s\"} updated)\\n`,\n stderr: \"\",\n exitCode: 0,\n };\n}\n","import type Database from \"better-sqlite3\";\n\nimport { BLUE, DIM, RST } from \"../../ansi.js\";\nimport { descendantsInDb } from \"../../topics/dag.js\";\nimport { titleCase } from \"../../topics/yaml.js\";\n\nexport interface TopicsShowRecord {\n slug: string;\n title: string | null;\n description: string | null;\n parents: string[];\n children: string[];\n pages: string[];\n descendants_used?: boolean;\n}\n\nexport function pagesDirectlyTagged(\n db: Database.Database,\n slug: string,\n): string[] {\n return db\n .prepare<[string], { page_slug: string }>(\n `SELECT pt.page_slug\n FROM page_topics pt\n JOIN pages p ON p.slug = pt.page_slug\n WHERE pt.topic_slug = ? AND p.archived_at IS NULL\n ORDER BY pt.page_slug`,\n )\n .all(slug)\n .map((r) => r.page_slug);\n}\n\nexport function pagesForSubtree(\n db: Database.Database,\n slug: string,\n): string[] {\n const slugs = [slug, ...descendantsInDb(db, slug)];\n // Deduplicate + preserve order via a Set — a page can belong to\n // multiple topics in the subtree and we only want one row per page.\n const placeholders = slugs.map(() => \"?\").join(\", \");\n const rows = db\n .prepare<unknown[], { page_slug: string }>(\n `SELECT DISTINCT pt.page_slug\n FROM page_topics pt\n JOIN pages p ON p.slug = pt.page_slug\n WHERE pt.topic_slug IN (${placeholders}) AND p.archived_at IS NULL\n ORDER BY pt.page_slug`,\n )\n .all(...slugs);\n return rows.map((r) => r.page_slug);\n}\n\nexport function formatShow(r: TopicsShowRecord): string {\n const lines: string[] = [];\n lines.push(`${DIM}slug:${RST} ${BLUE}${r.slug}${RST}`);\n lines.push(`${DIM}title:${RST} ${r.title ?? titleCase(r.slug)}`);\n lines.push(`${DIM}description:${RST} ${r.description ?? \"—\"}`);\n lines.push(\n `${DIM}parents:${RST} ${r.parents.length > 0 ? r.parents.join(\", \") : \"—\"}`,\n );\n lines.push(\n `${DIM}children:${RST} ${r.children.length > 0 ? r.children.join(\", \") : \"—\"}`,\n );\n const pagesLabel = r.descendants_used === true\n ? \"pages (incl. descendants)\"\n : \"pages\";\n lines.push(`${DIM}${pagesLabel}:${RST}`);\n if (r.pages.length === 0) {\n lines.push(\" —\");\n } else {\n for (const p of r.pages) lines.push(` ${BLUE}${p}${RST}`);\n }\n return `${lines.join(\"\\n\")}\\n`;\n}\n","import { ensureFreshIndex } from \"../../indexer/index.js\";\nimport { resolveWikiRoot } from \"../../indexer/resolve-wiki.js\";\nimport { openIndex } from \"../../indexer/schema.js\";\nimport { toKebabCase } from \"../../slug.js\";\nimport { indexDbPath } from \"../../topics/paths.js\";\nimport {\n formatShow,\n pagesDirectlyTagged,\n pagesForSubtree,\n type TopicsShowRecord,\n} from \"./read.js\";\nimport type { TopicsCommandOutput, TopicsShowOptions } from \"./types.js\";\n\n/**\n * `almanac topics show <slug>`. Prints metadata + parents, children,\n * and the page list. `--descendants` widens the page list to include\n * pages tagged with any descendant topic (via the DAG).\n */\nexport async function runTopicsShow(\n options: TopicsShowOptions,\n): Promise<TopicsCommandOutput> {\n const repoRoot = await resolveWikiRoot({ cwd: options.cwd, wiki: options.wiki });\n await ensureFreshIndex({ repoRoot });\n\n const slug = toKebabCase(options.slug);\n if (slug.length === 0) {\n return {\n stdout: \"\",\n stderr: `almanac: empty topic slug\\n`,\n exitCode: 1,\n };\n }\n\n const db = openIndex(indexDbPath(repoRoot));\n try {\n const row = db\n .prepare<\n [string],\n { slug: string; title: string | null; description: string | null }\n >(\"SELECT slug, title, description FROM topics WHERE slug = ?\")\n .get(slug);\n if (row === undefined) {\n return {\n stdout: \"\",\n stderr: `almanac: no such topic \"${slug}\"\\n`,\n exitCode: 1,\n };\n }\n\n const parents = db\n .prepare<[string], { parent_slug: string }>(\n \"SELECT parent_slug FROM topic_parents WHERE child_slug = ? ORDER BY parent_slug\",\n )\n .all(slug)\n .map((r) => r.parent_slug);\n\n const children = db\n .prepare<[string], { child_slug: string }>(\n \"SELECT child_slug FROM topic_parents WHERE parent_slug = ? ORDER BY child_slug\",\n )\n .all(slug)\n .map((r) => r.child_slug);\n\n const pageSlugs = options.descendants === true\n ? pagesForSubtree(db, slug)\n : pagesDirectlyTagged(db, slug);\n\n const record: TopicsShowRecord = {\n slug: row.slug,\n title: row.title,\n description: row.description,\n parents,\n children,\n pages: pageSlugs,\n descendants_used: options.descendants === true,\n };\n\n if (options.json === true) {\n return {\n stdout: `${JSON.stringify(record, null, 2)}\\n`,\n stderr: \"\",\n exitCode: 0,\n };\n }\n return { stdout: formatShow(record), stderr: \"\", exitCode: 0 };\n } finally {\n db.close();\n }\n}\n","import { runIndexer } from \"../../indexer/index.js\";\nimport { resolveWikiRoot } from \"../../indexer/resolve-wiki.js\";\nimport { toKebabCase } from \"../../slug.js\";\nimport { topicsYamlPath } from \"../../topics/paths.js\";\nimport {\n findTopic,\n loadTopicsFile,\n writeTopicsFile,\n} from \"../../topics/yaml.js\";\nimport type { TopicsCommandOutput, TopicsUnlinkOptions } from \"./types.js\";\n\n/**\n * `almanac topics unlink <child> <parent>`. Removes a DAG edge if it\n * exists. No-op (exit 0) if not. Never deletes topics.\n */\nexport async function runTopicsUnlink(\n options: TopicsUnlinkOptions,\n): Promise<TopicsCommandOutput> {\n const repoRoot = await resolveWikiRoot({ cwd: options.cwd, wiki: options.wiki });\n const child = toKebabCase(options.child);\n const parent = toKebabCase(options.parent);\n if (child.length === 0 || parent.length === 0) {\n return { stdout: \"\", stderr: `almanac: empty topic slug\\n`, exitCode: 1 };\n }\n const yamlPath = topicsYamlPath(repoRoot);\n const file = await loadTopicsFile(yamlPath);\n const childEntry = findTopic(file, child);\n if (childEntry === null || !childEntry.parents.includes(parent)) {\n return {\n stdout: `no edge ${child} → ${parent}\\n`,\n stderr: \"\",\n exitCode: 0,\n };\n }\n childEntry.parents = childEntry.parents.filter((p) => p !== parent);\n await writeTopicsFile(yamlPath, file);\n await runIndexer({ repoRoot });\n return {\n stdout: `unlinked ${child} → ${parent}\\n`,\n stderr: \"\",\n exitCode: 0,\n };\n}\n","import { existsSync } from \"node:fs\";\nimport { basename } from \"node:path\";\n\nimport { findNearestAlmanacDir } from \"../paths.js\";\nimport { toKebabCase } from \"../slug.js\";\nimport {\n addEntry,\n readRegistry,\n type RegistryEntry,\n} from \"./index.js\";\n\n/**\n * If the current working directory (or any parent) has a `.almanac/` that\n * isn't in the registry, silently add it. Runs as a side effect of every\n * command except `init` (which does its own registration) and `list --drop`\n * (which shouldn't resurrect the entry the user just removed).\n *\n * The contract is \"silent\" for environmental problems — missing home dir,\n * unreadable registry file, permission errors. Those shouldn't block the\n * real command from running. But a **malformed** registry IS surfaced: if\n * the JSON is corrupt, the user needs to know, not have auto-register\n * quietly pretend the registry was empty and start overwriting entries.\n */\nexport async function autoRegisterIfNeeded(\n cwd: string,\n): Promise<RegistryEntry | null> {\n try {\n const repoRoot = findNearestAlmanacDir(cwd);\n if (repoRoot === null) return null;\n\n // Double-check the directory still exists — `findNearestAlmanacDir`\n // already confirms this, but we're explicit about the precondition.\n if (!existsSync(repoRoot)) return null;\n\n // Read the registry ONCE. `resolveNameCollision` scans this snapshot\n // in memory; re-reading per iteration would be O(N²) in collision\n // count and needlessly hit the filesystem.\n const entries = await readRegistry();\n\n const existing = entries.find((e) => samePath(e.path, repoRoot));\n if (existing !== undefined) return existing;\n\n // Derive a kebab-case name from the directory. If the dir name is\n // somehow empty (e.g. repo is at filesystem root), skip — we don't\n // want to register a nameless entry.\n const name = toKebabCase(basename(repoRoot));\n if (name.length === 0) return null;\n\n // Resolve collisions on name by falling back to a disambiguated form.\n // Auto-registration should never overwrite an existing named entry\n // that points elsewhere.\n const finalName = resolveNameCollision(entries, name, repoRoot);\n if (finalName === null) return null;\n\n const entry: RegistryEntry = {\n name: finalName,\n description: \"\",\n path: repoRoot,\n registered_at: new Date().toISOString(),\n };\n await addEntry(entry);\n return entry;\n } catch (err: unknown) {\n // Only swallow errors that mean \"registry state isn't readable right\n // now\" — everything else (malformed JSON, programmer errors, bugs)\n // should propagate so the user can see it.\n if (\n err instanceof Error &&\n \"code\" in err &&\n (err.code === \"ENOENT\" ||\n err.code === \"EACCES\" ||\n err.code === \"EPERM\")\n ) {\n return null;\n }\n throw err;\n }\n}\n\n/**\n * If another repo already claims `name`, append `-2`, `-3`, ... until we\n * find an unused slug. Only relevant for auto-registration — `init` with\n * `--name` lets the user resolve collisions explicitly.\n *\n * Takes a snapshot of registry entries instead of re-reading the file per\n * iteration. Caps at 1000 attempts to prevent pathological loops if the\n * registry somehow contains every suffix (it can't, but we'd rather fail\n * explicitly than spin).\n */\nfunction resolveNameCollision(\n entries: RegistryEntry[],\n baseName: string,\n repoPath: string,\n): string | null {\n const owner = entries.find((e) => e.name === baseName);\n if (owner === undefined || samePath(owner.path, repoPath)) {\n return baseName;\n }\n const taken = new Set(entries.map((e) => e.name));\n const MAX_ATTEMPTS = 1000;\n for (let suffix = 2; suffix < MAX_ATTEMPTS + 2; suffix += 1) {\n const candidate = `${baseName}-${suffix}`;\n if (!taken.has(candidate)) return candidate;\n }\n return null;\n}\n\n/**\n * Mirror `pathsEqual` in `registry/index.ts` — case-insensitive on\n * macOS/Windows, case-sensitive on Linux. Duplicated here rather than\n * exported to keep the registry module's public surface small.\n */\nfunction samePath(a: string, b: string): boolean {\n if (process.platform === \"darwin\" || process.platform === \"win32\") {\n return a.toLowerCase() === b.toLowerCase();\n }\n return a === b;\n}\n","import { Command } from \"commander\";\n\nimport { runTag, runUntag } from \"../commands/tag.js\";\nimport {\n runTopicsCreate,\n runTopicsDelete,\n runTopicsDescribe,\n runTopicsLink,\n runTopicsList,\n runTopicsRename,\n runTopicsShow,\n runTopicsUnlink,\n} from \"../commands/topics.js\";\nimport { autoRegisterIfNeeded } from \"../registry/autoregister.js\";\nimport { collectOption, emit, readStdin } from \"./helpers.js\";\n\nexport function registerEditCommands(program: Command): void {\n program\n .command(\"tag [page] [topics...]\")\n .description(\"add topics to a page (auto-creates missing topics)\")\n .option(\"--stdin\", \"read page slugs from stdin (one per line)\")\n .option(\"--wiki <name>\", \"target a specific registered wiki\")\n .action(\n async (\n page: string | undefined,\n topicsArg: string[],\n opts: { stdin?: boolean; wiki?: string },\n ) => {\n await autoRegisterIfNeeded(process.cwd());\n const resolvedTopics = opts.stdin === true\n ? [page, ...topicsArg].filter(\n (t): t is string => typeof t === \"string\" && t.length > 0,\n )\n : topicsArg;\n const result = await runTag({\n cwd: process.cwd(),\n page: opts.stdin === true ? undefined : page,\n topics: resolvedTopics,\n stdin: opts.stdin,\n stdinInput: opts.stdin === true ? await readStdin() : undefined,\n wiki: opts.wiki,\n });\n emit(result);\n },\n );\n\n program\n .command(\"untag <page> <topic>\")\n .description(\"remove a topic from a page's frontmatter\")\n .option(\"--wiki <name>\", \"target a specific registered wiki\")\n .action(\n async (page: string, topic: string, opts: { wiki?: string }) => {\n await autoRegisterIfNeeded(process.cwd());\n const result = await runUntag({\n cwd: process.cwd(),\n page,\n topic,\n wiki: opts.wiki,\n });\n emit(result);\n },\n );\n\n const topics = program\n .command(\"topics\")\n .description(\"manage the topic DAG\");\n\n topics\n .command(\"list\", { isDefault: true })\n .description(\"list all topics with page counts\")\n .option(\"--wiki <name>\", \"target a specific registered wiki\")\n .option(\"--json\", \"emit structured JSON\")\n .action(async (opts: { wiki?: string; json?: boolean }) => {\n await autoRegisterIfNeeded(process.cwd());\n const result = await runTopicsList({\n cwd: process.cwd(),\n wiki: opts.wiki,\n json: opts.json,\n });\n emit(result);\n });\n\n topics\n .command(\"show <slug>\")\n .description(\"print a topic's metadata, parents, children, and pages\")\n .option(\"--descendants\", \"include pages tagged with descendant topics\")\n .option(\"--wiki <name>\", \"target a specific registered wiki\")\n .option(\"--json\", \"emit structured JSON\")\n .action(\n async (\n slug: string,\n opts: { descendants?: boolean; wiki?: string; json?: boolean },\n ) => {\n await autoRegisterIfNeeded(process.cwd());\n const result = await runTopicsShow({\n cwd: process.cwd(),\n slug,\n descendants: opts.descendants,\n wiki: opts.wiki,\n json: opts.json,\n });\n emit(result);\n },\n );\n\n topics\n .command(\"create <name>\")\n .description(\"create a topic (rejects if --parent slug does not exist)\")\n .option(\"--parent <slug>\", \"parent topic slug (repeat for multiple parents)\", collectOption, [] as string[])\n .option(\"--wiki <name>\", \"target a specific registered wiki\")\n .action(\n async (name: string, opts: { parent?: string[]; wiki?: string }) => {\n await autoRegisterIfNeeded(process.cwd());\n const result = await runTopicsCreate({\n cwd: process.cwd(),\n name,\n parents: opts.parent,\n wiki: opts.wiki,\n });\n emit(result);\n },\n );\n\n topics\n .command(\"link <child> <parent>\")\n .description(\"add a DAG edge (cycle-checked)\")\n .option(\"--wiki <name>\", \"target a specific registered wiki\")\n .action(async (child: string, parent: string, opts: { wiki?: string }) => {\n await autoRegisterIfNeeded(process.cwd());\n const result = await runTopicsLink({\n cwd: process.cwd(),\n child,\n parent,\n wiki: opts.wiki,\n });\n emit(result);\n });\n\n topics\n .command(\"unlink <child> <parent>\")\n .description(\"remove a DAG edge\")\n .option(\"--wiki <name>\", \"target a specific registered wiki\")\n .action(async (child: string, parent: string, opts: { wiki?: string }) => {\n await autoRegisterIfNeeded(process.cwd());\n const result = await runTopicsUnlink({\n cwd: process.cwd(),\n child,\n parent,\n wiki: opts.wiki,\n });\n emit(result);\n });\n\n topics\n .command(\"rename <old> <new>\")\n .description(\"rename a topic; rewrites every affected page's frontmatter\")\n .option(\"--wiki <name>\", \"target a specific registered wiki\")\n .action(async (oldSlug: string, newSlug: string, opts: { wiki?: string }) => {\n await autoRegisterIfNeeded(process.cwd());\n const result = await runTopicsRename({\n cwd: process.cwd(),\n oldSlug,\n newSlug,\n wiki: opts.wiki,\n });\n emit(result);\n });\n\n topics\n .command(\"delete <slug>\")\n .description(\"delete a topic; untags every affected page\")\n .option(\"--wiki <name>\", \"target a specific registered wiki\")\n .action(async (slug: string, opts: { wiki?: string }) => {\n await autoRegisterIfNeeded(process.cwd());\n const result = await runTopicsDelete({\n cwd: process.cwd(),\n slug,\n wiki: opts.wiki,\n });\n emit(result);\n });\n\n topics\n .command(\"describe <slug> <text>\")\n .description(\"set a topic's one-line description\")\n .option(\"--wiki <name>\", \"target a specific registered wiki\")\n .action(async (slug: string, text: string, opts: { wiki?: string }) => {\n await autoRegisterIfNeeded(process.cwd());\n const result = await runTopicsDescribe({\n cwd: process.cwd(),\n slug,\n description: text,\n wiki: opts.wiki,\n });\n emit(result);\n });\n}\n","import { existsSync } from \"node:fs\";\n\nimport { BLUE, BOLD, DIM, RST } from \"../ansi.js\";\nimport {\n dropEntry,\n readRegistry,\n type RegistryEntry,\n} from \"../registry/index.js\";\n\nexport interface ListOptions {\n json?: boolean;\n drop?: string;\n}\n\nexport interface ListCommandOutput {\n stdout: string;\n exitCode: number;\n}\n\n/**\n * `almanac list` — the global discovery surface. Three modes:\n *\n * - default: pretty table of reachable wikis\n * - `--json`: structured output (reachable wikis only, by default)\n * - `--drop <name>`: explicit removal, then exits\n *\n * **Unreachable paths are silently skipped in the default output**, but\n * never auto-dropped. This is the registry hygiene rule from the design —\n * branch switches, unmounted drives, and VM-offline repos should not cost\n * the user a registration. Only `--drop` removes entries.\n */\nexport async function listWikis(\n options: ListOptions,\n): Promise<ListCommandOutput> {\n if (options.drop !== undefined) {\n return handleDrop(options.drop);\n }\n\n const entries = await readRegistry();\n const reachable = entries.filter((e) => isReachable(e));\n\n if (options.json === true) {\n return { stdout: `${JSON.stringify(reachable, null, 2)}\\n`, exitCode: 0 };\n }\n\n return { stdout: formatPretty(reachable), exitCode: 0 };\n}\n\nasync function handleDrop(name: string): Promise<ListCommandOutput> {\n const removed = await dropEntry(name);\n if (removed === null) {\n return {\n stdout: `no registry entry named \"${name}\"\\n`,\n exitCode: 1,\n };\n }\n return {\n stdout: `removed \"${removed.name}\" (${removed.path})\\n`,\n exitCode: 0,\n };\n}\n\n/**\n * A registry path is \"reachable\" if something still exists at that path.\n * We use `existsSync` rather than `stat` — we don't care whether the path\n * is a directory or has a `.almanac/` inside; we only hide it from default\n * output when the path itself is gone (e.g., repo deleted, drive\n * unmounted).\n */\nfunction isReachable(entry: RegistryEntry): boolean {\n if (entry.path.length === 0) return false;\n return existsSync(entry.path);\n}\n\n/**\n * Human-readable listing. Empty state prints a gentle hint rather than a\n * blank screen, and entries render in registration order (chronological,\n * since `addEntry` appends).\n */\nfunction formatPretty(entries: RegistryEntry[]): string {\n if (entries.length === 0) {\n return `${DIM}no wikis registered. run \\`almanac bootstrap\\` in a repo to create one.${RST}\\n`;\n }\n\n // Column-width the name for alignment; cap at 30 so absurd names don't\n // stretch the whole table.\n const nameWidth = Math.min(\n 30,\n entries.reduce((w, e) => Math.max(w, e.name.length), 0),\n );\n\n const lines: string[] = [];\n for (const entry of entries) {\n const name = entry.name.padEnd(nameWidth);\n const desc = entry.description.length > 0 ? entry.description : \"—\";\n lines.push(`${BLUE}${BOLD}${name}${RST} ${desc}`);\n lines.push(`${\" \".repeat(nameWidth)} ${DIM}${entry.path}${RST}`);\n }\n return `${lines.join(\"\\n\")}\\n`;\n}\n","import { join } from \"node:path\";\n\nimport type Database from \"better-sqlite3\";\n\nimport { BLUE, RST } from \"../ansi.js\";\nimport { parseDuration } from \"../indexer/duration.js\";\nimport { ensureFreshIndex } from \"../indexer/index.js\";\nimport { looksLikeDir, normalizePath } from \"../indexer/paths.js\";\nimport { resolveWikiRoot } from \"../indexer/resolve-wiki.js\";\nimport { openIndex } from \"../indexer/schema.js\";\n\nexport interface SearchOptions {\n cwd: string;\n query?: string;\n topics: string[];\n mentions?: string;\n since?: string;\n stale?: string;\n orphan?: boolean;\n includeArchive?: boolean;\n archived?: boolean;\n wiki?: string;\n json?: boolean;\n limit?: number;\n}\n\nexport interface SearchResult {\n slug: string;\n title: string | null;\n updated_at: number;\n archived_at: number | null;\n superseded_by: string | null;\n topics: string[];\n}\n\nexport interface SearchCommandOutput {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\n/**\n * `almanac search` — the core query surface.\n *\n * Filters compose with AND logic. The implementation is deliberately\n * pedestrian: build a list of clauses + params, join them with `AND`,\n * intersect topic filters by requiring one subquery per `--topic`. No\n * clever query-planner tricks needed for the sizes we handle (<10k pages).\n *\n * All output ordering is stable: `updated_at DESC, slug ASC`. FTS5 rank\n * is layered on top when the user passed a text query — we ORDER BY rank\n * first, then fall through to the default.\n */\nexport async function runSearch(\n options: SearchOptions,\n): Promise<SearchCommandOutput> {\n const repoRoot = await resolveWikiRoot({\n cwd: options.cwd,\n wiki: options.wiki,\n });\n await ensureFreshIndex({ repoRoot });\n\n const dbPath = join(repoRoot, \".almanac\", \"index.db\");\n const db = openIndex(dbPath);\n\n try {\n const rows = executeQuery(db, options);\n const limited =\n options.limit !== undefined && options.limit >= 0\n ? rows.slice(0, options.limit)\n : rows;\n\n const stdout = formatResults(limited, options);\n const stderr = buildStderr(limited, options);\n return { stdout, stderr, exitCode: 0 };\n } finally {\n db.close();\n }\n}\n\ninterface PageRow {\n slug: string;\n title: string | null;\n updated_at: number;\n archived_at: number | null;\n superseded_by: string | null;\n}\n\nfunction executeQuery(\n db: Database.Database,\n options: SearchOptions,\n): SearchResult[] {\n const whereClauses: string[] = [];\n const params: (string | number)[] = [];\n\n // Archive scope. Three modes, mutually exclusive in practice:\n // - default → active only\n // - --include-archive → active + archived\n // - --archived → archived only\n // `--archived` wins over `--include-archive` when both are passed —\n // being explicit about \"only archived\" is strictly narrower than\n // \"include archived\", so intersecting them yields \"only archived\".\n if (options.archived === true) {\n whereClauses.push(\"p.archived_at IS NOT NULL\");\n } else if (options.includeArchive !== true) {\n whereClauses.push(\"p.archived_at IS NULL\");\n }\n\n // --topic foo --topic bar → page must have BOTH. We add one EXISTS\n // subquery per topic rather than grouping, which keeps param order\n // simple and the plan readable.\n for (const rawTopic of options.topics) {\n const topicSlug = slugForTopic(rawTopic);\n if (topicSlug.length === 0) continue;\n whereClauses.push(\n \"EXISTS (SELECT 1 FROM page_topics pt WHERE pt.page_slug = p.slug AND pt.topic_slug = ?)\",\n );\n params.push(topicSlug);\n }\n\n // --mentions: look for a file_refs row on this page that either\n // matches exactly, OR is a containing folder (is_dir=1 and the query\n // path starts with the row's path), OR the row itself lives inside\n // the queried folder. See spec → \"Graph querying → Query examples\".\n //\n // We deliberately avoid GLOB on the RHS of the comparison with stored\n // `r.path`, because stored paths can legitimately contain GLOB\n // metacharacters — Next.js dynamic routes like `src/[id]/page.tsx`\n // store a literal `[id]`, and `[abc]` is a SQL GLOB character class.\n // Concatenating `r.path || '*'` into a GLOB pattern would make\n // `src/[id]/page.tsx*` match `src/i/page.tsx` (spurious hit on the\n // character class).\n //\n // Instead we enumerate the prefix folders in JS and use parameterized\n // equality. For `src/checkout/handler.ts` the prefixes are\n // `['src/', 'src/checkout/']`; any file_refs row with is_dir=1 and\n // a path in that list is a containing folder of the queried file.\n // This also lets SQLite use `idx_file_refs_path` as an equality\n // probe rather than a range scan.\n if (options.mentions !== undefined && options.mentions.length > 0) {\n const isDir = looksLikeDir(options.mentions);\n const norm = normalizePath(options.mentions, isDir);\n if (isDir) {\n // Query is a folder. Match: the exact folder, OR any file/sub-\n // folder whose path starts with the folder prefix. The prefix\n // match is the one place we still need GLOB — but we escape any\n // wildcard metacharacters in `norm` first so a user-supplied\n // `src/[id]/` query is treated as a literal. Note: the query\n // path comes from the caller, not from stored data, but a user\n // typing `--mentions src/[id]/` should get the literal folder,\n // not a character class.\n const escaped = escapeGlobMeta(norm);\n whereClauses.push(\n `EXISTS (\n SELECT 1 FROM file_refs r\n WHERE r.page_slug = p.slug\n AND (r.path = ? OR r.path GLOB ?)\n )`,\n );\n params.push(norm, `${escaped}*`);\n } else {\n // Query is a file. Match: the exact file, OR any folder whose\n // path is a prefix of this file. Build the prefix list in JS and\n // probe file_refs with equality — no GLOB on stored values.\n const prefixes = parentFolderPrefixes(norm);\n if (prefixes.length === 0) {\n whereClauses.push(\n `EXISTS (\n SELECT 1 FROM file_refs r\n WHERE r.page_slug = p.slug AND r.path = ?\n )`,\n );\n params.push(norm);\n } else {\n const placeholders = prefixes.map(() => \"?\").join(\", \");\n whereClauses.push(\n `EXISTS (\n SELECT 1 FROM file_refs r\n WHERE r.page_slug = p.slug\n AND (\n r.path = ?\n OR (r.is_dir = 1 AND r.path IN (${placeholders}))\n )\n )`,\n );\n params.push(norm, ...prefixes);\n }\n }\n }\n\n const now = Math.floor(Date.now() / 1000);\n\n if (options.since !== undefined) {\n const seconds = parseDuration(options.since);\n whereClauses.push(\"p.updated_at >= ?\");\n params.push(now - seconds);\n }\n\n if (options.stale !== undefined) {\n const seconds = parseDuration(options.stale);\n whereClauses.push(\"p.updated_at < ?\");\n params.push(now - seconds);\n }\n\n if (options.orphan === true) {\n whereClauses.push(\n \"NOT EXISTS (SELECT 1 FROM page_topics pt WHERE pt.page_slug = p.slug)\",\n );\n }\n\n // FTS5 text query. When a text query is supplied we JOIN against\n // `fts_pages` so we can ORDER BY its `rank` column — lower (more\n // negative) ranks are better matches. Without a query we skip the\n // join entirely.\n let sql: string;\n if (options.query !== undefined && options.query.trim().length > 0) {\n const ftsExpr = buildFtsQuery(options.query);\n sql = `\n SELECT p.slug, p.title, p.updated_at, p.archived_at, p.superseded_by\n FROM pages p\n JOIN fts_pages f ON f.slug = p.slug\n WHERE fts_pages MATCH ?\n ${whereClauses.length > 0 ? `AND ${whereClauses.join(\" AND \")}` : \"\"}\n ORDER BY f.rank ASC, p.updated_at DESC, p.slug ASC\n `;\n // MATCH param goes first (it's the first `?` in the compiled SQL).\n params.unshift(ftsExpr);\n } else {\n sql = buildSql(whereClauses);\n }\n\n const rows = db.prepare<unknown[], PageRow>(sql).all(...params);\n\n // Attach topics in a second pass — simpler than a correlated\n // `GROUP_CONCAT`, and the output rows are small enough that N+1 on a\n // single prepared statement is fine.\n const topicStmt = db.prepare<[string], { topic_slug: string }>(\n \"SELECT topic_slug FROM page_topics WHERE page_slug = ? ORDER BY topic_slug\",\n );\n const out: SearchResult[] = rows.map((row) => ({\n slug: row.slug,\n title: row.title,\n updated_at: row.updated_at,\n archived_at: row.archived_at,\n superseded_by: row.superseded_by,\n topics: topicStmt.all(row.slug).map((t) => t.topic_slug),\n }));\n\n return out;\n}\n\nfunction buildSql(whereClauses: string[]): string {\n const where =\n whereClauses.length > 0 ? `WHERE ${whereClauses.join(\" AND \")}` : \"\";\n return `\n SELECT p.slug, p.title, p.updated_at, p.archived_at, p.superseded_by\n FROM pages p\n ${where}\n ORDER BY p.updated_at DESC, p.slug ASC\n `;\n}\n\n/**\n * Turn a user query into an FTS5 MATCH expression. FTS5's default\n * grammar gets unhappy with punctuation (hyphens, colons, slashes), so\n * we tokenize into alphanumeric runs and emit a conjunction of prefixed\n * tokens. Each token is suffixed with `*` so \"stri\" matches \"stripe\".\n *\n * Quoted input (`\"stripe webhook\"`) is treated as an FTS5 phrase query\n * — tokens must appear contiguously in that order. This matches shell\n * conventions where quoting something means \"match it literally\". We\n * strip the surrounding quotes, collapse inner punctuation to spaces,\n * and re-wrap in quotes for FTS5's phrase syntax. Any embedded `\"` in\n * the user input is dropped (FTS5 phrase syntax has no escape).\n *\n * Anything that tokenizes to empty (e.g. pure punctuation) falls back\n * to an empty MATCH, which yields no rows — which is the right answer.\n */\nfunction buildFtsQuery(raw: string): string {\n const trimmed = raw.trim();\n if (\n trimmed.length >= 2 &&\n trimmed.startsWith(\"\\\"\") &&\n trimmed.endsWith(\"\\\"\")\n ) {\n // Phrase mode. Strip outer quotes, lowercase, collapse non-alnum\n // runs to a single space, trim. Any surviving inner `\"` are\n // removed since FTS5 phrase syntax has no escape mechanism.\n const inner = trimmed\n .slice(1, -1)\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \" \")\n .trim();\n if (inner.length === 0) return \"\\\"\\\"\";\n return `\"${inner}\"`;\n }\n const tokens = trimmed\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter((t) => t.length > 0);\n if (tokens.length === 0) return \"\\\"\\\"\";\n return tokens.map((t) => `${t}*`).join(\" AND \");\n}\n\nfunction slugForTopic(raw: string): string {\n return raw\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\n/**\n * For a normalized file path like `src/checkout/handler.ts`, enumerate\n * every containing folder in the form those folders are stored (trailing\n * slash): `['src/', 'src/checkout/']`. Empty for paths with no folder\n * separator (e.g. `README.md`) — a top-level file has no parent folder\n * recorded in `file_refs`.\n *\n * Used by the `--mentions <file>` path to let us probe `file_refs` with\n * equality instead of GLOB, sidestepping wildcard-escape bugs entirely.\n */\nfunction parentFolderPrefixes(filePath: string): string[] {\n const out: string[] = [];\n let cursor = 0;\n while (true) {\n const next = filePath.indexOf(\"/\", cursor);\n if (next === -1) break;\n // Slice includes the slash — matches how directories are stored.\n out.push(filePath.slice(0, next + 1));\n cursor = next + 1;\n }\n return out;\n}\n\n/**\n * Escape SQLite GLOB metacharacters (`*`, `?`, `[`) by wrapping each in\n * a single-character class. SQLite GLOB has no backslash-escape, so the\n * idiomatic trick is `[*]` → literal `*`, `[?]` → literal `?`, `[[]` →\n * literal `[`. `]` doesn't need escaping outside a class.\n *\n * We only need this on the *query* side — stored paths aren't\n * concatenated into GLOB patterns anymore (see --mentions handling).\n * But a user-typed `--mentions src/[id]/` should still match a stored\n * `src/[id]/` literally, not as a character class over `i` or `d`.\n */\nfunction escapeGlobMeta(s: string): string {\n return s.replace(/[\\*\\?\\[]/g, (ch) => `[${ch}]`);\n}\n\nfunction formatResults(\n rows: SearchResult[],\n options: SearchOptions,\n): string {\n if (options.json === true) {\n return `${JSON.stringify(rows, null, 2)}\\n`;\n }\n // Default output: one slug per line. Empty result = empty output (not\n // \"no results found\") — makes piping into xargs / subsequent commands\n // degrade gracefully.\n if (rows.length === 0) return \"\";\n return `${rows.map((r) => `${BLUE}${r.slug}${RST}`).join(\"\\n\")}\\n`;\n}\n\nfunction buildStderr(rows: SearchResult[], options: SearchOptions): string {\n // Spec: \"print warns if >50 when not --json\". The warning goes to\n // stderr so it doesn't corrupt pipelines that filter stdout.\n if (options.json === true) return \"\";\n // Empty-result breadcrumb (v0.1.3). Interviews showed users saw blank\n // stdout and concluded the wiki was broken rather than the query\n // genuinely matched nothing. A single `# 0 results` line to stderr\n // makes the outcome legible without corrupting stdout pipelines (the\n // downstream command still sees zero lines). `--json` mode is silent\n // because `[]` is the unambiguous empty signal there.\n if (rows.length === 0) {\n return \"# 0 results\\n\";\n }\n if (options.limit !== undefined) return \"\";\n if (rows.length > 50) {\n return `almanac: ${rows.length} results — consider --limit or a narrower query\\n`;\n }\n return \"\";\n}\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport type Database from \"better-sqlite3\";\n\nimport { BLUE, DIM, RST } from \"../ansi.js\";\nimport { ensureFreshIndex } from \"../indexer/index.js\";\nimport { resolveWikiRoot } from \"../indexer/resolve-wiki.js\";\nimport { openIndex } from \"../indexer/schema.js\";\n\n/**\n * `almanac show <slug>` — structured view of a page.\n *\n * This file absorbs what used to be split across `show` (body), `info`\n * (metadata), and `path` (file path resolution). One command, multiple\n * view flags.\n *\n * Three output \"shapes\":\n *\n * 1. **Default** — metadata header + `\\n---\\n` separator + body. Useful\n * to skim a page and still see its topics/files/links at a glance.\n * 2. **View flags** (mutually exclusive-ish):\n * --json structured JSON, overrides everything else\n * --body body only (`--raw` is a deprecated CLI alias)\n * --meta metadata only, no body\n * --lead first paragraph of body only (cheap preview)\n * 3. **Field flags** (composable). Each selects one \"field\" of the page:\n * --title / --topics / --files / --links / --backlinks / --xwiki\n * --lineage / --updated / --path\n * A single field → bare, pipe-friendly output (one item per line).\n * Multiple fields → labeled sections, one per flag.\n *\n * `--stdin` is always JSON Lines (one record per line). This avoids the\n * separator ambiguity with markdown `---` in bulk output: the old `info`\n * used a human-readable array; the old `show` used `\\n---\\n` which\n * collided with page frontmatter delimiters.\n */\nexport interface ShowOptions {\n cwd: string;\n slug?: string;\n stdin?: boolean;\n stdinInput?: string;\n wiki?: string;\n\n // View modes (mutually exclusive-ish — precedence: json > raw > meta > lead > default).\n json?: boolean;\n raw?: boolean; // alias: body\n meta?: boolean;\n lead?: boolean;\n\n // Composable field flags.\n title?: boolean;\n topics?: boolean;\n files?: boolean;\n links?: boolean;\n backlinks?: boolean;\n xwiki?: boolean;\n lineage?: boolean;\n updated?: boolean;\n path?: boolean;\n}\n\nexport interface ShowCommandOutput {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\n/**\n * The structured record emitted by `--json`. Also the shape we read into\n * in order to compose any other output. Keeping this a plain flat object\n * means callers can JSON.parse downstream tooling without chasing nested\n * subschemas.\n */\nexport interface ShowRecord {\n slug: string;\n title: string | null;\n file_path: string;\n updated_at: number;\n archived_at: number | null;\n superseded_by: string | null;\n supersedes: string[];\n topics: string[];\n file_refs: Array<{ path: string; is_dir: boolean }>;\n wikilinks_out: string[];\n wikilinks_in: string[];\n cross_wiki_links: Array<{ wiki: string; target: string }>;\n body: string;\n}\n\nexport async function runShow(\n options: ShowOptions,\n): Promise<ShowCommandOutput> {\n const repoRoot = await resolveWikiRoot({\n cwd: options.cwd,\n wiki: options.wiki,\n });\n await ensureFreshIndex({ repoRoot });\n\n const dbPath = join(repoRoot, \".almanac\", \"index.db\");\n const db = openIndex(dbPath);\n\n try {\n const slugs = collectSlugs(options);\n if (slugs.length === 0) {\n return {\n stdout: \"\",\n stderr: \"almanac: show requires a slug (or --stdin)\\n\",\n exitCode: 1,\n };\n }\n\n const records: ShowRecord[] = [];\n const missing: string[] = [];\n for (const slug of slugs) {\n const rec = await fetchRecord(db, slug);\n if (rec === null) {\n missing.push(slug);\n continue;\n }\n records.push(rec);\n }\n\n const bulk = options.stdin === true;\n const stdout = bulk\n ? formatBulk(records)\n : formatSingle(records, options);\n\n const stderr = missing\n .map((s) => `almanac: no such page \"${s}\"\\n`)\n .join(\"\");\n\n return {\n stdout,\n stderr,\n exitCode: missing.length > 0 ? 1 : 0,\n };\n } finally {\n db.close();\n }\n}\n\n// ─── Record fetch ────────────────────────────────────────────────────\n\nasync function fetchRecord(\n db: Database.Database,\n slug: string,\n): Promise<ShowRecord | null> {\n const pageRow = db\n .prepare<\n [string],\n {\n slug: string;\n title: string | null;\n file_path: string;\n updated_at: number;\n archived_at: number | null;\n superseded_by: string | null;\n }\n >(\n \"SELECT slug, title, file_path, updated_at, archived_at, superseded_by FROM pages WHERE slug = ?\",\n )\n .get(slug);\n if (pageRow === undefined) return null;\n\n const topics = db\n .prepare<[string], { topic_slug: string }>(\n \"SELECT topic_slug FROM page_topics WHERE page_slug = ? ORDER BY topic_slug\",\n )\n .all(slug)\n .map((r) => r.topic_slug);\n\n const refs = db\n .prepare<[string], { original_path: string; is_dir: number }>(\n \"SELECT original_path, is_dir FROM file_refs WHERE page_slug = ? ORDER BY original_path\",\n )\n .all(slug)\n .map((r) => ({ path: r.original_path, is_dir: r.is_dir === 1 }));\n\n const linksOut = db\n .prepare<[string], { target_slug: string }>(\n \"SELECT target_slug FROM wikilinks WHERE source_slug = ? ORDER BY target_slug\",\n )\n .all(slug)\n .map((r) => r.target_slug);\n\n const linksIn = db\n .prepare<[string], { source_slug: string }>(\n \"SELECT source_slug FROM wikilinks WHERE target_slug = ? ORDER BY source_slug\",\n )\n .all(slug)\n .map((r) => r.source_slug);\n\n const xwiki = db\n .prepare<[string], { target_wiki: string; target_slug: string }>(\n \"SELECT target_wiki, target_slug FROM cross_wiki_links WHERE source_slug = ? ORDER BY target_wiki, target_slug\",\n )\n .all(slug)\n .map((r) => ({ wiki: r.target_wiki, target: r.target_slug }));\n\n const supersedesRows = db\n .prepare<[string], { slug: string }>(\n \"SELECT slug FROM pages WHERE superseded_by = ? ORDER BY slug\",\n )\n .all(slug)\n .map((r) => r.slug);\n\n // Read body (strip YAML frontmatter). `show` is the only command\n // permitted to read page content, per the spec.\n let body = \"\";\n try {\n body = stripFrontmatter(await readFile(pageRow.file_path, \"utf8\"));\n } catch {\n // If the page row exists but the file is unreadable (race with `git\n // mv`, permission change, …) we keep the record — everything else we\n // have is sourced from the index. Body renders as empty. The missing\n // view flags still work.\n }\n\n return {\n slug: pageRow.slug,\n title: pageRow.title,\n file_path: pageRow.file_path,\n updated_at: pageRow.updated_at,\n archived_at: pageRow.archived_at,\n superseded_by: pageRow.superseded_by,\n supersedes: supersedesRows,\n topics,\n file_refs: refs,\n wikilinks_out: linksOut,\n wikilinks_in: linksIn,\n cross_wiki_links: xwiki,\n body,\n };\n}\n\n// ─── Formatting ──────────────────────────────────────────────────────\n\nfunction formatBulk(records: ShowRecord[]): string {\n // JSON Lines. One record per line, trailing newline when non-empty.\n // Consumers split on `\\n` and `JSON.parse` each line.\n if (records.length === 0) return \"\";\n return records.map((r) => JSON.stringify(r)).join(\"\\n\") + \"\\n\";\n}\n\nfunction formatSingle(\n records: ShowRecord[],\n options: ShowOptions,\n): string {\n if (options.json === true) {\n // JSON always wins. Positional mode emits a single object (never an\n // array) for a found page, `null` for a missing one — matches the\n // shape contract from the old `info --json`.\n const only = records[0] ?? null;\n return `${JSON.stringify(only, null, 2)}\\n`;\n }\n return records.map((r) => formatRecord(r, options)).join(\"\");\n}\n\n/**\n * Figure out which fields the user asked for. Precedence:\n * 1. Body-only mode — everything else is ignored.\n * 2. `--meta` — metadata only, no body. Ignores `--lead`.\n * 3. `--lead` — first paragraph only.\n * 4. Any field flag (`--title`, `--topics`, …) set → those fields only.\n * 5. Nothing set → full view (metadata header + body).\n */\ntype FieldName =\n | \"title\"\n | \"topics\"\n | \"files\"\n | \"links\"\n | \"backlinks\"\n | \"xwiki\"\n | \"lineage\"\n | \"updated\"\n | \"path\";\n\nconst FIELD_ORDER: FieldName[] = [\n \"title\",\n \"topics\",\n \"files\",\n \"links\",\n \"backlinks\",\n \"xwiki\",\n \"lineage\",\n \"updated\",\n \"path\",\n];\n\nfunction selectedFields(options: ShowOptions): FieldName[] {\n const selected: FieldName[] = [];\n for (const f of FIELD_ORDER) {\n if (options[f] === true) selected.push(f);\n }\n return selected;\n}\n\nfunction formatRecord(rec: ShowRecord, options: ShowOptions): string {\n // 1. raw / body\n if (options.raw === true) {\n // Guarantee exactly one trailing newline. Without it, shell redirects\n // (`almanac show foo --body > foo.md`) produce files missing a final\n // newline, which confuses concatenation and diff tools. We don't\n // collapse multiple trailing newlines — a page that ends with a\n // blank line is intentional.\n if (rec.body.length === 0) return \"\";\n return rec.body.endsWith(\"\\n\") ? rec.body : `${rec.body}\\n`;\n }\n\n // 4. Field flags (check before meta/lead so --meta + --title is unambiguous).\n const fields = selectedFields(options);\n if (fields.length > 0) {\n if (fields.length === 1) {\n return bareField(rec, fields[0]!);\n }\n return labeledFields(rec, fields);\n }\n\n // 2. meta only\n if (options.meta === true) {\n return metadataHeader(rec) + \"\\n\";\n }\n\n // 3. lead only\n if (options.lead === true) {\n return firstParagraph(rec.body) + \"\\n\";\n }\n\n // 5. Default — metadata header + separator + body.\n const header = metadataHeader(rec);\n const body = rec.body;\n const sep = body.length > 0 ? `\\n\\n${DIM}---${RST}\\n\\n` : \"\\n\";\n return header + sep + body;\n}\n\n/**\n * Single-field bare output. One item per line, no labels, no colons —\n * designed to be piped directly into another command. The exact format\n * per field tries to preserve \"scriptable by default\":\n *\n * --title → the title (or empty if null)\n * --topics → one topic slug per line\n * --files → one file ref per line; trailing slash for folders\n * --links → one outgoing slug per line\n * --backlinks → one incoming slug per line\n * --xwiki → one `wiki:slug` per line\n * --lineage → archived_at / supersedes / superseded_by, one per line\n * (only the ones that exist — silent when all absent)\n * --updated → ISO-8601 UTC timestamp\n * --path → absolute file path (replaces the old `almanac path`)\n */\nfunction bareField(rec: ShowRecord, field: FieldName): string {\n switch (field) {\n case \"title\":\n return (rec.title ?? \"\") + \"\\n\";\n case \"topics\":\n return rec.topics.map((t) => `${t}\\n`).join(\"\");\n case \"files\":\n return rec.file_refs\n .map((r) => `${r.path}\\n`)\n .join(\"\");\n case \"links\":\n return rec.wikilinks_out.map((t) => `${t}\\n`).join(\"\");\n case \"backlinks\":\n return rec.wikilinks_in.map((t) => `${t}\\n`).join(\"\");\n case \"xwiki\":\n return rec.cross_wiki_links\n .map((x) => `${x.wiki}:${x.target}\\n`)\n .join(\"\");\n case \"lineage\": {\n const lines: string[] = [];\n if (rec.archived_at !== null) {\n lines.push(\n `archived_at: ${new Date(rec.archived_at * 1000).toISOString()}`,\n );\n }\n if (rec.superseded_by !== null) {\n lines.push(`superseded_by: ${rec.superseded_by}`);\n }\n if (rec.supersedes.length > 0) {\n lines.push(`supersedes: ${rec.supersedes.join(\", \")}`);\n }\n return lines.length > 0 ? `${lines.join(\"\\n\")}\\n` : \"\";\n }\n case \"updated\":\n return `${new Date(rec.updated_at * 1000).toISOString()}\\n`;\n case \"path\":\n return `${rec.file_path}\\n`;\n }\n}\n\n/**\n * Multi-field labeled output. Each requested field renders as a labeled\n * section, in canonical order (the order of `FIELD_ORDER`, not the order\n * flags appeared on the command line). Sections are separated by blank\n * lines to make grep-by-label reliable.\n */\nfunction labeledFields(rec: ShowRecord, fields: FieldName[]): string {\n const parts: string[] = [];\n for (const f of fields) {\n parts.push(labeledSection(rec, f));\n }\n return parts.join(\"\\n\");\n}\n\nfunction labeledSection(rec: ShowRecord, field: FieldName): string {\n switch (field) {\n case \"title\":\n return `${DIM}title:${RST} ${rec.title ?? \"—\"}\\n`;\n case \"topics\":\n return rec.topics.length > 0\n ? `${DIM}topics:${RST} ${rec.topics.join(\", \")}\\n`\n : `${DIM}topics:${RST} —\\n`;\n case \"files\":\n return formatListSection(\n \"files\",\n rec.file_refs.map((r) => `${r.path}`),\n );\n case \"links\":\n return formatListSection(\"links\", rec.wikilinks_out);\n case \"backlinks\":\n return formatListSection(\"backlinks\", rec.wikilinks_in);\n case \"xwiki\":\n return formatListSection(\n \"xwiki\",\n rec.cross_wiki_links.map((x) => `${x.wiki}:${x.target}`),\n );\n case \"lineage\": {\n const lines: string[] = [`${DIM}lineage:${RST}`];\n if (rec.archived_at !== null) {\n lines.push(\n ` ${DIM}archived_at:${RST} ${new Date(rec.archived_at * 1000).toISOString()}`,\n );\n }\n if (rec.superseded_by !== null) {\n lines.push(` ${DIM}superseded_by:${RST} ${rec.superseded_by}`);\n }\n if (rec.supersedes.length > 0) {\n lines.push(` ${DIM}supersedes:${RST} ${rec.supersedes.join(\", \")}`);\n }\n if (lines.length === 1) lines.push(\" —\");\n return lines.join(\"\\n\") + \"\\n\";\n }\n case \"updated\":\n return `${DIM}updated:${RST} ${new Date(rec.updated_at * 1000).toISOString()}\\n`;\n case \"path\":\n return `${DIM}path:${RST} ${rec.file_path}\\n`;\n }\n}\n\nfunction formatListSection(label: string, items: string[]): string {\n if (items.length === 0) return `${DIM}${label}:${RST} —\\n`;\n if (items.length <= 3) return `${DIM}${label}:${RST} ${items.join(\", \")}\\n`;\n return `${DIM}${label}:${RST}\\n${items.map((i) => ` ${i}`).join(\"\\n\")}\\n`;\n}\n\n/**\n * Metadata header rendered for default and `--meta` views. Single-line\n * fields for short lists; indented block form for long lists. This is\n * the human-readable counterpart to the JSON dump — labeled, column-\n * aligned, skimmable.\n */\nfunction metadataHeader(rec: ShowRecord): string {\n const lines: string[] = [];\n lines.push(`${DIM}slug:${RST} ${BLUE}${rec.slug}${RST}`);\n lines.push(`${DIM}title:${RST} ${rec.title ?? \"—\"}`);\n lines.push(\n `${DIM}topics:${RST} ${rec.topics.length > 0 ? rec.topics.join(\", \") : \"—\"}`,\n );\n\n if (rec.file_refs.length > 0) {\n const parts = rec.file_refs.map(\n (r) => `${r.path}`,\n );\n lines.push(`${DIM}files:${RST} ${parts.join(\", \")}`);\n }\n\n lines.push(\n `${DIM}updated:${RST} ${new Date(rec.updated_at * 1000).toISOString()}`,\n );\n\n if (rec.wikilinks_out.length > 0) {\n lines.push(`${DIM}links:${RST} ${rec.wikilinks_out.join(\", \")}`);\n }\n if (rec.wikilinks_in.length > 0) {\n lines.push(`${DIM}backlinks:${RST} ${rec.wikilinks_in.join(\", \")}`);\n }\n if (rec.cross_wiki_links.length > 0) {\n lines.push(\n `${DIM}xwiki:${RST} ${rec.cross_wiki_links\n .map((x) => `${x.wiki}:${x.target}`)\n .join(\", \")}`,\n );\n }\n if (rec.archived_at !== null) {\n lines.push(\n `${DIM}archived:${RST} ${new Date(rec.archived_at * 1000).toISOString()}`,\n );\n }\n if (rec.superseded_by !== null) {\n lines.push(`${DIM}superseded_by:${RST} ${rec.superseded_by}`);\n }\n if (rec.supersedes.length > 0) {\n lines.push(`${DIM}supersedes:${RST} ${rec.supersedes.join(\", \")}`);\n }\n\n return lines.join(\"\\n\");\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────\n\n/**\n * Strip a leading YAML frontmatter block (fenced by `---` on its own\n * lines). Everything between the opening `---\\n` and the next `---\\n` is\n * dropped, along with the surrounding fence. If no opening fence is\n * present we return the source unchanged.\n *\n * Hand-rolled rather than pulling in gray-matter because the CLI's only\n * goal here is \"show the body without the YAML header\" — we don't need\n * the parsed fields, the indexer already has them.\n */\nfunction stripFrontmatter(src: string): string {\n if (!src.startsWith(\"---\\n\") && !src.startsWith(\"---\\r\\n\")) return src;\n // Strip the opening fence. A regex replace handles both LF and CRLF\n // line endings without the off-by-one that `src.indexOf(\"\\n\") + 1`\n // suffered on CRLF files — `indexOf(\"\\n\")` landed AFTER the `\\r`, so\n // the `\\r` survived as a prefix on the first body byte.\n const afterOpen = src.replace(/^---\\r?\\n/, \"\");\n const endMatch = afterOpen.match(/^---[ \\t]*\\r?\\n/m);\n if (endMatch === null || endMatch.index === undefined) return src;\n // Slice after the closing fence's line.\n return afterOpen.slice(endMatch.index + endMatch[0].length);\n}\n\n/**\n * First paragraph of the body, where \"paragraph\" is everything up to (but\n * not including) the first blank line. Skips a leading `# Title` line if\n * present so `--lead` previews the first real sentence, not the heading.\n */\nfunction firstParagraph(body: string): string {\n let src = body.trimStart();\n if (src.startsWith(\"# \")) {\n const nl = src.indexOf(\"\\n\");\n src = nl === -1 ? \"\" : src.slice(nl + 1).trimStart();\n }\n const blank = src.search(/\\n[ \\t]*\\n/);\n if (blank === -1) return src.trimEnd();\n return src.slice(0, blank).trimEnd();\n}\n\nfunction collectSlugs(options: ShowOptions): string[] {\n if (options.stdin === true && options.stdinInput !== undefined) {\n return options.stdinInput\n .split(/\\r?\\n/)\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n }\n if (options.slug !== undefined && options.slug.length > 0) {\n return [options.slug];\n }\n return [];\n}\n","import { Command } from \"commander\";\n\nimport { runHealth } from \"../commands/health.js\";\nimport { listWikis } from \"../commands/list.js\";\nimport { runSearch } from \"../commands/search.js\";\nimport { runShow } from \"../commands/show.js\";\nimport { autoRegisterIfNeeded } from \"../registry/autoregister.js\";\nimport {\n collectOption,\n deprecationWarning,\n emit,\n parsePositiveInt,\n readStdin,\n withWarning,\n} from \"./helpers.js\";\n\nexport function registerQueryCommands(program: Command): void {\n program\n .command(\"search [query]\")\n .description(\"find pages by text, topic, file mentions, freshness\")\n .option(\n \"--topic <name...>\",\n \"filter by topic (repeat for intersection)\",\n collectOption,\n [] as string[],\n )\n .option(\n \"--mentions <path>\",\n \"pages referencing this path; matches exact file, trailing-slash folders, and any file under a folder prefix\",\n )\n .option(\"--since <duration>\", \"updated within duration, by file mtime (e.g. 2w, 30d)\")\n .option(\"--stale <duration>\", \"NOT updated within duration, by file mtime\")\n .option(\"--orphan\", \"pages with no topics\")\n .option(\"--include-archive\", \"include archived pages\")\n .option(\"--archived\", \"archived pages only\")\n .option(\"--wiki <name>\", \"target a specific registered wiki\")\n .option(\"--json\", \"emit structured JSON\")\n .option(\"--limit <n>\", \"cap results\", parsePositiveInt)\n .action(\n async (\n query: string | undefined,\n opts: {\n topic?: string[];\n mentions?: string;\n since?: string;\n stale?: string;\n orphan?: boolean;\n includeArchive?: boolean;\n archived?: boolean;\n wiki?: string;\n json?: boolean;\n limit?: number;\n },\n ) => {\n await autoRegisterIfNeeded(process.cwd());\n const result = await runSearch({\n cwd: process.cwd(),\n query,\n topics: opts.topic ?? [],\n mentions: opts.mentions,\n since: opts.since,\n stale: opts.stale,\n orphan: opts.orphan,\n includeArchive: opts.includeArchive,\n archived: opts.archived,\n wiki: opts.wiki,\n json: opts.json,\n limit: opts.limit,\n });\n emit(result);\n },\n );\n\n program\n .command(\"show [slug]\")\n .description(\"print a page (metadata + body; flags to narrow)\")\n .option(\"--stdin\", \"read slugs from stdin (one per line)\")\n .option(\"--wiki <name>\", \"target a specific registered wiki\")\n .option(\"--json\", \"structured JSON (overrides other view/field flags)\")\n .option(\"--body\", \"body only\")\n .option(\"--raw\", \"deprecated alias for --body\")\n .option(\"--meta\", \"metadata only, no body\")\n .option(\"--lead\", \"first paragraph of the body only\")\n .option(\"--title\", \"print title\")\n .option(\"--topics\", \"print topics\")\n .option(\"--files\", \"print file refs\")\n .option(\"--links\", \"print outgoing wikilinks\")\n .option(\"--backlinks\", \"print incoming wikilinks\")\n .option(\"--xwiki\", \"print cross-wiki links\")\n .option(\"--lineage\", \"print archived_at / supersedes / superseded_by\")\n .option(\"--updated\", \"print updated timestamp\")\n .option(\"--path\", \"print absolute file path\")\n .action(\n async (\n slug: string | undefined,\n opts: {\n stdin?: boolean;\n wiki?: string;\n json?: boolean;\n raw?: boolean;\n body?: boolean;\n meta?: boolean;\n lead?: boolean;\n title?: boolean;\n topics?: boolean;\n files?: boolean;\n links?: boolean;\n backlinks?: boolean;\n xwiki?: boolean;\n lineage?: boolean;\n updated?: boolean;\n path?: boolean;\n },\n ) => {\n await autoRegisterIfNeeded(process.cwd());\n const result = await runShow({\n cwd: process.cwd(),\n slug,\n stdin: opts.stdin,\n stdinInput: opts.stdin === true ? await readStdin() : undefined,\n wiki: opts.wiki,\n json: opts.json,\n raw: opts.raw === true || opts.body === true,\n meta: opts.meta,\n lead: opts.lead,\n title: opts.title,\n topics: opts.topics,\n files: opts.files,\n links: opts.links,\n backlinks: opts.backlinks,\n xwiki: opts.xwiki,\n lineage: opts.lineage,\n updated: opts.updated,\n path: opts.path,\n });\n emit(opts.raw === true\n ? withWarning(\n result,\n deprecationWarning(\"almanac show <slug> --raw\", \"almanac show <slug> --body\"),\n )\n : result);\n },\n );\n\n program\n .command(\"health\")\n .description(\"report graph integrity problems\")\n .option(\"--topic <name>\", \"scope to a topic + its descendants\")\n .option(\"--stale <duration>\", \"stale threshold (default 90d)\")\n .option(\"--stdin\", \"read page slugs from stdin (limit to these pages)\")\n .option(\"--json\", \"emit structured JSON\")\n .option(\"--wiki <name>\", \"target a specific registered wiki\")\n .action(\n async (opts: {\n topic?: string;\n stale?: string;\n stdin?: boolean;\n json?: boolean;\n wiki?: string;\n }) => {\n await autoRegisterIfNeeded(process.cwd());\n const result = await runHealth({\n cwd: process.cwd(),\n topic: opts.topic,\n stale: opts.stale,\n stdin: opts.stdin,\n stdinInput: opts.stdin === true ? await readStdin() : undefined,\n json: opts.json,\n wiki: opts.wiki,\n });\n emit(result);\n },\n );\n\n program\n .command(\"list\")\n .description(\"list registered wikis\")\n .option(\"--json\", \"emit structured JSON\")\n .option(\n \"--drop <name>\",\n \"remove a wiki from the registry (the only way entries are ever removed)\",\n )\n .action(async (opts: { json?: boolean; drop?: string }) => {\n if (opts.drop === undefined) {\n await autoRegisterIfNeeded(process.cwd());\n }\n const result = await listWikis(opts);\n process.stdout.write(result.stdout);\n if (result.exitCode !== 0) {\n process.exitCode = result.exitCode;\n }\n });\n}\n","import { Command } from \"commander\";\n\nimport {\n runAgentsDoctor,\n runAgentsList,\n runAgentsModel,\n runAgentsUse,\n runDeprecatedSetAgentModel,\n runDeprecatedSetDefaultAgent,\n} from \"../commands/agents.js\";\nimport {\n runConfigGet,\n runConfigList,\n runConfigSet,\n runConfigUnset,\n} from \"../commands/config.js\";\nimport { runDoctor } from \"../commands/doctor.js\";\nimport { runSetup } from \"../commands/setup.js\";\nimport { runUninstall } from \"../commands/uninstall.js\";\nimport { runUpdate } from \"../commands/update.js\";\nimport { emit } from \"./helpers.js\";\n\nexport function registerSetupCommands(program: Command): void {\n const agents = program\n .command(\"agents\")\n .description(\"list supported AI agent providers and readiness\");\n\n agents\n .command(\"list\")\n .description(\"show supported provider status, defaults, and models\")\n .action(async () => {\n emit(await runAgentsList());\n });\n\n agents\n .command(\"doctor\")\n .description(\"diagnose supported AI agent providers\")\n .action(async () => {\n emit(await runAgentsDoctor());\n });\n\n agents\n .command(\"use\")\n .description(\"set the default AI agent provider\")\n .argument(\"<provider>\", \"claude, codex, cursor, or claude/<model>\")\n .action(async (provider: string) => {\n emit(await runAgentsUse({ provider }));\n });\n\n agents\n .command(\"model\")\n .description(\"set or reset a provider model\")\n .argument(\"<provider>\", \"claude, codex, or cursor\")\n .argument(\"[model]\", \"provider-specific model id\")\n .option(\"--default\", \"reset to provider default\")\n .action(async (\n provider: string,\n model: string | undefined,\n opts: { default?: boolean },\n ) => {\n emit(await runAgentsModel({\n provider,\n model,\n defaultModel: opts.default,\n }));\n });\n\n const config = program\n .command(\"config\")\n .description(\"read and write codealmanac settings\");\n\n config\n .command(\"list\")\n .description(\"show supported config keys\")\n .option(\"--json\", \"emit structured JSON\")\n .option(\"--show-origin\", \"show whether each value came from file or default\")\n .action(async (opts: { json?: boolean; showOrigin?: boolean }) => {\n emit(await runConfigList(opts));\n });\n\n config\n .command(\"get\")\n .description(\"print one config value\")\n .argument(\"<key>\", \"config key\")\n .option(\"--json\", \"emit structured JSON\")\n .option(\"--show-origin\", \"show whether the value came from file or default\")\n .action(async (\n key: string,\n opts: { json?: boolean; showOrigin?: boolean },\n ) => {\n emit(await runConfigGet({ key, ...opts }));\n });\n\n config\n .command(\"set\")\n .description(\"set one config value\")\n .argument(\"<key>\", \"config key\")\n .argument(\"<value>\", \"config value\")\n .option(\"--project\", \"write .almanac/config.toml for this repo\")\n .action(async (\n key: string,\n value: string,\n opts: { project?: boolean },\n ) => {\n emit(await runConfigSet({ key, value, project: opts.project }));\n });\n\n config\n .command(\"unset\")\n .description(\"restore one config value to default\")\n .argument(\"<key>\", \"config key\")\n .option(\"--project\", \"remove from .almanac/config.toml for this repo\")\n .action(async (key: string, opts: { project?: boolean }) => {\n emit(await runConfigUnset({ key, project: opts.project }));\n });\n\n program\n .command(\"set\")\n .description(\"configure codealmanac defaults\")\n .argument(\"<key>\", \"setting key, e.g. default-agent or model\")\n .argument(\"[value...]\", \"setting value\")\n .action(async (key: string, value: string[]) => {\n if (key === \"default-agent\") {\n emit(await runDeprecatedSetDefaultAgent({ provider: value[0] ?? \"\" }));\n return;\n }\n if (key === \"model\") {\n emit(await runDeprecatedSetAgentModel({\n provider: value[0] ?? \"\",\n model: value[1],\n }));\n return;\n }\n emit({\n stdout: \"\",\n stderr:\n \"almanac: unknown setting. Use `default-agent` or `model`.\\n\",\n exitCode: 1,\n });\n });\n\n program\n .command(\"setup\")\n .description(\"choose provider/model and install provider integrations\")\n .option(\"-y, --yes\", \"skip prompts; install everything\")\n .option(\"--agent <agent>\", \"default provider: claude, codex, or cursor\")\n .option(\"--model <model>\", \"default model for the selected agent\")\n .option(\"--skip-hook\", \"opt out of auto-capture hooks\")\n .option(\"--skip-guides\", \"opt out of provider guide install\")\n .action(\n async (opts: {\n yes?: boolean;\n agent?: string;\n model?: string;\n skipHook?: boolean;\n skipGuides?: boolean;\n }) => {\n const result = await runSetup({\n yes: opts.yes,\n agent: opts.agent,\n model: opts.model,\n skipHook: opts.skipHook,\n skipGuides: opts.skipGuides,\n });\n emit(result);\n },\n );\n\n program\n .command(\"doctor\")\n .description(\"report on the codealmanac install + current wiki health\")\n .option(\"--json\", \"emit structured JSON\")\n .option(\"--install-only\", \"report only on the install (skip wiki checks)\")\n .option(\"--wiki-only\", \"report only on the current wiki (skip install checks)\")\n .action(\n async (opts: {\n json?: boolean;\n installOnly?: boolean;\n wikiOnly?: boolean;\n }) => {\n const result = await runDoctor({\n cwd: process.cwd(),\n json: opts.json,\n installOnly: opts.installOnly,\n wikiOnly: opts.wikiOnly,\n });\n emit(result);\n },\n );\n\n program\n .command(\"update\")\n .description(\"install the latest codealmanac (synchronous foreground `npm i -g`)\")\n .option(\n \"--dismiss\",\n \"silence the update banner for the current `latest_version` without installing\",\n )\n .option(\"--check\", \"force a registry check now (bypasses the 24h cache); no install\")\n .option(\n \"--enable-notifier\",\n \"deprecated: use `almanac config set update_notifier true`\",\n )\n .option(\n \"--disable-notifier\",\n \"deprecated: use `almanac config set update_notifier false`\",\n )\n .action(\n async (opts: {\n dismiss?: boolean;\n check?: boolean;\n enableNotifier?: boolean;\n disableNotifier?: boolean;\n }) => {\n const result = await runUpdate({\n dismiss: opts.dismiss,\n check: opts.check,\n enableNotifier: opts.enableNotifier,\n disableNotifier: opts.disableNotifier,\n });\n emit(result);\n },\n );\n\n program\n .command(\"uninstall\")\n .description(\"remove the hook + guides + import line\")\n .option(\"-y, --yes\", \"skip confirmations; remove everything\")\n .option(\"--keep-hook\", \"don't remove auto-capture hooks (guides still prompted unless --yes)\")\n .option(\n \"--keep-guides\",\n \"don't remove guides or provider imports/rules (hook still prompted unless --yes)\",\n )\n .action(\n async (opts: {\n yes?: boolean;\n keepHook?: boolean;\n keepGuides?: boolean;\n }) => {\n const result = await runUninstall({\n yes: opts.yes,\n keepHook: opts.keepHook,\n keepGuides: opts.keepGuides,\n });\n emit(result);\n },\n );\n}\n","import { createWriteStream, existsSync, type WriteStream } from \"node:fs\";\nimport { mkdir, readdir } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\n\nimport type { SpawnCliFn } from \"../agent/providers/claude/index.js\";\nimport { assertAgentAuth } from \"../agent/providers.js\";\nimport { loadPrompt } from \"../agent/prompts.js\";\nimport { resolveAgentSelection } from \"../agent/selection.js\";\nimport { renderOutcome } from \"../cli/outcome.js\";\nimport {\n runAgent,\n type AgentResult,\n type AgentStreamMessage,\n type RunAgentOptions,\n} from \"../agent/sdk.js\";\nimport { findNearestAlmanacDir, getRepoAlmanacDir } from \"../paths.js\";\nimport { initWiki } from \"./init.js\";\n\nexport interface BootstrapOptions {\n cwd: string;\n /** Suppress per-tool-use streaming output; print only errors + final line. */\n quiet?: boolean;\n /** Override the agent model. Defaults to the SDK default (sonnet-4-6). */\n model?: string;\n /** Override the agent provider. Defaults to config precedence. */\n agent?: string;\n /** Overwrite a populated wiki. Default refuses with a pointer at `capture`. */\n force?: boolean;\n /** Emit CommandOutcome JSON instead of human output. */\n json?: boolean;\n /** Injectable agent runner — tests replace this with a fake. */\n runAgent?: (opts: RunAgentOptions) => Promise<AgentResult>;\n /**\n * Injectable subprocess spawner for the Claude auth-status check.\n * Tests substitute a stub that emits canned JSON without running the\n * bundled CLI. Production leaves this undefined and `assertClaudeAuth`\n * falls through to `defaultSpawnCli`.\n */\n spawnCli?: SpawnCliFn;\n /**\n * Clock injection, for tests. Otherwise `Date.now()` timestamps the\n * transcript log filename and defaults to the SDK's session_id once\n * we receive one (the filename is chosen BEFORE the agent starts so\n * we can stream to it).\n */\n now?: () => Date;\n}\n\nexport interface BootstrapResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\n/**\n * Tools the bootstrap agent is permitted to use. Bootstrap reads the repo\n * (Read/Glob/Grep), runs quick inspection commands (Bash — scoped by the\n * prompt, not by the SDK — the agent isn't expected to do anything\n * destructive), and writes scaffolding (Write/Edit). Notably absent:\n * - `Agent` — bootstrap has no subagents (reviewer is slice 5).\n * - `WebFetch` / `WebSearch` — prompt is explicit that we work from the\n * repo, not the internet. Adding these would invite drift.\n * - MCP servers — none needed for a local filesystem scan.\n */\nconst BOOTSTRAP_TOOLS = [\"Read\", \"Write\", \"Edit\", \"Glob\", \"Grep\", \"Bash\"];\n\n/**\n * `almanac bootstrap` — first Claude Agent SDK integration.\n *\n * Flow:\n * 1. Auth gate (ANTHROPIC_API_KEY). Fail fast with a clean error.\n * 2. Resolve repo root (existing `.almanac/` or cwd).\n * 3. Refuse-if-populated unless --force.\n * 4. Auto-init silently if `.almanac/` doesn't exist yet.\n * 5. Load `prompts/bootstrap.md`.\n * 6. Run the agent with BOOTSTRAP_TOOLS, cwd = repo root.\n * 7. Stream tool-uses to stdout (unless --quiet); write the full raw\n * transcript to `.almanac/logs/.bootstrap-<session>.log`.\n * 8. Print a final `[done]` / `[failed]` line with cost + turns.\n *\n * Non-zero exit on failure so shell users can pipe into `&&`.\n */\nexport async function runBootstrap(\n options: BootstrapOptions,\n): Promise<BootstrapResult> {\n // Repo root: honor an already-initialized wiki anywhere above us.\n // Otherwise treat `cwd` as the root for a fresh wiki. Resolve this before\n // provider selection so project-tier config can participate.\n const repoRoot = findNearestAlmanacDir(options.cwd) ?? options.cwd;\n // Fail before loading prompts so we don't do filesystem work on a request\n // that can't succeed. `assertClaudeAuth` accepts either subscription\n // OAuth (via the bundled SDK CLI) or `ANTHROPIC_API_KEY`; missing both\n // surfaces a two-option error and MUST exit non-zero so the SessionEnd\n // hook (which backgrounds the process and ignores stderr) doesn't\n // treat silent auth failure as success.\n const providerResolution = await resolveAgentSelection({\n agent: options.agent,\n model: options.model,\n cwd: repoRoot,\n });\n if (!providerResolution.ok) {\n return renderOutcome(\n { type: \"error\", message: providerResolution.error },\n { json: options.json },\n );\n }\n const { provider, model } = providerResolution;\n\n try {\n await assertAgentAuth({ provider, spawnCli: options.spawnCli });\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n return renderOutcome(\n {\n type: \"needs-action\",\n message: msg,\n fix: authFixFor(provider),\n data: { provider },\n },\n { json: options.json },\n );\n }\n\n const almanacDir = getRepoAlmanacDir(repoRoot);\n const pagesDir = join(almanacDir, \"pages\");\n\n // Refuse to clobber a populated wiki. `almanac capture` is the tool for\n // maintaining wikis after bootstrap.\n if (options.force !== true && existsSync(pagesDir)) {\n const existing = await countMarkdownPages(pagesDir);\n if (existing > 0) {\n return renderOutcome(\n {\n type: \"needs-action\",\n message:\n `.almanac/ already initialized with ${existing} page${existing === 1 ? \"\" : \"s\"}.`,\n fix: \"run: almanac capture (or pass --force to overwrite)\",\n data: { pages: existing },\n },\n { json: options.json },\n );\n }\n }\n\n // Auto-init silently if missing. `initWiki` is idempotent — re-running\n // on an existing wiki is a no-op for the README / pages dir.\n if (!existsSync(almanacDir)) {\n try {\n await initWiki({ cwd: repoRoot });\n } catch (err: unknown) {\n // Per the slice spec: auto-init failures should be loud. The user\n // needs to know init is broken, not see a cascading agent error.\n const msg = err instanceof Error ? err.message : String(err);\n return renderOutcome(\n { type: \"error\", message: `init failed during bootstrap: ${msg}` },\n { json: options.json },\n );\n }\n }\n\n const systemPrompt = await loadPrompt(\"bootstrap\");\n\n // Transcript log filename: timestamp-based so it's sortable. The session\n // ID from the SDK isn't known until the first message arrives — by then\n // we'd already want somewhere to stream to. Use a clock-derived prefix\n // that's still meaningful even on a run that fails before producing a\n // session_id.\n const now = options.now?.() ?? new Date();\n const logsDir = join(almanacDir, \"logs\");\n await mkdir(logsDir, { recursive: true });\n const logName = `.bootstrap-${formatTimestamp(now)}.log`;\n const logPath = join(logsDir, logName);\n const logStream = createWriteStream(logPath, { flags: \"w\" });\n\n // The streaming formatter is what the user sees on stdout unless\n // --quiet is set. The raw log captures EVERYTHING (including\n // `stream_event` partials) for postmortem.\n const out = process.stdout;\n const formatter = new StreamingFormatter({\n write: (line: string) => {\n if (options.quiet !== true && options.json !== true) out.write(line);\n },\n });\n\n const onMessage = (msg: AgentStreamMessage): void => {\n // Write the raw message to the transcript. Keep one JSON per line so\n // the log is grep-able and can be re-parsed if needed.\n try {\n logStream.write(`${JSON.stringify(msg)}\\n`);\n } catch {\n // Serialization failures are non-fatal — we'd rather keep streaming\n // to stdout than crash because one message had a circular ref.\n }\n formatter.handle(msg);\n };\n\n const runner = options.runAgent ?? runAgent;\n\n const userPrompt = `Begin the bootstrap now. Working directory: ${repoRoot}.`;\n\n let result: AgentResult;\n try {\n result = await runner({\n systemPrompt,\n prompt: userPrompt,\n allowedTools: BOOTSTRAP_TOOLS,\n cwd: repoRoot,\n provider,\n model,\n onMessage,\n });\n } finally {\n await closeStream(logStream);\n }\n\n const finalLine = formatFinalLine(result, logPath, repoRoot);\n\n if (result.success) {\n return renderOutcome(\n {\n type: \"success\",\n message: finalLine,\n data: runData(result, logPath, repoRoot),\n },\n { json: options.json },\n );\n }\n\n return renderOutcome(\n {\n type: \"error\",\n message: `bootstrap failed: ${result.error ?? \"unknown error\"}`,\n data: runData(result, logPath, repoRoot),\n },\n {\n json: options.json,\n stdout: options.quiet === true || options.json === true ? \"\" : `${finalLine}\\n`,\n },\n );\n}\n\nfunction authFixFor(provider: string): string {\n switch (provider) {\n case \"claude\":\n return \"run: claude auth login --claudeai (or export ANTHROPIC_API_KEY)\";\n case \"codex\":\n return \"run: codex login\";\n case \"cursor\":\n return \"run: cursor-agent login\";\n default:\n return \"run: almanac agents doctor\";\n }\n}\n\nfunction runData(\n result: AgentResult,\n logPath: string,\n repoRoot: string,\n): Record<string, unknown> {\n return {\n transcript: relative(repoRoot, logPath),\n cost: result.cost,\n turns: result.turns,\n sessionId: result.sessionId,\n };\n}\n\n/**\n * Format the final line the user sees. On success it's a one-liner with\n * cost + turns; on failure we still print the cost/turns so the user\n * knows what the partial run used. The log path is shown relative to\n * `repoRoot` to keep the line short.\n */\nfunction formatFinalLine(\n result: AgentResult,\n logPath: string,\n repoRoot: string,\n): string {\n const status = result.success ? \"done\" : \"failed\";\n const rel = relative(repoRoot, logPath);\n const usage = formatRunUsage(result);\n return `[${status}] ${usage} (transcript: ${rel})`;\n}\n\nexport function formatRunUsage(result: AgentResult): string {\n const parts: string[] = [];\n if (result.cost > 0 || result.usage === undefined) {\n parts.push(`cost: $${result.cost.toFixed(3)}`);\n }\n parts.push(`turns: ${result.turns}`);\n\n const usage = result.usage;\n if (usage !== undefined) {\n const tokenParts: string[] = [];\n if (usage.inputTokens !== undefined) {\n tokenParts.push(`${usage.inputTokens.toLocaleString(\"en-US\")} in`);\n }\n if (usage.outputTokens !== undefined) {\n tokenParts.push(`${usage.outputTokens.toLocaleString(\"en-US\")} out`);\n }\n if (usage.cachedInputTokens !== undefined) {\n tokenParts.push(\n `${usage.cachedInputTokens.toLocaleString(\"en-US\")} cached`,\n );\n }\n if (usage.reasoningOutputTokens !== undefined) {\n tokenParts.push(\n `${usage.reasoningOutputTokens.toLocaleString(\"en-US\")} reasoning`,\n );\n }\n if (tokenParts.length > 0) {\n parts.push(`tokens: ${tokenParts.join(\", \")}`);\n }\n }\n\n return parts.join(\", \");\n}\n\nasync function countMarkdownPages(pagesDir: string): Promise<number> {\n try {\n const entries = await readdir(pagesDir, { withFileTypes: true });\n return entries.filter((e) => e.isFile() && e.name.endsWith(\".md\")).length;\n } catch {\n return 0;\n }\n}\n\nfunction closeStream(stream: WriteStream): Promise<void> {\n return new Promise((resolve) => {\n stream.end(() => resolve());\n });\n}\n\nfunction formatTimestamp(d: Date): string {\n // YYYYMMDD-HHMMSS, local time. Collision-proof enough for human use\n // (one bootstrap per second, per repo is an acceptable ceiling).\n const pad = (n: number): string => n.toString().padStart(2, \"0\");\n const y = d.getFullYear();\n const mo = pad(d.getMonth() + 1);\n const da = pad(d.getDate());\n const h = pad(d.getHours());\n const mi = pad(d.getMinutes());\n const s = pad(d.getSeconds());\n return `${y}${mo}${da}-${h}${mi}${s}`;\n}\n\n/**\n * Translates SDK messages into one-line-per-tool-use output.\n *\n * Design rules:\n * - One line per `tool_use` block, not per token. Users scanning the\n * output want to see \"what the agent did\", not a tail of the\n * assistant's prose.\n * - `Bash` gets special formatting because the input is most usefully\n * rendered as the command being run.\n * - On final `result`, emit a summary line. Callers can suppress all\n * intermediate output via `--quiet` and still get the summary.\n * - Tool paths are shown relative to the cwd (not implemented here\n * because the SDK doesn't give us cwd on every message; users get\n * the raw input). The cwd-relative rendering is a nice-to-have that\n * we can layer on later without changing the API.\n *\n * Exported for testing — `StreamingFormatter` is easier to unit-test in\n * isolation than the whole command.\n */\nexport class StreamingFormatter {\n private readonly sink: { write: (line: string) => void };\n /**\n * Current agent label. Starts as \"bootstrap\"; switches when we see an\n * `Agent` tool-use (slice 5 will exercise this). We still track it here\n * so the formatter can stay shared between bootstrap and capture.\n */\n private currentAgent = \"bootstrap\";\n\n constructor(sink: { write: (line: string) => void }) {\n this.sink = sink;\n }\n\n /**\n * Swap the top-level agent label. `capture` uses this to relabel from\n * the default \"bootstrap\" to \"writer\" — otherwise the writer's tool-use\n * output would render as `[bootstrap] …`, which is confusing when you're\n * reading capture logs.\n */\n setAgent(name: string): void {\n this.currentAgent = name;\n }\n\n handle(msg: AgentStreamMessage): void {\n if (!isRecord(msg)) return;\n\n if (msg.type === \"assistant\" && isRecord(msg.message)) {\n const content = msg.message.content;\n if (!Array.isArray(content)) return;\n for (const block of msg.message.content) {\n if (!isRecord(block) || block.type !== \"tool_use\") continue;\n if (typeof block.name !== \"string\") continue;\n this.handleToolUse(block.name, block.input);\n }\n return;\n }\n\n if (msg.type === \"item.started\" && isRecord(msg.item)) {\n this.handleCodexItemStarted(msg.item);\n return;\n }\n\n if (msg.type === \"item.completed\" && isRecord(msg.item)) {\n this.handleCodexItemCompleted(msg.item);\n return;\n }\n\n if (msg.type === \"tool_call\") {\n this.handleCursorToolCall(msg);\n return;\n }\n }\n\n private handleToolUse(name: string, rawInput: unknown): void {\n const input = normalizeToolInput(rawInput);\n\n if (name === \"Agent\") {\n // Subagent dispatch. Track the label so subsequent tool-uses show\n // up under the right agent bracket. The label is whatever the\n // parent passed as `subagent_type`, falling back to \"subagent\" if\n // the input was malformed (shouldn't happen, but defensively).\n const sub =\n typeof input.subagent_type === \"string\" ? input.subagent_type : \"subagent\";\n this.currentAgent = sub;\n this.sink.write(`[${sub}] starting\\n`);\n return;\n }\n\n const summary = formatToolSummary(name, input);\n this.sink.write(`[${this.currentAgent}] ${summary}\\n`);\n }\n\n private handleCodexItemStarted(item: Record<string, unknown>): void {\n if (item.type !== \"command_execution\") return;\n const command = stringField(item, \"command\") ?? \"?\";\n this.sink.write(`[${this.currentAgent}] bash ${truncate(command, 80)}\\n`);\n }\n\n private handleCodexItemCompleted(item: Record<string, unknown>): void {\n if (item.type !== \"file_change\") return;\n const changes = Array.isArray(item.changes) ? item.changes : [];\n for (const change of changes) {\n if (!isRecord(change)) continue;\n const rawPath = stringField(change, \"path\") ?? \"?\";\n const kind = stringField(change, \"kind\") ?? \"edit\";\n const verb =\n kind === \"add\" ? \"writing\" : kind === \"delete\" ? \"deleting\" : \"editing\";\n this.sink.write(\n `[${this.currentAgent}] ${verb} ${formatCodexPath(rawPath)}\\n`,\n );\n }\n }\n\n private handleCursorToolCall(msg: Record<string, unknown>): void {\n if (msg.subtype !== \"started\" || !isRecord(msg.tool_call)) return;\n const summary = formatCursorToolSummary(msg.tool_call);\n if (summary === undefined) return;\n this.sink.write(`[${this.currentAgent}] ${summary}\\n`);\n }\n}\n\n/**\n * SDK quirk: `tool_use.input` arrives as either an object OR a\n * JSON-encoded string. Always normalize before touching fields.\n */\nfunction normalizeToolInput(raw: unknown): Record<string, unknown> {\n if (typeof raw === \"string\") {\n try {\n const parsed = JSON.parse(raw);\n if (parsed !== null && typeof parsed === \"object\") {\n return parsed as Record<string, unknown>;\n }\n } catch {\n // Fall through to empty object.\n }\n return {};\n }\n if (raw !== null && typeof raw === \"object\") {\n return raw as Record<string, unknown>;\n }\n return {};\n}\n\n/**\n * Render a tool call as a single human-readable line. Not exhaustive —\n * we cover the tools bootstrap actually invokes and fall back to the\n * tool name + a terse input summary for anything else.\n */\nfunction formatToolSummary(\n name: string,\n input: Record<string, unknown>,\n): string {\n switch (name) {\n case \"Read\": {\n const target = stringField(input, \"file_path\") ?? \"?\";\n return `reading ${target}`;\n }\n case \"Write\": {\n const target = stringField(input, \"file_path\") ?? \"?\";\n return `writing ${target}`;\n }\n case \"Edit\": {\n const target = stringField(input, \"file_path\") ?? \"?\";\n return `editing ${target}`;\n }\n case \"Glob\": {\n const pattern = stringField(input, \"pattern\") ?? \"?\";\n return `glob ${pattern}`;\n }\n case \"Grep\": {\n const pattern = stringField(input, \"pattern\") ?? \"?\";\n return `grep ${pattern}`;\n }\n case \"Bash\": {\n const command = stringField(input, \"command\") ?? \"?\";\n // Truncate long commands so one tool-use stays one line.\n return `bash ${truncate(command, 80)}`;\n }\n default: {\n // Unknown or MCP tool. Show the name; omit the input to avoid\n // spamming the terminal with arbitrary JSON.\n return name;\n }\n }\n}\n\nfunction formatCursorToolSummary(\n toolCall: Record<string, unknown>,\n): string | undefined {\n if (isRecord(toolCall.readToolCall)) {\n const args = recordField(toolCall.readToolCall, \"args\");\n return `reading ${stringField(args, \"path\") ?? \"?\"}`;\n }\n if (isRecord(toolCall.editToolCall)) {\n const args = recordField(toolCall.editToolCall, \"args\");\n return `editing ${formatCodexPath(stringField(args, \"path\") ?? \"?\")}`;\n }\n if (isRecord(toolCall.globToolCall)) {\n const args = recordField(toolCall.globToolCall, \"args\");\n return `glob ${stringField(args, \"globPattern\") ?? \"?\"}`;\n }\n if (isRecord(toolCall.grepToolCall)) {\n const args = recordField(toolCall.grepToolCall, \"args\");\n return `grep ${stringField(args, \"pattern\") ?? \"?\"}`;\n }\n if (isRecord(toolCall.shellToolCall)) {\n const args = recordField(toolCall.shellToolCall, \"args\");\n const description = stringField(toolCall.shellToolCall, \"description\");\n const command = stringField(args, \"command\") ?? description ?? \"?\";\n return `bash ${truncate(command, 80)}`;\n }\n return undefined;\n}\n\nfunction truncate(value: string, max: number): string {\n return value.length > max ? `${value.slice(0, max - 3)}...` : value;\n}\n\nfunction formatCodexPath(value: string): string {\n const marker = \"/.almanac/\";\n const idx = value.indexOf(marker);\n if (idx !== -1) {\n return `.almanac/${value.slice(idx + marker.length)}`;\n }\n return value;\n}\n\nfunction stringField(\n input: Record<string, unknown>,\n key: string,\n): string | undefined {\n const value = input[key];\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction recordField(\n input: Record<string, unknown>,\n key: string,\n): Record<string, unknown> {\n const value = input[key];\n return isRecord(value) ? value : {};\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\";\n}\n","import { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/**\n * Loads bundled prompt text from the `prompts/` directory that ships with\n * the npm package. Used by `almanac bootstrap` (slice 4) and `almanac\n * capture` (slice 5).\n *\n * ## Why not embed the prompts as TS string literals?\n *\n * The non-negotiable from the spec (see CLAUDE.md → \"Non-negotiables\"):\n * \"Prompts are shipped from the npm package. They live in `prompts/` at\n * repo root, are bundled into `files` in `package.json`, and the agent\n * harness reads them from the package install path at runtime.\"\n *\n * Keeping them as separate files means they can be reviewed as prose,\n * diffed meaningfully, and (in the future) edited by users without\n * rebuilding the package.\n *\n * ## Path resolution\n *\n * Two runtime layouts need to work:\n *\n * 1. **Installed (`npm i -g codealmanac`).** The entry point lives at\n * `dist/codealmanac.js`; prompts at `prompts/*.md`. Walking up from\n * `import.meta.url` (`.../<pkg>/dist/codealmanac.js`) one level and\n * into `prompts/` hits the right directory.\n *\n * 2. **Source dev.** During `npm run dev`, tsup emits to `dist/` just\n * like in production, so case 1 applies. Tests import\n * `src/agent/prompts.ts` directly via tsx/vitest; `import.meta.url`\n * points at `src/agent/prompts.ts`. Walking up two levels from there\n * lands at the repo root, where `prompts/` also lives.\n *\n * We probe a small list of candidates in order and use the first that\n * contains all three expected prompt files. This keeps a single source of\n * truth — the `prompts/` directory on disk — without baking in whether\n * we're running from `dist/` or `src/`.\n */\n\nexport type PromptName = \"bootstrap\" | \"writer\" | \"reviewer\";\n\nconst PROMPT_NAMES: readonly PromptName[] = [\n \"bootstrap\",\n \"writer\",\n \"reviewer\",\n];\n\n/**\n * Override the prompts directory, for tests. Production code should never\n * call this — the auto-resolution handles both installed + source layouts.\n */\nlet overrideDir: string | null = null;\n\nexport function setPromptsDirForTesting(dir: string | null): void {\n overrideDir = dir;\n}\n\n/**\n * Resolve the prompts directory by probing candidate locations. Cached\n * after the first call so repeated `loadPrompt()` calls don't stat the\n * filesystem more than once per process.\n */\nlet resolvedDir: string | null = null;\n\nexport function resolvePromptsDir(): string {\n if (overrideDir !== null) return overrideDir;\n if (resolvedDir !== null) return resolvedDir;\n\n const here = path.dirname(fileURLToPath(import.meta.url));\n\n // Candidates, most-specific first. Each path is where `prompts/` MIGHT\n // live given some plausible bundle layout. The first one that exists\n // and contains our three expected files wins.\n const candidates = [\n // Bundled dist layout: `.../<pkg>/dist/codealmanac.js` → `../prompts`\n path.resolve(here, \"..\", \"prompts\"),\n // Source layout: `.../<pkg>/src/agent/prompts.ts` → `../../prompts`\n path.resolve(here, \"..\", \"..\", \"prompts\"),\n // Defensive fallback: if tsup someday emits a nested `dist/src/agent`,\n // walk up three levels.\n path.resolve(here, \"..\", \"..\", \"..\", \"prompts\"),\n ];\n\n for (const dir of candidates) {\n if (isPromptsDir(dir)) {\n resolvedDir = dir;\n return dir;\n }\n }\n\n // If none matched, give a helpful error with the candidates we tried.\n // This typically means the package was installed without the `prompts/`\n // dir included — shouldn't happen unless someone broke `files` in\n // package.json.\n throw new Error(\n \"could not locate bundled prompts/ directory. Tried:\\n\" +\n candidates.map((c) => ` - ${c}`).join(\"\\n\"),\n );\n}\n\nfunction isPromptsDir(dir: string): boolean {\n if (!existsSync(dir)) return false;\n // Require all three prompts to be present. A half-populated directory\n // is worse than not finding one — we'd rather error early.\n return PROMPT_NAMES.every((name) =>\n existsSync(path.join(dir, `${name}.md`)),\n );\n}\n\nexport async function loadPrompt(name: PromptName): Promise<string> {\n const dir = resolvePromptsDir();\n return readFile(path.join(dir, `${name}.md`), \"utf8\");\n}\n","import { parseAgentSelection } from \"./provider-view.js\";\nimport {\n isAgentProviderId,\n readConfig,\n type AgentProviderId,\n} from \"../update/config.js\";\n\nexport type AgentSelection =\n | { ok: true; provider: AgentProviderId; model?: string }\n | { ok: false; error: string };\n\nexport async function resolveAgentSelection(args: {\n agent?: string;\n model?: string;\n cwd?: string;\n}): Promise<AgentSelection> {\n const config = await readConfig({ cwd: args.cwd });\n const rawAgent = args.agent ?? process.env.ALMANAC_AGENT ?? config.agent.default;\n const agentSource =\n args.agent !== undefined\n ? \"flag\"\n : process.env.ALMANAC_AGENT !== undefined\n ? \"env\"\n : \"config\";\n const parsed = parseAgentSelection(rawAgent);\n if (parsed.provider === null || !isAgentProviderId(parsed.provider)) {\n return {\n ok: false,\n error:\n `unknown agent '${rawAgent}'. Expected one of: claude, codex, cursor.`,\n };\n }\n const provider = parsed.provider;\n const configuredModel = config.agent.models[provider] ?? undefined;\n const model =\n args.model !== undefined\n ? args.model\n : parsed.model !== undefined && agentSource === \"flag\"\n ? parsed.model\n : process.env.ALMANAC_MODEL !== undefined\n ? process.env.ALMANAC_MODEL\n : parsed.model !== undefined\n ? parsed.model\n : configuredModel === null\n ? undefined\n : configuredModel;\n return { ok: true, provider, model };\n}\n","import type { CommandResult } from \"./helpers.js\";\n\nexport type CommandOutcome =\n | {\n type: \"success\";\n message: string;\n data?: Record<string, unknown>;\n }\n | {\n type: \"noop\";\n message: string;\n data?: Record<string, unknown>;\n }\n | {\n type: \"needs-action\";\n message: string;\n fix: string;\n data?: Record<string, unknown>;\n }\n | {\n type: \"error\";\n message: string;\n data?: Record<string, unknown>;\n };\n\nexport function renderOutcome(\n outcome: CommandOutcome,\n opts: { json?: boolean; exitCode?: number; stdout?: string } = {},\n): CommandResult {\n const exitCode = opts.exitCode ?? defaultExitCode(outcome);\n if (opts.json === true) {\n return {\n stdout: `${JSON.stringify(outcome, null, 2)}\\n`,\n stderr: \"\",\n exitCode,\n };\n }\n if (outcome.type === \"needs-action\") {\n return {\n stdout: opts.stdout ?? \"\",\n stderr: `almanac: ${outcome.message}\\n${outcome.fix}\\n`,\n exitCode,\n };\n }\n if (outcome.type === \"error\") {\n return {\n stdout: opts.stdout ?? \"\",\n stderr: `almanac: ${outcome.message}\\n`,\n exitCode,\n };\n }\n return {\n stdout: opts.stdout ?? `${outcome.message}\\n`,\n stderr: \"\",\n exitCode,\n };\n}\n\nfunction defaultExitCode(outcome: CommandOutcome): number {\n switch (outcome.type) {\n case \"success\":\n case \"noop\":\n return 0;\n case \"needs-action\":\n case \"error\":\n return 1;\n }\n}\n","import type { AgentProviderId } from \"../update/config.js\";\nimport { getAgentProvider, DEFAULT_AGENT_MODEL } from \"./providers/index.js\";\nimport type {\n AgentResult,\n AgentStreamMessage,\n AgentUsage,\n RunAgentOptions,\n} from \"./types.js\";\n\nexport { DEFAULT_AGENT_MODEL };\nexport type {\n AgentResult,\n AgentStreamMessage,\n AgentUsage,\n RunAgentOptions,\n};\n\n/**\n * Public agent facade used by bootstrap/capture. Provider-specific behavior\n * lives in `src/agent/providers/*`; this function only resolves the selected\n * provider and preserves the historical command-facing API.\n */\nexport async function runAgent(opts: RunAgentOptions): Promise<AgentResult> {\n const provider: AgentProviderId = opts.provider ?? \"claude\";\n return await getAgentProvider(provider).run(opts);\n}\n","import { existsSync } from \"node:fs\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { basename, join } from \"node:path\";\n\nimport { findNearestAlmanacDir, getRepoAlmanacDir } from \"../paths.js\";\nimport { toKebabCase } from \"../slug.js\";\nimport {\n addEntry,\n ensureGlobalDir,\n type RegistryEntry,\n} from \"../registry/index.js\";\n\nexport interface InitOptions {\n cwd: string;\n name?: string;\n description?: string;\n}\n\nexport interface InitResult {\n entry: RegistryEntry;\n almanacDir: string;\n created: boolean; // false if .almanac/ already existed (idempotent re-init)\n}\n\n/**\n * Scaffold `.almanac/` in the repo and register it globally.\n *\n * Idempotent: running `init` on a repo that already has `.almanac/` is\n * fine — we re-register (refreshing the name/description) and skip\n * anything that already exists. We never overwrite a user-authored\n * `README.md` or touch existing pages.\n *\n * If `cwd` lives inside a subdirectory of an existing wiki, we walk up to\n * the wiki root and operate there. `almanac init` from `src/nested/`\n * should update the enclosing wiki, not create a nested one at\n * `src/nested/.almanac/` (which would fragment the registry and leave a\n * confusing orphan `.almanac/` on disk).\n */\nexport async function initWiki(options: InitOptions): Promise<InitResult> {\n // If cwd is already inside a wiki, prefer that root. Otherwise treat\n // cwd as the new wiki root.\n const repoRoot = findNearestAlmanacDir(options.cwd) ?? options.cwd;\n\n const almanacDir = getRepoAlmanacDir(repoRoot);\n const pagesDir = join(almanacDir, \"pages\");\n const readmePath = join(almanacDir, \"README.md\");\n\n const alreadyExisted = existsSync(almanacDir);\n\n await mkdir(pagesDir, { recursive: true });\n\n if (!existsSync(readmePath)) {\n await writeFile(readmePath, starterReadme(), \"utf8\");\n }\n\n await ensureGitignoreHasIndexDb(repoRoot);\n\n const name = toKebabCase(options.name ?? basename(repoRoot));\n if (name.length === 0) {\n throw new Error(\n \"could not derive a wiki name from the current directory; pass --name\",\n );\n }\n\n const description = (options.description ?? \"\").trim();\n\n await ensureGlobalDir();\n const entry: RegistryEntry = {\n name,\n description,\n path: repoRoot,\n registered_at: new Date().toISOString(),\n };\n await addEntry(entry);\n\n return { entry, almanacDir, created: !alreadyExisted };\n}\n\n/**\n * Ensure `.gitignore` in the repo root contains the codealmanac-derived\n * files that should never be committed.\n *\n * We ignore three paths:\n * - `.almanac/index.db` — the SQLite index (derived)\n * - `.almanac/index.db-wal` — WAL mode sidecar (present during writes)\n * - `.almanac/index.db-shm` — shared-memory sidecar (WAL mode)\n *\n * All three are derived from the markdown pages. The sidecars only show\n * up while better-sqlite3 has the DB open and can vanish between `git\n * status` calls, but explicitly ignoring them prevents \"dirty worktree\"\n * noise during active reindexing.\n *\n * We add the block regardless of whether the file exists (creating\n * `.gitignore` if needed), and we add any target lines that aren't\n * already present. Existing targets are left alone. If none of the\n * target lines need adding, the file is not touched at all.\n *\n * Formatting: when we do append, we guarantee exactly one blank line\n * between the prior content and our appended block. If the `# codealmanac`\n * header is already present but new targets need adding, we just append\n * the missing lines (no duplicate header).\n */\nasync function ensureGitignoreHasIndexDb(cwd: string): Promise<void> {\n const path = join(cwd, \".gitignore\");\n const targets = [\n \".almanac/index.db\",\n \".almanac/index.db-wal\",\n \".almanac/index.db-shm\",\n // Runtime logs written by the AI pipeline. These can be multi-megabyte\n // JSONL files and should never be committed. Keep the old root-level\n // globs too so repos initialized by earlier releases stay quiet.\n \".almanac/logs/\",\n \".almanac/.capture-*\",\n \".almanac/.bootstrap-*\",\n \".almanac/.ingest-*\",\n ];\n\n let existing = \"\";\n if (existsSync(path)) {\n existing = await readFile(path, \"utf8\");\n }\n\n // Normalize to line comparison to avoid false negatives on trailing\n // whitespace or CRLF line endings.\n const lines = existing.split(/\\r?\\n/).map((l) => l.trim());\n const missing = targets.filter((t) => !lines.includes(t));\n if (missing.length === 0) return;\n\n const hasHeader = lines.includes(\"# codealmanac\");\n const block = hasHeader\n ? missing.join(\"\\n\") + \"\\n\"\n : `# codealmanac\\n${missing.join(\"\\n\")}\\n`;\n\n // Three cases for the separator before the appended block:\n // - empty file: no separator needed\n // - ends with newline: one more newline produces a single blank line\n // - no trailing newline: two newlines (one to terminate the last line,\n // one for the blank separator)\n const sep =\n existing.length === 0 ? \"\" : existing.endsWith(\"\\n\") ? \"\\n\" : \"\\n\\n\";\n await writeFile(path, `${existing}${sep}${block}`, \"utf8\");\n}\n\n/**\n * The starter `.almanac/README.md` content. Based on the \"Wiki README\" and\n * \"Notability bar\" sections of the design spec. Kept opinionated but short\n * (~70 lines) — the user is expected to edit it to fit the repo.\n */\nfunction starterReadme(): string {\n return `# Wiki\n\nThis is the codealmanac wiki for this repository. It captures the knowledge\nthe code itself can't say — decisions, flows, invariants, gotchas, incidents.\n\nThe primary reader is an AI coding agent. The secondary reader is a human\nskimming to understand the shape of the codebase. Write accordingly: dense,\nfactual, linked.\n\n## Notability bar\n\nWrite a page when there is **non-obvious knowledge that will help a future\nagent**. Specifically:\n\n- A decision that took discussion, research, or trial-and-error\n- A gotcha discovered through failure\n- A cross-cutting flow that spans multiple files and isn't obvious from any\n one of them\n- A constraint or invariant not visible from the code\n- An entity (technology, service, system) referenced by multiple pages\n\nDo not write pages that restate what the code does. Do not write pages of\ninference — only of observation. Silence is an acceptable outcome.\n\n## Topic taxonomy\n\nTopics form a DAG; pages can belong to multiple topics. Start with these and\ngrow as the wiki does:\n\n- \\`stack\\` — technologies and services we use (frameworks, databases, APIs)\n- \\`systems\\` — custom systems we built (auth, billing, search)\n- \\`flows\\` — multi-file processes end-to-end (checkout-flow, publish-flow)\n- \\`decisions\\` — \"why X over Y\"\n- \\`incidents\\` — recorded failures and their fixes\n- \\`concepts\\` — shared vocabulary specific to this codebase\n\nDomain topics (\\`auth\\`, \\`payments\\`, \\`frontend\\`, \\`backend\\`) live alongside\nthese. A page about JWT rotation belongs to both \\`auth\\` and \\`decisions\\`.\n\n## Page shapes\n\nFour shapes cover most of what gets written. They are suggestions, not a\nschema — a page that fits none of them is fine.\n\n- **Entity** — a stable named thing (Supabase, Stripe, the search service)\n- **Decision** — why we chose X over Y\n- **Flow** — how a multi-file process works end-to-end\n- **Gotcha** — a specific surprise, failure, or constraint\n\n## Writing conventions\n\n- Every sentence contains a specific fact. If it doesn't, cut it.\n- Neutral tone. \"is\", not \"serves as\". No \"plays a pivotal role\", no\n interpretive \"-ing\" clauses, no vague attribution (\"experts argue\").\n- No hedging or knowledge-gap disclaimers. If you don't know, don't write\n the sentence.\n- Prose first. Bullets for genuine lists. Tables only for structured\n comparison.\n- No formulaic conclusions. End with the last substantive fact.\n\n## Linking\n\nOne \\`[[...]]\\` syntax for everything, disambiguated by content:\n\n- \\`[[checkout-flow]]\\` — page slug\n- \\`[[src/checkout/handler.ts]]\\` — file reference\n- \\`[[src/checkout/]]\\` — folder reference (trailing slash)\n- \\`[[other-wiki:slug]]\\` — cross-wiki reference\n\nEvery page should link to at least one entity when possible. A page with no\nentity link is suspect.\n\n## Pages live in \\`.almanac/pages/\\`\n\nOne markdown file per page, kebab-case slug. Frontmatter carries \\`topics:\\`\nand optional \\`files:\\`. The rest is prose.\n`;\n}\n","import { createHash } from \"node:crypto\";\nimport {\n createWriteStream,\n existsSync,\n statSync,\n type WriteStream,\n} from \"node:fs\";\nimport { mkdir, readFile, readdir, stat } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { basename, join, relative } from \"node:path\";\n\nimport type { AgentDefinition } from \"@anthropic-ai/claude-agent-sdk\";\n\nimport { getProviderDefaultModel } from \"../agent/provider-view.js\";\nimport type { SpawnCliFn } from \"../agent/providers/claude/index.js\";\nimport { assertAgentAuth } from \"../agent/providers.js\";\nimport { loadPrompt } from \"../agent/prompts.js\";\nimport { resolveAgentSelection } from \"../agent/selection.js\";\nimport { renderOutcome } from \"../cli/outcome.js\";\nimport {\n runAgent,\n type AgentResult,\n type AgentStreamMessage,\n type RunAgentOptions,\n} from \"../agent/sdk.js\";\nimport { parseFrontmatter } from \"../indexer/frontmatter.js\";\nimport { findNearestAlmanacDir, getRepoAlmanacDir } from \"../paths.js\";\nimport { formatRunUsage, StreamingFormatter } from \"./bootstrap.js\";\nimport {\n buildStartedCaptureRecord,\n captureStatePath,\n finishCaptureRecord,\n writeCaptureRunRecord,\n} from \"./captureStatus.js\";\n\nexport interface CaptureOptions {\n cwd: string;\n /** Explicit transcript path. Skips auto-resolution. */\n transcriptPath?: string;\n /** Target a specific session ID. */\n sessionId?: string;\n /** Suppress per-tool-use streaming; print only the final summary line. */\n quiet?: boolean;\n /** Model override. Defaults to the SDK default (sonnet-4-6). */\n model?: string;\n /** Agent provider override. Defaults to config precedence. */\n agent?: string;\n /** Emit CommandOutcome JSON instead of human output. */\n json?: boolean;\n /** Injectable agent runner — tests replace this with a fake. */\n runAgent?: (opts: RunAgentOptions) => Promise<AgentResult>;\n /** Injectable auth gate — tests can bypass non-Claude provider CLIs. */\n assertAgentAuthFn?: typeof assertAgentAuth;\n /**\n * Injectable spawner for the Claude auth-status subprocess. Tests pass\n * a stub; production uses `defaultSpawnCli` which shells out to the\n * bundled SDK's `cli.js`.\n */\n spawnCli?: SpawnCliFn;\n /** Clock injection for deterministic log filenames in tests. */\n now?: () => Date;\n /**\n * Override the Claude Code projects directory when auto-resolving a\n * transcript. Production code leaves this undefined and we fall back to\n * `~/.claude/projects`; tests point it at a fixture dir.\n */\n claudeProjectsDir?: string;\n}\n\nexport interface CaptureResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\n/**\n * Tools the writer agent is permitted to use.\n *\n * - `Read` — read the session transcript, existing wiki pages, source files\n * - `Write` / `Edit` — create and update pages under `.almanac/pages/`\n * - `Glob` / `Grep` — navigate the wiki and source code\n * - `Bash` — interrogate the wiki via `almanac search/show/info/list`\n * - `Agent` — invoke the reviewer subagent\n *\n * `WebFetch`/`WebSearch` are intentionally absent: the writer should work\n * from the transcript + repo, not the open internet.\n */\nconst WRITER_TOOLS = [\"Read\", \"Write\", \"Edit\", \"Glob\", \"Grep\", \"Bash\", \"Agent\"];\n\n/**\n * Tools the reviewer subagent is permitted to use. The absence of\n * `Write`/`Edit`/`Agent` is the *only* thing preventing the reviewer from\n * editing files or chaining to further subagents — the SDK enforces this\n * based on the `tools` field in the `AgentDefinition`.\n */\nconst REVIEWER_TOOLS = [\"Read\", \"Grep\", \"Glob\", \"Bash\"];\n\nconst REVIEWER_DESCRIPTION =\n \"Reviews proposed wiki changes against the full knowledge base for \" +\n \"cohesion, duplication, missing links, notability, and writing conventions.\";\n\n/**\n * `almanac capture` — writer agent + reviewer subagent on a session transcript.\n *\n * Flow:\n * 1. Auth gate (ANTHROPIC_API_KEY).\n * 2. Resolve repo root (walk up for `.almanac/`). Refuse if none.\n * 3. Resolve transcript path (arg, --session, or auto-resolve from\n * Claude Code's session storage).\n * 4. Snapshot `.almanac/pages/` BEFORE the agent runs so we can compute\n * a created/updated/archived summary when it finishes.\n * 5. Load `prompts/writer.md` + `prompts/reviewer.md`. Build a reviewer\n * `AgentDefinition` with read-only tools.\n * 6. Run the writer agent with the reviewer registered under `agents`.\n * 7. Stream tool-uses via the shared `StreamingFormatter` (unless --quiet).\n * 8. Diff the snapshot → emit `[done] N updated, M created, K archived …`.\n *\n * Empty outcomes (writer wrote nothing) exit 0 with a clear \"notability bar\"\n * message — per the writer prompt, silence is a valid output.\n */\nexport async function runCapture(\n options: CaptureOptions,\n): Promise<CaptureResult> {\n // Resolve the repo root by walking up for `.almanac/`. Unlike bootstrap,\n // capture refuses to run when no wiki exists — the writer needs existing\n // pages to read against, and auto-initing here would hide the fact that\n // the user skipped `almanac bootstrap`.\n const repoRoot = findNearestAlmanacDir(options.cwd);\n if (repoRoot === null) {\n return renderOutcome(\n {\n type: \"needs-action\",\n message: \"no .almanac/ found in this directory or any parent.\",\n fix: \"run: almanac bootstrap\",\n },\n { json: options.json },\n );\n }\n\n // Fail before any filesystem work. `assertClaudeAuth` accepts either\n // subscription OAuth (via the bundled SDK CLI) or `ANTHROPIC_API_KEY`;\n // missing both surfaces a two-option error with exit 1 so the\n // SessionEnd hook (which backgrounds + redirects to a sidecar log)\n // doesn't silently treat auth failure as a successful capture.\n const providerResolution = await resolveAgentSelection({\n agent: options.agent,\n model: options.model,\n cwd: repoRoot,\n });\n if (!providerResolution.ok) {\n return renderOutcome(\n { type: \"error\", message: providerResolution.error },\n { json: options.json },\n );\n }\n const { provider, model } = providerResolution;\n const statusModel = model ?? getProviderDefaultModel(provider) ?? \"provider default\";\n\n try {\n await (options.assertAgentAuthFn ?? assertAgentAuth)({\n provider,\n spawnCli: options.spawnCli,\n });\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n return renderOutcome(\n {\n type: \"needs-action\",\n message: msg,\n fix: authFixFor(provider),\n data: { provider },\n },\n { json: options.json },\n );\n }\n\n const almanacDir = getRepoAlmanacDir(repoRoot);\n const pagesDir = join(almanacDir, \"pages\");\n\n // Resolve the transcript path up front. Doing this before we open a log\n // stream keeps bad-arg errors uncluttered by side effects.\n const transcriptResolution = await resolveTranscript({\n repoRoot,\n explicit: options.transcriptPath,\n sessionId: options.sessionId,\n claudeProjectsDir: options.claudeProjectsDir,\n });\n if (!transcriptResolution.ok) {\n return renderOutcome(\n {\n type: \"needs-action\",\n message: transcriptResolution.error,\n fix: transcriptFix(transcriptResolution.error),\n },\n { json: options.json },\n );\n }\n const transcriptPath = transcriptResolution.path;\n\n // Snapshot the pages dir BEFORE the writer runs. We compare against it\n // after the agent exits to compute a created/updated/archived tally.\n // Doing this in TS (not via the agent's self-reporting) means the summary\n // stays trustworthy even if the writer gets confused about what it did.\n const snapshotBefore = await snapshotPages(pagesDir);\n\n // Load the two prompts. Kept sequential rather than parallel — both files\n // are tiny and the second read is cache-warm.\n const systemPrompt = await loadPrompt(\"writer\");\n const reviewerPrompt = await loadPrompt(\"reviewer\");\n\n const agents: Record<string, AgentDefinition> = {\n reviewer: {\n description: REVIEWER_DESCRIPTION,\n prompt: reviewerPrompt,\n tools: REVIEWER_TOOLS,\n },\n };\n\n // Transcript log filename. Prefer the Claude Code session-id when we\n // have one (`--session` was passed — which is what the SessionEnd\n // hook does) so the SDK transcript and the hook's human-readable\n // sidecar share a stem in `.almanac/logs`: `.capture-<sid>.jsonl` (this\n // file) alongside `.capture-<sid>.log` (the hook's stdout redirect).\n // `ls -lah .almanac/logs/.capture-<sid>.*` tells one coherent story per\n // session. Fall back\n // to a timestamp for manual `almanac capture` invocations where no\n // session-id is available — we don't have the SDK session_id either\n // at this point; it arrives on the first SDK message, too late to\n // use as a filename.\n //\n // The file extension is `.jsonl` because that's what's actually\n // written (one JSON SDK message per line). Using `.log` here would\n // collide with the hook's stdout-redirect sidecar, which happens to\n // share the session-id stem; distinct extensions keep them from\n // clobbering each other.\n const startedAt = options.now?.() ?? new Date();\n const logStem =\n options.sessionId !== undefined && options.sessionId.length > 0\n ? options.sessionId\n : formatTimestamp(startedAt);\n const logsDir = join(almanacDir, \"logs\");\n await mkdir(logsDir, { recursive: true });\n const logName = `.capture-${logStem}.jsonl`;\n const logPath = join(logsDir, logName);\n const statePath = captureStatePath(logsDir, logStem);\n const stateRecord = buildStartedCaptureRecord({\n repoRoot,\n almanacDir: logsDir,\n stem: logStem,\n sessionId: options.sessionId,\n transcriptPath,\n model: statusModel,\n startedAt,\n });\n await writeCaptureRunRecord(statePath, stateRecord).catch(() => {});\n\n const logStream = createWriteStream(logPath, { flags: \"w\" });\n\n const out = process.stdout;\n const formatter = new StreamingFormatter({\n write: (line: string) => {\n if (options.quiet !== true && options.json !== true) out.write(line);\n },\n });\n // The shared StreamingFormatter defaults its currentAgent to \"bootstrap\"\n // because bootstrap was the first command to use it. For capture the\n // writer owns the top-level turn, so relabel.\n formatter.setAgent(\"writer\");\n\n const onMessage = (msg: AgentStreamMessage): void => {\n try {\n logStream.write(`${JSON.stringify(msg)}\\n`);\n } catch {\n // Best-effort: one unserializable message shouldn't kill the whole\n // stream. Humans read the log; if a line is missing they can re-run.\n }\n formatter.handle(msg);\n };\n\n // Pass an ABSOLUTE path for the transcript so the writer doesn't have to\n // guess at cwd semantics. Everything else (`.almanac/pages/`) is already\n // relative to the cwd the SDK gives its tools.\n const userPrompt =\n `Capture this coding session.\\n` +\n `Transcript: ${transcriptPath}.\\n` +\n `Working directory: ${repoRoot}.`;\n\n const runner = options.runAgent ?? runAgent;\n\n let result: AgentResult;\n try {\n result = await runner({\n systemPrompt,\n prompt: userPrompt,\n allowedTools: WRITER_TOOLS,\n agents,\n cwd: repoRoot,\n provider,\n model,\n // Capture sessions can touch many pages; give it more headroom than\n // bootstrap. The SDK treats `maxTurns` as a hard stop — better to\n // overshoot than to cut off mid-review.\n maxTurns: 150,\n onMessage,\n });\n } finally {\n await closeStream(logStream);\n }\n\n const snapshotAfter = await snapshotPages(pagesDir);\n const delta = diffSnapshots(snapshotBefore, snapshotAfter);\n const finishedAt = options.now?.() ?? new Date();\n const captureSummary = {\n ...delta,\n cost: result.cost,\n turns: result.turns,\n };\n\n if (!result.success) {\n await writeCaptureRunRecord(\n statePath,\n finishCaptureRecord({\n record: stateRecord,\n status: \"failed\",\n finishedAt,\n summary: captureSummary,\n error: result.error ?? \"unknown error\",\n }),\n ).catch(() => {});\n return renderOutcome(\n {\n type: \"error\",\n message:\n `capture failed: ${result.error ?? \"unknown error\"}\\n` +\n `(transcript: ${relative(repoRoot, logPath)})`,\n data: captureOutcomeData(result, delta, logPath, repoRoot),\n },\n {\n json: options.json,\n stdout: \"\",\n },\n );\n }\n\n await writeCaptureRunRecord(\n statePath,\n finishCaptureRecord({\n record: stateRecord,\n status: \"done\",\n finishedAt,\n summary: captureSummary,\n }),\n ).catch(() => {});\n\n const summary = formatSummary(result, delta, logPath, repoRoot);\n\n return renderOutcome(\n {\n type: isNoopDelta(delta) ? \"noop\" : \"success\",\n message: summary,\n data: captureOutcomeData(result, delta, logPath, repoRoot),\n },\n { json: options.json },\n );\n}\n\nfunction transcriptFix(error: string): string {\n if (error.includes(\"transcript not found\")) {\n return \"pass a valid <transcript-path>\";\n }\n return \"pass --session <id> or <transcript-path>\";\n}\n\nfunction authFixFor(provider: string): string {\n switch (provider) {\n case \"claude\":\n return \"run: claude auth login --claudeai (or export ANTHROPIC_API_KEY)\";\n case \"codex\":\n return \"run: codex login\";\n case \"cursor\":\n return \"run: cursor-agent login\";\n default:\n return \"run: almanac agents doctor\";\n }\n}\n\nfunction captureOutcomeData(\n result: AgentResult,\n delta: SnapshotDelta,\n logPath: string,\n repoRoot: string,\n): Record<string, unknown> {\n return {\n transcript: relative(repoRoot, logPath),\n updated: delta.updated,\n created: delta.created,\n archived: delta.archived,\n cost: result.cost,\n turns: result.turns,\n sessionId: result.sessionId,\n };\n}\n\nfunction isNoopDelta(delta: SnapshotDelta): boolean {\n return delta.created === 0 && delta.updated === 0 && delta.archived === 0;\n}\n\n// ─── Transcript resolution ────────────────────────────────────────────────\n\ninterface ResolvedTranscript {\n ok: true;\n path: string;\n}\ninterface FailedTranscript {\n ok: false;\n error: string;\n}\n\n/**\n * Resolve the transcript path from the three possible sources, in priority:\n * 1. Explicit positional arg (`almanac capture <path>`).\n * 2. `--session <id>`: find the single `.jsonl` matching that ID.\n * 3. Auto-resolve: most recent `.jsonl` under Claude Code's projects dir\n * whose parent directory hashes to `repoRoot`. If multiple candidates\n * or none match, return an error directing the user to pass a path.\n *\n * Claude Code names the per-project directory with a path-hash that we\n * can't deterministically reproduce without reading Claude Code's source.\n * Rather than guess at the hashing scheme, we scan all project dirs, pick\n * the one whose most recent transcript mentions the `repoRoot` in its\n * `cwd` field, and take the newest `.jsonl` from there.\n */\nasync function resolveTranscript(args: {\n repoRoot: string;\n explicit?: string;\n sessionId?: string;\n claudeProjectsDir?: string;\n}): Promise<ResolvedTranscript | FailedTranscript> {\n if (args.explicit !== undefined && args.explicit.length > 0) {\n if (!existsSync(args.explicit)) {\n return {\n ok: false,\n error: `transcript not found: ${args.explicit}`,\n };\n }\n return { ok: true, path: args.explicit };\n }\n\n const projectsDir =\n args.claudeProjectsDir ?? join(homedir(), \".claude\", \"projects\");\n if (!existsSync(projectsDir)) {\n return {\n ok: false,\n error:\n `could not auto-resolve transcript; ${projectsDir} does not exist. ` +\n `Pass --session <id> or <transcript-path>.`,\n };\n }\n\n const allTranscripts = await collectTranscripts(projectsDir);\n\n if (args.sessionId !== undefined && args.sessionId.length > 0) {\n const expected = `${args.sessionId}.jsonl`;\n const match = allTranscripts.find((t) => basename(t.path) === expected);\n if (match === undefined) {\n return {\n ok: false,\n error:\n `no transcript found for session ${args.sessionId} under ${projectsDir}`,\n };\n }\n return { ok: true, path: match.path };\n }\n\n // Auto-resolve: prefer transcripts whose `cwd` field matches `repoRoot`,\n // then fall back to the most recently modified if no cwd match is found.\n // We read a peek of each transcript (not the whole file) to check the\n // cwd — JSONL's first line typically carries it.\n const matches = await filterTranscriptsByCwd(allTranscripts, args.repoRoot);\n\n if (matches.length === 0) {\n return {\n ok: false,\n error:\n `could not auto-resolve transcript under ${projectsDir}; ` +\n `no session matches cwd ${args.repoRoot}. ` +\n `Pass --session <id> or <transcript-path>.`,\n };\n }\n\n // Sort by mtime desc and pick the newest.\n matches.sort((a, b) => b.mtime - a.mtime);\n return { ok: true, path: matches[0]!.path };\n}\n\ninterface TranscriptEntry {\n path: string;\n mtime: number;\n}\n\nasync function collectTranscripts(\n projectsDir: string,\n): Promise<TranscriptEntry[]> {\n const out: TranscriptEntry[] = [];\n let topLevel: string[];\n try {\n topLevel = await readdir(projectsDir);\n } catch {\n return out;\n }\n for (const name of topLevel) {\n const projectDir = join(projectsDir, name);\n let entries: string[];\n try {\n entries = await readdir(projectDir);\n } catch {\n continue;\n }\n for (const entry of entries) {\n if (!entry.endsWith(\".jsonl\")) continue;\n const full = join(projectDir, entry);\n try {\n const st = await stat(full);\n if (st.isFile()) {\n out.push({ path: full, mtime: st.mtimeMs });\n }\n } catch {\n // Transient read error, skip.\n }\n }\n }\n return out;\n}\n\n/**\n * Keep only transcripts whose first session record mentions a `cwd` that\n * matches `repoRoot` (exact string match). We also match on the Claude\n * Code project-hash heuristic: the per-project dir name is usually the\n * absolute repo path with `/` replaced by `-`. Falling back to the hash\n * heuristic means we still resolve sanely when the JSONL format changes.\n */\nasync function filterTranscriptsByCwd(\n transcripts: TranscriptEntry[],\n repoRoot: string,\n): Promise<TranscriptEntry[]> {\n const dirHash = `-${repoRoot.replace(/^\\/+/, \"\").replace(/\\//g, \"-\")}`;\n\n const byDirName = transcripts.filter((t) => {\n const parent = basename(join(t.path, \"..\"));\n return parent === dirHash || parent.endsWith(dirHash);\n });\n if (byDirName.length > 0) return byDirName;\n\n // Fallback: peek into each JSONL for a `\"cwd\":\"<repoRoot>\"` needle.\n const needle = `\"cwd\":\"${repoRoot}\"`;\n const hits: TranscriptEntry[] = [];\n for (const t of transcripts) {\n try {\n const head = await readHead(t.path, 4096);\n if (head.includes(needle)) hits.push(t);\n } catch {\n continue;\n }\n }\n return hits;\n}\n\nasync function readHead(path: string, bytes: number): Promise<string> {\n // Small files — just read the whole thing. We only call this on .jsonl\n // files, which can be large, so cap at `bytes` via slicing.\n const content = await readFile(path, \"utf8\");\n return content.length > bytes ? content.slice(0, bytes) : content;\n}\n\n// ─── Snapshot / delta ─────────────────────────────────────────────────────\n\ninterface PageSnapshotEntry {\n slug: string;\n /** SHA-256 of file bytes — cheap, stable, avoids relying on mtime. */\n hash: string;\n /** `true` when the frontmatter has `archived_at` set. */\n archived: boolean;\n}\n\ntype PageSnapshot = Map<string, PageSnapshotEntry>;\n\nasync function snapshotPages(pagesDir: string): Promise<PageSnapshot> {\n const out: PageSnapshot = new Map();\n if (!existsSync(pagesDir)) return out;\n\n let entries: string[];\n try {\n entries = await readdir(pagesDir);\n } catch {\n return out;\n }\n for (const entry of entries) {\n if (!entry.endsWith(\".md\")) continue;\n const slug = entry.slice(0, -3);\n const full = join(pagesDir, entry);\n try {\n const st = statSync(full);\n if (!st.isFile()) continue;\n const content = await readFile(full, \"utf8\");\n const hash = createHash(\"sha256\").update(content).digest(\"hex\");\n const fm = parseFrontmatter(content);\n out.set(slug, {\n slug,\n hash,\n archived: fm.archived_at !== null,\n });\n } catch {\n continue;\n }\n }\n return out;\n}\n\ninterface SnapshotDelta {\n created: number;\n updated: number;\n archived: number;\n}\n\nfunction diffSnapshots(\n before: PageSnapshot,\n after: PageSnapshot,\n): SnapshotDelta {\n let created = 0;\n let updated = 0;\n let archived = 0;\n\n for (const [slug, entry] of after) {\n const prev = before.get(slug);\n if (prev === undefined) {\n created += 1;\n continue;\n }\n if (prev.hash !== entry.hash) {\n // An edit that flips a page from active → archived counts as\n // \"archived\", not \"updated\" — the archive is the semantically\n // interesting thing.\n if (!prev.archived && entry.archived) {\n archived += 1;\n } else {\n updated += 1;\n }\n }\n }\n // Note: we deliberately don't track deleted pages. The writer prompt\n // tells agents to archive (via frontmatter), not delete — a page that\n // disappears entirely is a protocol violation worth surfacing, but not\n // by silently counting it in the summary.\n\n return { created, updated, archived };\n}\n\n// ─── Formatting ───────────────────────────────────────────────────────────\n\nfunction formatSummary(\n result: AgentResult,\n delta: SnapshotDelta,\n logPath: string,\n repoRoot: string,\n): string {\n const rel = relative(repoRoot, logPath);\n const usage = formatRunUsage(result);\n const { created, updated, archived } = delta;\n\n if (created === 0 && updated === 0 && archived === 0) {\n return (\n `[capture] no new knowledge met the notability bar (0 pages written), ` +\n `${usage} (transcript: ${rel})`\n );\n }\n\n return (\n `[done] ${updated} page${updated === 1 ? \"\" : \"s\"} updated, ` +\n `${created} created, ` +\n `${archived} archived, ` +\n `${usage} (transcript: ${rel})`\n );\n}\n\nfunction formatTimestamp(d: Date): string {\n const pad = (n: number): string => n.toString().padStart(2, \"0\");\n return (\n `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-` +\n `${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`\n );\n}\n\nfunction closeStream(stream: WriteStream): Promise<void> {\n return new Promise((resolve) => {\n stream.end(() => resolve());\n });\n}\n","import { existsSync } from \"node:fs\";\nimport { mkdir, readFile, readdir, rename, writeFile } from \"node:fs/promises\";\nimport { dirname, join, relative } from \"node:path\";\n\nimport { DEFAULT_AGENT_MODEL } from \"../agent/sdk.js\";\nimport { findNearestAlmanacDir, getRepoAlmanacDir } from \"../paths.js\";\n\nexport type CaptureRunStatus = \"running\" | \"done\" | \"failed\";\ntype DisplayStatus = CaptureRunStatus | \"stale\";\n\nexport interface CaptureRunSummary {\n created: number;\n updated: number;\n archived: number;\n cost: number;\n turns: number;\n}\n\nexport interface CaptureRunRecord {\n version: 1;\n kind: \"capture\";\n status: CaptureRunStatus;\n sessionId: string;\n repoRoot: string;\n pid: number;\n model: string;\n transcriptPath: string;\n startedAt: string;\n finishedAt?: string;\n durationMs?: number;\n logPath: string;\n jsonlPath: string;\n summary?: CaptureRunSummary;\n error?: string;\n}\n\nexport interface CaptureStatusOptions {\n cwd: string;\n json?: boolean;\n now?: () => Date;\n isPidAlive?: (pid: number) => boolean;\n}\n\nexport interface CaptureStatusResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\nexport function captureStatePath(dir: string, stem: string): string {\n return join(dir, `.capture-${stem}.state.json`);\n}\n\nexport async function writeCaptureRunRecord(\n path: string,\n record: CaptureRunRecord,\n): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n const tmp = `${path}.tmp-${process.pid}`;\n await writeFile(tmp, `${JSON.stringify(record, null, 2)}\\n`, \"utf8\");\n await rename(tmp, path);\n}\n\nexport function buildStartedCaptureRecord(args: {\n repoRoot: string;\n almanacDir: string;\n stem: string;\n sessionId?: string;\n transcriptPath: string;\n model?: string;\n startedAt: Date;\n}): CaptureRunRecord {\n return {\n version: 1,\n kind: \"capture\",\n status: \"running\",\n sessionId: args.sessionId ?? args.stem,\n repoRoot: args.repoRoot,\n pid: process.pid,\n model: args.model ?? DEFAULT_AGENT_MODEL,\n transcriptPath: args.transcriptPath,\n startedAt: args.startedAt.toISOString(),\n logPath: join(args.almanacDir, `.capture-${args.stem}.log`),\n jsonlPath: join(args.almanacDir, `.capture-${args.stem}.jsonl`),\n };\n}\n\nexport function finishCaptureRecord(args: {\n record: CaptureRunRecord;\n status: Exclude<CaptureRunStatus, \"running\">;\n finishedAt: Date;\n summary?: CaptureRunSummary;\n error?: string;\n}): CaptureRunRecord {\n const started = Date.parse(args.record.startedAt);\n const finished = args.finishedAt.getTime();\n return {\n ...args.record,\n status: args.status,\n finishedAt: args.finishedAt.toISOString(),\n durationMs: Number.isFinite(started) ? Math.max(0, finished - started) : undefined,\n summary: args.summary,\n error: args.error,\n };\n}\n\nexport async function runCaptureStatus(\n options: CaptureStatusOptions,\n): Promise<CaptureStatusResult> {\n const repoRoot = findNearestAlmanacDir(options.cwd);\n if (repoRoot === null) {\n return {\n stdout: \"\",\n stderr:\n \"almanac: no .almanac/ found in this directory or any parent. \" +\n \"Run 'almanac bootstrap' first.\\n\",\n exitCode: 1,\n };\n }\n\n const almanacDir = getRepoAlmanacDir(repoRoot);\n const records = await readCaptureRecords(almanacDir);\n const now = options.now?.() ?? new Date();\n const isPidAlive = options.isPidAlive ?? defaultIsPidAlive;\n const views = records\n .map((record) => toView(record, repoRoot, now, isPidAlive))\n .sort((a, b) => b.sortTime - a.sortTime);\n\n if (options.json === true) {\n return {\n stdout: `${JSON.stringify(\n {\n repo: repoRoot,\n captures: views.map(({ sortTime: _sortTime, ...v }) => v),\n },\n null,\n 2,\n )}\\n`,\n stderr: \"\",\n exitCode: 0,\n };\n }\n\n return {\n stdout: formatCaptureStatus(views),\n stderr: \"\",\n exitCode: 0,\n };\n}\n\nasync function readCaptureRecords(\n almanacDir: string,\n): Promise<CaptureRunRecord[]> {\n if (!existsSync(almanacDir)) return [];\n const out: CaptureRunRecord[] = [];\n const dirs = [join(almanacDir, \"logs\"), almanacDir];\n\n for (const dir of dirs) {\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.startsWith(\".capture-\") || !entry.endsWith(\".state.json\")) {\n continue;\n }\n try {\n const parsed = JSON.parse(await readFile(join(dir, entry), \"utf8\")) as unknown;\n if (isCaptureRunRecord(parsed)) out.push(parsed);\n } catch {\n continue;\n }\n }\n }\n return out;\n}\n\nfunction isCaptureRunRecord(value: unknown): value is CaptureRunRecord {\n if (value === null || typeof value !== \"object\") return false;\n const v = value as Partial<CaptureRunRecord>;\n return (\n v.version === 1 &&\n v.kind === \"capture\" &&\n (v.status === \"running\" || v.status === \"done\" || v.status === \"failed\") &&\n typeof v.sessionId === \"string\" &&\n typeof v.repoRoot === \"string\" &&\n typeof v.pid === \"number\" &&\n typeof v.model === \"string\" &&\n typeof v.transcriptPath === \"string\" &&\n typeof v.startedAt === \"string\" &&\n typeof v.logPath === \"string\" &&\n typeof v.jsonlPath === \"string\"\n );\n}\n\ninterface CaptureView {\n status: DisplayStatus;\n sessionId: string;\n model: string;\n elapsedMs: number;\n startedAt: string;\n finishedAt?: string;\n pid: number;\n logPath: string;\n jsonlPath: string;\n summary?: CaptureRunSummary;\n error?: string;\n sortTime: number;\n}\n\nfunction toView(\n record: CaptureRunRecord,\n repoRoot: string,\n now: Date,\n isPidAlive: (pid: number) => boolean,\n): CaptureView {\n const started = Date.parse(record.startedAt);\n const finished = record.finishedAt !== undefined ? Date.parse(record.finishedAt) : undefined;\n const elapsedMs =\n record.durationMs ??\n (Number.isFinite(started)\n ? Math.max(0, (finished ?? now.getTime()) - started)\n : 0);\n const status =\n record.status === \"running\" && !isPidAlive(record.pid) ? \"stale\" : record.status;\n\n return {\n status,\n sessionId: record.sessionId,\n model: record.model,\n elapsedMs,\n startedAt: record.startedAt,\n finishedAt: record.finishedAt,\n pid: record.pid,\n logPath: relative(repoRoot, record.logPath),\n jsonlPath: relative(repoRoot, record.jsonlPath),\n summary: record.summary,\n error:\n status === \"stale\"\n ? \"process ended without a final status\"\n : record.error,\n sortTime: finished ?? (Number.isFinite(started) ? started : 0),\n };\n}\n\nfunction formatCaptureStatus(views: CaptureView[]): string {\n const lines = [\"Capture jobs\", \"\"];\n\n if (views.length === 0) {\n lines.push(\"No capture jobs found.\");\n return `${lines.join(\"\\n\")}\\n`;\n }\n\n const active = views.filter((v) => v.status === \"running\" || v.status === \"stale\");\n const finished = views.filter((v) => v.status === \"done\" || v.status === \"failed\");\n\n if (active.length === 0) {\n lines.push(\"No active captures.\", \"\");\n } else {\n for (const view of active) {\n lines.push(formatRow(view));\n lines.push(` log: ${view.logPath}`);\n if (view.error !== undefined) lines.push(` error: ${view.error}`);\n lines.push(\"\");\n }\n }\n\n if (finished.length > 0) {\n lines.push(active.length === 0 ? \"Last capture:\" : \"Last finished:\");\n for (const view of finished.slice(0, 3)) {\n lines.push(formatRow(view));\n if (view.status === \"failed\") {\n lines.push(` log: ${view.logPath}`);\n if (view.error !== undefined) lines.push(` error: ${view.error}`);\n }\n }\n }\n\n return `${trimTrailingBlank(lines).join(\"\\n\")}\\n`;\n}\n\nfunction formatRow(view: CaptureView): string {\n const status = view.status.padEnd(7, \" \");\n const session = view.sessionId.padEnd(12, \" \");\n const model = view.model.padEnd(17, \" \");\n const elapsed = formatDuration(view.elapsedMs);\n const summary = formatSummary(view);\n return `${status} ${session} ${model} ${elapsed}${summary.length > 0 ? ` ${summary}` : \"\"}`;\n}\n\nfunction formatSummary(view: CaptureView): string {\n if (view.status === \"failed\") return \"failed; see log\";\n if (view.summary === undefined) return \"\";\n const parts: string[] = [];\n if (view.summary.updated > 0) {\n parts.push(`${view.summary.updated} updated`);\n }\n if (view.summary.created > 0) {\n parts.push(`${view.summary.created} created`);\n }\n if (view.summary.archived > 0) {\n parts.push(`${view.summary.archived} archived`);\n }\n return parts.length > 0 ? parts.join(\", \") : \"0 pages written\";\n}\n\nfunction formatDuration(ms: number): string {\n const totalSeconds = Math.max(0, Math.floor(ms / 1000));\n const minutes = Math.floor(totalSeconds / 60);\n const seconds = totalSeconds % 60;\n if (minutes < 60) return `${minutes}m${seconds.toString().padStart(2, \"0\")}s`;\n const hours = Math.floor(minutes / 60);\n const restMinutes = minutes % 60;\n return `${hours}h${restMinutes.toString().padStart(2, \"0\")}m`;\n}\n\nfunction trimTrailingBlank(lines: string[]): string[] {\n while (lines.length > 0 && lines[lines.length - 1] === \"\") {\n lines.pop();\n }\n return lines;\n}\n\nfunction defaultIsPidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n","import { runIndexer, type IndexResult } from \"../indexer/index.js\";\nimport { resolveWikiRoot } from \"../indexer/resolve-wiki.js\";\n\nexport interface ReindexOptions {\n cwd: string;\n wiki?: string;\n}\n\nexport interface ReindexCommandOutput {\n result: IndexResult;\n stdout: string;\n exitCode: number;\n}\n\n/**\n * `almanac reindex` — force a full rebuild.\n *\n * Unlike the implicit reindex every query command triggers, this one\n * prints a summary line so the user gets feedback for an explicitly\n * requested action. The summary is terse on purpose (one line, three\n * numbers) — verbose progress reporting would fight the design rule that\n * the CLI stays quiet by default.\n */\nexport async function runReindex(\n options: ReindexOptions,\n): Promise<ReindexCommandOutput> {\n const repoRoot = await resolveWikiRoot({\n cwd: options.cwd,\n wiki: options.wiki,\n });\n const result = await runIndexer({ repoRoot });\n // Summary wording: \"reindexed: N pages (K updated, R removed)\". When\n // some files were on disk but never made it into the index\n // (slug collisions, ENOENT races, un-sluggable filenames), tack on a\n // `; S skipped` suffix so the user notices. The per-file reason was\n // already written to stderr at indexing time.\n const skipSuffix =\n result.filesSkipped > 0 ? `; ${result.filesSkipped} skipped` : \"\";\n const stdout = `reindexed: ${result.pagesIndexed} page${result.pagesIndexed === 1 ? \"\" : \"s\"} (${result.changed} updated, ${result.removed} removed${skipSuffix})\\n`;\n return { result, stdout, exitCode: 0 };\n}\n","import { Command } from \"commander\";\n\nimport { runBootstrap } from \"../commands/bootstrap.js\";\nimport { runCapture } from \"../commands/capture.js\";\nimport { runCaptureStatus } from \"../commands/captureStatus.js\";\nimport {\n runHookInstall,\n runHookStatus,\n runHookUninstall,\n} from \"../commands/hook.js\";\nimport { runReindex } from \"../commands/reindex.js\";\nimport { autoRegisterIfNeeded } from \"../registry/autoregister.js\";\nimport { deprecationWarning, emit, withWarning } from \"./helpers.js\";\n\nexport function registerWikiLifecycleCommands(program: Command): void {\n program\n .command(\"bootstrap\")\n .description(\n \"scaffold a wiki in this repo via an AI agent (requires ANTHROPIC_API_KEY or Claude subscription)\",\n )\n .option(\"--quiet\", \"suppress per-tool streaming; print only the final line\")\n .option(\"--agent <agent>\", \"agent provider: claude, codex, or cursor\")\n .option(\"--model <model>\", \"override the agent model\")\n .option(\"--force\", \"overwrite an existing populated wiki (default: refuse)\")\n .option(\"--json\", \"emit structured CommandOutcome JSON\")\n .action(\n async (opts: {\n quiet?: boolean;\n agent?: string;\n model?: string;\n force?: boolean;\n json?: boolean;\n }) => {\n const result = await runBootstrap({\n cwd: process.cwd(),\n quiet: opts.quiet,\n agent: opts.agent,\n model: opts.model,\n force: opts.force,\n json: opts.json,\n });\n emit(result);\n },\n );\n\n const capture = program\n .command(\"capture [transcript]\")\n .alias(\"c\")\n .description(\"run the writer/reviewer pipeline on a session (usually automatic)\")\n .option(\"--session <id>\", \"target a specific session by ID\")\n .option(\"--quiet\", \"suppress per-tool streaming; print only the final summary\")\n .option(\"--agent <agent>\", \"agent provider: claude, codex, or cursor\")\n .option(\"--model <model>\", \"override the agent model\")\n .option(\"--json\", \"emit structured CommandOutcome JSON\")\n .action(\n async (\n transcript: string | undefined,\n opts: {\n session?: string;\n quiet?: boolean;\n agent?: string;\n model?: string;\n json?: boolean;\n },\n ) => {\n await autoRegisterIfNeeded(process.cwd());\n const result = await runCapture({\n cwd: process.cwd(),\n transcriptPath: transcript,\n sessionId: opts.session,\n quiet: opts.quiet,\n agent: opts.agent,\n model: opts.model,\n json: opts.json,\n });\n emit(result);\n },\n );\n\n capture\n .command(\"status\")\n .description(\"show running and recent capture jobs\")\n .option(\"--json\", \"emit structured JSON\")\n .action(async (opts: { json?: boolean }) => {\n await autoRegisterIfNeeded(process.cwd());\n const result = await runCaptureStatus({\n cwd: process.cwd(),\n json: opts.json,\n });\n emit(result);\n });\n\n program\n .command(\"ps\")\n .description(\"deprecated alias for capture status\")\n .option(\"--json\", \"emit structured JSON\")\n .action(async (opts: { json?: boolean }) => {\n await autoRegisterIfNeeded(process.cwd());\n const result = await runCaptureStatus({\n cwd: process.cwd(),\n json: opts.json,\n });\n emit(withWarning(\n result,\n deprecationWarning(\"almanac ps\", \"almanac capture status\"),\n ));\n });\n\n const hook = program\n .command(\"hook\")\n .description(\"manage the SessionEnd auto-capture hook\");\n\n hook\n .command(\"install\")\n .description(\"add a SessionEnd entry that runs 'almanac capture' on session end\")\n .option(\"--source <source>\", \"claude, codex, cursor, or all\")\n .action(async (opts: { source?: string }) => {\n const result = await runHookInstall({\n source: normalizeHookSource(opts.source),\n });\n emit(result);\n });\n\n hook\n .command(\"uninstall\")\n .description(\"remove codealmanac's SessionEnd entry; leaves foreign entries alone\")\n .action(async () => {\n const result = await runHookUninstall();\n emit(result);\n });\n\n hook\n .command(\"status\")\n .description(\"report whether the SessionEnd hook is installed\")\n .action(async () => {\n const result = await runHookStatus();\n emit(result);\n });\n\n program\n .command(\"reindex\")\n .description(\"force a full rebuild of .almanac/index.db\")\n .option(\"--wiki <name>\", \"target a specific registered wiki\")\n .action(async (opts: { wiki?: string }) => {\n await autoRegisterIfNeeded(process.cwd());\n const result = await runReindex({\n cwd: process.cwd(),\n wiki: opts.wiki,\n });\n process.stdout.write(result.stdout);\n if (result.exitCode !== 0) process.exitCode = result.exitCode;\n });\n}\n\nfunction normalizeHookSource(\n source: string | undefined,\n): \"claude\" | \"codex\" | \"cursor\" | \"all\" | undefined {\n if (\n source === \"claude\" ||\n source === \"codex\" ||\n source === \"cursor\" ||\n source === \"all\"\n ) {\n return source;\n }\n return undefined;\n}\n","import { Command } from \"commander\";\n\nimport { registerEditCommands } from \"./register-edit-commands.js\";\nimport { registerQueryCommands } from \"./register-query-commands.js\";\nimport { registerSetupCommands } from \"./register-setup-commands.js\";\nimport { registerWikiLifecycleCommands } from \"./register-wiki-lifecycle-commands.js\";\n\nexport function registerCommands(program: Command): void {\n registerQueryCommands(program);\n registerEditCommands(program);\n registerWikiLifecycleCommands(program);\n registerSetupCommands(program);\n}\n"],"mappings":"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","names":["values","parsed","stderr","repoRoot","readFile","join","join","readFile","repoRoot","repoRoot","repoRoot","existsSync","existsSync","join","join","readFile","join","join","readFile","existsSync","mkdir","join","existsSync","readFile","existsSync","readFile","existsSync","readFile","writeFile","basename","join","join","existsSync","writeFile","basename","path","readFile","join","existsSync","mkdir","createWriteStream","existsSync","mkdir","readFile","readdir","basename","join","relative","existsSync","mkdir","readFile","readdir","rename","writeFile","join","relative","join","path","mkdir","writeFile","rename","existsSync","readdir","readFile","relative","authFixFor","join","formatTimestamp","mkdir","createWriteStream","closeStream","relative","formatSummary","existsSync","basename","readdir","path","readFile"]}