A manifest declares repo path -> target, and the script walks it. Symlinks, not copies, so an installed file edits the repo and one commit reaches every other machine. Idempotent — a correct link is a no-op, safe after every pull. A real file where a link should go is moved to .bak-<timestamp> and reported, because on a new machine that may be the only copy of something written by hand. Conditional targets are what make it portable: officer.dev/CLAUDE.md and the officer-chat-events hook both name ~/officer-dev/platform, so a laptop with no Officer install gets neither instead of getting a document that lies. mask-secret is a command now rather than a path only true on this machine, and user/CLAUDE.md says so. It is a generated wrapper, not a symlink, and that is forced: mask-secret.py resolves its own path to find mask-hooks.d, so a symlink would reach back into the repo and run every machine's hooks. The wrapper sets MASK_HOOKS_DIR to the ones this machine installed and still runs the script from the repo, so no code is copied. Verified against a scratch HOME: re-run is all ok, the hand-written file survives as .bak, and a machine without Officer skips both conditional lines.
17 lines
814 B
Plaintext
17 lines
814 B
Plaintext
# What goes where. One line per file; install.sh walks this and nothing else.
|
|
#
|
|
# kind source (in this repo) target condition
|
|
#
|
|
# kind link — symlink target -> source. The file lives in git; the edit lands in git.
|
|
# hook — a mask-secret hook. Target is derived: $CLAUDE_SCRIPTS/mask-hooks.d/<name>.
|
|
#
|
|
# condition — optional. A path that must exist on this machine, or the line is skipped.
|
|
# This is what keeps officer.dev's workspace file off a laptop that has no Officer.
|
|
#
|
|
# ~ means $HOME. Columns are whitespace-separated, so no spaces in paths.
|
|
|
|
link user/CLAUDE.md ~/.claude/CLAUDE.md
|
|
link officer.dev/CLAUDE.md ~/officer-dev/CLAUDE.md ~/officer-dev/platform
|
|
|
|
hook tools/mask-hooks.d/officer-chat-events ~/officer-dev/platform
|