agent-sh 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.
Files changed (50) hide show
  1. package/README.md +659 -0
  2. package/dist/acp-client.d.ts +76 -0
  3. package/dist/acp-client.js +507 -0
  4. package/dist/context-manager.d.ts +45 -0
  5. package/dist/context-manager.js +405 -0
  6. package/dist/core.d.ts +41 -0
  7. package/dist/core.js +76 -0
  8. package/dist/event-bus.d.ts +140 -0
  9. package/dist/event-bus.js +79 -0
  10. package/dist/executor.d.ts +31 -0
  11. package/dist/executor.js +116 -0
  12. package/dist/extension-loader.d.ts +16 -0
  13. package/dist/extension-loader.js +164 -0
  14. package/dist/extensions/file-autocomplete.d.ts +2 -0
  15. package/dist/extensions/file-autocomplete.js +63 -0
  16. package/dist/extensions/shell-recall.d.ts +9 -0
  17. package/dist/extensions/shell-recall.js +8 -0
  18. package/dist/extensions/slash-commands.d.ts +2 -0
  19. package/dist/extensions/slash-commands.js +105 -0
  20. package/dist/extensions/tui-renderer.d.ts +2 -0
  21. package/dist/extensions/tui-renderer.js +354 -0
  22. package/dist/index.d.ts +2 -0
  23. package/dist/index.js +159 -0
  24. package/dist/input-handler.d.ts +48 -0
  25. package/dist/input-handler.js +302 -0
  26. package/dist/output-parser.d.ts +55 -0
  27. package/dist/output-parser.js +166 -0
  28. package/dist/shell.d.ts +54 -0
  29. package/dist/shell.js +219 -0
  30. package/dist/types.d.ts +71 -0
  31. package/dist/types.js +1 -0
  32. package/dist/utils/ansi.d.ts +12 -0
  33. package/dist/utils/ansi.js +23 -0
  34. package/dist/utils/box-frame.d.ts +21 -0
  35. package/dist/utils/box-frame.js +60 -0
  36. package/dist/utils/diff-renderer.d.ts +20 -0
  37. package/dist/utils/diff-renderer.js +506 -0
  38. package/dist/utils/diff.d.ts +24 -0
  39. package/dist/utils/diff.js +122 -0
  40. package/dist/utils/file-watcher.d.ts +31 -0
  41. package/dist/utils/file-watcher.js +101 -0
  42. package/dist/utils/markdown.d.ts +39 -0
  43. package/dist/utils/markdown.js +248 -0
  44. package/dist/utils/palette.d.ts +32 -0
  45. package/dist/utils/palette.js +36 -0
  46. package/dist/utils/tool-display.d.ts +33 -0
  47. package/dist/utils/tool-display.js +141 -0
  48. package/examples/extensions/interactive-prompts.ts +161 -0
  49. package/examples/extensions/solarized-theme.ts +27 -0
  50. package/package.json +72 -0
