@element47/ag 4.5.4 → 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 (66) hide show
  1. package/README.md +59 -11
  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 +137 -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 +16 -8
  19. package/dist/core/agent.d.ts.map +1 -1
  20. package/dist/core/agent.js +139 -406
  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 +5 -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 +23 -1
  45. package/dist/memory/memory.d.ts.map +1 -1
  46. package/dist/memory/memory.js +121 -8
  47. package/dist/memory/memory.js.map +1 -1
  48. package/dist/tools/__tests__/task.test.d.ts +2 -0
  49. package/dist/tools/__tests__/task.test.d.ts.map +1 -0
  50. package/dist/tools/__tests__/task.test.js +116 -0
  51. package/dist/tools/__tests__/task.test.js.map +1 -0
  52. package/dist/tools/agent.d.ts +7 -0
  53. package/dist/tools/agent.d.ts.map +1 -0
  54. package/dist/tools/agent.js +92 -0
  55. package/dist/tools/agent.js.map +1 -0
  56. package/dist/tools/file.d.ts.map +1 -1
  57. package/dist/tools/file.js +9 -5
  58. package/dist/tools/file.js.map +1 -1
  59. package/dist/tools/grep.d.ts.map +1 -1
  60. package/dist/tools/grep.js +7 -5
  61. package/dist/tools/grep.js.map +1 -1
  62. package/dist/tools/task.d.ts +6 -0
  63. package/dist/tools/task.d.ts.map +1 -0
  64. package/dist/tools/task.js +135 -0
  65. package/dist/tools/task.js.map +1 -0
  66. 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.
@@ -92,6 +114,8 @@ All action-based tools follow the pattern: `tool(action, ...params)`.
92
114
  | `git` | `status`, `init`, `branch`, `commit`, `push` | Git workflow |
93
115
  | `grep` | `search`, `find` | Search file contents (regex), find files by glob |
94
116
  | `web` | `fetch`, `search` | Fetch web pages, search for current info |
117
+ | `task` | `create`, `list`, `update`, `read`, `remove`, `clear` | Track tasks for multi-step work |
118
+ | `agent` | — | Spawn sub-agents for parallel work |
95
119
  | `skill` | — | Activate a skill by name |
96
120
 
97
121
  ### Background Processes
@@ -106,6 +130,22 @@ bash(action="kill", pid=12345) → stop the process
106
130
 
107
131
  Background processes are tracked by PID. Output is buffered (100KB rolling). All background processes are killed on exit.
108
132
 
133
+ ### Sub-Agents
134
+
135
+ Spawn independent agents to work on tasks in parallel:
136
+
137
+ ```
138
+ agent(prompt="Research auth best practices for Node.js")
139
+ agent(prompt="Set up the database schema", taskId=2)
140
+ agent(prompt="Write unit tests", model="anthropic/claude-haiku")
141
+ ```
142
+
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.
144
+
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.
146
+
147
+ Sub-agents cannot spawn sub-sub-agents (depth limit = 1). Extensions loaded on sub-agents can check `agent.isSilent()` to avoid output.
148
+
109
149
  ## Custom Tools
110
150
 
111
151
  Drop a `.mjs` file in a tools directory and it gets loaded at startup:
@@ -319,9 +359,10 @@ Three tiers, all plain markdown you can edit directly:
319
359
  projects/
320
360
  <id>/
321
361
  memory.md # project: architecture, decisions
322
- plans/ # timestamped plan files
362
+ plans/ # timestamped plan files (created on demand)
323
363
  2026-04-13T12-31-22-add-auth.md
324
- history.jsonl # conversation history
364
+ tasks.json # task tracking (created on demand)
365
+ history.jsonl # conversation history (created on demand)
325
366
  ```
326
367
 
327
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.
@@ -455,15 +496,15 @@ Tools execute in parallel when the model returns multiple tool calls.
455
496
  - A compact project file listing gives the model awareness of project structure.
456
497
  - `tool_choice: "auto"` encourages tool use over conversational responses.
457
498
  - Dangerous bash commands (`find ~`, `rm -rf /`, etc.) are blocked before execution.
458
- - 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.
459
500
  - For multi-step coding tasks, the agent creates a plan before starting and updates it as it goes.
460
501
  - For simple questions, it just answers directly.
461
- - At 25 iterations the REPL asks if you want to continue.
502
+ - At 200 iterations the REPL asks if you want to continue.
462
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.
463
504
 
464
505
  ## When to use something else
465
506
 
466
- - **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.
467
508
  - **aider** -- if your workflow is git-centric (commit-per-change, diff-based editing).
468
509
  - **Cursor / Windsurf** -- if you want IDE integration. ag is terminal-only.
469
510
 
@@ -476,9 +517,14 @@ src/
476
517
  cli.ts # entry point
477
518
  cli/parser.ts # arg parsing + help
478
519
  cli/repl.ts # interactive REPL (unified /noun commands)
479
- 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)
480
524
  core/config.ts # persistent config (~/.ag/config.json)
481
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
482
528
  core/skills.ts # skill discovery, parsing, loading
483
529
  core/registry.ts # skills.sh search + GitHub install
484
530
  core/types.ts # interfaces
@@ -488,13 +534,15 @@ src/
488
534
  core/guardrails.ts # prompt injection scanning (5 threat categories)
489
535
  core/loader.ts # custom tool loader (~/.ag/tools/, .ag/tools/)
490
536
  core/permissions.ts # permission manager with glob pattern matching
491
- 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
492
540
  tools/file.ts # file reading + directory listing
493
- tools/bash.ts # shell execution (with command safeguards)
494
- tools/memory.ts # memory tool
495
- tools/plan.ts # plan management tool
496
541
  tools/git.ts # git operations tool
497
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
498
546
  tools/web.ts # web fetch + search tool
499
547
  tools/skill.ts # skill activation tool
500
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;IA+Y3B,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);
@@ -602,6 +661,7 @@ export class REPL {
602
661
  console.error(` Global: ${stats.globalMemory ? C.green + 'yes' : C.dim + 'none'}${C.reset}`);
603
662
  console.error(` Project: ${stats.projectMemory ? C.green + 'yes' : C.dim + 'none'}${C.reset}`);
604
663
  console.error(` Plans: ${C.cyan}${stats.planCount}${C.reset}`);
664
+ console.error(` Tasks: ${C.cyan}${stats.taskCount}${C.reset}`);
605
665
  console.error(` History: ${C.cyan}${stats.historyLines}${C.reset} messages`);
606
666
  if (global)
607
667
  console.error(`\n${C.bold}Global:${C.reset}\n${renderMarkdown(global)}`);
@@ -699,7 +759,12 @@ export class REPL {
699
759
  }
700
760
  let parsed = value;
701
761
  if (key === 'maxIterations') {
702
- 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;
703
768
  }
704
769
  else if (key === 'autoApprove') {
705
770
  parsed = ['true', '1', 'yes'].includes(value.toLowerCase());