agent-sh 0.15.0 → 0.15.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/docs/README.md +14 -0
- package/docs/agent.md +398 -0
- package/docs/architecture.md +196 -0
- package/docs/context-management.md +200 -0
- package/docs/extensions.md +951 -0
- package/docs/library.md +84 -0
- package/docs/troubleshooting.md +65 -0
- package/docs/tui-composition.md +294 -0
- package/docs/usage.md +306 -0
- package/examples/extensions/ash-scheme/package.json +1 -1
- package/examples/extensions/ashi/EXTENDING.md +2 -2
- package/examples/extensions/ashi/README.md +2 -2
- package/examples/extensions/ashi/docs/ui-surface-protocol.md +1 -1
- package/examples/extensions/ashi/package.json +5 -3
- package/examples/extensions/ashi/src/cli.ts +6 -5
- package/examples/extensions/ashi/src/renderer.ts +22 -2
- package/examples/extensions/ashi/src/renderers/pi-tui/tool-group.ts +5 -8
- package/examples/extensions/ashi-ink/package.json +2 -2
- package/examples/extensions/claude-code-bridge/package.json +1 -1
- package/examples/extensions/opencode-bridge/package.json +1 -1
- package/package.json +3 -1
- package/src/agent/agent-loop.ts +1563 -0
- package/src/agent/entry-format.ts +19 -0
- package/src/agent/events.ts +151 -0
- package/src/agent/extensions/rolling-history/constants.ts +1 -0
- package/src/agent/extensions/rolling-history/index.ts +202 -0
- package/src/agent/extensions/rolling-history/recall.ts +131 -0
- package/src/agent/extensions/rolling-history/strategy.ts +404 -0
- package/src/agent/host-types.ts +192 -0
- package/src/agent/index.ts +591 -0
- package/src/agent/live-view.ts +279 -0
- package/src/agent/llm-client.ts +111 -0
- package/src/agent/llm-facade.ts +43 -0
- package/src/agent/normalize-args.ts +61 -0
- package/src/agent/nuclear-form.ts +382 -0
- package/src/agent/providers/deepseek.ts +39 -0
- package/src/agent/providers/ollama.ts +92 -0
- package/src/agent/providers/openai-compatible.ts +36 -0
- package/src/agent/providers/openai.ts +52 -0
- package/src/agent/providers/opencode.ts +142 -0
- package/src/agent/providers/openrouter.ts +105 -0
- package/src/agent/providers/zai-coding-plan.ts +33 -0
- package/src/agent/session-store.ts +336 -0
- package/src/agent/skills.ts +228 -0
- package/src/agent/store.ts +310 -0
- package/src/agent/subagent.ts +305 -0
- package/src/agent/system-prompt.ts +151 -0
- package/src/agent/token-budget.ts +12 -0
- package/src/agent/tool-protocol.ts +722 -0
- package/src/agent/tool-registry.ts +66 -0
- package/src/agent/tools/bash.ts +95 -0
- package/src/agent/tools/edit-file.ts +154 -0
- package/src/agent/tools/expand-home.ts +7 -0
- package/src/agent/tools/glob.ts +108 -0
- package/src/agent/tools/grep.ts +228 -0
- package/src/agent/tools/list-skills.ts +37 -0
- package/src/agent/tools/ls.ts +81 -0
- package/src/agent/tools/pwsh.ts +140 -0
- package/src/agent/tools/read-file.ts +164 -0
- package/src/agent/tools/write-file.ts +72 -0
- package/src/agent/types.ts +149 -0
- package/src/cli/args.ts +91 -0
- package/src/cli/auth/cli.ts +244 -0
- package/src/cli/auth/discover.ts +52 -0
- package/src/cli/auth/keys.ts +143 -0
- package/src/cli/index.ts +295 -0
- package/src/cli/init.ts +74 -0
- package/src/cli/install.ts +439 -0
- package/src/cli/shell-env.ts +68 -0
- package/src/cli/subcommands.ts +24 -0
- package/src/core/event-bus.ts +252 -0
- package/src/core/extension-loader.ts +347 -0
- package/src/core/index.ts +152 -0
- package/src/core/settings.ts +398 -0
- package/src/core/types.ts +61 -0
- package/src/extensions/file-autocomplete.ts +71 -0
- package/src/extensions/index.ts +38 -0
- package/src/extensions/slash-commands/events.ts +14 -0
- package/src/extensions/slash-commands/index.ts +269 -0
- package/src/shell/events.ts +73 -0
- package/src/shell/host-types.ts +150 -0
- package/src/shell/index.ts +159 -0
- package/src/shell/input-handler.ts +505 -0
- package/src/shell/output-parser.ts +156 -0
- package/src/shell/shell-context.ts +193 -0
- package/src/shell/shell.ts +414 -0
- package/src/shell/strategies/bash.ts +83 -0
- package/src/shell/strategies/fish.ts +77 -0
- package/src/shell/strategies/index.ts +24 -0
- package/src/shell/strategies/types.ts +64 -0
- package/src/shell/strategies/zsh.ts +92 -0
- package/src/shell/terminal.ts +124 -0
- package/src/shell/tui-input-view.ts +222 -0
- package/src/shell/tui-renderer.ts +1126 -0
- package/src/utils/ansi.ts +140 -0
- package/src/utils/box-frame.ts +138 -0
- package/src/utils/compositor.ts +157 -0
- package/src/utils/diff-renderer.ts +829 -0
- package/src/utils/diff.ts +244 -0
- package/src/utils/executor.ts +305 -0
- package/src/utils/file-watcher.ts +110 -0
- package/src/utils/floating-panel.ts +1160 -0
- package/src/utils/handler-registry.ts +110 -0
- package/src/utils/line-editor.ts +636 -0
- package/src/utils/markdown.ts +437 -0
- package/src/utils/message-utils.ts +113 -0
- package/src/utils/package-version.ts +12 -0
- package/src/utils/palette.ts +64 -0
- package/src/utils/ref-counter.ts +9 -0
- package/src/utils/ripgrep-path.ts +17 -0
- package/src/utils/shell-output-spill.ts +76 -0
- package/src/utils/stream-transform.ts +292 -0
- package/src/utils/terminal-buffer.ts +213 -0
- package/src/utils/tool-display.ts +315 -0
- package/src/utils/tool-interactive.ts +71 -0
- package/src/utils/tty.ts +14 -0
package/docs/usage.md
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# Usage Guide
|
|
2
|
+
|
|
3
|
+
## Running agent-sh
|
|
4
|
+
|
|
5
|
+
The simplest way to run agent-sh — just provide an API key and model:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Using environment variables
|
|
9
|
+
OPENAI_API_KEY="your-key" agent-sh --model gpt-4o
|
|
10
|
+
|
|
11
|
+
# Using CLI flags
|
|
12
|
+
agent-sh --api-key "your-key" --base-url http://localhost:11434/v1 --model llama3
|
|
13
|
+
|
|
14
|
+
# Using npx
|
|
15
|
+
npx agent-sh --api-key "$KEY" --model gpt-4o
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Environment variables `OPENAI_API_KEY` and `OPENAI_BASE_URL` are supported as alternatives to CLI flags.
|
|
19
|
+
|
|
20
|
+
### Other Options
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Use a different shell
|
|
24
|
+
agent-sh --shell /bin/zsh
|
|
25
|
+
|
|
26
|
+
# Launch a non-default agent backend (per-session override; doesn't touch settings)
|
|
27
|
+
agent-sh --backend pi
|
|
28
|
+
|
|
29
|
+
# Development mode (no build step)
|
|
30
|
+
npm run dev
|
|
31
|
+
|
|
32
|
+
# Debug mode
|
|
33
|
+
DEBUG=1 agent-sh --api-key "$KEY" --model gpt-4o
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Subcommands
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
agent-sh init # scaffold ~/.agent-sh/ (settings, examples, AGENTS.md)
|
|
40
|
+
agent-sh install <name> # install a bundled extension (e.g. agent-sh install pi-bridge)
|
|
41
|
+
agent-sh install ./path/to/ext # install from a local path
|
|
42
|
+
agent-sh uninstall <name> # remove an installed extension
|
|
43
|
+
agent-sh list # show extensions discovered from ~/.agent-sh/extensions/ and settings.json
|
|
44
|
+
agent-sh auth login [provider] # store an API key; provider list is discovered from built-ins, settings.json, and any extension that calls ctx.agent.providers.register
|
|
45
|
+
agent-sh auth logout <provider> # remove a stored key
|
|
46
|
+
agent-sh auth list # show configured providers and their key source ("(no auth required)" for local-daemon providers registered with noAuth: true)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Keys stored via `auth` live in `~/.agent-sh/keys.json` (chmod 0600). Resolution order when launching is `settings.json` → `keys.json` → env var, so explicit configuration always wins over the auth store.
|
|
50
|
+
|
|
51
|
+
Any provider you declare under `providers` in `settings.json` is also accepted by `auth login <id>`. This lets you keep custom endpoints in version control (id, baseURL, model list) while the key stays in `keys.json` out of the committable file:
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"providers": {
|
|
56
|
+
"my-llama": {
|
|
57
|
+
"baseURL": "http://localhost:8000/v1",
|
|
58
|
+
"defaultModel": "llama-3.1-70b",
|
|
59
|
+
"models": ["llama-3.1-70b"]
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
agent-sh auth login my-llama # prompts for the key, saves to keys.json
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`auth login <id>` also accepts ids it doesn't recognize (with a warning). This lets extensions that register their own provider at runtime tell users to run `agent-sh auth login <their-id>` — the key sits in `keys.json` until the extension loads and claims it. Such entries appear in `auth list` tagged `unattached`.
|
|
70
|
+
|
|
71
|
+
`install` accepts a bundled-extension name (see `agent-sh install` with no argument for the list), a `file:`/`./`/absolute path, or — once implemented — `npm:<pkg>` and `github:<user>/<repo>` specs.
|
|
72
|
+
|
|
73
|
+
## Updating
|
|
74
|
+
|
|
75
|
+
To pick up the latest changes, re-run the install command — npm replaces the global install in place. No uninstall step needed.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npm install -g agent-sh@latest # latest npm release (recommended)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
For unreleased changes on `main`, use the clone-and-link flow from the [Quick Start](../README.md#quick-start) — `npm install -g github:...` builds on your machine and can fail if the TypeScript toolchain doesn't extract cleanly.
|
|
82
|
+
|
|
83
|
+
## Provider Examples
|
|
84
|
+
|
|
85
|
+
agent-sh works with any OpenAI-compatible API. Here are common configurations:
|
|
86
|
+
|
|
87
|
+
### OpenAI
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
export OPENAI_API_KEY="sk-..."
|
|
91
|
+
agent-sh --model gpt-4o
|
|
92
|
+
# or: agent-sh --model gpt-4o-mini
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### DeepSeek
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
export DEEPSEEK_API_KEY="sk-..."
|
|
99
|
+
agent-sh
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Ollama (Local)
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
# No API key needed — Ollama doesn't require authentication
|
|
106
|
+
agent-sh --api-key dummy --base-url http://localhost:11434/v1 --model llama3
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### OpenRouter
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
agent-sh --api-key "$OPENROUTER_KEY" \
|
|
113
|
+
--base-url https://openrouter.ai/api/v1 \
|
|
114
|
+
--model anthropic/claude-sonnet-4-20250514
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Together AI
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
agent-sh --api-key "$TOGETHER_KEY" \
|
|
121
|
+
--base-url https://api.together.xyz/v1 \
|
|
122
|
+
--model meta-llama/Llama-3-70b-chat-hf
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Groq
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
agent-sh --api-key "$GROQ_KEY" \
|
|
129
|
+
--base-url https://api.groq.com/openai/v1 \
|
|
130
|
+
--model llama-3.3-70b-versatile
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### LM Studio
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
agent-sh --api-key dummy \
|
|
137
|
+
--base-url http://localhost:1234/v1 \
|
|
138
|
+
--model local-model
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### vLLM
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
agent-sh --api-key dummy \
|
|
145
|
+
--base-url http://localhost:8000/v1 \
|
|
146
|
+
--model your-model
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Using agent-sh as Your Default Shell
|
|
150
|
+
|
|
151
|
+
Add to the end of your `~/.zshrc` or `~/.bashrc`:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
if [[ -z "$AGENT_SH" && $- == *i* && -t 0 ]]; then
|
|
155
|
+
exec agent-sh --api-key "$OPENAI_API_KEY" --model gpt-4o
|
|
156
|
+
fi
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The `AGENT_SH` guard prevents infinite recursion. The checks ensure it only launches for interactive terminal sessions.
|
|
160
|
+
|
|
161
|
+
## Configuration
|
|
162
|
+
|
|
163
|
+
agent-sh stores settings and query history in `~/.agent-sh/`. Configure via `~/.agent-sh/settings.json` — all fields are optional with sensible defaults.
|
|
164
|
+
|
|
165
|
+
### Provider Profiles
|
|
166
|
+
|
|
167
|
+
Instead of passing `--api-key` and `--base-url` every time, define named providers in settings.json:
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"defaultProvider": "openai",
|
|
172
|
+
"providers": {
|
|
173
|
+
"openai": {
|
|
174
|
+
"apiKey": "$OPENAI_API_KEY",
|
|
175
|
+
"defaultModel": "gpt-4o",
|
|
176
|
+
"models": ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo"],
|
|
177
|
+
"contextWindow": 128000
|
|
178
|
+
},
|
|
179
|
+
"ollama": {
|
|
180
|
+
"apiKey": "not-needed",
|
|
181
|
+
"baseURL": "http://localhost:11434/v1",
|
|
182
|
+
"defaultModel": "llama3",
|
|
183
|
+
"models": ["llama3", "mistral", "codellama"]
|
|
184
|
+
},
|
|
185
|
+
"openrouter": {
|
|
186
|
+
"apiKey": "$OPENROUTER_KEY",
|
|
187
|
+
"baseURL": "https://openrouter.ai/api/v1",
|
|
188
|
+
"defaultModel": "anthropic/claude-sonnet-4.5",
|
|
189
|
+
"models": [
|
|
190
|
+
{ "id": "anthropic/claude-sonnet-4.5", "contextWindow": 200000, "reasoning": true },
|
|
191
|
+
{ "id": "google/gemini-2.5-pro", "contextWindow": 1000000 }
|
|
192
|
+
]
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Then just run:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
agent-sh # uses defaultProvider
|
|
202
|
+
agent-sh --provider ollama # use a specific provider
|
|
203
|
+
agent-sh --provider openai --model gpt-4-turbo # override the default model
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
The `apiKey` field supports `$ENV_VAR` and `${ENV_VAR}` syntax — variables are expanded at runtime, so you don't store secrets in the file.
|
|
207
|
+
|
|
208
|
+
### Declaring the context window
|
|
209
|
+
|
|
210
|
+
agent-sh adapts its auto-compaction trigger to the model's context window. There are two places to declare it:
|
|
211
|
+
|
|
212
|
+
- **Provider-level `contextWindow`** — applies to every model in that provider unless a more specific value is set.
|
|
213
|
+
- **Per-model `contextWindow`** (inside an entry of `models`) — overrides the provider-level value for a specific model, and also lets you tag reasoning-capable models via `reasoning: true`.
|
|
214
|
+
|
|
215
|
+
If neither is set, agent-sh falls back to a conservative 60k-token default.
|
|
216
|
+
|
|
217
|
+
Entries in `models` can be plain strings (just the model id, uses the provider-level `contextWindow`) or objects:
|
|
218
|
+
|
|
219
|
+
```json
|
|
220
|
+
"models": [
|
|
221
|
+
"gpt-4o-mini",
|
|
222
|
+
{ "id": "gpt-4o", "contextWindow": 128000 },
|
|
223
|
+
{ "id": "o1-preview", "contextWindow": 128000, "reasoning": true }
|
|
224
|
+
]
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Switching models at runtime
|
|
228
|
+
|
|
229
|
+
- **`/model`** — show the current model
|
|
230
|
+
- **`/model <name>`** — switch to a specific model (may cross providers; API key and base URL are reconfigured automatically)
|
|
231
|
+
|
|
232
|
+
Switching mid-conversation preserves your conversation state — only the LLM endpoint changes.
|
|
233
|
+
|
|
234
|
+
### CLI Flags
|
|
235
|
+
|
|
236
|
+
| Flag | Environment Variable | Description |
|
|
237
|
+
|---|---|---|
|
|
238
|
+
| `--provider <name>` | — | Use a named provider from settings.json |
|
|
239
|
+
| `--model <name>` | — | Model name (overrides provider default) |
|
|
240
|
+
| `--api-key <key>` | `OPENAI_API_KEY` | API key for OpenAI-compatible API |
|
|
241
|
+
| `--base-url <url>` | `OPENAI_BASE_URL` | Base URL for API endpoint |
|
|
242
|
+
| `--shell <path>` | `SHELL` | Shell to use (default: `/bin/bash`) |
|
|
243
|
+
| `--backend <name>` | — | Agent backend to launch (e.g. `ash`, `pi`); per-session override of `settings.defaultBackend`, does not persist. Errors out if the named backend isn't registered. |
|
|
244
|
+
| `-e, --extensions` | — | Extensions to load (comma-separated, repeatable) |
|
|
245
|
+
|
|
246
|
+
**Precedence** (highest to lowest): CLI flags → environment variables → provider profile in settings.json → defaults.
|
|
247
|
+
|
|
248
|
+
### General Settings
|
|
249
|
+
|
|
250
|
+
| Setting | Default | Description |
|
|
251
|
+
|---|---|---|
|
|
252
|
+
| `defaultProvider` | — | Which provider to use when no `--provider` flag is given |
|
|
253
|
+
| `defaultBackend` | `"ash"` | Which agent backend to activate. Set to an extension backend name (e.g. `"claude-code"`, `"pi"`) to use it by default |
|
|
254
|
+
| `extensions` | `[]` | Extensions to load (npm packages or file paths) |
|
|
255
|
+
| `historySize` | `500` | Max agent query history entries (persisted across sessions) |
|
|
256
|
+
| `shellTruncateThreshold` | `20` | Shell output lines before spill-to-tempfile |
|
|
257
|
+
| `shellHeadLines` / `shellTailLines` | `10` / `10` | Lines kept from start/end when output is spilled |
|
|
258
|
+
| `autoCompactThreshold` | `0.5` | Fraction of the model's context window at which conversation auto-compacts |
|
|
259
|
+
| `historyMaxBytes` | `104857600` | Max size of `~/.agent-sh/history` before front-truncation (100MB) |
|
|
260
|
+
| `historyStartupEntries` | `100` | Prior history entries injected as `[Prior session history]` preamble on launch |
|
|
261
|
+
| `maxCommandOutputLines` | `3` | Max tool output lines shown inline in TUI |
|
|
262
|
+
| `readOutputMaxLines` | `10` | Max read tool output lines shown inline (0 = hidden) |
|
|
263
|
+
| `diffMaxLines` | `Infinity` | Max diff lines rendered in the TUI. Defaults to no limit |
|
|
264
|
+
| `skillPaths` | `[]` | Extra directories to scan for skills (supports `~` expansion) |
|
|
265
|
+
| `diagnose` | `false` | Enable the `diagnose` tool — lets the agent evaluate JS expressions against its own runtime state (introspection; agent already has bash, so this is convenience, not new capability) |
|
|
266
|
+
| `startupBanner` | `true` | Show the startup banner (backend / model / extensions / skills) on launch |
|
|
267
|
+
| `promptIndicator` | `true` | Show a subtle agent-sh indicator in the shell prompt |
|
|
268
|
+
| `toolMode` | `"api"` | How tools are presented to the LLM. `"api"` sends all tool schemas. `"deferred"` bundles extension tools behind a `use_extension(name, args)` meta-tool (saves prompt tokens, loses schema fidelity). `"deferred-lookup"` keeps extension schemas dormant until the model calls `load_tool(names[])` — loaded tools then become first-class on the next turn with full schemas. `"inline"` describes tools as text. |
|
|
269
|
+
| `disabledExtensions` | `[]` | Names of user extensions in `~/.agent-sh/extensions/` to skip when auto-discovering. Match by basename without extension for files (`"peer-mesh"` matches `peer-mesh.ts`) or by directory name for dir-style extensions (`"superash"` matches `superash/index.ts`). Avoids having to rename files to `.disabled`. |
|
|
270
|
+
| `disabledBuiltins` | `[]` | Names of built-in extensions to disable. |
|
|
271
|
+
|
|
272
|
+
## Startup Banner
|
|
273
|
+
|
|
274
|
+
On launch, agent-sh displays a structured startup banner showing:
|
|
275
|
+
|
|
276
|
+
- **Backend** — which agent backend is active (`ash`, `claude-code`, `pi`, etc.)
|
|
277
|
+
- **Model** — current model with provider in brackets (e.g. `gpt-4o [openai]`)
|
|
278
|
+
- **Extensions** — loaded extensions (from CLI `-e`, settings, or `~/.agent-sh/extensions/`)
|
|
279
|
+
- **Skills** — discovered skills (global + project)
|
|
280
|
+
|
|
281
|
+
Set `startupBanner: false` in settings to disable.
|
|
282
|
+
|
|
283
|
+
## Shell Context
|
|
284
|
+
|
|
285
|
+
The agent automatically receives structured context about your shell session with each query:
|
|
286
|
+
|
|
287
|
+
- **Current working directory** — tracked via OSC 7 escape sequences
|
|
288
|
+
- **Recent commands and output** — new shell activity since the last turn is wrapped as `<shell_events>` inside `<query_context>` and prepended to your query
|
|
289
|
+
- **Long outputs are spilled to tempfiles** — outputs over `shellTruncateThreshold` lines are written to `<tmpdir>/agent-sh-<pid>/<id>.out` at capture; the agent sees head+tail plus the path and recovers the full text via the built-in `read_file` tool
|
|
290
|
+
|
|
291
|
+
This means you can run a failing command, then type `> fix this` and the agent knows exactly what happened — including a pointer to the full output if it got truncated. See [Context Management](context-management.md) for the full design.
|
|
292
|
+
|
|
293
|
+
## Slash Commands
|
|
294
|
+
|
|
295
|
+
| Command | Description |
|
|
296
|
+
|---|---|
|
|
297
|
+
| `/help` | Show available commands |
|
|
298
|
+
| `/model [name]` | Show current model; with a name, switch to that model |
|
|
299
|
+
| `/backend [name]` | List backends, or switch to a named backend |
|
|
300
|
+
| `/thinking [level]` | Set reasoning effort (off, low, medium, high) |
|
|
301
|
+
| `/compact` | Compact conversation (free up context space) |
|
|
302
|
+
| `/context` | Show context budget usage (active tokens vs. budget) |
|
|
303
|
+
| `/history [on\|off\|status]` | Pause/resume cross-session history writes for this session (no cache invalidation) |
|
|
304
|
+
| `/reload` | Reload user extensions from `~/.agent-sh/extensions/` |
|
|
305
|
+
|
|
306
|
+
See [Context Management](context-management.md) for how `/compact` and `/context` work, and [Extensions: Custom Agent Backends](extensions.md#custom-agent-backends) for `/backend`.
|
|
@@ -58,7 +58,7 @@ const myModel: RenderModel<...> = {
|
|
|
58
58
|
## Renderers
|
|
59
59
|
|
|
60
60
|
The whole TUI is swappable. ashi (the substrate) depends only on the `Renderer`
|
|
61
|
-
contract from [`@guanyilun/ashi/renderer`](src/renderer.ts) — the schema, theme,
|
|
61
|
+
contract from [`@guanyilun/ashi/renderer`](https://github.com/guanyilun/agent-sh/blob/main/examples/extensions/ashi/src/renderer.ts) — the schema, theme,
|
|
62
62
|
chat controllers, and frontend never import a concrete TUI library. The built-in
|
|
63
63
|
renderer is pi-tui (`src/renderers/pi-tui`).
|
|
64
64
|
|
|
@@ -113,6 +113,6 @@ agent-sh's shell clears OPOST on boot (pi-tui emits its own `\r`); ashi reads
|
|
|
113
113
|
gets the conventional terminal for free; only a raw driver like pi-tui sets
|
|
114
114
|
`rawOutput: true`.
|
|
115
115
|
|
|
116
|
-
See [`examples/extensions/ashi-ink`](
|
|
116
|
+
See [`examples/extensions/ashi-ink`](https://github.com/guanyilun/agent-sh/tree/main/examples/extensions/ashi-ink) for a worked example — a **working**
|
|
117
117
|
Ink (React) renderer (`ASHI_RENDERER=ink ashi -e ashi-ink`), verified with
|
|
118
118
|
`ink-testing-library`.
|
|
@@ -13,7 +13,7 @@ Rendering is **decoupled** — even *how* ashi draws tool calls and results is a
|
|
|
13
13
|
|---|---|
|
|
14
14
|
|  |  |
|
|
15
15
|
|
|
16
|
-
The claude-code-style renderer is [ashi-ink](
|
|
16
|
+
The claude-code-style renderer is [ashi-ink](https://github.com/guanyilun/agent-sh/tree/main/examples/extensions/ashi-ink), a working Ink (React) renderer; see [Extending ashi](#extending-ashi) for the contract.
|
|
17
17
|
|
|
18
18
|
## Install
|
|
19
19
|
|
|
@@ -159,7 +159,7 @@ Each tool inherits from `default` and is overridden by its own block. Unknown to
|
|
|
159
159
|
## Extending ashi
|
|
160
160
|
|
|
161
161
|
Other extensions can customize chat and tool-result rendering — and even swap the whole
|
|
162
|
-
TUI renderer (pi-tui, [Ink](
|
|
162
|
+
TUI renderer (pi-tui, [Ink](https://github.com/guanyilun/agent-sh/tree/main/examples/extensions/ashi-ink), …) — without forking ashi. See **[EXTENDING.md](EXTENDING.md)**
|
|
163
163
|
for the chat/tool render hooks, the declarative tool render schema, and the renderer
|
|
164
164
|
contract. For non-render concerns (commands, settings, tools, providers), use the
|
|
165
165
|
standard [agent-sh extension API](https://github.com/guanyilun/agent-sh/blob/main/docs/extensions.md).
|
|
@@ -152,7 +152,7 @@ contract has no free-placement layer yet. Use the dock, dialogs, and notices abo
|
|
|
152
152
|
|
|
153
153
|
## Working example
|
|
154
154
|
|
|
155
|
-
[`ashi-ui-demo.ts`](
|
|
155
|
+
[`ashi-ui-demo.ts`](https://github.com/guanyilun/agent-sh/blob/main/examples/extensions/ashi-ui-demo.ts) exercises every surface through the typed helper. Load
|
|
156
156
|
it and try the commands:
|
|
157
157
|
|
|
158
158
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guanyilun/ashi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Ash in an interactive TUI — agent-sh's built-in agent without the shell underneath",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cli.js",
|
|
@@ -28,7 +28,9 @@
|
|
|
28
28
|
},
|
|
29
29
|
"files": [
|
|
30
30
|
"dist",
|
|
31
|
-
"README.md"
|
|
31
|
+
"README.md",
|
|
32
|
+
"EXTENDING.md",
|
|
33
|
+
"docs"
|
|
32
34
|
],
|
|
33
35
|
"scripts": {
|
|
34
36
|
"dev": "tsx src/cli.ts",
|
|
@@ -68,7 +70,7 @@
|
|
|
68
70
|
},
|
|
69
71
|
"dependencies": {
|
|
70
72
|
"@earendil-works/pi-tui": "^0.74.0",
|
|
71
|
-
"agent-sh": "^0.
|
|
73
|
+
"agent-sh": "^0.15.0",
|
|
72
74
|
"chalk": "^5.5.0",
|
|
73
75
|
"cli-highlight": "^2.1.11"
|
|
74
76
|
},
|
|
@@ -180,7 +180,7 @@ async function main(): Promise<void> {
|
|
|
180
180
|
|
|
181
181
|
activateAgent(ctx);
|
|
182
182
|
activateShellContext(ctx);
|
|
183
|
-
|
|
183
|
+
await loadBuiltinExtensions(ctx);
|
|
184
184
|
|
|
185
185
|
const shell = new Shell({
|
|
186
186
|
bus: core.bus,
|
|
@@ -263,10 +263,11 @@ async function main(): Promise<void> {
|
|
|
263
263
|
await handle.rebuildChat();
|
|
264
264
|
ctx.bus.emit("ui:info", { message: `continued session ${resumeId.slice(0, 12)}…` });
|
|
265
265
|
} else {
|
|
266
|
-
// New-session only: skip on resume so a restored transcript isn't prefixed
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
266
|
+
// New-session only: skip on resume so a restored transcript isn't prefixed
|
|
267
|
+
// with this. List user/installed extensions only — built-ins are always present.
|
|
268
|
+
const userExtensions = [...new Set(loaded)];
|
|
269
|
+
if (userExtensions.length > 0) {
|
|
270
|
+
ctx.bus.emit("ui:info", { message: `extensions: ${userExtensions.join(" · ")}` });
|
|
270
271
|
}
|
|
271
272
|
}
|
|
272
273
|
|
|
@@ -167,7 +167,17 @@ export interface ToolGroupView {
|
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
/** Default tool-group rendering (`├`/`└` tree): one styled line per row, no indent. */
|
|
170
|
-
|
|
170
|
+
/** Tail-biased middle truncation — keeps the end (e.g. a filename) visible. */
|
|
171
|
+
function truncateMiddle(s: string, max: number): string {
|
|
172
|
+
if (max <= 0) return "";
|
|
173
|
+
if (s.length <= max) return s;
|
|
174
|
+
if (max === 1) return "…";
|
|
175
|
+
const keep = max - 1;
|
|
176
|
+
const tail = Math.ceil(keep * 0.65);
|
|
177
|
+
return (keep - tail > 0 ? s.slice(0, keep - tail) : "") + "…" + s.slice(s.length - tail);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function renderToolGroupLines(model: ToolGroupModel, width?: number): string[] {
|
|
171
181
|
const lines: string[] = [
|
|
172
182
|
segmentsToString([
|
|
173
183
|
{ text: model.icon, style: { color: "warning" } },
|
|
@@ -187,7 +197,17 @@ export function renderToolGroupLines(model: ToolGroupModel): string[] {
|
|
|
187
197
|
model.children.forEach((child, idx) => {
|
|
188
198
|
const segs: Segment[] = [{ text: idx === model.children.length - 1 ? "└" : "├", style: { color: "muted" } }, " "];
|
|
189
199
|
if (child.name !== model.kind) segs.push({ text: child.name, style: { bold: true, color: "toolTitle" } }, " ");
|
|
190
|
-
|
|
200
|
+
let detail = child.detail;
|
|
201
|
+
if (width && width > 0) {
|
|
202
|
+
// Everything else on the row is fixed-width; truncate the detail (path/
|
|
203
|
+
// command) so the row stays one line instead of wrapping.
|
|
204
|
+
let overhead = 3; // tree char + space, and the space after detail
|
|
205
|
+
if (child.name !== model.kind) overhead += child.name.length + 1;
|
|
206
|
+
if (!child.status) overhead += 2;
|
|
207
|
+
else overhead += 2 + (child.status.summary ? 1 + child.status.summary.length : 0);
|
|
208
|
+
detail = truncateMiddle(detail, Math.max(8, width - overhead));
|
|
209
|
+
}
|
|
210
|
+
segs.push({ text: detail, style: { color: "muted" } }, " ");
|
|
191
211
|
if (!child.status) {
|
|
192
212
|
segs.push(" ", { text: "…", style: { color: "muted" } });
|
|
193
213
|
} else {
|
|
@@ -3,18 +3,15 @@ import { createNodes } from "./nodes.js";
|
|
|
3
3
|
|
|
4
4
|
export function createPiTuiToolGroup(): ToolGroupView {
|
|
5
5
|
const nodes = createNodes();
|
|
6
|
-
const rows = nodes.container();
|
|
7
6
|
const container = nodes.container();
|
|
8
7
|
container.addChild(nodes.spacer(1));
|
|
9
|
-
|
|
8
|
+
const text = nodes.text({ paddingX: 1 });
|
|
9
|
+
container.addChild(text.node);
|
|
10
10
|
|
|
11
11
|
const update = (model: ToolGroupModel): void => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
t.setText(line);
|
|
16
|
-
rows.addChild(t.node);
|
|
17
|
-
}
|
|
12
|
+
// paddingX:1 on both sides → content area is width-2; render width-aware so
|
|
13
|
+
// long paths truncate to fit rather than wrap.
|
|
14
|
+
text.setRenderFn((width) => renderToolGroupLines(model, Math.max(1, width - 2)));
|
|
18
15
|
};
|
|
19
16
|
|
|
20
17
|
return { node: container.node, update };
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@earendil-works/pi-tui": "^0.74.0",
|
|
14
|
-
"@guanyilun/ashi": "^0.
|
|
15
|
-
"agent-sh": "^0.
|
|
14
|
+
"@guanyilun/ashi": "^0.3.0",
|
|
15
|
+
"agent-sh": "^0.15.0",
|
|
16
16
|
"cli-table3": "^0.6.0",
|
|
17
17
|
"ink": "^5.0.0",
|
|
18
18
|
"ink-spinner": "^5.0.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-sh",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.1",
|
|
4
4
|
"description": "A composable agent runtime — pair any frontend with any agent backend over one shared extension layer",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"workspaces": [
|
|
@@ -140,6 +140,8 @@
|
|
|
140
140
|
},
|
|
141
141
|
"files": [
|
|
142
142
|
"dist",
|
|
143
|
+
"src",
|
|
144
|
+
"docs",
|
|
143
145
|
"examples/extensions"
|
|
144
146
|
],
|
|
145
147
|
"scripts": {
|