package/README.md ADDED
@@ -0,0 +1,659 @@
1
+ # agent-sh
2
+
3
+ Not a shell that lives in an agent — an agent that lives in a shell.
4
+
5
+ agent-sh is a real terminal first. Every keystroke goes to a real PTY. `cd`, pipes, vim, job control — they all just work. But type `>` at the start of a line, and you're talking to an AI agent that has full context of what you've been doing: your working directory, recent commands, their output.
6
+
7
+ The agent connects via the [Agent Client Protocol (ACP)](https://agentclientprotocol.com/), so you can plug in **any** ACP-compatible agent: [pi](https://github.com/svkozak/pi-acp), claude-code, codex, gemini-cli, goose, etc.
8
+
9
+ ```
10
+ ⚡ src $ ls -la # real shell command
11
+ ⚡ src $ cd ../tests && npm test # real cd, env, aliases — all just work
12
+ ⚡ src $ vim file.ts # opens vim in the same PTY
13
+ ⚡ src $ > refactor the auth middleware # → sent to agent via ACP
14
+ ⚡ src $ > explain the last error # agent sees your recent commands + output
15
+ ```
16
+
17
+ ## Why shell-first?
18
+
19
+ Most AI coding tools are agent-first: the LLM drives the experience and the shell is bolted on. That means no real PTY, no job control, no interactive commands, and fragile `cd` tracking that reimplements what bash gives you for free.
20
+
21
+ agent-sh starts from the opposite end. The shell is the primary interface — it's your terminal, not the agent's. The agent is a tool you reach for when you need it, not the other way around.
22
+
23
+ ### Why ACP?
24
+
25
+ The [Agent Client Protocol](https://agentclientprotocol.com/) decouples the shell from any specific agent:
26
+
27
+ - **Pluggable agents** — swap between pi-acp, claude-code, codex with a CLI flag
28
+ - **Standard protocol** — JSON-RPC 2.0 over stdio, well-specified capability negotiation
29
+ - **Agent handles LLM details** — no API keys, tool definitions, or context windows to manage
30
+ - **Terminal delegation** — ACP defines `terminal/create`, `terminal/output`, `terminal/wait_for_exit` — exactly what an agent needs to run commands in your shell
31
+
32
+ ## Key Features
33
+
34
+ - **🚀 Instant Start** — Shell starts immediately, no waiting for agent connection
35
+ - **🔄 Smart Connection** — Agent connects asynchronously in the background
36
+ - **⏳ Auto-Wait** — Queries automatically wait for agent to finish connecting
37
+ - **📊 Real-time Streaming** — Agent responses stream live with syntax highlighting
38
+ - **⚡ Zero Latency** — Direct PTY access, full terminal compatibility
39
+ - **🧠 Context Aware** — Agent sees your cwd, recent commands, and their output
40
+ - **🎯 Multiple Agents** — Easy switching between pi-acp, claude, and other ACP agents
41
+ - **🏷️ Agent Info Display** — Shows current agent and model next to the prompt (e.g., `pi (gpt-4o) ●`)
42
+ - **📝 Inline Diff Preview** — File writes show syntax-highlighted diffs inline (Ctrl+O to expand)
43
+ - **💭 Thinking Display** — Toggle agent thinking/reasoning text with Ctrl+T
44
+ - **🎨 Themeable** — Semantic color palette, swappable via extensions
45
+
46
+ ## Install
47
+
48
+ ```bash
49
+ git clone https://github.com/guanyilun/agent-sh.git
50
+ cd agent-sh
51
+ npm install
52
+ npm run build
53
+ ```
54
+
55
+ Requires Node.js 18+ and an ACP-compatible agent installed on your system.
56
+
57
+ ## Running agent-sh
58
+
59
+ After building, you can run agent-sh in several ways:
60
+
61
+ ```bash
62
+ # Start with the default agent (pi-acp) - RECOMMENDED
63
+ npm start
64
+
65
+ # Quick shortcuts
66
+ npm run pi # Start with pi-acp
67
+ npm run claude # Start with claude-agent-acp (Anthropic's official Claude agent)
68
+
69
+ # Using the built binary directly
70
+ node dist/index.js --agent <agent-name>
71
+
72
+ # Using npm script with custom agent
73
+ npm start -- --agent <agent-name>
74
+
75
+ # Using npx (if published to npm)
76
+ npx agent-sh --agent <agent-name>
77
+
78
+ # Make the built file executable and run directly
79
+ chmod +x dist/index.js
80
+ ./dist/index.js --agent <agent-name>
81
+
82
+ # Using environment variable to set default agent
83
+ AGENT_SH_AGENT=claude-agent-acp npm start
84
+ ```
85
+
86
+ ### Install ACP-compatible agents
87
+
88
+ agent-sh requires an ACP-compatible agent. Here are some popular options:
89
+
90
+ | Agent | Install Command | Notes |
91
+ |-------|----------------|-------|
92
+ | **pi-acp** | `npm install -g pi-acp` | **Recommended default** - ACP adapter for pi coding agent |
93
+ | **claude-agent-acp** | `npm install -g @agentclientprotocol/claude-agent-acp` | Anthropic's official ACP Claude agent |
94
+
95
+ **⚠️ Important**: The `claude` CLI tool (Claude Code) does **not** support the ACP protocol. You must use `claude-agent-acp` or `pi-acp` with Anthropic models.
96
+
97
+ **Example: Installing pi-acp**
98
+ ```bash
99
+ npm install -g pi-acp
100
+ pi-acp --help # Verify installation
101
+ ```
102
+
103
+ ## Usage
104
+
105
+ ### Quick Start
106
+
107
+ ```bash
108
+ # 1. Install required agents (if not already installed)
109
+ npm install -g pi-acp
110
+ npm install -g @agentclientprotocol/claude-agent-acp
111
+
112
+ # 2. Set required API keys
113
+ export ANTHROPIC_API_KEY="your-anthropic-api-key"
114
+ # Or for OpenAI models with pi-acp:
115
+ # export OPENAI_API_KEY="your-openai-api-key"
116
+
117
+ # 3. Start agent-sh
118
+ npm start
119
+ ```
120
+
121
+ ### Common Usage Patterns
122
+
123
+ ```bash
124
+ # Start with the default agent (pi-acp)
125
+ npm start
126
+ # Shows: pi ● ❯ when entering agent mode
127
+
128
+ # Quick shortcuts
129
+ npm run pi # pi-acp
130
+ npm run claude # claude-agent-acp (Anthropic's official Claude agent)
131
+
132
+ # Start with a specific agent
133
+ npm start -- --agent pi-acp
134
+
135
+ # Pass arguments to the agent (including model)
136
+ npm start -- --agent claude-agent-acp --agent-args "--model claude-3-5-sonnet-20241022"
137
+ # Shows: claude-agent-acp (claude-3-5-sonnet-20241022) ● ❯ when entering agent mode
138
+
139
+ # Use pi-acp with Claude
140
+ npm start -- --agent pi-acp --agent-args "--provider anthropic --model claude-3-5-sonnet-20241022"
141
+ # Shows: pi (claude-3-5-sonnet-20241022) ● ❯
142
+
143
+ # Use pi-acp with OpenAI GPT-4
144
+ export OPENAI_API_KEY="your-openai-key"
145
+ npm start -- --agent pi-acp --agent-args "--provider openai --model gpt-4o"
146
+ # Shows: pi (gpt-4o) ● ❯
147
+
148
+ # Use a different shell
149
+ npm start -- --shell /bin/zsh
150
+
151
+ # Set default agent via environment variable
152
+ AGENT_SH_AGENT=claude-agent-acp npm start
153
+ ```
154
+
155
+ ### Common Claude Models
156
+
157
+ **Valid Claude model names** (for use with `--model` parameter):
158
+ - `claude-3-5-sonnet-20241022` (latest Sonnet)
159
+ - `claude-3-5-haiku-20241022` (latest Haiku)
160
+ - `claude-3-opus-20240229` (older Opus)
161
+ - `claude-3-sonnet-20240229` (older Sonnet)
162
+
163
+ **Example with claude-agent-acp**:
164
+ ```bash
165
+ export ANTHROPIC_API_KEY="your-key"
166
+ npm start -- --agent claude-agent-acp --agent-args "--model claude-3-5-sonnet-20241022"
167
+ ```
168
+
169
+ **Example with pi-acp**:
170
+ ```bash
171
+ export ANTHROPIC_API_KEY="your-key"
172
+ npm start -- --agent pi-acp --agent-args "--provider anthropic --model claude-3-5-sonnet-20241022"
173
+ ```
174
+
175
+ ### Agent environment configuration
176
+
177
+ agent-sh can be configured via environment variables:
178
+
179
+ ```bash
180
+ # Set the default agent to use
181
+ export AGENT_SH_AGENT=pi-acp # Default is pi-acp
182
+ ```
183
+
184
+ **Smart Connection**: agent-sh uses an intelligent connection system where the shell starts immediately and the agent connects in the background. If you send a query before the agent is fully connected, the system automatically waits for connection completion. This provides instant access to the shell while ensuring reliable agent communication.
185
+
186
+ Many ACP agents also require API keys. Set these before starting agent-sh:
187
+
188
+ #### pi-acp configuration
189
+
190
+ pi-acp uses the same environment variables as the [pi](https://github.com/mariozechner/pi-coding-agent) agent:
191
+
192
+ ```bash
193
+ # Anthropic Claude
194
+ export ANTHROPIC_API_KEY="your-anthropic-key"
195
+
196
+ # OpenAI
197
+ export OPENAI_API_KEY="your-openai-key"
198
+
199
+ # Google Gemini
200
+ export GEMINI_API_KEY="your-gemini-key"
201
+
202
+ # Groq
203
+ export GROQ_API_KEY="your-groq-key"
204
+
205
+ # xAI (Grok)
206
+ export XAI_API_KEY="your-xai-key"
207
+
208
+ # OpenRouter
209
+ export OPENROUTER_API_KEY="your-openrouter-key"
210
+ ```
211
+
212
+ You can also configure pi-acp by passing arguments:
213
+
214
+ ```bash
215
+ # Use Claude 3.5 Sonnet with pi-acp
216
+ npm start -- --agent pi-acp --agent-args "--provider anthropic --model claude-3-5-sonnet-20241022"
217
+ # Shows: pi (claude-3-5-sonnet-20241022) ● ❯
218
+
219
+ # Use GPT-4o with pi-acp
220
+ export OPENAI_API_KEY="your-openai-key"
221
+ npm start -- --agent pi-acp --agent-args "--provider openai --model gpt-4o"
222
+ # Shows: pi (gpt-4o) ● ❯
223
+
224
+ # Enable thinking mode
225
+ npm start -- --agent pi-acp --agent-args "--thinking high"
226
+
227
+ # Limit to read-only tools
228
+ npm start -- --agent pi-acp --agent-args "--tools read,grep,find,ls"
229
+ ```
230
+
231
+ **Model Display**: When you specify a model using `--model`, it will be displayed in parentheses next to the agent name when you enter agent mode. This helps you quickly identify which model you're using.
232
+
233
+ For more pi-acp options, run `pi --help` (pi-acp accepts the same arguments).
234
+
235
+ #### Other agent configurations
236
+
237
+ Refer to each agent's documentation for their specific environment variable requirements. Common patterns:
238
+
239
+ ```bash
240
+ # claude-agent-acp (Anthropic's official Claude agent)
241
+ export ANTHROPIC_API_KEY="your-key"
242
+
243
+ # gemini-cli
244
+ export GOOGLE_API_KEY="your-key"
245
+ ```
246
+
247
+ **Tip:** Add these to your `~/.zshrc` or `~/.bashrc` for persistent configuration.
248
+
249
+ ### Input modes
250
+
251
+ | Input | Behavior |
252
+ |---|---|
253
+ | `ls -la` | Runs in real shell (PTY), output displayed normally |
254
+ | `cd src && make` | Real shell — cd, env, aliases all just work |
255
+ | `vim file.ts` | Opens vim in the same PTY, no hacks needed |
256
+ | `> refactor this fn` | Sends to agent via ACP, streams response inline |
257
+ | `> /help` | Shows available slash commands |
258
+ | `Ctrl-C` | Standard signal to shell, or cancels active agent response |
259
+ | `Ctrl-O` | Expand/collapse truncated diff preview |
260
+ | `Ctrl-T` | Toggle thinking/reasoning text display |
261
+ | `Escape` | Exit agent input mode (when typing after `>`) |
262
+
263
+ When you type `>` at the start of a line, agent-sh enters **agent input mode** — the prompt changes to show the agent and model information (e.g., `pi (gpt-4o) ● ❯`) and your text is sent to the agent on Enter. The agent's response streams inline in real-time in a bordered box with markdown rendering and syntax highlighting.
264
+
265
+ **Agent Info Display**: When entering agent mode, you'll see the current agent name and model (if specified) next to the prompt, followed by a green dot (●) indicating the connection status. For example:
266
+ - `pi ● ❯` — pi agent without model specified
267
+ - `pi (gpt-4o) ● ❯` — pi agent with gpt-4o model
268
+ - `pi (claude-3-5-sonnet-20241022) ● ❯` — pi agent with Claude Sonnet model
269
+ - `claude-agent-acp (claude-3-5-sonnet-20241022) ● ❯` — Claude agent with Sonnet model
270
+
271
+ ### Slash commands
272
+
273
+ | Command | Description |
274
+ |---|---|
275
+ | `/help` | Show available commands |
276
+ | `/clear` | Start a new agent session |
277
+ | `/copy` | Copy last agent response to clipboard |
278
+ | `/compact` | Ask agent to summarize the conversation |
279
+ | `/quit` | Exit agent-sh |
280
+
281
+ Slash commands have tab-completion and arrow-key navigation in agent input mode.
282
+
283
+ ### Shell context
284
+
285
+ The agent automatically receives structured context about your shell session with each query, managed by the ContextManager:
286
+
287
+ - **Current working directory** — tracked via OSC 7 escape sequences
288
+ - **Recent commands and output** — truncated summaries of recent shell commands, agent queries, and tool executions
289
+ - **Recall tool** — the agent can run `__shell_recall --search "query"` or `__shell_recall --expand 42` to retrieve full output of any past exchange
290
+
291
+ This means you can run a failing command, then type `> fix this` and the agent knows exactly what happened. For long outputs, the agent sees a truncated summary and can recall the full content on demand.
292
+
293
+ ## Architecture
294
+
295
+ agent-sh is an ACP **client**. The agent is a subprocess launched with stdio transport.
296
+
297
+ ### Design philosophy: headless core + pluggable frontends
298
+
299
+ The core (`createCore()`) is a frontend-agnostic kernel — it wires up the EventBus, ContextManager, and AcpClient with zero knowledge of terminals, PTYs, or rendering. The interactive terminal (Shell + TUI + extensions) is one frontend built on top.
300
+
301
+ ```
302
+ createCore() — frontend-agnostic kernel:
303
+ │ EventBus — typed pub/sub + transform pipelines
304
+ │ ContextManager — exchange recording, context assembly
305
+ │ AcpClient — ACP protocol, terminal execution (yolo by default)
306
+
307
+ index.ts — interactive terminal frontend:
308
+ │ Shell — PTY lifecycle (delegates to InputHandler + OutputParser)
309
+
310
+ ├── Built-in extensions:
311
+ │ tuiRenderer — markdown rendering, inline diffs, thinking display, spinner
312
+ │ slashCommands — /help, /clear, /copy, /compact, /quit
313
+ │ fileAutocomplete — @ file path completion
314
+ │ shellRecall — __shell_recall terminal interception
315
+
316
+ ├── Shared utilities:
317
+ │ palette — semantic color system (accent, success, warning, error, muted)
318
+ │ diff-renderer — syntax-highlighted diffs (split/unified/summary)
319
+ │ box-frame — bordered TUI panels
320
+ │ tool-display — width-adaptive tool call rendering
321
+
322
+ └── User extensions (opt-in, loaded from -e flag / settings.json / extensions dir):
323
+ e.g. interactive-prompts, solarized-theme
324
+ ```
325
+
326
+ All components communicate exclusively through typed bus events. AcpClient has no reference to Shell — it emits lifecycle events (`agent:processing-start`, `agent:processing-done`) and Shell subscribes to manage its own state. Input flows the same way: any frontend emits `agent:submit` and the core routes it to the agent.
327
+
328
+ **The core works without any frontend.** This enables:
329
+
330
+ - **Library usage** — `import { createCore } from "agent-sh"` to build WebSocket servers, REST APIs, Electron apps, or test harnesses
331
+ - **Headless mode** — CI, scripting, embedding — no terminal needed
332
+ - **Alternative renderers** — web UI, logging backend, minimal TUI
333
+ - **Custom features** — add commands, autocomplete providers, tool interceptors by writing an extension
334
+
335
+ ### We send to the agent
336
+
337
+ | Method | When |
338
+ |---|---|
339
+ | `initialize` | Startup: negotiate capabilities |
340
+ | `session/new` | Create a conversation session |
341
+ | `session/prompt` | User types `> query` — sent with shell context |
342
+ | `session/cancel` | User hits Ctrl-C during agent response |
343
+
344
+ ### We handle from the agent
345
+
346
+ | Method | What we do |
347
+ |---|---|
348
+ | `terminal/create` | Execute command in an isolated child process |
349
+ | `terminal/output` | Return captured output for a terminal ID |
350
+ | `terminal/wait_for_exit` | Await command completion, return exit code |
351
+ | `terminal/kill` | Send signal to running command |
352
+ | `terminal/release` | Cleanup terminal session |
353
+ | `fs/read_text_file` | Read file from disk, return content |
354
+ | `fs/write_text_file` | Write file to disk |
355
+ | `session/request_permission` | Prompt user for y/n/allow-all confirmation |
356
+
357
+ ### We render from the agent
358
+
359
+ | Update type | What we render |
360
+ |---|---|
361
+ | `agent_message_chunk` | Real-time streaming markdown with syntax highlighting in a bordered box |
362
+ | `agent_thought_chunk` | Thinking spinner (default), or streaming text when toggled on with Ctrl+T |
363
+ | `tool_call` | Yellow header showing what the agent is invoking |
364
+ | `tool_call_update` | Status indicator (✓ or ✗ with exit code) |
365
+ | `file_write` | Inline diff preview in bordered box (Ctrl+O to expand/collapse) |
366
+
367
+ ## Project structure
368
+
369
+ ```
370
+ agent-sh/
371
+ ├── src/
372
+ │ ├── index.ts # Interactive terminal entry point (CLI args, Shell, extensions)
373
+ │ ├── core.ts # createCore() — frontend-agnostic kernel, library entry point
374
+ │ ├── event-bus.ts # Typed EventBus: emit/on, emitPipe, emitPipeAsync
375
+ │ ├── shell.ts # PTY lifecycle + wiring (InputHandler + OutputParser)
376
+ │ ├── input-handler.ts # Keyboard input, agent mode, bus-driven autocomplete
377
+ │ ├── output-parser.ts # OSC parsing, command boundary detection
378
+ │ ├── acp-client.ts # ACP protocol, terminal execution, session management
379
+ │ ├── context-manager.ts # Exchange log, context assembly, recall API
380
+ │ ├── extension-loader.ts # Extension loading (-e, settings.json, extensions dir)
381
+ │ ├── executor.ts # Isolated child process execution
382
+ │ ├── types.ts # Shared type definitions
383
+ │ ├── utils/
384
+ │ │ ├── palette.ts # Semantic color palette (accent/success/warning/error/muted)
385
+ │ │ ├── ansi.ts # ANSI utility functions (visibleLen, stripAnsi)
386
+ │ │ ├── diff.ts # Line-level LCS diff for file change previews
387
+ │ │ ├── diff-renderer.ts# Syntax-highlighted diff display (split/unified/summary)
388
+ │ │ ├── box-frame.ts # Bordered TUI panels (rounded/square/double/heavy)
389
+ │ │ ├── tool-display.ts # Width-adaptive tool call/result rendering
390
+ │ │ ├── file-watcher.ts # File change detection for agent tool writes
391
+ │ │ └── markdown.ts # Streaming markdown → ANSI renderer
392
+ │ └── extensions/
393
+ │ ├── tui-renderer.ts # Terminal rendering (markdown, spinner, tools)
394
+ │ ├── slash-commands.ts # /help, /clear, /copy, /compact, /quit
395
+ │ ├── file-autocomplete.ts # @ file path completion
396
+ │ └── shell-recall.ts # __shell_recall terminal interception
397
+ ├── examples/
398
+ │ └── extensions/
399
+ │ ├── interactive-prompts.ts # Example: permission gates (opt-in)
400
+ │ └── solarized-theme.ts # Example: color theme via setPalette()
401
+ ├── package.json
402
+ └── tsconfig.json
403
+ ```
404
+
405
+ ## Development
406
+
407
+ ```bash
408
+ # Run in development mode (no build step)
409
+ npm run dev
410
+
411
+ # Build
412
+ npm run build
413
+
414
+ # Run the built version (uses default agent pi-acp)
415
+ npm start
416
+
417
+ # Quick shortcuts for different agents
418
+ npm run pi # Start with pi-acp
419
+ npm run claude # Start with claude-agent-acp (Anthropic's official Claude agent)
420
+
421
+ # Debug mode — logs ACP protocol details to stderr
422
+ DEBUG=1 npm start
423
+
424
+ # Test with specific agent
425
+ npm run dev -- --agent pi-acp
426
+ ```
427
+
428
+ ## How it works
429
+
430
+ 1. agent-sh spawns a real PTY running your shell (zsh or bash, with your full rc config — oh-my-zsh, p10k, aliases, plugins, PATH) and sets up raw stdin passthrough
431
+ 2. It launches the specified ACP agent as a subprocess with stdio transport
432
+ 3. All keyboard input goes directly to the PTY — zero latency, full terminal compatibility
433
+ 4. **Smart connection**: The agent connects asynchronously in the background while the shell starts immediately
434
+ 5. **Auto-wait**: If you send a query before the agent is fully connected, the system automatically waits for connection completion
435
+ 6. When you type `>` at the start of a line, agent-sh intercepts and enters agent input mode
436
+ 7. On Enter, the query (plus shell context) is sent to the agent via `session/prompt`
437
+ 8. The agent's streaming response renders inline in a bordered markdown box with real-time output
438
+ 9. If the agent needs to run commands, it calls `terminal/create` and agent-sh executes them in isolated child processes, streaming output back
439
+ 10. When the agent finishes, normal shell operation resumes
440
+
441
+ ### EventBus
442
+
443
+ All communication between components flows through a typed EventBus. Components emit events (shell commands, agent responses, tool calls) and extensions subscribe to events they care about. The bus supports three modes:
444
+
445
+ - **emit/on** — fire-and-forget notifications (e.g., `agent:response-chunk`)
446
+ - **emitPipe/onPipe** — synchronous transform chains (e.g., `autocomplete:request` where extensions append completion items)
447
+ - **emitPipeAsync/onPipeAsync** — async transform chains (e.g., `permission:request` where extensions prompt the user and return a decision)
448
+
449
+ ### Writing extensions
450
+
451
+ An extension is a module that exports a default (or named `activate`) function. It receives an `ExtensionContext` with access to all core services:
452
+
453
+ ```typescript
454
+ // my-extension.js
455
+ export default function activate(ctx) {
456
+ // Listen to agent events
457
+ ctx.bus.on("agent:response-done", (e) => {
458
+ console.log(`Agent responded with ${e.response.length} chars`);
459
+ });
460
+
461
+ // Add a slash command
462
+ ctx.bus.on("command:execute", (e) => {
463
+ if (e.name === "/greet") {
464
+ ctx.bus.emit("ui:info", { message: "Hello from my extension!" });
465
+ }
466
+ });
467
+ ctx.bus.onPipe("autocomplete:request", (payload) => {
468
+ if (!payload.buffer.startsWith("/g")) return payload;
469
+ return { ...payload, items: [...payload.items, { name: "/greet", description: "Say hello" }] };
470
+ });
471
+
472
+ // Intercept terminal commands
473
+ ctx.bus.onPipe("agent:terminal-intercept", (payload) => {
474
+ if (payload.command !== "my-tool") return payload;
475
+ return { ...payload, intercepted: true, output: "custom output" };
476
+ });
477
+ }
478
+ ```
479
+
480
+ The `ExtensionContext` provides:
481
+
482
+ | Property | Type | Description |
483
+ |---|---|---|
484
+ | `bus` | `EventBus` | Subscribe to events, emit events, register pipe handlers |
485
+ | `contextManager` | `ContextManager` | Access exchange history, cwd, search, expand |
486
+ | `getAcpClient` | `() => AcpClient` | Lazy getter for the agent client |
487
+ | `quit` | `() => void` | Exit agent-sh |
488
+ | `setPalette` | `(overrides: Partial<ColorPalette>) => void` | Override color palette slots for theming |
489
+
490
+ ### Yolo mode
491
+
492
+ By default, agent-sh runs in **yolo mode** — all tool calls and file writes are auto-approved. This matches pi's design philosophy where the agent operates freely unless you explicitly add permission gates.
493
+
494
+ To add permission prompts, load the example extension:
495
+ ```bash
496
+ # One-off
497
+ npm start -- -e ./examples/extensions/interactive-prompts.ts
498
+
499
+ # Permanent: copy to your extensions dir
500
+ cp examples/extensions/interactive-prompts.ts ~/.agent-sh/extensions/
501
+
502
+ # Or add to settings.json
503
+ echo '{ "extensions": ["./examples/extensions/interactive-prompts.ts"] }' > ~/.agent-sh/settings.json
504
+ ```
505
+
506
+ ### Theming
507
+
508
+ agent-sh uses a semantic color palette with ~10 base roles (`accent`, `success`, `warning`, `error`, `muted`, plus background variants and style modifiers). Extensions can override any slot via `setPalette()`:
509
+
510
+ ```typescript
511
+ // solarized-theme.ts
512
+ export default function activate({ setPalette }) {
513
+ setPalette({
514
+ accent: "\x1b[38;2;38;139;210m", // solarized blue
515
+ success: "\x1b[38;2;133;153;0m", // solarized green
516
+ warning: "\x1b[38;2;181;137;0m", // solarized yellow
517
+ error: "\x1b[38;2;220;50;47m", // solarized red
518
+ muted: "\x1b[38;2;88;110;117m", // solarized base01
519
+ });
520
+ }
521
+ ```
522
+
523
+ Load a theme like any other extension:
524
+ ```bash
525
+ npm start -- -e ./examples/extensions/solarized-theme.ts
526
+ ```
527
+
528
+ A complete example is included at `examples/extensions/solarized-theme.ts`.
529
+
530
+ ### Loading extensions
531
+
532
+ Extensions are loaded from three sources (in order, deduplicated):
533
+
534
+ **1. CLI flag (`-e` / `--extensions`)** — npm packages or file paths, repeatable:
535
+ ```bash
536
+ npm start -- -e my-ext-package -e ./local-ext.ts
537
+ npm start -- -e my-ext-package,another-package # comma-separated also works
538
+ ```
539
+
540
+ **2. Settings file** — `~/.agent-sh/settings.json`:
541
+ ```json
542
+ {
543
+ "extensions": [
544
+ "my-published-extension",
545
+ "/absolute/path/to/ext.ts",
546
+ "./relative/path/to/ext.js"
547
+ ]
548
+ }
549
+ ```
550
+
551
+ **3. Extensions directory** — files and directories in `~/.agent-sh/extensions/`:
552
+ ```bash
553
+ ~/.agent-sh/extensions/
554
+ ├── my-extension.ts # loaded directly
555
+ ├── another.js # JS works too
556
+ └── complex-extension/ # directory with index file
557
+ ├── index.ts # entry point (auto-detected)
558
+ └── helpers.ts # supporting modules
559
+ ```
560
+
561
+ Extensions can be written in **TypeScript or JavaScript** — `.ts`, `.tsx`, `.mts`, `.js`, `.mjs` are all supported. TS extensions are transpiled at runtime via tsx.
562
+
563
+ Bare names (e.g. `my-ext-package`) resolve as **npm packages** via Node's standard module resolution. Install them globally or locally and reference by name.
564
+
565
+ Errors in extension loading are non-fatal — a `ui:error` is emitted and the next extension continues loading.
566
+
567
+ ### Using as a library
568
+
569
+ The core can be imported directly for building custom frontends — no terminal required:
570
+
571
+ ```typescript
572
+ import { createCore } from "agent-sh";
573
+
574
+ const core = createCore({ agentCommand: "pi-acp" });
575
+
576
+ // Subscribe to events
577
+ core.bus.on("agent:response-chunk", ({ text }) => process.stdout.write(text));
578
+ core.bus.on("agent:processing-done", () => console.log("\n[done]"));
579
+
580
+ // Handle permissions (auto-approve, or wire to your own UI)
581
+ core.bus.onPipeAsync("permission:request", async (p) => {
582
+ return { ...p, decision: { approved: true } };
583
+ });
584
+
585
+ // Connect and send a query
586
+ await core.start();
587
+ core.bus.emit("agent:submit", { query: "explain this codebase" });
588
+ ```
589
+
590
+ This works for WebSocket servers, REST APIs, Electron apps, test harnesses, or any environment where you want agent-sh's context management and ACP integration without the interactive terminal.
591
+
592
+ ## Troubleshooting
593
+
594
+ ### Agent connection issues
595
+
596
+ **Problem**: "Agent not connected. Please wait a moment and try again."
597
+
598
+ **Solutions**:
599
+ 1. **Check agent installation**:
600
+ ```bash
601
+ which pi-acp
602
+ which claude-agent-acp
603
+ ```
604
+
605
+ 2. **Verify ACP compatibility**:
606
+ ```bash
607
+ # Test if agent supports ACP protocol
608
+ echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":1,"clientInfo":{"name":"test","version":"1.0.0"},"clientCapabilities":{"terminal":true,"fs":{"readTextFile":true,"writeTextFile":true}}}}' | pi-acp
609
+ ```
610
+
611
+ 3. **Install missing agents**:
612
+ ```bash
613
+ npm install -g pi-acp
614
+ npm install -g @agentclientprotocol/claude-agent-acp
615
+ ```
616
+
617
+ 4. **Check environment variables**:
618
+ ```bash
619
+ echo $ANTHROPIC_API_KEY
620
+ echo $OPENAI_API_KEY
621
+ echo $GEMINI_API_KEY
622
+ ```
623
+
624
+ ### Common errors
625
+
626
+ **Error**: "claude: command not found"
627
+ - **Cause**: Trying to use `claude` CLI tool which doesn't support ACP
628
+ - **Solution**: Use `claude-agent-acp` or `pi-acp` instead
629
+
630
+ **Error**: "API key not found"
631
+ - **Cause**: Missing required API key environment variable
632
+ - **Solution**: Set the appropriate API key (e.g., `export ANTHROPIC_API_KEY="your-key"`)
633
+
634
+ **Error**: "Invalid model name"
635
+ - **Cause**: Using incorrect model name
636
+ - **Solution**: Use valid model names like `claude-3-5-sonnet-20241022` or `gpt-4o`
637
+
638
+ **Error**: "Agent process exited with code X"
639
+ - **Cause**: Agent crashed or failed to start
640
+ - **Solution**: Check agent installation and API key validity
641
+
642
+ ### Debug mode
643
+
644
+ Enable debug mode to see detailed ACP protocol information:
645
+ ```bash
646
+ DEBUG=1 npm start -- --agent pi-acp
647
+ ```
648
+
649
+ ### Getting help
650
+
651
+ If you encounter issues:
652
+ 1. Check the [ACP Protocol Documentation](https://agentclientprotocol.com/)
653
+ 2. Verify agent installation: `pi-acp --version` or `claude-agent-acp --version`
654
+ 3. Test with different agents to isolate the problem
655
+ 4. Check GitHub issues for known problems
656
+
657
+ ## License
658
+
659
+ MIT