termux-dev 1.8.2 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +16 -6
  2. package/assets/banner.svg +1 -1
  3. package/assets/preview.svg +1 -1
  4. package/dist/cli/background-ui.js +8 -8
  5. package/dist/cli/files.js +2 -12
  6. package/dist/cli/finder.js +10 -2
  7. package/dist/cli/headless.js +3 -2
  8. package/dist/cli/index.js +131 -75
  9. package/dist/cli/keys.js +12 -3
  10. package/dist/cli/liveblock.js +47 -9
  11. package/dist/cli/mcp-panel.js +2 -0
  12. package/dist/cli/picker.js +2 -0
  13. package/dist/cli/prompt.js +54 -7
  14. package/dist/cli/provider-manager.js +197 -0
  15. package/dist/cli/providers.js +0 -5
  16. package/dist/cli/settings.js +38 -17
  17. package/dist/cli/typeahead.js +10 -17
  18. package/dist/cli/ui.js +33 -13
  19. package/dist/cli/update-screen.js +383 -0
  20. package/dist/cli/updater.js +119 -145
  21. package/dist/cli/usage-panel.js +17 -4
  22. package/dist/cli/width.js +18 -0
  23. package/dist/core/background.js +14 -0
  24. package/dist/core/context-stats.js +1 -0
  25. package/dist/core/cost.js +25 -11
  26. package/dist/core/custom-providers.js +161 -0
  27. package/dist/core/images.js +46 -0
  28. package/dist/core/local-config.js +3 -1
  29. package/dist/core/locales/de.js +69 -7
  30. package/dist/core/locales/en.js +69 -7
  31. package/dist/core/locales/es.js +69 -7
  32. package/dist/core/locales/fr.js +69 -7
  33. package/dist/core/locales/ja.js +69 -7
  34. package/dist/core/locales/pt.js +69 -7
  35. package/dist/core/locales/ru.js +69 -7
  36. package/dist/core/locales/uk.js +69 -7
  37. package/dist/core/locales/zh.js +69 -7
  38. package/dist/core/loop.js +32 -1
  39. package/dist/core/prices.js +8 -4
  40. package/dist/core/session-media.js +115 -0
  41. package/dist/core/session.js +17 -2
  42. package/dist/core/title.js +19 -2
  43. package/dist/core/types.js +1 -1
  44. package/dist/core/usage.js +41 -4
  45. package/dist/permissions/guard.js +3 -0
  46. package/dist/providers/anthropic.js +67 -13
  47. package/dist/providers/index.js +16 -3
  48. package/dist/providers/openai.js +99 -41
  49. package/dist/providers/shared.js +83 -0
  50. package/dist/tools/fs.js +32 -2
  51. package/package.json +1 -1
package/README.md CHANGED
@@ -55,10 +55,12 @@ The terminal interface is built around a framed prompt that carries the status i
55
55
  When an architectural plan is finalized, review and confirm with `Go` to immediately unleash the coder agent.
56
56
  - **Multimodal Vision Attachments:**
57
57
  Paste screenshots straight from the clipboard with `Ctrl+V` or `Alt+V` (also `/image`, or a pasted file path). They appear as badges like `[Image #1 203kb]` and are sent to the model as real images, unless the model is known to read text only. Needs `wl-clipboard` on Wayland, `xclip` on X11, `pngpaste` on macOS.
58
+ - **The agent can look at images on disk:**
59
+ Ask it about `screenshot.png` or a mockup in any folder and it opens the file with `read_file`, the same way it sees a picture you attach. PNG, JPEG, WebP and GIF up to 5 MB; the file contents must really be an image. Only the newest pictures are sent in full, older ones stay as a note in the chat (the model can open the file again). A model without vision gets a clear message instead.
58
60
  - **Built-in Live Web Server (`/serve`):**
59
61
  Instantly preview web apps and HTML5 games on port 3000. Seamlessly opens on Android via `termux-open-url`.
