@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
@@ -0,0 +1,226 @@
1
+ /**
2
+ * TUI Bridge Types
3
+ * Type definitions for external control via TUI Bridge MCP
4
+ */
5
+
6
+ import type { PermissionMode } from "../../../../types/index.js";
7
+
8
+ /**
9
+ * Bridge event types that can be sent to external controllers
10
+ */
11
+ export type BridgeEventType =
12
+ | "state_update"
13
+ | "message_added"
14
+ | "message_updated"
15
+ | "loading_changed"
16
+ | "model_changed"
17
+ | "session_changed"
18
+ | "command_executed"
19
+ | "error";
20
+
21
+ /**
22
+ * Bridge state snapshot for external controllers
23
+ */
24
+ export interface BridgeState {
25
+ /** Current session ID */
26
+ sessionId: string;
27
+ /** Current model */
28
+ model: string;
29
+ /** Permission mode */
30
+ permissionMode: PermissionMode;
31
+ /** Is currently processing */
32
+ isLoading: boolean;
33
+ /** Total messages count */
34
+ messageCount: number;
35
+ /** Working directory */
36
+ workingDirectory: string;
37
+ /** Tokens used */
38
+ tokensUsed: number;
39
+ /** Total cost */
40
+ totalCost: number;
41
+ /** Timestamp */
42
+ timestamp: number;
43
+ }
44
+
45
+ /**
46
+ * Bridge event payload
47
+ */
48
+ export interface BridgeEvent<T = unknown> {
49
+ /** Event type */
50
+ type: BridgeEventType;
51
+ /** Event payload */
52
+ payload: T;
53
+ /** Timestamp */
54
+ timestamp: number;
55
+ }
56
+
57
+ /**
58
+ * Bridge event map for type-safe event handling
59
+ */
60
+ export interface BridgeEventMap {
61
+ state_update: BridgeState;
62
+ message_added: { id: string; content: string; role: "user" | "assistant" | "system" };
63
+ message_updated: { id: string; content: string };
64
+ loading_changed: { isLoading: boolean };
65
+ model_changed: { model: string };
66
+ session_changed: { sessionId: string };
67
+ command_executed: BridgeCommand;
68
+ error: { message: string; code?: string };
69
+ }
70
+
71
+ /**
72
+ * Command that can be sent to coder via bridge
73
+ */
74
+ export type BridgeCommand =
75
+ | { type: "send_message"; content: string }
76
+ | { type: "execute_command"; command: string }
77
+ | { type: "set_model"; model: string }
78
+ | { type: "clear_messages" }
79
+ | { type: "export_session"; format: "jsonl" | "json" | "markdown" }
80
+ | { type: "get_state" }
81
+ | { type: "get_screen" };
82
+
83
+ /**
84
+ * Bridge command result
85
+ */
86
+ export interface BridgeCommandResult<T = unknown> {
87
+ /** Whether command succeeded */
88
+ success: boolean;
89
+ /** Result data if any */
90
+ data?: T;
91
+ /** Error message if failed */
92
+ error?: string;
93
+ }
94
+
95
+ /**
96
+ * TUI Bridge configuration
97
+ */
98
+ export interface TUIBridgeConfig {
99
+ /** Enable bridge mode */
100
+ enabled: boolean;
101
+ /** Unix socket path for IPC */
102
+ socketPath?: string;
103
+ /** Port for HTTP bridge */
104
+ httpPort?: number;
105
+ /** Callback for external events */
106
+ onEvent?: (event: BridgeEvent) => void;
107
+ }
108
+
109
+ /**
110
+ * Screen buffer cell for TUI parsing
111
+ */
112
+ export interface ScreenCell {
113
+ /** Character at this position */
114
+ char: string;
115
+ /** Foreground color */
116
+ fg?: string;
117
+ /** Background color */
118
+ bg?: string;
119
+ /** Text attributes */
120
+ bold?: boolean;
121
+ italic?: boolean;
122
+ underline?: boolean;
123
+ dim?: boolean;
124
+ inverse?: boolean;
125
+ }
126
+
127
+ /**
128
+ * Screen buffer representation
129
+ */
130
+ export interface ScreenBuffer {
131
+ /** Screen width */
132
+ width: number;
133
+ /** Screen height */
134
+ height: number;
135
+ /** 2D array of cells [row][col] */
136
+ cells: ScreenCell[][];
137
+ /** Cursor position */
138
+ cursor: { x: number; y: number; visible: boolean };
139
+ /** Timestamp */
140
+ timestamp: number;
141
+ }
142
+
143
+ /**
144
+ * Parsed screen content
145
+ */
146
+ export interface ParsedScreen {
147
+ /** Plain text content */
148
+ text: string;
149
+ /** Screen buffer */
150
+ buffer: ScreenBuffer;
151
+ /** Detected UI elements */
152
+ elements: UIElement[];
153
+ /** Timestamp */
154
+ timestamp: number;
155
+ }
156
+
157
+ /**
158
+ * Detected UI element types
159
+ */
160
+ export type UIElementType =
161
+ | "button"
162
+ | "input"
163
+ | "menu"
164
+ | "menu_item"
165
+ | "list"
166
+ | "list_item"
167
+ | "dialog"
168
+ | "text"
169
+ | "header"
170
+ | "footer";
171
+
172
+ /**
173
+ * Detected UI element in the screen
174
+ */
175
+ export interface UIElement {
176
+ /** Element type */
177
+ type: UIElementType;
178
+ /** Bounding box */
179
+ bounds: {
180
+ x: number;
181
+ y: number;
182
+ width: number;
183
+ height: number;
184
+ };
185
+ /** Text content */
186
+ text: string;
187
+ /** Is focused/selected */
188
+ focused?: boolean;
189
+ /** Is clickable */
190
+ clickable?: boolean;
191
+ }
192
+
193
+ /**
194
+ * Bridge message format for IPC
195
+ */
196
+ export interface BridgeMessage {
197
+ /** Message ID for request/response correlation */
198
+ id: string;
199
+ /** Message type */
200
+ type: "request" | "response" | "event";
201
+ /** Method name (for requests) */
202
+ method?: string;
203
+ /** Parameters (for requests) */
204
+ params?: unknown;
205
+ /** Result (for responses) */
206
+ result?: unknown;
207
+ /** Error (for error responses) */
208
+ error?: { code: number; message: string };
209
+ /** Event data (for events) */
210
+ event?: BridgeEvent;
211
+ }
212
+
213
+ /**
214
+ * Bridge methods available for external control
215
+ */
216
+ export type BridgeMethod =
217
+ | "getState"
218
+ | "sendMessage"
219
+ | "executeCommand"
220
+ | "setModel"
221
+ | "clearMessages"
222
+ | "exportSession"
223
+ | "getScreen"
224
+ | "parseScreen"
225
+ | "subscribe"
226
+ | "unsubscribe";
@@ -0,0 +1,210 @@
1
+ /**
2
+ * useBridge Hook
3
+ * React hook for TUI Bridge integration
4
+ */
5
+
6
+ import { useCallback, useEffect, useState, useRef } from "react";
7
+ import { TUIBridge } from "./index.js";
8
+ import type {
9
+ BridgeState,
10
+ BridgeEvent,
11
+ BridgeCommand,
12
+ BridgeCommandResult,
13
+ TUIBridgeConfig,
14
+ } from "./types.js";
15
+
16
+ /**
17
+ * Hook options
18
+ */
19
+ export interface UseBridgeOptions {
20
+ /** Enable bridge mode */
21
+ enabled?: boolean;
22
+ /** Unix socket path */
23
+ socketPath?: string;
24
+ /** HTTP port */
25
+ httpPort?: number;
26
+ /** Event callback */
27
+ onEvent?: (event: BridgeEvent) => void;
28
+ }
29
+
30
+ /**
31
+ * Hook return type
32
+ */
33
+ export interface UseBridgeReturn {
34
+ /** Bridge instance */
35
+ bridge: TUIBridge | null;
36
+ /** Current state */
37
+ state: BridgeState | null;
38
+ /** Is bridge enabled */
39
+ isEnabled: boolean;
40
+ /** Update state */
41
+ updateState: (state: Partial<BridgeState>) => void;
42
+ /** Execute command */
43
+ executeCommand: (command: BridgeCommand) => Promise<BridgeCommandResult>;
44
+ /** Emit event */
45
+ emitEvent: <T>(type: BridgeEvent<T>["type"], payload: T) => void;
46
+ /** Last received command */
47
+ lastCommand: BridgeCommand | null;
48
+ /** Clear last command */
49
+ clearLastCommand: () => void;
50
+ }
51
+
52
+ /**
53
+ * Hook for TUI Bridge integration
54
+ */
55
+ export function useBridge(options: UseBridgeOptions = {}): UseBridgeReturn {
56
+ const { enabled = false, socketPath, httpPort, onEvent } = options;
57
+
58
+ const [state, setState] = useState<BridgeState | null>(null);
59
+ const [lastCommand, setLastCommand] = useState<BridgeCommand | null>(null);
60
+ const bridgeRef = useRef<TUIBridge | null>(null);
61
+
62
+ // Initialize bridge
63
+ useEffect(() => {
64
+ if (!enabled) {
65
+ return;
66
+ }
67
+
68
+ const config: TUIBridgeConfig = {
69
+ enabled,
70
+ socketPath,
71
+ httpPort,
72
+ onEvent: (event) => {
73
+ // Update local state on state_update events
74
+ if (event.type === "state_update") {
75
+ setState(event.payload as BridgeState);
76
+ }
77
+ // Call user callback
78
+ onEvent?.(event);
79
+ },
80
+ };
81
+
82
+ bridgeRef.current = TUIBridge.init(config);
83
+
84
+ return () => {
85
+ bridgeRef.current?.destroy();
86
+ bridgeRef.current = null;
87
+ };
88
+ }, [enabled, socketPath, httpPort, onEvent]);
89
+
90
+ // Listen for command events
91
+ useEffect(() => {
92
+ if (!bridgeRef.current) {
93
+ return;
94
+ }
95
+
96
+ const handleCommand = (event: BridgeEvent) => {
97
+ if (event.type === "command_executed") {
98
+ setLastCommand(event.payload as BridgeCommand);
99
+ }
100
+ };
101
+
102
+ bridgeRef.current.on("event", handleCommand);
103
+
104
+ return () => {
105
+ bridgeRef.current?.off("event", handleCommand);
106
+ };
107
+ }, [enabled]);
108
+
109
+ const updateState = useCallback((newState: Partial<BridgeState>) => {
110
+ bridgeRef.current?.updateState(newState);
111
+ setState((prev) => (prev ? { ...prev, ...newState } : null));
112
+ }, []);
113
+
114
+ const executeCommand = useCallback(async (command: BridgeCommand): Promise<BridgeCommandResult> => {
115
+ if (!bridgeRef.current) {
116
+ return { success: false, error: "Bridge not initialized" };
117
+ }
118
+ return bridgeRef.current.executeCommand(command);
119
+ }, []);
120
+
121
+ const emitEvent = useCallback(<T,>(type: BridgeEvent<T>["type"], payload: T) => {
122
+ bridgeRef.current?.emitEvent(type, payload);
123
+ }, []);
124
+
125
+ const clearLastCommand = useCallback(() => {
126
+ setLastCommand(null);
127
+ }, []);
128
+
129
+ return {
130
+ bridge: bridgeRef.current,
131
+ state,
132
+ isEnabled: enabled && bridgeRef.current !== null,
133
+ updateState,
134
+ executeCommand,
135
+ emitEvent,
136
+ lastCommand,
137
+ clearLastCommand,
138
+ };
139
+ }
140
+
141
+ /**
142
+ * Hook for handling bridge commands in TUI components
143
+ */
144
+ export interface UseBridgeCommandHandlerOptions {
145
+ /** Bridge instance from useBridge */
146
+ bridge: TUIBridge | null;
147
+ /** Handler for send_message commands */
148
+ onSendMessage?: (content: string) => void;
149
+ /** Handler for execute_command commands */
150
+ onExecuteCommand?: (command: string) => void;
151
+ /** Handler for set_model commands */
152
+ onSetModel?: (model: string) => void;
153
+ /** Handler for clear_messages commands */
154
+ onClearMessages?: () => void;
155
+ /** Handler for export_session commands */
156
+ onExportSession?: (format: "jsonl" | "json" | "markdown") => void;
157
+ }
158
+
159
+ /**
160
+ * Hook for handling bridge commands
161
+ */
162
+ export function useBridgeCommandHandler(options: UseBridgeCommandHandlerOptions): void {
163
+ const { bridge, onSendMessage, onExecuteCommand, onSetModel, onClearMessages, onExportSession } = options;
164
+
165
+ useEffect(() => {
166
+ if (!bridge) {
167
+ return;
168
+ }
169
+
170
+ const handleEvent = async (event: BridgeEvent) => {
171
+ if (event.type !== "command_executed") {
172
+ return;
173
+ }
174
+
175
+ const command = event.payload as BridgeCommand;
176
+
177
+ switch (command.type) {
178
+ case "send_message":
179
+ onSendMessage?.(command.content);
180
+ break;
181
+
182
+ case "execute_command":
183
+ onExecuteCommand?.(command.command);
184
+ break;
185
+
186
+ case "set_model":
187
+ onSetModel?.(command.model);
188
+ break;
189
+
190
+ case "clear_messages":
191
+ onClearMessages?.();
192
+ break;
193
+
194
+ case "export_session":
195
+ onExportSession?.(command.format);
196
+ break;
197
+
198
+ case "get_state":
199
+ // Already handled by bridge
200
+ break;
201
+ }
202
+ };
203
+
204
+ bridge.on("event", handleEvent);
205
+
206
+ return () => {
207
+ bridge.off("event", handleEvent);
208
+ };
209
+ }, [bridge, onSendMessage, onExecuteCommand, onSetModel, onClearMessages, onExportSession]);
210
+ }
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * CLI Bootstrap - PTY Wrapper Detection
4
+ *
5
+ * This file MUST run before any other imports to handle PTY wrapping.
6
+ * ES modules hoist imports, so we need a separate entry point.
7
+ *
8
+ * Usage: bun bootstrap.ts [args...]
9
+ */
10
+
11
+ import { spawn, spawnSync } from "node:child_process";
12
+ import process from "node:process";
13
+
14
+ /**
15
+ * Check if we need PTY wrapping and handle it
16
+ * Returns true if we should continue to main module, false if we're the parent waiting
17
+ */
18
+ function checkPtyWrapper(): boolean {
19
+ const isTTY = process.stdin.isTTY;
20
+ const isDoppler = !!process.env.DOPPLER_TOKEN;
21
+ const forceInteractive = process.env.CLAUDE_FORCE_INTERACTIVE === "true";
22
+ const hasQuery = process.argv.includes("-q") || process.argv.includes("--query");
23
+ const hasVersion = process.argv.includes("--version") || process.argv.includes("-v");
24
+ const hasHelp = process.argv.includes("--help") || process.argv.includes("-h");
25
+ const alreadyWrapped = process.env.CODER_PTY_WRAPPED === "1";
26
+
27
+ // Debug output
28
+ if (process.env.CODER_DEBUG === "1") {
29
+ console.error("[bootstrap] isTTY:", isTTY);
30
+ console.error("[bootstrap] isDoppler:", isDoppler);
31
+ console.error("[bootstrap] alreadyWrapped:", alreadyWrapped);
32
+ console.error("[bootstrap] forceInteractive:", forceInteractive);
33
+ }
34
+
35
+ // Skip if we have a TTY, force interactive, have a query, version, help, or already wrapped
36
+ if (isTTY || forceInteractive || hasQuery || hasVersion || hasHelp || alreadyWrapped) {
37
+ if (process.env.CODER_DEBUG === "1") {
38
+ console.error("[bootstrap] Skipping wrapper - conditions met");
39
+ }
40
+ return true; // Continue to main module
41
+ }
42
+
43
+ // If under doppler without TTY, try to wrap with unbuffer
44
+ if (isDoppler) {
45
+ if (process.env.CODER_DEBUG === "1") {
46
+ console.error("[bootstrap] Attempting PTY wrap...");
47
+ }
48
+ const result = tryWrapWithUnbuffer();
49
+ if (process.env.CODER_DEBUG === "1") {
50
+ console.error("[bootstrap] Wrap result:", result.wrapped ? "success" : "failed");
51
+ }
52
+ if (result.wrapped && result.child) {
53
+ // Successfully wrapped - child is running
54
+ // Wait for child to exit, then exit parent with same code
55
+ const child = result.child;
56
+ child.on("exit", (code: number | null) => {
57
+ process.exit(code ?? 1);
58
+ });
59
+ child.on("error", (err: Error) => {
60
+ console.error("Child process error:", err.message);
61
+ process.exit(1);
62
+ });
63
+
64
+ // Keep parent alive
65
+ process.stdin.resume();
66
+ setInterval(() => {
67
+ /* keep alive */
68
+ }, 60000);
69
+
70
+ return false; // Don't continue - parent just waits
71
+ }
72
+
73
+ // Failed to wrap - show helpful error
74
+ console.error("Error: Interactive mode requires a TTY.");
75
+ console.error("");
76
+ console.error("When using 'doppler run', TTY is not passed through.");
77
+ console.error("");
78
+ console.error("Install 'expect' package for automatic PTY support:");
79
+ console.error(" macOS: brew install expect");
80
+ console.error(" Ubuntu: sudo apt install expect");
81
+ console.error(" Fedora: sudo dnf install expect");
82
+ console.error("");
83
+ console.error("Or use one of these alternatives:");
84
+ console.error(" 1. Single query: doppler run -- coder -q \"your question\"");
85
+ console.error(" 2. Export secrets: doppler secrets download --no-file && coder");
86
+ console.error(" 3. Force simple: CLAUDE_FORCE_INTERACTIVE=true doppler run -- coder");
87
+ console.error(" 4. With unbuffer: unbuffer doppler run -- coder");
88
+ process.exit(1);
89
+ }
90
+
91
+ return true; // Continue to main module
92
+ }
93
+
94
+ /**
95
+ * Try to wrap execution with unbuffer to provide a PTY
96
+ */
97
+ function tryWrapWithUnbuffer(): { wrapped: boolean; child?: ReturnType<typeof spawn> } {
98
+ try {
99
+ // Check if unbuffer is available
100
+ const checkResult = spawnSync("which", ["unbuffer"], {
101
+ stdio: "ignore",
102
+ timeout: 1000,
103
+ });
104
+
105
+ if (checkResult.status !== 0) {
106
+ return { wrapped: false };
107
+ }
108
+
109
+ // Re-exec ourselves with unbuffer
110
+ const args = [process.execPath, ...process.argv.slice(1)];
111
+ const child = spawn("unbuffer", args, {
112
+ stdio: "inherit",
113
+ env: {
114
+ ...process.env,
115
+ CODER_PTY_WRAPPED: "1",
116
+ },
117
+ });
118
+
119
+ return { wrapped: true, child };
120
+ } catch {
121
+ return { wrapped: false };
122
+ }
123
+ }
124
+
125
+ // Run PTY check and conditionally load main module
126
+ if (checkPtyWrapper()) {
127
+ // Dynamic import of main module - only after PTY check passes
128
+ import("./index.js").catch((err) => {
129
+ console.error("Failed to load main module:", err);
130
+ process.exit(1);
131
+ });
132
+ }