The install command only touches one folder

A git clone into ~/.claude/skills/TBaguette sits right next to whatever else already lives under ~/.claude/skills/ — other skills, other plugins, things you already trust. It's a fair question whether installing this one could touch any of that. It can't. This page is why, and the actual test that proves it, not a claim restated in different words.

What the command does

The published command checks for an existing clone before doing anything: if ~/.claude/skills/TBaguette/.git already exists, it runs git pull in place; otherwise it runs git clone. The first run installs. Every run after that updates. Neither branch ever reads or writes anywhere else.

Why it can't reach anything else

Both branches are scoped to the single path ~/.claude/skills/TBaguette by construction — a git clone or git pull targeting that path has no mechanism to write outside it. The one case worth naming explicitly is a real collision: something else already sitting at that exact path, that isn't a clone of this repo. git clone refuses outright when its target already exists and is non-empty — it does not merge, does not overwrite, does not ask. It fails loudly and leaves whatever was there exactly as it was. That refusal is git's own behavior, not something this project added on top.

Four scenarios, not one

A single happy-path test would only prove the command works when nothing is in its way, which is the one case nobody actually worried about. The real test runs four scenarios, each against a throwaway HOME directory seeded with fake sibling skills and plugins alongside TBaguette, checksummed before and after:

  • A — fresh install. Nothing at that path yet. Confirms the clone succeeds and the siblings are untouched.
  • B — running it again. Continues from A. Confirms the second run updates in place (git pull) instead of erroring the way a bare git clone would, and that the siblings are still untouched.
  • C — an empty directory already named TBaguette. Confirms git clone is willing to use an empty directory that happens to already exist, and that siblings survive.
  • D — the real collision. A non-empty, non-git directory already sitting at that exact path, with its own unrelated content. Confirms the command refuses rather than merging into it — and that both the colliding directory's own content and every sibling skill survive the refusal.

All twelve checks across those four scenarios pass before this site is ever deployed — plus twelve more that run the exact command string published above, word for word, through every POSIX-ish shell this project's own build machine has installed (bash, zsh, fish, and sh), rather than trusting that this page's Python reimplementation of it stayed faithful to what actually ships. run_tests.py runs all twenty-four alongside everything else, not as a separate manual step someone has to remember.

Every platform, not just one

The install frame above shows a prompt addressed to Claude, not a command for you to type — paste it into a Claude Code conversation and Claude detects your OS and runs the matching command itself. That command, on every platform, is exactly what follows: a POSIX command for macOS, Linux, and anyone on Windows already inside WSL or Git Bash, and a PowerShell command for everyone else on Windows — which is most Windows users, since PowerShell has been the default terminal since Windows 10. All three do the exact same thing, in the same order, for the same reason: check for an existing clone, git pull if it's there, git clone if it isn't.

The POSIX command is the one machine-verified above, across four shells:

[ -d ~/.claude/skills/TBaguette/.git ] && git -C ~/.claude/skills/TBaguette pull || git clone https://github.com/LeSplooch/tbaguette-skills.git ~/.claude/skills/TBaguette

The PowerShell command carries the exact same logic over to Test-Path and native cmdlets:

if (Test-Path "$HOME\.claude\skills\TBaguette\.git" -PathType Container) { git -C "$HOME\.claude\skills\TBaguette" pull } else { git clone https://github.com/LeSplooch/tbaguette-skills.git "$HOME\.claude\skills\TBaguette" }

That one isn't cross-checked by an automated test the way the POSIX command is — this project's own build has no PowerShell runtime to run it against — so its correctness rests on Test-Path, git -C, and git clone being ordinary, well-documented behavior, not on an executed proof. If that's not good enough, that's a fair position to hold; the honest answer is "verified by careful construction," not "verified," for this one specifically.

And for Command Prompt, which nothing in the install frame targets but which still exists on every Windows machine:

if exist "%USERPROFILE%\.claude\skills\TBaguette\.git" (git -C "%USERPROFILE%\.claude\skills\TBaguette" pull) else (git clone https://github.com/LeSplooch/tbaguette-skills.git "%USERPROFILE%\.claude\skills\TBaguette")

Same logic again, translated to IF EXIST and %USERPROFILE% — with one honest imprecision. IF EXIST can't cleanly test "is this specifically a directory" in one line the way -d and -PathType Container can, so it matches a file or a directory at that path indiscriminately. That's never actually wrong here, since a real git checkout's .git is always a directory, but it's a looser guarantee than the other two, worth naming rather than glossing over.

The test itself

This is scripts/test_install_command.py, unedited — the same file the test suite actually runs, not a representative excerpt.

  1. """Proves the published install command (templates.INSTALL_COMMAND) can never
  2. alter, overwrite, or merge into anything a user already has under
  3. ~/.claude/skills/ that this repo doesn't own — a user asked "won't this alter
  4. other skills?" and this is the actual answer, re-checked on every run rather
  5. than trusted from memory.
  6. Stdlib only, and deliberately not a shell script (this file replaces what was
  7. originally test_install_command.sh): bash is not guaranteed to exist on every
  8. system this might run on, notably Windows without Git Bash or WSL, and even
  9. where a POSIX shell is present, the coreutils it calls out to differ across
  10. platforms in ways that silently break things — the original script's
  11. sha256sum has no equivalent by that name on macOS (it ships shasum -a 256
  12. instead). Python 3 is a much narrower, more realistic requirement here, since
  13. it's already needed to run scripts/generate.py, and git is already required
  14. to install the plugin in the first place — nothing new to ask a reader to
  15. have.
  16. Four scenarios, each in its own throwaway HOME so a real ~/.claude is never
  17. touched:
  18. A. fresh install — clones cleanly
  19. B. re-running once already installed — updates in place (git pull),
  20. does not error
  21. C. an empty dir already named TBaguette — clones into it cleanly
  22. D. a NON-empty, non-git dir already named
  23. TBaguette (the real collision case) — refuses, leaves it untouched
  24. Sibling directories (other skills, other plugins) are populated in every
  25. scenario and checksummed before/after to prove they are never touched, not
  26. just assumed to be.
  27. The install() function below reimplements the published command's logic as
  28. direct git subprocess calls rather than shelling out to run the literal
  29. string — the same two git operations, in the same order, gated by the same
  30. condition, but expressed so this file's core guarantee holds even on a
  31. system with no POSIX shell at all. Where a shell *is* available,
  32. check_matches_published_command() additionally runs the literal string from
  33. templates.INSTALL_COMMAND itself, so the claim "the published command is
  34. safe" stays backed by the actual text shown on the site, not only by code
  35. that is merely intended to match it.
  36. Usage:
  37. python3 scripts/test_install_command.py
  38. """
  39. from __future__ import annotations
  40. import hashlib
  41. import os
  42. import shutil
  43. import subprocess
  44. import sys
  45. import tempfile
  46. from pathlib import Path
  47. from templates import INSTALL_COMMAND
  48. REPO_URL = "https://github.com/LeSplooch/tbaguette-skills.git"
  49. class Tally:
  50. """Accumulates check() results instead of raising on the first failure —
  51. deliberately different from checker.Checker's fail-fast style (used by
  52. this project's other plain assert-based test files). This file's whole
  53. point is comparing results across four independent scenarios, and across
  54. several shells in the bonus check; stopping at the first failure would
  55. hide whether everything else still held, which is exactly the question
  56. a report of "3 of 24 failed, here's which three" answers and a bare
  57. traceback from the first one doesn't."""
  58. def __init__(self) -> None:
  59. self.total = 0
  60. self.failures = 0
  61. def check(self, label: str, condition: bool) -> None:
  62. self.total += 1
  63. if condition:
  64. print(f" ok {label}")
  65. else:
  66. print(f" FAIL {label}")
  67. self.failures += 1
  68. tally = Tally()
  69. check = tally.check
  70. def install(home: Path) -> subprocess.CompletedProcess:
  71. """The published install command's logic, as direct git calls instead of
  72. a shell string: if ~/.claude/skills/TBaguette/.git already exists, pull
  73. in place; otherwise clone. Scoped to that single path by construction —
  74. neither branch has any mechanism to read or write anywhere else."""
  75. target = home / ".claude" / "skills" / "TBaguette"
  76. if (target / ".git").is_dir():
  77. return subprocess.run(
  78. ["git", "-C", str(target), "pull"],
  79. capture_output=True, text=True, check=False,
  80. )
  81. return subprocess.run(
  82. ["git", "clone", REPO_URL, str(target)],
  83. capture_output=True, text=True, check=False,
  84. )
  85. def seed_sibling_skills(home: Path) -> None:
  86. """Fake pre-existing content the install must never touch: a loose
  87. skill directory and a separate plugin, exactly the two shapes
  88. ~/.claude/skills/ actually holds (see orienting-in-unfamiliar-code and
  89. the TBaguette plugin's own layout)."""
  90. other_skill = home / ".claude" / "skills" / "some-other-skill"
  91. other_skill.mkdir(parents=True, exist_ok=True)
  92. (other_skill / "SKILL.md").write_text(
  93. "precious user content that must survive\n", encoding="utf-8"
  94. )
  95. plugin = home / ".claude" / "skills" / "another-plugin"
  96. (plugin / ".claude-plugin").mkdir(parents=True, exist_ok=True)
  97. (plugin / ".claude-plugin" / "plugin.json").write_text(
  98. '{"name": "another-plugin"}\n', encoding="utf-8"
  99. )
  100. (plugin / "skills" / "foo").mkdir(parents=True, exist_ok=True)
  101. (plugin / "skills" / "foo" / "SKILL.md").write_text(
  102. "more precious user content\n", encoding="utf-8"
  103. )
  104. def siblings_checksum(home: Path) -> list[tuple[str, str]]:
  105. """(path relative to home, sha256) for every file under the two sibling
  106. directories, sorted — needs no external tool at all, unlike the original
  107. script's `find ... -exec sha256sum {} \\; | sort`, so there is nothing
  108. here that can be missing or spelled differently across platforms."""
  109. roots = (
  110. home / ".claude" / "skills" / "some-other-skill",
  111. home / ".claude" / "skills" / "another-plugin",
  112. )
  113. entries = [
  114. (str(path.relative_to(home)), hashlib.sha256(path.read_bytes()).hexdigest())
  115. for root in roots
  116. for path in root.rglob("*")
  117. if path.is_file()
  118. ]
  119. return sorted(entries)
  120. # Every shell worth naming explicitly rather than just "a POSIX shell": bash
  121. # and zsh cover the macOS/Linux default in every version this century, sh is
  122. # whatever /bin/sh resolves to locally (often bash or dash), and fish is
  123. # included deliberately even though it isn't POSIX-compatible in general —
  124. # fish 3.0+ (2018) added && / || specifically so commands copy-pasted from
  125. # bash/zsh docs would still work, which is exactly this page's use case, and
  126. # it's worth proving rather than assuming.
  127. CANDIDATE_SHELLS = ("bash", "zsh", "fish", "sh")
  128. def check_matches_published_command(workdir: Path) -> None:
  129. """Best-effort bonus: for every shell in CANDIDATE_SHELLS actually
  130. present on PATH, runs the literal published command string — not this
  131. file's reimplementation of it — against its own fresh scenario. A shell
  132. missing from PATH is skipped with a note, not a failure; on a bare
  133. Windows system with neither Git Bash nor WSL, every candidate is
  134. skipped, and that's fine — install() above already proves the same
  135. guarantee via git directly, with no shell dependency at all. This
  136. function exists to additionally cross-check the exact string published
  137. on the site, on whichever real shells happen to be available here."""
  138. tested_any = False
  139. for shell_name in CANDIDATE_SHELLS:
  140. shell = shutil.which(shell_name)
  141. if shell is None:
  142. print(f" (skipped {shell_name}: not on PATH)")
  143. continue
  144. tested_any = True
  145. home = workdir / f"shellcheck-{shell_name}"
  146. home.mkdir()
  147. seed_sibling_skills(home)
  148. before = siblings_checksum(home)
  149. env = {**os.environ, "HOME": str(home)}
  150. result = subprocess.run(
  151. [shell, "-c", INSTALL_COMMAND], env=env, capture_output=True, text=True, check=False,
  152. )
  153. check(f"the literal published command exits 0 under {shell_name}",
  154. result.returncode == 0)
  155. check(f"...and actually installs something, under {shell_name}",
  156. (home / ".claude" / "skills" / "TBaguette" / "README.md").is_file())
  157. check(f"...without touching the sibling skills under {shell_name}, using "
  158. "the exact string published on the site, not a reimplementation of it",
  159. before == siblings_checksum(home))
  160. if not tested_any:
  161. print(" (skipped entirely: no POSIX-ish shell on PATH — install() above "
  162. "already verified the same guarantee via git directly, without one)")
  163. def main() -> int:
  164. workdir = Path(tempfile.mkdtemp(prefix="tbaguette-install-test-"))
  165. try:
  166. print("scenario A: fresh install")
  167. home_a = workdir / "a"
  168. home_a.mkdir()
  169. seed_sibling_skills(home_a)
  170. before_a = siblings_checksum(home_a)
  171. result = install(home_a)
  172. check("clone exits 0", result.returncode == 0)
  173. check("TBaguette content actually present",
  174. (home_a / ".claude" / "skills" / "TBaguette" / "README.md").is_file())
  175. check("sibling skills byte-identical after install",
  176. before_a == siblings_checksum(home_a))
  177. print("scenario B: re-run when already installed (must update, not error)")
  178. home_b = home_a # continues from A, which already installed TBaguette
  179. before_b = siblings_checksum(home_b)
  180. result = install(home_b)
  181. combined_output = (result.stdout or "") + (result.stderr or "")
  182. check("re-run exits 0 (does not error)", result.returncode == 0)
  183. check(
  184. "re-run reports a pull, not a clone-refused error",
  185. any(marker in combined_output for marker in
  186. ("Already up to date", "Updating", "Fast-forward")),
  187. )
  188. check("sibling skills still byte-identical after re-run",
  189. before_b == siblings_checksum(home_b))
  190. print("scenario C: an empty pre-existing TBaguette directory")
  191. home_c = workdir / "c"
  192. (home_c / ".claude" / "skills" / "TBaguette").mkdir(parents=True)
  193. seed_sibling_skills(home_c)
  194. before_c = siblings_checksum(home_c)
  195. result = install(home_c)
  196. check("clone into empty existing dir exits 0", result.returncode == 0)
  197. check("TBaguette content actually present",
  198. (home_c / ".claude" / "skills" / "TBaguette" / "README.md").is_file())
  199. check("sibling skills byte-identical", before_c == siblings_checksum(home_c))
  200. print("scenario D: a non-empty, non-git TBaguette directory (real collision)")
  201. home_d = workdir / "d"
  202. target_d = home_d / ".claude" / "skills" / "TBaguette"
  203. target_d.mkdir(parents=True)
  204. marker_file = target_d / "dont-touch-me.txt"
  205. marker_file.write_text(
  206. "unrelated content some other tool put here\n", encoding="utf-8"
  207. )
  208. seed_sibling_skills(home_d)
  209. before_marker = hashlib.sha256(marker_file.read_bytes()).hexdigest()
  210. before_d = siblings_checksum(home_d)
  211. result = install(home_d)
  212. check("refuses (nonzero exit) rather than merging into unrelated content",
  213. result.returncode != 0)
  214. check("the colliding directory's own content is untouched",
  215. hashlib.sha256(marker_file.read_bytes()).hexdigest() == before_marker)
  216. check("sibling skills byte-identical even in the refusal case",
  217. before_d == siblings_checksum(home_d))
  218. print("bonus: the literal published command string, where a shell exists")
  219. check_matches_published_command(workdir)
  220. finally:
  221. shutil.rmtree(workdir, ignore_errors=True)
  222. print()
  223. if tally.failures == 0:
  224. print(f"{tally.total} checks passed.")
  225. return 0
  226. print(f"{tally.failures} of {tally.total} checks FAILED.")
  227. return 1
  228. if __name__ == "__main__":
  229. sys.exit(main())

Run it yourself

Clone the repo and run python3 scripts/test_install_command.py directly, or python3 scripts/run_tests.py for the full suite this page's claims are checked against. Standard-library Python only — no bash required, since it isn't guaranteed to exist on every system this might run on.