@element47/ag 4.5.5 → 4.5.6
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/README.md +44 -14
- package/dist/cli/parser.d.ts.map +1 -1
- package/dist/cli/parser.js +8 -5
- package/dist/cli/parser.js.map +1 -1
- package/dist/cli/repl.d.ts.map +1 -1
- package/dist/cli/repl.js +136 -72
- package/dist/cli/repl.js.map +1 -1
- package/dist/core/__tests__/agent-units.test.d.ts +2 -0
- package/dist/core/__tests__/agent-units.test.d.ts.map +1 -0
- package/dist/core/__tests__/agent-units.test.js +144 -0
- package/dist/core/__tests__/agent-units.test.js.map +1 -0
- package/dist/core/__tests__/context.test.js +24 -0
- package/dist/core/__tests__/context.test.js.map +1 -1
- package/dist/core/__tests__/events.test.js +1 -1
- package/dist/core/__tests__/events.test.js.map +1 -1
- package/dist/core/__tests__/streaming.test.js +2 -1
- package/dist/core/__tests__/streaming.test.js.map +1 -1
- package/dist/core/agent.d.ts +7 -8
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +94 -428
- package/dist/core/agent.js.map +1 -1
- package/dist/core/compaction.d.ts +27 -0
- package/dist/core/compaction.d.ts.map +1 -0
- package/dist/core/compaction.js +102 -0
- package/dist/core/compaction.js.map +1 -0
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js +6 -2
- package/dist/core/context.js.map +1 -1
- package/dist/core/events.d.ts.map +1 -1
- package/dist/core/events.js +6 -1
- package/dist/core/events.js.map +1 -1
- package/dist/core/prompt.d.ts +23 -0
- package/dist/core/prompt.d.ts.map +1 -0
- package/dist/core/prompt.js +122 -0
- package/dist/core/prompt.js.map +1 -0
- package/dist/core/types.d.ts +1 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/utils.d.ts +11 -0
- package/dist/core/utils.d.ts.map +1 -0
- package/dist/core/utils.js +82 -0
- package/dist/core/utils.js.map +1 -0
- package/dist/memory/__tests__/memory.test.js +47 -2
- package/dist/memory/__tests__/memory.test.js.map +1 -1
- package/dist/memory/memory.d.ts +8 -0
- package/dist/memory/memory.d.ts.map +1 -1
- package/dist/memory/memory.js +93 -6
- package/dist/memory/memory.js.map +1 -1
- package/dist/tools/agent.js +17 -15
- package/dist/tools/agent.js.map +1 -1
- package/dist/tools/file.d.ts.map +1 -1
- package/dist/tools/file.js +9 -5
- package/dist/tools/file.js.map +1 -1
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/grep.js +7 -5
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/task.d.ts.map +1 -1
- package/dist/tools/task.js +40 -33
- package/dist/tools/task.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -44,12 +44,34 @@ export OPENROUTER_API_KEY=sk-or-v1-...
|
|
|
44
44
|
-k, --key <key> API key (or set OPENROUTER_API_KEY)
|
|
45
45
|
-s, --system <prompt> Custom system prompt
|
|
46
46
|
-b, --base-url <url> API base URL (default: OpenRouter; use for local LLMs)
|
|
47
|
-
-n, --max-iterations <n> Max tool-call iterations (default:
|
|
47
|
+
-n, --max-iterations <n> Max tool-call iterations (default: 200)
|
|
48
48
|
-y, --yes Auto-approve all tool calls (skip confirmation prompts)
|
|
49
49
|
--stats Show memory file paths and status
|
|
50
50
|
-h, --help Show help
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
+
## Steering
|
|
54
|
+
|
|
55
|
+
Press **Tab** while the agent is working to course-correct without aborting. This opens a `steer>` prompt with full editing support (backspace, arrow keys, paste). Output is buffered while you type.
|
|
56
|
+
|
|
57
|
+
- **Tab** — opens steer prompt, pauses output
|
|
58
|
+
- **Enter** — submits the steer message and resumes. The LLM sees it on the next turn.
|
|
59
|
+
- **Escape** — aborts everything (destructive, same as before)
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
you> build an API with auth
|
|
63
|
+
|
|
64
|
+
⠧ [bash] npm init...
|
|
65
|
+
← press Tab
|
|
66
|
+
steer> use PostgreSQL not SQLite ← type your correction
|
|
67
|
+
← press Enter
|
|
68
|
+
[steered] use PostgreSQL not SQLite
|
|
69
|
+
✓ [bash] done ← buffered output replays
|
|
70
|
+
⠧ thinking [2/200] ← LLM adjusts
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Steer messages are queued and injected before the next LLM call — current tool calls are not interrupted.
|
|
74
|
+
|
|
53
75
|
## REPL Commands
|
|
54
76
|
|
|
55
77
|
All commands follow the pattern: `/noun` to show, `/noun subcommand` to act.
|
|
@@ -118,11 +140,11 @@ agent(prompt="Set up the database schema", taskId=2)
|
|
|
118
140
|
agent(prompt="Write unit tests", model="anthropic/claude-haiku")
|
|
119
141
|
```
|
|
120
142
|
|
|
121
|
-
Sub-agents get project memory, plan, skills, and
|
|
143
|
+
Sub-agents get project memory, plan, skills, tools, and extensions but start with a clean context (no conversation history). When linked to a task via `taskId`, the task is auto-marked `in_progress` at start and `done` on completion. Tasks can include a `description` for richer context.
|
|
122
144
|
|
|
123
|
-
Multiple `agent()` calls in the same turn run in parallel. Use `model` to route cheap tasks to faster/cheaper models.
|
|
145
|
+
Multiple `agent()` calls in the same turn run in parallel. Use `model` to route cheap tasks to faster/cheaper models. Sub-agents run silently — only the parent shows `[agent]` start/result lines.
|
|
124
146
|
|
|
125
|
-
Sub-agents cannot spawn sub-sub-agents (depth limit = 1).
|
|
147
|
+
Sub-agents cannot spawn sub-sub-agents (depth limit = 1). Extensions loaded on sub-agents can check `agent.isSilent()` to avoid output.
|
|
126
148
|
|
|
127
149
|
## Custom Tools
|
|
128
150
|
|
|
@@ -337,9 +359,10 @@ Three tiers, all plain markdown you can edit directly:
|
|
|
337
359
|
projects/
|
|
338
360
|
<id>/
|
|
339
361
|
memory.md # project: architecture, decisions
|
|
340
|
-
plans/ # timestamped plan files
|
|
362
|
+
plans/ # timestamped plan files (created on demand)
|
|
341
363
|
2026-04-13T12-31-22-add-auth.md
|
|
342
|
-
|
|
364
|
+
tasks.json # task tracking (created on demand)
|
|
365
|
+
history.jsonl # conversation history (created on demand)
|
|
343
366
|
```
|
|
344
367
|
|
|
345
368
|
All memory is injected into the system prompt on every API call (capped at ~6000 chars total to avoid context bloat). The agent reads it automatically and writes via the `memory` and `plan` tools.
|
|
@@ -473,15 +496,15 @@ Tools execute in parallel when the model returns multiple tool calls.
|
|
|
473
496
|
- A compact project file listing gives the model awareness of project structure.
|
|
474
497
|
- `tool_choice: "auto"` encourages tool use over conversational responses.
|
|
475
498
|
- Dangerous bash commands (`find ~`, `rm -rf /`, etc.) are blocked before execution.
|
|
476
|
-
- Tool results over
|
|
499
|
+
- Tool results over 32KB are smart-truncated (first 100 + last 100 lines) to preserve context.
|
|
477
500
|
- For multi-step coding tasks, the agent creates a plan before starting and updates it as it goes.
|
|
478
501
|
- For simple questions, it just answers directly.
|
|
479
|
-
- At
|
|
502
|
+
- At 200 iterations the REPL asks if you want to continue.
|
|
480
503
|
- At 90% context window usage, ag automatically summarizes older conversation messages to free space. Use `/context compact` to trigger manually. Only message history is compacted — system prompt, tools, and skills are unaffected.
|
|
481
504
|
|
|
482
505
|
## When to use something else
|
|
483
506
|
|
|
484
|
-
- **Claude Code** -- if you have a subscription and want
|
|
507
|
+
- **Claude Code** -- if you have a subscription and want MCP, git worktrees, and a polished IDE integration. ag has sub-agents, tasks, and extensions but is terminal-only.
|
|
485
508
|
- **aider** -- if your workflow is git-centric (commit-per-change, diff-based editing).
|
|
486
509
|
- **Cursor / Windsurf** -- if you want IDE integration. ag is terminal-only.
|
|
487
510
|
|
|
@@ -494,9 +517,14 @@ src/
|
|
|
494
517
|
cli.ts # entry point
|
|
495
518
|
cli/parser.ts # arg parsing + help
|
|
496
519
|
cli/repl.ts # interactive REPL (unified /noun commands)
|
|
497
|
-
core/agent.ts #
|
|
520
|
+
core/agent.ts # agent class, chat loop, tool execution, steering
|
|
521
|
+
core/utils.ts # spinner, retry, truncation, promise helpers
|
|
522
|
+
core/prompt.ts # environment detection, read-only rules, request building
|
|
523
|
+
core/compaction.ts # context compaction (summarize old messages)
|
|
498
524
|
core/config.ts # persistent config (~/.ag/config.json)
|
|
499
525
|
core/context.ts # context window usage tracking
|
|
526
|
+
core/events.ts # event system for extensions (8 lifecycle events)
|
|
527
|
+
core/extensions.ts # extension discovery and loading
|
|
500
528
|
core/skills.ts # skill discovery, parsing, loading
|
|
501
529
|
core/registry.ts # skills.sh search + GitHub install
|
|
502
530
|
core/types.ts # interfaces
|
|
@@ -506,13 +534,15 @@ src/
|
|
|
506
534
|
core/guardrails.ts # prompt injection scanning (5 threat categories)
|
|
507
535
|
core/loader.ts # custom tool loader (~/.ag/tools/, .ag/tools/)
|
|
508
536
|
core/permissions.ts # permission manager with glob pattern matching
|
|
509
|
-
memory/memory.ts #
|
|
537
|
+
memory/memory.ts # memory, plans, tasks, history
|
|
538
|
+
tools/agent.ts # sub-agent spawning (in-process, parallel)
|
|
539
|
+
tools/bash.ts # shell execution + background processes
|
|
510
540
|
tools/file.ts # file reading + directory listing
|
|
511
|
-
tools/bash.ts # shell execution (with command safeguards)
|
|
512
|
-
tools/memory.ts # memory tool
|
|
513
|
-
tools/plan.ts # plan management tool
|
|
514
541
|
tools/git.ts # git operations tool
|
|
515
542
|
tools/grep.ts # code search + file find
|
|
543
|
+
tools/memory.ts # memory tool
|
|
544
|
+
tools/plan.ts # plan management tool
|
|
545
|
+
tools/task.ts # task tracking tool
|
|
516
546
|
tools/web.ts # web fetch + search tool
|
|
517
547
|
tools/skill.ts # skill activation tool
|
|
518
548
|
```
|
package/dist/cli/parser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/cli/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,GAAG;IAAE,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/cli/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,GAAG;IAAE,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAqB/E;AAED,wBAAgB,QAAQ,IAAI,IAAI,CAkC/B"}
|
package/dist/cli/parser.js
CHANGED
|
@@ -11,8 +11,11 @@ export function parseArgs(args) {
|
|
|
11
11
|
options.system = args[++i];
|
|
12
12
|
else if ((arg === '--base-url' || arg === '-b') && i + 1 < args.length)
|
|
13
13
|
options.baseURL = args[++i];
|
|
14
|
-
else if ((arg === '--max-iterations' || arg === '-n') && i + 1 < args.length)
|
|
15
|
-
|
|
14
|
+
else if ((arg === '--max-iterations' || arg === '-n') && i + 1 < args.length) {
|
|
15
|
+
const n = parseInt(args[++i], 10);
|
|
16
|
+
if (!isNaN(n) && n > 0)
|
|
17
|
+
options.maxIterations = n;
|
|
18
|
+
}
|
|
16
19
|
else if (arg === '--stats')
|
|
17
20
|
options.stats = true;
|
|
18
21
|
else if (arg === '--yes' || arg === '-y')
|
|
@@ -39,7 +42,7 @@ Options:
|
|
|
39
42
|
-k, --key <key> API key (or set OPENROUTER_API_KEY)
|
|
40
43
|
-s, --system <prompt> Custom system prompt
|
|
41
44
|
-b, --base-url <url> API base URL (default: OpenRouter; use for local LLMs)
|
|
42
|
-
-n, --max-iterations <n> Max tool-call iterations (default:
|
|
45
|
+
-n, --max-iterations <n> Max tool-call iterations (default: 200)
|
|
43
46
|
-y, --yes Auto-approve all tool calls (skip confirmation prompts)
|
|
44
47
|
--stats Show memory file locations and status
|
|
45
48
|
-h, --help Show this help
|
|
@@ -55,8 +58,8 @@ REPL commands:
|
|
|
55
58
|
/exit Exit
|
|
56
59
|
|
|
57
60
|
Install:
|
|
58
|
-
npx @
|
|
59
|
-
npm install -g @
|
|
61
|
+
npx @element47/ag # Run directly
|
|
62
|
+
npm install -g @element47/ag # Install globally
|
|
60
63
|
`);
|
|
61
64
|
}
|
|
62
65
|
//# sourceMappingURL=parser.js.map
|
package/dist/cli/parser.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/cli/parser.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,SAAS,CAAC,IAAc;IACtC,MAAM,OAAO,GAAe,EAAE,CAAC;IAC/B,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,CAAC,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM;YAAE,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;aACrF,IAAI,CAAC,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM;YAAE,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;aACtF,IAAI,CAAC,GAAG,KAAK,UAAU,IAAI,GAAG,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM;YAAE,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;aAC5F,IAAI,CAAC,GAAG,KAAK,YAAY,IAAI,GAAG,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM;YAAE,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;aAC/F,IAAI,CAAC,GAAG,KAAK,kBAAkB,IAAI,GAAG,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM;
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/cli/parser.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,SAAS,CAAC,IAAc;IACtC,MAAM,OAAO,GAAe,EAAE,CAAC;IAC/B,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,CAAC,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM;YAAE,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;aACrF,IAAI,CAAC,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM;YAAE,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;aACtF,IAAI,CAAC,GAAG,KAAK,UAAU,IAAI,GAAG,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM;YAAE,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;aAC5F,IAAI,CAAC,GAAG,KAAK,YAAY,IAAI,GAAG,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM;YAAE,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;aAC/F,IAAI,CAAC,GAAG,KAAK,kBAAkB,IAAI,GAAG,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YAC7E,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAClC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;gBAAE,OAAO,CAAC,aAAa,GAAG,CAAC,CAAC;QACpD,CAAC;aACI,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;aAC5C,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;aACxD,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;aAC1D,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgCb,CAAC,CAAC;AACH,CAAC"}
|
package/dist/cli/repl.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repl.d.ts","sourceRoot":"","sources":["../../src/cli/repl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,SAAS,EAAsB,MAAM,eAAe,CAAC;AAC/E,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAKzC,OAAO,EAAE,iBAAiB,EAAgB,MAAM,wBAAwB,CAAC;AACzE,OAAO,KAAK,EAAE,eAAe,EAAiB,MAAM,kBAAkB,CAAC;AAqCvE,wBAAgB,qBAAqB,CAAC,QAAQ,CAAC,EAAE,SAAS,GAAG,eAAe,GAAG;IAAE,YAAY,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAA;CAAE,CAgBnH;AAED,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,iBAAiB,EAAE,QAAQ,CAAC,EAAE,SAAS,GAAG,eAAe,GAAG;IAAE,YAAY,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAA;CAAE,CAiD7I;AA8GD,qBAAa,IAAI;IACf,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAY;IAC/B,OAAO,CAAC,QAAQ,CAAC,EAAE,CAA2B;IAC9C,OAAO,CAAC,SAAS,CAAqD;gBAE1D,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,iBAAiB,EAAE,SAAS,CAAC,EAAE,UAAU,CAAC,OAAO,wBAAwB,CAAC;IAqBnG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"repl.d.ts","sourceRoot":"","sources":["../../src/cli/repl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,SAAS,EAAsB,MAAM,eAAe,CAAC;AAC/E,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAKzC,OAAO,EAAE,iBAAiB,EAAgB,MAAM,wBAAwB,CAAC;AACzE,OAAO,KAAK,EAAE,eAAe,EAAiB,MAAM,kBAAkB,CAAC;AAqCvE,wBAAgB,qBAAqB,CAAC,QAAQ,CAAC,EAAE,SAAS,GAAG,eAAe,GAAG;IAAE,YAAY,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAA;CAAE,CAgBnH;AAED,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,iBAAiB,EAAE,QAAQ,CAAC,EAAE,SAAS,GAAG,eAAe,GAAG;IAAE,YAAY,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAA;CAAE,CAiD7I;AA8GD,qBAAa,IAAI;IACf,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAY;IAC/B,OAAO,CAAC,QAAQ,CAAC,EAAE,CAA2B;IAC9C,OAAO,CAAC,SAAS,CAAqD;gBAE1D,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,iBAAiB,EAAE,SAAS,CAAC,EAAE,UAAU,CAAC,OAAO,wBAAwB,CAAC;IAqBnG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAkRd,aAAa;IAkZ3B,OAAO,CAAC,GAAG;CAGZ"}
|
package/dist/cli/repl.js
CHANGED
|
@@ -305,7 +305,11 @@ export class REPL {
|
|
|
305
305
|
const runAgent = async (message) => {
|
|
306
306
|
const controller = new AbortController();
|
|
307
307
|
let activeSpinnerStop = null;
|
|
308
|
-
// ──
|
|
308
|
+
// ── Steer state ──
|
|
309
|
+
let steerActive = false;
|
|
310
|
+
let steerResolve = null;
|
|
311
|
+
const chunkBuffer = [];
|
|
312
|
+
// ── Keypress handler: Escape to abort, Tab to steer ──
|
|
309
313
|
const onKeypress = (_ch, key) => {
|
|
310
314
|
if (key?.name === 'escape') {
|
|
311
315
|
// 1. Clear whatever spinner is showing
|
|
@@ -318,6 +322,37 @@ export class REPL {
|
|
|
318
322
|
// 3. Signal abort (async propagation begins)
|
|
319
323
|
controller.abort();
|
|
320
324
|
}
|
|
325
|
+
else if (key?.name === 'tab' && !steerActive) {
|
|
326
|
+
// Tab opens steer prompt — pause spinner, buffer output until resolved
|
|
327
|
+
steerActive = true;
|
|
328
|
+
clearSpinner();
|
|
329
|
+
process.stdin.removeListener('keypress', onKeypress);
|
|
330
|
+
this.rl.resume();
|
|
331
|
+
this.rl.question(` ${C.yellow}steer>${C.reset} `, (answer) => {
|
|
332
|
+
this.rl.pause();
|
|
333
|
+
if (answer.trim()) {
|
|
334
|
+
this.agent.queueSteer(answer.trim());
|
|
335
|
+
}
|
|
336
|
+
// Clear the steer prompt line, show acknowledgement
|
|
337
|
+
process.stderr.write('\x1b[A\x1b[2K');
|
|
338
|
+
if (answer.trim()) {
|
|
339
|
+
process.stderr.write(` ${C.yellow}[steered]${C.reset} ${C.dim}${answer.trim()}${C.reset}\n`);
|
|
340
|
+
}
|
|
341
|
+
// Flush buffered chunks
|
|
342
|
+
for (const buffered of chunkBuffer) {
|
|
343
|
+
renderChunk(buffered);
|
|
344
|
+
}
|
|
345
|
+
chunkBuffer.length = 0;
|
|
346
|
+
steerActive = false;
|
|
347
|
+
process.stdin.on('keypress', onKeypress);
|
|
348
|
+
process.stdin.resume();
|
|
349
|
+
// Unblock any waiting permission prompts
|
|
350
|
+
if (steerResolve) {
|
|
351
|
+
steerResolve();
|
|
352
|
+
steerResolve = null;
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
}
|
|
321
356
|
};
|
|
322
357
|
if (process.stdin.isTTY) {
|
|
323
358
|
this.rl.pause(); // Detach readline so only our keypress handler runs
|
|
@@ -342,7 +377,6 @@ export class REPL {
|
|
|
342
377
|
this.agent.setSpinnerControl({
|
|
343
378
|
pause: () => {
|
|
344
379
|
clearSpinner();
|
|
345
|
-
// Flush any in-progress text line so extension output starts on a clean line
|
|
346
380
|
if (hasText) {
|
|
347
381
|
flushLines(true);
|
|
348
382
|
process.stderr.write('\n');
|
|
@@ -351,6 +385,19 @@ export class REPL {
|
|
|
351
385
|
},
|
|
352
386
|
resume: () => { },
|
|
353
387
|
});
|
|
388
|
+
// Wrap permissions to wait for steer to finish
|
|
389
|
+
if (this.confirmCb) {
|
|
390
|
+
const originalCb = this.agent.getConfirmToolCall();
|
|
391
|
+
if (originalCb) {
|
|
392
|
+
this.agent.setConfirmToolCall(async (toolName, args, pk) => {
|
|
393
|
+
// Wait for active steer to resolve before prompting
|
|
394
|
+
while (steerActive) {
|
|
395
|
+
await new Promise(r => { steerResolve = r; });
|
|
396
|
+
}
|
|
397
|
+
return originalCb(toolName, args, pk);
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
}
|
|
354
401
|
const flushLines = (final) => {
|
|
355
402
|
const parts = lineBuf.split('\n');
|
|
356
403
|
lineBuf = final ? '' : (parts.pop() || '');
|
|
@@ -362,78 +409,86 @@ export class REPL {
|
|
|
362
409
|
lineBuf = '';
|
|
363
410
|
}
|
|
364
411
|
};
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
if (
|
|
380
|
-
if (hadTools)
|
|
381
|
-
process.stderr.write('\n');
|
|
382
|
-
process.stderr.write(`${C.bold}agent>${C.reset} `);
|
|
383
|
-
hasText = true;
|
|
384
|
-
}
|
|
385
|
-
lineBuf += (chunk.content || '');
|
|
386
|
-
if (lineBuf.includes('\n'))
|
|
387
|
-
flushLines(false);
|
|
388
|
-
break;
|
|
389
|
-
case 'tool_start': {
|
|
390
|
-
clearSpinner();
|
|
391
|
-
if (hasText) {
|
|
392
|
-
flushLines(true);
|
|
412
|
+
const renderChunk = (chunk) => {
|
|
413
|
+
switch (chunk.type) {
|
|
414
|
+
case 'thinking':
|
|
415
|
+
clearSpinner();
|
|
416
|
+
if (hasText) {
|
|
417
|
+
flushLines(true);
|
|
418
|
+
process.stderr.write('\n');
|
|
419
|
+
hasText = false;
|
|
420
|
+
}
|
|
421
|
+
setSpinner(startSpinner(chunk.content || 'thinking'));
|
|
422
|
+
break;
|
|
423
|
+
case 'text':
|
|
424
|
+
clearSpinner();
|
|
425
|
+
if (!hasText) {
|
|
426
|
+
if (hadTools)
|
|
393
427
|
process.stderr.write('\n');
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
const cmdPreview = truncateCommand(chunk.content || '', 60);
|
|
397
|
-
setSpinner(startSpinner(`[${chunk.toolName}] ${cmdPreview}`));
|
|
398
|
-
break;
|
|
428
|
+
process.stderr.write(`${C.bold}agent>${C.reset} `);
|
|
429
|
+
hasText = true;
|
|
399
430
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
process.stderr.write(` ${icon} ${C.dim}[${endLabel}]${C.reset} ${C.dim}${preview}${(chunk.content || '').length > 150 ? '...' : ''}${C.reset}\n`);
|
|
411
|
-
break;
|
|
431
|
+
lineBuf += (chunk.content || '');
|
|
432
|
+
if (lineBuf.includes('\n'))
|
|
433
|
+
flushLines(false);
|
|
434
|
+
break;
|
|
435
|
+
case 'tool_start': {
|
|
436
|
+
clearSpinner();
|
|
437
|
+
if (hasText) {
|
|
438
|
+
flushLines(true);
|
|
439
|
+
process.stderr.write('\n');
|
|
440
|
+
hasText = false;
|
|
412
441
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
flushLines(true);
|
|
417
|
-
process.stderr.write('\n\n');
|
|
418
|
-
}
|
|
419
|
-
else if (!hadTools)
|
|
420
|
-
process.stderr.write(`${C.bold}agent>${C.reset} ${renderMarkdown(chunk.content || '')}\n\n`);
|
|
421
|
-
break;
|
|
422
|
-
case 'max_iterations':
|
|
423
|
-
clearSpinner();
|
|
424
|
-
hitMaxIterations = true;
|
|
425
|
-
break;
|
|
426
|
-
case 'interrupted':
|
|
427
|
-
clearSpinner();
|
|
428
|
-
interrupted = true;
|
|
429
|
-
if (hasText) {
|
|
430
|
-
flushLines(true);
|
|
431
|
-
process.stderr.write('\n');
|
|
432
|
-
}
|
|
433
|
-
const summary = chunk.content || 'stopped';
|
|
434
|
-
process.stderr.write(` ${C.yellow}⚡ Interrupted${C.reset} ${C.dim}(${summary})${C.reset}\n\n`);
|
|
435
|
-
break;
|
|
442
|
+
const cmdPreview = truncateCommand(chunk.content || '', 60);
|
|
443
|
+
setSpinner(startSpinner(`[${chunk.toolName}] ${cmdPreview}`));
|
|
444
|
+
break;
|
|
436
445
|
}
|
|
446
|
+
case 'tool_end': {
|
|
447
|
+
clearSpinner();
|
|
448
|
+
hadTools = true;
|
|
449
|
+
let endLabel = chunk.toolName || '';
|
|
450
|
+
const activeSkills = this.agent.getActiveSkillNames();
|
|
451
|
+
if (endLabel === 'bash' && activeSkills.length > 0) {
|
|
452
|
+
endLabel = `${endLabel} via ${activeSkills[activeSkills.length - 1]}`;
|
|
453
|
+
}
|
|
454
|
+
const icon = chunk.success ? `${C.green}✓` : `${C.red}✗`;
|
|
455
|
+
const preview = (chunk.content || '').slice(0, 150).split('\n')[0];
|
|
456
|
+
process.stderr.write(` ${icon} ${C.dim}[${endLabel}]${C.reset} ${C.dim}${preview}${(chunk.content || '').length > 150 ? '...' : ''}${C.reset}\n`);
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
case 'done':
|
|
460
|
+
clearSpinner();
|
|
461
|
+
if (hasText) {
|
|
462
|
+
flushLines(true);
|
|
463
|
+
process.stderr.write('\n\n');
|
|
464
|
+
}
|
|
465
|
+
else if (!hadTools)
|
|
466
|
+
process.stderr.write(`${C.bold}agent>${C.reset} ${renderMarkdown(chunk.content || '')}\n\n`);
|
|
467
|
+
break;
|
|
468
|
+
case 'max_iterations':
|
|
469
|
+
clearSpinner();
|
|
470
|
+
hitMaxIterations = true;
|
|
471
|
+
break;
|
|
472
|
+
case 'steer':
|
|
473
|
+
break;
|
|
474
|
+
case 'interrupted':
|
|
475
|
+
clearSpinner();
|
|
476
|
+
interrupted = true;
|
|
477
|
+
if (hasText) {
|
|
478
|
+
flushLines(true);
|
|
479
|
+
process.stderr.write('\n');
|
|
480
|
+
}
|
|
481
|
+
process.stderr.write(` ${C.yellow}⚡ Interrupted${C.reset} ${C.dim}(${chunk.content || 'stopped'})${C.reset}\n\n`);
|
|
482
|
+
break;
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
try {
|
|
486
|
+
for await (const chunk of this.agent.chatStream(message, controller.signal)) {
|
|
487
|
+
if (steerActive) {
|
|
488
|
+
chunkBuffer.push(chunk);
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
renderChunk(chunk);
|
|
437
492
|
}
|
|
438
493
|
clearSpinner();
|
|
439
494
|
}
|
|
@@ -448,10 +503,14 @@ export class REPL {
|
|
|
448
503
|
}
|
|
449
504
|
}
|
|
450
505
|
finally {
|
|
451
|
-
this.agent.setSpinnerControl(null);
|
|
452
506
|
if (process.stdin.isTTY) {
|
|
453
507
|
process.stdin.removeListener('keypress', onKeypress);
|
|
454
508
|
}
|
|
509
|
+
this.agent.setSpinnerControl(null);
|
|
510
|
+
// Restore original confirm callback if we wrapped it
|
|
511
|
+
if (this.confirmCb) {
|
|
512
|
+
this.agent.setConfirmToolCall(this.confirmCb);
|
|
513
|
+
}
|
|
455
514
|
}
|
|
456
515
|
};
|
|
457
516
|
await runAgent(input);
|
|
@@ -700,7 +759,12 @@ export class REPL {
|
|
|
700
759
|
}
|
|
701
760
|
let parsed = value;
|
|
702
761
|
if (key === 'maxIterations') {
|
|
703
|
-
|
|
762
|
+
const n = parseInt(value, 10);
|
|
763
|
+
if (isNaN(n) || n <= 0) {
|
|
764
|
+
console.error(`${C.red}Invalid number: ${value}${C.reset}\n`);
|
|
765
|
+
break;
|
|
766
|
+
}
|
|
767
|
+
parsed = n;
|
|
704
768
|
}
|
|
705
769
|
else if (key === 'autoApprove') {
|
|
706
770
|
parsed = ['true', '1', 'yes'].includes(value.toLowerCase());
|