orquesta-cli 0.2.112 → 0.2.115

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.
@@ -91,7 +91,7 @@ export const TodoPanel = React.memo(({ todos, currentTodoId, isProcessing = fals
91
91
  React.createElement(Box, null,
92
92
  React.createElement(Box, { width: 2 }, isInProgress && isProcessing ? (React.createElement(Text, { color: "blueBright" },
93
93
  React.createElement(Spinner, { type: "dots2" }))) : (React.createElement(Text, { color: config.color }, config.icon))),
94
- React.createElement(Text, { color: isCompleted ? 'gray' : isInProgress ? 'white' : 'gray', bold: isInProgress, dimColor: isCompleted, strikethrough: isCompleted }, todo.title),
94
+ React.createElement(Text, { color: isCompleted ? 'gray' : isInProgress ? 'white' : 'gray', bold: isInProgress, strikethrough: isCompleted }, todo.title),
95
95
  isInProgress && isProcessing && (React.createElement(Text, { color: "blueBright" }, " \u2190"))),
96
96
  todo.error && (React.createElement(Box, { marginLeft: 2 },
97
97
  React.createElement(Text, { color: "red", dimColor: true },
@@ -86,9 +86,9 @@ export const ActivityIndicator = ({ activity, startTime, detail, subActivities =
86
86
  formatTime(elapsedSeconds)),
87
87
  renderProgressBar()),
88
88
  detail && (React.createElement(Box, { marginLeft: 2 },
89
- React.createElement(Text, { color: "gray", dimColor: true }, detail))),
89
+ React.createElement(Text, { color: "cyan" }, detail))),
90
90
  stepName && (React.createElement(Box, { marginLeft: 2 },
91
- React.createElement(Text, { color: "gray", dimColor: true },
91
+ React.createElement(Text, { color: "gray" },
92
92
  currentStep,
93
93
  "/",
94
94
  totalSteps,
@@ -98,15 +98,15 @@ export const ActivityIndicator = ({ activity, startTime, detail, subActivities =
98
98
  const subInfo = ACTIVITY_INFO[sub.type];
99
99
  return (React.createElement(Box, { key: idx },
100
100
  getStatusIcon(sub.status),
101
- React.createElement(Text, { color: "gray", dimColor: true },
101
+ React.createElement(Text, { color: "gray" },
102
102
  " ",
103
103
  subInfo.label),
104
- sub.detail && React.createElement(Text, { color: "gray", dimColor: true },
104
+ sub.detail && React.createElement(Text, { color: "white" },
105
105
  ": ",
106
106
  sub.detail)));
107
107
  }))),
108
108
  tokenCount !== undefined && (React.createElement(Box, { marginLeft: 2 },
109
- React.createElement(Text, { color: "gray", dimColor: true },
109
+ React.createElement(Text, { color: "gray" },
110
110
  formatTokens(tokenCount),
111
111
  " tokens",
112
112
  tokensPerSecond !== undefined && tokensPerSecond > 0 && ` · ${tokensPerSecond.toFixed(0)} tok/s`)))));
@@ -3,6 +3,7 @@ interface CustomTextInputProps {
3
3
  value: string;
4
4
  onChange: (value: string) => void;
5
5
  onSubmit?: (value: string) => void;
6
+ onCtrlS?: (value: string) => void;
6
7
  onHistoryPrev?: () => void;
7
8
  onHistoryNext?: () => void;
8
9
  placeholder?: string;
@@ -1,7 +1,7 @@
1
1
  import React, { useState, useEffect, useRef } from 'react';
2
2
  import { Box, Text, useStdin } from 'ink';
3
3
  const MULTILINE_COLLAPSE_THRESHOLD = 10;
4
- export const CustomTextInput = ({ value, onChange, onSubmit, onHistoryPrev, onHistoryNext, placeholder = '', focus = true, }) => {
4
+ export const CustomTextInput = ({ value, onChange, onSubmit, onCtrlS, onHistoryPrev, onHistoryNext, placeholder = '', focus = true, }) => {
5
5
  const { stdin, setRawMode } = useStdin();
6
6
  const [cursorPosition, setCursorPosition] = useState(value.length);
7
7
  const previousValueLength = useRef(value.length);
@@ -10,6 +10,7 @@ export const CustomTextInput = ({ value, onChange, onSubmit, onHistoryPrev, onHi
10
10
  const cursorPositionRef = useRef(cursorPosition);
11
11
  const onChangeRef = useRef(onChange);
12
12
  const onSubmitRef = useRef(onSubmit);
13
+ const onCtrlSRef = useRef(onCtrlS);
13
14
  const onHistoryPrevRef = useRef(onHistoryPrev);
14
15
  const onHistoryNextRef = useRef(onHistoryNext);
15
16
  const setIsCollapsedViewRef = useRef(setIsCollapsedView);
@@ -19,6 +20,7 @@ export const CustomTextInput = ({ value, onChange, onSubmit, onHistoryPrev, onHi
19
20
  cursorPositionRef.current = cursorPosition;
20
21
  onChangeRef.current = onChange;
21
22
  onSubmitRef.current = onSubmit;
23
+ onCtrlSRef.current = onCtrlS;
22
24
  onHistoryPrevRef.current = onHistoryPrev;
23
25
  onHistoryNextRef.current = onHistoryNext;
24
26
  setIsCollapsedViewRef.current = setIsCollapsedView;
@@ -49,6 +51,10 @@ export const CustomTextInput = ({ value, onChange, onSubmit, onHistoryPrev, onHi
49
51
  const str = data.toString();
50
52
  const currentValue = valueRef.current;
51
53
  const currentCursor = cursorPositionRef.current;
54
+ if (str === '\x13') {
55
+ onCtrlSRef.current?.(currentValue);
56
+ return;
57
+ }
52
58
  const deleteWordBeforeCursor = (value, cursor) => {
53
59
  if (cursor > 0) {
54
60
  const beforeCursor = value.slice(0, cursor);
@@ -50,7 +50,7 @@ const require = createRequire(import.meta.url);
50
50
  const pkg = require('../../../package.json');
51
51
  const VERSION = pkg.version;
52
52
  const TOOLS_REQUIRING_APPROVAL = new Set(['create_file', 'edit_file', 'bash']);
53
- const STARTUP_TIP = 'Type /help for available commands. Type /model to switch models.';
53
+ const STARTUP_TIP = 'Type /help for commands, /model to switch models. Press Ctrl+S to send now (or steer the AI mid-task).';
54
54
  function shortenPath(fullPath) {
55
55
  const homeDir = os.homedir();
56
56
  if (fullPath.startsWith(homeDir)) {
@@ -640,30 +640,33 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeL
640
640
  if (key.ctrl && inputChar === 'o') {
641
641
  setShowLogFiles(prev => !prev);
642
642
  }
643
- if (key.ctrl && inputChar === 's') {
644
- if (showSessionBrowser ||
645
- showSettings ||
646
- showSetupWizard ||
647
- fileBrowserState.showFileBrowser ||
648
- commandBrowserState.showCommandBrowser ||
649
- planExecutionState.askUserRequest ||
650
- pendingToolApproval) {
651
- return;
652
- }
653
- const text = input.trim();
654
- if (text) {
655
- if (isProcessing) {
656
- addLog({ type: 'user_input', content: `(⚡ steering now — picked up at next step) ${text}` });
657
- setPendingUserMessage(text);
658
- setInput('');
659
- }
660
- else {
661
- handleSubmitRef.current?.(text);
662
- }
663
- }
643
+ });
644
+ const handleCtrlS = useCallback((rawValue) => {
645
+ if (showSessionBrowser ||
646
+ showSettings ||
647
+ showSetupWizard ||
648
+ fileBrowserState.showFileBrowser ||
649
+ commandBrowserState.showCommandBrowser ||
650
+ planExecutionState.askUserRequest ||
651
+ pendingToolApproval) {
664
652
  return;
665
653
  }
666
- });
654
+ const text = (rawValue ?? '').trim();
655
+ if (!text)
656
+ return;
657
+ if (isProcessing) {
658
+ addLog({ type: 'user_input', content: `(⚡ Ctrl+S — steering now, picked up at next step) ${text}` });
659
+ setPendingUserMessage(text);
660
+ setInput('');
661
+ }
662
+ else {
663
+ handleSubmitRef.current?.(text);
664
+ }
665
+ }, [
666
+ showSessionBrowser, showSettings, showSetupWizard,
667
+ fileBrowserState.showFileBrowser, commandBrowserState.showCommandBrowser,
668
+ planExecutionState.askUserRequest, pendingToolApproval, isProcessing, addLog,
669
+ ]);
667
670
  const handleFileSelect = useCallback((filePaths) => {
668
671
  logger.debug('File selected', { filePaths });
669
672
  const newInput = fileBrowserState.handleFileSelect(filePaths, input);
@@ -1348,6 +1351,7 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeL
1348
1351
  : reason;
1349
1352
  return (React.createElement(Box, { key: entry.id, flexDirection: "column", marginTop: 1 },
1350
1353
  React.createElement(Box, null,
1354
+ React.createElement(Text, { color: "gray", dimColor: true }, "\u2502 "),
1351
1355
  React.createElement(Text, { color: "cyan", bold: true },
1352
1356
  icon,
1353
1357
  " ",
@@ -1421,9 +1425,11 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeL
1421
1425
  ". ",
1422
1426
  item))))));
1423
1427
  case 'todo_start':
1424
- return (React.createElement(Box, { key: entry.id, marginTop: 1 },
1425
- React.createElement(Text, { color: "blue", bold: true }, "\uD83C\uDFB5 \u25B6 "),
1426
- React.createElement(Text, { bold: true }, entry.content)));
1428
+ return (React.createElement(Box, { key: entry.id, flexDirection: "column", marginTop: 1 },
1429
+ React.createElement(Text, { color: "gray", dimColor: true }, '─'.repeat(44)),
1430
+ React.createElement(Box, null,
1431
+ React.createElement(Text, { color: "blue", bold: true }, "\uD83C\uDFB5 \u25B6 "),
1432
+ React.createElement(Text, { bold: true }, entry.content))));
1427
1433
  case 'todo_complete':
1428
1434
  return (React.createElement(Box, { key: entry.id, marginLeft: 2 },
1429
1435
  React.createElement(Text, { color: "white" }, "\u23BF "),
@@ -1489,7 +1495,7 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeL
1489
1495
  React.createElement(ActivityIndicator, { activity: getCurrentActivityType(), startTime: activityStartTime, detail: activityDetail || currentToolName || '', subActivities: subActivities, modelName: currentModelInfo.model }),
1490
1496
  streamingText && (React.createElement(StreamingOutput, { text: streamingText })))),
1491
1497
  isDocsSearching && (React.createElement(DocsSearchProgress, { logs: docsSearchLogs, isSearching: isDocsSearching })),
1492
- planExecutionState.todos.length > 0 && (React.createElement(Box, { marginTop: 2, marginBottom: 1 },
1498
+ planExecutionState.todos.length > 0 && (React.createElement(Box, { marginTop: 1 },
1493
1499
  React.createElement(TodoPanel, { todos: planExecutionState.todos, currentTodoId: planExecutionState.currentTodoId, isProcessing: isProcessing }))),
1494
1500
  updatePhase === 'available' && (React.createElement(UpdateNotification, { currentVersion: VERSION, latestVersion: updateLatest })),
1495
1501
  updatePhase === 'updating' && (React.createElement(UpdateNotification, { currentVersion: VERSION, latestVersion: updateLatest, isUpdating: true, updateProgress: updateProgress })),
@@ -1508,7 +1514,7 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeL
1508
1514
  return;
1509
1515
  }
1510
1516
  setInput(value);
1511
- }, onSubmit: handleSubmit, onHistoryPrev: isAnyPanelOpen ? undefined : handleHistoryPrev, onHistoryNext: isAnyPanelOpen ? undefined : handleHistoryNext, placeholder: isProcessing
1517
+ }, onSubmit: handleSubmit, onCtrlS: handleCtrlS, onHistoryPrev: isAnyPanelOpen ? undefined : handleHistoryPrev, onHistoryNext: isAnyPanelOpen ? undefined : handleHistoryNext, placeholder: isProcessing
1512
1518
  ? "AI is working... (type + Ctrl+S to steer now)"
1513
1519
  : showSessionBrowser
1514
1520
  ? "Select a session or press ESC..."
@@ -1518,6 +1524,9 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeL
1518
1524
  ? "Select a doc source or press ESC..."
1519
1525
  : "Type your message... (@ files, / commands, Alt+Enter newline, Ctrl+S send)", focus: !showSessionBrowser && !showSettings && !showDocsBrowser && !planExecutionState.askUserRequest && updatePhase === 'hidden' })),
1520
1526
  input.length > 0 && (React.createElement(Text, { color: input.length > 4000 ? 'red' : input.length > 2000 ? 'yellow' : 'gray', dimColor: true }, input.length.toLocaleString())))),
1527
+ isProcessing && input.trim().length > 0 && (React.createElement(Box, { paddingX: 1 },
1528
+ React.createElement(Text, { color: "yellow", bold: true }, "\u26A1 Ctrl+S"),
1529
+ React.createElement(Text, { color: "gray" }, " to send now, or Enter to queue for the next step"))),
1521
1530
  fileBrowserState.showFileBrowser && !isProcessing && (React.createElement(Box, { marginTop: 0 }, fileBrowserState.isLoadingFiles ? (React.createElement(Box, { borderStyle: "single", borderColor: "yellow", paddingX: 1 },
1522
1531
  React.createElement(Spinner, { type: "dots" }),
1523
1532
  React.createElement(Text, { color: "yellow" }, " Loading files..."))) : (React.createElement(FileBrowser, { filter: fileBrowserState.filterText, onSelect: handleFileSelect, onCancel: fileBrowserState.handleFileBrowserCancel, cachedFiles: fileBrowserState.cachedFileList })))),
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  interface StreamingOutputProps {
3
3
  text: string;
4
+ height?: number;
4
5
  }
5
6
  export declare const StreamingOutput: React.FC<StreamingOutputProps>;
6
7
  export {};
@@ -1,9 +1,11 @@
1
1
  import React from 'react';
2
2
  import { Box, Text } from 'ink';
3
- export const StreamingOutput = ({ text }) => {
3
+ export const StreamingOutput = ({ text, height = 3 }) => {
4
4
  if (!text)
5
5
  return null;
6
- return (React.createElement(Box, { marginTop: 1, paddingX: 1 },
7
- React.createElement(Text, { wrap: "wrap" }, text.split('\n').slice(-8).join('\n'))));
6
+ const tail = text.split('\n').slice(-height);
7
+ const pad = Array(Math.max(0, height - tail.length)).fill('');
8
+ const rows = [...pad, ...tail];
9
+ return (React.createElement(Box, { paddingX: 1, flexDirection: "column", height: height }, rows.map((line, i) => (React.createElement(Text, { key: i, wrap: "truncate-end" }, line || ' ')))));
8
10
  };
9
11
  //# sourceMappingURL=StreamingOutput.js.map
@@ -1,13 +1,30 @@
1
1
  #!/usr/bin/env node
2
2
  import React from 'react';
3
+ import { execSync } from 'child_process';
3
4
  import { render } from 'ink';
4
5
  import { PlanExecuteApp } from './components/PlanExecuteApp.js';
5
6
  import { createLLMClient } from '../core/llm/llm-client.js';
6
7
  import { configManager } from '../core/config/config-manager.js';
7
8
  import { initializeOptionalTools } from '../tools/registry.js';
8
9
  import { logger } from '../utils/logger.js';
10
+ function disableFlowControl() {
11
+ if (process.platform === 'win32' || !process.stdin.isTTY)
12
+ return;
13
+ try {
14
+ execSync('stty -ixon', { stdio: 'inherit' });
15
+ process.on('exit', () => {
16
+ try {
17
+ execSync('stty ixon', { stdio: 'inherit' });
18
+ }
19
+ catch { }
20
+ });
21
+ }
22
+ catch {
23
+ }
24
+ }
9
25
  (async () => {
10
26
  try {
27
+ disableFlowControl();
11
28
  await configManager.initialize();
12
29
  await initializeOptionalTools();
13
30
  const llmClient = createLLMClient();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orquesta-cli",
3
- "version": "0.2.112",
3
+ "version": "0.2.115",
4
4
  "description": "Orquesta CLI - AI-powered coding assistant with team collaboration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",