60
62
  - **Background tasks (`/tasks`):**
61
- The agent can start a dev server, a watcher or a long test run in the background and keep working. It can wait for a line (`ready|error`), read the output later, and is told when a task ends or prints something it asked about; if you are at the prompt then, it picks the news up by itself. A `◈ 2 bg` badge in the prompt frame shows what runs; `/tasks` lists tasks, `/tasks <id>` shows output, `/tasks stop <id|all>` ends them. Starting a task asks for approval like `bash`; everything is stopped when devx exits. Set `"backgroundAutoWake": false` to stop the agent from reacting by itself.
63
+ The agent can start a dev server, a watcher or a long test run in the background and keep working. It can wait for a line (`ready|error`), read the output later, and is told when a task ends or prints something it asked about; if you are at the prompt then, it picks the news up by itself. A `◈ 2 bg` badge in the prompt frame shows what runs, and `· 1 failed` is added when a task ended badly until devx has told the agent about it; `/tasks` lists tasks, `/tasks <id>` shows output, `/tasks stop <id|all>` ends them. Starting a task asks for approval like `bash`; everything is stopped when devx exits. Set `"backgroundAutoWake": false` to stop the agent from reacting by itself.
62
64
  - **Instant Snapshot Rollback (`/undo`):**
63
65
  Every edit made with the file tools (`write_file`, `edit_file`) is recorded and can be rolled back with a single command. Changes made by shell commands (`bash`) are **not** tracked, and snapshots live in memory for the current session only.
64
66
  - **Self-Healing Diagnostics & Auto-Installer:**
@@ -77,6 +79,8 @@ The terminal interface is built around a framed prompt that carries the status i
77
79
  English (default), Русский, Українська, Deutsch, Español, Français, Português, 中文 and 日本語. Pick one in `/settings` → `Language`; it applies at once and is remembered. Messages, hints and the settings screen are translated; code, tool names and the model's own answers are not.
78
80
  - **Universal AI Provider Support:**
79
81
  **OpenRouter**, **Google Gemini**, **DeepSeek**, **Groq**, **Mistral**, **OpenAI**, **Anthropic**, **Alibaba**, and local models (**Ollama**, **LM Studio**).
82
+ - **Your own providers:**
83
+ `/provider` → **Add a provider**: give it a name, an address (any OpenAI- or Anthropic-compatible API) and a key. It is saved in `~/.devxrc.json` and shows up in the list next to the known ones; choose it again to use it, change its name, address or key, or delete it. A server on your own machine (LM Studio, a local gateway) can be added without a key.
80
84
 
81
85
  ---
82
86
 
@@ -161,7 +165,7 @@ Type `/` in the prompt to open the autocomplete command palette:
161
165
  | **`/plan`** | Switch to **PLAN** mode (architect & requirements planner) |
162
166
  | **`/agent`** | Switch to **AGENT** mode (coder & autonomous executor) |
163
167
  | **`/mcp`** | Manage Model Context Protocol (MCP) servers & external tools (`/mcp reload`) |
164
- | **`/usage`** | A panel (close with `Esc`): what fills the context window (system prompt, tool schemas, messages, tool results, images), the session cost and how sure it is, tokens, cached input, traffic |
168
+ | **`/usage`** | A panel (close with `Esc`): what fills the context window (system prompt, tool schemas, messages, tool results, images), the session cost and how sure it is, what was paid for (input, cached input, cache writes, output: tokens and money), traffic |
165
169
  | **`/export`** | Export session conversation to a clean GitHub-Flavored Markdown transcript |
