agent-sh 0.12.24 → 0.12.26

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 CHANGED
@@ -19,10 +19,12 @@ So I built agent-sh. Under the hood it's a normal shell on top of node-pty — y
19
19
  ~ $ > draft a commit message # agent reads your diff and shell history
20
20
  ```
21
21
 
22
- I still use a proper coding harness for serious work — this doesn't replace that. But for the quick stuff in the terminal, I reach for agent-sh almost every day now. The built-in agent is lightweight and good enough for most of what I throw at it, and when it isn't, you can swap in [pi](examples/extensions/pi-bridge/) as the backend — `agent-sh install pi-bridge` followed by `agent-sh --backend pi`.
22
+ agent-sh is built to be agent-agnostic. You can [bring your own coding agent](#bring-your-own-agent) or use the built-in agent `ash` a lightweight, extensible agent if you'd like to build extensions on top of it.
23
23
 
24
24
  ## Quick Start
25
25
 
26
+ ### Installation
27
+
26
28
  Install from npm:
27
29
 
28
30
  ```bash
@@ -41,7 +43,36 @@ npm run build # produces dist/
41
43
  npm link # exposes `agent-sh` globally
42
44
  ```
43
45
 
44
- Pick one of the zero-config paths below no settings file needed. agent-sh auto-activates a built-in provider when it sees a known key.
46
+ Requires Node.js 18+. Currently supports **bash** and **zsh**; other shells (fish, nushell, etc.) are not yet wired up.
47
+
48
+ **Windows:** the interactive shell layer is bash/zsh-only. Run agent-sh inside **WSL** for the full experience. Native Windows (cmd.exe / PowerShell) is not supported as the host shell, though headless / library / ACP-bridge usage may work — file an issue if you hit a gap.
49
+
50
+ Tip — add a shell alias:
51
+
52
+ ```bash
53
+ alias ash="agent-sh"
54
+ ```
55
+
56
+ Once installed, pick a backend below.
57
+
58
+ ### Option A: Bring your own coding agent
59
+
60
+ If you already use a coding agent, host it inside agent-sh — same terminal, same `>` entry point, same shell-context wiring. Three bridges ship in the box:
61
+
62
+ - **pi** — [pi-mono](https://github.com/badlogic/pi-mono) coding agent
63
+ - **claude-code** — official [Claude Agent SDK](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk)
64
+ - **opencode** — [opencode](https://opencode.ai/) via `@opencode-ai/sdk`
65
+
66
+ ```bash
67
+ agent-sh install pi-bridge
68
+ agent-sh --backend pi
69
+ ```
70
+
71
+ See [Bring your own agent](#bring-your-own-agent) below for full details and the other backends.
72
+
73
+ ### Option B: Use the built-in agent (ash)
74
+
75
+ `ash` is agent-sh's own lightweight agent. It works with any OpenAI-compatible API — pick one of the zero-config paths below, no settings file needed. agent-sh auto-activates a built-in provider when it sees a known key.
45
76
 
46
77
  **Hosted models via OpenRouter** (300+ models, one key):
47
78
 
@@ -77,15 +108,36 @@ Once running, switch models at any time with `/model <name>` (tab-completes; sel
77
108
 
78
109
  For richer configuration (multiple providers, extensions), run `agent-sh init` to scaffold `~/.agent-sh/settings.json` with copy-pasteable examples. See the [Usage Guide](docs/usage.md) for the full list of supported providers.
79
110
 
80
- Tip add a shell alias:
111
+ `ash` is designed to be extended. Extensions can add tools, content transforms (e.g. render LaTeX or Mermaid), themes, slash commands, or new input modes — see [Extensions](docs/extensions.md) for the full surface.
81
112
 
82
- ```bash
83
- alias ash="agent-sh"
84
- ```
113
+ ## Bring your own agent
85
114
 
86
- Requires Node.js 18+. Currently supports **bash** and **zsh**; other shells (fish, nushell, etc.) are not yet wired up.
115
+ The built-in agent (`ash`) is the default, but agent-sh can host a different coding agent as its backend — same terminal, same `>` entry point, same shell-context wiring. Three bridges ship in the box:
87
116
 
88
- **Windows:** the interactive shell layer is bash/zsh-only. Run agent-sh inside **WSL** for the full experience. Native Windows (cmd.exe / PowerShell) is not supported as the host shell, though headless / library / ACP-bridge usage may work — file an issue if you hit a gap.
117
+ - **[pi-bridge](examples/extensions/pi-bridge/)** runs [pi](https://github.com/badlogic/pi-mono) (`@mariozechner/pi-coding-agent`) in-process. Pi brings its own models, tools, and `~/.pi/agent/settings.json`.
118
+
119
+ ```bash
120
+ agent-sh install pi-bridge
121
+ agent-sh --backend pi
122
+ ```
123
+
124
+ - **[claude-code-bridge](examples/extensions/claude-code-bridge/)** — runs claude-code (the official [Claude Agent SDK](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk)) in-process. Uses claude-code's own `Read`/`Edit`/`Write`/`Bash`/`Glob`/`Grep` tools.
125
+
126
+ ```bash
127
+ agent-sh install claude-code-bridge
128
+ agent-sh --backend claude-code
129
+ ```
130
+
131
+ - **[opencode-bridge](examples/extensions/opencode-bridge/)** — runs [opencode](https://opencode.ai/) in-process via `@opencode-ai/sdk`. Uses opencode's tools, models, and `opencode auth login` credentials.
132
+
133
+ ```bash
134
+ agent-sh install opencode-bridge
135
+ agent-sh --backend opencode
136
+ ```
137
+
138
+ All three bridges receive agent-sh's per-query shell context (`<shell_events>`) and follow the PTY-tracked cwd, so the hosted agent sees what you ran and where you are. Switching at runtime with `/backend <name>` persists the choice across sessions automatically; the `--backend` flag above is per-session only.
139
+
140
+ **Caveat:** pi, claude-code, and opencode each manage their own tool surfaces, so agent-sh extensions that register tools (or skills, instructions, etc.) for the built-in `ash` agent generally won't be visible to a hosted backend. Frontend extensions (themes, content transforms, slash commands, the TUI renderer) keep working — only the agent-side capabilities differ. Use the bridges when you want that agent's toolset; stay on `ash` when you want agent-sh's extension ecosystem.
89
141
 
90
142
  ## Key Features
91
143
 
@@ -95,7 +147,7 @@ Requires Node.js 18+. Currently supports **bash** and **zsh**; other shells (fis
95
147
 
96
148
  **Context that just works.** Every query includes your cwd, recent commands, and their output. Run a failing test, type `> fix this`, and agent-sh knows exactly what happened. Context management works like shell history — continuous, persistent across restarts, no sessions to manage. See [Context Management](docs/context-management.md).
97
149
 
98
- **Any LLM, any backend.** agent-sh works with any OpenAI-compatible API out of the box. Define multiple providers in settings and switch models at runtime with `/model <name>`. Or swap in a completely different agent — `agent-sh install pi-bridge && agent-sh --backend pi` runs [pi](examples/extensions/pi-bridge/) as a drop-in backend.
150
+ **Any LLM, any backend.** agent-sh works with any OpenAI-compatible API out of the box. Define multiple providers in settings and switch models at runtime with `/model <name>`. Or swap in a completely different agent — bundled bridges run [pi](examples/extensions/pi-bridge/), [claude-code](examples/extensions/claude-code-bridge/), or [opencode](examples/extensions/opencode-bridge/) as a drop-in backend (see [Bring your own agent](#bring-your-own-agent)).
99
151
 
100
152
  **Extensible by design.** The entire system is built on a typed event bus. Extensions can add custom input modes, content transforms (render LaTeX as images, Mermaid as diagrams), themes, slash commands, or replace the agent backend entirely. The built-in TUI renderer is itself just an extension.
101
153
 
@@ -108,9 +108,9 @@ Extensions may register additional tools — follow their instructions.
108
108
  - Always check command exit codes for errors
109
109
 
110
110
  # Context Envelopes
111
- - \`<query_context>\` (e.g. \`<shell_events>\`): the user's situation when they sent this turn — ground "fix this" / "what just happened" requests with it.
111
+ - \`<query_context>\` (contains \`<cwd>\` always, and \`<shell_events>\` when there were user shell commands since the last turn): the user's situation when they sent this turn — \`<cwd>\` anchors where they are right now, \`<shell_events>\` grounds "fix this" / "what just happened" requests. Trust the most recent \`<cwd>\` over any cwd referenced in earlier history.
112
112
  - \`<dynamic_context>\`: current system state — in-flight work, mode markers, warnings.
113
- Either may be absent on any turn.
113
+ \`<dynamic_context>\` may be absent on any turn.
114
114
 
115
115
  # Preference Learning
116
116
 
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Tracks PTY commands and cwd, spills long outputs, contributes the
3
- * `<shell_events>` per-query envelope. Frontends without a PTY skip this
3
+ * per-query `<cwd>` (always) and `<shell_events>` (when there are fresh
4
+ * user-shell exchanges) signals. Frontends without a PTY skip this
4
5
  * built-in and the agent runs cwd-aware via core's process.cwd() default.
5
6
  */
6
7
  import type { ExtensionContext } from "../types.js";
@@ -43,15 +43,16 @@ export default function activate(ctx) {
43
43
  bus.on("shell:agent-exec-done", () => { agentShellActive = false; });
44
44
  // Override core's process.cwd() default with the PTY-tracked value.
45
45
  ctx.advise("cwd", () => currentCwd);
46
- ctx.registerContextProducer("shell-events", () => {
46
+ ctx.registerContextProducer("shell-context", () => {
47
+ const cwdTag = `<cwd>${currentCwd}</cwd>`;
47
48
  const fresh = exchanges.filter((ex) => ex.id > lastSeq && ex.source !== "agent");
48
49
  if (fresh.length === 0)
49
- return null;
50
+ return cwdTag;
50
51
  lastSeq = exchanges[exchanges.length - 1].id;
51
52
  const text = fresh.map(formatExchangeTruncated).filter(Boolean).join("\n");
52
53
  if (!text)
53
- return null;
54
- return `<shell_events>\n${text}\n</shell_events>`;
54
+ return cwdTag;
55
+ return `${cwdTag}\n<shell_events>\n${text}\n</shell_events>`;
55
56
  }, { mode: "per-query" });
56
57
  ctx.define("shell:context-recent", (n = 25) => {
57
58
  const recent = exchanges.slice(-n);
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn } from "node:child_process";
3
3
  import * as path from "node:path";
4
- import { activateShell } from "./shell/index.js";
4
+ import { activateShell, registerShellHandlers } from "./shell/index.js";
5
5
  import { createCore } from "./core.js";
6
6
  import { palette as p } from "./utils/palette.js";
7
7
  import { loadBuiltinExtensions } from "./extensions/index.js";
@@ -236,49 +236,11 @@ async function main() {
236
236
  }
237
237
  process.exit(0);
238
238
  };
239
- // ── Extension context (must precede shell activation) ────────
240
- if (process.env.DEBUG) {
241
- console.error('[agent-sh] Setting up extensions...');
242
- }
243
239
  const extCtx = core.extensionContext({ quit: cleanup });
244
- // ── Shell frontend bootstrap (special-cased; see src/shell/index.ts) ──
245
- if (process.env.DEBUG) {
246
- console.error('[agent-sh] Creating Shell...');
247
- }
248
- await new Promise(resolve => setTimeout(resolve, 100));
249
- shell = activateShell(extCtx, {
250
- cols,
251
- rows,
252
- shellPath: config.shell || process.env.SHELL || "/bin/bash",
253
- cwd: process.cwd(),
254
- onShowAgentInfo: () => {
255
- if (agentInfo) {
256
- return { info: `${p.dim}${agentInfo.name}${agentInfo.model ? ` (${agentInfo.model})` : ""}${p.reset}` };
257
- }
258
- return { info: "" };
259
- },
260
- });
261
- if (process.env.DEBUG) {
262
- console.error('[agent-sh] Shell created');
263
- }
264
- // ── Input mode ───────────────────────────────────────────────
265
- bus.emit("input-mode:register", {
266
- id: "agent",
267
- trigger: ">",
268
- label: "agent",
269
- promptIcon: "❯",
270
- indicator: "●",
271
- onSubmit(query, b) {
272
- b.emit("agent:submit", { query });
273
- },
274
- returnToSelf: true,
275
- });
276
- // Load built-in extensions (individually disableable via settings.disabledBuiltins)
240
+ // Before loadExtensions: extensions look up shell handlers at activation.
241
+ registerShellHandlers(extCtx);
242
+ // Load before spawning the shell so PS1 lands below the banner.
277
243
  await loadBuiltinExtensions(extCtx, getSettings().disabledBuiltins);
278
- // Load user extensions (may register alternative agent backends)
279
- if (process.env.DEBUG) {
280
- console.error('[agent-sh] Loading extensions...');
281
- }
282
244
  const loadExtensionsTimeoutMs = 10000;
283
245
  let loadedExtensions = [];
284
246
  await Promise.race([
@@ -287,17 +249,9 @@ async function main() {
287
249
  ]).catch((err) => {
288
250
  console.error(`Warning: ${err.message}`);
289
251
  });
290
- if (process.env.DEBUG) {
291
- console.error('[agent-sh] Extensions loaded');
292
- }
293
- // Names ride along so backend extensions can build banner sections.
294
252
  core.bus.emit("core:extensions-loaded", { names: loadedExtensions });
295
- // ── Activate agent backend ────────────────────────────────────
296
- // Extensions had their chance to register via agent:register-backend.
297
- // If none did, the built-in AgentLoop gets wired to bus events.
298
253
  const { names: backendNames } = core.bus.emitPipe("config:get-backends", { names: [], active: null });
299
254
  if (backendNames.length === 0) {
300
- shell?.kill();
301
255
  console.error("\nagent-sh: no agent backend available.\n\n" +
302
256
  " Export OPENROUTER_API_KEY or OPENAI_API_KEY for zero-config launch, or\n" +
303
257
  " pass --api-key on the command line, or\n" +
@@ -306,7 +260,6 @@ async function main() {
306
260
  process.exit(1);
307
261
  }
308
262
  if (config.backend && !backendNames.includes(config.backend)) {
309
- shell?.kill();
310
263
  const bridge = suggestBridgeFor(config.backend);
311
264
  const hint = bridge
312
265
  ? ` Try: agent-sh install ${bridge}\n`
@@ -316,9 +269,6 @@ async function main() {
316
269
  hint);
317
270
  process.exit(1);
318
271
  }
319
- // No await: banner must out-race the shell's PS1 arriving via PTY.
320
- core.activateBackend(config.backend);
321
- // ── Startup banner ───────────────────────────────────────────
322
272
  const settings = getSettings();
323
273
  if (settings.startupBanner !== false) {
324
274
  const termW = process.stdout.columns || 80;
@@ -346,6 +296,32 @@ async function main() {
346
296
  "\n " + hint + "\n" +
347
297
  borderLine + "\n\n");
348
298
  }
299
+ // 100ms sidesteps macOS SIGTTOU during fg-pgrp handoff.
300
+ await new Promise(resolve => setTimeout(resolve, 100));
301
+ shell = activateShell(extCtx, {
302
+ cols,
303
+ rows,
304
+ shellPath: config.shell || process.env.SHELL || "/bin/bash",
305
+ cwd: process.cwd(),
306
+ onShowAgentInfo: () => {
307
+ if (agentInfo) {
308
+ return { info: `${p.dim}${agentInfo.name}${agentInfo.model ? ` (${agentInfo.model})` : ""}${p.reset}` };
309
+ }
310
+ return { info: "" };
311
+ },
312
+ });
313
+ bus.emit("input-mode:register", {
314
+ id: "agent",
315
+ trigger: ">",
316
+ label: "agent",
317
+ promptIcon: "❯",
318
+ indicator: "●",
319
+ onSubmit(query, b) {
320
+ b.emit("agent:submit", { query });
321
+ },
322
+ returnToSelf: true,
323
+ });
324
+ core.activateBackend(config.backend);
349
325
  // ── Terminal lifecycle ────────────────────────────────────────
350
326
  process.on("SIGTERM", cleanup);
351
327
  process.on("SIGHUP", cleanup);
@@ -27,6 +27,11 @@ export interface ShellHandle {
27
27
  /** Forward terminal size changes to the PTY. */
28
28
  resize(cols: number, rows: number): void;
29
29
  }
30
+ /**
31
+ * Register shell-owned handlers extensions can `ctx.call`. Must run before
32
+ * `loadExtensions`; the handlers only need the bus, not the PTY.
33
+ */
34
+ export declare function registerShellHandlers(ctx: ExtensionContext): void;
30
35
  /**
31
36
  * Construct the Shell, wire resize forwarding, and register cleanup with the
32
37
  * provided ExtensionContext. Returns a handle the caller (typically
@@ -1,6 +1,19 @@
1
1
  import { Shell } from "./shell.js";
2
2
  import { StdoutSurface } from "../utils/compositor.js";
3
3
  import { TerminalBuffer } from "../utils/terminal-buffer.js";
4
+ /**
5
+ * Register shell-owned handlers extensions can `ctx.call`. Must run before
6
+ * `loadExtensions`; the handlers only need the bus, not the PTY.
7
+ */
8
+ export function registerShellHandlers(ctx) {
9
+ let terminalBufferSingleton;
10
+ ctx.define("terminal-buffer", () => {
11
+ if (terminalBufferSingleton !== undefined)
12
+ return terminalBufferSingleton;
13
+ terminalBufferSingleton = TerminalBuffer.createWired(ctx.bus);
14
+ return terminalBufferSingleton;
15
+ });
16
+ }
4
17
  /**
5
18
  * Construct the Shell, wire resize forwarding, and register cleanup with the
6
19
  * provided ExtensionContext. Returns a handle the caller (typically
@@ -13,14 +26,6 @@ export function activateShell(ctx, opts) {
13
26
  ctx.compositor.setDefault("agent", stdoutSurface);
14
27
  ctx.compositor.setDefault("query", stdoutSurface);
15
28
  ctx.compositor.setDefault("status", stdoutSurface);
16
- // Lazy because @xterm/headless is optional; null when not installed.
17
- let terminalBufferSingleton;
18
- ctx.define("terminal-buffer", () => {
19
- if (terminalBufferSingleton !== undefined)
20
- return terminalBufferSingleton;
21
- terminalBufferSingleton = TerminalBuffer.createWired(ctx.bus);
22
- return terminalBufferSingleton;
23
- });
24
29
  const shell = new Shell({
25
30
  bus: ctx.bus,
26
31
  handlers: { define: ctx.define, call: ctx.call },
@@ -229,9 +229,15 @@ export class InputHandler {
229
229
  return false;
230
230
  }
231
231
  renderModeInput() {
232
- this.view.clearAutocomplete();
233
- this.drawPrompt();
234
- this.updateAutocomplete();
232
+ this.view.beginFrame();
233
+ try {
234
+ this.view.clearAutocomplete();
235
+ this.drawPrompt();
236
+ this.updateAutocomplete();
237
+ }
238
+ finally {
239
+ this.view.endFrame();
240
+ }
235
241
  }
236
242
  updateAutocomplete() {
237
243
  const buf = this.editor.text;
@@ -278,13 +284,19 @@ export class InputHandler {
278
284
  else {
279
285
  this.editor.setText(selected.name);
280
286
  }
281
- this.view.clearAutocomplete();
282
- this.autocompleteActive = false;
283
- this.autocompleteItems = [];
284
- this.autocompleteIndex = 0;
285
- this.drawPrompt();
286
- if (isFileAc)
287
- this.updateAutocomplete();
287
+ this.view.beginFrame();
288
+ try {
289
+ this.view.clearAutocomplete();
290
+ this.autocompleteActive = false;
291
+ this.autocompleteItems = [];
292
+ this.autocompleteIndex = 0;
293
+ this.drawPrompt();
294
+ if (isFileAc)
295
+ this.updateAutocomplete();
296
+ }
297
+ finally {
298
+ this.view.endFrame();
299
+ }
288
300
  }
289
301
  dismissAutocomplete() {
290
302
  this.view.clearAutocomplete();
@@ -314,11 +326,17 @@ export class InputHandler {
314
326
  case "changed": {
315
327
  const switchMode = this.modes.get(this.editor.text);
316
328
  if (this.editor.text.length === 1 && switchMode && switchMode !== this.activeMode) {
317
- this.dismissAutocomplete();
318
- this.view.clearPromptArea();
319
- this.activeMode = switchMode;
320
- this.editor.clear();
321
- this.drawPrompt(false);
329
+ this.view.beginFrame();
330
+ try {
331
+ this.dismissAutocomplete();
332
+ this.view.clearPromptArea();
333
+ this.activeMode = switchMode;
334
+ this.editor.clear();
335
+ this.drawPrompt(false);
336
+ }
337
+ finally {
338
+ this.view.endFrame();
339
+ }
322
340
  break;
323
341
  }
324
342
  this.historyIndex = -1;
@@ -371,8 +389,14 @@ export class InputHandler {
371
389
  }
372
390
  case "cancel":
373
391
  if (this.autocompleteActive) {
374
- this.dismissAutocomplete();
375
- this.drawPrompt();
392
+ this.view.beginFrame();
393
+ try {
394
+ this.dismissAutocomplete();
395
+ this.drawPrompt();
396
+ }
397
+ finally {
398
+ this.view.endFrame();
399
+ }
376
400
  }
377
401
  else {
378
402
  this.exitMode();
@@ -393,9 +417,15 @@ export class InputHandler {
393
417
  this.autocompleteIndex === 0
394
418
  ? this.autocompleteItems.length - 1
395
419
  : this.autocompleteIndex - 1;
396
- this.view.clearAutocomplete();
397
- this.drawPrompt();
398
- this.view.drawAutocomplete({ items: this.autocompleteItems, selected: this.autocompleteIndex });
420
+ this.view.beginFrame();
421
+ try {
422
+ this.view.clearAutocomplete();
423
+ this.drawPrompt();
424
+ this.view.drawAutocomplete({ items: this.autocompleteItems, selected: this.autocompleteIndex });
425
+ }
426
+ finally {
427
+ this.view.endFrame();
428
+ }
399
429
  }
400
430
  else if (this.history.length > 0) {
401
431
  if (this.historyIndex === -1) {
@@ -406,8 +436,14 @@ export class InputHandler {
406
436
  this.historyIndex--;
407
437
  }
408
438
  this.editor.setText(this.history[this.historyIndex]);
409
- this.view.clearAutocomplete();
410
- this.drawPrompt();
439
+ this.view.beginFrame();
440
+ try {
441
+ this.view.clearAutocomplete();
442
+ this.drawPrompt();
443
+ }
444
+ finally {
445
+ this.view.endFrame();
446
+ }
411
447
  }
412
448
  break;
413
449
  case "arrow-down":
@@ -416,9 +452,15 @@ export class InputHandler {
416
452
  this.autocompleteIndex === this.autocompleteItems.length - 1
417
453
  ? 0
418
454
  : this.autocompleteIndex + 1;
419
- this.view.clearAutocomplete();
420
- this.drawPrompt();
421
- this.view.drawAutocomplete({ items: this.autocompleteItems, selected: this.autocompleteIndex });
455
+ this.view.beginFrame();
456
+ try {
457
+ this.view.clearAutocomplete();
458
+ this.drawPrompt();
459
+ this.view.drawAutocomplete({ items: this.autocompleteItems, selected: this.autocompleteIndex });
460
+ }
461
+ finally {
462
+ this.view.endFrame();
463
+ }
422
464
  }
423
465
  else if (this.historyIndex !== -1) {
424
466
  if (this.historyIndex < this.history.length - 1) {
@@ -429,8 +471,14 @@ export class InputHandler {
429
471
  this.historyIndex = -1;
430
472
  this.editor.setText(this.savedBuffer);
431
473
  }
432
- this.view.clearAutocomplete();
433
- this.drawPrompt();
474
+ this.view.beginFrame();
475
+ try {
476
+ this.view.clearAutocomplete();
477
+ this.drawPrompt();
478
+ }
479
+ finally {
480
+ this.view.endFrame();
481
+ }
434
482
  }
435
483
  break;
436
484
  }
@@ -26,7 +26,12 @@ export declare class TuiInputView {
26
26
  private cursorTermCol;
27
27
  private autocompleteLines;
28
28
  private readonly surface;
29
+ private frameBuf;
29
30
  constructor(surface?: RenderSurface);
31
+ beginFrame(): void;
32
+ endFrame(): void;
33
+ private emit;
34
+ private autoFrame;
30
35
  resetCursor(): void;
31
36
  enableModeKeys(): void;
32
37
  disableModeKeys(): void;