@spatz/cli 0.0.0 → 0.1.1
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.
- package/LICENSE +21 -0
- package/README.md +251 -1
- package/bin/spatz.js +40 -0
- package/package.json +26 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lorenz Hilpert
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1 +1,251 @@
|
|
|
1
|
-
|
|
1
|
+
# spatz
|
|
2
|
+
|
|
3
|
+
**Choose a model and effort for your coding task. Learn from the result.**
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://github.com/lorenzh/spatz/releases/latest)
|
|
7
|
+
[](https://bun.sh)
|
|
8
|
+
|
|
9
|
+
Do not use a cannon to shoot sparrows.
|
|
10
|
+
|
|
11
|
+
spatz ranks the model and effort pairs that you can use for a coding task.
|
|
12
|
+
It learns from task results to help choose cheaper pairs that succeed.
|
|
13
|
+
You or your coding agent run the task with the recommended pair.
|
|
14
|
+
|
|
15
|
+
[Quickstart](#quickstart) · [Documentation](#documentation) · [Contributing](#contributing) · [Releases](https://github.com/lorenzh/spatz/releases)
|
|
16
|
+
|
|
17
|
+
## Why spatz?
|
|
18
|
+
|
|
19
|
+
Different tasks need different models and effort levels.
|
|
20
|
+
spatz combines task classification with results from your previous tasks.
|
|
21
|
+
|
|
22
|
+
- **Use your available models.** Pass the candidates with `--models`.
|
|
23
|
+
- **Learn from results.** Record outcomes through hooks or `spatz report`.
|
|
24
|
+
- **Compare performance.** Use `spatz stats` to see results per task type or routing scope.
|
|
25
|
+
- **Connect to coding agents.** Hooks support Claude Code and Codex CLI. Other agents can use the CLI directly.
|
|
26
|
+
- **Keep learning data local.** spatz stores results in `~/.spatz/spatz.db`. It does not store task text.
|
|
27
|
+
|
|
28
|
+
## Quickstart
|
|
29
|
+
|
|
30
|
+
### 1. Install
|
|
31
|
+
|
|
32
|
+
Install the stable CLI with Node.js 18 or newer:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install -g @spatz/cli
|
|
36
|
+
spatz --version
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The npm package includes the Bun runtime. Keep optional dependencies enabled.
|
|
40
|
+
Linux needs glibc. spatz does not support Alpine Linux.
|
|
41
|
+
Windows x64 support is experimental. Some releases omit it.
|
|
42
|
+
|
|
43
|
+
For installation without Node.js, use a [release archive](#releases).
|
|
44
|
+
For the unstable nightly version, use `npm install -g @spatz/cli@nightly`.
|
|
45
|
+
|
|
46
|
+
### 2. Ask for a recommendation
|
|
47
|
+
|
|
48
|
+
Pass the task and the model pairs that your agent can run:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
spatz "Fix the off-by-one error in src/list.ts" \
|
|
52
|
+
--models claude-opus-5-5:high+medium,claude-sonnet-5-5:medium+low
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Each candidate uses `<model-id>:<effort>+<effort>...`.
|
|
56
|
+
Use model IDs and efforts that your environment supports.
|
|
57
|
+
The output includes a `suggestion_id` and a ranked list of pairs.
|
|
58
|
+
Add `--json` for machine-readable output.
|
|
59
|
+
|
|
60
|
+
spatz works without an API key.
|
|
61
|
+
Without Jev or learned results, it recommends the most expensive candidate.
|
|
62
|
+
Jev is the TypeSafe AI model that classifies the task and estimates candidate suitability.
|
|
63
|
+
To enable Jev, set your TypeSafe AI key:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
export TYPESAFE_AI_API_KEY="<your-key>"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
When you enable Jev, spatz sends task text and candidate details to TypeSafe AI.
|
|
70
|
+
Read [Privacy](#privacy) before using sensitive task text.
|
|
71
|
+
|
|
72
|
+
### 3. Run the task and report the result
|
|
73
|
+
|
|
74
|
+
Run the task with the chosen model and effort in your coding agent.
|
|
75
|
+
Then report the pair you actually used:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
spatz report <suggestion_id> \
|
|
79
|
+
--model claude-sonnet-5-5 --effort medium --result pass
|
|
80
|
+
|
|
81
|
+
spatz stats
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Replace `<suggestion_id>` with the ID from the recommendation.
|
|
85
|
+
Results can be `pass`, `partial`, or `fail`.
|
|
86
|
+
An explicit report overrides hook signals for that recommendation.
|
|
87
|
+
|
|
88
|
+
For experiments, add `--dry-run` to the recommendation command.
|
|
89
|
+
These suggestions never count toward learning or statistics.
|
|
90
|
+
|
|
91
|
+
## How it works
|
|
92
|
+
|
|
93
|
+
1. A local filter checks the task text for known secret patterns.
|
|
94
|
+
2. Jev classifies the task. Without Jev, spatz uses local keyword rules.
|
|
95
|
+
3. spatz ranks your candidates using classification and learned success estimates.
|
|
96
|
+
4. You or your agent choose a pair and run the task.
|
|
97
|
+
5. Hooks or `spatz report` record the outcome for future recommendations.
|
|
98
|
+
|
|
99
|
+
The CLI recommends pairs. The optional Claude Code mod can apply them automatically.
|
|
100
|
+
See [How it works](docs/how-it-works.md) and [Recommendation rules](docs/recommendation.md) for the decision process.
|
|
101
|
+
|
|
102
|
+
## Agent integrations
|
|
103
|
+
|
|
104
|
+
| Integration | What it does | Setup |
|
|
105
|
+
| --- | --- | --- |
|
|
106
|
+
| Claude Code `spatz-hooks` plugin | Record task signals and model usage. | [Hooks guide](docs/hooks.md) |
|
|
107
|
+
| Codex `spatz-hooks` plugin | Record shell results and model usage. | [Hooks guide](docs/hooks.md) |
|
|
108
|
+
| Claude Code `spatz` plugin (mod) | Show recommendations or apply model and effort choices. | [Mod guide](docs/claude-mod.md) |
|
|
109
|
+
| Other agents | Request recommendations and report outcomes through the CLI. | [CLI reference](docs/cli.md) |
|
|
110
|
+
|
|
111
|
+
The Claude Code mod supports `step`, `turn`, `subagent`, `session`, and `escalate` routing scopes.
|
|
112
|
+
Hooks and the mod can run together. The mod's `record: auto` avoids duplicate usage recording with the `spatz-hooks` plugin.
|
|
113
|
+
|
|
114
|
+
### Install the plugins
|
|
115
|
+
|
|
116
|
+
In Claude Code:
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
/plugin marketplace add lorenzh/spatz
|
|
120
|
+
/plugin install spatz-hooks@spatz
|
|
121
|
+
/plugin install spatz@spatz
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`spatz-hooks` records outcomes. `spatz` is the mod that recommends or applies model and effort. Install one or both.
|
|
125
|
+
|
|
126
|
+
In Codex:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
codex plugin marketplace add lorenzh/spatz
|
|
130
|
+
codex plugin add spatz-hooks@spatz
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Codex runs new hooks only after you trust them with `/hooks`.
|
|
134
|
+
|
|
135
|
+
Every plugin ships the `spatz` skill, which tells the agent when to ask spatz and how to report results, so you do not need to edit your agent instructions.
|
|
136
|
+
The plugins also ship a launcher. It uses `spatz` from your `PATH` if present. Otherwise it runs the matching `@spatz/cli` version through Bun or npx, which downloads about 60 MB on first use.
|
|
137
|
+
Codex hooks time out after 10 seconds, so run the launcher once before the first session: `"<plugin root>/bin/spatz" --version`.
|
|
138
|
+
The plugins need a POSIX shell. Native Windows is not supported yet.
|
|
139
|
+
If you added spatz hooks to `~/.claude/settings.json` or `~/.codex/hooks.json` by hand, remove them to avoid duplicate records.
|
|
140
|
+
See [Installation](docs/installation.md) for details.
|
|
141
|
+
|
|
142
|
+
## Privacy
|
|
143
|
+
|
|
144
|
+
spatz stores learning data under `~/.spatz`. It does not store task text or tool output.
|
|
145
|
+
Hooks process task signals locally and make no network requests.
|
|
146
|
+
|
|
147
|
+
When you enable Jev, spatz sends task text and candidate details to TypeSafe AI.
|
|
148
|
+
The secret filter catches known patterns. It cannot detect every secret.
|
|
149
|
+
Do not put secrets in task text.
|
|
150
|
+
|
|
151
|
+
To disable Jev:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
export SPATZ_NO_JEV=1
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
For a project, put `{"jev": false}` in `.spatz.json` in the working directory.
|
|
158
|
+
Without `TYPESAFE_AI_API_KEY`, Jev is also disabled.
|
|
159
|
+
|
|
160
|
+
Disabling Jev still allows OpenRouter requests for model prices. These requests contain no task data.
|
|
161
|
+
spatz caches prices for 24 hours. When the network is unavailable, spatz can still use cached prices.
|
|
162
|
+
The first `spatz stats` run downloads DuckDB's SQLite extension.
|
|
163
|
+
|
|
164
|
+
See the [Privacy guide](docs/privacy.md) for data flows and deletion instructions.
|
|
165
|
+
See [Configuration](docs/configuration.md) for environment variables and local files.
|
|
166
|
+
|
|
167
|
+
## Releases
|
|
168
|
+
|
|
169
|
+
Download an archive and its matching `.sha256` file from [GitHub Releases](https://github.com/lorenzh/spatz/releases).
|
|
170
|
+
Archives include the runtime. You do not need Node.js or Bun installed.
|
|
171
|
+
|
|
172
|
+
Choose `linux` or `darwin` (macOS), then `x64` or `arm64`.
|
|
173
|
+
Apple Silicon uses `darwin-arm64`. Linux builds need glibc.
|
|
174
|
+
Windows x64 ZIP archives are experimental. The agent plugins do not support native Windows.
|
|
175
|
+
|
|
176
|
+
Check the checksum before extraction. This Linux x64 example uses version `0.1.0`:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
version=0.1.0
|
|
180
|
+
archive="spatz-cli-$version-linux-x64.tar.gz"
|
|
181
|
+
sha256sum --check "$archive.sha256"
|
|
182
|
+
|
|
183
|
+
mkdir -p ~/.local/lib/spatz ~/.local/bin
|
|
184
|
+
tar -xzf "$archive" -C ~/.local/lib/spatz
|
|
185
|
+
ln -sfn "$HOME/.local/lib/spatz/${archive%.tar.gz}/spatz" ~/.local/bin/spatz
|
|
186
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
187
|
+
spatz --version
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Use your downloaded version. On macOS, use `shasum -a 256 --check "$archive.sha256"`.
|
|
191
|
+
If your shell does not include `~/.local/bin`, add the `PATH` line to your shell profile.
|
|
192
|
+
Keep the executable and DuckDB libraries together. `spatz stats` needs these libraries beside the executable.
|
|
193
|
+
|
|
194
|
+
The [nightly release](https://github.com/lorenzh/spatz/releases/tag/nightly) is unstable.
|
|
195
|
+
See [Releasing spatz](RELEASING.md) for the release process.
|
|
196
|
+
|
|
197
|
+
## Install from source
|
|
198
|
+
|
|
199
|
+
Install Bun 1.4, then clone the repository:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
git clone https://github.com/lorenzh/spatz.git
|
|
203
|
+
cd spatz
|
|
204
|
+
bun install
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Put a wrapper on your `PATH` so hooks can find `spatz` in non-interactive shells:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
mkdir -p ~/.local/bin
|
|
211
|
+
printf '#!/bin/sh\nexec bun "%s/packages/cli/src/cli.ts" "$@"\n' "$PWD" > ~/.local/bin/spatz
|
|
212
|
+
chmod +x ~/.local/bin/spatz
|
|
213
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
214
|
+
spatz --version
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
A shell alias does not work for hooks.
|
|
218
|
+
See [Contributing](CONTRIBUTING.md) for the full development setup.
|
|
219
|
+
|
|
220
|
+
## Documentation
|
|
221
|
+
|
|
222
|
+
| Guide | What you will find |
|
|
223
|
+
| --- | --- |
|
|
224
|
+
| [CLI reference](docs/cli.md) | Commands, flags, model IDs, output fields, and exit codes. |
|
|
225
|
+
| [How it works](docs/how-it-works.md) | Architecture and the flow from task to outcome. |
|
|
226
|
+
| [Recommendation rules](docs/recommendation.md) | Ranking, exploration, and control groups. |
|
|
227
|
+
| [Hooks](docs/hooks.md) | Claude Code and Codex CLI setup and recorded signals. |
|
|
228
|
+
| [Claude Code mod](docs/claude-mod.md) | Modes, routing scopes, and `/spatz` commands. |
|
|
229
|
+
| [Configuration](docs/configuration.md) | Environment variables and local files. |
|
|
230
|
+
| [Privacy](docs/privacy.md) | Network requests, stored data, and deletion. |
|
|
231
|
+
|
|
232
|
+
## Contributing
|
|
233
|
+
|
|
234
|
+
Bug reports and pull requests are welcome.
|
|
235
|
+
For larger changes, [open an issue](https://github.com/lorenzh/spatz/issues) first to agree on the scope.
|
|
236
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup and the test-first workflow.
|
|
237
|
+
Changes to `main` must go through a pull request.
|
|
238
|
+
|
|
239
|
+
Run the checks before submitting a change:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
bun test
|
|
243
|
+
bun run typecheck
|
|
244
|
+
bun run lint
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
For vulnerabilities, follow [SECURITY.md](SECURITY.md). Do not open a public issue.
|
|
248
|
+
|
|
249
|
+
## License
|
|
250
|
+
|
|
251
|
+
[MIT](LICENSE) © 2026 Lorenz Hilpert.
|
package/bin/spatz.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawnSync } = require("node:child_process");
|
|
3
|
+
const { constants } = require("node:os");
|
|
4
|
+
const { dirname, join } = require("node:path");
|
|
5
|
+
|
|
6
|
+
const packages = {
|
|
7
|
+
"linux-x64": "@spatz/cli-linux-x64",
|
|
8
|
+
"linux-arm64": "@spatz/cli-linux-arm64",
|
|
9
|
+
"darwin-arm64": "@spatz/cli-darwin-arm64",
|
|
10
|
+
"darwin-x64": "@spatz/cli-darwin-x64",
|
|
11
|
+
"win32-x64": "@spatz/cli-win32-x64",
|
|
12
|
+
};
|
|
13
|
+
const target = `${process.platform}-${process.arch}`;
|
|
14
|
+
const pkg = packages[target];
|
|
15
|
+
if (!pkg) {
|
|
16
|
+
console.error(`spatz: unsupported platform ${target}`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
let dir;
|
|
20
|
+
try {
|
|
21
|
+
dir = dirname(require.resolve(`${pkg}/package.json`));
|
|
22
|
+
} catch {
|
|
23
|
+
console.error(
|
|
24
|
+
`spatz: missing ${pkg}. Reinstall @spatz/cli with optional dependencies enabled (omit --no-optional / --omit=optional). The experimental Windows package may be unavailable for this release.`,
|
|
25
|
+
);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
const child = spawnSync(
|
|
29
|
+
join(dir, process.platform === "win32" ? "spatz.exe" : "spatz"),
|
|
30
|
+
process.argv.slice(2),
|
|
31
|
+
{ stdio: "inherit" },
|
|
32
|
+
);
|
|
33
|
+
if (child.error) {
|
|
34
|
+
console.error(`spatz: ${child.error.message}`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
if (child.signal) {
|
|
38
|
+
process.kill(process.pid, child.signal);
|
|
39
|
+
process.exit(128 + constants.signals[child.signal]);
|
|
40
|
+
} else process.exit(child.status ?? 1);
|
package/package.json
CHANGED
|
@@ -1 +1,26 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"name": "@spatz/cli",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Model and effort recommendations for coding agents",
|
|
5
|
+
"bin": {
|
|
6
|
+
"spatz": "bin/spatz.js"
|
|
7
|
+
},
|
|
8
|
+
"optionalDependencies": {
|
|
9
|
+
"@spatz/cli-linux-x64": "0.1.1",
|
|
10
|
+
"@spatz/cli-linux-arm64": "0.1.1",
|
|
11
|
+
"@spatz/cli-darwin-arm64": "0.1.1",
|
|
12
|
+
"@spatz/cli-darwin-x64": "0.1.1",
|
|
13
|
+
"@spatz/cli-win32-x64": "0.1.1"
|
|
14
|
+
},
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=18"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/lorenzh/spatz.git"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"bin"
|
|
25
|
+
]
|
|
26
|
+
}
|