codealmanac 0.1.10 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -21
- package/dist/agents-A4II4YJC.js +15 -0
- package/dist/auth-S5DVUIUJ.js +18 -0
- package/dist/{chunk-Z4MWLVS2.js → chunk-447U3GQJ.js} +162 -5
- package/dist/chunk-447U3GQJ.js.map +1 -0
- package/dist/{chunk-QLHJP2XK.js → chunk-D3B2EEHL.js} +12 -8
- package/dist/{chunk-QLHJP2XK.js.map → chunk-D3B2EEHL.js.map} +1 -1
- package/dist/{chunk-AXFPUHBN.js → chunk-F53U6JQG.js} +8 -49
- package/dist/chunk-F53U6JQG.js.map +1 -0
- package/dist/{chunk-BJVZLP6O.js → chunk-MX2EW5MR.js} +3 -3
- package/dist/{chunk-Z6MBJ3D2.js → chunk-QQHIVTXT.js} +6 -4
- package/dist/{chunk-Z6MBJ3D2.js.map → chunk-QQHIVTXT.js.map} +1 -1
- package/dist/chunk-R3URPHGH.js +194 -0
- package/dist/chunk-R3URPHGH.js.map +1 -0
- package/dist/chunk-SSYMRT4I.js +126 -0
- package/dist/chunk-SSYMRT4I.js.map +1 -0
- package/dist/{chunk-QHQ6YH7U.js → chunk-V3QOQSXI.js} +5 -3
- package/dist/{chunk-QHQ6YH7U.js.map → chunk-V3QOQSXI.js.map} +1 -1
- package/dist/chunk-WRUSDYYE.js +97 -0
- package/dist/chunk-WRUSDYYE.js.map +1 -0
- package/dist/{chunk-3LC55TG6.js → chunk-ZDJSJIB6.js} +77 -126
- package/dist/chunk-ZDJSJIB6.js.map +1 -0
- package/dist/{cli-W3OYVJYH.js → cli-CMGYLJSN.js} +52 -13
- package/dist/cli-CMGYLJSN.js.map +1 -0
- package/dist/codealmanac.js +1 -1
- package/dist/doctor-BTH7GCFV.js +17 -0
- package/dist/{hook-CRJMWSSO.js → hook-2NP3UE7U.js} +2 -2
- package/dist/{register-commands-JHC2OFKM.js → register-commands-7SKQLQW4.js} +330 -32
- package/dist/register-commands-7SKQLQW4.js.map +1 -0
- package/dist/uninstall-FDIOBAAR.js +15 -0
- package/dist/uninstall-FDIOBAAR.js.map +1 -0
- package/dist/update-RAF7QRYF.js +11 -0
- package/dist/update-RAF7QRYF.js.map +1 -0
- package/hooks/almanac-capture.sh +40 -7
- package/package.json +1 -1
- package/dist/chunk-3LC55TG6.js.map +0 -1
- package/dist/chunk-AXFPUHBN.js.map +0 -1
- package/dist/chunk-Z4MWLVS2.js.map +0 -1
- package/dist/cli-W3OYVJYH.js.map +0 -1
- package/dist/doctor-ODFNJUKH.js +0 -15
- package/dist/register-commands-JHC2OFKM.js.map +0 -1
- package/dist/uninstall-HE2Z2LN2.js +0 -12
- package/dist/update-IL243I4E.js +0 -10
- /package/dist/{doctor-ODFNJUKH.js.map → agents-A4II4YJC.js.map} +0 -0
- /package/dist/{hook-CRJMWSSO.js.map → auth-S5DVUIUJ.js.map} +0 -0
- /package/dist/{chunk-BJVZLP6O.js.map → chunk-MX2EW5MR.js.map} +0 -0
- /package/dist/{uninstall-HE2Z2LN2.js.map → doctor-BTH7GCFV.js.map} +0 -0
- /package/dist/{update-IL243I4E.js.map → hook-2NP3UE7U.js.map} +0 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
removeImportLine,
|
|
4
|
+
runUninstall
|
|
5
|
+
} from "./chunk-MX2EW5MR.js";
|
|
6
|
+
import "./chunk-ZDJSJIB6.js";
|
|
7
|
+
import "./chunk-447U3GQJ.js";
|
|
8
|
+
import "./chunk-SSYMRT4I.js";
|
|
9
|
+
import "./chunk-WRUSDYYE.js";
|
|
10
|
+
import "./chunk-7JUX4ADQ.js";
|
|
11
|
+
export {
|
|
12
|
+
removeImportLine,
|
|
13
|
+
runUninstall
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=uninstall-FDIOBAAR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/hooks/almanac-capture.sh
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# codealmanac SessionEnd hook
|
|
3
3
|
#
|
|
4
|
-
# Claude
|
|
4
|
+
# Claude, Codex, and Cursor invoke this on session end/stop with JSON on stdin.
|
|
5
|
+
# Common payload fields:
|
|
5
6
|
# { "session_id": "...", "transcript_path": "...", "cwd": "..." }
|
|
7
|
+
# Cursor may omit cwd and provide workspace_roots instead.
|
|
6
8
|
#
|
|
7
9
|
# We walk upward from cwd looking for a `.almanac/` directory. If found, we
|
|
8
10
|
# background `almanac capture` (so session end never waits on us) and
|
|
@@ -28,13 +30,21 @@ fi
|
|
|
28
30
|
INPUT=$(cat)
|
|
29
31
|
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty')
|
|
30
32
|
TRANSCRIPT=$(echo "$INPUT" | jq -r '.transcript_path // empty')
|
|
31
|
-
CWD=$(echo "$INPUT" | jq -r '.cwd // empty')
|
|
33
|
+
CWD=$(echo "$INPUT" | jq -r '.cwd // .workspace_roots[0] // empty')
|
|
34
|
+
FINAL_STATUS=$(echo "$INPUT" | jq -r '.final_status // .reason // empty')
|
|
35
|
+
HOOK_EVENT=$(echo "$INPUT" | jq -r '.hook_event_name // empty')
|
|
32
36
|
|
|
33
37
|
# Without a transcript or cwd there's nothing for capture to do. Don't
|
|
34
38
|
# error — Claude Code doesn't care, and the user doesn't want a scary log.
|
|
35
39
|
[ -z "$TRANSCRIPT" ] && exit 0
|
|
36
40
|
[ -z "$CWD" ] && exit 0
|
|
37
41
|
|
|
42
|
+
# Cursor passes final_status; skip aborted/failed sessions. Claude/Codex
|
|
43
|
+
# payloads do not always include this, so empty remains allowed.
|
|
44
|
+
if [ -n "$FINAL_STATUS" ] && [ "$FINAL_STATUS" != "completed" ] && [ "$FINAL_STATUS" != "stop" ]; then
|
|
45
|
+
exit 0
|
|
46
|
+
fi
|
|
47
|
+
|
|
38
48
|
# Walk up from cwd looking for a wiki. Bound the loop at the filesystem
|
|
39
49
|
# root ('/') so we can't infinite-loop if cwd is weird.
|
|
40
50
|
DIR="$CWD"
|
|
@@ -53,15 +63,38 @@ while [ "$DIR" != "/" ] && [ -n "$DIR" ]; do
|
|
|
53
63
|
exit 0
|
|
54
64
|
fi
|
|
55
65
|
|
|
66
|
+
run_capture() {
|
|
67
|
+
cd "$DIR" && \
|
|
68
|
+
$CMD capture "$TRANSCRIPT" --session "$SESSION_ID" --quiet \
|
|
69
|
+
> "$LOG_DIR/.capture-$SESSION_ID.log" 2>&1
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# Codex Stop is turn-scoped, not session-scoped. Debounce it so an
|
|
73
|
+
# interactive session that pauses between turns doesn't run capture
|
|
74
|
+
# repeatedly. Each Stop refreshes a marker; only the last quiet marker
|
|
75
|
+
# gets to run capture after the delay.
|
|
76
|
+
if [ "$HOOK_EVENT" = "Stop" ]; then
|
|
77
|
+
DELAY="${CODEALMANAC_CAPTURE_DEBOUNCE_SECONDS:-120}"
|
|
78
|
+
MARKER="$LOG_DIR/.capture-$SESSION_ID.debounce"
|
|
79
|
+
NOW=$(date +%s)
|
|
80
|
+
echo "$NOW" > "$MARKER" || exit 0
|
|
81
|
+
(
|
|
82
|
+
sleep "$DELAY"
|
|
83
|
+
[ -f "$MARKER" ] || exit 0
|
|
84
|
+
CURRENT=$(cat "$MARKER" 2>/dev/null || true)
|
|
85
|
+
[ "$CURRENT" = "$NOW" ] || exit 0
|
|
86
|
+
run_capture
|
|
87
|
+
rm -f "$MARKER"
|
|
88
|
+
) &
|
|
89
|
+
disown $! 2>/dev/null || true
|
|
90
|
+
exit 0
|
|
91
|
+
fi
|
|
92
|
+
|
|
56
93
|
# Background the capture, redirect all output to a session-scoped log.
|
|
57
94
|
# `--quiet` keeps streaming off (the log still captures the raw
|
|
58
95
|
# SDK transcript); `--session` lets the capture command name its
|
|
59
96
|
# own log file consistently if desired.
|
|
60
|
-
(
|
|
61
|
-
cd "$DIR" && \
|
|
62
|
-
$CMD capture "$TRANSCRIPT" --session "$SESSION_ID" --quiet \
|
|
63
|
-
> "$LOG_DIR/.capture-$SESSION_ID.log" 2>&1
|
|
64
|
-
) &
|
|
97
|
+
( run_capture ) &
|
|
65
98
|
# Detach so the shell doesn't wait on the subprocess.
|
|
66
99
|
disown $! 2>/dev/null || true
|
|
67
100
|
exit 0
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/setup.ts","../src/agent/auth.ts","../src/commands/setup/install-path.ts","../src/commands/setup/next-steps.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport {\n copyFile,\n mkdir,\n readFile,\n writeFile,\n} from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport {\n checkClaudeAuth,\n type ClaudeAuthStatus,\n type SpawnCliFn,\n UNAUTHENTICATED_MESSAGE,\n} from \"../agent/auth.js\";\nimport { runHookInstall } from \"./hook.js\";\nimport {\n detectCurrentInstallPath,\n detectEphemeral,\n spawnGlobalInstall,\n} from \"./setup/install-path.js\";\nimport {\n countExistingPages,\n printNextSteps,\n} from \"./setup/next-steps.js\";\n\n/**\n * `codealmanac setup` — the MCP-style branded TUI that runs when a user\n * invokes the bare `codealmanac` binary (or `almanac setup` / `codealmanac\n * setup` explicitly).\n *\n * Model: `mcp-ts/src/setup.ts` from openalmanac. Same ASCII banner + badge\n * + step-indicator style, same interactive + `--yes` + non-interactive\n * modes.\n *\n * Three things get installed:\n *\n * 1. The `SessionEnd` hook in `~/.claude/settings.json` (delegated to\n * `runHookInstall` from `./hook.ts`).\n * 2. The short \"how to use codealmanac\" guide at\n * `~/.claude/codealmanac.md`, sourced from `guides/mini.md` in the\n * package.\n * 3. The full reference at `~/.claude/codealmanac-reference.md`,\n * sourced from `guides/reference.md`.\n * 4. An `@~/.claude/codealmanac.md` import line in `~/.claude/CLAUDE.md`\n * so Claude Code picks up the short guide globally.\n *\n * Everything is idempotent — running setup again is safe. `--skip-hook`\n * and `--skip-guides` opt out of the individual installs. `--yes` or a\n * non-TTY stdin skips all prompts and installs everything.\n */\n\nexport interface SetupOptions {\n /** Install everything without prompting. */\n yes?: boolean;\n /** Don't install the SessionEnd hook. */\n skipHook?: boolean;\n /** Don't install the CLAUDE.md guides. */\n skipGuides?: boolean;\n\n // ─── Injection points (tests only) ────────────────────────────────\n /** Override the subprocess spawner for `claude auth status`. */\n spawnCli?: SpawnCliFn;\n /** Override `~/.claude/settings.json` path. */\n settingsPath?: string;\n /** Override the bundled hook script path. */\n hookScriptPath?: string;\n /** Override the stable hooks directory for the hook script copy. */\n stableHooksDir?: string;\n /** Override `~/.claude/` dir for guide install. */\n claudeDir?: string;\n /** Override the directory containing `mini.md` / `reference.md`. */\n guidesDir?: string;\n /** Override interactivity; defaults to `process.stdin.isTTY`. */\n isTTY?: boolean;\n /** Stdout sink; defaults to `process.stdout`. */\n stdout?: NodeJS.WritableStream;\n /**\n * Override the install-path probe result. When `null` the probe is\n * bypassed (tests that don't care about the ephemeral-path step).\n * When a string it's treated as the detected install path.\n */\n installPath?: string | null;\n /**\n * Override the npm global install spawner (tests inject a no-op to\n * avoid actually spawning npm during CI).\n */\n spawnGlobalInstall?: () => Promise<void>;\n}\n\nexport interface SetupResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\n// ─── ANSI helpers ────────────────────────────────────────────────────\n\nconst RST = \"\\x1b[0m\";\nconst DIM = \"\\x1b[2m\";\nconst WHITE_BOLD = \"\\x1b[1;37m\";\nconst BLUE = \"\\x1b[38;5;75m\";\nconst ACCENT_BG = \"\\x1b[48;5;252m\\x1b[38;5;16m\";\n\nconst GRADIENT = [\n \"\\x1b[38;5;255m\",\n \"\\x1b[38;5;253m\",\n \"\\x1b[38;5;251m\",\n \"\\x1b[38;5;249m\",\n \"\\x1b[38;5;246m\",\n \"\\x1b[38;5;243m\",\n];\n\n// `codealmanac` 11-letter ASCII banner. Chosen for tasteful rendering —\n// same banner used in the MCP setup wizard design, retooled letters for\n// the word \"codealmanac\". Each glyph is 6 lines tall.\n//\n// If you tweak this, keep it to ≤80 visual columns wide so it fits in\n// narrow terminals (80 cols is the classic default).\nconst LOGO_LINES = [\n \" ___ ___ ___ ___ _ _ __ __ _ _ _ _ ___ \",\n \" / __/ _ \\\\| \\\\| __| /_\\\\ | | | \\\\/ | /_\\\\ | \\\\| | /_\\\\ / __|\",\n \"| (_| (_) | |) | _| / _ \\\\| |__| |\\\\/| |/ _ \\\\| .` |/ _ \\\\ (__ \",\n \" \\\\___\\\\___/|___/|___/_/ \\\\_\\\\____|_| |_/_/ \\\\_\\\\_|\\\\_/_/ \\\\_\\\\___|\",\n \" \",\n \" a living wiki for codebases, for your agent \",\n];\n\nconst BAR = ` ${DIM}\\u2502${RST}`;\n\nfunction printBanner(out: NodeJS.WritableStream): void {\n out.write(\"\\n\");\n for (let i = 0; i < LOGO_LINES.length; i++) {\n const color = GRADIENT[Math.min(i, GRADIENT.length - 1)] ?? \"\";\n out.write(`${color}${LOGO_LINES[i]}${RST}\\n`);\n }\n out.write(`\\n${WHITE_BOLD} Install the hook + agent guides${RST}\\n`);\n}\n\nfunction printBadge(out: NodeJS.WritableStream): void {\n out.write(`\\n ${ACCENT_BG} codealmanac ${RST}\\n\\n`);\n}\n\nfunction stepDone(out: NodeJS.WritableStream, msg: string): void {\n out.write(` ${BLUE}\\u25c7${RST} ${msg}\\n`);\n}\n\nfunction stepActive(out: NodeJS.WritableStream, msg: string): void {\n out.write(` ${BLUE}\\u25c6${RST} ${msg}\\n`);\n}\n\nfunction stepSkipped(out: NodeJS.WritableStream, msg: string): void {\n out.write(` ${DIM}\\u25cb ${msg}${RST}\\n`);\n}\n\n// ─── Entry point ─────────────────────────────────────────────────────\n\nexport async function runSetup(\n options: SetupOptions = {},\n): Promise<SetupResult> {\n const out = options.stdout ?? process.stdout;\n const isTTY =\n options.isTTY ?? (process.stdin.isTTY === true);\n const interactive = isTTY && options.yes !== true;\n\n // No-op fast path. When the caller explicitly skipped every install\n // step, rendering the full banner + step markers + \"Setup complete\"\n // box is actively misleading — nothing was actually set up. Emit a\n // single terse line and exit so the user gets honest feedback and\n // piped callers (CI, scripts) don't parse through nine lines of ANSI\n // to conclude nothing happened.\n if (options.skipHook === true && options.skipGuides === true) {\n out.write(\n \"codealmanac: nothing to install — use --help to see what setup does\\n\",\n );\n return { stdout: \"\", stderr: \"\", exitCode: 0 };\n }\n\n printBanner(out);\n printBadge(out);\n\n // Step 1: auth status. We report what we find; we don't block. The user\n // can still install the hook + guides without being logged in — they'll\n // hit the auth wall on first `capture`, not on setup.\n const auth = await safeCheckAuth(options.spawnCli);\n reportAuth(out, auth);\n out.write(BAR + \"\\n\");\n\n // Step 1b: ephemeral install detection. When codealmanac was invoked via\n // `npx codealmanac` (no prior `npm i -g`), the binary lives inside an\n // npx cache directory or pnpm store that can be evicted at any time.\n // `almanac` is also not on PATH, so the user can't use it after setup.\n //\n // When we detect an ephemeral location, we offer (or, on --yes, perform)\n // a `npm install -g codealmanac` to make the install permanent.\n //\n // This is Bug #2 from codealmanac-known-bugs.md.\n const ephem = options.installPath !== undefined\n ? (options.installPath !== null\n ? detectEphemeral(options.installPath)\n : false)\n : detectEphemeral(detectCurrentInstallPath());\n if (ephem) {\n let globalAction: InstallDecision = \"install\";\n if (interactive) {\n globalAction = await confirm(\n out,\n `Running from an ephemeral npx location. Install globally so 'almanac' stays on PATH?`,\n true,\n );\n }\n if (globalAction === \"install\") {\n stepActive(out, \"Installing codealmanac globally…\");\n try {\n await (options.spawnGlobalInstall ?? spawnGlobalInstall)();\n stepDone(out, \"codealmanac installed globally (almanac now on PATH)\");\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n stepActive(out, `Global install failed: ${msg}`);\n out.write(\n ` ${DIM}You can retry manually: npm install -g codealmanac${RST}\\n`,\n );\n }\n } else {\n stepSkipped(\n out,\n `Global install ${DIM}skipped — almanac will not be on PATH after this session${RST}`,\n );\n }\n out.write(BAR + \"\\n\");\n }\n\n // Step 2: install the hook (default yes).\n let hookAction: InstallDecision = \"install\";\n if (options.skipHook === true) {\n hookAction = \"skip\";\n } else if (interactive) {\n hookAction = await confirm(\n out,\n \"Install the SessionEnd hook so capture runs at the end of every Claude Code session?\",\n true,\n );\n }\n\n let hookResultLine = \"\";\n if (hookAction === \"install\") {\n const res = await runHookInstall({\n settingsPath: options.settingsPath,\n hookScriptPath: options.hookScriptPath,\n stableHooksDir: options.stableHooksDir,\n });\n if (res.exitCode !== 0) {\n stepActive(out, `SessionEnd hook: ${res.stderr.trim()}`);\n return {\n stdout: \"\",\n stderr: res.stderr,\n exitCode: res.exitCode,\n };\n }\n hookResultLine = res.stdout.includes(\"already installed\")\n ? `SessionEnd hook ${DIM}already installed${RST}`\n : `SessionEnd hook installed`;\n stepDone(out, hookResultLine);\n } else {\n stepSkipped(out, `SessionEnd hook ${DIM}skipped${RST}`);\n }\n out.write(BAR + \"\\n\");\n\n // Step 3: install the guides.\n let guidesAction: InstallDecision = \"install\";\n if (options.skipGuides === true) {\n guidesAction = \"skip\";\n } else if (interactive) {\n guidesAction = await confirm(\n out,\n \"Install the codealmanac usage guides into ~/.claude/ and import them from CLAUDE.md?\",\n true,\n );\n }\n\n let guidesSummary: string;\n if (guidesAction === \"install\") {\n try {\n const summary = await installGuides({\n claudeDir: options.claudeDir ?? path.join(homedir(), \".claude\"),\n guidesDir: options.guidesDir ?? resolveGuidesDir(),\n });\n guidesSummary = summary.anyChanges\n ? `Guides installed (${summary.filesWritten.join(\", \")})`\n : `Guides ${DIM}already installed${RST}`;\n stepDone(out, guidesSummary);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n return {\n stdout: \"\",\n stderr: `almanac: guide install failed: ${msg}\\n`,\n exitCode: 1,\n };\n }\n } else {\n stepSkipped(out, `Guides ${DIM}skipped${RST}`);\n }\n out.write(BAR + \"\\n\");\n\n stepDone(out, `${BLUE}Setup complete${RST}`);\n out.write(\"\\n\");\n\n // Detect whether the current working directory is inside a repo that\n // already has a wiki with pages. This fixes Bug #6 from\n // codealmanac-known-bugs.md: Engineer B clones a repo that already has\n // `.almanac/pages/` (committed by Engineer A) and gets told to run\n // `almanac bootstrap`, which is wrong — the wiki already exists.\n const existingPageCount = countExistingPages(process.cwd());\n printNextSteps(out, existingPageCount);\n\n return { stdout: \"\", stderr: \"\", exitCode: 0 };\n}\n\n// ─── Auth reporting ──────────────────────────────────────────────────\n\nasync function safeCheckAuth(\n spawnCli?: SpawnCliFn,\n): Promise<ClaudeAuthStatus> {\n try {\n return await checkClaudeAuth(spawnCli);\n } catch {\n return { loggedIn: false };\n }\n}\n\nfunction reportAuth(\n out: NodeJS.WritableStream,\n auth: ClaudeAuthStatus,\n): void {\n if (auth.loggedIn) {\n const who = auth.email ?? \"Claude account\";\n const plan =\n auth.subscriptionType !== undefined\n ? ` ${DIM}(${auth.subscriptionType})${RST}`\n : \"\";\n stepDone(out, `Claude auth: ${WHITE_BOLD}${who}${RST}${plan}`);\n return;\n }\n if (\n process.env.ANTHROPIC_API_KEY !== undefined &&\n process.env.ANTHROPIC_API_KEY.length > 0\n ) {\n stepDone(out, `Claude auth: ${WHITE_BOLD}ANTHROPIC_API_KEY${RST} set`);\n return;\n }\n // Not blocking — just report and show the two paths.\n stepActive(out, `Claude auth: ${DIM}not signed in${RST}`);\n for (const line of UNAUTHENTICATED_MESSAGE.split(\"\\n\")) {\n out.write(` ${DIM}\\u2502 ${line}${RST}\\n`);\n }\n}\n\n// ─── Guide installation ──────────────────────────────────────────────\n\ninterface InstallGuidesOptions {\n claudeDir: string;\n guidesDir: string;\n}\n\ninterface InstallGuidesResult {\n anyChanges: boolean;\n filesWritten: string[];\n}\n\n/**\n * Copy the two guide files into `~/.claude/` and append an `@import`\n * line to `~/.claude/CLAUDE.md`. Every step is idempotent:\n *\n * - Guide files are compared by bytes before we write. If the content\n * matches the bundled version, we skip (so `setup` doesn't cause a\n * spurious mtime bump on every invocation).\n * - The import line is appended only if `CLAUDE.md` doesn't already\n * contain the exact `@~/.claude/codealmanac.md` token on a line by\n * itself. We don't try to parse the file — any mention of the token\n * on a non-comment line is treated as \"already present\".\n *\n * Returns a summary the caller uses to decide whether to say \"installed\"\n * or \"already installed\" in the TUI.\n */\nasync function installGuides(\n options: InstallGuidesOptions,\n): Promise<InstallGuidesResult> {\n await mkdir(options.claudeDir, { recursive: true });\n\n const srcMini = path.join(options.guidesDir, \"mini.md\");\n const srcRef = path.join(options.guidesDir, \"reference.md\");\n if (!existsSync(srcMini)) {\n throw new Error(`missing bundled guide: ${srcMini}`);\n }\n if (!existsSync(srcRef)) {\n throw new Error(`missing bundled guide: ${srcRef}`);\n }\n\n const destMini = path.join(options.claudeDir, \"codealmanac.md\");\n const destRef = path.join(options.claudeDir, \"codealmanac-reference.md\");\n\n const miniChanged = await copyIfChanged(srcMini, destMini);\n const refChanged = await copyIfChanged(srcRef, destRef);\n\n const claudeMd = path.join(options.claudeDir, \"CLAUDE.md\");\n const importChanged = await ensureImport(claudeMd);\n\n const filesWritten: string[] = [];\n if (miniChanged) filesWritten.push(\"codealmanac.md\");\n if (refChanged) filesWritten.push(\"codealmanac-reference.md\");\n if (importChanged) filesWritten.push(\"CLAUDE.md\");\n\n return { anyChanges: filesWritten.length > 0, filesWritten };\n}\n\nasync function copyIfChanged(src: string, dest: string): Promise<boolean> {\n const srcBytes = await readFile(src);\n if (existsSync(dest)) {\n try {\n const destBytes = await readFile(dest);\n if (srcBytes.equals(destBytes)) return false;\n } catch {\n // Fall through to write.\n }\n }\n await copyFile(src, dest);\n return true;\n}\n\n/** The exact import line we manage. Changing this requires updating\n * uninstall too. */\nexport const IMPORT_LINE = \"@~/.claude/codealmanac.md\";\n\n/**\n * Append the import line to `~/.claude/CLAUDE.md` if it isn't already\n * present. Creates the file if absent. Returns true when we wrote, false\n * when the line was already there.\n *\n * We match on `@~/.claude/codealmanac.md` appearing on any non-empty\n * line (trimmed). This catches both the bare line we write and any\n * user-edited variant (comments, trailing whitespace). We deliberately\n * do NOT try to repair a user who deleted the newline — that's their\n * file to shape.\n */\nasync function ensureImport(claudeMdPath: string): Promise<boolean> {\n let existing = \"\";\n if (existsSync(claudeMdPath)) {\n existing = await readFile(claudeMdPath, \"utf8\");\n }\n if (hasImportLine(existing)) return false;\n\n const sep =\n existing.length === 0 ? \"\" : existing.endsWith(\"\\n\") ? \"\\n\" : \"\\n\\n\";\n const body = `${existing}${sep}${IMPORT_LINE}\\n`;\n await writeFile(claudeMdPath, body, \"utf8\");\n return true;\n}\n\nexport function hasImportLine(contents: string): boolean {\n // Match line-starts-with-token rather than exact-line equality so a\n // user who annotated the import line (`@~/.claude/codealmanac.md #\n // codealmanac`) doesn't cause us to re-append a duplicate below.\n // The trailing-character check rules out accidental matches on a\n // longer line like `@~/.claude/codealmanac.md-extra`.\n const lines = contents.split(/\\r?\\n/).map((l) => l.trim());\n return lines.some((line) => {\n if (line === IMPORT_LINE) return true;\n if (!line.startsWith(IMPORT_LINE)) return false;\n const next = line[IMPORT_LINE.length];\n return next === \" \" || next === \"\\t\";\n });\n}\n\n// ─── Interactive prompt ──────────────────────────────────────────────\n\ntype InstallDecision = \"install\" | \"skip\";\n\n/**\n * Minimal `[Y/n]` prompt. No raw mode, no cursor — just readline. The\n * MCP setup uses a fancy arrow-key TUI for multi-choice; we only have\n * binary decisions here, so a line-reader prompt is clearer and doesn't\n * fight with the step-indicator rendering above it.\n */\nfunction confirm(\n out: NodeJS.WritableStream,\n question: string,\n defaultYes: boolean,\n): Promise<InstallDecision> {\n return new Promise((resolve) => {\n const hint = defaultYes ? \"[Y/n]\" : \"[y/N]\";\n out.write(` ${BLUE}\\u25c6${RST} ${question} ${DIM}${hint}${RST} `);\n\n let buf = \"\";\n const onData = (chunk: Buffer): void => {\n buf += chunk.toString(\"utf8\");\n const nl = buf.indexOf(\"\\n\");\n if (nl === -1) return;\n process.stdin.removeListener(\"data\", onData);\n process.stdin.pause();\n\n const answer = buf.slice(0, nl).trim().toLowerCase();\n const accepted =\n answer.length === 0\n ? defaultYes\n : answer === \"y\" || answer === \"yes\";\n resolve(accepted ? \"install\" : \"skip\");\n };\n\n process.stdin.resume();\n process.stdin.on(\"data\", onData);\n });\n}\n\n// ─── Guides path resolution ──────────────────────────────────────────\n\n/**\n * Locate `guides/` relative to the installed package. Mirrors\n * `resolvePromptsDir` from `src/agent/prompts.ts`.\n *\n * Two runtime layouts to handle:\n *\n * 1. **Bundled dist.** `dist/codealmanac.js` → walk one level up →\n * `guides/`.\n * 2. **Source (tests / tsx).** `src/commands/setup.ts` → walk two\n * levels up → `guides/`.\n *\n * We also try `createRequire` to resolve the package root from the\n * `codealmanac/package.json` manifest, as a belt-and-suspenders fallback\n * for unusual install layouts (monorepo hoisting, etc.). That path is\n * only exercised when the direct walk-up fails.\n */\nexport function resolveGuidesDir(): string {\n const here = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(here, \"..\", \"guides\"), // dist layout\n path.resolve(here, \"..\", \"..\", \"guides\"), // src layout\n path.resolve(here, \"..\", \"..\", \"..\", \"guides\"),\n ];\n for (const dir of candidates) {\n if (looksLikeGuidesDir(dir)) return dir;\n }\n // Fallback: resolve via the package.json of the currently-running\n // codealmanac. createRequire lets us ask Node's resolver rather than\n // guessing at directory layouts.\n try {\n const require = createRequire(import.meta.url);\n const pkgJson = require.resolve(\"codealmanac/package.json\");\n const guides = path.join(path.dirname(pkgJson), \"guides\");\n if (looksLikeGuidesDir(guides)) return guides;\n } catch {\n // Ignore — we'll throw with the candidate list below.\n }\n throw new Error(\n \"could not locate bundled guides/ directory. Tried:\\n\" +\n candidates.map((c) => ` - ${c}`).join(\"\\n\"),\n );\n}\n\nfunction looksLikeGuidesDir(dir: string): boolean {\n return existsSync(path.join(dir, \"mini.md\"));\n}\n","import { spawn, spawnSync, type ChildProcess } from \"node:child_process\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join } from \"node:path\";\n\n/**\n * Claude auth gate — accepts either an active Claude subscription login\n * OR an `ANTHROPIC_API_KEY` environment variable.\n *\n * Claude Code owns subscription OAuth credentials. Users who are logged in\n * via `claude auth login --claudeai` should be able to run bootstrap/capture\n * without exporting an API key. Conversely, users on pay-per-token API keys\n * shouldn't be required to go through the OAuth flow.\n *\n * Current Claude Agent SDK packages no longer ship the old private\n * `cli.js` entrypoint, so the primary probe is the public Claude Code CLI:\n * `claude auth status --json`. We keep the SDK `cli.js` probe as a legacy\n * fallback for older SDK layouts.\n */\n\nexport interface ClaudeAuthStatus {\n loggedIn: boolean;\n email?: string;\n subscriptionType?: string;\n authMethod?: string;\n}\n\nexport interface SpawnedProcess {\n stdout: { on: (event: \"data\", cb: (data: Buffer | string) => void) => void };\n stderr: { on: (event: \"data\", cb: (data: Buffer | string) => void) => void };\n on: (event: \"close\" | \"error\", cb: (arg: number | null | Error) => void) => void;\n kill: (signal?: string) => void;\n}\n\n/**\n * The subprocess spawner is injectable so tests can replace it with a\n * fake that emits canned JSON without touching the filesystem.\n */\nexport type SpawnCliFn = (args: string[]) => SpawnedProcess;\n\nconst AUTH_TIMEOUT_MS = 10_000;\n\n/**\n * Resolve the installed Claude Code executable from PATH. The Agent SDK can\n * accept this path via `pathToClaudeCodeExecutable`, and the auth probe uses\n * the same binary so CodeAlmanac agrees with `claude auth status`.\n */\nexport function resolveClaudeExecutable(): string | undefined {\n const result = spawnSync(\"sh\", [\"-lc\", \"command -v claude\"], {\n encoding: \"utf8\",\n });\n if (result.status !== 0) return undefined;\n const found = result.stdout.trim().split(\"\\n\")[0]?.trim();\n return found !== undefined && found.length > 0 ? found : undefined;\n}\n\n/**\n * Resolve legacy `cli.js` from older `@anthropic-ai/claude-agent-sdk`\n * installs. SDK 0.2.129+ no longer ships this file; callers must treat\n * failure as expected and fall back to the public `claude` binary.\n */\nfunction resolveCliJsPath(): string {\n const require = createRequire(import.meta.url);\n const entry = require.resolve(\"@anthropic-ai/claude-agent-sdk\");\n return join(dirname(entry), \"cli.js\");\n}\n\n/**\n * Default subprocess spawner for production use — invokes the installed\n * Claude Code CLI.\n */\nexport const defaultSpawnCli: SpawnCliFn = (args: string[]) => {\n const command = resolveClaudeExecutable() ?? \"claude\";\n const child = spawn(command, args, {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n return child as unknown as SpawnedProcess;\n};\n\nexport const legacySdkSpawnCli: SpawnCliFn = (args: string[]) => {\n const cliPath = resolveCliJsPath();\n const child = spawn(process.execPath, [cliPath, ...args], {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n return child as unknown as SpawnedProcess;\n};\n\n/**\n * Check whether the user is authenticated via Claude subscription OAuth.\n *\n * Spawns `claude auth status --json`, falling back to the legacy SDK CLI\n * layout when available. On any failure (spawn error, non-JSON stdout,\n * non-zero exit, timeout) we return `{ loggedIn: false }` rather than\n * propagating the error — the caller will fall back to the\n * `ANTHROPIC_API_KEY` path and, if that's also missing, produce a clean\n * two-option error message.\n *\n * The 10s timeout guards against the CLI hanging on a broken network or\n * keychain prompt. In practice `auth status` is a cheap local read.\n */\nexport async function checkClaudeAuth(\n spawnCli: SpawnCliFn = defaultSpawnCli,\n): Promise<ClaudeAuthStatus> {\n if (spawnCli === defaultSpawnCli) {\n const status = await checkClaudeAuthWith(defaultSpawnCli);\n if (status.loggedIn) return status;\n return await checkClaudeAuthWith(legacySdkSpawnCli);\n }\n return await checkClaudeAuthWith(spawnCli);\n}\n\nasync function checkClaudeAuthWith(\n spawnCli: SpawnCliFn,\n): Promise<ClaudeAuthStatus> {\n let child: SpawnedProcess;\n try {\n child = spawnCli([\"auth\", \"status\", \"--json\"]);\n } catch {\n return { loggedIn: false };\n }\n\n return new Promise<ClaudeAuthStatus>((resolve) => {\n let stdout = \"\";\n let stderr = \"\";\n let settled = false;\n\n const settle = (value: ClaudeAuthStatus): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve(value);\n };\n\n const timer = setTimeout(() => {\n try {\n child.kill(\"SIGTERM\");\n } catch {\n // Kill can fail if the process already exited; nothing we can do.\n }\n settle({ loggedIn: false });\n }, AUTH_TIMEOUT_MS);\n\n child.stdout.on(\"data\", (data) => {\n stdout += data.toString();\n });\n child.stderr.on(\"data\", (data) => {\n stderr += data.toString();\n });\n\n child.on(\"error\", () => {\n settle({ loggedIn: false });\n });\n\n child.on(\"close\", (code) => {\n // The SDK writes `{\"loggedIn\": false, ...}` to stdout with a zero\n // exit code when the user isn't signed in, so we only reject on\n // non-zero + empty stdout. An empty stdout with zero exit (shouldn't\n // happen in practice) also fails safely to `loggedIn: false`.\n if (code !== 0 && stdout.trim().length === 0) {\n // `stderr` isn't surfaced to the user here — the caller's error\n // message covers both auth paths — but it would be captured by\n // `stderr` if we ever wanted to log it for debugging.\n void stderr;\n settle({ loggedIn: false });\n return;\n }\n try {\n settle(parseClaudeAuthStatus(stdout.trim()));\n } catch {\n settle({ loggedIn: false });\n }\n });\n });\n}\n\nfunction parseClaudeAuthStatus(raw: string): ClaudeAuthStatus {\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n const loggedIn = parsed.loggedIn === true;\n const out: ClaudeAuthStatus = { loggedIn };\n if (typeof parsed.email === \"string\") out.email = parsed.email;\n if (typeof parsed.subscriptionType === \"string\") {\n out.subscriptionType = parsed.subscriptionType;\n }\n if (typeof parsed.authMethod === \"string\") {\n out.authMethod = parsed.authMethod;\n }\n return out;\n}\n\n/**\n * Human-readable error when neither auth path is available. The text is\n * deliberately verbose — users hitting this wall for the first time\n * deserve both options in front of them, not a terse hint.\n */\nexport const UNAUTHENTICATED_MESSAGE =\n \"not authenticated to Claude.\\n\\n\" +\n \"Option 1 — use your Claude subscription (Pro/Max):\\n\" +\n \" claude auth login --claudeai\\n\\n\" +\n \"Option 2 — use a pay-per-token API key:\\n\" +\n \" Get one at https://console.anthropic.com\\n\" +\n \" export ANTHROPIC_API_KEY=sk-ant-...\\n\\n\" +\n \"Verify with: claude auth status\";\n\n/**\n * Assert that at least one auth path is satisfied. Prefers subscription\n * auth (fewer surprises for Claude Pro/Max users) but accepts\n * `ANTHROPIC_API_KEY` as a fallback. On failure throws with\n * `code = \"CLAUDE_AUTH_MISSING\"` so callers can distinguish this from\n * other errors if they ever want to.\n *\n * Returns the resolved auth status so callers that want to display the\n * logged-in email in a preamble can do so without a second subprocess.\n */\nexport async function assertClaudeAuth(\n spawnCli: SpawnCliFn = defaultSpawnCli,\n): Promise<ClaudeAuthStatus> {\n const status = await checkClaudeAuth(spawnCli);\n if (status.loggedIn) {\n return status;\n }\n const apiKey = process.env.ANTHROPIC_API_KEY;\n if (apiKey !== undefined && apiKey.length > 0) {\n // Signal to callers that we're on the API-key path. Not \"loggedIn\"\n // in the OAuth sense, but the SDK will pick up the env var and\n // succeed — so we return a status that tells bootstrap/capture the\n // gate is open.\n return { loggedIn: true, authMethod: \"apiKey\" };\n }\n const err = new Error(UNAUTHENTICATED_MESSAGE);\n (err as { code?: string }).code = \"CLAUDE_AUTH_MISSING\";\n throw err;\n}\n\n// Internal re-export — helps keep the public type surface minimal while\n// still letting tests import the `ChildProcess` shape when needed.\nexport type { ChildProcess };\n","import { execFile } from \"node:child_process\";\nimport { createRequire } from \"node:module\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/**\n * Return the directory of the currently-running codealmanac install by\n * walking up from this module's file to the nearest `package.json` whose\n * `name` is `codealmanac`. Returns the empty string when the walk fails.\n */\nexport function detectCurrentInstallPath(): string {\n try {\n const req = createRequire(import.meta.url);\n const here = fileURLToPath(import.meta.url);\n let dir = path.dirname(here);\n for (let i = 0; i < 6; i++) {\n const pkgPath = path.join(dir, \"package.json\");\n try {\n const raw = req(\"fs\").readFileSync(pkgPath, \"utf-8\") as string;\n const pkg = JSON.parse(raw) as { name?: unknown };\n if (pkg.name === \"codealmanac\") return dir;\n } catch {\n // keep walking\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n } catch {\n // import.meta.url unavailable — not ephemeral for our purposes.\n }\n return \"\";\n}\n\n/**\n * Return true when the given install path looks ephemeral.\n *\n * Ephemeral locations we recognize:\n * - `~/.npm/_npx/` — npm's npx cache (GC'd on version bumps or\n * `npm cache clean`)\n * - `~/.local/share/pnpm/dlx/` — pnpm's dlx (like npx) cache\n * - `/tmp/` or `/var/folders/` — common CI / temp paths\n *\n * A global install (`~/.nvm/.../lib/node_modules/`, `/usr/local/lib/...`,\n * `~/.local/lib/node_modules/`) is NOT ephemeral.\n */\nexport function detectEphemeral(installPath: string): boolean {\n if (installPath.length === 0) return false;\n const home = homedir();\n if (installPath.startsWith(path.join(home, \".npm\", \"_npx\"))) return true;\n if (\n installPath.startsWith(path.join(home, \".local\", \"share\", \"pnpm\", \"dlx\"))\n ) return true;\n if (installPath.startsWith(\"/tmp/\")) return true;\n if (installPath.startsWith(\"/var/folders/\")) return true;\n return false;\n}\n\n/**\n * Spawn `npm install -g codealmanac@latest` in a child process and wait\n * for it to finish. Rejects on non-zero exit or spawn error.\n */\nexport function spawnGlobalInstall(): Promise<void> {\n return new Promise((resolve, reject) => {\n execFile(\n \"npm\",\n [\"install\", \"-g\", \"codealmanac@latest\"],\n { shell: false },\n (err, _stdout, stderr) => {\n if (err !== null) {\n reject(\n new Error(\n stderr.length > 0\n ? stderr.trim().split(\"\\n\")[0] ?? err.message\n : err.message,\n ),\n );\n } else {\n resolve();\n }\n },\n );\n });\n}\n","import { existsSync, readdirSync } from \"node:fs\";\nimport path from \"node:path\";\n\nconst RST = \"\\x1b[0m\";\nconst BOLD = \"\\x1b[1m\";\nconst DIM = \"\\x1b[2m\";\nconst WHITE_BOLD = \"\\x1b[1;37m\";\nconst BLUE = \"\\x1b[38;5;75m\";\nconst BLUE_DIM = \"\\x1b[38;5;69m\";\n\n/**\n * Print the \"Next steps\" box. When `existingPageCount` is greater than 0,\n * the current working directory already has a wiki with committed pages.\n * In that case we skip the `almanac bootstrap` step and tell the user to\n * start querying.\n */\nexport function printNextSteps(\n out: NodeJS.WritableStream,\n existingPageCount: number,\n): void {\n const innerW = 62;\n const vis = (s: string): number =>\n s.replace(/\\x1b\\[[0-9;]*m/g, \"\").length;\n const row = (content: string): string => {\n const padding = Math.max(0, innerW - vis(content));\n return ` ${BLUE_DIM}\\u2502${RST}${content}${\" \".repeat(padding)}${BLUE_DIM}\\u2502${RST}\\n`;\n };\n const empty = row(\"\");\n\n out.write(` ${BLUE_DIM}\\u256d${\"─\".repeat(innerW)}\\u256e${RST}\\n`);\n out.write(empty);\n out.write(row(` ${WHITE_BOLD}Next steps${RST}`));\n out.write(empty);\n\n if (existingPageCount > 0) {\n out.write(\n row(\n ` ${BLUE}\\u25c7${RST} This repo already has a wiki ${DIM}(${existingPageCount} page${existingPageCount === 1 ? \"\" : \"s\"})${RST}`,\n ),\n );\n out.write(empty);\n out.write(row(` ${BLUE}1.${RST} Start querying your wiki:`));\n out.write(row(` ${BOLD}almanac search --mentions <file>${RST}`));\n out.write(\n row(` ${BLUE}2.${RST} Work normally — capture runs on session end`),\n );\n } else {\n out.write(\n row(` ${BLUE}1.${RST} ${BOLD}cd${RST} into a repo you want to document`),\n );\n out.write(\n row(\n ` ${BLUE}2.${RST} ${BOLD}almanac bootstrap${RST} ${DIM}# scaffold the wiki${RST}`,\n ),\n );\n out.write(\n row(` ${BLUE}3.${RST} Work normally — capture runs on session end`),\n );\n }\n\n out.write(empty);\n out.write(` ${BLUE_DIM}\\u2570${\"─\".repeat(innerW)}\\u256f${RST}\\n\\n`);\n}\n\n/**\n * Count `.md` files in `.almanac/pages/` under the current working\n * directory or any parent. Returns 0 when no wiki is found or the pages\n * directory is empty.\n */\nexport function countExistingPages(cwd: string): number {\n try {\n let dir = cwd;\n for (let i = 0; i < 10; i++) {\n const pagesDir = path.join(dir, \".almanac\", \"pages\");\n if (existsSync(pagesDir)) {\n try {\n const entries = readdirSync(pagesDir);\n return entries.filter((e) => e.endsWith(\".md\")).length;\n } catch {\n return 0;\n }\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n } catch {\n // Swallow — never crash setup because of this.\n }\n return 0;\n}\n"],"mappings":";;;;;;AAAA,SAAS,cAAAA,mBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,iBAAAC,sBAAqB;AAC9B,SAAS,WAAAC,gBAAe;AACxB,OAAOC,WAAU;AACjB,SAAS,iBAAAC,sBAAqB;;;ACV9B,SAAS,OAAO,iBAAoC;AACpD,SAAS,qBAAqB;AAC9B,SAAS,SAAS,YAAY;AAqC9B,IAAM,kBAAkB;AAOjB,SAAS,0BAA8C;AAC5D,QAAM,SAAS,UAAU,MAAM,CAAC,OAAO,mBAAmB,GAAG;AAAA,IAC3D,UAAU;AAAA,EACZ,CAAC;AACD,MAAI,OAAO,WAAW,EAAG,QAAO;AAChC,QAAM,QAAQ,OAAO,OAAO,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC,GAAG,KAAK;AACxD,SAAO,UAAU,UAAa,MAAM,SAAS,IAAI,QAAQ;AAC3D;AAOA,SAAS,mBAA2B;AAClC,QAAMC,WAAU,cAAc,YAAY,GAAG;AAC7C,QAAM,QAAQA,SAAQ,QAAQ,gCAAgC;AAC9D,SAAO,KAAK,QAAQ,KAAK,GAAG,QAAQ;AACtC;AAMO,IAAM,kBAA8B,CAAC,SAAmB;AAC7D,QAAM,UAAU,wBAAwB,KAAK;AAC7C,QAAM,QAAQ,MAAM,SAAS,MAAM;AAAA,IACjC,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA,EAClC,CAAC;AACD,SAAO;AACT;AAEO,IAAM,oBAAgC,CAAC,SAAmB;AAC/D,QAAM,UAAU,iBAAiB;AACjC,QAAM,QAAQ,MAAM,QAAQ,UAAU,CAAC,SAAS,GAAG,IAAI,GAAG;AAAA,IACxD,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA,EAClC,CAAC;AACD,SAAO;AACT;AAeA,eAAsB,gBACpB,WAAuB,iBACI;AAC3B,MAAI,aAAa,iBAAiB;AAChC,UAAM,SAAS,MAAM,oBAAoB,eAAe;AACxD,QAAI,OAAO,SAAU,QAAO;AAC5B,WAAO,MAAM,oBAAoB,iBAAiB;AAAA,EACpD;AACA,SAAO,MAAM,oBAAoB,QAAQ;AAC3C;AAEA,eAAe,oBACb,UAC2B;AAC3B,MAAI;AACJ,MAAI;AACF,YAAQ,SAAS,CAAC,QAAQ,UAAU,QAAQ,CAAC;AAAA,EAC/C,QAAQ;AACN,WAAO,EAAE,UAAU,MAAM;AAAA,EAC3B;AAEA,SAAO,IAAI,QAA0B,CAAC,YAAY;AAChD,QAAI,SAAS;AACb,QAAI,SAAS;AACb,QAAI,UAAU;AAEd,UAAM,SAAS,CAAC,UAAkC;AAChD,UAAI,QAAS;AACb,gBAAU;AACV,mBAAa,KAAK;AAClB,cAAQ,KAAK;AAAA,IACf;AAEA,UAAM,QAAQ,WAAW,MAAM;AAC7B,UAAI;AACF,cAAM,KAAK,SAAS;AAAA,MACtB,QAAQ;AAAA,MAER;AACA,aAAO,EAAE,UAAU,MAAM,CAAC;AAAA,IAC5B,GAAG,eAAe;AAElB,UAAM,OAAO,GAAG,QAAQ,CAAC,SAAS;AAChC,gBAAU,KAAK,SAAS;AAAA,IAC1B,CAAC;AACD,UAAM,OAAO,GAAG,QAAQ,CAAC,SAAS;AAChC,gBAAU,KAAK,SAAS;AAAA,IAC1B,CAAC;AAED,UAAM,GAAG,SAAS,MAAM;AACtB,aAAO,EAAE,UAAU,MAAM,CAAC;AAAA,IAC5B,CAAC;AAED,UAAM,GAAG,SAAS,CAAC,SAAS;AAK1B,UAAI,SAAS,KAAK,OAAO,KAAK,EAAE,WAAW,GAAG;AAI5C,aAAK;AACL,eAAO,EAAE,UAAU,MAAM,CAAC;AAC1B;AAAA,MACF;AACA,UAAI;AACF,eAAO,sBAAsB,OAAO,KAAK,CAAC,CAAC;AAAA,MAC7C,QAAQ;AACN,eAAO,EAAE,UAAU,MAAM,CAAC;AAAA,MAC5B;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;AAEA,SAAS,sBAAsB,KAA+B;AAC5D,QAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QAAM,WAAW,OAAO,aAAa;AACrC,QAAM,MAAwB,EAAE,SAAS;AACzC,MAAI,OAAO,OAAO,UAAU,SAAU,KAAI,QAAQ,OAAO;AACzD,MAAI,OAAO,OAAO,qBAAqB,UAAU;AAC/C,QAAI,mBAAmB,OAAO;AAAA,EAChC;AACA,MAAI,OAAO,OAAO,eAAe,UAAU;AACzC,QAAI,aAAa,OAAO;AAAA,EAC1B;AACA,SAAO;AACT;AAOO,IAAM,0BACX;AAkBF,eAAsB,iBACpB,WAAuB,iBACI;AAC3B,QAAM,SAAS,MAAM,gBAAgB,QAAQ;AAC7C,MAAI,OAAO,UAAU;AACnB,WAAO;AAAA,EACT;AACA,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,WAAW,UAAa,OAAO,SAAS,GAAG;AAK7C,WAAO,EAAE,UAAU,MAAM,YAAY,SAAS;AAAA,EAChD;AACA,QAAM,MAAM,IAAI,MAAM,uBAAuB;AAC7C,EAAC,IAA0B,OAAO;AAClC,QAAM;AACR;;;ACtOA,SAAS,gBAAgB;AACzB,SAAS,iBAAAC,sBAAqB;AAC9B,SAAS,eAAe;AACxB,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAOvB,SAAS,2BAAmC;AACjD,MAAI;AACF,UAAM,MAAMA,eAAc,YAAY,GAAG;AACzC,UAAM,OAAO,cAAc,YAAY,GAAG;AAC1C,QAAI,MAAM,KAAK,QAAQ,IAAI;AAC3B,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,YAAM,UAAU,KAAK,KAAK,KAAK,cAAc;AAC7C,UAAI;AACF,cAAM,MAAM,IAAI,IAAI,EAAE,aAAa,SAAS,OAAO;AACnD,cAAM,MAAM,KAAK,MAAM,GAAG;AAC1B,YAAI,IAAI,SAAS,cAAe,QAAO;AAAA,MACzC,QAAQ;AAAA,MAER;AACA,YAAM,SAAS,KAAK,QAAQ,GAAG;AAC/B,UAAI,WAAW,IAAK;AACpB,YAAM;AAAA,IACR;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAcO,SAAS,gBAAgB,aAA8B;AAC5D,MAAI,YAAY,WAAW,EAAG,QAAO;AACrC,QAAM,OAAO,QAAQ;AACrB,MAAI,YAAY,WAAW,KAAK,KAAK,MAAM,QAAQ,MAAM,CAAC,EAAG,QAAO;AACpE,MACE,YAAY,WAAW,KAAK,KAAK,MAAM,UAAU,SAAS,QAAQ,KAAK,CAAC,EACxE,QAAO;AACT,MAAI,YAAY,WAAW,OAAO,EAAG,QAAO;AAC5C,MAAI,YAAY,WAAW,eAAe,EAAG,QAAO;AACpD,SAAO;AACT;AAMO,SAAS,qBAAoC;AAClD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC;AAAA,MACE;AAAA,MACA,CAAC,WAAW,MAAM,oBAAoB;AAAA,MACtC,EAAE,OAAO,MAAM;AAAA,MACf,CAAC,KAAK,SAAS,WAAW;AACxB,YAAI,QAAQ,MAAM;AAChB;AAAA,YACE,IAAI;AAAA,cACF,OAAO,SAAS,IACZ,OAAO,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC,KAAK,IAAI,UACpC,IAAI;AAAA,YACV;AAAA,UACF;AAAA,QACF,OAAO;AACL,kBAAQ;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;;;ACpFA,SAAS,YAAY,mBAAmB;AACxC,OAAOC,WAAU;AAEjB,IAAM,MAAM;AACZ,IAAM,OAAO;AACb,IAAM,MAAM;AACZ,IAAM,aAAa;AACnB,IAAM,OAAO;AACb,IAAM,WAAW;AAQV,SAAS,eACd,KACA,mBACM;AACN,QAAM,SAAS;AACf,QAAM,MAAM,CAAC,MACX,EAAE,QAAQ,mBAAmB,EAAE,EAAE;AACnC,QAAM,MAAM,CAAC,YAA4B;AACvC,UAAM,UAAU,KAAK,IAAI,GAAG,SAAS,IAAI,OAAO,CAAC;AACjD,WAAO,KAAK,QAAQ,SAAS,GAAG,GAAG,OAAO,GAAG,IAAI,OAAO,OAAO,CAAC,GAAG,QAAQ,SAAS,GAAG;AAAA;AAAA,EACzF;AACA,QAAM,QAAQ,IAAI,EAAE;AAEpB,MAAI,MAAM,KAAK,QAAQ,SAAS,SAAI,OAAO,MAAM,CAAC,SAAS,GAAG;AAAA,CAAI;AAClE,MAAI,MAAM,KAAK;AACf,MAAI,MAAM,IAAI,KAAK,UAAU,aAAa,GAAG,EAAE,CAAC;AAChD,MAAI,MAAM,KAAK;AAEf,MAAI,oBAAoB,GAAG;AACzB,QAAI;AAAA,MACF;AAAA,QACE,KAAK,IAAI,SAAS,GAAG,kCAAkC,GAAG,IAAI,iBAAiB,QAAQ,sBAAsB,IAAI,KAAK,GAAG,IAAI,GAAG;AAAA,MAClI;AAAA,IACF;AACA,QAAI,MAAM,KAAK;AACf,QAAI,MAAM,IAAI,KAAK,IAAI,KAAK,GAAG,6BAA6B,CAAC;AAC7D,QAAI,MAAM,IAAI,UAAU,IAAI,mCAAmC,GAAG,EAAE,CAAC;AACrE,QAAI;AAAA,MACF,IAAI,KAAK,IAAI,KAAK,GAAG,oDAA+C;AAAA,IACtE;AAAA,EACF,OAAO;AACL,QAAI;AAAA,MACF,IAAI,KAAK,IAAI,KAAK,GAAG,KAAK,IAAI,KAAK,GAAG,mCAAmC;AAAA,IAC3E;AACA,QAAI;AAAA,MACF;AAAA,QACE,KAAK,IAAI,KAAK,GAAG,KAAK,IAAI,oBAAoB,GAAG,KAAK,GAAG,sBAAsB,GAAG;AAAA,MACpF;AAAA,IACF;AACA,QAAI;AAAA,MACF,IAAI,KAAK,IAAI,KAAK,GAAG,oDAA+C;AAAA,IACtE;AAAA,EACF;AAEA,MAAI,MAAM,KAAK;AACf,MAAI,MAAM,KAAK,QAAQ,SAAS,SAAI,OAAO,MAAM,CAAC,SAAS,GAAG;AAAA;AAAA,CAAM;AACtE;AAOO,SAAS,mBAAmB,KAAqB;AACtD,MAAI;AACF,QAAI,MAAM;AACV,aAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,YAAM,WAAWA,MAAK,KAAK,KAAK,YAAY,OAAO;AACnD,UAAI,WAAW,QAAQ,GAAG;AACxB,YAAI;AACF,gBAAM,UAAU,YAAY,QAAQ;AACpC,iBAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,EAAE;AAAA,QAClD,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AACA,YAAM,SAASA,MAAK,QAAQ,GAAG;AAC/B,UAAI,WAAW,IAAK;AACpB,YAAM;AAAA,IACR;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;;;AHWA,IAAMC,OAAM;AACZ,IAAMC,OAAM;AACZ,IAAMC,cAAa;AACnB,IAAMC,QAAO;AACb,IAAM,YAAY;AAElB,IAAM,WAAW;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAQA,IAAM,aAAa;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,MAAM,KAAKF,IAAG,SAASD,IAAG;AAEhC,SAAS,YAAY,KAAkC;AACrD,MAAI,MAAM,IAAI;AACd,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;AAC1C,UAAM,QAAQ,SAAS,KAAK,IAAI,GAAG,SAAS,SAAS,CAAC,CAAC,KAAK;AAC5D,QAAI,MAAM,GAAG,KAAK,GAAG,WAAW,CAAC,CAAC,GAAGA,IAAG;AAAA,CAAI;AAAA,EAC9C;AACA,MAAI,MAAM;AAAA,EAAKE,WAAU,oCAAoCF,IAAG;AAAA,CAAI;AACtE;AAEA,SAAS,WAAW,KAAkC;AACpD,MAAI,MAAM;AAAA,KAAQ,SAAS,gBAAgBA,IAAG;AAAA;AAAA,CAAM;AACtD;AAEA,SAAS,SAAS,KAA4B,KAAmB;AAC/D,MAAI,MAAM,KAAKG,KAAI,SAASH,IAAG,KAAK,GAAG;AAAA,CAAI;AAC7C;AAEA,SAAS,WAAW,KAA4B,KAAmB;AACjE,MAAI,MAAM,KAAKG,KAAI,SAASH,IAAG,KAAK,GAAG;AAAA,CAAI;AAC7C;AAEA,SAAS,YAAY,KAA4B,KAAmB;AAClE,MAAI,MAAM,KAAKC,IAAG,WAAW,GAAG,GAAGD,IAAG;AAAA,CAAI;AAC5C;AAIA,eAAsB,SACpB,UAAwB,CAAC,GACH;AACtB,QAAM,MAAM,QAAQ,UAAU,QAAQ;AACtC,QAAM,QACJ,QAAQ,SAAU,QAAQ,MAAM,UAAU;AAC5C,QAAM,cAAc,SAAS,QAAQ,QAAQ;AAQ7C,MAAI,QAAQ,aAAa,QAAQ,QAAQ,eAAe,MAAM;AAC5D,QAAI;AAAA,MACF;AAAA,IACF;AACA,WAAO,EAAE,QAAQ,IAAI,QAAQ,IAAI,UAAU,EAAE;AAAA,EAC/C;AAEA,cAAY,GAAG;AACf,aAAW,GAAG;AAKd,QAAM,OAAO,MAAM,cAAc,QAAQ,QAAQ;AACjD,aAAW,KAAK,IAAI;AACpB,MAAI,MAAM,MAAM,IAAI;AAWpB,QAAM,QAAQ,QAAQ,gBAAgB,SACjC,QAAQ,gBAAgB,OACrB,gBAAgB,QAAQ,WAAW,IACnC,QACJ,gBAAgB,yBAAyB,CAAC;AAC9C,MAAI,OAAO;AACT,QAAI,eAAgC;AACpC,QAAI,aAAa;AACf,qBAAe,MAAM;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,QAAI,iBAAiB,WAAW;AAC9B,iBAAW,KAAK,uCAAkC;AAClD,UAAI;AACF,eAAO,QAAQ,sBAAsB,oBAAoB;AACzD,iBAAS,KAAK,sDAAsD;AAAA,MACtE,SAAS,KAAc;AACrB,cAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,mBAAW,KAAK,0BAA0B,GAAG,EAAE;AAC/C,YAAI;AAAA,UACF,KAAKC,IAAG,qDAAqDD,IAAG;AAAA;AAAA,QAClE;AAAA,MACF;AAAA,IACF,OAAO;AACL;AAAA,QACE;AAAA,QACA,kBAAkBC,IAAG,gEAA2DD,IAAG;AAAA,MACrF;AAAA,IACF;AACA,QAAI,MAAM,MAAM,IAAI;AAAA,EACtB;AAGA,MAAI,aAA8B;AAClC,MAAI,QAAQ,aAAa,MAAM;AAC7B,iBAAa;AAAA,EACf,WAAW,aAAa;AACtB,iBAAa,MAAM;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,MAAI,iBAAiB;AACrB,MAAI,eAAe,WAAW;AAC5B,UAAM,MAAM,MAAM,eAAe;AAAA,MAC/B,cAAc,QAAQ;AAAA,MACtB,gBAAgB,QAAQ;AAAA,MACxB,gBAAgB,QAAQ;AAAA,IAC1B,CAAC;AACD,QAAI,IAAI,aAAa,GAAG;AACtB,iBAAW,KAAK,oBAAoB,IAAI,OAAO,KAAK,CAAC,EAAE;AACvD,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,QAAQ,IAAI;AAAA,QACZ,UAAU,IAAI;AAAA,MAChB;AAAA,IACF;AACA,qBAAiB,IAAI,OAAO,SAAS,mBAAmB,IACpD,mBAAmBC,IAAG,oBAAoBD,IAAG,KAC7C;AACJ,aAAS,KAAK,cAAc;AAAA,EAC9B,OAAO;AACL,gBAAY,KAAK,mBAAmBC,IAAG,UAAUD,IAAG,EAAE;AAAA,EACxD;AACA,MAAI,MAAM,MAAM,IAAI;AAGpB,MAAI,eAAgC;AACpC,MAAI,QAAQ,eAAe,MAAM;AAC/B,mBAAe;AAAA,EACjB,WAAW,aAAa;AACtB,mBAAe,MAAM;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,MAAI;AACJ,MAAI,iBAAiB,WAAW;AAC9B,QAAI;AACF,YAAM,UAAU,MAAM,cAAc;AAAA,QAClC,WAAW,QAAQ,aAAaI,MAAK,KAAKC,SAAQ,GAAG,SAAS;AAAA,QAC9D,WAAW,QAAQ,aAAa,iBAAiB;AAAA,MACnD,CAAC;AACD,sBAAgB,QAAQ,aACpB,qBAAqB,QAAQ,aAAa,KAAK,IAAI,CAAC,MACpD,UAAUJ,IAAG,oBAAoBD,IAAG;AACxC,eAAS,KAAK,aAAa;AAAA,IAC7B,SAAS,KAAc;AACrB,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,QAAQ,kCAAkC,GAAG;AAAA;AAAA,QAC7C,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF,OAAO;AACL,gBAAY,KAAK,UAAUC,IAAG,UAAUD,IAAG,EAAE;AAAA,EAC/C;AACA,MAAI,MAAM,MAAM,IAAI;AAEpB,WAAS,KAAK,GAAGG,KAAI,iBAAiBH,IAAG,EAAE;AAC3C,MAAI,MAAM,IAAI;AAOd,QAAM,oBAAoB,mBAAmB,QAAQ,IAAI,CAAC;AAC1D,iBAAe,KAAK,iBAAiB;AAErC,SAAO,EAAE,QAAQ,IAAI,QAAQ,IAAI,UAAU,EAAE;AAC/C;AAIA,eAAe,cACb,UAC2B;AAC3B,MAAI;AACF,WAAO,MAAM,gBAAgB,QAAQ;AAAA,EACvC,QAAQ;AACN,WAAO,EAAE,UAAU,MAAM;AAAA,EAC3B;AACF;AAEA,SAAS,WACP,KACA,MACM;AACN,MAAI,KAAK,UAAU;AACjB,UAAM,MAAM,KAAK,SAAS;AAC1B,UAAM,OACJ,KAAK,qBAAqB,SACtB,IAAIC,IAAG,IAAI,KAAK,gBAAgB,IAAID,IAAG,KACvC;AACN,aAAS,KAAK,gBAAgBE,WAAU,GAAG,GAAG,GAAGF,IAAG,GAAG,IAAI,EAAE;AAC7D;AAAA,EACF;AACA,MACE,QAAQ,IAAI,sBAAsB,UAClC,QAAQ,IAAI,kBAAkB,SAAS,GACvC;AACA,aAAS,KAAK,gBAAgBE,WAAU,oBAAoBF,IAAG,MAAM;AACrE;AAAA,EACF;AAEA,aAAW,KAAK,gBAAgBC,IAAG,gBAAgBD,IAAG,EAAE;AACxD,aAAW,QAAQ,wBAAwB,MAAM,IAAI,GAAG;AACtD,QAAI,MAAM,KAAKC,IAAG,YAAY,IAAI,GAAGD,IAAG;AAAA,CAAI;AAAA,EAC9C;AACF;AA6BA,eAAe,cACb,SAC8B;AAC9B,QAAM,MAAM,QAAQ,WAAW,EAAE,WAAW,KAAK,CAAC;AAElD,QAAM,UAAUI,MAAK,KAAK,QAAQ,WAAW,SAAS;AACtD,QAAM,SAASA,MAAK,KAAK,QAAQ,WAAW,cAAc;AAC1D,MAAI,CAACE,YAAW,OAAO,GAAG;AACxB,UAAM,IAAI,MAAM,0BAA0B,OAAO,EAAE;AAAA,EACrD;AACA,MAAI,CAACA,YAAW,MAAM,GAAG;AACvB,UAAM,IAAI,MAAM,0BAA0B,MAAM,EAAE;AAAA,EACpD;AAEA,QAAM,WAAWF,MAAK,KAAK,QAAQ,WAAW,gBAAgB;AAC9D,QAAM,UAAUA,MAAK,KAAK,QAAQ,WAAW,0BAA0B;AAEvE,QAAM,cAAc,MAAM,cAAc,SAAS,QAAQ;AACzD,QAAM,aAAa,MAAM,cAAc,QAAQ,OAAO;AAEtD,QAAM,WAAWA,MAAK,KAAK,QAAQ,WAAW,WAAW;AACzD,QAAM,gBAAgB,MAAM,aAAa,QAAQ;AAEjD,QAAM,eAAyB,CAAC;AAChC,MAAI,YAAa,cAAa,KAAK,gBAAgB;AACnD,MAAI,WAAY,cAAa,KAAK,0BAA0B;AAC5D,MAAI,cAAe,cAAa,KAAK,WAAW;AAEhD,SAAO,EAAE,YAAY,aAAa,SAAS,GAAG,aAAa;AAC7D;AAEA,eAAe,cAAc,KAAa,MAAgC;AACxE,QAAM,WAAW,MAAM,SAAS,GAAG;AACnC,MAAIE,YAAW,IAAI,GAAG;AACpB,QAAI;AACF,YAAM,YAAY,MAAM,SAAS,IAAI;AACrC,UAAI,SAAS,OAAO,SAAS,EAAG,QAAO;AAAA,IACzC,QAAQ;AAAA,IAER;AAAA,EACF;AACA,QAAM,SAAS,KAAK,IAAI;AACxB,SAAO;AACT;AAIO,IAAM,cAAc;AAa3B,eAAe,aAAa,cAAwC;AAClE,MAAI,WAAW;AACf,MAAIA,YAAW,YAAY,GAAG;AAC5B,eAAW,MAAM,SAAS,cAAc,MAAM;AAAA,EAChD;AACA,MAAI,cAAc,QAAQ,EAAG,QAAO;AAEpC,QAAM,MACJ,SAAS,WAAW,IAAI,KAAK,SAAS,SAAS,IAAI,IAAI,OAAO;AAChE,QAAM,OAAO,GAAG,QAAQ,GAAG,GAAG,GAAG,WAAW;AAAA;AAC5C,QAAM,UAAU,cAAc,MAAM,MAAM;AAC1C,SAAO;AACT;AAEO,SAAS,cAAc,UAA2B;AAMvD,QAAM,QAAQ,SAAS,MAAM,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AACzD,SAAO,MAAM,KAAK,CAAC,SAAS;AAC1B,QAAI,SAAS,YAAa,QAAO;AACjC,QAAI,CAAC,KAAK,WAAW,WAAW,EAAG,QAAO;AAC1C,UAAM,OAAO,KAAK,YAAY,MAAM;AACpC,WAAO,SAAS,OAAO,SAAS;AAAA,EAClC,CAAC;AACH;AAYA,SAAS,QACP,KACA,UACA,YAC0B;AAC1B,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,OAAO,aAAa,UAAU;AACpC,QAAI,MAAM,KAAKH,KAAI,SAASH,IAAG,KAAK,QAAQ,IAAIC,IAAG,GAAG,IAAI,GAAGD,IAAG,GAAG;AAEnE,QAAI,MAAM;AACV,UAAM,SAAS,CAAC,UAAwB;AACtC,aAAO,MAAM,SAAS,MAAM;AAC5B,YAAM,KAAK,IAAI,QAAQ,IAAI;AAC3B,UAAI,OAAO,GAAI;AACf,cAAQ,MAAM,eAAe,QAAQ,MAAM;AAC3C,cAAQ,MAAM,MAAM;AAEpB,YAAM,SAAS,IAAI,MAAM,GAAG,EAAE,EAAE,KAAK,EAAE,YAAY;AACnD,YAAM,WACJ,OAAO,WAAW,IACd,aACA,WAAW,OAAO,WAAW;AACnC,cAAQ,WAAW,YAAY,MAAM;AAAA,IACvC;AAEA,YAAQ,MAAM,OAAO;AACrB,YAAQ,MAAM,GAAG,QAAQ,MAAM;AAAA,EACjC,CAAC;AACH;AAoBO,SAAS,mBAA2B;AACzC,QAAM,OAAOI,MAAK,QAAQG,eAAc,YAAY,GAAG,CAAC;AACxD,QAAM,aAAa;AAAA,IACjBH,MAAK,QAAQ,MAAM,MAAM,QAAQ;AAAA;AAAA,IACjCA,MAAK,QAAQ,MAAM,MAAM,MAAM,QAAQ;AAAA;AAAA,IACvCA,MAAK,QAAQ,MAAM,MAAM,MAAM,MAAM,QAAQ;AAAA,EAC/C;AACA,aAAW,OAAO,YAAY;AAC5B,QAAI,mBAAmB,GAAG,EAAG,QAAO;AAAA,EACtC;AAIA,MAAI;AACF,UAAMI,WAAUC,eAAc,YAAY,GAAG;AAC7C,UAAM,UAAUD,SAAQ,QAAQ,0BAA0B;AAC1D,UAAM,SAASJ,MAAK,KAAKA,MAAK,QAAQ,OAAO,GAAG,QAAQ;AACxD,QAAI,mBAAmB,MAAM,EAAG,QAAO;AAAA,EACzC,QAAQ;AAAA,EAER;AACA,QAAM,IAAI;AAAA,IACR,yDACE,WAAW,IAAI,CAAC,MAAM,OAAO,CAAC,EAAE,EAAE,KAAK,IAAI;AAAA,EAC/C;AACF;AAEA,SAAS,mBAAmB,KAAsB;AAChD,SAAOE,YAAWF,MAAK,KAAK,KAAK,SAAS,CAAC;AAC7C;","names":["existsSync","createRequire","homedir","path","fileURLToPath","require","createRequire","path","RST","DIM","WHITE_BOLD","BLUE","path","homedir","existsSync","fileURLToPath","require","createRequire"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/update/config.ts","../src/update/semver.ts","../src/update/state.ts","../src/update/check.ts"],"sourcesContent":["import { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\nimport { getGlobalAlmanacDir } from \"../paths.js\";\n\n/**\n * `~/.almanac/config.json` — global, cross-wiki configuration. Today\n * the only field is `update_notifier` (on/off toggle for the pre-command\n * banner); designed as an object so we can add more knobs without\n * breaking users who already have the file on disk.\n *\n * Missing or malformed → defaults. Same tolerance as `UpdateState`:\n * the CLI must not be able to fail because this file drifted.\n */\nexport interface GlobalConfig {\n /** When `false`, suppress the pre-command update-nag banner. Default: true. */\n update_notifier: boolean;\n}\n\nexport function defaultConfig(): GlobalConfig {\n return { update_notifier: true };\n}\n\nexport function getConfigPath(): string {\n return join(getGlobalAlmanacDir(), \"config.json\");\n}\n\nexport async function readConfig(path?: string): Promise<GlobalConfig> {\n const file = path ?? getConfigPath();\n let raw: string;\n try {\n raw = await readFile(file, \"utf8\");\n } catch {\n return defaultConfig();\n }\n const trimmed = raw.trim();\n if (trimmed.length === 0) return defaultConfig();\n try {\n const parsed = JSON.parse(trimmed) as Partial<GlobalConfig>;\n return {\n update_notifier:\n typeof parsed.update_notifier === \"boolean\"\n ? parsed.update_notifier\n : true,\n };\n } catch {\n return defaultConfig();\n }\n}\n\nexport async function writeConfig(\n config: GlobalConfig,\n path?: string,\n): Promise<void> {\n const file = path ?? getConfigPath();\n await mkdir(dirname(file), { recursive: true });\n const body = `${JSON.stringify(config, null, 2)}\\n`;\n const tmp = `${file}.tmp`;\n await writeFile(tmp, body, \"utf8\");\n await rename(tmp, file);\n}\n","/**\n * Tiny semver comparator for update-version checks. We do NOT take a\n * dependency on `semver` — we only need a subset (compare two\n * well-formed `x.y.z` strings, optionally with a pre-release tag), and\n * adding a 400KB install for six lines of logic isn't a good trade.\n *\n * What we handle:\n * - Numeric `major.minor.patch` with or without a `-pre` tag.\n * - Missing parts default to 0 (`1.2` → `1.2.0`).\n * - Leading `v` is stripped.\n *\n * What we don't handle:\n * - Build metadata (`+sha.abcd`) — ignored.\n * - Pre-release precedence rules beyond \"tagged < untagged at same\n * numeric triple\". Good enough for codealmanac's linear release\n * cadence; if we ever publish `-rc.1` vs `-rc.2` and care which\n * comes first, revisit.\n */\n\ninterface Parsed {\n major: number;\n minor: number;\n patch: number;\n /** Empty string = no pre-release (counts as \"higher\" than a pre-release at the same triple). */\n pre: string;\n}\n\nfunction parse(v: string): Parsed | null {\n const trimmed = v.trim().replace(/^v/i, \"\");\n // Strip build metadata (everything from the first `+`).\n const noBuild = trimmed.split(\"+\")[0] ?? \"\";\n // Split off pre-release tag.\n const dashAt = noBuild.indexOf(\"-\");\n const core = dashAt === -1 ? noBuild : noBuild.slice(0, dashAt);\n const pre = dashAt === -1 ? \"\" : noBuild.slice(dashAt + 1);\n\n const parts = core.split(\".\").map((p) => Number.parseInt(p, 10));\n if (parts.length === 0 || parts.some((n) => !Number.isFinite(n) || n < 0)) {\n return null;\n }\n return {\n major: parts[0] ?? 0,\n minor: parts[1] ?? 0,\n patch: parts[2] ?? 0,\n pre,\n };\n}\n\n/**\n * Return `true` iff `latest` > `installed`. Returns `false` on unparseable\n * input rather than throwing — a bad version string must not be able to\n * crash the CLI's every-command banner path.\n */\nexport function isNewer(latest: string, installed: string): boolean {\n const a = parse(latest);\n const b = parse(installed);\n if (a === null || b === null) return false;\n\n if (a.major !== b.major) return a.major > b.major;\n if (a.minor !== b.minor) return a.minor > b.minor;\n if (a.patch !== b.patch) return a.patch > b.patch;\n\n // Same numeric triple. Empty pre-release beats a tagged pre-release\n // (1.2.3 > 1.2.3-rc.1). Two tagged pre-releases compare lexically.\n if (a.pre === b.pre) return false;\n if (a.pre === \"\" && b.pre !== \"\") return true;\n if (a.pre !== \"\" && b.pre === \"\") return false;\n return a.pre > b.pre;\n}\n","import { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\nimport { getGlobalAlmanacDir } from \"../paths.js\";\n\n/**\n * `~/.almanac/update-state.json` — the only piece of persistent state\n * the update system owns. Written by the background check worker (once\n * per 24h) and by `almanac update` / `almanac update --dismiss`; read\n * by the pre-command banner, `almanac doctor`, and the check path to\n * decide whether 24h have elapsed since the last check.\n *\n * Format is a single flat object with no schema version: fields are\n * only ever added (and read with defaults), never removed or reshaped.\n * If we ever need a breaking migration, the file is trivially\n * regenerable — the worst case is a single extra registry round-trip.\n *\n * Corruption handling: every read path tolerates missing or malformed\n * JSON as \"no state\" (empty defaults). We never propagate a parse\n * error up to the CLI banner, because a corrupt state file must not\n * be able to break every invocation.\n */\nexport interface UpdateState {\n /** Unix epoch seconds of the last registry query. */\n last_check_at: number;\n /** The codealmanac version running when we last wrote state. */\n installed_version: string;\n /** The newest version the registry has published at last check. */\n latest_version: string;\n /** Versions the user dismissed via `almanac update --dismiss`. */\n dismissed_versions: string[];\n /**\n * Epoch seconds of the last registry fetch attempt that FAILED. Used\n * by the check scheduler to back off (one failure shouldn't hammer\n * the registry on every command) — reads tolerate a missing field.\n */\n last_fetch_failed_at?: number;\n}\n\nexport function emptyState(): UpdateState {\n return {\n last_check_at: 0,\n installed_version: \"\",\n latest_version: \"\",\n dismissed_versions: [],\n };\n}\n\nexport function getStatePath(): string {\n return join(getGlobalAlmanacDir(), \"update-state.json\");\n}\n\n/**\n * Read the state file. Missing, empty, or malformed → empty state.\n * We deliberately swallow all errors here: the update system is\n * best-effort, and a read failure must not break any command.\n */\nexport async function readState(path?: string): Promise<UpdateState> {\n const file = path ?? getStatePath();\n let raw: string;\n try {\n raw = await readFile(file, \"utf8\");\n } catch {\n return emptyState();\n }\n const trimmed = raw.trim();\n if (trimmed.length === 0) return emptyState();\n try {\n const parsed = JSON.parse(trimmed) as Partial<UpdateState>;\n return {\n last_check_at:\n typeof parsed.last_check_at === \"number\" ? parsed.last_check_at : 0,\n installed_version:\n typeof parsed.installed_version === \"string\"\n ? parsed.installed_version\n : \"\",\n latest_version:\n typeof parsed.latest_version === \"string\" ? parsed.latest_version : \"\",\n dismissed_versions: Array.isArray(parsed.dismissed_versions)\n ? parsed.dismissed_versions.filter(\n (v): v is string => typeof v === \"string\" && v.length > 0,\n )\n : [],\n last_fetch_failed_at:\n typeof parsed.last_fetch_failed_at === \"number\"\n ? parsed.last_fetch_failed_at\n : undefined,\n };\n } catch {\n return emptyState();\n }\n}\n\n/**\n * Write the state file atomically (tmp + rename). Makes the concurrent\n * \"two commands ran their update checks at once\" race safe — one rename\n * wins, the other is dropped. Creates `~/.almanac/` if missing.\n */\nexport async function writeState(\n state: UpdateState,\n path?: string,\n): Promise<void> {\n const file = path ?? getStatePath();\n await mkdir(dirname(file), { recursive: true });\n const body = `${JSON.stringify(state, null, 2)}\\n`;\n const tmp = `${file}.tmp`;\n await writeFile(tmp, body, \"utf8\");\n await rename(tmp, file);\n}\n","import { createRequire } from \"node:module\";\n\nimport { readState, writeState, type UpdateState } from \"./state.js\";\n\n/**\n * Background update check. Called by the detached worker (`--internal-\n * check-updates`) after any normal command exits; also reachable via\n * `almanac update --check` for a synchronous \"am I current?\" readout.\n *\n * Contract:\n * - Reads `~/.almanac/update-state.json` if present.\n * - If the last check is older than `cacheSeconds` (default 24h), queries\n * the npm registry for `codealmanac`'s `dist-tags.latest` and writes a\n * new state file.\n * - Network timeout is 3s: registry flakes must not prevent a check\n * cycle on the next invocation.\n * - All errors are swallowed; the returned state is always a usable\n * snapshot (possibly the old one when the fetch failed).\n *\n * The fetch function is injectable. Tests pass a stub; production uses\n * the native `globalThis.fetch`. No dependency on `node-fetch`.\n */\n\nexport interface CheckOptions {\n /** Override the installed version (prod: read from package.json). */\n installedVersion?: string;\n /** Cache window; no registry call if last check is newer than this. */\n cacheSeconds?: number;\n /** Network timeout in ms (default 3000). */\n timeoutMs?: number;\n /** Clock. Tests inject to make \"24h ago\" deterministic. */\n now?: () => number;\n /** Fetch function (default `globalThis.fetch`). */\n fetchFn?: typeof fetch;\n /** Override the state file path (tests point it at a tmpdir). */\n statePath?: string;\n /** Force a registry call regardless of the cache. Used by `update --check`. */\n force?: boolean;\n}\n\nexport interface CheckResult {\n /** The state after the check (either refreshed or unchanged). */\n state: UpdateState;\n /** True when a registry call actually happened this run. */\n fetched: boolean;\n /** True when the registry call failed (network / timeout / parse). */\n fetchFailed: boolean;\n}\n\nconst DEFAULT_CACHE_SECONDS = 24 * 60 * 60;\nconst DEFAULT_TIMEOUT_MS = 3000;\nconst REGISTRY_URL = \"https://registry.npmjs.org/codealmanac\";\n\nexport async function checkForUpdate(\n opts: CheckOptions = {},\n): Promise<CheckResult> {\n const now = opts.now ?? (() => Math.floor(Date.now() / 1000));\n const cacheSeconds = opts.cacheSeconds ?? DEFAULT_CACHE_SECONDS;\n const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const fetchFn = opts.fetchFn ?? globalThis.fetch;\n const installed = opts.installedVersion ?? readInstalledVersion();\n\n const state = await readState(opts.statePath);\n\n // Cache gate. Skip the registry call when the previous check is\n // fresh enough, unless `force: true` (used by `update --check` so\n // the user can see real-time status without waiting out the window).\n if (\n !opts.force &&\n state.last_check_at > 0 &&\n now() - state.last_check_at < cacheSeconds\n ) {\n return { state, fetched: false, fetchFailed: false };\n }\n\n // Query the registry with a hard timeout. `AbortController` is the\n // idiomatic Node 20+ way to bound a fetch; `setTimeout` fires abort,\n // `clearTimeout` cancels if fetch resolves first.\n let latest: string | null = null;\n let failed = false;\n try {\n const ac = new AbortController();\n const timer = setTimeout(() => ac.abort(), timeoutMs);\n try {\n const res = await fetchFn(REGISTRY_URL, {\n signal: ac.signal,\n headers: { accept: \"application/json\" },\n });\n if (!res.ok) {\n failed = true;\n } else {\n const body = (await res.json()) as {\n [\"dist-tags\"]?: { latest?: unknown };\n };\n const tag = body[\"dist-tags\"]?.latest;\n if (typeof tag === \"string\" && tag.length > 0) {\n latest = tag;\n } else {\n failed = true;\n }\n }\n } finally {\n clearTimeout(timer);\n }\n } catch {\n failed = true;\n }\n\n if (failed || latest === null) {\n // Record the failure but DON'T clobber the previous latest_version —\n // an offline check shouldn't make us forget that 0.1.6 is out.\n const next: UpdateState = {\n ...state,\n // We still bump last_check_at on a failed attempt; without this,\n // every subsequent command would re-try the registry. A one-shot\n // retry on the next invocation is enough; sustained failure gets\n // retried on the 24h cadence like a success.\n last_check_at: now(),\n installed_version: installed,\n last_fetch_failed_at: now(),\n };\n try {\n await writeState(next, opts.statePath);\n } catch {\n // Even the state write failed (permissions, disk full). Return\n // whatever we have — the CLI doesn't care.\n }\n return { state: next, fetched: true, fetchFailed: true };\n }\n\n const next: UpdateState = {\n last_check_at: now(),\n installed_version: installed,\n latest_version: latest,\n dismissed_versions: state.dismissed_versions,\n // Clear the failure marker on success.\n last_fetch_failed_at: undefined,\n };\n try {\n await writeState(next, opts.statePath);\n } catch {\n // Silent — same rationale as above.\n }\n return { state: next, fetched: true, fetchFailed: false };\n}\n\n/**\n * Read the `version` field from `package.json`. Matches the same\n * lookup strategy as `readPackageVersion` in `cli.ts` and `doctor.ts`;\n * duplicated here to keep the update module self-contained and to\n * avoid a circular import at CLI startup.\n */\nfunction readInstalledVersion(): string {\n try {\n const require = createRequire(import.meta.url);\n const pkg = require(\"../../package.json\") as { version?: unknown };\n if (typeof pkg.version === \"string\" && pkg.version.length > 0) {\n return pkg.version;\n }\n } catch {\n // Fall through.\n }\n try {\n const require = createRequire(import.meta.url);\n const pkg = require(\"../package.json\") as { version?: unknown };\n if (typeof pkg.version === \"string\" && pkg.version.length > 0) {\n return pkg.version;\n }\n } catch {\n // Fall through.\n }\n return \"unknown\";\n}\n"],"mappings":"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","names":["mkdir","readFile","rename","writeFile","dirname","join","join","readFile","mkdir","dirname","writeFile","rename","next","require"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/hook.ts","../src/commands/hook/script.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport {\n copyToStableHooksDir,\n resolveHookScriptPath,\n resolveSettingsPath,\n type ScriptResolution,\n} from \"./hook/script.js\";\n\n/**\n * `almanac hook install|uninstall|status` — wires the bundled\n * `hooks/almanac-capture.sh` into `~/.claude/settings.json` as a\n * `SessionEnd` hook.\n *\n * Design notes:\n *\n * - **Schema.** Claude Code validates `settings.json` against a strict\n * schema: each entry in an event array (like `SessionEnd`) is a\n * `{matcher, hooks: [...]}` container, and the actual command objects\n * live in the nested `hooks` array. v0.1.0–v0.1.4 wrote command objects\n * directly at the event-array level; newer Claude Code versions now\n * reject that shape. We produce the wrapped form on install, and when\n * encountering a legacy unwrapped entry that we recognize as ours (by\n * `command` ending in `almanac-capture.sh`) we migrate it on next\n * install. `SessionEnd` never uses the `matcher` field to discriminate\n * anything — we always emit an empty `matcher: \"\"` (matches\n * everything, which is what session-end lifecycle hooks want).\n *\n * - **Idempotent.** `install` twice leaves one entry, not two. We match by\n * `command` string equality on the inner `hooks[]` entries. If the user\n * replaces our absolute path with a symlink pointing at the same\n * script, we'll treat it as foreign. That's acceptable; the `status`\n * output shows the path we'd use, so the user can reconcile manually.\n *\n * - **Refuse foreign entries.** If `SessionEnd` is already populated with\n * a command we don't recognize, we print the existing value and exit\n * non-zero. Claude Code lets users wire their own hooks (notifications,\n * git autocommit scripts, etc.) and silently replacing them would be\n * rude. Foreign wrapped containers that don't reference our script are\n * preserved byte-for-byte.\n *\n * - **Atomic write.** `settings.json` is small but heavily touched by\n * Claude Code. Writing via tmp-file + rename avoids corrupting the file\n * if we crash mid-write.\n *\n * - **Non-interactive.** No prompts, no confirmations. The caller is\n * already making an intentional choice by running `almanac hook\n * install`.\n */\n\nexport interface HookCommandOptions {\n /**\n * Override the hook script path. Production code leaves this undefined\n * and we resolve the bundled `hooks/almanac-capture.sh`. Tests pass a\n * fixture path to avoid depending on the runtime-install layout.\n */\n hookScriptPath?: string;\n /**\n * Override `~/.claude/settings.json`. Tests sandbox this to a tmpdir;\n * production code leaves it undefined.\n */\n settingsPath?: string;\n /**\n * Override the stable hooks directory where we copy the script.\n * Defaults to `~/.claude/hooks/`. Tests sandbox this to a tmpdir.\n *\n * Bug #1 fix: we always copy the bundled script to this stable path\n * before writing it into settings.json. This way the settings entry\n * points at a user-owned location that survives npm version bumps,\n * npx cache evictions, and nvm version switches — instead of an\n * ephemeral path inside ~/.npm/_npx/<sha>/... or the nvm-versioned\n * node_modules/.\n */\n stableHooksDir?: string;\n}\n\nexport interface HookCommandResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\nconst HOOK_TIMEOUT_SECONDS = 10;\n\n/** A single command invocation inside a wrapper's `hooks[]` array. */\ninterface HookCommand {\n type: \"command\";\n command: string;\n timeout?: number;\n}\n\n/** A wrapped SessionEnd entry per Claude Code's schema. */\ninterface WrappedEntry {\n matcher: string;\n hooks: HookCommand[];\n}\n\n/**\n * What we read from `settings.hooks.SessionEnd`. During a read we may\n * encounter the legacy unwrapped shape (`HookCommand` directly) written\n * by v0.1.0–v0.1.4 — we recognize and migrate it. Unknown entries we\n * can't classify are preserved as-is via `unknown`.\n */\ntype RawEntry = WrappedEntry | HookCommand | unknown;\n\n/**\n * Claude Code's `settings.json` is a free-form JSON object; we only care\n * about the `hooks.SessionEnd` array. Preserve everything else verbatim\n * so we don't drop user settings when we write the file back.\n */\ntype SettingsJson = Record<string, unknown> & {\n hooks?: Record<string, RawEntry[] | undefined>;\n};\n\n/**\n * Heuristic: does this command path look like one we installed?\n *\n * We match on the filename `almanac-capture.sh` regardless of the parent\n * directory. This covers:\n * - the stable path: `~/.claude/hooks/almanac-capture.sh`\n * - legacy paths from v0.1.0–v0.1.5: inside the nvm node_modules or\n * npx cache\n * The stable path is what new installs produce; legacy paths are what\n * we migrate when the user runs `almanac hook install` again.\n */\nfunction isOurCommandPath(command: string): boolean {\n return command.endsWith(\"almanac-capture.sh\");\n}\n\n/**\n * Classify a raw SessionEnd entry. Wrapped entries are the canonical\n * shape; unwrapped-command entries are legacy output from v0.1.0–v0.1.4.\n * Anything else (random user JSON) is `unknown` and we leave it alone.\n */\ntype Classified =\n | { kind: \"wrapped\"; entry: WrappedEntry }\n | { kind: \"legacy\"; entry: HookCommand }\n | { kind: \"unknown\"; entry: unknown };\n\nfunction classifyEntry(raw: RawEntry): Classified {\n if (raw === null || typeof raw !== \"object\") {\n return { kind: \"unknown\", entry: raw };\n }\n const obj = raw as Record<string, unknown>;\n if (Array.isArray(obj.hooks)) {\n // Wrapped shape. `matcher` may be absent in hand-edited files; treat\n // absent as \"\" so we don't throw on slightly malformed input.\n const matcher = typeof obj.matcher === \"string\" ? obj.matcher : \"\";\n const hooks: HookCommand[] = [];\n for (const h of obj.hooks as unknown[]) {\n if (h !== null && typeof h === \"object\") {\n const ho = h as Record<string, unknown>;\n if (ho.type === \"command\" && typeof ho.command === \"string\") {\n const cmd: HookCommand = {\n type: \"command\",\n command: ho.command,\n };\n if (typeof ho.timeout === \"number\") cmd.timeout = ho.timeout;\n hooks.push(cmd);\n }\n }\n }\n return { kind: \"wrapped\", entry: { matcher, hooks } };\n }\n if (obj.type === \"command\" && typeof obj.command === \"string\") {\n // Legacy unwrapped shape — v0.1.0–v0.1.4 wrote this form.\n const cmd: HookCommand = {\n type: \"command\",\n command: obj.command as string,\n };\n if (typeof obj.timeout === \"number\") cmd.timeout = obj.timeout;\n return { kind: \"legacy\", entry: cmd };\n }\n return { kind: \"unknown\", entry: raw };\n}\n\n/** True when the entry references our script and is safely ours to manage. */\nfunction isOurWrapped(entry: WrappedEntry): boolean {\n return entry.hooks.some((h) => isOurCommandPath(h.command));\n}\n\nexport async function runHookInstall(\n options: HookCommandOptions = {},\n): Promise<HookCommandResult> {\n const bundled = resolveHookScriptPath(options);\n if (!bundled.ok) {\n return { stdout: \"\", stderr: `almanac: ${bundled.error}\\n`, exitCode: 1 };\n }\n\n // Copy the bundled hook script to a stable user-owned location before\n // writing that path into settings.json. This is the Bug #1 fix:\n //\n // OLD behavior: settings.json pointed at the bundled path (inside\n // ~/.nvm/versions/node/<ver>/lib/node_modules/codealmanac/hooks/... or\n // ~/.npm/_npx/<sha>/node_modules/codealmanac/hooks/...). When the user\n // switches Node versions or the npx cache is evicted, the path breaks\n // silently and captures stop firing.\n //\n // NEW behavior: we copy almanac-capture.sh to ~/.claude/hooks/ (same\n // directory Claude Code uses for its own built-in hooks, always present)\n // and point settings.json there. The stable path is independent of\n // Node version and npm cache state. When the user upgrades codealmanac,\n // `almanac hook install` copies a fresh script and updates settings.json\n // if the path changed.\n //\n // When `hookScriptPath` is explicitly provided (test injection), the\n // caller has already specified the destination path — skip the copy and\n // use that path directly. The stable-copy concern only applies to the\n // production flow where we resolved from the bundled package layout.\n const script: ScriptResolution = options.hookScriptPath !== undefined\n ? bundled // already the caller-provided path, no copy needed\n : await copyToStableHooksDir(bundled.path, options);\n if (!script.ok) {\n return { stdout: \"\", stderr: `almanac: ${script.error}\\n`, exitCode: 1 };\n }\n\n const settingsPath = resolveSettingsPath(options);\n const settings = await readSettings(settingsPath);\n const existing = (settings.hooks?.SessionEnd ?? []).slice();\n\n // Walk existing entries and split them into buckets:\n // - `preserved` — foreign wrapped/unknown entries we leave alone.\n // - `oursAlready` — a wrapped entry that already points at OUR exact\n // script path (makes install a no-op).\n // - `oursStale` — a wrapped or legacy entry that references our\n // capture script but at a different absolute path\n // (old install, `npm i` moved us) or in the legacy\n // unwrapped shape. We'll collapse these into a\n // single fresh entry at the new path.\n const preserved: RawEntry[] = [];\n let oursAlready: WrappedEntry | null = null;\n const staleCount = { n: 0 };\n\n for (const raw of existing) {\n const c = classifyEntry(raw);\n if (c.kind === \"wrapped\") {\n if (!isOurWrapped(c.entry)) {\n preserved.push(raw);\n continue;\n }\n // Entry belongs to us. Does it already point at the exact script\n // path? If every command in its `hooks[]` that looks like ours is\n // already at `script.path`, it's up to date.\n const exactMatch = c.entry.hooks.some(\n (h) => h.command === script.path,\n );\n if (exactMatch && oursAlready === null) {\n oursAlready = c.entry;\n } else {\n staleCount.n += 1;\n }\n } else if (c.kind === \"legacy\") {\n if (isOurCommandPath(c.entry.command)) {\n // Legacy unwrapped entry of ours — always migrate to wrapped.\n staleCount.n += 1;\n } else {\n // Foreign legacy entry (user had their own script before\n // settings.json required wrapping). Leave it alone.\n preserved.push(raw);\n }\n } else {\n // Unknown shape — we can't classify it. Preserve verbatim.\n preserved.push(raw);\n }\n }\n\n // If every non-ours entry is a foreign unwrapped command (not a\n // wrapped one) we refuse to touch the file — Claude Code's newer\n // schema will already reject such files, but surfacing it here lets\n // the user clean up before we stack our entry on top. Wrapped foreign\n // entries are fine to leave alongside ours.\n const foreignLegacy = preserved.filter((raw) => {\n const c = classifyEntry(raw);\n return c.kind === \"legacy\";\n });\n if (foreignLegacy.length > 0) {\n const lines = foreignLegacy\n .map((raw) => {\n const c = classifyEntry(raw);\n if (c.kind === \"legacy\") return ` - ${c.entry.command}`;\n return \" - <unrecognized>\";\n })\n .join(\"\\n\");\n return {\n stdout: \"\",\n stderr:\n `almanac: SessionEnd has a foreign legacy entry:\\n${lines}\\n` +\n `Remove or rewrap it manually in ${settingsPath} before installing.\\n`,\n exitCode: 1,\n };\n }\n\n if (oursAlready !== null && staleCount.n === 0) {\n return {\n stdout: `almanac: SessionEnd hook already installed at ${script.path}\\n`,\n stderr: \"\",\n exitCode: 0,\n };\n }\n\n // Build the fresh wrapped entry and append to preserved foreign\n // entries. Stale entries of ours are dropped (we only ever want a\n // single active entry; multiple copies of the capture hook would\n // double-fire on session end).\n const fresh: WrappedEntry = {\n matcher: \"\",\n hooks: [\n {\n type: \"command\",\n command: script.path,\n timeout: HOOK_TIMEOUT_SECONDS,\n },\n ],\n };\n\n const newEntries: RawEntry[] = [...preserved, fresh];\n\n settings.hooks = { ...(settings.hooks ?? {}), SessionEnd: newEntries };\n await writeSettings(settingsPath, settings);\n\n return {\n stdout:\n `almanac: SessionEnd hook installed\\n` +\n ` script: ${script.path}\\n` +\n ` settings: ${settingsPath}\\n`,\n stderr: \"\",\n exitCode: 0,\n };\n}\n\nexport async function runHookUninstall(\n options: HookCommandOptions = {},\n): Promise<HookCommandResult> {\n const settingsPath = resolveSettingsPath(options);\n\n if (!existsSync(settingsPath)) {\n return {\n stdout: `almanac: SessionEnd hook not installed (no settings file)\\n`,\n stderr: \"\",\n exitCode: 0,\n };\n }\n\n const settings = await readSettings(settingsPath);\n const existing = (settings.hooks?.SessionEnd ?? []).slice();\n\n const kept: RawEntry[] = [];\n let removed = 0;\n\n for (const raw of existing) {\n const c = classifyEntry(raw);\n if (c.kind === \"wrapped\") {\n // Filter out our command(s) from the inner hooks array. Keep\n // anything else in the array intact — a foreign wrapper that\n // happened to include our script alongside its own commands\n // (unusual, but survivable) loses our entry and keeps theirs.\n const innerKept = c.entry.hooks.filter(\n (h) => !isOurCommandPath(h.command),\n );\n const innerRemoved = c.entry.hooks.length - innerKept.length;\n removed += innerRemoved;\n if (innerKept.length === 0) {\n // Only drop the outer wrapper when it was entirely ours. A\n // foreign wrapper that never contained our script stays verbatim\n // below (handled by `innerRemoved === 0`, which leaves\n // `innerKept.length === c.entry.hooks.length`, hence we fall\n // through to the else-branch).\n if (innerRemoved === 0) kept.push(raw);\n // else: fully owned by us, drop the container.\n } else if (innerRemoved === 0) {\n // Untouched foreign wrapper — preserve the raw object to keep\n // any fields (like matcher) byte-for-byte.\n kept.push(raw);\n } else {\n // Partial: rebuild with just the kept inner entries, preserving\n // the original matcher string.\n kept.push({ matcher: c.entry.matcher, hooks: innerKept });\n }\n } else if (c.kind === \"legacy\") {\n if (isOurCommandPath(c.entry.command)) {\n removed += 1;\n } else {\n kept.push(raw);\n }\n } else {\n kept.push(raw);\n }\n }\n\n if (removed === 0) {\n return {\n stdout: `almanac: SessionEnd hook not installed\\n`,\n stderr: \"\",\n exitCode: 0,\n };\n }\n\n if (settings.hooks !== undefined) {\n if (kept.length === 0) {\n // Empty SessionEnd array confuses some linters; drop the key when\n // nothing's left.\n const { SessionEnd: _dropped, ...rest } = settings.hooks;\n void _dropped;\n settings.hooks = rest;\n } else {\n settings.hooks = { ...settings.hooks, SessionEnd: kept };\n }\n\n // If `hooks` itself is now empty (user had only our SessionEnd entry\n // and no other hook categories), drop the `hooks` key entirely so\n // uninstall leaves the settings file in the same shape it would be\n // in had we never run install. An empty `\"hooks\": {}` is an obvious\n // breadcrumb in commit diffs.\n if (Object.keys(settings.hooks).length === 0) {\n delete settings.hooks;\n }\n }\n\n await writeSettings(settingsPath, settings);\n\n return {\n stdout: `almanac: SessionEnd hook removed\\n`,\n stderr: \"\",\n exitCode: 0,\n };\n}\n\nexport async function runHookStatus(\n options: HookCommandOptions = {},\n): Promise<HookCommandResult> {\n const script = resolveHookScriptPath(options);\n const settingsPath = resolveSettingsPath(options);\n\n if (!existsSync(settingsPath)) {\n return {\n stdout:\n `SessionEnd hook: not installed\\n` +\n `settings: ${settingsPath} (does not exist)\\n` +\n (script.ok ? `script would be: ${script.path}\\n` : \"\"),\n stderr: \"\",\n exitCode: 0,\n };\n }\n\n const settings = await readSettings(settingsPath);\n const existing = settings.hooks?.SessionEnd ?? [];\n\n // Walk the array looking for any entry (wrapped or legacy) that\n // references our capture script. Gathering foreign entries separately\n // lets us show them to the user if nothing of ours was found.\n let ourCommand: string | null = null;\n const foreignSummary: string[] = [];\n for (const raw of existing) {\n const c = classifyEntry(raw);\n if (c.kind === \"wrapped\") {\n for (const h of c.entry.hooks) {\n if (isOurCommandPath(h.command)) {\n ourCommand ??= h.command;\n } else {\n foreignSummary.push(h.command);\n }\n }\n } else if (c.kind === \"legacy\") {\n if (isOurCommandPath(c.entry.command)) {\n ourCommand ??= c.entry.command;\n } else {\n foreignSummary.push(c.entry.command);\n }\n }\n }\n\n if (ourCommand === null) {\n const foreignLines = foreignSummary\n .map((c) => ` - ${c}`)\n .join(\"\\n\");\n return {\n stdout:\n `SessionEnd hook: not installed\\n` +\n `settings: ${settingsPath}\\n` +\n (foreignSummary.length > 0\n ? `(${foreignSummary.length} foreign entr${foreignSummary.length === 1 ? \"y\" : \"ies\"} present:\\n${foreignLines})\\n`\n : \"\") +\n (script.ok ? `script would be: ${script.path}\\n` : \"\"),\n stderr: \"\",\n exitCode: 0,\n };\n }\n\n return {\n stdout:\n `SessionEnd hook: installed\\n` +\n `script: ${ourCommand}\\n` +\n `settings: ${settingsPath}\\n`,\n stderr: \"\",\n exitCode: 0,\n };\n}\n\n// ─── Settings JSON helpers ───────────────────────────────────────────\n\nasync function readSettings(settingsPath: string): Promise<SettingsJson> {\n if (!existsSync(settingsPath)) return {};\n try {\n const raw = await readFile(settingsPath, \"utf8\");\n if (raw.trim().length === 0) return {};\n const parsed = JSON.parse(raw) as unknown;\n if (parsed === null || typeof parsed !== \"object\") return {};\n return parsed as SettingsJson;\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`failed to read ${settingsPath}: ${msg}`);\n }\n}\n\nasync function writeSettings(\n settingsPath: string,\n settings: SettingsJson,\n): Promise<void> {\n const dir = path.dirname(settingsPath);\n await mkdir(dir, { recursive: true });\n\n // Atomic write: JSON.stringify → tmp file → rename. `rename` within the\n // same filesystem is atomic on POSIX; Claude Code never sees a partial\n // file. Formatted with 2-space indent to match the existing settings.\n const tmp = `${settingsPath}.almanac-tmp-${process.pid}`;\n const body = `${JSON.stringify(settings, null, 2)}\\n`;\n await writeFile(tmp, body, \"utf8\");\n await rename(tmp, settingsPath);\n}\n","import { existsSync } from \"node:fs\";\nimport { copyFile, mkdir, readFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nexport interface HookPathOptions {\n hookScriptPath?: string;\n settingsPath?: string;\n stableHooksDir?: string;\n}\n\nexport type ScriptResolution =\n | { ok: true; path: string }\n | { ok: false; error: string };\n\nexport function resolveSettingsPath(options: HookPathOptions): string {\n if (options.settingsPath !== undefined) return options.settingsPath;\n return path.join(homedir(), \".claude\", \"settings.json\");\n}\n\n/**\n * Copy the bundled hook script to `~/.claude/hooks/almanac-capture.sh`.\n *\n * This stable, user-owned destination survives Node version switches and\n * npm/npx cache evictions. The copy is idempotent: if bytes already match\n * we skip writing so repeated setup runs do not bump mtimes.\n */\nexport async function copyToStableHooksDir(\n bundledPath: string,\n options: HookPathOptions,\n): Promise<ScriptResolution> {\n const stableHooksDir =\n options.stableHooksDir ?? path.join(homedir(), \".claude\", \"hooks\");\n const dest = path.join(stableHooksDir, \"almanac-capture.sh\");\n\n try {\n await mkdir(stableHooksDir, { recursive: true });\n const srcBytes = await readFile(bundledPath);\n let needsCopy = true;\n if (existsSync(dest)) {\n try {\n const destBytes = await readFile(dest);\n if (srcBytes.equals(destBytes)) needsCopy = false;\n } catch {\n // Can't read dest — overwrite.\n }\n }\n if (needsCopy) {\n await copyFile(bundledPath, dest);\n }\n return { ok: true, path: dest };\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n return {\n ok: false,\n error: `could not copy hook script to ${dest}: ${msg}`,\n };\n }\n}\n\n/**\n * Locate the bundled `hooks/almanac-capture.sh`. Mirrors\n * `resolvePromptsDir` from `src/agent/prompts.ts`: two plausible layouts\n * (installed dist vs. source dev), probe each.\n */\nexport function resolveHookScriptPath(\n options: HookPathOptions,\n): ScriptResolution {\n if (options.hookScriptPath !== undefined) {\n return { ok: true, path: options.hookScriptPath };\n }\n\n const here = path.dirname(fileURLToPath(import.meta.url));\n\n const candidates = [\n // Bundled: `.../codealmanac/dist/codealmanac.js` → `../hooks/…`\n path.resolve(here, \"..\", \"hooks\", \"almanac-capture.sh\"),\n // Source after ts-node-style module layout or nested dist helpers.\n path.resolve(here, \"..\", \"..\", \"hooks\", \"almanac-capture.sh\"),\n // Source: `.../codealmanac/src/commands/hook/script.ts` → `../../../hooks/…`\n path.resolve(here, \"..\", \"..\", \"..\", \"hooks\", \"almanac-capture.sh\"),\n // Defensive nested fallback.\n path.resolve(here, \"..\", \"..\", \"..\", \"..\", \"hooks\", \"almanac-capture.sh\"),\n ];\n\n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n return { ok: true, path: candidate };\n }\n }\n\n return {\n ok: false,\n error:\n `could not locate hooks/almanac-capture.sh. Tried:\\n` +\n candidates.map((c) => ` - ${c}`).join(\"\\n\"),\n };\n}\n"],"mappings":"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","names":["existsSync","mkdir","readFile","path","existsSync","readFile","path","mkdir"]}
|
package/dist/cli-W3OYVJYH.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/cli/help.ts","../src/update/announce.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\nimport { basename } from \"node:path\";\n\nimport { Command } from \"commander\";\n\nimport { runSetup } from \"./commands/setup.js\";\nimport { configureGroupedHelp } from \"./cli/help.js\";\nimport { emit } from \"./cli/helpers.js\";\nimport { announceUpdateIfAvailable } from \"./update/announce.js\";\nimport {\n runInternalUpdateCheck,\n scheduleBackgroundUpdateCheck,\n} from \"./update/schedule.js\";\n\n/**\n * Optional dependency overrides for `run`. Tests use these to avoid\n * spawning the real setup wizard, the real update background check,\n * and the real update banner. Production callers pass nothing.\n */\nexport interface RunDeps {\n /** Replace the setup wizard (bare `codealmanac` / `almanac setup`). */\n runSetup?: typeof runSetup;\n /** Replace the pre-command update-nag banner. */\n announceUpdate?: (stderr: NodeJS.WritableStream) => void;\n /** Replace the post-command background update check scheduler. */\n scheduleUpdateCheck?: (argv: string[]) => void;\n /** Replace the internal update-check worker (run on --internal-check-updates). */\n runInternalUpdateCheck?: () => Promise<void>;\n}\n\n/**\n * Process-level CLI entrypoint. This owns invocation-level behavior:\n * update checks, bare `codealmanac` setup routing, Commander creation,\n * grouped help, and parsing. Individual command wiring lives in\n * `src/cli/register-commands.ts`.\n */\nexport async function run(argv: string[], deps: RunDeps = {}): Promise<void> {\n const runSetupFn = deps.runSetup ?? runSetup;\n const announceUpdateFn = deps.announceUpdate ?? announceUpdateIfAvailable;\n const scheduleUpdateCheckFn =\n deps.scheduleUpdateCheck ?? scheduleBackgroundUpdateCheck;\n const runInternalUpdateCheckFn =\n deps.runInternalUpdateCheck ?? runInternalUpdateCheck;\n\n if (argv.slice(2).includes(\"--internal-check-updates\")) {\n await runInternalUpdateCheckFn();\n return;\n }\n\n const programName = getProgramName(argv);\n\n announceUpdateFn(process.stderr);\n scheduleUpdateCheckFn(argv);\n\n const program = new Command();\n program\n .name(programName)\n .description(\n \"codealmanac — a living wiki for codebases, maintained by AI agents\",\n )\n .version(readPackageVersion(), \"-v, --version\", \"print version\");\n\n if (isRootVersionInvocation(argv.slice(2))) {\n await program.parseAsync(argv);\n return;\n }\n\n if (programName === \"codealmanac\") {\n const setupInvocation = tryParseSetupShortcut(argv.slice(2));\n if (setupInvocation !== null) {\n emit(await runSetupFn(setupInvocation));\n return;\n }\n }\n\n if (await tryRunSqliteFreeCommand(argv.slice(2), runSetupFn)) {\n return;\n }\n\n const { registerCommands } = await import(\"./cli/register-commands.js\");\n registerCommands(program);\n configureGroupedHelp(program);\n\n await program.parseAsync(argv);\n}\n\nfunction getProgramName(argv: string[]): \"almanac\" | \"codealmanac\" {\n const invoked = argv[1] !== undefined ? basename(argv[1]) : \"almanac\";\n return invoked === \"codealmanac\" ? \"codealmanac\" : \"almanac\";\n}\n\nfunction isRootVersionInvocation(args: string[]): boolean {\n return args.length === 1 && (args[0] === \"--version\" || args[0] === \"-v\");\n}\n\nfunction parseSetupFlags(args: string[]): {\n yes?: boolean;\n skipHook?: boolean;\n skipGuides?: boolean;\n} {\n return {\n yes: args.includes(\"--yes\") || args.includes(\"-y\"),\n skipHook: args.includes(\"--skip-hook\"),\n skipGuides: args.includes(\"--skip-guides\"),\n };\n}\n\nfunction parseUpdateFlags(args: string[]): {\n dismiss?: boolean;\n check?: boolean;\n enableNotifier?: boolean;\n disableNotifier?: boolean;\n} {\n return {\n dismiss: args.includes(\"--dismiss\"),\n check: args.includes(\"--check\"),\n enableNotifier: args.includes(\"--enable-notifier\"),\n disableNotifier: args.includes(\"--disable-notifier\"),\n };\n}\n\nfunction parseUninstallFlags(args: string[]): {\n yes?: boolean;\n keepHook?: boolean;\n keepGuides?: boolean;\n} {\n return {\n yes: args.includes(\"--yes\") || args.includes(\"-y\"),\n keepHook: args.includes(\"--keep-hook\"),\n keepGuides: args.includes(\"--keep-guides\"),\n };\n}\n\nfunction parseDoctorFlags(args: string[]): {\n json?: boolean;\n installOnly?: boolean;\n wikiOnly?: boolean;\n} {\n return {\n json: args.includes(\"--json\"),\n installOnly: args.includes(\"--install-only\"),\n wikiOnly: args.includes(\"--wiki-only\"),\n };\n}\n\nasync function tryRunSqliteFreeCommand(\n args: string[],\n runSetupFn: typeof runSetup,\n): Promise<boolean> {\n if (args.includes(\"--help\") || args.includes(\"-h\")) return false;\n\n const [command, subcommand] = args;\n if (command === undefined) return false;\n\n if (command === \"setup\") {\n emit(await runSetupFn(parseSetupFlags(args.slice(1))));\n return true;\n }\n\n if (command === \"hook\") {\n const { runHookInstall, runHookStatus, runHookUninstall } = await import(\n \"./commands/hook.js\"\n );\n if (subcommand === \"install\") {\n emit(await runHookInstall());\n return true;\n }\n if (subcommand === \"uninstall\") {\n emit(await runHookUninstall());\n return true;\n }\n if (subcommand === \"status\") {\n emit(await runHookStatus());\n return true;\n }\n return false;\n }\n\n if (command === \"update\") {\n const { runUpdate } = await import(\"./commands/update.js\");\n emit(await runUpdate(parseUpdateFlags(args.slice(1))));\n return true;\n }\n\n if (command === \"doctor\") {\n const { runDoctor } = await import(\"./commands/doctor.js\");\n emit(await runDoctor({\n cwd: process.cwd(),\n ...parseDoctorFlags(args.slice(1)),\n }));\n return true;\n }\n\n if (command === \"uninstall\") {\n const { runUninstall } = await import(\"./commands/uninstall.js\");\n emit(await runUninstall(parseUninstallFlags(args.slice(1))));\n return true;\n }\n\n return false;\n}\n\nfunction readPackageVersion(): string {\n try {\n const require = createRequire(import.meta.url);\n const pkg = require(\"../package.json\") as { version?: unknown };\n if (typeof pkg.version === \"string\" && pkg.version.length > 0) {\n return pkg.version;\n }\n } catch {\n // Fall back to \"unknown\" rather than crashing the CLI on a broken install.\n }\n return \"unknown\";\n}\n\nexport interface SetupShortcutOptions {\n yes?: boolean;\n skipHook?: boolean;\n skipGuides?: boolean;\n}\n\n/**\n * Decide whether a bare `codealmanac [...args]` invocation should route\n * straight to `runSetup` (and if so, with which flags). Returns the\n * options object when it's a setup shortcut, or `null` when Commander\n * should parse the invocation normally.\n */\nexport function tryParseSetupShortcut(args: string[]): SetupShortcutOptions | null {\n if (args.length === 0) return {};\n\n const opts: SetupShortcutOptions = {};\n for (const arg of args) {\n if (arg === \"--yes\" || arg === \"-y\") {\n opts.yes = true;\n continue;\n }\n if (arg === \"--skip-hook\") {\n opts.skipHook = true;\n continue;\n }\n if (arg === \"--skip-guides\") {\n opts.skipGuides = true;\n continue;\n }\n return null;\n }\n return opts;\n}\n","import { Command, type Help } from \"commander\";\n\nimport { BLUE, BOLD, DIM, RST } from \"../ansi.js\";\n\nconst HELP_GROUPS: Array<{ title: string; commands: string[] }> = [\n {\n title: \"Query\",\n commands: [\"search\", \"show\", \"health\", \"list\"],\n },\n {\n title: \"Edit\",\n commands: [\"tag\", \"untag\", \"topics\"],\n },\n {\n title: \"Wiki lifecycle\",\n commands: [\"bootstrap\", \"capture\", \"hook\", \"reindex\"],\n },\n {\n title: \"Setup\",\n commands: [\"setup\", \"uninstall\", \"doctor\", \"update\"],\n },\n];\n\n/**\n * Install a custom `formatHelp` that replaces commander's flat\n * \"Commands:\" section with grouped headings. Keeps usage + options +\n * per-command short descriptions; only the commands section changes.\n */\nexport function configureGroupedHelp(program: Command): void {\n program.configureHelp({\n formatHelp(cmd, helper): string {\n if (cmd.parent !== null) {\n return renderDefault(cmd, helper);\n }\n\n const termWidth = helper.padWidth(cmd, helper);\n const helpWidth =\n helper.helpWidth ?? process.stdout.columns ?? 80;\n const itemSepWidth = 2;\n\n const out: string[] = [];\n out.push(`${BOLD}Usage:${RST} ${helper.commandUsage(cmd)}\\n`);\n\n const description = helper.commandDescription(cmd);\n if (description.length > 0) {\n out.push(\n helper.wrap(description, helpWidth, 0) + \"\\n\",\n );\n }\n\n const optionList = helper\n .visibleOptions(cmd)\n .map((o) => {\n const term = helper.optionTerm(o);\n const pad = \" \".repeat(Math.max(0, termWidth - term.length) + itemSepWidth);\n return `${BLUE}${term}${RST}${pad}${DIM}${helper.optionDescription(o)}${RST}`;\n });\n if (optionList.length > 0) {\n out.push(`${BOLD}Options:${RST}`);\n for (const l of optionList) out.push(` ${l}`);\n out.push(\"\");\n }\n\n const visible = helper.visibleCommands(cmd);\n const byName = new Map<string, (typeof visible)[number]>();\n for (const c of visible) byName.set(c.name(), c);\n\n for (const group of HELP_GROUPS) {\n const members = group.commands\n .map((n) => byName.get(n))\n .filter((c): c is (typeof visible)[number] => c !== undefined);\n if (members.length === 0) continue;\n out.push(`${BOLD}${group.title}:${RST}`);\n for (const c of members) {\n const term = helper.subcommandTerm(c);\n const desc = helper.subcommandDescription(c);\n const padding = Math.max(\n 0,\n termWidth - term.length + itemSepWidth,\n );\n out.push(` ${BLUE}${term}${RST}${\" \".repeat(padding)}${DIM}${desc}${RST}`);\n byName.delete(c.name());\n }\n out.push(\"\");\n }\n\n byName.delete(\"help\");\n if (byName.size > 0) {\n out.push(`${BOLD}Other:${RST}`);\n for (const c of byName.values()) {\n const term = helper.subcommandTerm(c);\n const desc = helper.subcommandDescription(c);\n const padding = Math.max(\n 0,\n termWidth - term.length + itemSepWidth,\n );\n out.push(` ${BLUE}${term}${RST}${\" \".repeat(padding)}${DIM}${desc}${RST}`);\n }\n out.push(\"\");\n }\n\n return out.join(\"\\n\");\n },\n });\n}\n\nfunction renderDefault(cmd: Command, helper: Help): string {\n const termWidth = helper.padWidth(cmd, helper);\n const helpWidth = helper.helpWidth ?? process.stdout.columns ?? 80;\n const itemSepWidth = 2;\n\n const lines: string[] = [`${BOLD}Usage:${RST} ${helper.commandUsage(cmd)}\\n`];\n const description = helper.commandDescription(cmd);\n if (description.length > 0) {\n lines.push(helper.wrap(description, helpWidth, 0) + \"\\n\");\n }\n\n const args = helper.visibleArguments(cmd).map((a) => {\n const term = helper.argumentTerm(a);\n const pad = \" \".repeat(Math.max(0, termWidth - term.length) + itemSepWidth);\n return `${BLUE}${term}${RST}${pad}${DIM}${helper.argumentDescription(a)}${RST}`;\n });\n if (args.length > 0) {\n lines.push(`${BOLD}Arguments:${RST}`);\n for (const a of args) lines.push(` ${a}`);\n lines.push(\"\");\n }\n\n const opts = helper.visibleOptions(cmd).map((o) => {\n const term = helper.optionTerm(o);\n const pad = \" \".repeat(Math.max(0, termWidth - term.length) + itemSepWidth);\n return `${BLUE}${term}${RST}${pad}${DIM}${helper.optionDescription(o)}${RST}`;\n });\n if (opts.length > 0) {\n lines.push(`${BOLD}Options:${RST}`);\n for (const o of opts) lines.push(` ${o}`);\n lines.push(\"\");\n }\n\n const subs = helper.visibleCommands(cmd).map((c) => {\n const term = helper.subcommandTerm(c);\n const pad = \" \".repeat(Math.max(0, termWidth - term.length) + itemSepWidth);\n return `${BLUE}${term}${RST}${pad}${DIM}${helper.subcommandDescription(c)}${RST}`;\n });\n if (subs.length > 0) {\n lines.push(`${BOLD}Commands:${RST}`);\n for (const s of subs) lines.push(` ${s}`);\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\");\n}\n","import { readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\n\nimport { getConfigPath } from \"./config.js\";\nimport { isNewer } from \"./semver.js\";\nimport { getStatePath, type UpdateState } from \"./state.js\";\n\n/**\n * Pre-command update-nag banner. Runs synchronously at the very top of\n * every `run()` invocation, before commander even touches argv. Prints\n * one line to stderr if:\n *\n * 1. `~/.almanac/update-state.json` exists and parses.\n * 2. `latest_version` is strictly newer than `installed_version`.\n * 3. `latest_version` is NOT in `dismissed_versions`.\n * 4. `~/.almanac/config.json`.`update_notifier` is not `false`.\n *\n * Otherwise silent. Deliberately synchronous and filesystem-blocking:\n * this runs in the CLI critical path and waiting on a Promise would\n * force every command to become async upfront. The files are tiny\n * (<1KB each); a sync read is cheap.\n *\n * Why stderr: agents and scripts parse stdout; stderr is the\n * conventional channel for diagnostics and nags. A tool piping\n * `almanac search` into `xargs` shouldn't see the banner mixed into\n * its slug list. Users running interactively see stderr anyway, so\n * the nag is visible in practice.\n */\n\nexport interface AnnounceOptions {\n statePath?: string;\n configPath?: string;\n /** Override for tests — normally reads from package.json at import time. */\n installedVersion?: string;\n /** Enable ANSI coloring regardless of isTTY. Tests pass `false`. */\n color?: boolean;\n}\n\nconst RST = \"\\x1b[0m\";\nconst BOLD = \"\\x1b[1m\";\nconst YELLOW = \"\\x1b[33m\";\n\nexport function announceUpdateIfAvailable(\n stderr: NodeJS.WritableStream,\n opts: AnnounceOptions = {},\n): void {\n const statePath = opts.statePath ?? getStatePath();\n const configPath = opts.configPath ?? getConfigPath();\n const installed = opts.installedVersion ?? readInstalledVersion();\n\n // Config gate. Must be checked before state: a user who disabled the\n // notifier shouldn't pay even a state-file read.\n if (!shouldNotify(configPath)) return;\n\n const state = readStateSync(statePath);\n if (state === null) return;\n if (state.latest_version.length === 0) return;\n if (!isNewer(state.latest_version, installed)) return;\n if (state.dismissed_versions.includes(state.latest_version)) return;\n\n const useColor =\n opts.color ?? (process.stderr.isTTY === true && !(\"NO_COLOR\" in process.env));\n const warn = useColor ? `${YELLOW}${BOLD}\\u26a0${RST}` : \"!\";\n const cmd = useColor ? `${BOLD}almanac update${RST}` : \"almanac update\";\n stderr.write(\n `${warn} codealmanac ${state.latest_version} available ` +\n `(you're on ${installed}) — run: ${cmd}\\n`,\n );\n}\n\n/**\n * Sync-read the state file. Returns `null` when missing, empty, or\n * malformed — the announce path MUST NOT throw into the CLI critical\n * path. Avoids the `async readState` used by the worker because\n * `run()` would otherwise need `await announceUpdateIfAvailable(...)`\n * on every invocation, which turns into a multi-millisecond penalty\n * on commands that don't care.\n */\nfunction readStateSync(path: string): UpdateState | null {\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch {\n return null;\n }\n const trimmed = raw.trim();\n if (trimmed.length === 0) return null;\n try {\n const parsed = JSON.parse(trimmed) as Partial<UpdateState>;\n return {\n last_check_at:\n typeof parsed.last_check_at === \"number\" ? parsed.last_check_at : 0,\n installed_version:\n typeof parsed.installed_version === \"string\"\n ? parsed.installed_version\n : \"\",\n latest_version:\n typeof parsed.latest_version === \"string\" ? parsed.latest_version : \"\",\n dismissed_versions: Array.isArray(parsed.dismissed_versions)\n ? parsed.dismissed_versions.filter(\n (v): v is string => typeof v === \"string\",\n )\n : [],\n };\n } catch {\n return null;\n }\n}\n\nfunction shouldNotify(configPath: string): boolean {\n let raw: string;\n try {\n raw = readFileSync(configPath, \"utf8\");\n } catch {\n return true; // no config file → default notify on\n }\n const trimmed = raw.trim();\n if (trimmed.length === 0) return true;\n try {\n const parsed = JSON.parse(trimmed) as { update_notifier?: unknown };\n if (parsed.update_notifier === false) return false;\n return true;\n } catch {\n return true;\n }\n}\n\nfunction readInstalledVersion(): string {\n // Dev: `src/update/announce.ts` → `../../package.json`. Bundled:\n // `dist/codealmanac.js` → `../package.json`. Try both.\n try {\n const require = createRequire(import.meta.url);\n const pkg = require(\"../../package.json\") as { version?: unknown };\n if (typeof pkg.version === \"string\" && pkg.version.length > 0) {\n return pkg.version;\n }\n } catch {\n // Fall through.\n }\n try {\n const require = createRequire(import.meta.url);\n const pkg = require(\"../package.json\") as { version?: unknown };\n if (typeof pkg.version === \"string\" && pkg.version.length > 0) {\n return pkg.version;\n }\n } catch {\n // Fall through.\n }\n return \"unknown\";\n}\n"],"mappings":"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","names":["createRequire","RST","BOLD","require","require","createRequire"]}
|
package/dist/doctor-ODFNJUKH.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
runDoctor
|
|
4
|
-
} from "./chunk-QLHJP2XK.js";
|
|
5
|
-
import "./chunk-4CODZRHH.js";
|
|
6
|
-
import "./chunk-QHQ6YH7U.js";
|
|
7
|
-
import "./chunk-FM3VRDK7.js";
|
|
8
|
-
import "./chunk-3LC55TG6.js";
|
|
9
|
-
import "./chunk-Z4MWLVS2.js";
|
|
10
|
-
import "./chunk-AXFPUHBN.js";
|
|
11
|
-
import "./chunk-7JUX4ADQ.js";
|
|
12
|
-
export {
|
|
13
|
-
runDoctor
|
|
14
|
-
};
|
|
15
|
-
//# sourceMappingURL=doctor-ODFNJUKH.js.map
|