Add scripts/install.sh: reproducibly generate the /faber command with a repo-derived path - #12
Conversation
Adds an idempotent installer that generates ~/.claude/commands/faber.md from a
new version-controlled template, with the control-plane repo path derived from
the script's own location instead of a hardcoded ~/git/fabrica. This makes the
hand-made /faber command reproducible (reusable-by-anyone) and restorable
(full-backup).
- scripts/install.sh: resolves repo root via the script's real path (symlink-
aware), renders templates/faber-command.md by substituting {{FABRICA_ROOT}},
creates ~/.claude/commands/ if missing, backs up a differing faber.md to
.bak before overwriting, no-ops when identical, and prints next steps in the
setup-target-repo.sh style.
- templates/faber-command.md: the command body, version-controlled, with a
{{FABRICA_ROOT}} placeholder (faithful to the current command; only paths
parameterized).
- RESTORE.md: section 1 now recreates /faber by running scripts/install.sh
instead of by hand.
- README.md: Layout lists install.sh and the new template.
Closes #10
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Codex reviewer (cross-vendor, read-only)Posted verbatim by The installer can generate invalid output or fail for valid clone paths containing sed metacharacters, which undermines the repo-derived path behavior this patch adds. Review comment:
|
Codex [P2]: the sed-based placeholder substitution broke for valid clone
paths containing sed metacharacters — '&' in the replacement re-expanded to
the matched text and the '#' delimiter terminated the expression, so a path
like /Users/me/R&D#fabrica produced unusable output or failed the install.
Replace the sed call with bash literal string replacement
(${var//pattern/replacement}), which treats the replacement literally and
correctly handles &, #, /, and spaces. Placeholder name {{FABRICA_ROOT}} and
all existing behavior (symlink-aware path, idempotency, .bak backup,
next-steps output) are unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Round 1 — addressed Codex [P2]Finding: placeholder substitution used Fix: dropped template_contents="$(cat "$template")"
rendered="${template_contents//'{{FABRICA_ROOT}}'/$repo_root}"Bash Behavior preserved: symlink-aware repo path, template sourced from Verification (sandboxed HOME, real
Commit: |
Codex reviewer (cross-vendor, read-only)Posted verbatim by The new installer/template works for simple paths but emits broken shell commands when the derived repo path contains whitespace, despite explicitly aiming to support arbitrary clone locations. Review comment:
|
The generated /faber command embedded the {{FABRICA_ROOT}} path unquoted in
runnable shell snippets, so clones living under a path with spaces (e.g.
/Users/me/My Projects/fabrica) would word-split and break the command — which
defeats the installer's support for clones living anywhere.
Double-quote the substituted path in every runnable command snippet:
- templates/faber-command.md: the Codex reviewer invocation and the
setup-target-repo.sh setup command.
- scripts/install.sh: the printed next-steps setup-target-repo.sh command.
Behavior is otherwise unchanged: path still derived from script location,
template-sourced, idempotent, .bak on differ, no leftover placeholder.
Verified under a sandboxed HOME by installing from a clone path containing a
space; the generated faber.md has quoted runnable command lines with the path
intact and no leftover placeholder. shellcheck clean; CI structure check passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Round 2 — addressed Codex [P2] (quote generated command paths)Finding: the generated Fix: double-quoted the substituted path in every runnable command snippet:
Plain prose references to files that are read (e.g. the Behavior preserved: path still derived from script location (symlink-aware), template-sourced, idempotent, Verification (sandboxed HOME, real
Commit: |
Codex reviewer (cross-vendor, read-only)Posted verbatim by The generated Review comment:
|
Faber — escalating to human (round-3 cap)CI green every round. Reached the ~3-round cap, so flagging Round history:
Note on the round-2 finding: this is a pre-existing content bug inherited verbatim from the current hand-made Recommendation (your call at the gate):
Substantive #10 goal is met: |
…ance The generated /faber command's setup guidance invoked setup-target-repo.sh with no argument, but that script requires <owner>/<repo> (usage: $0 <owner>/<repo>) and exits non-zero when it is omitted — so the label-bootstrap instruction could not complete. Append <owner>/<repo> to the runnable command in templates/faber-command.md (keeping the existing double-quoting around the substituted path) so the user runs it for the target repo. Text- only correctness fix; install.sh logic and its already-correct printed next-step are unchanged. Addresses Codex [P2] on PR #12 (issue #10). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Round 3 — addressed Codex [P2] (pass
|
Codex reviewer (cross-vendor, read-only)Posted verbatim by The added installer, template, and documentation changes are internally consistent and do not introduce any clearly actionable correctness issues. |
Faber — confirming round clean ✅Applied the Removing |
Closes #10
What changed
scripts/install.sh(new) — idempotent installer that generates~/.claude/commands/faber.md. It resolves the control-plane repo root from the script's own location (symlink-aware: followsreadlinkthencd … && pwd -P), renders the new template by substituting the resolved absolute path for{{FABRICA_ROOT}}, creates~/.claude/commands/if missing, and prints next steps in the same style assetup-target-repo.sh.templates/faber-command.md(new) — the/fabercommand body, now version-controlled, with a{{FABRICA_ROOT}}placeholder. It is faithful to the current live command; only the hardcoded~/git/fabricapaths were replaced with the placeholder (verified byte-identical when substituted back).RESTORE.md— section 1 ("Recreate Faber") now recreates the/fabercommand by runningscripts/install.shinstead of by hand. (The runbook previously didn't cover the slash command at all — the exact gap the issue flags.)README.md— Layout block listsscripts/install.shandtemplates/faber-command.md, one line each.Why
The
/fabercommand was made by hand and hardcoded~/git/fabricain ~6 places — not reproducible (reusable-by-anyone) and not restorable (full-backup). Deriving the path from the clone's own location fixes both.Idempotency / safety
faber.mdthat differs is backed up tofaber.md.bakbefore overwriting — never clobbered silently.~/git/fabricaliteral and no{{FABRICA_ROOT}}placeholder in the generated output.How tested (sandboxed HOME — the real
~/.claude/commands/faber.mdwas never touched)Evidence:
Created: $tmphome/.claude/commands/faber.md, repo path derived to/Users/<me>/git/fabrica-i10.Already up to date— idempotent, no.bakwritten when content is identical.{{FABRICA_ROOT}}placeholder · ✅ no~/git/fabricaliteral · ✅ resolved repo path present.faber.md: ✅ old content backed up tofaber.md.bak, new file carries the resolved path.~/.claude/commands/faber.mdwas not modified and no real.bakwas created. Temp dirs cleaned up.CI
shellcheckclean acrossinstall.sh,setup-target-repo.sh,codex-review.sh.🤖 Generated with Claude Code