Three ways to extend supertool: add a custom op for this project, bundle ops into a preset, or add a validator that runs after writes.
Custom op — one-project command, lives in .supertool.json. Done in 5 lines. See Custom ops below.
Preset — reusable bundle for a tool or platform (e.g. GitLab, Kubernetes). Shareable across projects. See Presets below, or the preset catalog for shipped examples.
Validator — post-write hook that runs after a file is saved. Syntax check, lint, type check. See Validators for the adapter contract and field reference.
Declare ops in .supertool.json under "ops":
{
"ops": {
"mypy": {
"cmd": "mypy {file}",
"timeout": 30,
"description": "Type-check a Python file.",
"syntax": "mypy:FILE",
"example": "mypy:src/app/module.py"
},
"lint": "ruff check {file}"
}
}Shorthand string ops ("lint": "ruff check {file}") work with a 60s default timeout. Full object form gives you explicit timeout, description, syntax, and example.
| Key | Required | Description |
|---|---|---|
cmd |
yes | Shell command to run. Supports placeholders (see below). |
timeout |
no | Seconds before the subprocess is killed. Default: 60. |
description |
no | One-line description shown in ops listing. |
syntax |
no | Usage pattern shown in help, e.g. mypy:FILE. |
example |
no | Concrete example, e.g. mypy:src/app/module.py. |
status |
no | "experimental" or "stable". Informational only. |
| Placeholder | Expands to | Example |
|---|---|---|
{file} |
First argument, shell-quoted, treated as file path | cat {file} |
{dir} |
Directory of {file} |
ls {dir} |
{arg} |
First argument, shell-quoted, no path validation | glab issue view {arg} |
{args} |
All arguments, each shell-quoted | python3 tool.py {args} |
{path} |
Preset directory with trailing / (presets only) |
python3 {path}gitlab/issue.py {arg} |
Use {file}/{dir} for file operations, {arg}/{args} for non-file arguments (issue numbers, job IDs, etc.).
Built-in ops → custom ops (including preset ops) → aliases. Built-ins always win. Project ops override preset ops on name conflict.
Any key in an op config that isn't a reserved key (cmd, timeout, description, syntax, example, status) is passed to the subprocess as a SUPERTOOL_-prefixed environment variable:
{
"ops": {
"job": {
"cmd": "python3 job.py {arg}",
"lines": 80,
"error_patterns": "ERROR,FAIL,Fatal"
}
}
}The script receives SUPERTOOL_LINES=80 and SUPERTOOL_ERROR_PATTERNS=ERROR,FAIL,Fatal. Use this to tune op behavior from JSON without modifying scripts.
A preset is a JSON file declaring ops (and optionally aliases and validators) for a specific tool or platform.
presets/
mytools.json # op manifest
mytools/
status.py # helper scripts co-located here
deploy.py
Place the manifest at ./presets/NAME.json (project-level) or ~/.config/supertool/presets/NAME.json (user-level). Helper scripts go in ./presets/NAME/ — the {path} placeholder resolves to the manifest's directory with a trailing /.
./presets/{name}.json— project-level (team-specific, committed to the repo)~/.config/supertool/presets/{name}.json— user-level (personal, not committed){supertool install dir}/presets/{name}.json— shipped with supertool
First found wins. Project ops always override preset ops on name conflict.
Same op schema as custom ops, wrapped in a manifest:
{
"description": "My team's deploy tools",
"requires": "kubectl",
"ops": {
"deploy-status": {
"cmd": "python3 {path}mytools/status.py {arg}",
"timeout": 15,
"description": "Check deployment status for a service.",
"syntax": "deploy-status:SERVICE",
"example": "deploy-status:api-gateway"
}
}
}The requires field is documentation only — supertool does not enforce it at runtime.
Enable the preset in .supertool.json:
{ "presets": ["mytools"] }See docs/presets/index.md for the shipped preset catalog and more authoring notes.
Validators are post-write hooks — they run after a file is written and report errors back to the caller. See docs/validators.md for the full adapter contract and field reference. Don't duplicate that here — it's the authoritative source.
- Python stdlib preferred. No third-party dependencies. If an op needs
requests, reconsider the design. - Exit 0 on graceful skip. If the required CLI tool is missing, print a friendly message and exit 0. Don't fail the whole supertool call because
kubectlisn't installed. - Validators output JSON. See validators.md for the exact schema. Other scripts can output anything — supertool passes it through as-is.
- One file per op.
gitlab/issue.py,gitlab/mr.py,gitlab/pipeline.py— not one monolithicgitlab.pywith a dispatch table. - Scripts are co-located with their preset.
presets/mytools/status.py, notscripts/status.py. The{path}placeholder makes this work without hardcoded paths.
python3 -m pytest tests/293 tests, 80% minimum coverage (enforced by pytest-cov). Current: 94%.
Enable the pre-push hook (runs pytest + enforces 80% coverage before every push):
git config core.hooksPath .githooksThe hook is in .githooks/pre-push, committed to the repo. Bypass with git push --no-verify (discouraged).
Want to add a preset, op, or validator to the shipped supertool?
- Branch naming:
feat/short-descriptionfor features,fix/short-descriptionfor bugs,docs/short-descriptionfor documentation. - One feature per PR. A new preset is one PR. Adding a validator adapter is one PR. Bundling both makes review harder.
- Tests in
tests/. New ops and validators need test coverage. Check existing tests for the pattern. - README update if introducing new shape. If your PR adds a new top-level config key or changes op schema, update the README config reference section.
- Commit messages:
feat: add kubectl preset/fix: {path} placeholder on Windows/docs: add contributing guide. Present tense, imperative mood, lowercase.