run 0.3.2

a.k.a. runtool: the bridge between human and AI tooling
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
# run

[![Crates.io](https://img.shields.io/crates/v/run.svg)](https://crates.io/crates/run)
[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)

**a.k.a. runtool: the bridge between human and AI tooling**

Define functions in a `Runfile`. Your AI agent discovers and executes them via the built-in MCP server. You run them from the terminal too with instant startup and tab completion. Shell, Python, Node—whatever fits the task.

```bash
# Runfile

# @desc Search the codebase for a pattern
# @shell python
# @arg pattern The regex pattern to search for
search(pattern: str) {
    import sys, os, re
    for root, _, files in os.walk('.'):
        for f in files:
            path = os.path.join(root, f)
            try:
                for i, line in enumerate(open(path), 1):
                    if re.search(sys.argv[1], line):
                        print(f"{path}:{i}: {line.rstrip()}")
            except: pass
}

# @desc Deploy to an environment
deploy(env: str, version = "latest") {
    echo "Deploying $version to $env..."
    ./scripts/deploy.sh $env $version
}

# @desc Analyze a JSON file
function analyze(file: str) {
    #!/usr/bin/env python3
    import sys, json
    with open(sys.argv[1]) as f:
        data = json.load(f)
        print(f"Found {len(data)} records")
}
```

_The syntax is designed to be similar to bash/sh, while being permissive & flexible, with added features for AI integration._

Humans can run these functions directly from the terminal:

```bash
$ run search "TODO"
$ run deploy staging
$ run analyze data.json
```

Point your AI agent at the Runfile, and it can discover and execute these tools automatically.

---

## Table of Contents

- [AI Agent Integration (MCP)]#ai-agent-integration-mcp
- [Installation]#installation
- [The Runfile Syntax]#the-runfile-syntax
  - [Basic Functions]#basic-functions
  - [Block Syntax]#block-syntax
  - [Function Signatures]#function-signatures
  - [Attributes & Polyglot Scripts]#attributes--polyglot-scripts
  - [Nested Namespaces]#nested-namespaces
  - [Function Composition]#function-composition
- [Configuration]#configuration
- [License]#license

---

## AI Agent Integration (MCP)

`run` has built-in support for the **Model Context Protocol (MCP)**, allowing AI agents like Claude to discover and execute your Runfile functions as tools.

### MCP Server Mode

Start `run` as an MCP server:

```bash
run --serve-mcp
```

Configure in your AI client (e.g., Claude Desktop `claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "my-project": {
      "command": "run",
      "args": ["--serve-mcp", "--runfile", "/path/to/your/project/"]
    }
  }
}
```

Now your AI agent can discover and call your tools automatically.

### Describing Tools for AI

Use `@desc` to describe what a function does, and declare parameters in the function signature:

```bash
# @desc Search the codebase for a regex pattern
search(pattern: str) {
    #!/usr/bin/env python3
    import sys, os, re
    for root, _, files in os.walk('.'):
        for f in files:
            path = os.path.join(root, f)
            try:
                for i, line in enumerate(open(path), 1):
                    if re.search(sys.argv[1], line):
                        print(f"{path}:{i}: {line.rstrip()}")
            except: pass
}

# @desc Deploy the application to a specific environment
deploy(environment: str, version = "latest") {
    ./scripts/deploy.sh $environment $version
}
```

Functions with `@desc` are automatically exposed as MCP tools with typed parameters.

### Inspect Tool Schema

View the generated JSON schema for all MCP-enabled functions:

```bash
run --inspect
```

This outputs the tool definitions that AI agents will see—useful for debugging and validation.

---

## Installation

### Recommended

**macOS/Linux (Homebrew)**
```bash
brew tap nihilok/tap
brew install runtool
```

**Windows (Scoop)**
```powershell
scoop bucket add nihilok https://github.com/nihilok/scoop-bucket
scoop install runtool
```

### Alternative: Cargo

Works on all platforms:

```bash
cargo install run  # or: cargo install runtool
```

### Tab Completions

Auto-detect your shell and install completions:

```bash
run --install-completion
```

Supports `bash`, `zsh`, `fish`, and `powershell`.

---


## The Runfile Syntax

`run` parses your `Runfile` to find function definitions. The syntax is designed to be familiar to anyone who has used `bash` or `sh`.

### Basic Functions

For simple, one-line commands, you don't need braces.

```bash
# Usage: run dev
dev() cargo run

# Usage: run fmt
fmt() cargo fmt
```

### Block Syntax

Use `{}` for multi-statement functions. This avoids the need for trailing backslashes.

```bash
ci() {
    echo "Running CI..."
    cargo fmt -- --check
    cargo clippy
    cargo test
    echo "Done!"
}
```

### Function Signatures

Declare parameters directly in the function signature for cleaner, self-documenting code:

```bash
# @desc Deploy to an environment
deploy(env: str, version = "latest") {
    echo "Deploying $version to $env..."
    ./scripts/deploy.sh $env $version
}

# @desc Resize an image
resize(width: int, height: int, file: str) {
    convert $file -resize ${width}x${height} output.png
}
```

**Type annotations** (`str`, `int`, `bool`) are used for:
- MCP JSON schema generation (AI agents see typed parameters)
- Self-documenting functions
- Optional runtime validation

**Default values** make parameters optional:

```bash
# version defaults to "latest" if not provided
deploy(env: str, version = "latest") { ... }
```

**Legacy positional syntax** still works for simple cases:

```bash
# Access arguments as $1, $2, $@
deploy() {
    env=$1
    version=${2:-latest}
    ./scripts/deploy.sh $env $version
}
```

**Combining with `@arg` for descriptions:**

```bash
# @desc Deploy the application
# @arg env Target environment (staging|prod)
# @arg version Version tag to deploy
deploy(env: str, version = "latest") {
    ./scripts/deploy.sh $env $version
}
```

When both signature params and `@arg` exist, the signature defines names/types/defaults, and `@arg` provides descriptions for MCP.

### Attributes & Polyglot Scripts

You can use comment attributes (`# @key value`) or shebang lines to modify function behaviour and select interpreters.

#### Platform Guards (`@os`)

Restrict functions to specific operating systems. This allows you to define platform-specific implementations of the same task.

```bash
# @os windows
clean() del /Q dist

# @os unix
clean() rm -rf dist
```

When you run `run clean`, only the variant matching your current OS will execute.

#### Interpreter Selection

There are two ways to specify a custom interpreter:

**1. Shebang detection**

The first line of your function body can be a shebang, just like standalone scripts:

```
analyze() {
    #!/usr/bin/env python
    import sys, json
    with open(sys.argv[1]) as f:
        data = json.load(f)
        print(f"Found {len(data)} records")
}
```

```
server() {
    #!/usr/bin/env node
    const port = process.argv[1] || 3000;
    require('http').createServer((req, res) => {
        res.end('Hello!');
    }).listen(port);
}
```

**2. Attribute syntax** (`@shell`):

Use comment attributes for explicit control or when you need to override a shebang:

```python
# @shell python3
calc() {
    import sys, math
    radius = float(sys.argv[1])
    print(f"Area: {math.pi * radius**2:.2f}")
}
```

**Precedence**: If both are present, `@shell` takes precedence over the shebang.

**Supported interpreters:** `python`, `python3`, `node`, `ruby`, `pwsh`, `bash`, `sh`

### Nested Namespaces

Organise related commands using colons. `run` parses `name:subname` as a single identifier.

```bash
docker:build() docker build -t app .
docker:up() docker compose up -d
docker:logs() docker compose logs -f
```

Execute them with spaces:

```bash
$ run docker build
$ run docker logs
```

### Function Composition

Functions can call other functions defined in the same Runfile, enabling task composition and code reuse without duplication.

```bash
# Base tasks
build() cargo build --release
test() cargo test
lint() cargo clippy

# Composed task that calls other functions
ci() {
    echo "Running CI pipeline..."
    lint
    test
    build
}

# Deploy depends on successful build
deploy() {
    build || exit 1
    echo "Deploying..."
    scp target/release/app server:/bin/
}
```

When you run `run ci`, all compatible functions are automatically injected into the execution scope, so you can call them directly without spawning new processes.

**Key features:**
- Functions can call sibling functions defined in the same file
- Exit codes are properly propagated (use `|| exit 1` to stop on failure)
- Works across different shells when interpreters are compatible
- Top-level variables are also available to all functions

---


## Configuration

### Shell Selection

By default, `run` uses:

- **Windows**: PowerShell (`pwsh` if available, else `powershell`)
- **Unix**: `sh`

You can override this default by setting the `RUN_SHELL` environment variable.

```bash
# Force Zsh for this command
RUN_SHELL=zsh run build

# Make it permanent for your session
export RUN_SHELL=bash
```

**Note**: The commands in your Runfile must be compatible with the configured shell, unless an explicit interpreter (e.g., `# @shell python`) is defined for that function.

### Global Runfile

Create a `~/.runfile` in your home directory to define global commands available anywhere.

```bash
# ~/.runfile

# Usage: run update
update() {
    brew update
    brew upgrade
    rustup update
}

# Usage: run clone <repo>
clone() {
    git clone "https://github.com/$1"
    cd "$(basename "$1" .git)"
}
```

If a local `./Runfile` exists, `run` looks there first. If the command isn't found locally, it falls back to `~/.runfile`.

---

## License

MIT