stynx-code-engine 3.9.0

Agentic query engine with tool-use loop
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
# stynx-code

An interactive AI coding assistant for the terminal. Multi-provider, tool-using, fast.

```
 ____   _____ __   __ _   _ __  __
/ ___| |_   _|\ \ / /| \ | |\ \/ /
\___ \   | |   \ V / |  \| | \  /
 ___) |  | |    | |  | |\  | /  \
|____/   |_|    |_|  |_| \_|/_/\_\
            c o d e
```

[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

The product is `stynx-code`. The command you'll actually type is `stynx`.

## What it is

A self-contained terminal app for getting work done with an LLM. Speaks Anthropic's Claude API natively and any OpenAI-compatible endpoint (DeepSeek, OpenAI, OpenRouter, Together, local Ollama, …) for cheap delegation. Runs an autonomous tool-using loop with bash / file edit / glob / grep / web fetch behind a permission system that prompts you only when you ask it to.

The engine runs concurrent-safe tools in parallel via `tokio::spawn`, sequential otherwise. Context is compacted automatically at 60% token threshold through a 4-stage pipeline (Auto → Micro → Session Memory → Full). File mutations are tracked on an undo stack. Provider overload triggers exponential backoff with up to 3 retries.

Highlights:

- **Modern terminal UI** — sidebar, command palette, model picker, session list, runtime theme switcher (rose-pine, catppuccin, tokyo-night, gruvbox), mouse + bracketed paste, `@`-file mentions, multi-line input, slash-command popover with descriptions, toast notifications, colorized diff renderer for file edits.
- **Intern mode** — Claude as the senior, one or more cheaper OpenAI-compat models (DeepSeek, OpenRouter, OpenAI, custom) as interns. Each intern shows up as its own `delegate_to_<name>` tool the senior can pick from, or call directly via `/intern <name> <task>`.
- **Permission-first** — Normal / Auto-accept / Plan / Bypass modes, per-tool allow rules, in-TUI confirmation modal (no terminal mode-switching).
- **Skills as slash commands** — drop a markdown file under `.claude/skills/` and it appears as `/your-skill`.
- **Sessions** — automatic per-project persistence at `~/.stynx-code/projects/<slug>/session.json`.
- **Hooks**`session-start`, `pre-tool-use`, `post-tool-use`, `stop` shell hooks for integrations.

## Install

```bash
# from source
git clone https://github.com/maulanasdqn/stynx-code
cd stynx-code
cargo install --path stynx-code

# with nix
nix develop
cargo build --release --workspace
./target/release/stynx
```

The installed binary is **`stynx`**.

## Quickstart

```bash
# interactive TUI (the main mode)
stynx

# one-shot prompt
stynx -p "find every TODO comment under src/ and group by file"

# pipe mode — read stdin as the prompt
echo "explain this file" | stynx

# JSON output for scripting
stynx --json -p "list files modified in the last commit"
```

## Configuration

### Anthropic credentials

Resolved in order:

1. macOS Keychain / Linux libsecret (the Claude Code OAuth token at `Claude Code-credentials`)
2. `~/.claude/.credentials.json`
3. `~/.claude/settings.json``auth_token`
4. `ANTHROPIC_API_KEY` env var

Run `/login` inside `stynx` for instructions.

### Interns (OpenAI-compatible providers)

You can configure any number of interns concurrently — DeepSeek and OpenRouter together, mixed providers, whatever you want. Each becomes its own `delegate_to_<name>` tool the senior model can pick from.

**Option 1 — `.env` shorthand** (zero settings.json edits):

```bash
# DeepSeek (legacy single-intern shortcut)
DEEPSEEK_API_KEY=sk-...
DEEPSEEK_MODEL=deepseek-chat                  # optional

# OpenRouter — declare multiple interns via name:model pairs
OPENROUTER_API_KEY=sk-or-...
OPENROUTER_INTERNS=qwen-coder:qwen/qwen3-coder,haiku:anthropic/claude-haiku-4.5,kimi:moonshotai/kimi-k2,gemini-flash:google/gemini-2.5-flash,mimo:mimo-v2.5-pro

# Qwen / Alibaba DashScope (auto-registers a "qwen" intern; multi via QWEN_INTERNS)
QWEN_API_KEY=sk-...
QWEN_MODEL=qwen-plus                         # optional; qwen-max / qwen-turbo / qwen3-coder-plus
# QWEN_INTERNS=qwen-max:qwen-max,qwen-coder:qwen3-coder-plus
```

**Option 2 — `interns` array in `settings.json`** (full control):

```json
{
  "interns": [
    {
      "name": "deepseek",
      "provider": "deepseek",
      "model": "deepseek-chat",
      "description": "Cheap general-purpose intern; good for boilerplate and mechanical refactors."
    },
    {
      "name": "qwen-coder",
      "provider": "openrouter",
      "model": "qwen/qwen3-coder",
      "description": "Coding specialist; strong at multi-file edits."
    },
    {
      "name": "qwen-max",
      "provider": "qwen",
      "model": "qwen-max",
      "description": "Complex features, architecture, hard bugs."
    },
    {
      "name": "haiku",
      "provider": "openrouter",
      "model": "anthropic/claude-haiku-4.5",
      "description": "Fast, well-balanced general intern."
    },
    {
      "name": "kimi",
      "provider": "openrouter",
      "model": "moonshotai/kimi-k2",
      "description": "Strong reasoning and long-context tasks."
    },
    {
      "name": "gemini-flash",
      "provider": "openrouter",
      "model": "google/gemini-2.5-flash",
      "description": "Boilerplate, simple edits, formatting."
    },
    {
      "name": "mimo",
      "provider": "openrouter",
      "model": "mimo-v2.5-pro",
      "description": "Mechanical edits, grep/glob grunt work."
    },
    {
      "name": "local",
      "provider": "custom",
      "base_url": "http://localhost:11434/v1",
      "api_key_env": "OLLAMA_API_KEY",
      "model": "qwen2.5-coder:32b",
      "description": "Local Ollama intern; free but slower."
    }
  ]
}
```

`provider` shorthand resolves to:

| provider     | base_url                                                       | default api key env     |
| ------------ | -------------------------------------------------------------- | ----------------------- |
| `deepseek`   | `https://api.deepseek.com/v1`                                  | `DEEPSEEK_API_KEY`      |
| `openrouter` | `https://openrouter.ai/api/v1`                                 | `OPENROUTER_API_KEY`    |
| `openai`     | `https://api.openai.com/v1`                                    | `OPENAI_API_KEY`        |
| `qwen`       | `https://dashscope-intl.aliyuncs.com/compatible-mode/v1`       | `QWEN_API_KEY`          |
| `custom`     | (set via `base_url`)                                           | (set via `api_key_env`) |

Copy `.env.example` to `.env` and fill in your API keys. Add `.env` to your `.gitignore` — `stynx` autoloads it on startup.

At launch you'll see one `· intern ready: <name> (<provider> / <model>)` line per intern that successfully resolved. Interns missing their API key are silently skipped (a `WARN` is emitted to logs).

### Settings file

`~/.claude/settings.json` (global) and project-local settings get merged. Schema:

```json
{
  "model": "claude-sonnet-4-6",
  "max_turns": 200,
  "max_tokens": 8192,
  "effort": "medium",
  "commit_attribution": false,
  "interns": [
    { "name": "deepseek", "provider": "deepseek", "model": "deepseek-chat" }
  ],
  "permissions": {
    "allow": ["bash:cargo *", "read:*"],
    "deny":  ["bash:rm -rf*"]
  },
  "hooks": {
    "SessionStart":  [{ "command": "scripts/session-start.sh" }],
    "PreToolUse":    [{ "matcher": "file_write", "command": "scripts/check.sh" }],
    "PostToolUse":   [{ "command": "scripts/audit.sh" }]
  }
}
```

`commit_attribution: false` (the default) tells the assistant to omit AI/assistant attribution from commits — no `Co-Authored-By:` trailers, no `🤖 Generated with …`. Set it to `true` if you want attribution back.

Run `/config` inside the session to see the merged view.

## Daily flow

| Key            | Action                                       |
|----------------|----------------------------------------------|
| `Enter`        | Submit message                               |
| `Shift+Enter`  | Newline (also `Alt+Enter`, `Ctrl+J`)         |
| `Esc`          | Interrupt streaming · or vim normal mode     |
| `Ctrl+P`       | Command palette                              |
| `Ctrl+S`       | Session list                                 |
| `Ctrl+M`       | Switch model                                 |
| `Ctrl+B`       | Toggle sidebar                               |
| `Ctrl+T`       | Toggle tool block details                    |
| `Shift+Tab`    | Cycle permission mode                        |
| `Shift+↑/↓`    | Scroll messages by line (works while typing) |
| `PgUp/PgDn`    | Scroll by page                               |
| `Ctrl+Alt+U/D` | Scroll messages by half page                 |
| `@`            | File-mention picker                          |
| `/`            | Slash-command popover                        |
| `Ctrl+C`       | Quit                                         |

Mouse scroll wheel works in any terminal with mouse capture.

## Slash commands

```
/help                 show this help
/status               git status
/version              show stynx version
/quit, /exit          exit

/model [name]         switch model
/fast                 toggle fast (haiku) mode
/effort low|medium|high|max    set effort
/think                toggle extended thinking

/mode                 cycle permission mode
/plan [task]          toggle plan mode (read-only tools only)

/compact              summarize older turns to free up context
/cost                 show token usage and cost
/usage                show plan limits (OAuth only)

/diff                 show git diff
/review               review the current diff
/commit               generate a commit message
/init                 create/update CLAUDE.md
/memory               show CLAUDE.md

/add <path>           pin a file into every message
/files                list pinned files
/skills               list available skills
/intern <task>             hand work to the first intern
/intern <name> <task>      hand work to a specific intern by name

/session              list / load sessions
/rewind [n]           remove last n exchanges
/undo [n]             restore last n file edits
/export               write transcript to disk
/copy                 copy last response to clipboard

/config               show merged config
/permissions          show allow / deny rules
/login, /logout       credential management

!<command>            run a shell command without leaving the TUI
```

## Intern mode in 30 seconds

```bash
# .env — quick mix of providers
DEEPSEEK_API_KEY=sk-...
OPENROUTER_API_KEY=sk-or-...
OPENROUTER_INTERNS=qwen-coder:qwen/qwen3-coder,haiku:anthropic/claude-haiku-4.5,kimi:moonshotai/kimi-k2,gemini-flash:google/gemini-2.5-flash,mimo:mimo-v2.5-pro
```

```
> refactor every println! in src/foo.rs to tracing::info!; hand it to the qwen-coder intern
```

The senior (Claude) calls `delegate_to_qwen_coder` (or any other registered intern) with a focused task description. The intern runs with a restricted toolset — bash, read, file_write, file_edit, glob, grep — and cannot spawn further sub-agents. It returns `Summary / Files changed / Output`. The senior reviews and integrates.

Direct invocation:

```
/intern                                                # show available interns
/intern list every public function and one-line them    # picks the first intern
/intern qwen-coder write a unit test for util::strip_ansi
```

When multiple interns are configured, the senior picks based on each tool's `description` — so write descriptions that say what each intern is good at (speed, cost, specialty). The intern's transcript is shown as a system message in the conversation, including any tool calls it made.

## Terminal sessions

`bash` is a **persistent shell**, not a series of disposable subshells. The first call boots a long-lived `bash --norc --noprofile` process; every subsequent call runs through the same shell, so `cd`, `export`, sourced files, and function definitions survive across calls.

For long-running processes (dev servers, watchers, log tails), pass `background: true`:

```jsonc
// start a dev server
{"command": "bun run dev", "background": true}        // → "started background process 'bg1'"

// check on it (only new output since last read)
{"status": "bg1"}                                     // → "[running, 12s]\n..."

// read everything it has emitted
{"status": "bg1", "full": true}

// see all background processes
{"list": true}

// stop it
{"kill": "bg1"}
```

Foreground commands have a 120s default timeout (override with `"timeout": <secs>`). If you need longer, use `background: true` instead — a timed-out foreground command leaves the persistent shell in an unknown state.

## Hooks

Each hook command receives the relevant JSON on stdin and writes text to stdout (mixed into the conversation as a system message). Configure under `hooks` in `settings.json`:

- `SessionStart` — runs once when a session begins
- `PreToolUse` — runs before each tool invocation; can short-circuit by exiting non-zero
- `PostToolUse` — runs after each tool invocation; can post-process the result
- `Stop` — runs when the assistant finishes a turn

Each can have an optional `matcher` (substring against tool name) and a required `command`.

## Available Tools

The engine provides a rich set of tools categorized as follows:

- **File I/O**: `read`, `file_write`, `file_edit`
- **Discovery**: `glob`, `grep`
- **Shell**: `bash` (persistent session)
- **Web**: `web_fetch`, `web_search`
- **Tasks**: `todo_read`, `todo_write`
- **Background tasks**: `task_create`, `task_get`, `task_list`, `task_stop`, `task_update`, `task_output`
- **Scheduling**: `cron_create`, `cron_delete`
- **Interaction**: `ask_user_question`
- **Integration**: MCP (dynamic tools loaded per server), `lsp`
- **Skills**: `skill` invocation
- **Misc**: `notebook_edit`, `repl`, `send_message`, `sleep`, `synthetic_output`, `plan_mode`

## How it works

### Engine loop

The QueryEngine runs up to N turns (default 200, configurable via `max_turns`). Each turn:

1. Sends the conversation to the provider (with read-only tools only in plan mode)
2. Streams the response and extracts tool calls
3. Runs pre-tool hooks for each tool
4. Executes concurrent-safe tools in parallel (via tokio::spawn); non-concurrent tools sequentially
5. Runs post-tool hooks and collects results
6. Adds results back to conversation
7. Repeats if the model calls tools, or returns if the model stopped

At 60% token threshold, the engine automatically invokes the compactor to free up context.

### Context compaction (4-stage pipeline)

When tokens exceed 60% of the limit:

1. **Auto** — checks whether compaction is needed based on the token threshold
2. **Micro** — truncates oversized individual tool results in-place
3. **Session Memory** — extracts key memories before discarding content
4. **Full** — sends older turns to the LLM for summarization, keeping the last 1–2 turns intact

The compactor preserves key decisions and context needed to continue.

### File edit undo stack

Every file write/edit is tracked on an undo stack. Use `/undo [n]` to restore the last n file mutations.

## Architecture

The project is structured as a 19-crate Rust workspace, each following Clean Architecture principles with `domain/`, `application/`, and `infrastructure/` layers.

- `stynx-code-errors`: Defines common application error types and results.
- `stynx-code-types`: Provides core traits and structs for tools, providers, messages, and permissions.
- `stynx-code-tools`: Implements over 50 tools, including file operations, shell commands, and task management.
- `stynx-code-provider`: Handles integrations with Anthropic SSE streaming and OpenAI-compatible providers.
- `stynx-code-engine`: The core query engine, managing the multi-turn agentic loop, tool execution, context compaction, hooks, and undo stack.
- `stynx-code-server`: Provides an Axum HTTP API for headless operation.
- `stynx-code`: The main executable, handling CLI parsing and orchestrating the TUI and agent/intern interactions.
- `stynx-code-auth`: Manages OAuth PKCE and API key credential resolution.
- `stynx-code-permission`: Implements interactive and configuration-driven permission gating for tools.
- `stynx-code-commands`: Handles slash-command expansion within the TUI.
- `stynx-code-memory`: Manages per-project session persistence.
- `stynx-code-config`: Loads and merges global and project-specific settings, including hook configurations.
- `stynx-code-services`: Provides analytics, an LSP bridge, rate limiting, token estimation, diagnostics, and notifications.
- `stynx-code-compact`: Implements the 4-stage conversation summarization pipeline.
- `stynx-code-coordinator`: Facilitates multi-agent communication and task management via a message bus.
- `stynx-code-bridge`: Handles inter-crate communication.
- `stynx-code-plugins`: Manages the lifecycle of plugins.
- `stynx-code-skills`: Loads both bundled and user-defined skills.
- `stynx-code-tui`: Implements the `ratatui` terminal user interface, including state management and rendering.

## License

MIT