auto-mode 0.1.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.
- package/LICENSE +21 -0
- package/README.md +145 -0
- package/dist/classify-locally-BHl0xiqa.js +786 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +102 -0
- package/dist/index.d.ts +219 -0
- package/dist/index.js +2 -0
- package/package.json +55 -0
- package/policy/classifier.md +268 -0
- package/policy/rules.md +357 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Geoff Whatley
|
|
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
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# auto-mode
|
|
2
|
+
|
|
3
|
+
A permission classifier that runs as a hook. It reads the agent's next action and
|
|
4
|
+
answers allow, deny, or nothing at all — so you can leave an agent unattended
|
|
5
|
+
without approving every step.
|
|
6
|
+
|
|
7
|
+
Built for coding agents that are **not** Claude, in harnesses that are not Claude
|
|
8
|
+
Code. Claude Code has its own auto mode; this gives the same shape to everything
|
|
9
|
+
else.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm i -g auto-mode
|
|
15
|
+
auto-mode init claude # or codex, or muse
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`init` prints the hook entry and the path to paste it into. It does not write
|
|
19
|
+
that file. Your settings are yours, and a tool that installs its own restraints
|
|
20
|
+
can remove them.
|
|
21
|
+
|
|
22
|
+
## The two tiers
|
|
23
|
+
|
|
24
|
+
1. **Local rules.** Deterministic matching, no network call, well under a
|
|
25
|
+
millisecond. It answers **allow** or **escalate** and never denies — a wrong
|
|
26
|
+
local allow costs one unwatched action, a wrong local deny stops work you
|
|
27
|
+
asked for.
|
|
28
|
+
2. **The model.** Everything the first tier declines goes to a small reasoning
|
|
29
|
+
model with the policy as its system prompt. Measured at 13-24 seconds on a
|
|
30
|
+
hard case, which is why tier one has to catch the ordinary ones.
|
|
31
|
+
|
|
32
|
+
## The policy
|
|
33
|
+
|
|
34
|
+
Two files, and everything in them is what the model reads. Nothing there is
|
|
35
|
+
addressed to you.
|
|
36
|
+
|
|
37
|
+
| File | What it holds | Change it to |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| `policy/rules.md` | 22 rules, 7 exceptions | Change what is blocked |
|
|
40
|
+
| `policy/classifier.md` | Threat model, consent bar, evaluation rules, output contract | Change how judgement works |
|
|
41
|
+
|
|
42
|
+
`classifier.md` carries a `<rules>` marker on its own line. At run time auto-mode
|
|
43
|
+
replaces it with the whole of `policy/rules.md` and the two become one prompt.
|
|
44
|
+
`auto-mode print-prompt` writes exactly what the model receives.
|
|
45
|
+
|
|
46
|
+
**Read `policy/rules.md` before you turn this on.** If you disagree with a rule,
|
|
47
|
+
change the rule.
|
|
48
|
+
|
|
49
|
+
## Configuration
|
|
50
|
+
|
|
51
|
+
`~/.config/auto-mode/config.json`. Everything has a default; the file is
|
|
52
|
+
optional.
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"preset": "spark",
|
|
57
|
+
"provider": { "apiKeyCommand": "my-key-helper" },
|
|
58
|
+
"onFailure": "defer"
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Three presets: `spark` (the default), `claude`, `glm`. Each names a base URL, a
|
|
63
|
+
model, a key source, and the three settings that move together — whether the
|
|
64
|
+
model reasons, its token budget, and its timeout.
|
|
65
|
+
|
|
66
|
+
| Key | Default | Meaning |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| `preset` | `spark` | Which provider to start from |
|
|
69
|
+
| `provider.*` | from the preset | Override any single field |
|
|
70
|
+
| `classifierPath`, `rulesPath` | the shipped files | Use your own policy |
|
|
71
|
+
| `transcriptEntries` | `40` | How much history the model sees |
|
|
72
|
+
| `onFailure` | `defer` | `defer` writes nothing; `deny` fails closed |
|
|
73
|
+
|
|
74
|
+
**Muse Spark is the default** because it reasons, it was correct on every case
|
|
75
|
+
measured, and a contributor-tier call costs roughly a fortieth of the
|
|
76
|
+
alternatives. Its token budget is a floor, not a preference: below it, Spark
|
|
77
|
+
returns nothing at all.
|
|
78
|
+
|
|
79
|
+
The key comes from `provider.apiKeyEnv` first, then from `provider.apiKeyCommand`
|
|
80
|
+
if that is unset. Muse runs hooks with a scrubbed environment, so under Muse the
|
|
81
|
+
command is the only one that works.
|
|
82
|
+
|
|
83
|
+
## Writing nothing is an answer
|
|
84
|
+
|
|
85
|
+
It means auto-mode has no opinion, and the harness then does what it would have
|
|
86
|
+
done alone. That is the output for an unknown harness, an event that is not a
|
|
87
|
+
tool gate, malformed input, a missing key, and a model that times out. So this
|
|
88
|
+
can only turn an ask into an allow or a deny — it never removes a gate that was
|
|
89
|
+
already there.
|
|
90
|
+
|
|
91
|
+
Set `onFailure` to `deny` if you would rather fail closed.
|
|
92
|
+
|
|
93
|
+
## Harnesses
|
|
94
|
+
|
|
95
|
+
Claude Code, Codex, and Muse Code. The verdict contract is the same for all
|
|
96
|
+
three, and all three were verified end to end: the hook denied a real tool call
|
|
97
|
+
and each harness stopped it.
|
|
98
|
+
|
|
99
|
+
They differ in how the hook is registered, and each difference makes the hook
|
|
100
|
+
silently not run if you get it wrong. `auto-mode init` prints these.
|
|
101
|
+
|
|
102
|
+
| Harness | Match every tool | Also needed |
|
|
103
|
+
|---|---|---|
|
|
104
|
+
| Claude | `"matcher": ".*"` | — |
|
|
105
|
+
| Codex | omit `matcher` | `hooks = true`, and trust the hook once |
|
|
106
|
+
| Muse | `"matcher": ""` | no `timeout_ms` key |
|
|
107
|
+
|
|
108
|
+
Three traps worth naming:
|
|
109
|
+
|
|
110
|
+
- Claude matches tool names against a **regular expression**. `"*"` is not valid
|
|
111
|
+
regex and `""` matches only a tool named empty; both leave the hook installed
|
|
112
|
+
and never firing. Muse reads `""` as every tool, so the same value means
|
|
113
|
+
opposite things in the two harnesses.
|
|
114
|
+
- Codex will not run an **untrusted** hook. It is skipped silently until you
|
|
115
|
+
trust it once; the answer is recorded under `[hooks.state]` in `config.toml`.
|
|
116
|
+
- Muse **rejects a handler carrying `timeout_ms`**, reporting an unknown field
|
|
117
|
+
and skipping the whole handler.
|
|
118
|
+
|
|
119
|
+
Detection order matters too. Muse is identified by `model_provider`, which the
|
|
120
|
+
others do not send. Claude sends `prompt_id`. Codex sends `turn_id` — and so
|
|
121
|
+
does Muse, so Muse has to be tested first.
|
|
122
|
+
|
|
123
|
+
## What this cannot do
|
|
124
|
+
|
|
125
|
+
- **A hook that cannot start fails open.** If the command is missing or crashes,
|
|
126
|
+
the harness logs it and carries on. `onFailure: "deny"` cannot help, because
|
|
127
|
+
the process never runs.
|
|
128
|
+
- **It can only narrow what the harness already allows.** A tool your own
|
|
129
|
+
permission settings deny is refused before the hook sees it.
|
|
130
|
+
- **The agent's own judgement comes first.** A model that refuses to issue a
|
|
131
|
+
command means the hook is never consulted for it.
|
|
132
|
+
|
|
133
|
+
## Commands
|
|
134
|
+
|
|
135
|
+
| Command | What it does |
|
|
136
|
+
|---|---|
|
|
137
|
+
| `auto-mode run` | Read a payload on stdin, write a verdict on stdout |
|
|
138
|
+
| `auto-mode run --explain` | Also write the reasoning to stderr |
|
|
139
|
+
| `auto-mode run --local-only` | Skip the model tier |
|
|
140
|
+
| `auto-mode print-prompt` | Print the assembled system prompt |
|
|
141
|
+
| `auto-mode init <harness>` | Print the hook entry to add |
|
|
142
|
+
|
|
143
|
+
## Licence
|
|
144
|
+
|
|
145
|
+
MIT.
|