166
170
  | **`/theme [name]`** | Switch the UI theme: searchable list of 18 palettes with a live preview (see [Themes](#themes)) |
167
171
  | **`/doctor`** | Run environment health diagnostics (Node, Termux, Compilers, API keys) |
@@ -181,7 +185,7 @@ Type `/` in the prompt to open the autocomplete command palette:
181
185
  | **`/resume`** | Browse and resume saved chat sessions |
182
186
  | **`/session del`** | Selectively or bulk delete saved sessions |
183
187
  | **`/settings`** | Configure theme, interface, YOLO auto-approve, project memory, update checks and the interface language |
184
- | **`/update`** | Check and install latest updates from GitHub |
188
+ | **`/update`** | Check for a new version: a card with what is new, then **Update now**, **Later** or (at start-up) **Skip this version**. The install runs on a live screen step by step, checks that npm really has the version, and restarts devx. If it fails you get the reason and the command to run by hand |
185
189
  | **`/model`** | Switch model for the active provider with live search |
186
190
  | **`/effort [level]`** | Set the reasoning effort. Only the levels the current model supports are offered (see [Reasoning effort](#reasoning-effort)) |
187
191
  | **`/provider`** | Switch provider (OpenRouter, Gemini, Groq, DeepSeek, Local) |
@@ -200,8 +204,9 @@ Type `/` in the prompt to open the autocomplete command palette:
200
204
  - **`@filename`** — Fast fuzzy file autocomplete and full file embedding.
201
205
  - **`Backspace`** — Atomically deletes entire badges `[Pasted text #1]` and `[1.png 203kb]` in one press.
202
206
  - **`Ctrl+Backspace`** / **`Alt+Backspace`** — Erase the text word by word (also while the agent works).
203
- - **`Ctrl+Enter`** — While the agent works: stop its current task and send your message right now (`Alt+Enter` does the same on terminals that cannot tell `Ctrl+Enter` from `Enter`).
207
+ - **`Ctrl+Enter`** — While typing: start a new line (a new paragraph) instead of sending. `Alt+Enter` and `Shift+Enter` do the same where the terminal reports them; on a terminal that cannot tell them from `Enter` (a phone keyboard, for one) type `\` before `Enter`.
204
208
  - **`Ctrl+P`** / **`Ctrl+O`** — Unfold or fold the reasoning of the model (`Ctrl+P`) and the commands with their output (`Ctrl+O`) in the whole chat, also what is already above. Both are folded by default; works while the agent works and at the prompt.
209
+ - **`Esc`** — Stop the current answer and whatever it started (a running command); what you typed waits in the next prompt. Esc also closes every list and card.
205
210
  - **`Ctrl+C`** — Stop current generation immediately or exit.
206
211
 
207
212
  ---
@@ -268,7 +273,7 @@ A model that none of the sources describes gets no levels instead of a guess. If
268
273
 
269
274
  ## Typing while the agent works
270
275
 
271
- The prompt frame stays at the bottom of the terminal during a task (with the live context and cost), and the agent's work scrolls above it. Type into it while the agent reads files, runs commands or thinks, and press Enter: the message is queued and the agent receives it **at its next step**, between two tool calls, so it carries on with its work but takes your remark into account. A message typed while the model writes its final answer gets an answer of its own instead of being dropped. `Ctrl+Enter` does not wait for the next step: it stops the task the agent is on and sends your message at once (your terminal must report the key; `Alt+Enter` is the fallback). `Ctrl+Backspace` erases a word. `Esc` clears the line, `Ctrl+C` still stops the agent and puts what you typed back into the next prompt. `@file` mentions work in these messages too. While an approval card or a question is on screen the line steps aside, so your `y` / `n` never ends up in the message. Turn it off with `"ui": { "typeAhead": false }` in `~/.devxrc.json`.
276
+ The prompt frame stays at the bottom of the terminal during a task (with the live context and cost), and the agent's work scrolls above it. Type into it while the agent reads files, runs commands or thinks, and press Enter: the message is queued and the agent receives it **at its next step**, between two tool calls, so it carries on with its work but takes your remark into account. A message typed while the model writes its final answer gets an answer of its own instead of being dropped. `Ctrl+Enter` starts a new line in your message instead of sending it (`Alt+Enter` and `Shift+Enter` too; a `\` before `Enter` does it on terminals that cannot tell them apart). `Ctrl+Backspace` erases a word. `Esc` (or `Ctrl+C`) stops the agent and puts what you typed back into the next prompt. `@file` mentions work in these messages too. While an approval card or a question is on screen the line steps aside, so your `y` / `n` never ends up in the message. Turn it off with `"ui": { "typeAhead": false }` in `~/.devxrc.json`.
272
277
 
273
278
  ---
274
279
 
@@ -293,6 +298,8 @@ Two wire formats exist: **OpenAI** (`/v1/chat/completions`) and **Anthropic** (`
293
298
 
294
299
  ## Cost
295
300
 
301
+ Every request resends the whole conversation, and the number next to the context meter adds up what each of those requests cost (cached input is cheaper, and priced as such). The context meter itself shows 0 for an empty chat and, from the first message on, everything the next request carries: system prompt and tool schemas included.
302
+
296
303
  The number next to the context meter is the money spent in this session. It says how sure devx is:
297
304
 
298
305
  | Shown | Meaning |
@@ -302,7 +309,7 @@ The number next to the context meter is the money spent in this session. It says
302
309
  | `$0.0123+` | at least this much: some requests have no known price and are not counted |
303
310
  | `n/a` | no price is known for the model |
304
311
 
305
- Prices are looked up in this order, the first that knows the model wins: the cost the provider put into its response (OpenRouter `usage.cost`, LiteLLM) - your own `prices` in `~/.devxrc.json` - the endpoint's own price list (OpenRouter, LiteLLM) - the models.dev catalogue (about 8500 models; a snapshot ships with devx, `/effort refresh` downloads the current one). Cached input tokens, cache writes and long-context tiers are priced separately, as the vendors do. A model that is not found is never shown as free: only `:free` models and local servers cost nothing. `/usage` also lists the cached input and how many requests could not be priced.
312
+ Prices are looked up in this order, the first that knows the model wins: the cost the provider put into its response (OpenRouter `usage.cost`, LiteLLM) - your own `prices` in `~/.devxrc.json` - the endpoint's own price list (OpenRouter, LiteLLM) - the models.dev catalogue (about 8500 models; a snapshot ships with devx, `/effort refresh` downloads the current one). Cached input tokens, cache writes and long-context tiers are priced separately, as the vendors do. A request that went out but got no answer (cancelled with Ctrl+C, connection lost, a gateway timeout of a slow model) is usually billed by the provider all the same, and a retry sends the whole conversation again: such requests are counted too, as an estimate (`~`). The small side jobs (chat title, `/commit` message, `/compact` summary) run with the lightest reasoning level the model offers and their cost is part of the total. A model that is not found is never shown as free: only `:free` models and local servers cost nothing. `/usage` also lists the cached input and how many requests could not be priced.
306
313
 
307
314
  ```json
308
315
  {
@@ -376,9 +383,12 @@ Colours are exact on terminals with truecolor support (`COLORTERM=truecolor`); o
376
383
  - **Project MCP servers need approval.** MCP servers declared by a project (`.mcp.json`, `.devx/mcp.json`, `.claude/mcp.json`, `.devx.json`) are programs that would run on your machine. You review the exact commands once; if they change, you are asked again. In headless mode (`-p`) unapproved project servers are skipped. Servers from `~/.devxrc.json` and `~/.devx/mcp.json` always start.
377
384
  - **Confirmations.** In safe mode `bash`, `install_package`, MCP tools, the preview server and every read or write **outside the project folder** require confirmation.
378
385
  - **YOLO (`--yolo`) is not a sandbox.** It skips routine confirmations, but still asks before dangerous shell commands (recursive deletes outside the project, `find -delete` on broad paths, `curl … | sh`, `sudo`, `git push --force`, …) and before touching credentials (`~/.ssh`, `~/.aws`, `~/.devxrc.json`, `.env`, private keys). The shell check analyses commands token by token, but a deliberately obfuscated command can still slip through, so use YOLO only in folders and environments you can afford to lose. In headless mode those confirmations cannot be shown, so the action is denied.
386
+ - **Extra (`--extra`, or `/settings` while YOLO is on) asks nothing at all.** Not the dangerous commands, not the credential files, not a new MCP server: the agent may run any command. A warning is shown when you switch it on, the prompt frame says `extra`, and like YOLO it is off again at the next start. Use it only in an environment you can afford to lose.
379
387
  - **`fetch_url`** refuses localhost, private networks and cloud metadata addresses (also after redirects). Set `DEVX_ALLOW_PRIVATE_NETWORK=1` to allow local documentation servers.
380
388
  - **`/serve`** listens on your local network (that is how the phone QR code works). It never serves dotfiles, keys, `node_modules`, or symlinks leaving the project, does not send permissive CORS headers and rejects requests addressed to a public host name.
381
389
  - **Secrets at rest.** `~/.devxrc.json`, `~/.devx/` and saved sessions are created readable by you only (`0600` / `0700`). Session files contain the full conversation, including file contents the agent read.
390
+ - **Silent connections.** A request that gets no answer and no new data for 300 s is given up on and sent again (a phone can lose the connection without the app noticing). `"requestTimeoutSeconds": 600` in `~/.devxrc.json` changes the time, `0` turns it off. A model that thinks for minutes without sending a byte needs a larger value.
391
+ - **Pictures in saved chats** are kept in files next to the chat (`~/.devx/sessions/<id>.images/`), not inside its JSON, so saving a chat stays fast and `/resume` opens quickly. They are removed with the chat.
382
392
  - **Limits.** `bash` stops commands after 120 s by default (the model can pass `timeout_seconds`, max 1800) and closes stdin. Long output keeps its beginning and its end. `read_file` returns at most ~60 000 characters per call and supports `startLine` / `endLine`.
383
393
 
384
394
  ---
package/assets/banner.svg CHANGED
@@ -16,7 +16,7 @@
16
16
  <rect width="1000" height="280" fill="url(#bg)"/>
17
17
  </g>
18
18
  <rect x="1.5" y="1.5" width="997" height="277" rx="18" fill="none" stroke="#23252c" stroke-width="1.5"/>
19
- <text x="500.0" y="70" text-anchor="middle" font-family="'Inter',system-ui,-apple-system,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif" font-size="13" font-weight="600" letter-spacing="5" fill="#7d8896">THE TERMINAL-NATIVE AI CODING AGENT<tspan dx="16" fill="#2fe3ee" font-weight="700">v1.8.2</tspan></text>
19
+ <text x="500.0" y="70" text-anchor="middle" font-family="'Inter',system-ui,-apple-system,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif" font-size="13" font-weight="600" letter-spacing="5" fill="#7d8896">THE TERMINAL-NATIVE AI CODING AGENT<tspan dx="16" fill="#2fe3ee" font-weight="700">v1.9.1</tspan></text>
20
20
  <g>
21
21
  <text x="500.0" y="178" text-anchor="middle" font-family="'JetBrains Mono','Fira Code','SF Mono',ui-monospace,Menlo,Consolas,'DejaVu Sans Mono',monospace" font-size="112" font-weight="800" letter-spacing="14" fill="#2fe3ee">DEVX</text>
22
22
  </g>
@@ -32,7 +32,7 @@
32
32
  <rect x="595.2" y="574.0" width="243.2" height="19" fill="#000000"/>
33
33
  <text x="44.8" y="62.0" class="t" fill="#b794f6">▌</text>
34
34
  <text x="61.6" y="62.0" class="t b" fill="#f4f7fb">devx</text>
35
- <text x="112.0" y="62.0" class="t" fill="#5f6b7a">v1.8.2</text>
35
+ <text x="112.0" y="62.0" class="t" fill="#5f6b7a">v1.9.1</text>
36
36
  <text x="61.6" y="106.0" class="t" fill="#5f6b7a">model</text>
37
37
  <text x="128.8" y="106.0" class="t" fill="#e5eaf1">deepseek-v4.1-flash · think high</text>
38
38
  <text x="406.0" y="106.0" class="t" fill="#5f6b7a">/model</text>
@@ -1,12 +1,14 @@
1
1
  import { formatDuration } from '../core/background.js';
2
2
  import { palette } from './ui.js';
3
3
  import { displayWidth, padEndWidth, truncateWidth } from './width.js';
4
- /** What the prompt frame shows: how many tasks run, how many ended badly and were not looked at yet. */
5
- export function backgroundSummary(manager, seenFailures) {
6
- const all = manager.list();
4
+ /**
5
+ * What the prompt frame shows: how many tasks run, and how many ended badly without the model
6
+ * having been told yet. The failure disappears from the frame once the model has the news.
7
+ */
8
+ export function backgroundSummary(manager) {
7
9
  return {
8
- running: all.filter((t) => t.status === 'running').length,
9
- failed: all.filter((t) => t.status === 'failed' && !seenFailures.has(t.id)).length,
10
+ running: manager.running().length,
11
+ failed: manager.unreportedFailures(),
10
12
  };
11
13
  }
12
14
  const STATE_LABEL = {
@@ -81,11 +83,10 @@ export function noteRows(content) {
81
83
  .filter(Boolean);
82
84
  }
83
85
  /** `/tasks ...`: returns what to print. Stopping is asynchronous, so this is too. */
84
- export async function runTasksCommand(args, manager, cols, markSeen) {
86
+ export async function runTasksCommand(args, manager, cols) {
85
87
  const [first, second] = args;
86
88
  const word = (first ?? '').toLowerCase();
87
89
  if (!first || word === 'list' || word === 'ls') {
88
- markSeen(manager.list().map((t) => t.id));
89
90
  return renderTaskList(manager, cols);
90
91
  }
91
92
  if (word === 'stop' || word === 'kill') {
@@ -111,6 +112,5 @@ export async function runTasksCommand(args, manager, cols, markSeen) {
111
112
  const id = ['output', 'logs', 'log', 'show'].includes(word) ? second : first;
112
113
  if (!id)
113
114
  return [` ${palette.warn('!')} ${palette.warn('Usage: /tasks output <id>')}`];
114
- markSeen([id]);
115
115
  return renderTaskOutput(manager, id, cols);
116
116
  }
package/dist/cli/files.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import fs from 'fs/promises';
2
2
  import fsSync from 'fs';
3
3
  import path from 'path';
4
+ import { IMAGE_EXTS, IMAGE_MAX_BYTES, IMAGE_WARN_BYTES } from '../core/images.js';
4
5
  const IGNORED_NAMES = new Set([
5
6
  'node_modules',
6
7
  '.git',
@@ -49,18 +50,7 @@ export async function scanProjectFiles(force = false) {
49
50
  lastScanTime = now;
50
51
  return results;
51
52
  }
52
- // Formats the image APIs accept (SVG is text, not an image for a model).
53
- const IMAGE_EXTS = {
54
- '.png': 'image/png',
55
- '.jpg': 'image/jpeg',
56
- '.jpeg': 'image/jpeg',
57
- '.webp': 'image/webp',
58
- '.gif': 'image/gif'
59
- };
60
- /** Above this an API usually refuses the image (Anthropic: 5 MB). */
61
- export const IMAGE_WARN_BYTES = 5 * 1024 * 1024;
62
- /** Above this the image is not sent at all. */
63
- export const IMAGE_MAX_BYTES = 20 * 1024 * 1024;
53
+ export { IMAGE_WARN_BYTES, IMAGE_MAX_BYTES };
64
54
  export async function resolveAtMentions(text) {
65
55
  // @path, or @"path with spaces" for a path that has unusual characters
66
56
  const mentionRegex = /@(?:"([^"\n]+)"|([a-zA-Z0-9_\-./\u0400-\u04FF]+))/g;
@@ -38,10 +38,14 @@ function hintLine(width, typing) {
38
38
  return '';
39
39
  }
40
40
  /** The card around some content rows; `rows` are already padded to the inner width by the caller's `box`. */
41
- function frame(title, subtitle, cols, body, footer) {
41
+ /** The card every list and field is drawn in: a title and a subtitle in the border, a body, a line of hints under it. */
42
+ export function frame(title, subtitle, cols, body, footer) {
42
43
  if (cols < 60) {
43
44
  const width = Math.max(20, cols - 1);
44
- const head = ' ' + palette.bold(palette.accent(title)) + (subtitle ? palette.faint(' · ' + truncateWidth(subtitle, Math.max(6, width - displayWidth(title) - 6))) : '');
45
+ // Never wider than the screen, however long the title is in the language of the interface.
46
+ const shownTitle = truncateWidth(title, Math.max(4, width - 1));
47
+ const subRoom = width - 1 - displayWidth(shownTitle) - 3;
48
+ const head = ' ' + palette.bold(palette.accent(shownTitle)) + (subtitle && subRoom >= 6 ? palette.faint(' · ' + truncateWidth(subtitle, subRoom)) : '');
45
49
  const plain = (c, shaded = false) => (shaded ? palette.shade(padEndWidth(' ' + c, width)) : ' ' + c);
46
50
  return [head, ...body(width - 1, plain), ' ' + footer];
47
51
  }
@@ -132,9 +136,11 @@ export function runFinder(options, io = { input: process.stdin, output: process.
132
136
  const rows = () => output.rows || 40;
133
137
  const draw = () => block.draw(renderFinder(options, query, shown, focus, output.columns || 80, rows()));
134
138
  const restore = openIO(io);
139
+ const stopWatching = block.watchResize(draw);
135
140
  const finish = (value) => {
136
141
  done = true;
137
142
  input.off('data', onData);
143
+ stopWatching();
138
144
  block.erase();
139
145
  restore();
140
146
  resolve(value);
@@ -209,9 +215,11 @@ export function runTextInput(options, io = { input: process.stdin, output: proce
209
215
  let done = false;
210
216
  const draw = () => block.draw(renderTextInput(options, value, output.columns || 80));
211
217
  const restore = openIO(io);
218
+ const stopWatching = block.watchResize(draw);
212
219
  const finish = (result) => {
213
220
  done = true;
214
221
  input.off('data', onData);
222
+ stopWatching();
215
223
  block.erase();
216
224
  restore();
217
225
  resolve(result);
@@ -13,7 +13,8 @@ import { MCPManager } from '../mcp/manager.js';
13
13
  import { getBackgroundManager } from '../core/background.js';
14
14
  export async function runHeadlessMode(userPrompt, config, options = {}) {
15
15
  const planMode = !!options.planMode;
16
- const isYolo = !!options.yolo || !!config.autoApprove;
16
+ const isExtra = !!options.extra || !!config.autoApproveAll;
17
+ const isYolo = !!options.yolo || !!config.autoApprove || isExtra;
17
18
  const isQuiet = !!options.quiet;
18
19
  const isJson = !!options.json;
19
20
  let history = new History();
@@ -43,7 +44,7 @@ export async function runHeadlessMode(userPrompt, config, options = {}) {
43
44
  await refreshFormats(config).catch(() => null);
44
45
  const provider = createProvider(config);
45
46
  const tools = getTools(planMode);
46
- const guard = new CLIConsoleGuard(isYolo);
47
+ const guard = new CLIConsoleGuard(isYolo, [], { extra: isExtra });
47
48
  const agent = new Agent(config, provider, tools, history, guard);
48
49
  if (!isQuiet && !isJson) {
49
50
  console.log(pc.bold(pc.cyan(`devx v${DEVX_VERSION} (headless) | ${planMode ? 'PLAN' : 'AGENT'} | ${config.model}`)));