mirror of
https://github.com/thedotmack/claude-mem.git
synced 2026-09-20 04:23:02 +08:00
work/unbundle
6 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d959572bde |
fix(setup): guard plugin deps on completeness, not node_modules existence (#3972)
* fix(setup): guard plugin deps on completeness, not node_modules existence `ensurePluginDependencies()` decided whether to run `bun install` by asking whether `node_modules/` existed. A tree that is merely present — but short of the declared closure — satisfied that check and permanently skipped repair on every subsequent Setup run. Two trigger paths, and the second needs no corruption at all: 1. An install interrupted mid-fetch (network timeout, OOM, registry 5xx) leaves `node_modules/` behind incomplete. 2. A tree that was complete *for the version that created it*. `zod` was added to plugin deps after some users had already installed; their node_modules has been incomplete ever since, and no upgrade heals it because the stale tree is gitignored and gets re-seeded into each new cache version. The worker then dies at boot on `Cannot find module 'zod/v3'` while memory search keeps working — `mcp-server.cjs` bundles zod (build-hooks.js:519 hard-fails if it ever externalizes it) while `worker-service.cjs` has 19 external zod requires. So the plugin looks alive while capture is dead. One reporter lost ~4 months of capture with no visible symptom. Guard on completeness instead: every key of `package.json` `dependencies` must resolve, with a `<dep>/package.json` fallback for bin-only packages like tree-sitter-cli (gh #2730), plus the zod subpaths the worker requires. This mirrors `verifyCriticalModules` (src/npx-cli/install/setup-runtime.ts:245), which already applies exactly this contract on the npx install path but was never reachable from the Setup hook. It cannot be imported here — this script is standalone and dependency-free — so the probe is inlined and the two are cross-referenced. Two consequences fall out: - The post-failure `rmSync` of node_modules is removed. It existed only because the existence guard would otherwise block retry forever; the completeness guard re-detects the gap on the next run, so deleting bought nothing while actively destroying a partial tree that still powers search. - A zero exit from `bun install` is no longer trusted. It can exit 0 with a failed integrity check, so the closure is re-probed afterwards and the diagnostic reports what actually resolves. The install diagnostic now names the unresolvable modules, which is what stops this failure mode from being silent. Fixes #3755. Refs #3604 (plan-16), #2730. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NT5K64VU4a7Kbc36oTVjyc * fix(setup): keep the completeness probe inside the plugin's own node_modules Greptile P1 on #3872, and it was right. `require.resolve(dep, { paths: [nodeModulesPath] })` reads as tree-scoped but is not. `paths` only seeds Node's lookup; resolution then walks every ancestor directory and always consults the global folders ($HOME/.node_modules, $PREFIX/lib/node). Plugin roots live at ~/.claude/plugins/cache/thedotmack/claude-mem/<version>/, so a copy of a dependency anywhere above them — or installed globally — answered for the plugin's own. Verified against the committed probe: a plugin whose node_modules is completely EMPTY, with a valid zod one directory up, exits 0 with no output and never runs the install. That is the #3755 bug reintroduced by the fix for it, and it would have shipped silently. Presence is now checked by statting `<node_modules>/<dep>/package.json` directly, which cannot escape the tree. This is also the signal scripts/check-postinstall-allowlist.js:75-78 already uses, and it handles scoped names (split into path segments) and bin-only packages like tree-sitter-cli (package.json present, no entry point — gh #2730) without the bare-name/fallback dance. zod's subpaths still need real resolution, since they are `exports` entries that a present directory does not guarantee. Those are now accepted only when the resolved file lands inside the plugin's own zod directory. Both sides are realpath'd before comparison: bun can materialise node_modules entries as links into a shared store, and Node returns the real path of what it resolved, so a literal comparison would report a healthy linked install as missing and loop the install forever. Containment uses path.relative rather than string prefixing so a sibling like zod-extra is not mistaken for being inside zod. Regression test added; it fails against the previous commit's probe. Note for follow-up: verifyCriticalModules (setup-runtime.ts:245) has the same ancestor/global escape. It is far less dangerous there — a post-install assertion that fails loud, not the gate deciding whether repair runs at all — but worth tightening. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NT5K64VU4a7Kbc36oTVjyc --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
83dd59256f |
fix: centralize windows spawn shims
(cherry picked from commit
|
||
|
|
eae20a410a |
fix(setup): auto-install plugin dependencies at Setup phase (2649) (#2650)
* fix(setup): auto-install plugin dependencies at Setup phase (2649) The plugin marketplace extracts files into ~/.claude/plugins/cache/... but does not run `bun install`. On fresh installs the worker crashes with `Cannot find module 'zod/v3'` on the first hook invocation (gh #2640, #2637). PR #2644 fixed this by auto-installing dependencies inside `bun-runner.js`, but that runs on the SessionStart / UserPromptSubmit critical path. Review on #2644 (YOMXXX, #2649) flagged this as the wrong architectural home: corporate proxies, offline machines, permissions, registry timeouts — every install failure lands on the user's first prompt instead of at install time. Move the auto-install to `version-check.js` (Setup phase), the only standalone hook script and the natural place to materialise plugin runtime state. Setup has a 300s timeout (vs 60s for SessionStart), runs once per Claude Code launch, and the node_modules guard short- circuits subsequent invocations. Failure modes surfaced explicitly (spawn exception, non-zero exit, signal-killed including OOM SIGKILL — where spawnSync leaves status=null and error=undefined). RED → GREEN proof: - /tmp/issue-2649-proof/RED.log (unpatched: 1 pass, 1 fail) - /tmp/issue-2649-proof/GREEN.log (patched: 2 pass, 0 fail) - Full-suite baseline: 1803 pass / 55 fail (unchanged after fix: 1805 pass / 55 fail — +2 = new tests, 0 regressions). Existing `plugin-version-check.test.ts` updated to pre-create node_modules in beforeEach so its marker-compat assertions (`stderr === ''`) are unaffected by the new Setup-phase install path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(setup): cleanup partial node_modules + success log (#2650 review) Address Greptile review findings on PR #2650: P1 — failed install permanently blocks retry. `bun install` often creates the node_modules directory before it terminates under failure (network timeout mid-fetch, registry 5xx, OOM kill). The existsSync(node_modules) guard would then permanently skip every subsequent Setup run, leaving the plugin broken with no recovery short of manual `rm -rf node_modules`. Remove the partial dir in the failure branch so the next Setup invocation can retry automatically. P2 — no completion confirmation. A Setup hook that can block for up to 120s needs an explicit success line so users can distinguish a hung install from one that finished silently. Emit a success diagnostic in the no-error else branch. New test `cleans up partial node_modules after a failed install` uses a `partial-then-fail` fake-bun behavior that creates node_modules then exits non-zero (mirrors real bun under mid-fetch failure). Asserts the failure diagnostic surfaces AND node_modules is gone after, proving the retry path is unblocked. RED -> GREEN proof: - /tmp/issue-2650-proof/RED-review-fixes.log (without fix: 1 pass / 2 fail — success diagnostic missing AND partial node_modules NOT cleaned up) - /tmp/issue-2650-proof/GREEN-review-fixes.log (with fix: 3 pass / 0 fail) Full-suite baseline preserved: - /tmp/issue-2650-proof/full-suite-GREEN.log (1806 pass / 19 skip / 55 fail — same 55 pre-existing failures, +1 new test vs prior commit, 0 regressions) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
65f2fd8cdd |
fix: harden startup and schema repair contracts
Reliability patch covering startup path resolution, install marker compatibility, export CLI request contracts, schema repair safety, hard-stop retry-loop handling, and the PR babysit status helper. |
||
|
|
56db06811e |
Add native Codex hooks integration (#2319)
* Add native Codex hooks integration * Address Codex review feedback * Use durable Codex marketplace root * Address Codex file context review feedback * Harden Codex installer review paths * Report Codex legacy cleanup failures * fix: keep MCP manifests in marketplace sync * fix: bundle zod in MCP server * fix: warn on Codex legacy cleanup failure * Fix hook observation readiness timeouts * Address Codex hook review notes * Tighten Codex MCP file context matching * Resolve final Codex review nits * Add Codex marketplace version guidance * Reset worker failure counter on API fallback * Fix Codex cat flag file extraction |
||
|
|
9e2973059a |
UX redesign: installer + provider rename + /learn-codebase + welcome card + SessionStart hint (#2255)
* feat(ux): claude-mem UX improvements with installer enhancements Squashed PR #2156 commits for clean rebase onto main: - feat(installer): add provider selection, model prompt, worker auto-start - refactor: rename *Agent provider classes to *Provider - feat: add /learn-codebase skill and viewer welcome card - feat(worker): inject welcome hint when project has zero observations - fix(pr-2156): address greptile review comments - fix(pr-2156): address coderabbit review comments - fix(pr-2156): persist CLAUDE_MEM_PROVIDER for non-claude in non-TTY mode - fix(pr-2156): file-backed settings reads in installer + env-first SKILL doc Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * build: rebuild plugin artifacts after rebase onto v12.4.7 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(skills): strip claude-mem internals from learn-codebase The learn-codebase skill, install next-step copy, WelcomeCard, and welcome-hint previously walked the primary agent through worker endpoints and synthetic observation payloads. The PostToolUse hook already captures every Read/Edit the agent makes — the agent should have no awareness that the memory layer exists. Collapse the skill to one instruction ("read every source file in full") and rephrase touchpoints to describe only what the user observes (Claude reading files), not what happens behind the scenes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(sync): preflight version mismatch + settings-aware port resolution Two related fixes for build-and-sync's worker restart step: 1. Read CLAUDE_MEM_WORKER_PORT from ~/.claude-mem/settings.json the same way the worker does, instead of computing the default port from the uid alone. Previously, users with a custom port saw a misleading "Worker not running" message because the restart POST hit the wrong port and got ECONNREFUSED. 2. Add a preflight check that aborts the sync when the running worker's reported version does not match the version we are about to build. Claude Code's plugin loader pins the worker to a specific cache version per session, so syncing into a newer cache directory has no effect until the user runs `claude plugin update thedotmack/claude-mem` to bump the pin. The preflight surfaces this explicitly with the exact command to run; --force bypasses it for intentional cases. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(learn-codebase): note sed for partial reads of large files Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor: strip comments codebase-wide Removed prose comments from all tracked source. Preserved directives (@ts-ignore, eslint-disable, biome-ignore, prettier-ignore, triple-slash references, webpack magic, shebangs). Deleted two tests that asserted on comment text rather than runtime behavior. Net: 401 files, -14,587 / +389 lines, -10.4% bytes. Verified: typecheck passes, build passes, test count unchanged from baseline (22 pre-existing fails, all unrelated). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(installer): move runtime setup into npx, eliminate hook dead air Smart-install ran 3 times during a fresh install — the worst run was silent, fired by Claude Code's Setup hook after `claude plugin install`, producing ~30s of dead air that looked like the plugin was hung. This change makes `npx claude-mem install` the single place heavy work happens, with a visible spinner. Hooks become runtime-only. - New `src/npx-cli/install/setup-runtime.ts` module: ensureBun, ensureUv, installPluginDependencies, read/writeInstallMarker, isInstallCurrent. Marker schema preserved exactly ({version, bun, uv, installedAt}) so ContextBuilder and BranchManager readers keep working. - `npx claude-mem install`: ungated copy/register/enable for every IDE, inserts a "Setting up runtime" task with honest "first install can take ~30s" spinner. The claude-code shell-out to `claude plugin install` is removed — npx already populated everything Claude reads. - New `npx claude-mem repair` command for post-`claude plugin update` recovery, force-reinstalls runtime. - Setup hook now runs `plugin/scripts/version-check.js` (29ms wall) instead of smart-install. Mismatch prints "run: npx claude-mem repair" on stderr. Always exits 0 (non-blocking, per CLAUDE.md exit-code strategy). - SessionStart loses the smart-install entry; 2 hooks remain (worker start, context fetch). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(installer): delete smart-install sources, retarget tests - Delete scripts/smart-install.js + plugin/scripts/smart-install.js (both are source files kept in sync manually; both must go). - Delete tests/smart-install.test.ts (covered surface is gone). - tests/plugin-scripts-line-endings: drop smart-install.js entry. - tests/infrastructure/plugin-distribution: retarget two assertions at version-check.js (the new Setup hook script). - New tests/setup-runtime.test.ts: 9 tests covering marker read/write, isInstallCurrent semantics. Marker schema invariant verified. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(installer): describe npx-driven setup + version-check Setup hook Sweep public docs and architecture notes to reflect the new flow: npx installer does Bun/uv setup with a visible spinner; Setup hook runs sub-100ms version-check.js; users hit `npx claude-mem repair` after a `claude plugin update`. - docs/architecture-overview.md: hook lifecycle table + npx flow paragraph - docs/public/configuration.mdx: tree + hook config example - docs/public/development.mdx: build output line - docs/public/hooks-architecture.mdx: full rewrite of pre-hook section, timing table, performance table - docs/public/architecture/{overview,hooks,worker-service}.mdx: tree comments, JSON config example, Bun requirement section docs/reports/* untouched (historical incident reports). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(install): mergeSettings writes via USER_SETTINGS_PATH Greptile P1 (#2156): `settingsFilePath()` only resolved `process.env.CLAUDE_MEM_DATA_DIR`, while `getSetting()` reads via `USER_SETTINGS_PATH` which `resolveDataDir()` populates from BOTH the env var AND a `CLAUDE_MEM_DATA_DIR` entry persisted in `~/.claude-mem/settings.json`. Result: a user with the data dir saved in settings.json but not exported in their shell would have provider/model settings silently written to `~/.claude-mem/settings.json` while `getSetting()` read from `/custom/path/settings.json` — read/write split. Drop `settingsFilePath()` and the now-unused `homedir` import; reuse the already-imported `USER_SETTINGS_PATH` constant. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(cli): parse --provider, --model, --no-auto-start install flags Greptile P1 (#2156): InstallOptions has fields `provider`, `model`, `noAutoStart`, but the install case in the npx-cli switch only parsed `--ide`. The other three flags were silently dropped — `npx claude-mem install --provider gemini` was a no-op. Extract a `parseInstallOptions(argv)` helper, share it between the bare `npx claude-mem` and `npx claude-mem install` paths, and validate `--provider` against the allowed set. Update help text accordingly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(install): pipe runtime-setup output, always show IDE multiselect Two issues caught in a docker test of the installer: 1. The bun.sh installer, uv installer, and `bun install` were using stdio: 'inherit', dumping their stdout/stderr through clack's spinner region — visible as raw "downloading uv 0.11.8…" / "Checked 58 installs across 38 packages…" text streaming under the spinner. Switch to stdio: 'pipe' and surface captured stderr only on failure (via a shared describeExecError() helper that includes stdout when stderr is empty). Spinner stays clean on the happy path. 2. promptForIDESelection() silently picked claude-code when no IDEs were detected, never showing the user the multiselect. On a fresh machine with no IDEs present yet (e.g. our docker test container), the user never got to choose. Now: always show the full IDE list when interactive; mark detected ones with [detected] hints and pre-select them; show a warn line if zero are detected explaining they should pick what they plan to use. Non-TTY callers still get the silent claude-code default at the call site (unchanged). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(install): skip marketplace work for claude-code-only, offer to install Claude Code Two related UX fixes from a docker test: **Delay between "Saved Claude model=…" and "Plugin files copied OK"** After dropping the needsManualInstall gate, every install was unconditionally running `copyPluginToMarketplace` (which copied the entire root node_modules tree — thousands of files, dozens of seconds) and `runNpmInstallInMarketplace` (npm install --production) even when only claude-code was selected. Neither is needed for claude-code: that path uses the plugin cache dir + the installed_plugins.json + enabledPlugins flag, all of which we already write. - Drop `node_modules` from `copyPluginToMarketplace`'s allowed-entries list; the dependency-install task populates it on the destination side anyway. - Re-introduce `needsMarketplace = selectedIDEs.some(id => id !== 'claude-code')` scoped *only* to `copyPluginToMarketplace`, `runNpmInstallInMarketplace`, and the pre-install `shutdownWorkerAndWait` (also pointless for claude-code- only flows since we're not overwriting the worker's running cache dir source). All other tasks (cache copy, register, enable, runtime setup) stay unconditional. **Claude Code missing → silent install of an IDE that isn't there** When the user picked claude-code on a machine without it (e.g. a fresh container), the install completed but `claude` was unavailable and the only hint was a generic warn line. Replace with an explicit pre-flight prompt: Claude Code is not installed. Claude-mem works best in Claude Code, but also works with the IDEs below. ? Install Claude Code now? ◆ Yes — install Claude Code (recommended) ◯ No — pick another IDE below ◯ Cancel installation If the user picks "Yes", run `curl -fsSL https://claude.ai/install.sh | bash` (or the PowerShell equivalent on Windows), then re-detect IDEs and proceed with claude-code pre-selected. If the install fails or the user picks "No", the multiselect still appears with claude-code visible (just unmarked [detected]), so they can opt in or pick another IDE. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(install): detect Claude Code via `claude` CLI, not ~/.claude dir The directory `~/.claude` can exist (e.g. mounted in Docker, or created by tooling) without Claude Code actually being installed. Detect the `claude` command in PATH instead so the installer correctly offers to install Claude Code when missing. * docs(learn-codebase): add reviewer note explaining the cost tradeoff The skill intentionally reads every file in full to build a cognitive cache that pays off across the rest of the project. Add a brief note so reviewers (human or bot) understand the tradeoff before flagging the unbounded read as a cost issue. * fix: address Greptile P1 feedback on welcome hint and learn-codebase - SearchRoutes: skip welcome hint when caller passes ?full=true so explicit full-context requests aren't intercepted by the hint. - learn-codebase: replace `sed` instruction with the Read tool's offset/limit parameters, since Bash is gated in Claude Code by default. * feat(install): ASCII-animated logo splash on interactive install Plays a ~1s bloom animation of the claude-mem sunburst logomark when the installer starts in an interactive terminal — geometrically rendered via 12 ray curves around a center disc, in the brand orange. The wordmark and tagline type on alongside the final frame. Auto-skipped on non-TTY, in CI, when NO_COLOR or CLAUDE_MEM_NO_BANNER is set, or when the terminal is too narrow. Inspired by ghostty +boo. * feat(banner): replace rotation frames with angular-sector bloom generator Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(banner): replace rotation frames with angular-sector bloom generator Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(banner): three-act choreography renderer with radial gradient and diff redraw Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(banner): update preview script to support small/medium/hero tier selection Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(docker): add COLORTERM=truecolor to test-installer sandbox Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(install): auto-apply PATH for Claude Code with spinner UX The Claude Code install.sh prints a Setup notes block telling users to manually edit "your shell config file" to add ~/.local/bin to PATH — which left fresh installs unable to launch claude from the command line. After a successful install, detect ~/.local/bin/claude on disk and, if the dir is missing from PATH, append the right export line to .zshrc / .bash_profile / .bashrc / fish config (idempotent, marked with a comment). Also updates process.env.PATH for the current install run. Wraps the curl|bash install in a clack spinner (interactive only) so the ~4 minute native-build download doesn't look frozen — output is captured silently and dumped on failure for debuggability. Non-interactive mode keeps inherited stdio for CI logs. Verified end-to-end in the test-installer docker sandbox: spinner animates, .bashrc gets the export, fresh login shell resolves claude. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(banner): video-frame ASCII renderer with three-act choreography Generator switched from a single Jimp-rendered logo to pre-extracted video frames concatenated with \x01 separators and gzip-deflated, ported from ghostty's boo wire format. Renderer rewritten around three acts (ignite → stagger bloom → text reveal + breathe) with adaptive sizing, radial gradient, and diff-based redraw. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(onboarding): unify install / SessionStart / viewer around one first-success moment Three surfaces now point at the same north-star moment — open the viewer, do anything in Claude Code, watch an observation appear within seconds — with the same verbatim timing and privacy lines, and a single canonical "how it works" explainer instead of three diverging copies. - Canonical explainer at src/services/worker/onboarding-explainer.md served via GET /api/onboarding/explainer; mirrored into plugin/skills/how-it-works/SKILL.md - SessionStart welcome hint rewritten as third-person status (no imperatives Claude tries to execute), pinned with a default-value regression test - Post-install Next Steps reframed as "two paths": passive default + optional /learn-codebase front-load; drops /mem-search and /knowledge-agent from this surface; adds verbatim timing + privacy lines and /how-it-works link - /api/stats response gains firstObservationAt for the viewer stat row - Viewer WelcomeCard branches on observationCount === 0: empty state shows live worker-connection dot + "waiting for activity"; has-data state shows observations · projects · since [date] and two example prompts. v2 dismiss key - jimp added to package.json to fix pre-existing banner-frame build break Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(banner): play unconditionally; only honor CLAUDE_MEM_NO_BANNER The 128-col / TTY / CI / NO_COLOR gates silently swallowed the banner in narrower terminals, CI logs, and any non-TTY pipe — including Docker runs where -it should preserve the experience but column width was the wrong gate. Remove the implicit gates; keep the explicit opt-out only. If a frame wraps in a narrow terminal, that's better than the banner not playing at all. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * revert(banner): restore 15:33 gating logic per user request Reverts |