@ebowwa/coder 0.7.64 → 0.7.66

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 (101) hide show
  1. package/dist/index.js +36233 -32
  2. package/dist/interfaces/ui/terminal/cli/index.js +34318 -158
  3. package/dist/interfaces/ui/terminal/native/README.md +53 -0
  4. package/dist/interfaces/ui/terminal/native/claude_code_native.darwin-x64.node +0 -0
  5. package/dist/interfaces/ui/terminal/native/claude_code_native.dylib +0 -0
  6. package/dist/interfaces/ui/terminal/native/index.d.ts +0 -0
  7. package/dist/interfaces/ui/terminal/native/index.darwin-arm64.node +0 -0
  8. package/dist/interfaces/ui/terminal/native/index.js +43 -0
  9. package/dist/interfaces/ui/terminal/native/index.node +0 -0
  10. package/dist/interfaces/ui/terminal/native/package.json +34 -0
  11. package/dist/native/README.md +53 -0
  12. package/dist/native/claude_code_native.darwin-x64.node +0 -0
  13. package/dist/native/claude_code_native.dylib +0 -0
  14. package/dist/native/index.d.ts +0 -480
  15. package/dist/native/index.darwin-arm64.node +0 -0
  16. package/dist/native/index.js +43 -1625
  17. package/dist/native/index.node +0 -0
  18. package/dist/native/package.json +34 -0
  19. package/native/index.darwin-arm64.node +0 -0
  20. package/native/index.js +33 -19
  21. package/package.json +3 -2
  22. package/packages/src/core/agent-loop/__tests__/compaction.test.ts +17 -14
  23. package/packages/src/core/agent-loop/compaction.ts +6 -2
  24. package/packages/src/core/agent-loop/index.ts +2 -0
  25. package/packages/src/core/agent-loop/loop-state.ts +1 -1
  26. package/packages/src/core/agent-loop/turn-executor.ts +4 -0
  27. package/packages/src/core/agent-loop/types.ts +4 -0
  28. package/packages/src/core/api-client-impl.ts +377 -176
  29. package/packages/src/core/cognitive-security/hooks.ts +2 -1
  30. package/packages/src/core/config/todo +7 -0
  31. package/packages/src/core/context/__tests__/integration.test.ts +334 -0
  32. package/packages/src/core/context/compaction.ts +170 -0
  33. package/packages/src/core/context/constants.ts +58 -0
  34. package/packages/src/core/context/extraction.ts +85 -0
  35. package/packages/src/core/context/index.ts +66 -0
  36. package/packages/src/core/context/summarization.ts +251 -0
  37. package/packages/src/core/context/token-estimation.ts +98 -0
  38. package/packages/src/core/context/types.ts +59 -0
  39. package/packages/src/core/models.ts +81 -4
  40. package/packages/src/core/normalizers/todo +5 -1
  41. package/packages/src/core/providers/README.md +230 -0
  42. package/packages/src/core/providers/__tests__/providers.test.ts +135 -0
  43. package/packages/src/core/providers/index.ts +419 -0
  44. package/packages/src/core/providers/types.ts +132 -0
  45. package/packages/src/core/retry.ts +10 -0
  46. package/packages/src/ecosystem/tools/index.ts +174 -0
  47. package/packages/src/index.ts +23 -2
  48. package/packages/src/interfaces/ui/index.ts +17 -20
  49. package/packages/src/interfaces/ui/spinner.ts +2 -2
  50. package/packages/src/interfaces/ui/terminal/bridge/index.ts +370 -0
  51. package/packages/src/interfaces/ui/terminal/bridge/ipc.ts +829 -0
  52. package/packages/src/interfaces/ui/terminal/bridge/screen-export.ts +968 -0
  53. package/packages/src/interfaces/ui/terminal/bridge/types.ts +226 -0
  54. package/packages/src/interfaces/ui/terminal/bridge/useBridge.ts +210 -0
  55. package/packages/src/interfaces/ui/terminal/cli/bootstrap.ts +132 -0
  56. package/packages/src/interfaces/ui/terminal/cli/index.ts +200 -13
  57. package/packages/src/interfaces/ui/terminal/cli/interactive/index.ts +110 -0
  58. package/packages/src/interfaces/ui/terminal/cli/interactive/input-handler.ts +402 -0
  59. package/packages/src/interfaces/ui/terminal/cli/interactive/interactive-runner.ts +820 -0
  60. package/packages/src/interfaces/ui/terminal/cli/interactive/message-store.ts +299 -0
  61. package/packages/src/interfaces/ui/terminal/cli/interactive/types.ts +274 -0
  62. package/packages/src/interfaces/ui/terminal/shared/index.ts +13 -0
  63. package/packages/src/interfaces/ui/terminal/shared/query.ts +9 -3
  64. package/packages/src/interfaces/ui/terminal/shared/setup.ts +5 -1
  65. package/packages/src/interfaces/ui/terminal/shared/spinner-frames.ts +73 -0
  66. package/packages/src/interfaces/ui/terminal/shared/status-line.ts +10 -2
  67. package/packages/src/native/index.ts +404 -27
  68. package/packages/src/native/tui_v2_types.ts +39 -0
  69. package/packages/src/teammates/coordination.test.ts +279 -0
  70. package/packages/src/teammates/coordination.ts +646 -0
  71. package/packages/src/teammates/index.ts +95 -25
  72. package/packages/src/teammates/integration.test.ts +272 -0
  73. package/packages/src/teammates/runner.test.ts +235 -0
  74. package/packages/src/teammates/runner.ts +750 -0
  75. package/packages/src/teammates/schemas.ts +673 -0
  76. package/packages/src/types/index.ts +1 -0
  77. package/packages/src/core/context-compaction.ts +0 -578
  78. package/packages/src/interfaces/ui/Screenshot 2026-03-02 at 9.23.10/342/200/257PM.png +0 -0
  79. package/packages/src/interfaces/ui/Screenshot 2026-03-03 at 10.55.11/342/200/257AM.png +0 -0
  80. package/packages/src/interfaces/ui/terminal/tui/HelpPanel.tsx +0 -262
  81. package/packages/src/interfaces/ui/terminal/tui/InputContext.tsx +0 -232
  82. package/packages/src/interfaces/ui/terminal/tui/InputField.tsx +0 -62
  83. package/packages/src/interfaces/ui/terminal/tui/InteractiveTUI.tsx +0 -537
  84. package/packages/src/interfaces/ui/terminal/tui/MessageArea.tsx +0 -107
  85. package/packages/src/interfaces/ui/terminal/tui/MessageStore.tsx +0 -240
  86. package/packages/src/interfaces/ui/terminal/tui/StatusBar.tsx +0 -54
  87. package/packages/src/interfaces/ui/terminal/tui/commands.ts +0 -438
  88. package/packages/src/interfaces/ui/terminal/tui/components/InteractiveElements.tsx +0 -584
  89. package/packages/src/interfaces/ui/terminal/tui/components/MultilineInput.tsx +0 -614
  90. package/packages/src/interfaces/ui/terminal/tui/components/PaneManager.tsx +0 -333
  91. package/packages/src/interfaces/ui/terminal/tui/components/Sidebar.tsx +0 -604
  92. package/packages/src/interfaces/ui/terminal/tui/components/index.ts +0 -118
  93. package/packages/src/interfaces/ui/terminal/tui/console.ts +0 -49
  94. package/packages/src/interfaces/ui/terminal/tui/index.ts +0 -90
  95. package/packages/src/interfaces/ui/terminal/tui/run.tsx +0 -42
  96. package/packages/src/interfaces/ui/terminal/tui/spinner.ts +0 -69
  97. package/packages/src/interfaces/ui/terminal/tui/tui-app.tsx +0 -390
  98. package/packages/src/interfaces/ui/terminal/tui/tui-footer.ts +0 -422
  99. package/packages/src/interfaces/ui/terminal/tui/types.ts +0 -186
  100. package/packages/src/interfaces/ui/terminal/tui/useInputHandler.ts +0 -104
  101. package/packages/src/interfaces/ui/terminal/tui/useNativeInput.ts +0 -239
