~/.claude/. Three concerns are kept separate: layout (what files
exist in the repo), install (how they reach ~/.claude/), and
runtime (what binaries Claude Code can call).
Layout
Install model
scripts/install.bash is a symlink installer. For each tracked top-level
file or directory, it:
- Refuses to run as root.
- Verifies the repo looks like a jstack checkout (
settings.jsonandskills/must exist). - Symlinks
settings.jsonandCLAUDE.mdinto~/.claude/. - Symlinks
skills/,agents/,commands/,hooks/, andplugins/into~/.claude/. - Builds the runtime via
nix-build runtime/default.nixand links the result under~/.local/state/jstack/runtime. - Updates the shell
rcso~/.local/state/jstack/runtime/binis onPATH.
~/.claude/.jstack-backups/<timestamp>/. A RESTORE.md is
dropped alongside.
The installer is idempotent: re-running on a clean tree reports zero
actions.
Runtime model
runtime/default.nix is a pkgs.buildEnv derivation. Whatever binaries
it includes are exposed to Claude Code via PATH. The current build
includes:
pyright— Python LSPnil— Nix LSPtypescript-language-server— TS/JS LSP
paths list. After
editing runtime/default.nix, re-run install.bash to rebuild and
re-link.
Plugins
Each plugin lives atplugins/<name>/ and is self-contained. The
expected layout per plugin:
install.bash symlinks the entire plugins/ tree as a single unit, so
new plugins are picked up without touching the installer.
Marketplace UI conflict
After install,~/.claude/plugins/ is a symlink into this repo. If you
install a plugin via Claude Code’s marketplace UI (/plugin install foo),
it writes a new directory under plugins/ and shows up in git status.
Either commit the new plugin to the repo or revert it.
Why no submodules
An earlier iteration of jstack vendored upstream plugin sources via git submodules undervendor/, with symlinks into plugins/jstack-vendored/.
That layer was removed: every plugin currently shipped is owned content
that lives directly under plugins/<name>/. There is no vendor/ and no
bump-vendor.bash.