@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.
Files changed (59) hide show
  1. package/README.md +44 -14
  2. package/dist/cli/parser.d.ts.map +1 -1
  3. package/dist/cli/parser.js +8 -5
  4. package/dist/cli/parser.js.map +1 -1
  5. package/dist/cli/repl.d.ts.map +1 -1
  6. package/dist/cli/repl.js +136 -72
  7. package/dist/cli/repl.js.map +1 -1
  8. package/dist/core/__tests__/agent-units.test.d.ts +2 -0
  9. package/dist/core/__tests__/agent-units.test.d.ts.map +1 -0
  10. package/dist/core/__tests__/agent-units.test.js +144 -0
  11. package/dist/core/__tests__/agent-units.test.js.map +1 -0
  12. package/dist/core/__tests__/context.test.js +24 -0
  13. package/dist/core/__tests__/context.test.js.map +1 -1
  14. package/dist/core/__tests__/events.test.js +1 -1
  15. package/dist/core/__tests__/events.test.js.map +1 -1
  16. package/dist/core/__tests__/streaming.test.js +2 -1
  17. package/dist/core/__tests__/streaming.test.js.map +1 -1
  18. package/dist/core/agent.d.ts +7 -8
  19. package/dist/core/agent.d.ts.map +1 -1
  20. package/dist/core/agent.js +94 -428
  21. package/dist/core/agent.js.map +1 -1
  22. package/dist/core/compaction.d.ts +27 -0
  23. package/dist/core/compaction.d.ts.map +1 -0
  24. package/dist/core/compaction.js +102 -0
  25. package/dist/core/compaction.js.map +1 -0
  26. package/dist/core/context.d.ts.map +1 -1
  27. package/dist/core/context.js +6 -2
  28. package/dist/core/context.js.map +1 -1
  29. package/dist/core/events.d.ts.map +1 -1
  30. package/dist/core/events.js +6 -1
  31. package/dist/core/events.js.map +1 -1
  32. package/dist/core/prompt.d.ts +23 -0
  33. package/dist/core/prompt.d.ts.map +1 -0
  34. package/dist/core/prompt.js +122 -0
  35. package/dist/core/prompt.js.map +1 -0
  36. package/dist/core/types.d.ts +1 -1
  37. package/dist/core/types.d.ts.map +1 -1
  38. package/dist/core/utils.d.ts +11 -0
  39. package/dist/core/utils.d.ts.map +1 -0
  40. package/dist/core/utils.js +82 -0
  41. package/dist/core/utils.js.map +1 -0
  42. package/dist/memory/__tests__/memory.test.js +47 -2
  43. package/dist/memory/__tests__/memory.test.js.map +1 -1
  44. package/dist/memory/memory.d.ts +8 -0
  45. package/dist/memory/memory.d.ts.map +1 -1
  46. package/dist/memory/memory.js +93 -6
  47. package/dist/memory/memory.js.map +1 -1
  48. package/dist/tools/agent.js +17 -15
  49. package/dist/tools/agent.js.map +1 -1
  50. package/dist/tools/file.d.ts.map +1 -1
  51. package/dist/tools/file.js +9 -5
  52. package/dist/tools/file.js.map +1 -1
  53. package/dist/tools/grep.d.ts.map +1 -1
  54. package/dist/tools/grep.js +7 -5
  55. package/dist/tools/grep.js.map +1 -1
  56. package/dist/tools/task.d.ts.map +1 -1
  57. package/dist/tools/task.js +40 -33
  58. package/dist/tools/task.js.map +1 -1
  59. 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: 25)
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 tools 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.
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
- history.jsonl # conversation history
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 8KB are smart-truncated (first 50 + last 50 lines) to preserve context.
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 25 iterations the REPL asks if you want to continue.
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 the full harness with MCP, sub-agents, and a polished UI. ag is not trying to replace it.
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 # the loop + skill activation
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 # three-tier file memory
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
  ```
@@ -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,CAkB/E;AAED,wBAAgB,QAAQ,IAAI,IAAI,CAkC/B"}
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"}
@@ -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
- options.maxIterations = parseInt(args[++i], 10);
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: 25)
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 @iambarryking/ag # Run directly
59
- npm install -g @iambarryking/ag # Install globally
61
+ npx @element47/ag # Run directly
62
+ npm install -g @element47/ag # Install globally
60
63
  `);
61
64
  }
62
65
  //# sourceMappingURL=parser.js.map
@@ -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;YAAE,OAAO,CAAC,aAAa,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;aACzH,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"}
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"}
@@ -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;YAwNd,aAAa;IAgZ3B,OAAO,CAAC,GAAG;CAGZ"}
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
- // ── Escape key listener: immediate visual feedback ──
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
- try {
366
- for await (const chunk of this.agent.chatStream(message, controller.signal)) {
367
- switch (chunk.type) {
368
- case 'thinking':
369
- clearSpinner();
370
- if (hasText) {
371
- flushLines(true);
372
- process.stderr.write('\n');
373
- hasText = false;
374
- }
375
- setSpinner(startSpinner(chunk.content || 'thinking'));
376
- break;
377
- case 'text':
378
- clearSpinner();
379
- if (!hasText) {
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
- hasText = false;
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
- case 'tool_end': {
401
- clearSpinner();
402
- hadTools = true;
403
- let endLabel = chunk.toolName || '';
404
- const activeSkills = this.agent.getActiveSkillNames();
405
- if (endLabel === 'bash' && activeSkills.length > 0) {
406
- endLabel = `${endLabel} via ${activeSkills[activeSkills.length - 1]}`;
407
- }
408
- const icon = chunk.success ? `${C.green}✓` : `${C.red}✗`;
409
- const preview = (chunk.content || '').slice(0, 150).split('\n')[0];
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
- case 'done':
414
- clearSpinner();
415
- if (hasText) {
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
- parsed = parseInt(value, 10);
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());