@@ -1,422 +0,0 @@
1
- /**
2
- * TUI Footer Component for Coder CLI
3
- * Provides a persistent status bar at the bottom of the terminal
4
- *
5
- * Uses ANSI escape codes for cursor positioning:
6
- * - ESC[s Save cursor position
7
- * - ESC[u Restore cursor position
8
- * - ESC[#;#H Move cursor to row, col
9
- * - ESC[2K Clear entire line
10
- * - ESC[J Clear from cursor to end of screen
11
- */
12
-
13
- import chalk from "chalk";
14
- import type { PermissionMode } from "../../../../types/index.js";
15
- import {
16
- calculateContextInfo,
17
- formatPermissionMode,
18
- type StatusLineOptions,
19
- } from "../shared/status-line.js";
20
- import { spinnerFrames } from "./spinner.js";
21
-
22
- // ============================================
23
- // ANSI ESCAPE CODES
24
- // ============================================
25
-
26
- const ANSI = {
27
- // Cursor
28
- SAVE_CURSOR: "\x1b[s",
29
- RESTORE_CURSOR: "\x1b[u",
30
- MOVE_TO: (row: number, col: number) => `\x1b[${row};${col}H`,
31
- MOVE_TO_BOTTOM: (offset = 0) => `\x1b[999;1H\x1b[${offset + 1}A`,
32
-
33
- // Clear
34
- CLEAR_LINE: "\x1b[2K",
35
- CLEAR_TO_END: "\x1b[J",
36
- CLEAR_SCREEN: "\x1b[2J",
37
-
38
- // Scrolling
39
- SCROLL_UP: (lines: number) => `\x1b[${lines}S`,
40
- SCROLL_DOWN: (lines: number) => `\x1b[${lines}T`,
41
-
42
- // Colors reset
43
- RESET: "\x1b[0m",
44
-
45
- // Alternate screen buffer
46
- ENTER_ALT_SCREEN: "\x1b[?1049h",
47
- EXIT_ALT_SCREEN: "\x1b[?1049l",
48
-
49
- // Cursor visibility
50
- HIDE_CURSOR: "\x1b[?25l",
51
- SHOW_CURSOR: "\x1b[?25h",
52
- };
53
-
54
- // Export ANSI for external use
55
- export { ANSI };
56
-
57
- // ============================================
58
- // TYPES
59
- // ============================================
60
-
61
- export interface TUIFooterOptions {
62
- /** Permission mode to display */
63
- permissionMode: PermissionMode;
64
- /** Tokens used in context */
65
- tokensUsed: number;
66
- /** Current model */
67
- model: string;
68
- /** Is loading/spinning */
69
- isLoading?: boolean;
70
- /** Verbose mode */
71
- verbose?: boolean;
72
- /** Show version */
73
- showVersion?: boolean;
74
- }
75
-
76
- export interface TUIFooterState {
77
- isEnabled: boolean;
78
- lastRender: string;
79
- lastOptions: TUIFooterOptions | null;
80
- renderInterval: Timer | null;
81
- spinnerFrame: number;
82
- terminalHeight: number;
83
- terminalWidth: number;
84
- }
85
-
86
- // ============================================
87
- // FOOTER RENDERER
88
- // ============================================
89
-
90
- /**
91
- * Render footer content (without positioning)
92
- */
93
- function renderFooterContent(options: TUIFooterOptions, spinnerFrame?: string): string {
94
- const { permissionMode, tokensUsed, model, isLoading, verbose, showVersion } = options;
95
- const contextInfo = calculateContextInfo(tokensUsed, model);
96
-
97
- // Build status parts
98
- const parts: string[] = [];
99
-
100
- // 1. Loading spinner if active
101
- if (isLoading && spinnerFrame) {
102
- parts.push(chalk.cyan(spinnerFrame));
103
- }
104
-
105
- // 2. Context percentage with color
106
- const contextValue = contextInfo.isCritical
107
- ? chalk.red(`${contextInfo.percentRemaining.toFixed(0)}%`)
108
- : contextInfo.isLow
109
- ? chalk.yellow(`${contextInfo.percentRemaining.toFixed(0)}%`)
110
- : chalk.dim(`${contextInfo.percentRemaining.toFixed(0)}%`);
111
- parts.push(`Context: ${contextValue}`);
112
-
113
- // 3. Permission mode
114
- const permDisplay = formatPermissionMode(permissionMode);
115
- parts.push(permDisplay);
116
-
117
- // 4. Version (if verbose or showVersion)
118
- if (verbose || showVersion) {
119
- parts.push(chalk.dim(`v${getVersion()}`));
120
- }
121
-
122
- return parts.join(chalk.dim(" | "));
123
- }
124
-
125
- /**
126
- * Get VERSION dynamically to avoid circular imports
127
- */
128
- function getVersion(): string {
129
- try {
130
- // Dynamic import to avoid circular dependency
131
- const statusLine = require("../shared/status-line.js");
132
- return statusLine.VERSION || "0.0.0";
133
- } catch {
134
- return "0.0.0";
135
- }
136
- }
137
-
138
- // ============================================
139
- // TUI FOOTER CLASS
140
- // ============================================
141
-
142
- export class TUIFooter {
143
- private state: TUIFooterState;
144
- private static instance: TUIFooter | null = null;
145
-
146
- private constructor() {
147
- this.state = {
148
- isEnabled: false,
149
- lastRender: "",
150
- lastOptions: null,
151
- renderInterval: null,
152
- spinnerFrame: 0,
153
- terminalHeight: process.stdout.rows || 24,
154
- terminalWidth: process.stdout.columns || 80,
155
- };
156
- }
157
-
158
- /**
159
- * Get singleton instance
160
- */
161
- static getInstance(): TUIFooter {
162
- if (!TUIFooter.instance) {
163
- TUIFooter.instance = new TUIFooter();
164
- }
165
- return TUIFooter.instance;
166
- }
167
-
168
- /**
169
- * Reset singleton (for testing)
170
- */
171
- static reset(): void {
172
- if (TUIFooter.instance) {
173
- TUIFooter.instance.disable();
174
- TUIFooter.instance = null;
175
- }
176
- }
177
-
178
- /**
179
- * Enable the footer (start tracking terminal size)
180
- */
181
- enable(): void {
182
- if (this.state.isEnabled) return;
183
-
184
- this.state.isEnabled = true;
185
- this.updateTerminalSize();
186
-
187
- // Listen for terminal resize
188
- process.stdout.on("resize", this.handleResize);
189
- }
190
-
191
- /**
192
- * Disable the footer and clear it
193
- */
194
- disable(): void {
195
- if (!this.state.isEnabled) return;
196
-
197
- this.stopSpinner();
198
- this.clear();
199
-
200
- process.stdout.off("resize", this.handleResize);
201
- this.state.isEnabled = false;
202
- }
203
-
204
- /**
205
- * Check if footer is enabled
206
- */
207
- isEnabled(): boolean {
208
- return this.state.isEnabled;
209
- }
210
-
211
- /**
212
- * Update terminal dimensions
213
- */
214
- private updateTerminalSize(): void {
215
- this.state.terminalHeight = process.stdout.rows || 24;
216
- this.state.terminalWidth = process.stdout.columns || 80;
217
- }
218
-
219
- /**
220
- * Handle terminal resize
221
- */
222
- private handleResize = (): void => {
223
- this.updateTerminalSize();
224
- if (this.state.lastOptions) {
225
- this.render(this.state.lastOptions);
226
- }
227
- };
228
-
229
- /**
230
- * Render the footer at the bottom of the screen
231
- * Saves cursor position, moves to bottom, renders, restores cursor
232
- */
233
- render(options: TUIFooterOptions): void {
234
- if (!this.state.isEnabled) return;
235
-
236
- this.state.lastOptions = options;
237
-
238
- // Build footer content
239
- const content = renderFooterContent(options);
240
-
241
- // Skip if content unchanged (optimization)
242
- if (content === this.state.lastRender && !options.isLoading) {
243
- return;
244
- }
245
-
246
- this.state.lastRender = content;
247
-
248
- // Build ANSI sequence:
249
- // 1. Save cursor position
250
- // 2. Move to bottom of screen
251
- // 3. Clear the line
252
- // 4. Render footer content
253
- // 5. Restore cursor position
254
- const output =
255
- ANSI.SAVE_CURSOR +
256
- ANSI.MOVE_TO_BOTTOM() +
257
- ANSI.CLEAR_LINE +
258
- "\r" + // Go to start of line
259
- chalk.dim("┌") +
260
- content +
261
- ANSI.RESTORE_CURSOR;
262
-
263
- process.stdout.write(output);
264
- }
265
-
266
- /**
267
- * Render with loading spinner animation
268
- */
269
- renderLoading(options: TUIFooterOptions): void {
270
- const frame = spinnerFrames[this.state.spinnerFrame];
271
- this.state.spinnerFrame = (this.state.spinnerFrame + 1) % spinnerFrames.length;
272
-
273
- const content = renderFooterContent(options, frame);
274
- this.state.lastRender = content;
275
-
276
- if (!this.state.isEnabled) return;
277
-
278
- const output =
279
- ANSI.SAVE_CURSOR +
280
- ANSI.MOVE_TO_BOTTOM() +
281
- ANSI.CLEAR_LINE +
282
- "\r" +
283
- chalk.dim("┌") +
284
- content +
285
- ANSI.RESTORE_CURSOR;
286
-
287
- process.stdout.write(output);
288
- }
289
-
290
- /**
291
- * Start spinner animation for loading state
292
- */
293
- startSpinner(options: TUIFooterOptions, interval = 80): void {
294
- this.stopSpinner();
295
-
296
- this.state.renderInterval = setInterval(() => {
297
- this.renderLoading(options);
298
- }, interval);
299
- }
300
-
301
- /**
302
- * Stop spinner animation
303
- */
304
- stopSpinner(): void {
305
- if (this.state.renderInterval) {
306
- clearInterval(this.state.renderInterval);
307
- this.state.renderInterval = null;
308
- }
309
- }
310
-
311
- /**
312
- * Clear the footer line
313
- */
314
- clear(): void {
315
- if (!this.state.isEnabled) return;
316
-
317
- const output =
318
- ANSI.SAVE_CURSOR +
319
- ANSI.MOVE_TO_BOTTOM() +
320
- ANSI.CLEAR_LINE +
321
- ANSI.RESTORE_CURSOR;
322
-
323
- process.stdout.write(output);
324
- this.state.lastRender = "";
325
- }
326
-
327
- /**
328
- * Update just the token count (for incremental updates)
329
- */
330
- updateTokens(tokensUsed: number): void {
331
- if (this.state.lastOptions) {
332
- this.render({
333
- ...this.state.lastOptions,
334
- tokensUsed,
335
- });
336
- }
337
- }
338
-
339
- /**
340
- * Update loading state
341
- */
342
- setLoading(isLoading: boolean): void {
343
- if (this.state.lastOptions) {
344
- const options = {
345
- ...this.state.lastOptions,
346
- isLoading,
347
- };
348
-
349
- if (isLoading) {
350
- this.startSpinner(options);
351
- } else {
352
- this.stopSpinner();
353
- this.render(options);
354
- }
355
- }
356
- }
357
-
358
- /**
359
- * Get terminal dimensions
360
- */
361
- getTerminalSize(): { width: number; height: number } {
362
- return {
363
- width: this.state.terminalWidth,
364
- height: this.state.terminalHeight,
365
- };
366
- }
367
- }
368
-
369
- // ============================================
370
- // CONVENIENCE EXPORTS
371
- // ============================================
372
-
373
- /**
374
- * Get the global TUI footer instance
375
- */
376
- export function getTUIFooter(): TUIFooter {
377
- return TUIFooter.getInstance();
378
- }
379
-
380
- /**
381
- * Enable TUI footer
382
- */
383
- export function enableTUIFooter(): TUIFooter {
384
- const footer = TUIFooter.getInstance();
385
- footer.enable();
386
- return footer;
387
- }
388
-
389
- /**
390
- * Disable TUI footer
391
- */
392
- export function disableTUIFooter(): void {
393
- TUIFooter.getInstance().disable();
394
- }
395
-
396
- /**
397
- * Render footer status (convenience function)
398
- */
399
- export function renderTUIFooter(options: TUIFooterOptions): void {
400
- TUIFooter.getInstance().render(options);
401
- }
402
-
403
- /**
404
- * Clear the footer line
405
- */
406
- export function clearTUIFooter(): void {
407
- TUIFooter.getInstance().clear();
408
- }
409
-
410
- // ============================================
411
- // EXPORTS
412
- // ============================================
413
-
414
- export default {
415
- TUIFooter,
416
- getTUIFooter,
417
- enableTUIFooter,
418
- disableTUIFooter,
419
- renderTUIFooter,
420
- clearTUIFooter,
421
- ANSI,
422
- };
@@ -1,186 +0,0 @@
1
- /**
2
- * TUI Types
3
- * Type definitions for the interactive TUI components
4
- */
5
-
6
- import type { PermissionMode, ClaudeModel, Message as ApiMessage, ToolDefinition } from "../../../../types/index.js";
7
- import type { HookManager } from "../../../../ecosystem/hooks/index.js";
8
- import type { TerminalHandle } from "../../../../native/index.js";
9
- import type { LoadedSession, SessionSummary } from "../../../../core/sessions/types.js";
10
-
11
- /**
12
- * Message sub-type for granular categorization
13
- * - tool_call: Assistant is invoking a tool
14
- * - tool_result: Tool execution result (success or error)
15
- * - hook: Hook intercept/decision message
16
- * - info: General system info (session start, commands, etc.)
17
- * - error: Error messages
18
- * - thinking: Extended thinking output
19
- */
20
- export type MessageSubType =
21
- | "tool_call"
22
- | "tool_result"
23
- | "hook"
24
- | "info"
25
- | "error"
26
- | "thinking";
27
-
28
- /**
29
- * UI-specific message representation
30
- */
31
- export interface UIMessage {
32
- id: string;
33
- /** Base role: user, assistant, or system */
34
- role: "user" | "assistant" | "system";
35
- /** Message content for display */
36
- content: string;
37
- /** Timestamp in milliseconds */
38
- timestamp: number;
39
- /** Optional sub-type for granular display labels */
40
- subType?: MessageSubType;
41
- /** For tool messages: the tool name */
42
- toolName?: string;
43
- /** For tool_result: whether it was an error */
44
- isError?: boolean;
45
- }
46
-
47
- /**
48
- * Props for the main InteractiveTUI component
49
- */
50
- export interface InteractiveTUIProps {
51
- apiKey: string;
52
- model: ClaudeModel;
53
- permissionMode: PermissionMode;
54
- maxTokens: number;
55
- systemPrompt: string;
56
- tools: ToolDefinition[];
57
- hookManager: HookManager;
58
- sessionStore: SessionStore;
59
- sessionId: string;
60
- setSessionId: (id: string) => void;
61
- initialMessages: ApiMessage[];
62
- workingDirectory: string;
63
- onExit: () => void;
64
- }
65
-
66
- /**
67
- * Session store interface (subset used by TUI)
68
- */
69
- export interface SessionStore {
70
- saveMessage(message: ApiMessage): Promise<void>;
71
- saveMetrics(metrics: unknown): Promise<void>;
72
- exportSession(sessionId: string, format: "jsonl" | "json" | "markdown"): Promise<string>;
73
- listSessions(limit?: number): Promise<SessionSummary[]>;
74
- resumeSession(sessionId: string): Promise<LoadedSession | null>;
75
- createSession(options: {
76
- model: string;
77
- workingDirectory: string;
78
- agentName?: string;
79
- agentColor?: string;
80
- teamName?: string;
81
- }): Promise<string>;
82
- }
83
-
84
- /**
85
- * Session info for listing
86
- */
87
- export interface SessionInfo {
88
- id: string;
89
- messageCount: number;
90
- lastActivity?: number;
91
- metadata?: Record<string, unknown>;
92
- }
93
-
94
- /**
95
- * Message area component props
96
- */
97
- export interface MessageAreaProps {
98
- messages: UIMessage[];
99
- isLoading: boolean;
100
- spinnerFrame: string;
101
- height: number;
102
- scrollOffset: number;
103
- contextWarning: string | null;
104
- streamingText?: string;
105
- }
106
-
107
- /**
108
- * Status bar component props
109
- */
110
- export interface StatusBarProps {
111
- permissionMode: PermissionMode;
112
- tokensUsed: number;
113
- model: string;
114
- isLoading: boolean;
115
- spinnerFrame: string;
116
- }
117
-
118
- /**
119
- * Input field component props
120
- */
121
- export interface InputFieldProps {
122
- value: string;
123
- cursorPos: number;
124
- placeholder: string;
125
- isActive: boolean;
126
- }
127
-
128
- /**
129
- * Terminal layout configuration
130
- */
131
- export interface TerminalLayout {
132
- terminalHeight: number;
133
- inputHeight: number;
134
- statusHeight: number;
135
- messageHeight: number;
136
- }
137
-
138
- /**
139
- * Command handler context
140
- */
141
- export interface CommandContext {
142
- sessionId: string;
143
- setSessionId: (id: string) => void;
144
- model: ClaudeModel;
145
- setModel: (model: ClaudeModel) => void;
146
- apiMessages: ApiMessage[];
147
- setApiMessages: (messages: ApiMessage[]) => void;
148
- setMessages: (messages: UIMessage[]) => void;
149
- processedCountRef: React.MutableRefObject<number>;
150
- totalCost: number;
151
- setTotalCost: (cost: number) => void;
152
- totalTokens: number;
153
- setTotalTokens: (tokens: number) => void;
154
- permissionMode: PermissionMode;
155
- tools: ToolDefinition[];
156
- workingDirectory: string;
157
- sessionStore: SessionStore;
158
- addSystemMessage: (content: string) => void;
159
- messagesLength: number;
160
- onExit: () => void;
161
- exit: () => void;
162
- // Session selection state
163
- sessionSelectMode: boolean;
164
- setSessionSelectMode: (mode: boolean) => void;
165
- setSelectableSessions: (sessions: SessionInfo[]) => void;
166
- // Help mode state
167
- helpMode: boolean;
168
- setHelpMode: (mode: boolean) => void;
169
- helpSection: number;
170
- setHelpSection: (section: number) => void;
171
- }
172
-
173
- /**
174
- * Context info from status-line
175
- */
176
- export interface ContextInfo {
177
- percentRemaining: number;
178
- isLow: boolean;
179
- isCritical: boolean;
180
- }
181
-
182
- /**
183
- * Re-export types from parent for convenience
184
- */
185
- export type { PermissionMode, ClaudeModel, Message as ApiMessage, ToolDefinition } from "../../../../types/index.js";
186
- export type { HookManager } from "../../../../ecosystem/hooks/index.js";
@@ -1,104 +0,0 @@
1
- /**
2
- * TUI Input Handler Hook
3
- * Custom hook for handling keyboard input in TUI
4
- */
5
-
6
- import { useCallback } from "react";
7
- import { useInput } from "ink";
8
-
9
- interface UseInputHandlerOptions {
10
- isLoading: boolean;
11
- inputValue: string;
12
- cursorPos: number;
13
- setInputValue: (value: string | ((prev: string) => string)) => void;
14
- setCursorPos: (value: number | ((prev: number) => number)) => void;
15
- onSubmit: (value: string) => void;
16
- onCommand: (cmd: string) => void;
17
- }
18
-
19
- /**
20
- * Hook for handling keyboard input in the TUI
21
- */
22
- export function useInputHandler({
23
- isLoading,
24
- inputValue,
25
- cursorPos,
26
- setInputValue,
27
- setCursorPos,
28
- onSubmit,
29
- onCommand,
30
- }: UseInputHandlerOptions) {
31
- // Main input handler
32
- useInput(
33
- (input, key) => {
34
- if (isLoading) return;
35
-
36
- // Submit on Enter
37
- if (key.return) {
38
- if (inputValue.trim()) {
39
- if (inputValue.startsWith("/")) {
40
- onCommand(inputValue);
41
- } else {
42
- onSubmit(inputValue);
43
- }
44
- setInputValue("");
45
- setCursorPos(0);
46
- }
47
- return;
48
- }
49
-
50
- // Backspace
51
- if (key.backspace || key.delete) {
52
- if (cursorPos > 0) {
53
- setInputValue((v) => v.slice(0, cursorPos - 1) + v.slice(cursorPos));
54
- setCursorPos((p) => p - 1);
55
- }
56
- return;
57
- }
58
-
59
- // Arrow keys
60
- if (key.leftArrow) {
61
- setCursorPos((p) => Math.max(0, p - 1));
62
- return;
63
- }
64
-
65
- if (key.rightArrow) {
66
- setCursorPos((p) => Math.min(inputValue.length, p + 1));
67
- return;
68
- }
69
-
70
- // Home/End keys (Ctrl+A / Ctrl+E)
71
- if (key.ctrl && input === "a") {
72
- setCursorPos(0);
73
- return;
74
- }
75
-
76
- if (key.ctrl && input === "e") {
77
- setCursorPos(inputValue.length);
78
- return;
79
- }
80
-
81
- // Regular character
82
- if (input && !key.ctrl && !key.meta) {
83
- setInputValue((v) => v.slice(0, cursorPos) + input + v.slice(cursorPos));
84
- setCursorPos((p) => p + input.length);
85
- }
86
- },
87
- { isActive: !isLoading }
88
- );
89
- }
90
-
91
- /**
92
- * Hook for handling exit shortcuts (Ctrl+C)
93
- */
94
- export function useExitHandler(onExit: () => void, exit: () => void) {
95
- useInput(
96
- (input, key) => {
97
- if (key.ctrl && input === "c") {
98
- onExit();
99
- exit();
100
- }
101
- },
102
- { isActive: true }
103
- );
104
- }