@rtorcato/repo-tooling 3.34.0 → 3.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,9 +14,17 @@ import { shellQuote } from '../utils/shell.js';
14
14
  import { isNewerVersion, resolveShippedVersion } from '../utils/version.js';
15
15
  /**
16
16
  * Skills this package owns the content of and keeps up to date. The loop first —
17
- * it is the pipeline; the other three are its drivers (burst, on-ramp, status).
17
+ * it is the pipeline; the next three are its drivers (burst, on-ramp, status).
18
+ * `dogfood` stands apart: it tests the consuming repo's own tooling rather than
19
+ * driving the loop, and it is the only one that writes nothing outside a temp dir.
18
20
  */
19
- export const SHIPPED_SKILLS = ['ai-issue-loop', 'ai-workflow', 'ai-issue', 'ai-loop-status'];
21
+ export const SHIPPED_SKILLS = [
22
+ 'ai-issue-loop',
23
+ 'ai-workflow',
24
+ 'ai-issue',
25
+ 'ai-loop-status',
26
+ 'dogfood',
27
+ ];
20
28
  /** The primary skill — the default everywhere a single name is accepted. */
21
29
  export const SHIPPED_SKILL = 'ai-issue-loop';
22
30
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rtorcato/repo-tooling",
3
- "version": "3.34.0",
3
+ "version": "3.35.0",
4
4
  "description": "One CLI to scaffold, audit and fix your repo's whole toolchain — linting, tests, commits, releases & CI.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -0,0 +1,209 @@
