codealmanac install audit
Historical hook-era audit. Current auto-capture is scheduler-backed:
almanac automation installregisters the scheduledalmanac capture sweepjob, and setup/uninstall clean old hook entries privately.
Version examined: 0.1.5 (from https://github.com/AlmanacCode/codealmanac.git, cloned 2026-04-16).
Context: User ran npx codealmanac in /Users/kushagrachitkara/Downloads/reverie/openalmanac. Bootstrap and capture hook produced output; almanac is not on PATH; which almanac returns nothing.
Scope: Install surface only. The wiki data model, capture/writer/reviewer agents, and query CLI design are out of scope — those work.
TL;DR#
Three real problems, compounding:
- The README advertises
npx codealmanacas "installs globally + runs the setup wizard." The wizard does not install anything globally. It installs a hook, copies guide files, and edits~/.claude/CLAUDE.md. Thealmanacbinary stays inside npx's per-invocation cache and is never linked onto PATH. - The
SessionEndhook written into~/.claude/settings.jsonis an absolute path into npx's content-addressed cache (~/.npm/_npx/<sha>/node_modules/codealmanac/hooks/almanac-capture.sh). That<sha>is a hash of the resolved dependency tree for the exact version that was run. A future version bump, an npm cache GC, or anpm cache cleansilently invalidates the hook and captures stop firing. - The setup wizard's side-effect model is inverted: it performs globally persistent modifications to the user's Claude config (hook, CLAUDE.md import, two guide files in
~/.claude/) while refusing to do the one locally-reversible thing (install itself on PATH). The things that require consent are automated; the thing that could be automated is skipped.
Evidence#
Evidence 1 — the README claim#
README.md:22 states:
npx codealmanac # installs globally + runs the setup wizardThis is not what happens. Searching the entire src/ tree for any call that would install the package globally:
grep -rn 'npm i -g\|spawn.*npm\|install.*codealmanac' src/
returns zero hits in src/commands/setup.ts. The only references to npm i -g anywhere in the source are:
src/commands/update.ts:17— the separatealmanac updatecommand, which does shell out tonpm i -g codealmanac@latestbut is invoked explicitly by the user, not bysetup.src/commands/doctor.ts:196— a diagnostic string printed when the binary isn't detectable:"reinstall with: npm install -g codealmanac". Doctor knows the install can end up in a broken state; setup doesn't prevent it.
So setup.ts — the thing that runs when you invoke bare codealmanac or npx codealmanac — installs the hook, the guides, and the CLAUDE.md import, and that is all. The package binary is wherever npx put it.
Evidence 2 — the hook path on this machine#
~/.claude/settings.json (this user, post-install):
{
"SessionEnd": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "/Users/kushagrachitkara/.npm/_npx/1123f5c16135f0b0/node_modules/codealmanac/hooks/almanac-capture.sh",
"timeout": 10
}
]
}
]
}The directory 1123f5c16135f0b0 is npm's content-addressed cache identifier — a SHA of (package name, version, resolved dependency set). Per-machine, per-version. The equivalent path does not exist on any other machine, and will not exist on this machine once:
codealmanacpublishes a new version andnpx codealmanacis run again (new hash, new directory, old hook path still points at the old cache entry);- npm GCs old npx cache entries (happens automatically on some npm versions, or on
npm cache clean); - the user runs
rm -rf ~/.npm/_npx/*to reclaim disk space.
In every one of those cases the hook script file disappears and Claude Code invokes a nonexistent command at session end. By design, SessionEnd failures don't surface to the user, so the wiki silently stops being captured. The user's evidence that the hook did fire in the current session is ~/.almanac/.capture-e09d8ede-…jsonl (1.2MB, today) — but that only proves it worked once, while the cache entry still existed.
Evidence 3 — the hook script itself knows about this#
hooks/almanac-capture.sh:38-40:
# Prefer `almanac` on PATH; fall back to `npx codealmanac` if the
# binary isn't linked (happens with non-global installs).
if command -v almanac >/dev/null 2>&1; then
CMD="almanac"The script is aware that almanac may not be on PATH and falls back to npx codealmanac. But this fallback only helps if the shell script itself is reachable. The fallback lives inside the script; the path to the script is the unprotected part. The outer invocation is the single point of failure, and the setup wizard puts that single point of failure on an ephemeral cache directory.
Evidence 4 — npm bin discovery is stale#
Attempting to run npm bin -g during this audit returned:
Unknown command: "bin"
npm bin was removed in npm 9. Any install documentation or script that relies on it is broken against modern Node distributions. This isn't the root cause of the user's issue but indicates the install tooling hasn't been tested against current npm in some time.
Evidence 5 — the wiki is fine#
To rule out wiki-level corruption: .almanac/ in the user's project contains:
- 12 markdown pages in
pages/(aws-s3, doppler, electron-gui, mcp-server, meilisearch, observability, quill-agent, redis, ref-token, research-pipeline, supabase, wiki-data-model) topics.yaml(topic DAG)index.db(SQLite FTS index, 139KB, fresh)README.md(the notability bar + conventions).bootstrap-20260416-131328.log(1.8MB) — from a different user (cwd: /Users/rohan/Desktop/Projects/openalmanac). This is fine and expected: a collaborator scaffolded the wiki on their machine and committed it; every subsequent clone auto-registers on first query. This is the intended cross-user workflow, not a bug.
The wiki content is healthy. The problem is purely that this user cannot query it from the shell because almanac is not on PATH.
Root causes#
Root cause 1 — documentation diverges from behavior#
setup.ts is documented (in its own docstring, lines 21–44) as doing hook install, guide install, and CLAUDE.md import. Those are the three steps it performs. The README, however, positions bare npx codealmanac as the canonical install invocation and claims it "installs globally." Either:
setup.tsshould do a global install (or warn loudly that it didn't), or- the README should tell users explicitly that
npm i -g codealmanacis the install step andcodealmanacis only the post-install configurator.
Currently the README makes the user believe the first thing happened. It didn't.
Root cause 2 — encoding install-path-at-time-of-run into user config#
hook.ts resolves the bundled hooks/almanac-capture.sh relative to the running codealmanac module and writes that absolute path into ~/.claude/settings.json. When codealmanac is running from ~/.npm/_npx/<sha>/…, that's the path it writes. The written config is therefore only valid for the exact package directory that was running at configuration time.
The cleaner primitive is: copy the hook script into a stable location under the user's own config tree — for example ~/.claude/hooks/codealmanac-capture.sh — and write that path into settings.json. Then the hook survives version bumps, cache GC, and uninstall/reinstall cycles. This is what hook.ts does for guide files (they get copied to ~/.claude/codealmanac.md and ~/.claude/codealmanac-reference.md); the same pattern should extend to the hook script itself.
Root cause 3 — inverted side-effect asymmetry#
The setup wizard happily modifies user-global, user-visible configuration:
- edits
~/.claude/settings.jsonto register a hook; - copies
mini.mdto~/.claude/codealmanac.md; - copies
reference.mdto~/.claude/codealmanac-reference.md; - appends
@~/.claude/codealmanac.mdto~/.claude/CLAUDE.md.
All of these are persistent, span every Claude Code project the user has, and touch files the user might have their own edits to. They are the right side-effects for this tool — but they are also the ones with the highest blast radius if wrong.
The one thing setup won't do — put almanac on PATH — is (a) locally reversible (npm uninstall -g codealmanac), (b) the specific side-effect the user unambiguously wants from running an "install" command, and (c) the thing whose absence leaves the user confused. The priority ordering is backwards: the high-blast-radius actions are automated, the low-blast-radius action is skipped.
Root cause 4 — "branded TUI" prioritized over mechanics#
setup.ts lines 97–110 define an 11-letter ASCII banner with a six-stop grey gradient. The file has more code dedicated to banner rendering and ANSI color helpers than to validating that the resulting install is functional. Doctor does have a detectInstallPath() primitive (doctor.ts:799–827) that walks up from import.meta.url looking for package.json with name === "codealmanac" — the right tool to detect "I am running from an ephemeral npx cache." Setup does not use it. If it did, it could print:
! codealmanac is running from an npx cache directory
(~/.npm/_npx/1123f5c16135f0b0/node_modules/codealmanac).
The `almanac` binary will not be on your PATH after this completes.
To put it on PATH: npm install -g codealmanac
Or continue anyway: the hook will fall back to `npx codealmanac`
at session end, but this is fragile — the cache
path may be GC'd between invocations.
That is five lines of code and it closes the entire failure class.
Impact#
On a fresh machine, a user running npx codealmanac ends up with:
| Resource | State | Persistent? |
|---|---|---|
~/.claude/settings.json SessionEnd hook |
Registered, pointing at ephemeral cache path | Yes (until cache evicted) |
~/.claude/codealmanac.md |
Present | Yes |
~/.claude/codealmanac-reference.md |
Present | Yes |
~/.claude/CLAUDE.md |
Modified to import the guide | Yes |
almanac on PATH |
Not present | N/A |
codealmanac on PATH |
Not present | N/A |
The user cannot run the documented query commands (almanac search, almanac show, almanac doctor) from their shell. Capture may continue to work for some period — until one of the cache-invalidation events in Evidence 2 fires — at which point it silently stops and the user has no indication until they inspect their sessions and notice no .almanac/.capture-*.jsonl files appearing.
The failure is silent, time-delayed, and invisible to the tool's own diagnostics unless the user specifically runs almanac doctor — which they can't run, because almanac is not on PATH.
Recommendations#
For the codealmanac maintainer#
In rough priority order:
-
Fix the README, today. Either delete the line that says
npx codealmanac"installs globally," or change the wizard to actually do that when invoked under npx. The current divergence is the single highest-leverage bug because it mis-sets user expectations before any code runs. -
Copy the hook script into a stable location on install. Write
~/.claude/hooks/codealmanac-capture.sh(or equivalent) during setup and point settings.json at that copy. Follows the existing pattern for guide files. Removes the ephemeral-cache failure mode entirely. -
Detect ephemeral-install in setup and warn. Reuse
detectInstallPath()fromdoctor.ts. If the resolved install path is under/.npm/_npx/,/.pnpm-store/, a Yarn cache, or similar, print a prominent warning with thenpm i -g codealmanacfix. Exit zero — don't block — but make sure the user sees it. -
Have
codealmanac(bare) self-install on first run. If the current process isn't reachable via PATH, prompt: "Install codealmanac globally so you can runalmanacfrom your shell? [Y/n]". On yes, spawnnpm install -g codealmanac. This is what users thinknpx codealmanacdoes and what the README already claims. -
Fix
npm bin-era tooling. Replacenpm bin -gwithnpm prefix -g+/binconstruction, which works on npm 9+. -
Add a smoke test for the install surface. A CI job that runs
npx codealmanac --yesin a clean container, then assertswhich almanacsucceeds, scheduled auto-capture uses a durable command path, andalmanac doctorreports no errors. The current failure mode survived to a shipping release because nothing tests the post-install invariant.
For a user stuck in this state (the reader of this audit)#
Execute in order:
npm install -g codealmanac
which almanac # should resolve under your npm prefix
almanac automation install # installs scheduled capture from the durable command path
almanac doctor # reports binary location + automation + guides + import line + wiki statsAfter almanac automation install, inspect almanac automation status or almanac doctor --install-only and verify the scheduled command does not point under ~/.npm/_npx/<sha>/.... Any query command (almanac search --mentions <path>, almanac show <slug>) should now work.
Appendix A — what's actually good#
Flagging this so the audit isn't purely negative:
detectInstallPathindoctor.ts:799is the right primitive for self-location. It just isn't called from the place that needs it most (setup).- The hook script's fallback (
command -v almanac || npx codealmanac) is defensive in the right direction. It just can't protect against its own path disappearing. - Wiki auto-registration on first query handles the cross-user committed-
.almanac/case gracefully. The fact thatrohan's bootstrap log is intact in the user's repo and the wiki data is queryable once the CLI is installed shows this works. - Hook script itself is careful: JSON payload parsing, walks up looking for
.almanac/, backgrounds capture, exits zero on all paths. Zero complaints. almanac doctor's install-probe design (walkimport.meta.url→ findpackage.jsonwith matching name) is correct and portable. Works for global, npx, local, and dev-from-source installs. Good primitive.
The install surface problems are concentrated in roughly three files (setup.ts, hook.ts, README.md) and are all fixable without touching the wiki data model, the query commands, or the agent pipeline. The core of the product is sound; the onboarding is where it falls down.