airlok 0.4.1 → 0.6.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/CHANGELOG.md +33 -0
- package/README.md +98 -11
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,39 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to airlok. The format follows Keep a Changelog; versions follow SemVer.
|
|
4
4
|
|
|
5
|
+
## [0.6.0] - 2026-09-12
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- MCP servers. `[[mcp]]` blocks configure servers over stdio or streamable HTTP, and their tools are offered to the model as `<server>__<tool>`, alongside the built-ins. A server starts on the first turn that can use it; one that fails to start is reported and skipped, and the run continues without it. Tools can be limited with `tools = ["name"]`, and a built-in keeps its name if a server ever claims one. The client is the official `rmcp` SDK.
|
|
10
|
+
- `trust` per server decides the gate: `prompt`, the default, shows the server, the tool, and the arguments and asks with the usual `[y]es / [n]o / [a]ll / [q]uit`, where `a` covers that one server for the run; `allow` never asks; `deny` keeps the server's tools from the model. `[safety] confirm_mcp` turns the prompting off wholesale, and `--yes` includes it.
|
|
11
|
+
- `rehydrate` per server decides what an MCP server receives. It is off by default, so secrets found in your files leave as placeholders rather than values, and the confirmation shows exactly what will be sent. The provider API key is refused in arguments either way, as it is for every tool.
|
|
12
|
+
- Tool descriptions and results from a server reach the model inside markers naming the server and saying the text is data, so a server cannot instruct the model or change airlok's own confirmations and deny list.
|
|
13
|
+
- Secrets for a server come from commands: `env_cmd` and `header_cmd` take their values from stdout, the way `api_key_cmd` does, so no token is written in the config.
|
|
14
|
+
- `airlok mcp list` shows each server, whether it answers, and its tools. `airlok mcp call <server> <tool> '<json>'` calls one tool through the same gates. `/mcp` does the listing in a session, and `/mcp <name>` enables a disabled server for that session.
|
|
15
|
+
- Plan mode offers no MCP tools, as it offers no write tools, and starts no servers.
|
|
16
|
+
|
|
17
|
+
## [0.5.0] - 2026-09-12
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- A status line during each turn: a spinner, what airlok is doing (`thinking`, `reading src/main.rs`, `running cargo test`), the seconds so far, and the tokens used this turn, redrawn in place below the output and cleared before anything else prints. It is off when stdout is not a terminal, with `NO_COLOR`, and with `-v`.
|
|
22
|
+
- Esc cancels a running turn as Ctrl-C does: the partial reply is kept and marked interrupted. The terminal is in cbreak mode only while a turn runs, and is restored when the turn ends, before each confirmation, on a panic, and on SIGTERM. Keys typed during a turn start the next prompt.
|
|
23
|
+
- Plan mode, with `/plan` or `--plan`. The model gets only `read_file`, `glob`, `grep`, and `list_dir`, the system prompt says the rest are unavailable, and it ends its reply with a plan. `/go` runs the plan as the task in normal mode; `/plan` again leaves without running it. Per-model settings are sent unchanged in both modes.
|
|
24
|
+
- `!command` runs a command in the working directory and adds the command and its output to the conversation. `#note` appends a list item to `./AIRLOK.md`, creating it, and rebuilds the context block.
|
|
25
|
+
- `@` completes paths from the working directory, fuzzy and gitignore-aware; Tab inserts the path as plain text. Typing `/` shows the matching commands with their descriptions. A prefix runs the first match, and an unknown command suggests the closest.
|
|
26
|
+
- Alt+Enter inserts a newline, and so does Shift+Enter in terminals that send Esc then Enter for it.
|
|
27
|
+
- Write confirmations show line numbers, syntax highlighting, and +/- gutters, and page at 40 lines; `v` shows the rest.
|
|
28
|
+
- A dim footer after each turn: model, context %, session id.
|
|
29
|
+
- `[models."<id>"]` config sections with `reasoning_effort`, sent by the openai provider only for that model id. Azure's `gpt-6-astra` needs `reasoning_effort = "none"` to use tools on Chat Completions; when a provider rejects its reasoning effort, airlok names the setting to add. `/model` shows the configured effort.
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- The REPL starts with one line: version, model, directory, notes such as a resumed session or confirmations being off, and `/help for commands`.
|
|
34
|
+
- Ctrl-C at an empty prompt does nothing; it used to print a hint.
|
|
35
|
+
- Runs of read-only tool calls show on the status line and end with one `read N files` line, in place of the updating `reading N files...` line.
|
|
36
|
+
- Slash commands are listed harmless and frequent first, since a prefix now runs the first match.
|
|
37
|
+
|
|
5
38
|
## [0.4.1] - 2026-09-11
|
|
6
39
|
|
|
7
40
|
### Added
|
package/README.md
CHANGED
|
@@ -5,18 +5,23 @@ airlok is a terminal coding agent with one differentiator: it is a privacy airlo
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
|
-
|
|
9
|
-
# or
|
|
10
|
-
npm install -g airlok
|
|
8
|
+
curl -fsSL https://airlok.dev/install.sh | sh
|
|
11
9
|
# or
|
|
12
10
|
brew install airlok-dev/tap/airlok
|
|
11
|
+
# or
|
|
12
|
+
npm install -g airlok
|
|
13
|
+
# or, built from source
|
|
14
|
+
cargo install --git https://github.com/airlok-dev/airlok airlok --locked
|
|
13
15
|
```
|
|
14
16
|
|
|
17
|
+
`install.sh` installs the latest release for macOS or Linux. It checks the release's installer against the sha256 digest GitHub publishes for it, then runs it; set `AIRLOK_NO_MODIFY_PATH=1` to leave your shell profile alone.
|
|
18
|
+
|
|
15
19
|
## Usage
|
|
16
20
|
|
|
17
21
|
```sh
|
|
18
22
|
airlok # interactive session in the current directory
|
|
19
23
|
airlok "create hello.txt containing hello" # run one task in the current directory
|
|
24
|
+
airlok --plan # start in plan mode: the model researches, then proposes a plan
|
|
20
25
|
airlok --resume # continue the latest saved session here (REPL, or with a task)
|
|
21
26
|
airlok --resume=<id> "summarise what we did" # continue a particular one
|
|
22
27
|
airlok sessions # list saved sessions for this directory
|
|
@@ -30,30 +35,64 @@ airlok config init # write a commented config to the
|
|
|
30
35
|
airlok config show # print the effective config and the key source
|
|
31
36
|
airlok context # print the context block sent with the system prompt, after redaction
|
|
32
37
|
airlok redactions # list what the redactor detects and how each kind is treated
|
|
38
|
+
airlok mcp list # the configured MCP servers, whether they answer, and their tools
|
|
33
39
|
```
|
|
34
40
|
|
|
35
|
-
Every `write_file` and `edit_file` call shows a
|
|
41
|
+
Every `write_file` and `edit_file` call shows a diff with line numbers, three lines of context, +/- gutters, and syntax highlighting, and asks `Apply? [y]es / [n]o / [a]ll / [q]uit`. A diff longer than 40 lines stops at `... N more lines, [v] to view all`; `v` prints the rest and asks again. Every `bash` call that is not on the allow list shows the command and asks the same way. `y` applies this one, `n` sends a rejection back to the model so it can adapt, `a` approves the rest of that kind for the run, and `q` aborts the run with a non-zero exit. Prompts are read from the terminal, not stdin, so piped input still works; without a terminal, pass `--yes` or turn the confirmations off in the config.
|
|
36
42
|
|
|
37
|
-
Model output is rendered as markdown (headings, emphasis, lists, tables, highlighted code) when stdout is a terminal; set `NO_COLOR` or redirect stdout for plain text. Runs of read-only tool calls
|
|
43
|
+
Model output is rendered as markdown (headings, emphasis, lists, tables, highlighted code) when stdout is a terminal; set `NO_COLOR` or redirect stdout for plain text. Runs of read-only tool calls show on the status line and end with one `read N files` line; `-v` shows every call.
|
|
38
44
|
|
|
39
|
-
###
|
|
45
|
+
### Interactive sessions
|
|
46
|
+
|
|
47
|
+
`airlok` with no task opens a session. It starts with one line (version, model, directory, `/help for commands`), the prompt shows the model and the directory (`gpt-5.5 airlok> `), each line you send is one turn, and the conversation carries across turns. After each turn a dim footer shows the model, how full the context is, and the session id.
|
|
48
|
+
|
|
49
|
+
While a turn runs, a status line below the output shows a spinner, what airlok is doing (`thinking`, `reading src/main.rs`, `running cargo test`), the seconds so far, and the tokens used this turn. It is cleared before anything else prints. It is off when stdout is not a terminal, when `NO_COLOR` is set, and with `-v`, whose logs share the terminal.
|
|
40
50
|
|
|
41
|
-
|
|
51
|
+
| Key | What it does |
|
|
52
|
+
|---|---|
|
|
53
|
+
| Enter | send the line |
|
|
54
|
+
| Alt+Enter | start a new line in the same message. Shift+Enter does the same in terminals that send Esc then Enter for it; most send a plain Enter |
|
|
55
|
+
| Tab | take the first `/` command or `@` path on offer; Tab again cycles through the rest |
|
|
56
|
+
| Right arrow | take the rest of the `/` command shown after the cursor |
|
|
57
|
+
| Esc or Ctrl-C | during a turn, cancel it |
|
|
58
|
+
| Ctrl-C | at the prompt, clear the line; on an empty line it does nothing |
|
|
59
|
+
| Ctrl-D | save and quit |
|
|
60
|
+
| Ctrl-R | search this run's history; Up and Down step through it |
|
|
61
|
+
|
|
62
|
+
A cancelled turn keeps the text streamed so far in the history, marked as interrupted; tool calls that had not run are dropped, and you get the prompt back. Keys typed while a turn runs are kept and start the next prompt.
|
|
63
|
+
|
|
64
|
+
| Input | What it does |
|
|
65
|
+
|---|---|
|
|
66
|
+
| `/` at the start | a slash command. A menu of the matching commands and what they do shows as you type. A prefix runs the first match in the menu (`/co` is `/cost`); an unknown name gets the closest command suggested |
|
|
67
|
+
| `@` anywhere | `@` and part of a path offers matching files and directories from the working directory, fuzzy and gitignore-aware. Tab inserts the path as plain text, without the `@` |
|
|
68
|
+
| `!` at the start | runs the rest with `sh -c` in the working directory and prints the output. The command and its output go into the conversation for the model's next turn. `bash_timeout_secs` applies, and the command cannot read input |
|
|
69
|
+
| `#` at the start | appends the rest to `./AIRLOK.md` as a list item, creating the file, and rebuilds the context block so it applies from the next turn. A new `AIRLOK.md` takes precedence over a `CLAUDE.md` or `AGENTS.md` beside it, and the confirmation says so. Instructions are read from the repository root, so from a subdirectory the note lands in a file the context block does not read |
|
|
42
70
|
|
|
43
71
|
| Command | What it does |
|
|
44
72
|
|---|---|
|
|
45
73
|
| `/help` | list the commands |
|
|
46
74
|
| `/model [<id>]` | show the model, or use `<id>` for the rest of the session; not validated, the provider rejects a bad id on the next turn |
|
|
75
|
+
| `/mcp` | list the MCP servers and their tools; `/mcp <name>` enables a disabled one for this session |
|
|
76
|
+
| `/plan` | turn plan mode on or off |
|
|
77
|
+
| `/go` | carry out the plan from plan mode, back in normal mode |
|
|
47
78
|
| `/provider [<name>]` | show the provider, or switch to `anthropic` or `openai` if a key is available for it; says which key is missing otherwise |
|
|
48
|
-
| `/clear` | start a new session; the current one stays saved |
|
|
49
|
-
| `/compact` | summarise older turns to free context |
|
|
50
79
|
| `/cost` | tokens used so far, and whether they are estimates |
|
|
51
|
-
| `/
|
|
80
|
+
| `/compact` | summarise older turns to free context |
|
|
52
81
|
| `/config` | the effective configuration |
|
|
53
|
-
| `/
|
|
82
|
+
| `/redactions` | what was redacted before leaving this machine (kind and length only) |
|
|
83
|
+
| `/clear` | start a new session; the current one stays saved |
|
|
84
|
+
| `/exit` | save and quit; `/quit` works too |
|
|
85
|
+
|
|
86
|
+
#### Plan mode
|
|
87
|
+
|
|
88
|
+
`/plan`, or `--plan` at startup, switches to plan mode, and the prompt shows `[plan]`. The model gets only the read-only tools (`read_file`, `glob`, `grep`, `list_dir`): `write_file`, `edit_file`, and `bash` are left out of the request, the system prompt says they are unavailable, and a call to one of them anyway is refused. The model researches the task and ends its reply with a plan. `/go` leaves plan mode and sends that plan back as the task, with every tool available again. `/plan` a second time leaves without running anything. Per-model settings such as `reasoning_effort` are sent the same way in both modes. Plan mode is not saved with the session, so `--resume` starts in normal mode unless `--plan` is given.
|
|
89
|
+
|
|
90
|
+
#### Providers in a session
|
|
54
91
|
|
|
55
92
|
`[provider]` settings (`api_key_cmd`, `api_key_env`, `base_url`) belong to the configured provider. After `/provider` switches to the other one, it runs on that provider's default model and its default key variables (`ANTHROPIC_API_KEY`; `AZURE_OPENAI_API_KEY` or `OPENAI_API_KEY`); switching back restores the configured one. Both changes are written to the session file, and `--resume` continues on the session's last model when it used the configured provider and `--model` is not given.
|
|
56
93
|
|
|
94
|
+
### Sessions
|
|
95
|
+
|
|
57
96
|
Every turn, one-shot or interactive, is saved under `$XDG_DATA_HOME/airlok/sessions/<hash of the directory>/` (`~/.local/share/airlok` by default). `airlok --resume` picks up the latest session for the directory; it rebuilds the context block, so the model sees the current tree and git state, and adds a note with the resume time. `airlok sessions` lists what is there.
|
|
58
97
|
|
|
59
98
|
A session file holds the plaintext conversation and the real value behind every placeholder, because that is what rehydration on resume needs. That is why the file is written with mode 0600 in a 0700 directory, and why the provider API key is the one thing left out: it is stored blank and re-read from your config on every start.
|
|
@@ -74,6 +113,49 @@ Long sessions are compacted. Once the last request used more than `compact_at` (
|
|
|
74
113
|
|
|
75
114
|
Tool results over 50 KiB are cut with a marker telling the model to page with `read_file`'s `offset` and `limit`.
|
|
76
115
|
|
|
116
|
+
## MCP servers
|
|
117
|
+
|
|
118
|
+
airlok can offer the model tools from external [MCP](https://modelcontextprotocol.io) servers. They are configured with `[[mcp]]` blocks, which merge across the user and project files like every other setting:
|
|
119
|
+
|
|
120
|
+
```toml
|
|
121
|
+
[[mcp]]
|
|
122
|
+
name = "files" # its tools reach the model as files__<tool>
|
|
123
|
+
command = "npx"
|
|
124
|
+
args = ["-y", "@modelcontextprotocol/server-filesystem", "."]
|
|
125
|
+
# env = { NODE_ENV = "production" }
|
|
126
|
+
# env_cmd = { TOKEN = "op read op://vault/item/token" } # stdout is the value
|
|
127
|
+
|
|
128
|
+
[[mcp]]
|
|
129
|
+
name = "docs"
|
|
130
|
+
transport = "http"
|
|
131
|
+
url = "https://example.com/mcp"
|
|
132
|
+
headers = { Accept = "application/json" }
|
|
133
|
+
header_cmd = { Authorization = "printf 'Bearer %s' $(cat ~/.docs-token)" }
|
|
134
|
+
tools = ["search"] # or "all", the default
|
|
135
|
+
trust = "prompt" # "prompt" (default), "allow", "deny"
|
|
136
|
+
rehydrate = false # the default
|
|
137
|
+
timeout_secs = 30 # starting the server, and every call
|
|
138
|
+
enabled = true
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
`env_cmd` and `header_cmd` take their values from a command's stdout, the way `api_key_cmd` does, so a token never sits in the file. `airlok config show` prints the command, never what it produced.
|
|
142
|
+
|
|
143
|
+
Servers start on the first turn that can use them, not when airlok starts. One that fails to start prints a line and is skipped, and the run continues without it. Tools are named `<server>__<tool>`; a built-in keeps its name if a server ever claims one.
|
|
144
|
+
|
|
145
|
+
`trust` decides the gate. The default, `prompt`, shows the server, the tool, and the arguments, and asks with the same `[y]es / [n]o / [a]ll / [q]uit` prompt as a shell command, where `a` approves that one server for the rest of the run. `allow` never asks. `deny` keeps a server's tools from the model, while `airlok mcp list` still shows them.
|
|
146
|
+
|
|
147
|
+
`rehydrate` decides what the server receives. By default airlok sends placeholders: a secret found in your files leaves as `<<SECRET_1>>` rather than as the value, because an MCP server is a third party in the same way the model is, and the confirmation prompt shows you exactly what will be sent. Set `rehydrate = true` for a server that genuinely needs the value. The provider API key is refused either way, as it is for every tool.
|
|
148
|
+
|
|
149
|
+
A server is untrusted input. Its tool descriptions and its results reach the model inside markers saying they are data from that server, so a description reading "ignore previous instructions" is quoted text and nothing more. Nothing a server sends changes the deny list, the confirmations, or anything else about how airlok behaves.
|
|
150
|
+
|
|
151
|
+
| Command | What it does |
|
|
152
|
+
|---|---|
|
|
153
|
+
| `airlok mcp list` | every configured server, whether it answers, and the tools it offers |
|
|
154
|
+
| `airlok mcp call <server> <tool> '<json>'` | call one tool with the same gates, for debugging |
|
|
155
|
+
| `/mcp` | in a session, the same list; `/mcp <name>` enables a disabled server for this session |
|
|
156
|
+
|
|
157
|
+
Plan mode offers no MCP tools, just as it offers no write tools, and it starts no servers.
|
|
158
|
+
|
|
77
159
|
## Context and AIRLOK.md
|
|
78
160
|
|
|
79
161
|
Every run starts with a context block in the system prompt: the working directory, OS and shell, the git branch with `git status --short` and the last five commit subjects, a gitignore-aware file tree (four levels, at most 200 entries), and project instructions. Instructions come from `AIRLOK.md` in the repository root, or `CLAUDE.md` or `AGENTS.md` if there is no `AIRLOK.md`, followed by `~/.config/airlok/AIRLOK.md`. The block goes through the redactor like everything else, and `airlok context` prints exactly what would be sent.
|
|
@@ -123,6 +205,9 @@ max_bytes = 32768 # cap on the context block; tree is cut first, then ins
|
|
|
123
205
|
|
|
124
206
|
[redact]
|
|
125
207
|
show_secrets_in_output = false # show secrets from files in full in the terminal instead of masked
|
|
208
|
+
|
|
209
|
+
[models."gpt-6-astra"] # settings for one model id (the deployment name on Azure); none by default
|
|
210
|
+
reasoning_effort = "none" # openai only, sent as reasoning_effort; not validated
|
|
126
211
|
```
|
|
127
212
|
|
|
128
213
|
For openai, `base_url` falls back to the `OPENAI_BASE_URL` environment variable when the config does not set it.
|
|
@@ -145,6 +230,8 @@ base_url = "https://<resource>.openai.azure.com/openai/v1"
|
|
|
145
230
|
api_key_cmd = "az cognitiveservices account keys list -n <resource> -g <resource-group> --query key1 -o tsv"
|
|
146
231
|
```
|
|
147
232
|
|
|
233
|
+
Azure's `gpt-6-astra` accepts tools on Chat Completions only with reasoning off, so it needs `[models."gpt-6-astra"]` with `reasoning_effort = "none"`. Without it, airlok shows the provider's error and names that setting. The entry applies however the model is chosen, `/model gpt-6-astra` included.
|
|
234
|
+
|
|
148
235
|
With this in the user config, plain `airlok "..."` works. The key is sent in Azure's `api-key` header, chosen from the host. No shell wrapper or exported variable is needed.
|
|
149
236
|
|
|
150
237
|
## Licence
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"hasInstallScript": true,
|
|
20
20
|
"license": "MIT OR Apache-2.0",
|
|
21
21
|
"name": "airlok",
|
|
22
|
-
"version": "0.
|
|
22
|
+
"version": "0.6.0"
|
|
23
23
|
},
|
|
24
24
|
"node_modules/detect-libc": {
|
|
25
25
|
"engines": {
|
|
@@ -48,5 +48,5 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"requires": true,
|
|
51
|
-
"version": "0.
|
|
51
|
+
"version": "0.6.0"
|
|
52
52
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"artifactDownloadUrls": [
|
|
3
|
-
"https://github.com/airlok-dev/airlok/releases/download/v0.
|
|
3
|
+
"https://github.com/airlok-dev/airlok/releases/download/v0.6.0"
|
|
4
4
|
],
|
|
5
5
|
"bin": {
|
|
6
6
|
"airlok": "run-airlok.js"
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"zipExt": ".tar.xz"
|
|
61
61
|
}
|
|
62
62
|
},
|
|
63
|
-
"version": "0.
|
|
63
|
+
"version": "0.6.0",
|
|
64
64
|
"volta": {
|
|
65
65
|
"node": "18.14.1",
|
|
66
66
|
"npm": "9.5.0"
|