openhorse 0.2.2 → 0.2.3
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 +23 -0
- package/dist/cli.js +163 -22
- package/dist/cli.js.map +1 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +82 -2
- package/dist/commands/index.js.map +1 -1
- package/dist/framework/prompt.d.ts +4 -0
- package/dist/framework/prompt.d.ts.map +1 -1
- package/dist/framework/prompt.js +18 -0
- package/dist/framework/prompt.js.map +1 -1
- package/dist/framework/query.d.ts +7 -0
- package/dist/framework/query.d.ts.map +1 -1
- package/dist/framework/query.js +14 -0
- package/dist/framework/query.js.map +1 -1
- package/dist/framework/store.d.ts +3 -1
- package/dist/framework/store.d.ts.map +1 -1
- package/dist/framework/store.js +1 -0
- package/dist/framework/store.js.map +1 -1
- package/dist/ink-ui/App.d.ts +4 -1
- package/dist/ink-ui/App.d.ts.map +1 -1
- package/dist/ink-ui/App.js +2 -2
- package/dist/ink-ui/App.js.map +1 -1
- package/dist/ink-ui/components/NativeCursor.d.ts +12 -0
- package/dist/ink-ui/components/NativeCursor.d.ts.map +1 -0
- package/dist/ink-ui/components/NativeCursor.js +35 -0
- package/dist/ink-ui/components/NativeCursor.js.map +1 -0
- package/dist/ink-ui/components/PromptInput.d.ts +5 -1
- package/dist/ink-ui/components/PromptInput.d.ts.map +1 -1
- package/dist/ink-ui/components/PromptInput.js +10 -6
- package/dist/ink-ui/components/PromptInput.js.map +1 -1
- package/dist/ink-ui/components/RunningHorseIndicator.d.ts +1 -1
- package/dist/ink-ui/components/RunningHorseIndicator.d.ts.map +1 -1
- package/dist/ink-ui/components/RunningHorseIndicator.js +3 -41
- package/dist/ink-ui/components/RunningHorseIndicator.js.map +1 -1
- package/dist/ink-ui/components/SelectList.d.ts +2 -1
- package/dist/ink-ui/components/SelectList.d.ts.map +1 -1
- package/dist/ink-ui/components/SelectList.js +26 -11
- package/dist/ink-ui/components/SelectList.js.map +1 -1
- package/dist/ink-ui/components/StatusLine.d.ts.map +1 -1
- package/dist/ink-ui/components/StatusLine.js +22 -31
- package/dist/ink-ui/components/StatusLine.js.map +1 -1
- package/dist/ink-ui/components/TerminalCursor.d.ts +4 -2
- package/dist/ink-ui/components/TerminalCursor.d.ts.map +1 -1
- package/dist/ink-ui/components/TerminalCursor.js +18 -43
- package/dist/ink-ui/components/TerminalCursor.js.map +1 -1
- package/dist/ink-ui/components/ToolActivity.d.ts +17 -0
- package/dist/ink-ui/components/ToolActivity.d.ts.map +1 -0
- package/dist/ink-ui/components/ToolActivity.js +107 -0
- package/dist/ink-ui/components/ToolActivity.js.map +1 -0
- package/dist/ink-ui/components/Transcript.d.ts +7 -1
- package/dist/ink-ui/components/Transcript.d.ts.map +1 -1
- package/dist/ink-ui/components/Transcript.js +40 -3
- package/dist/ink-ui/components/Transcript.js.map +1 -1
- package/dist/ink-ui/controllers/chat-controller.d.ts +1 -33
- package/dist/ink-ui/controllers/chat-controller.d.ts.map +1 -1
- package/dist/ink-ui/controllers/chat-controller.js +15 -437
- package/dist/ink-ui/controllers/chat-controller.js.map +1 -1
- package/dist/ink-ui/hooks/use-raw-input-bridge.d.ts +6 -0
- package/dist/ink-ui/hooks/use-raw-input-bridge.d.ts.map +1 -0
- package/dist/ink-ui/hooks/use-raw-input-bridge.js +27 -0
- package/dist/ink-ui/hooks/use-raw-input-bridge.js.map +1 -0
- package/dist/ink-ui/hooks/use-terminal-size.d.ts +7 -0
- package/dist/ink-ui/hooks/use-terminal-size.d.ts.map +1 -0
- package/dist/ink-ui/hooks/use-terminal-size.js +34 -0
- package/dist/ink-ui/hooks/use-terminal-size.js.map +1 -0
- package/dist/ink-ui/launch.d.ts +6 -0
- package/dist/ink-ui/launch.d.ts.map +1 -1
- package/dist/ink-ui/launch.js +60 -2
- package/dist/ink-ui/launch.js.map +1 -1
- package/dist/ink-ui/runtime/grapheme.d.ts +9 -0
- package/dist/ink-ui/runtime/grapheme.d.ts.map +1 -0
- package/dist/ink-ui/runtime/grapheme.js +63 -0
- package/dist/ink-ui/runtime/grapheme.js.map +1 -0
- package/dist/ink-ui/runtime/input-buffer.d.ts +32 -0
- package/dist/ink-ui/runtime/input-buffer.d.ts.map +1 -0
- package/dist/ink-ui/runtime/input-buffer.js +165 -0
- package/dist/ink-ui/runtime/input-buffer.js.map +1 -0
- package/dist/ink-ui/runtime/layout-budget.d.ts +13 -0
- package/dist/ink-ui/runtime/layout-budget.d.ts.map +1 -0
- package/dist/ink-ui/runtime/layout-budget.js +25 -0
- package/dist/ink-ui/runtime/layout-budget.js.map +1 -0
- package/dist/ink-ui/runtime/native-cursor.d.ts +62 -0
- package/dist/ink-ui/runtime/native-cursor.d.ts.map +1 -0
- package/dist/ink-ui/runtime/native-cursor.js +423 -0
- package/dist/ink-ui/runtime/native-cursor.js.map +1 -0
- package/dist/ink-ui/runtime/prompt-layout.d.ts +20 -1
- package/dist/ink-ui/runtime/prompt-layout.d.ts.map +1 -1
- package/dist/ink-ui/runtime/prompt-layout.js +129 -11
- package/dist/ink-ui/runtime/prompt-layout.js.map +1 -1
- package/dist/ink-ui/runtime/raw-input.d.ts +4 -0
- package/dist/ink-ui/runtime/raw-input.d.ts.map +1 -0
- package/dist/ink-ui/runtime/raw-input.js +15 -0
- package/dist/ink-ui/runtime/raw-input.js.map +1 -0
- package/dist/ink-ui/runtime/stdout.d.ts +11 -0
- package/dist/ink-ui/runtime/stdout.d.ts.map +1 -0
- package/dist/ink-ui/runtime/stdout.js +82 -0
- package/dist/ink-ui/runtime/stdout.js.map +1 -0
- package/dist/ink-ui/runtime/transcript-state.d.ts +37 -0
- package/dist/ink-ui/runtime/transcript-state.d.ts.map +1 -0
- package/dist/ink-ui/runtime/transcript-state.js +89 -0
- package/dist/ink-ui/runtime/transcript-state.js.map +1 -0
- package/dist/ink-ui/screens/ReplScreen.d.ts +8 -2
- package/dist/ink-ui/screens/ReplScreen.d.ts.map +1 -1
- package/dist/ink-ui/screens/ReplScreen.js +247 -151
- package/dist/ink-ui/screens/ReplScreen.js.map +1 -1
- package/dist/ink-ui/types.d.ts +1 -42
- package/dist/ink-ui/types.d.ts.map +1 -1
- package/dist/ink-ui/types.js +15 -0
- package/dist/ink-ui/types.js.map +1 -1
- package/dist/print-ui/launch.d.ts +8 -0
- package/dist/print-ui/launch.d.ts.map +1 -0
- package/dist/print-ui/launch.js +205 -0
- package/dist/print-ui/launch.js.map +1 -0
- package/dist/runtime/agent-runtime-controller.d.ts +69 -0
- package/dist/runtime/agent-runtime-controller.d.ts.map +1 -0
- package/dist/runtime/agent-runtime-controller.js +225 -0
- package/dist/runtime/agent-runtime-controller.js.map +1 -0
- package/dist/runtime/agent-runtime-protocol.d.ts +90 -0
- package/dist/runtime/agent-runtime-protocol.d.ts.map +1 -0
- package/dist/runtime/agent-runtime-protocol.js +80 -0
- package/dist/runtime/agent-runtime-protocol.js.map +1 -0
- package/dist/runtime/chat-controller.d.ts +41 -0
- package/dist/runtime/chat-controller.d.ts.map +1 -0
- package/dist/runtime/chat-controller.js +577 -0
- package/dist/runtime/chat-controller.js.map +1 -0
- package/dist/runtime/ui-events.d.ts +59 -0
- package/dist/runtime/ui-events.d.ts.map +1 -0
- package/dist/runtime/ui-events.js +3 -0
- package/dist/runtime/ui-events.js.map +1 -0
- package/dist/services/commit-plan.d.ts +17 -0
- package/dist/services/commit-plan.d.ts.map +1 -0
- package/dist/services/commit-plan.js +161 -0
- package/dist/services/commit-plan.js.map +1 -0
- package/dist/services/config.d.ts +3 -2
- package/dist/services/config.d.ts.map +1 -1
- package/dist/services/config.js +7 -6
- package/dist/services/config.js.map +1 -1
- package/dist/services/doctor.d.ts +33 -0
- package/dist/services/doctor.d.ts.map +1 -0
- package/dist/services/doctor.js +211 -0
- package/dist/services/doctor.js.map +1 -0
- package/dist/services/file-context.d.ts +21 -0
- package/dist/services/file-context.d.ts.map +1 -0
- package/dist/services/file-context.js +194 -0
- package/dist/services/file-context.js.map +1 -0
- package/dist/services/global-config.d.ts +1 -1
- package/dist/services/global-config.d.ts.map +1 -1
- package/dist/services/project-instructions.d.ts +16 -0
- package/dist/services/project-instructions.d.ts.map +1 -0
- package/dist/services/project-instructions.js +135 -0
- package/dist/services/project-instructions.js.map +1 -0
- package/dist/services/prompt-context.d.ts +3 -0
- package/dist/services/prompt-context.d.ts.map +1 -0
- package/dist/services/prompt-context.js +13 -0
- package/dist/services/prompt-context.js.map +1 -0
- package/dist/services/session-storage.d.ts +3 -0
- package/dist/services/session-storage.d.ts.map +1 -1
- package/dist/services/session-storage.js +11 -0
- package/dist/services/session-storage.js.map +1 -1
- package/dist/services/workspace-diff.d.ts +27 -0
- package/dist/services/workspace-diff.d.ts.map +1 -0
- package/dist/services/workspace-diff.js +129 -0
- package/dist/services/workspace-diff.js.map +1 -0
- package/dist/terminal-ui/completion.d.ts +6 -0
- package/dist/terminal-ui/completion.d.ts.map +1 -0
- package/dist/terminal-ui/completion.js +80 -0
- package/dist/terminal-ui/completion.js.map +1 -0
- package/dist/terminal-ui/editor.d.ts +17 -0
- package/dist/terminal-ui/editor.d.ts.map +1 -0
- package/dist/terminal-ui/editor.js +42 -0
- package/dist/terminal-ui/editor.js.map +1 -0
- package/dist/terminal-ui/launch.d.ts +23 -0
- package/dist/terminal-ui/launch.d.ts.map +1 -0
- package/dist/terminal-ui/launch.js +567 -0
- package/dist/terminal-ui/launch.js.map +1 -0
- package/dist/terminal-ui/raw-editor.d.ts +57 -0
- package/dist/terminal-ui/raw-editor.d.ts.map +1 -0
- package/dist/terminal-ui/raw-editor.js +339 -0
- package/dist/terminal-ui/raw-editor.js.map +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +61 -19
- package/dist/tools/index.js.map +1 -1
- package/dist/tui-core/frame.d.ts +25 -0
- package/dist/tui-core/frame.d.ts.map +1 -0
- package/dist/tui-core/frame.js +104 -0
- package/dist/tui-core/frame.js.map +1 -0
- package/dist/tui-core/input-parser.d.ts +25 -0
- package/dist/tui-core/input-parser.d.ts.map +1 -0
- package/dist/tui-core/input-parser.js +207 -0
- package/dist/tui-core/input-parser.js.map +1 -0
- package/dist/tui-core/terminal-writer.d.ts +34 -0
- package/dist/tui-core/terminal-writer.d.ts.map +1 -0
- package/dist/tui-core/terminal-writer.js +92 -0
- package/dist/tui-core/terminal-writer.js.map +1 -0
- package/dist/tui-ui/launch.d.ts +7 -0
- package/dist/tui-ui/launch.d.ts.map +1 -0
- package/dist/tui-ui/launch.js +194 -0
- package/dist/tui-ui/launch.js.map +1 -0
- package/dist/tui-ui/layout.d.ts +9 -0
- package/dist/tui-ui/layout.d.ts.map +1 -0
- package/dist/tui-ui/layout.js +239 -0
- package/dist/tui-ui/layout.js.map +1 -0
- package/dist/tui-ui/pickers.d.ts +13 -0
- package/dist/tui-ui/pickers.d.ts.map +1 -0
- package/dist/tui-ui/pickers.js +79 -0
- package/dist/tui-ui/pickers.js.map +1 -0
- package/dist/tui-ui/runner.d.ts +43 -0
- package/dist/tui-ui/runner.d.ts.map +1 -0
- package/dist/tui-ui/runner.js +387 -0
- package/dist/tui-ui/runner.js.map +1 -0
- package/dist/tui-ui/state.d.ts +106 -0
- package/dist/tui-ui/state.d.ts.map +1 -0
- package/dist/tui-ui/state.js +202 -0
- package/dist/tui-ui/state.js.map +1 -0
- package/package.json +1 -1
|
@@ -37,24 +37,31 @@ exports.visibleCommandItems = visibleCommandItems;
|
|
|
37
37
|
exports.getFileQuery = getFileQuery;
|
|
38
38
|
exports.visibleFileItems = visibleFileItems;
|
|
39
39
|
exports.sessionItems = sessionItems;
|
|
40
|
+
exports.permissionItems = permissionItems;
|
|
41
|
+
exports.normalizePastedInput = normalizePastedInput;
|
|
42
|
+
exports.isMultilinePasteValue = isMultilinePasteValue;
|
|
40
43
|
exports.ReplScreen = ReplScreen;
|
|
41
44
|
const react_1 = __importStar(require("react"));
|
|
42
45
|
const ink_1 = require("ink");
|
|
43
46
|
const fs_1 = require("fs");
|
|
44
47
|
const path_1 = require("path");
|
|
45
48
|
const commands_1 = require("../../commands");
|
|
46
|
-
const parser_1 = require("../../commands/parser");
|
|
47
49
|
const types_1 = require("../../commands/types");
|
|
48
|
-
const
|
|
50
|
+
const agent_runtime_controller_1 = require("../../runtime/agent-runtime-controller");
|
|
49
51
|
const global_config_1 = require("../../services/global-config");
|
|
50
52
|
const sessions_1 = require("../../ui-v2/state/sessions");
|
|
53
|
+
const NativeCursor_1 = require("../components/NativeCursor");
|
|
51
54
|
const PromptInput_1 = require("../components/PromptInput");
|
|
52
55
|
const PixelHorseBanner_1 = require("../components/PixelHorseBanner");
|
|
53
56
|
const SelectList_1 = require("../components/SelectList");
|
|
54
57
|
const StatusLine_1 = require("../components/StatusLine");
|
|
55
|
-
const TerminalCursor_1 = require("../components/TerminalCursor");
|
|
56
58
|
const Transcript_1 = require("../components/Transcript");
|
|
57
|
-
const
|
|
59
|
+
const use_raw_input_bridge_1 = require("../hooks/use-raw-input-bridge");
|
|
60
|
+
const use_terminal_size_1 = require("../hooks/use-terminal-size");
|
|
61
|
+
const input_buffer_1 = require("../runtime/input-buffer");
|
|
62
|
+
const layout_budget_1 = require("../runtime/layout-budget");
|
|
63
|
+
const raw_input_1 = require("../runtime/raw-input");
|
|
64
|
+
const transcript_state_1 = require("../runtime/transcript-state");
|
|
58
65
|
let nextTranscriptId = 1;
|
|
59
66
|
function createId() {
|
|
60
67
|
return `ui-${nextTranscriptId++}`;
|
|
@@ -144,162 +151,181 @@ function sessionItems(request) {
|
|
|
144
151
|
].filter(Boolean).join(' '),
|
|
145
152
|
}));
|
|
146
153
|
}
|
|
147
|
-
function
|
|
148
|
-
const
|
|
149
|
-
|
|
154
|
+
function compactPermissionArgs(args) {
|
|
155
|
+
for (const key of ['path', 'file_path', 'file', 'cwd', 'command', 'pattern', 'query', 'url', 'target', 'sessionId']) {
|
|
156
|
+
const value = args[key];
|
|
157
|
+
if (typeof value === 'string') {
|
|
158
|
+
return value.length > 72 ? `${value.slice(0, 69)}...` : value;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const firstString = Object.values(args).find(value => typeof value === 'string');
|
|
162
|
+
if (typeof firstString === 'string') {
|
|
163
|
+
return firstString.length > 72 ? `${firstString.slice(0, 69)}...` : firstString;
|
|
164
|
+
}
|
|
165
|
+
return '';
|
|
166
|
+
}
|
|
167
|
+
function permissionItems(request) {
|
|
168
|
+
const detail = compactPermissionArgs(request.args);
|
|
169
|
+
return [
|
|
170
|
+
{
|
|
171
|
+
value: 'allow',
|
|
172
|
+
label: `Allow ${request.name}`,
|
|
173
|
+
description: [detail, request.reason].filter(Boolean).join(' '),
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
value: 'deny',
|
|
177
|
+
label: `Deny ${request.name}`,
|
|
178
|
+
description: 'Do not run this tool call',
|
|
179
|
+
},
|
|
180
|
+
];
|
|
181
|
+
}
|
|
182
|
+
function normalizePastedInput(value) {
|
|
183
|
+
return value
|
|
184
|
+
.replace(/\x1b\[200~/g, '')
|
|
185
|
+
.replace(/\x1b\[201~/g, '')
|
|
186
|
+
.replace(/\r\n/g, '\n')
|
|
187
|
+
.replace(/\r/g, '\n');
|
|
150
188
|
}
|
|
151
|
-
function
|
|
189
|
+
function isMultilinePasteValue(value) {
|
|
190
|
+
if (!value)
|
|
191
|
+
return false;
|
|
192
|
+
const normalized = normalizePastedInput(value);
|
|
193
|
+
return normalized.length > 1 && normalized.includes('\n');
|
|
194
|
+
}
|
|
195
|
+
function ReplScreen({ runtime, cursorController, resizeEpoch = 0 }) {
|
|
152
196
|
const app = (0, ink_1.useApp)();
|
|
153
197
|
const { stdout } = (0, ink_1.useStdout)();
|
|
154
|
-
const
|
|
155
|
-
const
|
|
198
|
+
const terminalSize = (0, use_terminal_size_1.useTerminalSize)(stdout);
|
|
199
|
+
const terminalHeight = terminalSize.height;
|
|
200
|
+
const terminalWidth = terminalSize.width;
|
|
201
|
+
const [transcriptState, dispatchTranscript] = (0, react_1.useReducer)(transcript_state_1.transcriptReducer, transcript_state_1.initialTranscriptState);
|
|
202
|
+
const [inputBuffer, dispatchInput] = (0, react_1.useReducer)(input_buffer_1.reduceInputBuffer, input_buffer_1.initialInputBuffer);
|
|
203
|
+
const input = inputBuffer.value;
|
|
204
|
+
const inputCursor = inputBuffer.cursor;
|
|
156
205
|
const [overlay, setOverlay] = (0, react_1.useState)(null);
|
|
206
|
+
const layout = (0, react_1.useMemo)(() => (0, layout_budget_1.getInkLayoutBudget)(terminalWidth, terminalHeight, { overlayVisible: overlay !== null }), [terminalWidth, terminalHeight, overlay]);
|
|
207
|
+
const { layoutWidth, maxLiveTranscriptItems, maxOverlayItems, maxPromptRows } = layout;
|
|
157
208
|
const [processing, setProcessing] = (0, react_1.useState)(false);
|
|
158
209
|
const [statusMessage, setStatusMessage] = (0, react_1.useState)('');
|
|
210
|
+
const [exiting, setExiting] = (0, react_1.useState)(false);
|
|
159
211
|
const [history, setHistory] = (0, react_1.useState)(() => (0, global_config_1.getInputHistory)());
|
|
160
212
|
const [historyIndex, setHistoryIndex] = (0, react_1.useState)(-1);
|
|
161
213
|
const [, setStoreVersion] = (0, react_1.useState)(0);
|
|
162
|
-
const
|
|
163
|
-
const
|
|
164
|
-
const inputRef = (0, react_1.useRef)(
|
|
214
|
+
const shuttingDownRef = (0, react_1.useRef)(false);
|
|
215
|
+
const lastCtrlCEventAtRef = (0, react_1.useRef)(0);
|
|
216
|
+
const inputRef = (0, react_1.useRef)(input_buffer_1.initialInputBuffer);
|
|
217
|
+
const promptBoxRef = (0, react_1.useRef)(null);
|
|
165
218
|
(0, react_1.useEffect)(() => {
|
|
166
|
-
inputRef.current =
|
|
167
|
-
}, [
|
|
219
|
+
inputRef.current = inputBuffer;
|
|
220
|
+
}, [inputBuffer]);
|
|
168
221
|
(0, react_1.useEffect)(() => runtime.store.subscribe(() => setStoreVersion(version => version + 1)), [runtime.store]);
|
|
169
222
|
const append = (0, react_1.useCallback)((entry) => {
|
|
170
223
|
const id = createId();
|
|
171
|
-
|
|
224
|
+
const next = { id, ...entry };
|
|
225
|
+
dispatchTranscript({ type: 'append', entry: next });
|
|
172
226
|
return id;
|
|
173
227
|
}, []);
|
|
174
228
|
const update = (0, react_1.useCallback)((id, patch) => {
|
|
175
|
-
|
|
229
|
+
dispatchTranscript({ type: 'update', id, patch });
|
|
230
|
+
}, []);
|
|
231
|
+
const finalize = (0, react_1.useCallback)((id, patch) => {
|
|
232
|
+
dispatchTranscript({ type: 'finalize', id, patch });
|
|
233
|
+
}, []);
|
|
234
|
+
const remove = (0, react_1.useCallback)((id) => {
|
|
235
|
+
dispatchTranscript({ type: 'remove', id });
|
|
176
236
|
}, []);
|
|
177
237
|
const events = (0, react_1.useMemo)(() => ({
|
|
178
238
|
append,
|
|
179
239
|
update,
|
|
180
|
-
|
|
240
|
+
finalize,
|
|
241
|
+
remove,
|
|
242
|
+
replaceTranscript: entries => {
|
|
243
|
+
dispatchTranscript({ type: 'replace', entries });
|
|
244
|
+
},
|
|
245
|
+
clearTranscript: () => {
|
|
246
|
+
dispatchTranscript({ type: 'clear' });
|
|
247
|
+
},
|
|
181
248
|
setStatus: setStatusMessage,
|
|
182
249
|
showSessionPicker: request => setOverlay({ type: 'sessions', selectedIndex: 0, request }),
|
|
250
|
+
showPermissionRequest: request => setOverlay({ type: 'permission', selectedIndex: 0, request }),
|
|
183
251
|
setProcessing,
|
|
184
|
-
}), [append, update]);
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}, [append]);
|
|
198
|
-
const runTurn = (0, react_1.useCallback)(async (firstInput) => {
|
|
199
|
-
let nextInput = firstInput;
|
|
200
|
-
while (nextInput && nextInput.trim()) {
|
|
201
|
-
appendSubmittedInput(nextInput);
|
|
202
|
-
const turn = turnControllerRef.current.beginTurn(nextInput);
|
|
203
|
-
runningRef.current = true;
|
|
204
|
-
setProcessing(true);
|
|
205
|
-
runtime.store.setProcessing(true);
|
|
206
|
-
setStatusMessage('');
|
|
207
|
-
try {
|
|
208
|
-
await controller.runInput(nextInput, { abortSignal: turn.abortSignal });
|
|
209
|
-
}
|
|
210
|
-
finally {
|
|
211
|
-
const revision = turnControllerRef.current.finishTurn(turn.id);
|
|
212
|
-
if (revision?.trim()) {
|
|
213
|
-
setStatusMessage('Restarting with latest instruction...');
|
|
214
|
-
nextInput = revision;
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
nextInput = undefined;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
runningRef.current = false;
|
|
222
|
-
setProcessing(false);
|
|
252
|
+
}), [append, finalize, remove, stdout, update]);
|
|
253
|
+
const agentController = (0, react_1.useMemo)(() => new agent_runtime_controller_1.AgentRuntimeController({
|
|
254
|
+
runtime,
|
|
255
|
+
events,
|
|
256
|
+
exitConfirmWindowMs: 5000,
|
|
257
|
+
useRuntimeToolPermissions: true,
|
|
258
|
+
beforeTurn: () => setStatusMessage(''),
|
|
259
|
+
}), [runtime, events]);
|
|
260
|
+
const shutdown = (0, react_1.useCallback)(() => {
|
|
261
|
+
if (shuttingDownRef.current)
|
|
262
|
+
return;
|
|
263
|
+
shuttingDownRef.current = true;
|
|
264
|
+
cursorController.disable();
|
|
223
265
|
runtime.store.setProcessing(false);
|
|
224
|
-
|
|
266
|
+
setProcessing(false);
|
|
267
|
+
setExiting(true);
|
|
268
|
+
setTimeout(() => {
|
|
269
|
+
app.exit();
|
|
270
|
+
}, 50);
|
|
271
|
+
void runtime.shutdown().catch(error => {
|
|
272
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
273
|
+
process.stderr.write(`OpenHorse shutdown warning: ${message}\n`);
|
|
274
|
+
});
|
|
275
|
+
}, [app, cursorController, runtime]);
|
|
225
276
|
const submit = (0, react_1.useCallback)((value) => {
|
|
226
277
|
const submitted = value.trim();
|
|
227
278
|
if (!submitted)
|
|
228
279
|
return;
|
|
229
|
-
|
|
280
|
+
dispatchInput({ type: 'clear' });
|
|
230
281
|
setOverlay(null);
|
|
231
282
|
(0, global_config_1.addToInputHistory)(submitted);
|
|
232
283
|
setHistory((0, global_config_1.getInputHistory)());
|
|
233
284
|
setHistoryIndex(-1);
|
|
234
|
-
|
|
285
|
+
const result = agentController.handle({ type: 'submit', text: submitted, source: 'composer' });
|
|
286
|
+
if (result.type === 'exit_requested') {
|
|
235
287
|
void shutdown();
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
if (turnControllerRef.current.hasActiveTurn()) {
|
|
239
|
-
const parsed = (0, parser_1.parseInput)(submitted);
|
|
240
|
-
if (parsed.isCommand) {
|
|
241
|
-
setStatusMessage('Command ignored while agent is running. Press Ctrl+C to interrupt first.');
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
turnControllerRef.current.clearExitIntent();
|
|
245
|
-
turnControllerRef.current.requestRevision(submitted);
|
|
246
|
-
setStatusMessage('Revision received. Interrupting current response...');
|
|
247
|
-
return;
|
|
248
288
|
}
|
|
249
|
-
|
|
250
|
-
|
|
289
|
+
}, [agentController, shutdown]);
|
|
290
|
+
const answerPermission = (0, react_1.useCallback)((request, approved) => {
|
|
291
|
+
setOverlay(null);
|
|
292
|
+
agentController.handle({
|
|
293
|
+
type: 'permission_decision',
|
|
294
|
+
requestId: request.id,
|
|
295
|
+
approved,
|
|
296
|
+
source: 'keyboard',
|
|
297
|
+
});
|
|
298
|
+
}, [agentController]);
|
|
251
299
|
const closeOverlay = (0, react_1.useCallback)(() => {
|
|
252
300
|
if (!overlay)
|
|
253
301
|
return false;
|
|
302
|
+
if (overlay.type === 'permission') {
|
|
303
|
+
answerPermission(overlay.request, false);
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
254
306
|
setOverlay(null);
|
|
255
307
|
return true;
|
|
256
|
-
}, [overlay]);
|
|
308
|
+
}, [answerPermission, overlay]);
|
|
257
309
|
const handleCtrlC = (0, react_1.useCallback)(() => {
|
|
258
310
|
if (closeOverlay()) {
|
|
259
|
-
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
if (turnControllerRef.current.hasActiveTurn()) {
|
|
263
|
-
const shouldExit = turnControllerRef.current.registerExitIntent();
|
|
264
|
-
turnControllerRef.current.interruptActiveTurn();
|
|
265
|
-
if (shouldExit) {
|
|
266
|
-
void shutdown();
|
|
267
|
-
}
|
|
268
|
-
else {
|
|
269
|
-
setStatusMessage('again exits');
|
|
270
|
-
}
|
|
311
|
+
agentController.handle({ type: 'clear_exit_intent' });
|
|
271
312
|
return;
|
|
272
313
|
}
|
|
273
|
-
|
|
314
|
+
const result = agentController.handle({ type: 'interrupt', source: 'keyboard' });
|
|
315
|
+
if (result.type === 'exit_requested') {
|
|
274
316
|
void shutdown();
|
|
317
|
+
}
|
|
318
|
+
}, [agentController, closeOverlay, shutdown]);
|
|
319
|
+
const handleCtrlCEvent = (0, react_1.useCallback)((options = {}) => {
|
|
320
|
+
const now = Date.now();
|
|
321
|
+
const delta = now - lastCtrlCEventAtRef.current;
|
|
322
|
+
if (!options.allowRapidRepeat && delta < 30) {
|
|
275
323
|
return;
|
|
276
324
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
(0, react_1.useEffect)(() => {
|
|
280
|
-
let lastDataCtrlCAt = 0;
|
|
281
|
-
const onData = (chunk) => {
|
|
282
|
-
const text = Buffer.isBuffer(chunk) ? chunk.toString('utf8') : chunk;
|
|
283
|
-
const ctrlCCount = [...text].filter(char => char === '\u0003').length;
|
|
284
|
-
if (ctrlCCount > 0) {
|
|
285
|
-
lastDataCtrlCAt = Date.now();
|
|
286
|
-
}
|
|
287
|
-
for (let i = 0; i < ctrlCCount; i++) {
|
|
288
|
-
handleCtrlC();
|
|
289
|
-
}
|
|
290
|
-
};
|
|
291
|
-
const onSignal = () => {
|
|
292
|
-
if (Date.now() - lastDataCtrlCAt < 50)
|
|
293
|
-
return;
|
|
294
|
-
handleCtrlC();
|
|
295
|
-
};
|
|
296
|
-
process.stdin.on('data', onData);
|
|
297
|
-
process.on('SIGINT', onSignal);
|
|
298
|
-
return () => {
|
|
299
|
-
process.stdin.off('data', onData);
|
|
300
|
-
process.off('SIGINT', onSignal);
|
|
301
|
-
};
|
|
325
|
+
lastCtrlCEventAtRef.current = now;
|
|
326
|
+
handleCtrlC();
|
|
302
327
|
}, [handleCtrlC]);
|
|
328
|
+
const lastRawInputRef = (0, use_raw_input_bridge_1.useRawInputBridge)(handleCtrlCEvent);
|
|
303
329
|
const commandItems = visibleCommandItems(input);
|
|
304
330
|
const fileItems = visibleFileItems(runtime.cwd, input);
|
|
305
331
|
const completeCommand = (0, react_1.useCallback)((item, submitImmediately) => {
|
|
@@ -310,14 +336,14 @@ function ReplScreen({ runtime }) {
|
|
|
310
336
|
submit(value);
|
|
311
337
|
}
|
|
312
338
|
else {
|
|
313
|
-
|
|
339
|
+
dispatchInput({ type: 'set', value });
|
|
314
340
|
}
|
|
315
341
|
}, [submit]);
|
|
316
342
|
const completeFile = (0, react_1.useCallback)((item) => {
|
|
317
|
-
const fileQuery = getFileQuery(inputRef.current);
|
|
343
|
+
const fileQuery = getFileQuery(inputRef.current.value);
|
|
318
344
|
if (!fileQuery)
|
|
319
345
|
return;
|
|
320
|
-
|
|
346
|
+
dispatchInput({ type: 'set', value: `${fileQuery.base}@${item.value}` });
|
|
321
347
|
setOverlay(null);
|
|
322
348
|
}, []);
|
|
323
349
|
const selectSession = (0, react_1.useCallback)((request, index) => {
|
|
@@ -325,16 +351,26 @@ function ReplScreen({ runtime }) {
|
|
|
325
351
|
if (!session)
|
|
326
352
|
return;
|
|
327
353
|
setOverlay(null);
|
|
328
|
-
|
|
329
|
-
|
|
354
|
+
const result = agentController.handle({
|
|
355
|
+
type: 'select_session',
|
|
356
|
+
sessionId: session.id,
|
|
357
|
+
allProjects: request.allProjects,
|
|
358
|
+
source: 'picker',
|
|
359
|
+
});
|
|
360
|
+
if (result.type === 'exit_requested') {
|
|
361
|
+
void shutdown();
|
|
362
|
+
}
|
|
363
|
+
}, [agentController, shutdown]);
|
|
330
364
|
(0, ink_1.useInput)((value, key) => {
|
|
331
365
|
const isReturn = key?.return || value === '\r' || value === '\n';
|
|
332
|
-
if (value === '
|
|
366
|
+
if (key?.ctrl && value === 'c') {
|
|
367
|
+
handleCtrlCEvent();
|
|
333
368
|
return;
|
|
334
369
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
370
|
+
agentController.handle({ type: 'clear_exit_intent' });
|
|
371
|
+
if (!key?.ctrl && isMultilinePasteValue(value)) {
|
|
372
|
+
dispatchInput({ type: 'inputChunk', text: normalizePastedInput(value) });
|
|
373
|
+
setOverlay(null);
|
|
338
374
|
return;
|
|
339
375
|
}
|
|
340
376
|
if (overlay?.type === 'shortcuts') {
|
|
@@ -411,75 +447,135 @@ function ReplScreen({ runtime }) {
|
|
|
411
447
|
return;
|
|
412
448
|
}
|
|
413
449
|
}
|
|
450
|
+
if (overlay?.type === 'permission') {
|
|
451
|
+
const items = permissionItems(overlay.request);
|
|
452
|
+
if (key?.escape || value?.toLowerCase() === 'n') {
|
|
453
|
+
answerPermission(overlay.request, false);
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
if (value?.toLowerCase() === 'y') {
|
|
457
|
+
answerPermission(overlay.request, true);
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
if (key?.upArrow) {
|
|
461
|
+
setOverlay({ ...overlay, selectedIndex: Math.max(0, overlay.selectedIndex - 1) });
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
if (key?.downArrow || key?.tab) {
|
|
465
|
+
setOverlay({ ...overlay, selectedIndex: Math.min(items.length - 1, overlay.selectedIndex + 1) });
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
if (isReturn) {
|
|
469
|
+
answerPermission(overlay.request, items[overlay.selectedIndex]?.value === 'allow');
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
414
474
|
if (isReturn && key?.meta) {
|
|
415
|
-
|
|
475
|
+
dispatchInput({ type: 'insert', text: '\n' });
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
if (key?.leftArrow) {
|
|
479
|
+
dispatchInput({ type: 'move', direction: 'left' });
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
if (key?.rightArrow) {
|
|
483
|
+
dispatchInput({ type: 'move', direction: 'right' });
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
if (key?.ctrl && value === 'a') {
|
|
487
|
+
dispatchInput({ type: 'move', direction: 'home' });
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
if (key?.ctrl && value === 'e') {
|
|
491
|
+
dispatchInput({ type: 'move', direction: 'end' });
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
if (key?.ctrl && value === 'u') {
|
|
495
|
+
const rawInput = lastRawInputRef.current;
|
|
496
|
+
dispatchInput({ type: 'inputChunk', text: rawInput.startsWith('\x15') ? rawInput : '\x15' });
|
|
497
|
+
setOverlay(null);
|
|
416
498
|
return;
|
|
417
499
|
}
|
|
418
500
|
if (isReturn) {
|
|
419
|
-
submit(inputRef.current);
|
|
501
|
+
submit(inputRef.current.value);
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
if (value && (0, raw_input_1.hasDeletionRawInput)(value)) {
|
|
505
|
+
dispatchInput({ type: 'inputChunk', text: value });
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
if (key?.backspace) {
|
|
509
|
+
dispatchInput({ type: 'backspace' });
|
|
420
510
|
return;
|
|
421
511
|
}
|
|
422
|
-
if (key?.
|
|
423
|
-
|
|
512
|
+
if (key?.delete) {
|
|
513
|
+
dispatchInput({ type: (0, raw_input_1.deleteActionFromRawInput)(value || lastRawInputRef.current) });
|
|
424
514
|
return;
|
|
425
515
|
}
|
|
426
|
-
if (key?.upArrow && inputRef.current === '' && history.length > 0) {
|
|
516
|
+
if (key?.upArrow && inputRef.current.value === '' && history.length > 0) {
|
|
427
517
|
const nextIndex = Math.min(history.length - 1, historyIndex + 1);
|
|
428
518
|
setHistoryIndex(nextIndex);
|
|
429
|
-
|
|
519
|
+
dispatchInput({ type: 'set', value: history[nextIndex]?.content ?? '' });
|
|
430
520
|
return;
|
|
431
521
|
}
|
|
432
522
|
if (key?.downArrow && historyIndex >= 0) {
|
|
433
523
|
const nextIndex = historyIndex - 1;
|
|
434
524
|
setHistoryIndex(nextIndex);
|
|
435
|
-
|
|
525
|
+
dispatchInput({ type: 'set', value: nextIndex >= 0 ? history[nextIndex]?.content ?? '' : '' });
|
|
436
526
|
return;
|
|
437
527
|
}
|
|
438
528
|
if (key?.tab) {
|
|
439
|
-
if (inputRef.current.startsWith('/')) {
|
|
529
|
+
if (inputRef.current.value.startsWith('/')) {
|
|
440
530
|
setOverlay({ type: 'commands', selectedIndex: 0 });
|
|
441
531
|
}
|
|
442
|
-
else if (getFileQuery(inputRef.current)) {
|
|
532
|
+
else if (getFileQuery(inputRef.current.value)) {
|
|
443
533
|
setOverlay({ type: 'files', selectedIndex: 0 });
|
|
444
534
|
}
|
|
445
535
|
return;
|
|
446
536
|
}
|
|
447
|
-
if (value === '/' && inputRef.current === '' && !
|
|
448
|
-
|
|
537
|
+
if (value === '/' && inputRef.current.value === '' && !agentController.hasActiveTurn()) {
|
|
538
|
+
dispatchInput({ type: 'set', value: '/' });
|
|
449
539
|
setOverlay({ type: 'commands', selectedIndex: 0 });
|
|
450
540
|
return;
|
|
451
541
|
}
|
|
452
|
-
if (value === '@' && !
|
|
453
|
-
|
|
542
|
+
if (value === '@' && !agentController.hasActiveTurn()) {
|
|
543
|
+
dispatchInput({ type: 'insert', text: '@' });
|
|
454
544
|
setOverlay({ type: 'files', selectedIndex: 0 });
|
|
455
545
|
return;
|
|
456
546
|
}
|
|
457
|
-
if (value === '?' && inputRef.current === '') {
|
|
547
|
+
if (value === '?' && inputRef.current.value === '') {
|
|
458
548
|
setOverlay({ type: 'shortcuts' });
|
|
459
549
|
return;
|
|
460
550
|
}
|
|
461
551
|
if (value && !key?.ctrl) {
|
|
462
|
-
|
|
552
|
+
dispatchInput({ type: 'inputChunk', text: value });
|
|
463
553
|
}
|
|
464
554
|
});
|
|
465
|
-
const terminalHeight = stdout?.rows || process.stdout.rows || 24;
|
|
466
|
-
const terminalWidth = stdout?.columns || process.stdout.columns || 80;
|
|
467
|
-
const transcriptItems = Math.max(6, terminalHeight - 10);
|
|
468
555
|
const modeText = (0, types_1.getModeDisplayText)(runtime.store.getSnapshot().permissionMode);
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
556
|
+
const staticItems = (0, react_1.useMemo)(() => [
|
|
557
|
+
{ id: 'openhorse-banner', type: 'banner' },
|
|
558
|
+
...(0, transcript_state_1.staticTranscriptEntries)(transcriptState).map(entry => ({ ...entry, type: 'entry' })),
|
|
559
|
+
], [transcriptState]);
|
|
560
|
+
const liveEntries = (0, react_1.useMemo)(() => (0, transcript_state_1.liveTranscriptEntries)(transcriptState), [transcriptState]);
|
|
561
|
+
if (exiting) {
|
|
562
|
+
return react_1.default.createElement(ink_1.Box, { flexDirection: "column" });
|
|
563
|
+
}
|
|
564
|
+
return (react_1.default.createElement(ink_1.Box, { flexDirection: "column" },
|
|
565
|
+
react_1.default.createElement(ink_1.Static, { key: `${transcriptState.generation}:${resizeEpoch}`, items: staticItems }, item => item.type === 'banner' ? (react_1.default.createElement(ink_1.Box, { key: item.id, flexDirection: "column", marginBottom: 1 },
|
|
566
|
+
react_1.default.createElement(PixelHorseBanner_1.PixelHorseBanner, { runtime: runtime, width: layoutWidth }))) : (react_1.default.createElement(Transcript_1.TranscriptEntryBlock, { key: item.id, entry: item, width: layoutWidth }))),
|
|
567
|
+
react_1.default.createElement(Transcript_1.Transcript, { entries: liveEntries, maxItems: maxLiveTranscriptItems, width: layoutWidth, emptyMessage: null }),
|
|
568
|
+
overlay?.type === 'commands' ? (react_1.default.createElement(SelectList_1.SelectList, { title: `Commands ${input.slice(1) ? `"${input.slice(1)}"` : ''}`, items: commandItems, selectedIndex: overlay.selectedIndex, maxVisibleItems: maxOverlayItems, footer: "\u2191\u2193 navigate Tab complete Enter select Esc cancel", width: layoutWidth })) : null,
|
|
569
|
+
overlay?.type === 'files' ? (react_1.default.createElement(SelectList_1.SelectList, { title: "Files", items: fileItems, selectedIndex: overlay.selectedIndex, maxVisibleItems: maxOverlayItems, footer: "\u2191\u2193 navigate Tab/Enter complete Esc cancel", width: layoutWidth })) : null,
|
|
570
|
+
overlay?.type === 'sessions' ? (react_1.default.createElement(SelectList_1.SelectList, { title: overlay.request.title, items: sessionItems(overlay.request), selectedIndex: overlay.selectedIndex, maxVisibleItems: Math.min(overlay.request.maxVisibleItems ?? maxOverlayItems, maxOverlayItems), footer: "\u2191\u2193 scroll PgUp/PgDn Enter resume Esc cancel", width: layoutWidth })) : null,
|
|
571
|
+
overlay?.type === 'permission' ? (react_1.default.createElement(SelectList_1.SelectList, { title: "Tool Permission", items: permissionItems(overlay.request), selectedIndex: overlay.selectedIndex, maxVisibleItems: 2, footer: "\u2191\u2193 choose Enter select y allow n/Esc deny", width: layoutWidth })) : null,
|
|
572
|
+
overlay?.type === 'shortcuts' ? (react_1.default.createElement(ink_1.Box, { width: layoutWidth, borderStyle: "single", borderColor: "gray", paddingX: 1, flexDirection: "column" },
|
|
477
573
|
react_1.default.createElement(ink_1.Text, { color: "cyan" }, "Shortcuts"),
|
|
478
574
|
react_1.default.createElement(ink_1.Text, null, "/ commands @ file picker ? shortcuts"),
|
|
479
575
|
react_1.default.createElement(ink_1.Text, null, "Alt+Enter newline Ctrl+C interrupt / twice exits \u2191\u2193 history or picker navigation"),
|
|
480
576
|
react_1.default.createElement(ink_1.Text, { color: "gray" }, "Enter or Esc closes this panel."))) : null,
|
|
481
|
-
react_1.default.createElement(StatusLine_1.StatusLine, { runtime: runtime, running: processing, statusMessage: statusMessage, width:
|
|
482
|
-
react_1.default.createElement(PromptInput_1.PromptInput, { value: input, running: processing, modeText: modeText, width:
|
|
483
|
-
react_1.default.createElement(
|
|
577
|
+
react_1.default.createElement(StatusLine_1.StatusLine, { runtime: runtime, running: processing, statusMessage: statusMessage, width: layoutWidth }),
|
|
578
|
+
react_1.default.createElement(PromptInput_1.PromptInput, { ref: promptBoxRef, value: input, cursor: inputCursor, running: processing, modeText: modeText, width: layoutWidth, maxRows: maxPromptRows }),
|
|
579
|
+
react_1.default.createElement(NativeCursor_1.NativeCursor, { cursorController: cursorController, promptRef: promptBoxRef, value: input, cursor: inputCursor, maxRows: maxPromptRows, terminalWidth: layoutWidth })));
|
|
484
580
|
}
|
|
485
581
|
//# sourceMappingURL=ReplScreen.js.map
|