Purpose
Verify and manage the audit's optional Git-format configuration. Setup owns only this file; it never
edits Claude Code settings, pluginConfigs, Git remotes, branches, worktrees, or the installed plugin.
The config file itself is optional to create, but a no-argument /repo-fleet-hygiene:audit
requires scope from somewhere: CLI bare path / --root / --repo, or fleet.root / fleet.repo
entries in a consumed config. Absence of every config on the ladder is therefore INFO for check
(nothing to validate yet) and a hard failure for a subsequent no-argument audit, not a silent
default to the current project. Check-centric per the uniform setup contract
(docs/PLUGIN-PHILOSOPHY.md "Setup is explicit and repeatable" in the marketplace repository):
check inspects read-only; apply creates or updates the file, then re-runs check. No argument
or check runs the check; apply runs the check first, then the write. All non-interactive: when
the arguments fully specify the change, apply proceeds without prompting.
Default config path: ${CLAUDE_PROJECT_DIR}/.claude/repo-fleet-hygiene.conf. An explicit --config
may choose another path. Resolve relative roots/repos/canonical paths from the config file directory.
Scoping rule (state it in check/apply output): the audit consumes config through a ladder:
explicit --config, else the project-scoped default above, else the user-global
~/.claude/repo-fleet-hygiene.conf. A project-scoped config is therefore consumed only when the
audit runs with that same project directory; a fleet config meant to apply from every project
belongs at the user-global path (apply --config ~/.claude/repo-fleet-hygiene.conf). The audit
report header names which config (if any) was consumed.
Argument grammar
check | apply [--config <path>] [--root <dir>]... [--repo <dir>]...
[--canonical <github.com/owner/repo=path>]... [--ack-unavailable <github.com/owner/repo>]...
[--skip <name>]... [--max-depth <1..12>]
--max-depth writes [fleet] maxDepth; --skip writes repeatable [fleet] skip entries. This
skill owns the config file that carries both. See "Configuration grammar" below for the skip
replace semantics, and state them whenever you write a skip entry, because "extend" is the
naive reading.
check (read-only)
The config file and the grammar below are the source of truth. Probe, report a PASS/FAIL/INFO table
with one remediation line per FAIL, and modify nothing. Do NOT run the collector. That is
/repo-fleet-hygiene:audit; check only validates the configuration the audit would consume, using
git config --file and read-only filesystem probes.
- Config presence. Resolve the config path (
--config or the default). Absent → INFO naming
the full ladder: the audit next probes the user-global ~/.claude/repo-fleet-hygiene.conf (report
whether one exists there); with no config on the ladder and no scope argument, a bare
/repo-fleet-hygiene:audit fails rather than auditing the current project. apply scaffolds a
config only if the user wants bounded roots or overrides.
- Parse validity. Present config:
git config --file "<path>" --list >/dev/null. A non-zero exit
is FAIL with the parse error in the remediation line. Never source the file.
- Entry resolution. For each
[fleet] root/repo and each [canonical …] path, resolve it from
the config directory and confirm the directory exists and (for roots/repos) git rev-parse succeeds
read-only. A referenced path that does not resolve is FAIL, naming the entry.
maxDepth. Present and outside 1..12 is FAIL; absent is INFO (the audit's own default applies).
- Canonical identity. INFO for each
[canonical "github.com/owner/repository"] entry: report the
normalized key. Flag as FAIL only a key that is not a normalizable github.com/owner/repository.
- Acknowledged identities. INFO listing each
fleet.ackUnavailable entry (normalized). FAIL any
value that is not a normalizable github.com/owner/repository.
- Discovery skip names. INFO listing each
fleet.skip entry. FAIL any value that is empty or
contains a path separator (must be a bare directory name). Remind that any present fleet.skip
replaces the audit default skip list rather than appending.
- Tracked-file pair (only when the config lives inside a git worktree, e.g. a project's
tracked
.claude/repo-fleet-hygiene.conf): resolve the worktree that owns the file
(git -C "$(dirname -- "<path>")" rev-parse --show-toplevel) and run both probes there
with a path relative to that toplevel — git -C "<toplevel>" check-ignore -v -- "<rel>"
reports no match (a match is FAIL with the pattern — teammates would never receive the
config) AND git -C "<toplevel>" ls-files --error-unmatch -- "<rel>" exits 0 (non-zero is
un-ignored but untracked; FAIL with "commit it to share with the team"). An explicit
--config in another checkout is still that other worktree's file; do not run the pair
against the current project's index. Skip both, saying so, when no owning worktree exists
(user-global ~/.claude/… or any path outside a repository) — a git verdict there is
meaningless.
apply (idempotent)
Run check, then create or update the config from the supplied arguments.
Parse only the declared argument grammar. Validate every root/repository/canonical path with
read-only filesystem and git rev-parse checks. Normalize canonical keys to
github.com/owner/repository (lowercase host, case-preserving owner/name is acceptable).
Validate --max-depth as an integer in 1..12 and write it as [fleet] maxDepth; this skill
owns the file that carries it, so it must be settable here rather than by hand-editing.
Validate each --ack-unavailable value as a normalizable github.com/owner/repository
(no filesystem probe, the identity is expected to be inaccessible); write it as a repeatable
[fleet] ackUnavailable entry, deduplicating case-insensitively against entries already
present. Like roots/repos, apply is additive. Removing an acknowledgment is a manual edit of
the consumer's own config file.
Validate each --skip value as a bare directory name (reject empty and any path separator);
write it as a repeatable [fleet] skip entry, deduplicating exact matches against entries
already present. State the replace semantics from "Configuration grammar" when writing.
Removing a skip entry is a manual edit.
If the config exists, read it with git config --file <path> --list --show-origin. Preserve every
unrelated entry. Never source it.
State the proposed additions/updates before writing. With complete arguments, proceed
non-interactively; otherwise ask only for the missing values. An empty invocation may create the
minimal current-project config:
[fleet]
repo = ..
maxDepth = 5
(.. is relative to .claude/ and therefore names ${CLAUDE_PROJECT_DIR}.)
Write/update with an ordinary file edit, not git config --file ... --add: the file may be tracked
and the user must see a deterministic diff. Preserve comments and unrelated sections. Prefer a
path relative to the config file's directory for any root/repository/canonical target expressible
that way. The grammar resolves relative paths from that directory and both forms audit
identically, while some consumer environments run a write-time path-portability guard that rejects
absolute paths in tracked config. "Expressible that way" is the real constraint: on Windows, no
relative path exists between two volumes, so a fleet root on D: with a config on C: has only
the absolute form. Write the absolute path, and say in the report that the relative form was
unavailable because the target is on another volume. Otherwise the guard's rejection reads as a
consumer mistake. This preference is prose guidance followed by the model, not a property a
deterministic component enforces; treat it as a default to justify departing from, not a
guarantee.
Verify after remediation. Re-run every check probe against the written file (never claim success on
the edit alone). Config-only, exactly as check defines them:
- Parse validity.
git config --file "<config-path>" --list >/dev/null
- Entry resolution. For each
[fleet] root/repo and each [canonical …] path, resolve from
the config directory and confirm the directory exists and (for roots/repos) git rev-parse succeeds
read-only
maxDepth. When present, confirm it is an integer in 1..12
- Canonical identity and acknowledged identities. Confirm each key/value normalizes to
github.com/owner/repository
- Discovery skip names. When present, confirm each
fleet.skip value is a bare directory
name (non-empty, no path separator)
- Tracked-file pair (when the config lives inside a git worktree, e.g. the tracked
.claude/repo-fleet-hygiene.conf): resolve the owning worktree
(git -C "$(dirname -- "<config-path>")" rev-parse --show-toplevel) and run both probes
there with a toplevel-relative path — git -C "<toplevel>" check-ignore -v -- "<rel>"
reports no match (a match is FAIL with the pattern — teammates would never receive the
config) AND git -C "<toplevel>" ls-files --error-unmatch -- "<rel>" exits 0 (non-zero
means un-ignored but still untracked, the guaranteed state right after a fresh write;
report "written but untracked: commit it to share with the team", never success). An
explicit --config in another checkout is still that other worktree's file. Skip both,
saying so, when no owning worktree exists — a git verdict there is meaningless.
Do not invoke the collector to verify a write. It is the full fleet walk this skill says it
never runs: per-repository network queries across every configured root, minutes on a real fleet,
and it proves nothing about the file that the check probes do not already prove. If the user
explicitly wants an end-to-end run, say that it is a real audit and hand off by invoking
/repo-fleet-hygiene:audit via the Skill tool.
Report path, inferred/explicit entries, preserved entries, and the config-verification result.
Re-running apply with the same arguments after everything resolves changes nothing and reports
"already configured".
Configuration grammar
[fleet]
root = ../../repos/github.com # repeatable discovery root
repo = ../../special/repo # repeatable exact target
maxDepth = 5 # integer 1..12
ackUnavailable = github.com/owner/repository # repeatable; acknowledge a known-inaccessible identity
skip = vendor # repeatable; REPLACE default discovery skip list (not append)
[canonical "github.com/owner/repository"]
path = ../../../canonical-checkout
ackUnavailable demotes a 404/403 github-identity-unavailable finding for that identity from
UNKNOWN to ACKNOWLEDGED in the audit report, still reported, never suppressed, and never
affecting non-404/403 failures or successful-response evidence. Use it for foreseeable 404s:
upstream repositories made private or deleted, or repositories owned by a different GitHub account
than the authenticated gh login.
skip replaces the audit's default discovery skip list (node_modules, vendor, .venv)
whenever any entry is present. It does not append. A lone skip = third_party means only
third_party is skipped (plus unconditional . / .. / .git). To extend the defaults, write
those three plus the extra names. CLI --skip and config fleet.skip compose additively with each
other the same way other scope inputs do.
Resolution priority is explicit audit CLI override, canonical config entry, then the discovered
checkout's own main worktree (the first record of git worktree list --porcelain). A canonical
override is therefore not needed merely to steer the audit away from a linked worktree. The audit
resolves that itself. Never add one because two directory names look similar; verify the normalized
GitHub remote identity on both sides first.
What this skill does NOT do
- Run a fleet audit. That is
/repo-fleet-hygiene:audit. check validates config only; it never
walks the fleet.
- Write the plugin cache, Claude Code user settings, or
pluginConfigs. Nor any machine-local
state. The config is the consumer's own tracked file.
- Touch Git remotes, branches, or worktrees.
Gotchas
- Absolute paths in tracked config can be rejected by consumer write guards. A root, repository, or
canonical target written as an absolute path may trip a consumer's write-time path-portability guard;
the same target written relative to the config file's directory passes and resolves identically, so
apply prefers the relative form, except across Windows volumes, where no relative form exists and
the absolute path is the only honest option. When that happens, write the absolute path, say in the
report that no relative path exists between volumes, and name the consumer's remedies if the guard
still rejects the write: colocate the config with the fleet root on one volume, exempt this file or
path from the guard, or keep a user-global config outside the guard's scan. Do not fabricate a
relative path or leave the consumer to conclude they misconfigured something.
- A project-scoped config is consumed only from its own project. Per the Scoping rule above, a
config meant to apply from every project belongs at the user-global
~/.claude/repo-fleet-hygiene.conf, not a per-project path. Otherwise the audit silently narrows to
the project it was authored in. Two cases collapse this warning, and check should say which one
applies rather than repeating a caution that cannot bite: when the project directory is the home
directory both rungs name the same file, and when no project directory reaches the audit (the
session did not supply one) the project rung is unreachable outright, leaving the
user-global path the only one that can be consumed.
- Config-supplied scope is additive to the audit's CLI scope. A configured root is walked even
when the audit is invoked with an explicit
--repo, so a persisted fleet config widens every later
run. The audit's Scope: header line names each contributing rung; a consumed config cannot be
suppressed for a single run.
1---2name: setup-523description: Verify and configure repo-fleet-hygiene for a consumer project. check inspects the optional .claude/repo-fleet-hygiene.conf read-only (presence, parse validity, path resolution); apply creates or updates it — adding bounded fleet roots, exact repositories, and remote-keyed canonical checkout overrides — preserving unrelated entries. Use when: 'set up repo fleet audit', 'is repo-fleet-hygiene configured', 'configure fleet roots', 'canonical repo override', 'dotfiles-manager checkout'. Re-runnable and safe.4---56## Purpose78Verify and manage the audit's optional Git-format configuration. Setup owns only this file; it never9edits Claude Code settings, `pluginConfigs`, Git remotes, branches, worktrees, or the installed plugin.1011The config file itself is optional to *create*, but a no-argument `/repo-fleet-hygiene:audit`12requires scope from somewhere: CLI bare path / `--root` / `--repo`, or `fleet.root` / `fleet.repo`13entries in a consumed config. Absence of every config on the ladder is therefore INFO for `check`14(nothing to validate yet) and a hard failure for a subsequent no-argument audit, not a silent15default to the current project. Check-centric per the uniform setup contract16(`docs/PLUGIN-PHILOSOPHY.md` "Setup is explicit and repeatable" in the marketplace repository):17`check` inspects read-only; `apply` creates or updates the file, then re-runs `check`. No argument18or `check` runs the check; `apply` runs the check first, then the write. All non-interactive: when19the arguments fully specify the change, `apply` proceeds without prompting.2021Default config path: `${CLAUDE_PROJECT_DIR}/.claude/repo-fleet-hygiene.conf`. An explicit `--config`22may choose another path. Resolve relative roots/repos/canonical paths from the config file directory.2324**Scoping rule (state it in `check`/`apply` output):** the audit consumes config through a ladder:25explicit `--config`, else the project-scoped default above, else the user-global26`~/.claude/repo-fleet-hygiene.conf`. A project-scoped config is therefore consumed only when the27audit runs with that same project directory; a fleet config meant to apply from every project28belongs at the user-global path (`apply --config ~/.claude/repo-fleet-hygiene.conf`). The audit29report header names which config (if any) was consumed.3031## Argument grammar3233```text34check | apply [--config <path>] [--root <dir>]... [--repo <dir>]...35 [--canonical <github.com/owner/repo=path>]... [--ack-unavailable <github.com/owner/repo>]...36 [--skip <name>]... [--max-depth <1..12>]37```3839`--max-depth` writes `[fleet] maxDepth`; `--skip` writes repeatable `[fleet] skip` entries. This40skill owns the config file that carries both. See "Configuration grammar" below for the `skip`41replace semantics, and state them whenever you write a `skip` entry, because "extend" is the42naive reading.4344## `check` (read-only)4546The config file and the grammar below are the source of truth. Probe, report a PASS/FAIL/INFO table47with one remediation line per FAIL, and modify nothing. Do NOT run the collector. That is48`/repo-fleet-hygiene:audit`; `check` only validates the configuration the audit would consume, using49`git config --file` and read-only filesystem probes.50511. **Config presence**. Resolve the config path (`--config` or the default). Absent → INFO naming52 the full ladder: the audit next probes the user-global `~/.claude/repo-fleet-hygiene.conf` (report53 whether one exists there); with no config on the ladder and no scope argument, a bare54 `/repo-fleet-hygiene:audit` fails rather than auditing the current project. `apply` scaffolds a55 config only if the user wants bounded roots or overrides.562. **Parse validity**. Present config: `git config --file "<path>" --list >/dev/null`. A non-zero exit57 is FAIL with the parse error in the remediation line. Never `source` the file.583. **Entry resolution**. For each `[fleet] root`/`repo` and each `[canonical …] path`, resolve it from59 the config directory and confirm the directory exists and (for roots/repos) `git rev-parse` succeeds60 read-only. A referenced path that does not resolve is FAIL, naming the entry.614. **`maxDepth`**. Present and outside `1..12` is FAIL; absent is INFO (the audit's own default applies).625. **Canonical identity**. INFO for each `[canonical "github.com/owner/repository"]` entry: report the63 normalized key. Flag as FAIL only a key that is not a normalizable `github.com/owner/repository`.646. **Acknowledged identities**. INFO listing each `fleet.ackUnavailable` entry (normalized). FAIL any65 value that is not a normalizable `github.com/owner/repository`.667. **Discovery skip names**. INFO listing each `fleet.skip` entry. FAIL any value that is empty or67 contains a path separator (must be a bare directory name). Remind that any present `fleet.skip`68 **replaces** the audit default skip list rather than appending.698. **Tracked-file pair** (only when the config lives inside a git worktree, e.g. a project's70 tracked `.claude/repo-fleet-hygiene.conf`): resolve the worktree that owns the file71 (`git -C "$(dirname -- "<path>")" rev-parse --show-toplevel`) and run both probes there72 with a path relative to that toplevel — `git -C "<toplevel>" check-ignore -v -- "<rel>"`73 reports no match (a match is FAIL with the pattern — teammates would never receive the74 config) AND `git -C "<toplevel>" ls-files --error-unmatch -- "<rel>"` exits 0 (non-zero is75 un-ignored but untracked; FAIL with "commit it to share with the team"). An explicit76 `--config` in another checkout is still that other worktree's file; do not run the pair77 against the current project's index. Skip both, saying so, when no owning worktree exists78 (user-global `~/.claude/…` or any path outside a repository) — a git verdict there is79 meaningless.8081## `apply` (idempotent)8283Run `check`, then create or update the config from the supplied arguments.84851. Parse only the declared argument grammar. Validate every root/repository/canonical path with86 read-only filesystem and `git rev-parse` checks. Normalize canonical keys to87 `github.com/owner/repository` (lowercase host, case-preserving owner/name is acceptable).88 Validate `--max-depth` as an integer in `1..12` and write it as `[fleet] maxDepth`; this skill89 owns the file that carries it, so it must be settable here rather than by hand-editing.90 Validate each `--ack-unavailable` value as a normalizable `github.com/owner/repository`91 (no filesystem probe, the identity is expected to be inaccessible); write it as a repeatable92 `[fleet] ackUnavailable` entry, deduplicating case-insensitively against entries already93 present. Like roots/repos, apply is additive. Removing an acknowledgment is a manual edit of94 the consumer's own config file.95 Validate each `--skip` value as a bare directory name (reject empty and any path separator);96 write it as a repeatable `[fleet] skip` entry, deduplicating exact matches against entries97 already present. State the replace semantics from "Configuration grammar" when writing.98 Removing a skip entry is a manual edit.992. If the config exists, read it with `git config --file <path> --list --show-origin`. Preserve every100 unrelated entry. Never source it.1013. State the proposed additions/updates before writing. With complete arguments, proceed102 non-interactively; otherwise ask only for the missing values. An empty invocation may create the103 minimal current-project config:104105 ```gitconfig106 [fleet]107 repo = ..108 maxDepth = 5109 ```110111 (`..` is relative to `.claude/` and therefore names `${CLAUDE_PROJECT_DIR}`.)1124. Write/update with an ordinary file edit, not `git config --file ... --add`: the file may be tracked113 and the user must see a deterministic diff. Preserve comments and unrelated sections. Prefer a114 path relative to the config file's directory for any root/repository/canonical target expressible115 that way. The grammar resolves relative paths from that directory and both forms audit116 identically, while some consumer environments run a write-time path-portability guard that rejects117 absolute paths in tracked config. "Expressible that way" is the real constraint: on Windows, no118 relative path exists between two volumes, so a fleet root on `D:` with a config on `C:` has only119 the absolute form. Write the absolute path, and say in the report that the relative form was120 unavailable because the target is on another volume. Otherwise the guard's rejection reads as a121 consumer mistake. This preference is prose guidance followed by the model, not a property a122 deterministic component enforces; treat it as a default to justify departing from, not a123 guarantee.1245. Verify after remediation. Re-run every `check` probe against the written file (never claim success on125 the edit alone). Config-only, exactly as `check` defines them:126127 - **Parse validity**. `git config --file "<config-path>" --list >/dev/null`128 - **Entry resolution**. For each `[fleet] root`/`repo` and each `[canonical …] path`, resolve from129 the config directory and confirm the directory exists and (for roots/repos) `git rev-parse` succeeds130 read-only131 - **`maxDepth`**. When present, confirm it is an integer in `1..12`132 - **Canonical identity** and **acknowledged identities**. Confirm each key/value normalizes to133 `github.com/owner/repository`134 - **Discovery skip names**. When present, confirm each `fleet.skip` value is a bare directory135 name (non-empty, no path separator)136 - **Tracked-file pair** (when the config lives inside a git worktree, e.g. the tracked137 `.claude/repo-fleet-hygiene.conf`): resolve the owning worktree138 (`git -C "$(dirname -- "<config-path>")" rev-parse --show-toplevel`) and run both probes139 there with a toplevel-relative path — `git -C "<toplevel>" check-ignore -v -- "<rel>"`140 reports no match (a match is FAIL with the pattern — teammates would never receive the141 config) AND `git -C "<toplevel>" ls-files --error-unmatch -- "<rel>"` exits 0 (non-zero142 means un-ignored but still untracked, the guaranteed state right after a fresh write;143 report "written but untracked: commit it to share with the team", never success). An144 explicit `--config` in another checkout is still that other worktree's file. Skip both,145 saying so, when no owning worktree exists — a git verdict there is meaningless.146147 Do **not** invoke the collector to verify a write. It is the full fleet walk this skill says it148 never runs: per-repository network queries across every configured root, minutes on a real fleet,149 and it proves nothing about the file that the `check` probes do not already prove. If the user150 explicitly wants an end-to-end run, say that it is a real audit and hand off by invoking151 `/repo-fleet-hygiene:audit` via the Skill tool.1521536. Report path, inferred/explicit entries, preserved entries, and the config-verification result.154155Re-running `apply` with the same arguments after everything resolves changes nothing and reports156"already configured".157158## Configuration grammar159160```gitconfig161[fleet]162 root = ../../repos/github.com # repeatable discovery root163 repo = ../../special/repo # repeatable exact target164 maxDepth = 5 # integer 1..12165 ackUnavailable = github.com/owner/repository # repeatable; acknowledge a known-inaccessible identity166 skip = vendor # repeatable; REPLACE default discovery skip list (not append)167168[canonical "github.com/owner/repository"]169 path = ../../../canonical-checkout170```171172`ackUnavailable` demotes a 404/403 `github-identity-unavailable` finding for that identity from173`UNKNOWN` to `ACKNOWLEDGED` in the audit report, still reported, never suppressed, and never174affecting non-404/403 failures or successful-response evidence. Use it for foreseeable 404s:175upstream repositories made private or deleted, or repositories owned by a different GitHub account176than the authenticated `gh` login.177178`skip` replaces the audit's default discovery skip list (`node_modules`, `vendor`, `.venv`)179whenever any entry is present. It does **not** append. A lone `skip = third_party` means only180`third_party` is skipped (plus unconditional `.` / `..` / `.git`). To extend the defaults, write181those three plus the extra names. CLI `--skip` and config `fleet.skip` compose additively with each182other the same way other scope inputs do.183184Resolution priority is explicit audit CLI override, canonical config entry, then the discovered185checkout's own **main worktree** (the first record of `git worktree list --porcelain`). A canonical186override is therefore not needed merely to steer the audit away from a linked worktree. The audit187resolves that itself. Never add one because two directory names look similar; verify the normalized188GitHub remote identity on both sides first.189190## What this skill does NOT do191192- Run a fleet audit. That is `/repo-fleet-hygiene:audit`. `check` validates config only; it never193 walks the fleet.194- Write the plugin cache, Claude Code user settings, or `pluginConfigs`. Nor any machine-local195 state. The config is the consumer's own tracked file.196- Touch Git remotes, branches, or worktrees.197198## Gotchas199200- **Absolute paths in tracked config can be rejected by consumer write guards.** A root, repository, or201 canonical target written as an absolute path may trip a consumer's write-time path-portability guard;202 the same target written relative to the config file's directory passes and resolves identically, so203 `apply` prefers the relative form, except across Windows volumes, where no relative form exists and204 the absolute path is the only honest option. When that happens, write the absolute path, say in the205 report that no relative path exists between volumes, and name the consumer's remedies if the guard206 still rejects the write: colocate the config with the fleet root on one volume, exempt this file or207 path from the guard, or keep a user-global config outside the guard's scan. Do not fabricate a208 relative path or leave the consumer to conclude they misconfigured something.209- **A project-scoped config is consumed only from its own project.** Per the Scoping rule above, a210 config meant to apply from every project belongs at the user-global211 `~/.claude/repo-fleet-hygiene.conf`, not a per-project path. Otherwise the audit silently narrows to212 the project it was authored in. Two cases collapse this warning, and `check` should say which one213 applies rather than repeating a caution that cannot bite: when the project directory *is* the home214 directory both rungs name the same file, and when no project directory reaches the audit (the215 session did not supply one) the project rung is unreachable outright, leaving the216 user-global path the only one that can be consumed.217- **Config-supplied scope is additive to the audit's CLI scope.** A configured root is walked even218 when the audit is invoked with an explicit `--repo`, so a persisted fleet config widens every later219 run. The audit's `Scope:` header line names each contributing rung; a consumed config cannot be220 suppressed for a single run.