1
+ ---
2
+ name: dogfood
3
+ description: |
4
+ Run this repo's own tooling against throwaway greenfield fixtures in a temp
5
+ directory and report what breaks. Use when the user asks to "dogfood",
6
+ "test the tool on a fresh repo", "see what happens on a new project", or
7
+ invokes `/dogfood`. Asks what to exercise before it starts. Writes only
8
+ under a temp directory, never touches the repo's working tree, and never
9
+ deletes anything — it hands the path back for the user to remove.
10
+ ---
11
+
12
+ # dogfood
13
+
14
+ Point the repo's own tooling at repos it has never seen and find out what it
15
+ does wrong. Arguments: $ARGUMENTS
16
+
17
+ **The bugs live where the tool meets content it did not write.** An empty
18
+ directory finds nothing — every finding from the run this skill is based on came
19
+ from a fixture that already had a `package.json`, a manifest, or a source file
20
+ with an opinion in it. Scaffolding onto nothing is the one case the authors
21
+ already tested.
22
+
23
+ ## What this never does
24
+
25
+ - **Never writes outside its temp directory.** Not the repo's working tree, not
26
+ `~/.claude`, not a global git config (`git config --global` writes a stowed
27
+ dotfile on this machine).
28
+ - **Never deletes.** `rm` is often permission-blocked for an agent, and a
29
+ half-deleted fixture is worse than a kept one. Report the path and size at the
30
+ end; the user removes it when they are done reading it.
31
+ - **Never files an issue without asking**, and never labels one `ai-ready` —
32
+ that label is the human's gate into `ai-issue-loop`.
33
+
34
+ ## Step 1 — ask what to exercise
35
+
36
+ Use `AskUserQuestion`. Look at the repo first so the options are real — read its
37
+ `package.json` `bin`, its CLI's `--help`, or its presets/templates directory —
38
+ then ask:
39
+
40
+ 1. **What to exercise.** Offer the actual entry points found (multiSelect).
41
+ For a scaffolding tool that is its presets; for a linter its rule sets; for a
42
+ codemod its transforms.
43
+ 2. **Fixture shape.** *Realistic pre-existing repos* (recommended — this is what
44
+ finds bugs) vs *empty directories* (only worth it to check the happy path
45
+ still works).
46
+ 3. **What to do with findings.** *Report in the transcript only* (recommended
47
+ for a first run) vs *also file GitHub issues*. If they choose issues, every
48
+ one opens with `🤖 *Filed by an agent via dogfood.*` and carries no
49
+ `ai-ready` label.
50
+
51
+ Skip a question the arguments already answer.
52
+
53
+ ## Step 2 — pin the build and the version
54
+
55
+ **A finding with no version stamp is unreproducible and will be argued with.**
56
+ Build from source, and record the commit — not the version in `package.json`,
57
+ which under semantic-release without `@semantic-release/git` never moves:
58
+
59
+ ```bash
60
+ ROOT=$(git rev-parse --show-toplevel)
61
+ cd "$ROOT" && pnpm build-cli # or whatever CLAUDE.md says the build is
62
+ REF=$(git -C "$ROOT" rev-parse --short HEAD)
63
+ DIRTY=$(git -C "$ROOT" status --porcelain | head -1)
64
+ ```
65
+
66
+ Say in every finding: *Reproduced with `dist/` built from `<REF>`*, and mention
67
+ it if the tree was dirty. Invoke the built artefact directly
68
+ (`node "$ROOT/dist/cli/index.js" …`) — never `npx <package>`, which silently
69
+ tests the *published* version instead of the working tree.
70
+
71
+ ## Step 3 — make the temp root, once
72
+
73
+ ```bash
74
+ BASE="${TMPDIR:-/tmp}/dogfood-$REF-$$"
75
+ mkdir -p "$BASE"
76
+ echo "$BASE"
77
+ ```
78
+
79
+ **Pin `$BASE` as an absolute path and reuse that literal for the rest of the
80
+ run.** `$TMPDIR` resolves differently inside and outside the command sandbox, so
81
+ re-expanding it in a later command lands in a different directory and the run
82
+ silently splits in two. A unique suffix means a re-run never collides, which is
83
+ what makes never-deleting safe.
84
+
85
+ ## Step 4 — build each fixture, and commit it
86
+
87
+ A fixture is a *plausible* repo, not a stub. Give it the things the tool will
88
+ read and be tempted to rewrite: a real name (scoped and unscoped both matter —
89
+ a scope is a code path), a source file, a manifest that already declares
90
+ something, an existing script.
91
+
92
+ Then **`git init` and commit it**. That baseline commit is the whole trick:
93
+
94
+ ```bash
95
+ git -C "$BASE/$NAME" init -q
96
+ git -C "$BASE/$NAME" add -A
97
+ git -C "$BASE/$NAME" -c user.email=dogfood@local -c user.name=dogfood commit -qm baseline
98
+ ```
99
+
100
+ `-c` on the command, never `git config --global`. Without the commit, "what did
101
+ the tool overwrite" is a question nobody can answer afterwards.
102
+
103
+ ## Step 5 — before, run, after
104
+
105
+ ```bash
106
+ node "$ROOT/dist/cli/index.js" doctor --json > "$BASE/$NAME.before.json" 2>&1 || true
107
+ node "$ROOT/dist/cli/index.js" setup --preset <p> --yes > "$BASE/$NAME.setup.log" 2>&1; echo "exit=$?"
108
+ node "$ROOT/dist/cli/index.js" doctor --json > "$BASE/$NAME.after.json" 2>&1 || true
109
+ ```
110
+
111
+ `> file 2>&1`, in that order — `2>&1 > file` leaves stderr on the terminal, which
112
+ is where the interesting output usually is. `|| true` because a diagnostic
113
+ command exiting non-zero *is* data, not a reason to abort the run.
114
+
115
+ ## Step 6 — the three checks that actually find things
116
+
117
+ Run all three on every fixture. Each one found a distinct real bug.
118
+
119
+ ### a. What did it overwrite?
120
+
121
+ ```bash
122
+ git -C "$BASE/$NAME" diff --stat
123
+ git -C "$BASE/$NAME" diff -- <every file the tool claims to merge rather than replace>
124
+ ```
125
+
126
+ A tool that says it preserves your `name`, `version`, and scripts is making a
127
+ promise; the diff is where you check it. This is how a preset was caught
128
+ renaming a package after its directory and orphaning the sources — **and the
129
+ build still exited 0**, because the build system ignored the now-undeclared
130
+ directory. A green build is not evidence.
131
+
132
+ ### b. Does it pass its own check?
133
+
134
+ ```bash
135
+ node "$ROOT/dist/cli/index.js" doctor; echo "exit=$?"
136
+ ```
137
+
138
+ Non-zero on a repo the tool itself just created is a bug every time, however
139
+ small the detail. "Run setup, you're aligned" either holds or the promise is
140
+ worthless — and a user's CI or pre-commit hook keys on exactly that exit code.
141
+ Diff `before.json` against `after.json` too: a check that *stopped* running is
142
+ invisible in the exit code.
143
+
144
+ ### c. Does the contract it wrote actually hold?
145
+
146
+ The subtlest class, and the most damaging. The tool writes a *declaration* —
147
+ `exports`, `main`, entry points, a target list — and separately preserves a
148
+ *producer* — the build script. Nothing checks that the producer emits what the
149
+ declaration promises. So:
150
+
151
+ ```bash
152
+ cd "$BASE/$NAME" && <the repo's own build> && ls -R dist 2>/dev/null
153
+ ```
154
+
155
+ then read the manifest and confirm every path it names exists on disk. A package
156
+ whose `main` points at a file its own `build` cannot emit publishes green and
157
+ breaks every consumer.
158
+
159
+ ## Step 7 — report
160
+
161
+ Per fixture, one short block: preset, exit codes, and each finding as
162
+ *what happened → why it matters → the evidence*. Paste the diff hunk or the JSON
163
+ line; a paraphrase is not a reproduction.
164
+
165
+ Rank by **silence, not severity**. A loud failure gets noticed by whoever hits
166
+ it. A wrong result that exits 0 does not, and that is the finding worth the
167
+ user's attention — lead with it.
168
+
169
+ If the user chose to file issues, one issue per finding, ≤30 lines each: what,
170
+ the minimal reproduction, the version stamp, and a `## What to change` section
171
+ that names the options rather than picking one. Attach no `ai-ready`.
172
+
173
+ **Say what you could not test.** A preset you skipped, a build you could not
174
+ run, an area still in flight — an unexamined corner reported as unexamined is
175
+ useful; one left silent reads as covered.
176
+
177
+ ## Step 8 — hand back the temp directory
178
+
179
+ Last line of the run, always:
180
+
181
+ ```bash
182
+ du -sh "$BASE"
183
+ ```
184
+
185
+ Tell the user the path and the size, and give them the exact command:
186
+
187
+ ```
188
+ ! rm -rf <BASE>
189
+ ```
190
+
191
+ The `!` prefix runs it in their session. Do not run it yourself, do not offer to
192
+ run it later, and do not delete it on a subsequent invocation — the fixtures are
193
+ the evidence behind every finding, and they are worth more than the disk.
194
+
195
+ ## Gotchas that cost real time
196
+
197
+ - **Shell aliases corrupt captured output.** `ls` aliased to a colouriser emits
198
+ escape codes into anything you parse. `unalias ls` / `unalias g` first, or use
199
+ `command ls`.
200
+ - **Ambient `GIT_DIR` / `GIT_WORK_TREE` / `GIT_CONFIG*` outrank `-C`.** If any is
201
+ exported, every fixture git command silently operates on the wrong repo.
202
+ `env -u GIT_DIR -u GIT_WORK_TREE git …` when in doubt.
203
+ - **A function, not a variable, for a wrapped command.** `G="env -u X git"` does
204
+ not word-split under zsh; define `g() { env -u X git "$@"; }`.
205
+ - **`pnpm install` may need the sandbox disabled** — it writes to a store outside
206
+ the working directory. That is a legitimate escalation for this one command.
207
+ - **Never reuse a fixture between runs.** A second `setup` over an
208
+ already-set-up repo tests idempotency, which is a different question; mixing
209
+ the two makes both answers unreliable.