| name | toolbox-contract |
| description | The micro-product contract for the IT Guy toolbox — acceptance criteria, directory layout, README template, registry schema, dry-run requirement, double-clickable wrappers, the evolution ladder, and the pattern catalogue used to offer a user automations they did not know to ask for. Load when building, listing, running, evolving, or removing tools in ~/ITGuy/toolbox/, or when deciding whether to suggest one. |
Toolbox Contract
Every automation the IT guy builds is left behind as a named tool the user owns. Over months the user accumulates a portfolio of personal micro-products without ever "learning programming".
Two ways a tool gets built
The user asks (/mac-it-guy-pro:automate) — they describe a chore and it becomes a tool.
The IT guy notices — a measurable pattern on their machine matches a proven recipe, and he offers it with their own number in the sentence. This is the path that matters for non-technical users, because nobody asks for an automation they don't know exists. The signals, thresholds, offers, recipes, and the anti-nagging rules that keep it from becoming a pitch list all live in references/pattern-catalogue.md. Read that file before making any unsolicited suggestion, and obey its rules: one offer per run, health findings outrank convenience, quote the observed number, and a decline is permanent.
Both paths produce the same thing, and both must pass the test below.
Acceptance test — all three, or don't build it
- Real problem: it removes a chore the user actually described, even if only theirs.
- Repeat use: the chore recurs. A one-off task is just done directly, not turned into a tool.
- Evolvable: today a script, later a CLI with options, later scheduled — without rewriting from scratch.
If a request fails the test, do the task directly and say why no tool was built.
Directory layout
~/ITGuy/toolbox/<tool-name>/
├── run.sh # or run.py — the tool itself
├── README.md # plain language, template below
└── <Tool Name>.command # double-clickable Finder wrapper
<tool-name> is kebab-case, verb-first: rename-photos-by-date, file-desktop-screenshots.
run.sh starts with #!/bin/bash and set -euo pipefail; run.py uses only the Python standard library. No dependencies without naming the dependency to the user and getting a yes.
Non-negotiable tool behaviors
- Dry-run is the default. Running the tool with no arguments prints what it would do and changes nothing. The real run requires
--go.
- Trash, never rm — same rule as the safety contract. Tools delete via Finder Trash (recipe in
macos-recipes).
- Never overwrite — collisions get
(2) suffixes.
- Print a summary line at the end: how many files touched, how much space affected, where.
- Exit non-zero on any error, with a message a non-technical user understands.
README.md template (plain language)
# <Tool Name>
**What it does:** <one sentence a non-technical reader understands>
**Built:** YYYY-MM-DD, because: <the chore, in the user's own words>
## How to run it
1. Double-click `<Tool Name>.command` — it shows a preview and changes nothing.
2. Happy with the preview? Run it for real: <exact command with --go>.
## Example
<one real before → after example from the test run>
## What this taught you
<one or two sentences: the transferable idea behind this tool — "previewing
before acting ", " '
, " — >
YYYY-MM-DD: built (v1)
The What this taught you section is the point of the toolbox, not a decoration. A user who accumulates twenty scripts has a folder; a user who accumulates twenty ideas about how work gets automated can decide what to build next. Keep it to the transferable idea and leave the implementation out — and write it in the user's language while the code, filenames, and tool name stay English.
.command wrapper
macOS runs .command files in Terminal on double-click. Wrapper content:
#!/bin/bash
cd "$(dirname "$0")"
bash run.sh
echo ""
read -p "Preview done — press Return to close (run with --go to apply)."
Mark it executable (chmod +x). The wrapper always runs the preview, never --go — real runs stay deliberate.
Registry — ~/ITGuy/toolbox.json
{
"tools": [
{
"name": "rename-photos-by-date",
"pattern": "camera-named-photos",
"purpose": "Renames photos to YYYY-MM-DD-<original>.jpg using the date each photo was taken",
"built": "2026-07-29",
"last_used": "2026-07-29",
"runs": 1,
"stage": "script"
}
],
"declined": ["desktop-screenshots"]
}
stage is one of script | cli | scheduled. Update last_used and runs on every run.
pattern is the catalogue id this tool was built from, or absent for a tool the user requested directly. It is what marks a pattern as handled — the tool's own name cannot serve that purpose, because /automate lets users name tools whatever they like, so a user who calls it tidy-my-desktop-shots would otherwise be offered desktop-screenshots forever. Always set it when building from a catalogue recipe.
declined holds catalogue ids the user has turned down — the backticked code such as desktop-screenshots, never the recipe name such as file-desktop-screenshots; the two differ by a word and a decline recorded under the wrong one is a decline no reader will ever match. A decline is permanent — never raise that pattern again. Remove the entry only if the user later asks for that tool themselves. If toolbox.json is absent, create it as {"tools": [], "declined": []}; an absent declined key means nothing has been declined yet.
Evolution ladder
| Stage | Trigger to advance | What changes |
|---|
| script | used 5+ times, or user asks for options | add flags (--folder, --since), input validation, --help in plain language |
| cli | user says "do this every day/week" | add a launchd LaunchAgent (recipe in macos-recipes), log to ~/ITGuy/toolbox/<name>/runs.log |
| scheduled | — | terminal stage in v0.1 |
When a run of /mac-it-guy-pro:toolbox notices a trigger condition, offer the upgrade — never apply it unasked.