dexto 1.5.1 → 1.5.2

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 (27) hide show
  1. package/dist/cli/ink-cli/components/ResourceAutocomplete.d.ts.map +1 -1
  2. package/dist/cli/ink-cli/components/ResourceAutocomplete.js +8 -2
  3. package/dist/cli/ink-cli/components/TextBufferInput.d.ts.map +1 -1
  4. package/dist/cli/ink-cli/components/TextBufferInput.js +17 -3
  5. package/dist/cli/ink-cli/components/chat/MessageItem.d.ts +3 -1
  6. package/dist/cli/ink-cli/components/chat/MessageItem.d.ts.map +1 -1
  7. package/dist/cli/ink-cli/components/chat/MessageItem.js +33 -9
  8. package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.js +3 -3
  9. package/dist/cli/ink-cli/components/modes/StaticCLI.d.ts.map +1 -1
  10. package/dist/cli/ink-cli/components/modes/StaticCLI.js +10 -3
  11. package/dist/cli/ink-cli/components/renderers/GenericRenderer.js +1 -1
  12. package/dist/cli/ink-cli/components/renderers/SearchRenderer.js +1 -1
  13. package/dist/cli/ink-cli/components/shared/MarkdownText.d.ts.map +1 -1
  14. package/dist/cli/ink-cli/components/shared/MarkdownText.js +4 -4
  15. package/dist/cli/ink-cli/constants/processingPhrases.d.ts.map +1 -1
  16. package/dist/cli/ink-cli/constants/processingPhrases.js +58 -48
  17. package/dist/cli/ink-cli/constants/tips.d.ts.map +1 -1
  18. package/dist/cli/ink-cli/constants/tips.js +33 -32
  19. package/dist/cli/ink-cli/containers/InputContainer.d.ts.map +1 -1
  20. package/dist/cli/ink-cli/containers/InputContainer.js +18 -11
  21. package/dist/cli/ink-cli/containers/OverlayContainer.d.ts.map +1 -1
  22. package/dist/cli/ink-cli/containers/OverlayContainer.js +24 -1
  23. package/dist/cli/ink-cli/hooks/useCLIState.d.ts.map +1 -1
  24. package/dist/cli/ink-cli/hooks/useCLIState.js +14 -1
  25. package/dist/cli/ink-cli/utils/messageFormatting.d.ts.map +1 -1
  26. package/dist/cli/ink-cli/utils/messageFormatting.js +92 -5
  27. package/package.json +7 -7
@@ -1 +1 @@
1
- {"version":3,"file":"ResourceAutocomplete.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/ResourceAutocomplete.tsx"],"names":[],"mappings":"AAAA,OAAO,KAQN,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kCAAkC,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,WAAW,0BAA0B;IACvC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED,UAAU,yBAAyB;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACvD,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,EAAE,UAAU,CAAC;CACrB;AA6ED;;GAEG;AACH,QAAA,MAAM,yBAAyB,8GAgR9B,CAAC;AAEF;;;GAGG;AACH,QAAA,MAAM,oBAAoB,EAErB,OAAO,yBAAyB,CAAC;AAEtC,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"ResourceAutocomplete.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/ResourceAutocomplete.tsx"],"names":[],"mappings":"AAAA,OAAO,KAQN,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kCAAkC,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,WAAW,0BAA0B;IACvC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED,UAAU,yBAAyB;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACvD,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,EAAE,UAAU,CAAC;CACrB;AA6ED;;GAEG;AACH,QAAA,MAAM,yBAAyB,8GAoQ9B,CAAC;AAEF;;;GAGG;AACH,QAAA,MAAM,oBAAoB,EAErB,OAAO,yBAAyB,CAAC;AAEtC,eAAe,oBAAoB,CAAC"}
@@ -74,7 +74,7 @@ const ResourceAutocompleteInner = forwardRef(function ResourceAutocomplete({ isV
74
74
  // Combined state to guarantee single render on navigation
75
75
  const [selection, setSelection] = useState({ index: 0, offset: 0 });
76
76
  const selectedIndexRef = useRef(0);
77
- const MAX_VISIBLE_ITEMS = 8;
77
+ const MAX_VISIBLE_ITEMS = 5;
78
78
  // Update selection AND scroll offset in a single state update
79
79
  // This guarantees exactly one render per navigation action
80
80
  const updateSelection = useCallback((indexUpdater) => {
@@ -122,6 +122,10 @@ const ResourceAutocompleteInner = forwardRef(function ResourceAutocomplete({ isV
122
122
  cancelled = true;
123
123
  };
124
124
  }, [isVisible, agent]);
125
+ // NOTE: Auto-close logic is handled synchronously in TextBufferInput.tsx
126
+ // (on backspace deleting @ and on space after @). We don't use useEffect here
127
+ // because React batches state updates, causing race conditions where isVisible
128
+ // and searchQuery update at different times.
125
129
  // Extract query from @mention (everything after @)
126
130
  const mentionQuery = useMemo(() => {
127
131
  // Find the last @ that's at start or after space
@@ -250,7 +254,9 @@ const ResourceAutocompleteInner = forwardRef(function ResourceAutocomplete({ isV
250
254
  const uriParts = resource.uri.split('/');
251
255
  const displayName = resource.name || uriParts[uriParts.length - 1] || resource.uri;
252
256
  const isImage = (resource.mimeType || '').startsWith('image/');
253
- return (_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [isImage && (_jsx(Text, { color: isSelected ? 'cyan' : 'gray', children: "\uD83D\uDDBC\uFE0F " })), _jsx(Text, { color: isSelected ? 'cyan' : 'gray', bold: isSelected, children: displayName }), resource.serverName && (_jsx(Box, { marginLeft: 1, children: _jsxs(Text, { color: isSelected ? 'white' : 'gray', children: ["[", resource.serverName, "]"] }) }))] }), _jsx(Box, { marginLeft: isImage ? 3 : 0, children: _jsx(Text, { color: isSelected ? 'white' : 'gray', children: resource.uri }) }), resource.description && (_jsx(Box, { marginLeft: isImage ? 3 : 0, children: _jsx(Text, { color: isSelected ? 'white' : 'gray', children: resource.description }) }))] }) }, resource.uri));
257
+ // Truncate URI for display (show last 40 chars with ellipsis)
258
+ const truncatedUri = resource.uri.length > 50 ? '…' + resource.uri.slice(-49) : resource.uri;
259
+ return (_jsxs(Box, { children: [isImage && _jsx(Text, { color: isSelected ? 'cyan' : 'gray', children: "\uD83D\uDDBC\uFE0F " }), _jsx(Text, { color: isSelected ? 'cyan' : 'white', bold: isSelected, children: displayName }), resource.serverName && (_jsxs(Text, { color: "gray", children: [" [", resource.serverName, "]"] })), _jsxs(Text, { color: "gray", children: [" ", truncatedUri] })] }, resource.uri));
254
260
  })] }));
255
261
  });
256
262
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"TextBufferInput.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/TextBufferInput.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGnE,+DAA+D;AAC/D,MAAM,MAAM,cAAc,GAAG,oBAAoB,GAAG,uBAAuB,GAAG,OAAO,CAAC;AActF,UAAU,oBAAoB;IAC1B,oCAAoC;IACpC,MAAM,EAAE,UAAU,CAAC;IACnB,+CAA+C;IAC/C,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,mEAAmE;IACnE,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACrE,0DAA0D;IAC1D,gBAAgB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACnE,wCAAwC;IACxC,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,kDAAkD;IAClD,QAAQ,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACpE,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,iDAAiD;IACjD,YAAY,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC3D,iEAAiE;IACjE,MAAM,CAAC,EAAE,YAAY,EAAE,GAAG,SAAS,CAAC;IACpC,4DAA4D;IAC5D,aAAa,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACxD,wDAAwD;IACxD,YAAY,CAAC,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC;IACzC,oEAAoE;IACpE,YAAY,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC1D,8DAA8D;IAC9D,kBAAkB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC5F,iEAAiE;IACjE,kBAAkB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC7D,4DAA4D;IAC5D,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAuCD,wBAAgB,eAAe,CAAC,EAC5B,MAAM,EACN,QAAQ,EACR,WAAW,EACX,UAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAqB,EACrB,QAAQ,EACR,gBAAgB,EAChB,UAAc,EACd,YAAY,EACZ,MAAW,EACX,aAAa,EACb,YAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,GACjB,EAAE,oBAAoB,2CA2gBtB"}
1
+ {"version":3,"file":"TextBufferInput.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/TextBufferInput.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGnE,+DAA+D;AAC/D,MAAM,MAAM,cAAc,GAAG,oBAAoB,GAAG,uBAAuB,GAAG,OAAO,CAAC;AActF,UAAU,oBAAoB;IAC1B,oCAAoC;IACpC,MAAM,EAAE,UAAU,CAAC;IACnB,+CAA+C;IAC/C,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,mEAAmE;IACnE,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACrE,0DAA0D;IAC1D,gBAAgB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACnE,wCAAwC;IACxC,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,kDAAkD;IAClD,QAAQ,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACpE,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,iDAAiD;IACjD,YAAY,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC3D,iEAAiE;IACjE,MAAM,CAAC,EAAE,YAAY,EAAE,GAAG,SAAS,CAAC;IACpC,4DAA4D;IAC5D,aAAa,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACxD,wDAAwD;IACxD,YAAY,CAAC,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC;IACzC,oEAAoE;IACpE,YAAY,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC1D,8DAA8D;IAC9D,kBAAkB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC5F,iEAAiE;IACjE,kBAAkB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC7D,4DAA4D;IAC5D,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAuCD,wBAAgB,eAAe,CAAC,EAC5B,MAAM,EACN,QAAQ,EACR,WAAW,EACX,UAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAqB,EACrB,QAAQ,EACR,gBAAgB,EAChB,UAAc,EACd,YAAY,EACZ,MAAW,EACX,aAAa,EACb,YAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,GACjB,EAAE,oBAAoB,2CA0hBtB"}
@@ -250,14 +250,23 @@ export function TextBufferInput({ buffer, onSubmit, placeholder, isDisabled = fa
250
250
  buffer.backspace();
251
251
  checkRemovedImages();
252
252
  checkRemovedPasteBlocks();
253
+ // Check if we should close overlay after backspace
254
+ // NOTE: buffer.text is memoized and won't update until next render,
255
+ // so we calculate the expected new text ourselves
253
256
  if (onTriggerOverlay && cursorPos > 0) {
254
257
  const deletedChar = prevText[cursorPos - 1];
255
- const newText = buffer.text;
258
+ // Calculate what the text will be after backspace
259
+ const expectedNewText = prevText.slice(0, cursorPos - 1) + prevText.slice(cursorPos);
256
260
  if (deletedChar === '/' && cursorPos === 1) {
257
261
  onTriggerOverlay('close');
258
262
  }
259
- else if (deletedChar === '@' && !newText.includes('@')) {
260
- onTriggerOverlay('close');
263
+ else if (deletedChar === '@') {
264
+ // Close if no valid @ mention remains
265
+ // A valid @ is at start of text or after whitespace
266
+ const hasValidAt = /(^|[\s])@/.test(expectedNewText);
267
+ if (!hasValidAt) {
268
+ onTriggerOverlay('close');
269
+ }
261
270
  }
262
271
  }
263
272
  return;
@@ -388,6 +397,11 @@ export function TextBufferInput({ buffer, onSubmit, placeholder, isDisabled = fa
388
397
  else if (key.sequence === '@') {
389
398
  onTriggerOverlay('resource-autocomplete');
390
399
  }
400
+ else if (/\s/.test(key.sequence)) {
401
+ // Close resource autocomplete when user types whitespace
402
+ // Whitespace means user is done with the mention (either selected or abandoned)
403
+ onTriggerOverlay('close');
404
+ }
391
405
  }
392
406
  }
393
407
  }, [
@@ -6,6 +6,8 @@
6
6
  import type { Message } from '../../state/types.js';
7
7
  interface MessageItemProps {
8
8
  message: Message;
9
+ /** Terminal width for proper text wrapping calculations */
10
+ terminalWidth?: number;
9
11
  }
10
12
  /**
11
13
  * Pure presentational component for a single message
@@ -14,6 +16,6 @@ interface MessageItemProps {
14
16
  * Memoization with custom comparator prevents re-renders when message array changes
15
17
  * but individual message content hasn't changed.
16
18
  */
17
- export declare const MessageItem: import("react").MemoExoticComponent<({ message }: MessageItemProps) => import("react/jsx-runtime").JSX.Element>;
19
+ export declare const MessageItem: import("react").MemoExoticComponent<({ message, terminalWidth }: MessageItemProps) => import("react/jsx-runtime").JSX.Element>;
18
20
  export {};
19
21
  //# sourceMappingURL=MessageItem.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MessageItem.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/chat/MessageItem.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EACR,OAAO,EAUV,MAAM,sBAAsB,CAAC;AAuC9B,UAAU,gBAAgB;IACtB,OAAO,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,oDACN,gBAAgB,6CA2JjC,CAAC"}
1
+ {"version":3,"file":"MessageItem.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/chat/MessageItem.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EACR,OAAO,EAUV,MAAM,sBAAsB,CAAC;AAuC9B,UAAU,gBAAgB;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,mEACc,gBAAgB,6CA+LrD,CAAC"}
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  /**
3
3
  * MessageItem Component
4
4
  * Displays a single message with visual hierarchy
@@ -6,6 +6,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  */
7
7
  import { memo } from 'react';
8
8
  import { Box, Text } from 'ink';
9
+ import wrapAnsi from 'wrap-ansi';
9
10
  import { ConfigBox, StatsBox, HelpBox, SessionListBox, SessionHistoryBox, LogConfigBox, ShortcutsBox, SyspromptBox, } from './styled-boxes/index.js';
10
11
  import { ToolResultRenderer } from '../renderers/index.js';
11
12
  import { MarkdownText } from '../shared/MarkdownText.js';
@@ -36,7 +37,7 @@ function formatDuration(ms) {
36
37
  * Memoization with custom comparator prevents re-renders when message array changes
37
38
  * but individual message content hasn't changed.
38
39
  */
39
- export const MessageItem = memo(({ message }) => {
40
+ export const MessageItem = memo(({ message, terminalWidth = 80 }) => {
40
41
  // Check for styled message first
41
42
  if (message.styledType && message.styledData) {
42
43
  switch (message.styledType) {
@@ -59,7 +60,7 @@ export const MessageItem = memo(({ message }) => {
59
60
  const tokensStr = data.totalTokens >= 1000
60
61
  ? `, Used ${(data.totalTokens / 1000).toFixed(1)}K tokens`
61
62
  : '';
62
- return (_jsx(Box, { marginTop: 1, marginBottom: 1, width: "100%", children: _jsxs(Text, { color: "gray", children: ["\u2500 Worked for ", durationStr, tokensStr, " \u2500"] }) }));
63
+ return (_jsx(Box, { marginTop: 1, marginBottom: 1, width: terminalWidth, children: _jsxs(Text, { color: "gray", children: ["\u2500 Worked for ", durationStr, tokensStr, " \u2500"] }) }));
63
64
  }
64
65
  case 'shortcuts':
65
66
  return _jsx(ShortcutsBox, { data: message.styledData });
@@ -68,8 +69,18 @@ export const MessageItem = memo(({ message }) => {
68
69
  }
69
70
  }
70
71
  // User message: '>' prefix with gray background
72
+ // Properly wrap text accounting for prefix "> " (2 chars) and paddingX={1} (2 chars total)
71
73
  if (message.role === 'user') {
72
- return (_jsx(Box, { flexDirection: "column", marginTop: 2, marginBottom: 1, width: "100%", children: _jsxs(Box, { flexDirection: "row", paddingX: 1, backgroundColor: "gray", children: [_jsx(Text, { color: "green", children: '> ' }), _jsx(Text, { color: "white", wrap: "wrap", children: message.content })] }) }));
74
+ const prefix = '> ';
75
+ const paddingChars = 2; // paddingX={1} = 1 char on each side
76
+ const availableWidth = Math.max(20, terminalWidth - prefix.length - paddingChars);
77
+ const wrappedContent = wrapAnsi(message.content, availableWidth, {
78
+ hard: true,
79
+ wordWrap: true,
80
+ trim: false,
81
+ });
82
+ const lines = wrappedContent.split('\n');
83
+ return (_jsx(Box, { flexDirection: "column", marginTop: 2, marginBottom: 1, width: terminalWidth, children: _jsx(Box, { flexDirection: "column", paddingX: 1, backgroundColor: "gray", children: lines.map((line, i) => (_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: "green", children: i === 0 ? prefix : ' ' }), _jsx(Text, { color: "white", children: line })] }, i))) }) }));
73
84
  }
74
85
  // Assistant message: Gray circle indicator (unless continuation)
75
86
  // IMPORTANT: width="100%" is required to prevent Ink layout failures on large content.
@@ -78,12 +89,12 @@ export const MessageItem = memo(({ message }) => {
78
89
  if (message.role === 'assistant') {
79
90
  // Continuation messages: no indicator, just content
80
91
  if (message.isContinuation) {
81
- return (_jsx(Box, { flexDirection: "column", width: "100%", children: _jsx(MarkdownText, { children: message.content || '' }) }));
92
+ return (_jsx(Box, { flexDirection: "column", width: terminalWidth, children: _jsx(MarkdownText, { children: message.content || '' }) }));
82
93
  }
83
94
  // Regular assistant message: bullet prefix inline with first line
84
95
  // Text wraps at terminal width - wrapped lines may start at column 0
85
96
  // This is simpler and avoids mid-word splitting issues with Ink's wrap
86
- return (_jsx(Box, { flexDirection: "column", marginTop: 1, width: "100%", children: _jsx(MarkdownText, { bulletPrefix: "\u23FA ", children: message.content || '' }) }));
97
+ return (_jsx(Box, { flexDirection: "column", marginTop: 1, width: terminalWidth, children: _jsx(MarkdownText, { bulletPrefix: "\u23FA ", children: message.content || '' }) }));
87
98
  }
88
99
  // Tool message: Animated icon based on status
89
100
  // - Running: green spinner + "Running..."
@@ -98,10 +109,22 @@ export const MessageItem = memo(({ message }) => {
98
109
  const parenIndex = message.content.indexOf('(');
99
110
  const toolName = parenIndex > 0 ? message.content.slice(0, parenIndex) : message.content;
100
111
  const toolArgs = parenIndex > 0 ? message.content.slice(parenIndex) : '';
101
- return (_jsxs(Box, { flexDirection: "column", marginTop: 1, width: "100%", children: [_jsxs(Box, { flexDirection: "row", children: [_jsx(ToolIcon, { status: message.toolStatus || 'finished', isError: message.isError ?? false }), _jsx(Box, { flexGrow: 1, flexShrink: 1, children: _jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { bold: true, children: toolName }), _jsx(Text, { children: toolArgs }), isRunning && _jsx(Text, { color: "green", children: " Running..." }), isPending && _jsx(Text, { color: "yellowBright", children: " Waiting..." })] }) })] }), hasStructuredDisplay ? (_jsx(ToolResultRenderer, { display: message.toolDisplayData, content: message.toolContent })) : (message.toolResult && (_jsx(Box, { flexDirection: "column", children: _jsxs(Text, { color: "gray", children: [" \u23BF ", message.toolResult] }) })))] }));
112
+ // Build the full tool header text for wrapping
113
+ const statusSuffix = isRunning ? ' Running...' : isPending ? ' Waiting...' : '';
114
+ const fullToolText = `${toolName}${toolArgs}${statusSuffix}`;
115
+ // ToolIcon takes 2 chars ("● "), so available width is terminalWidth - 2
116
+ const iconWidth = 2;
117
+ const availableWidth = Math.max(20, terminalWidth - iconWidth);
118
+ const wrappedToolText = wrapAnsi(fullToolText, availableWidth, {
119
+ hard: true,
120
+ wordWrap: true,
121
+ trim: false,
122
+ });
123
+ const toolLines = wrappedToolText.split('\n');
124
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, width: terminalWidth, children: [toolLines.map((line, i) => (_jsxs(Box, { flexDirection: "row", children: [i === 0 ? (_jsx(ToolIcon, { status: message.toolStatus || 'finished', isError: message.isError ?? false })) : (_jsx(Text, { children: ' ' })), _jsx(Text, { children: i === 0 ? (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, children: line.slice(0, toolName.length) }), _jsx(Text, { children: line.slice(toolName.length) })] })) : (line) })] }, i))), hasStructuredDisplay ? (_jsx(ToolResultRenderer, { display: message.toolDisplayData, content: message.toolContent })) : (message.toolResult && (_jsx(Box, { flexDirection: "column", children: _jsxs(Text, { color: "gray", children: [" \u23BF ", message.toolResult] }) })))] }));
102
125
  }
103
126
  // System message: Compact gray text
104
- return (_jsx(Box, { flexDirection: "column", marginBottom: 1, width: "100%", children: _jsx(Text, { color: "gray", children: message.content }) }));
127
+ return (_jsx(Box, { flexDirection: "column", marginBottom: 1, width: terminalWidth, children: _jsx(Text, { color: "gray", children: message.content }) }));
105
128
  },
106
129
  // Custom comparator: only re-render if message content actually changed
107
130
  (prev, next) => {
@@ -116,6 +139,7 @@ export const MessageItem = memo(({ message }) => {
116
139
  prev.message.isContinuation === next.message.isContinuation &&
117
140
  prev.message.isError === next.message.isError &&
118
141
  prev.message.toolDisplayData === next.message.toolDisplayData &&
119
- prev.message.toolContent === next.message.toolContent);
142
+ prev.message.toolContent === next.message.toolContent &&
143
+ prev.terminalWidth === next.terminalWidth);
120
144
  });
121
145
  MessageItem.displayName = 'MessageItem';
@@ -94,7 +94,7 @@ export function AlternateBufferCLI({ agent, initialSessionId, startupInfo, onSel
94
94
  return () => clearTimeout(timer);
95
95
  }, [selectionHintVisible]);
96
96
  // Get terminal dimensions - updates on resize
97
- const { rows: terminalHeight } = useTerminalSize();
97
+ const { rows: terminalHeight, columns: terminalWidth } = useTerminalSize();
98
98
  // Build list data: header as first item, then finalized + pending + dequeued buffer
99
99
  // In alternate buffer mode, everything is re-rendered anyway, so we combine all
100
100
  // Order: finalized messages → pending/streaming → dequeued user messages (guarantees order)
@@ -119,8 +119,8 @@ export function AlternateBufferCLI({ agent, initialSessionId, startupInfo, onSel
119
119
  if (item.type === 'header') {
120
120
  return (_jsx(Header, { modelName: session.modelName, sessionId: session.id || undefined, hasActiveSession: session.hasActiveSession, startupInfo: startupInfo }));
121
121
  }
122
- return _jsx(MessageItem, { message: item.message });
123
- }, [session.modelName, session.id, session.hasActiveSession, startupInfo]);
122
+ return _jsx(MessageItem, { message: item.message, terminalWidth: terminalWidth });
123
+ }, [session.modelName, session.id, session.hasActiveSession, startupInfo, terminalWidth]);
124
124
  // Smart height estimation based on item type and content
125
125
  const estimateItemHeight = useCallback((index) => {
126
126
  const item = listData[index];
@@ -1 +1 @@
1
- {"version":3,"file":"StaticCLI.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/modes/StaticCLI.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAM9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAkBxD,UAAU,cAAc;IACpB,KAAK,EAAE,UAAU,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,WAAW,CAAC;IACzB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,SAAS,CAAC,EACtB,KAAK,EACL,gBAAgB,EAChB,WAAW,EACX,YAAmB,GACtB,EAAE,cAAc,2CA2MhB"}
1
+ {"version":3,"file":"StaticCLI.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/modes/StaticCLI.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAM9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAkBxD,UAAU,cAAc;IACpB,KAAK,EAAE,UAAU,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,WAAW,CAAC;IACzB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,SAAS,CAAC,EACtB,KAAK,EACL,gBAAgB,EAChB,WAAW,EACX,YAAmB,GACtB,EAAE,cAAc,2CAoNhB"}
@@ -87,9 +87,16 @@ export function StaticCLI({ agent, initialSessionId, startupInfo, useStreaming =
87
87
  const staticItems = useMemo(() => {
88
88
  const items = [
89
89
  _jsx(Header, { modelName: session.modelName, sessionId: session.id || undefined, hasActiveSession: session.hasActiveSession, startupInfo: startupInfo }, "header"),
90
- ...visibleMessages.map((msg) => _jsx(MessageItem, { message: msg }, msg.id)),
90
+ ...visibleMessages.map((msg) => (_jsx(MessageItem, { message: msg, terminalWidth: terminalWidth }, msg.id))),
91
91
  ];
92
92
  return items;
93
- }, [visibleMessages, session.modelName, session.id, session.hasActiveSession, startupInfo]);
94
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Static, { items: staticItems, children: (item) => item }, staticRemountKey), pendingMessages.map((message) => (_jsx(MessageItem, { message: message }, message.id))), dequeuedBuffer.map((message) => (_jsx(MessageItem, { message: message }, message.id))), _jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [_jsx(StatusBar, { agent: agent, isProcessing: ui.isProcessing, isThinking: ui.isThinking, approvalQueueCount: approvalQueue.length, copyModeEnabled: ui.copyModeEnabled, isAwaitingApproval: approval !== null }), _jsx(QueuedMessagesDisplay, { messages: queuedMessages }), _jsx(InputContainer, { ref: inputContainerRef, buffer: buffer, input: input, ui: ui, session: session, approval: approval, queuedMessages: queuedMessages, setInput: setInput, setUi: setUi, setSession: setSession, setMessages: setMessages, setPendingMessages: setPendingMessages, setDequeuedBuffer: setDequeuedBuffer, setQueuedMessages: setQueuedMessages, setApproval: setApproval, setApprovalQueue: setApprovalQueue, agent: agent, inputService: inputService, useStreaming: useStreaming }), _jsx(OverlayContainer, { ref: overlayContainerRef, ui: ui, input: input, session: session, approval: approval, setInput: setInput, setUi: setUi, setSession: setSession, setMessages: setMessages, setApproval: setApproval, setApprovalQueue: setApprovalQueue, agent: agent, inputService: inputService, buffer: buffer, refreshStatic: refreshStatic, onSubmitPromptCommand: handleSubmitPromptCommand }), ui.exitWarningShown && (_jsxs(Box, { paddingX: 1, children: [_jsx(Text, { color: "yellowBright", bold: true, children: "\u26A0 Press Ctrl+C again to exit" }), _jsx(Text, { color: "gray", children: " (or press any key to cancel)" })] })), _jsx(Footer, { modelName: session.modelName, cwd: process.cwd(), autoApproveEdits: ui.autoApproveEdits }), ui.historySearch.isActive && (_jsx(HistorySearchBar, { query: ui.historySearch.query, hasMatch: historySearchHasMatch }))] })] }));
93
+ }, [
94
+ visibleMessages,
95
+ session.modelName,
96
+ session.id,
97
+ session.hasActiveSession,
98
+ startupInfo,
99
+ terminalWidth,
100
+ ]);
101
+ return (_jsxs(Box, { flexDirection: "column", width: terminalWidth, children: [_jsx(Static, { items: staticItems, children: (item) => item }, staticRemountKey), pendingMessages.map((message) => (_jsx(MessageItem, { message: message, terminalWidth: terminalWidth }, message.id))), dequeuedBuffer.map((message) => (_jsx(MessageItem, { message: message, terminalWidth: terminalWidth }, message.id))), _jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [_jsx(StatusBar, { agent: agent, isProcessing: ui.isProcessing, isThinking: ui.isThinking, approvalQueueCount: approvalQueue.length, copyModeEnabled: ui.copyModeEnabled, isAwaitingApproval: approval !== null }), _jsx(QueuedMessagesDisplay, { messages: queuedMessages }), _jsx(InputContainer, { ref: inputContainerRef, buffer: buffer, input: input, ui: ui, session: session, approval: approval, queuedMessages: queuedMessages, setInput: setInput, setUi: setUi, setSession: setSession, setMessages: setMessages, setPendingMessages: setPendingMessages, setDequeuedBuffer: setDequeuedBuffer, setQueuedMessages: setQueuedMessages, setApproval: setApproval, setApprovalQueue: setApprovalQueue, agent: agent, inputService: inputService, useStreaming: useStreaming }), _jsx(OverlayContainer, { ref: overlayContainerRef, ui: ui, input: input, session: session, approval: approval, setInput: setInput, setUi: setUi, setSession: setSession, setMessages: setMessages, setApproval: setApproval, setApprovalQueue: setApprovalQueue, agent: agent, inputService: inputService, buffer: buffer, refreshStatic: refreshStatic, onSubmitPromptCommand: handleSubmitPromptCommand }), ui.exitWarningShown && (_jsxs(Box, { paddingX: 1, children: [_jsx(Text, { color: "yellowBright", bold: true, children: "\u26A0 Press Ctrl+C again to exit" }), _jsx(Text, { color: "gray", children: " (or press any key to cancel)" })] })), _jsx(Footer, { modelName: session.modelName, cwd: process.cwd(), autoApproveEdits: ui.autoApproveEdits }), ui.historySearch.isActive && (_jsx(HistorySearchBar, { query: ui.historySearch.query, hasMatch: historySearchHasMatch }))] })] }));
95
102
  }
@@ -22,5 +22,5 @@ export function GenericRenderer({ content, maxLines = 15 }) {
22
22
  const line = lines[0];
23
23
  return (_jsxs(Text, { color: "gray", children: [' ⎿ ', line.slice(0, 80), line.length > 80 ? '...' : ''] }));
24
24
  }
25
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "gray", children: [' ⎿ ', displayLines.length, " lines"] }), displayLines.map((line, i) => (_jsxs(Text, { color: "gray", wrap: "truncate", children: [' ', line] }, i))), truncated && (_jsxs(Text, { color: "gray", children: [' ', "... ", lines.length - maxLines, " more lines"] }))] }));
25
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "gray", children: [' ⎿ ', displayLines.length, " lines"] }), displayLines.map((line, i) => (_jsxs(Text, { color: "gray", wrap: "truncate", children: ['', line] }, i))), truncated && (_jsxs(Text, { color: "gray", children: ['', "... ", lines.length - maxLines, " more lines"] }))] }));
26
26
  }
@@ -8,5 +8,5 @@ export function SearchRenderer({ data, maxMatches = 5 }) {
8
8
  const { pattern, matches, totalMatches, truncated: dataTruncated } = data;
9
9
  const displayMatches = matches.slice(0, maxMatches);
10
10
  const truncated = dataTruncated || matches.length > maxMatches;
11
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "gray", children: [' ⎿ ', totalMatches, " match", totalMatches !== 1 ? 'es' : '', " for \"", pattern, "\"", truncated && ' (truncated)'] }), displayMatches.map((match, i) => (_jsxs(Text, { color: "gray", children: [' ', match.file, match.line > 0 && `:${match.line}`] }, i))), matches.length > maxMatches && (_jsxs(Text, { color: "gray", children: [' ', "... ", matches.length - maxMatches, " more"] }))] }));
11
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "gray", children: [' ⎿ ', totalMatches, " match", totalMatches !== 1 ? 'es' : '', " for \"", pattern, "\"", truncated && ' (truncated)'] }), displayMatches.map((match, i) => (_jsxs(Text, { color: "gray", wrap: "truncate", children: ['', match.file, match.line > 0 && `:${match.line}`] }, i))), matches.length > maxMatches && (_jsxs(Text, { color: "gray", children: ['', "... ", matches.length - maxMatches, " more"] }))] }));
12
12
  }
@@ -1 +1 @@
1
- {"version":3,"file":"MarkdownText.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/shared/MarkdownText.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAwB,MAAM,OAAO,CAAC;AAU7C,UAAU,aAAa;IACnB,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,eAAe,GAAG,MAAM,GAAG,KAAK,CAAC;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,iBAAS,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE,CAuE1D;AA0ID,UAAU,iBAAiB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,IAAI,CAAC,EAAE,OAAO,CAAC;CAClB;AAkED,QAAA,MAAM,YAAY,+CAA6B,CAAC;AAMhD,UAAU,iBAAiB;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AA4RD,eAAO,MAAM,YAAY,+CAA6B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,CAAC"}
1
+ {"version":3,"file":"MarkdownText.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/shared/MarkdownText.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAwB,MAAM,OAAO,CAAC;AAU7C,UAAU,aAAa;IACnB,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,eAAe,GAAG,MAAM,GAAG,KAAK,CAAC;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,iBAAS,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE,CAuE1D;AAyID,UAAU,iBAAiB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,IAAI,CAAC,EAAE,OAAO,CAAC;CAClB;AAkED,QAAA,MAAM,YAAY,+CAA6B,CAAC;AAMhD,UAAU,iBAAiB;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AA4RD,eAAO,MAAM,YAAY,+CAA6B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,CAAC"}
@@ -168,12 +168,12 @@ const WrappedParagraphInternal = ({ text, defaultColor, bulletPrefix, isFirstPar
168
168
  return wrapped.split('\n');
169
169
  }, [text, defaultColor, bulletPrefix, isFirstParagraph, terminalWidth]);
170
170
  // Calculate indent for continuation lines (spaces to align with first line content)
171
- const continuationIndent = bulletPrefix && isFirstParagraph ? ' '.repeat(stringWidth(bulletPrefix)) : '';
171
+ // All lines get indentation when bulletPrefix is provided, for consistent left margin
172
+ const indentSpaces = bulletPrefix ? ' '.repeat(stringWidth(bulletPrefix)) : '';
172
173
  return (_jsx(_Fragment, { children: wrappedLines.map((line, i) => {
173
174
  const isFirstLine = i === 0;
174
- const prefix = isFirstLine && bulletPrefix && isFirstParagraph
175
- ? bulletPrefix
176
- : continuationIndent;
175
+ // First line of first paragraph gets bullet, all other lines get space indent
176
+ const prefix = isFirstLine && isFirstParagraph && bulletPrefix ? bulletPrefix : indentSpaces;
177
177
  return (_jsx(Box, { children: _jsxs(Text, { children: [prefix, line] }) }, i));
178
178
  }) }));
179
179
  };
@@ -1 +1 @@
1
- {"version":3,"file":"processingPhrases.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/constants/processingPhrases.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,iBAAiB,EAAE,MAAM,EAsDrC,CAAC;AAEF;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAGxC"}
1
+ {"version":3,"file":"processingPhrases.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/constants/processingPhrases.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,iBAAiB,EAAE,MAAM,EAiErC,CAAC;AAEF;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAGxC"}
@@ -4,61 +4,71 @@
4
4
  */
5
5
  export const processingPhrases = [
6
6
  // pop culture
7
- 'I want to be the very best...',
8
- 'I will be the next hokage...',
9
- 'Shinzou Sasageyo...',
10
- 'My soldiers! Rage!...',
11
- 'May the force be with you...',
12
- 'Why so serious?...',
13
- "That's what she said ...",
14
- 'Winter is coming...',
15
- "It's over 9000!...",
16
- "I'm Batman...",
17
- "You can't handle the truth...",
18
- 'We were on a break!...',
19
- 'Bazinga!...',
20
- "How you doin'?...",
21
- "There's always money in the banana stand...",
22
- 'I am the one who knocks...',
23
- 'Yabba dabba doo!...',
24
- 'The tribe has spoken...',
25
- 'This is the Way...',
26
- 'Plata o plomo...',
27
- 'Yeah, science!...',
28
- "They're minerals, Marie!...",
29
- 'The North remembers...',
30
- 'Life is like a box of chocolates...',
7
+ 'I want to be the very best',
8
+ 'I will be the next hokage',
9
+ 'Shinzou Sasageyo',
10
+ 'My soldiers! Rage!…',
11
+ 'May the force be with you',
12
+ 'Why so serious?…',
13
+ "That's what she said ",
14
+ 'Winter is coming',
15
+ "It's over 9000!…",
16
+ "I'm Batman",
17
+ "You can't handle the truth",
18
+ 'We were on a break!…',
19
+ 'Bazinga!…',
20
+ "How you doin'?…",
21
+ "There's always money in the banana stand",
22
+ 'I am the one who knocks',
23
+ 'Yabba dabba doo!…',
24
+ 'The tribe has spoken',
25
+ 'This is the Way',
26
+ 'Plata o plomo',
27
+ 'Yeah, science!…',
28
+ "They're minerals, Marie!…",
29
+ 'The North remembers',
30
+ 'Life is like a box of chocolates',
31
31
  'Avengers, assemble!',
32
- 'I can do this all day...',
33
- 'Elementary, my dear Watson...',
34
- 'Identity theft is not a joke, Jim! ...',
35
- "I'm not superstitious, but I am a little stitious...",
32
+ 'I can do this all day',
33
+ 'Elementary, my dear Watson',
34
+ 'Identity theft is not a joke, Jim! ',
35
+ "I'm not superstitious, but I am a little stitious",
36
36
  // Playful
37
- 'Let me cook...',
38
- 'Manifesting greatness..',
39
- 'Rizzing the huzz...',
40
- 'Memeing...',
41
- 'Outperforming other AI agents...',
42
- 'Rolling with the squad...',
43
- 'Incanting secret scripts...',
44
- 'Making no mistakes...',
45
- 'Making you rich...',
46
- 'Farming easy points...',
47
- 'Using 200+ IQ...',
48
- 'Turning into Jarvis...',
49
- 'Dextomaxxing...',
50
- 'Zapping...',
51
- 'Braining...',
52
- 'Using all 3 brain cells...',
53
- //'Installing malware (just kidding)...',
37
+ 'Let me cook',
38
+ 'Manifesting greatness',
39
+ 'Rizzing the huzz',
40
+ 'Memeing',
41
+ 'Outperforming other AI agents',
42
+ 'Rolling with the squad',
43
+ 'Incanting secret scripts',
44
+ 'Making no mistakes',
45
+ 'Making you rich',
46
+ 'Farming easy points',
47
+ 'Using 200+ IQ',
48
+ 'Turning into Jarvis',
49
+ 'Dextomaxxing',
50
+ 'Zapping',
51
+ 'Braining',
52
+ 'Using all 3 brain cells',
53
+ //'Installing malware (just kidding)',
54
+ // Vines
55
+ 'Look at all those chickens…',
56
+ 'What are those!!?…',
57
+ 'He needs some milk…',
58
+ 'Something came in the mail today…',
59
+ 'Road work ahead? Sure hope it does…',
60
+ 'Merry Chrysler…',
61
+ "I'm in me mum's car. Broom broom…",
62
+ 'I could have dropped my croissant…',
63
+ 'That was legitness…',
54
64
  // Nerdy
55
- 'Attention is all I need...',
56
- 'Transformer powers activate...',
65
+ 'Attention is all I need',
66
+ 'Transformer powers activate',
57
67
  ];
58
68
  /**
59
69
  * Get a random phrase from the list
60
70
  */
61
71
  export function getRandomPhrase() {
62
72
  const index = Math.floor(Math.random() * processingPhrases.length);
63
- return processingPhrases[index] ?? 'Processing...';
73
+ return processingPhrases[index] ?? 'Processing';
64
74
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tips.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/constants/tips.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,IAAI,EAAE,MAAM,EAuCxB,CAAC;AAEF;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAGrC"}
1
+ {"version":3,"file":"tips.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/constants/tips.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,IAAI,EAAE,MAAM,EAwCxB,CAAC;AAEF;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAGrC"}
@@ -9,45 +9,46 @@
9
9
  */
10
10
  export const tips = [
11
11
  // Command tips
12
- 'Type /help to see all available commands...',
13
- 'Use /model to switch between different AI models...',
14
- 'Use /resume to load a previous conversation...',
15
- 'Use /search to find messages across all sessions...',
16
- 'Use /mcp to manage MCP servers...',
17
- 'Use /tools to see all available tools...',
18
- 'Use /prompts to browse, add and delete custom prompts...',
19
- 'Use /log to change logging verbosity...',
20
- 'Use /clear to clear the screen...',
21
- 'Use /exit or /quit to close dexto...',
22
- 'Use /docs to access documentation...',
23
- 'Use /copy to copy the previous response...',
24
- 'Use /shortcuts to see all available shortcuts...',
25
- 'Use /sysprompt to see the current system prompt...',
12
+ 'Type /help to see all available commands',
13
+ 'Use /model to switch between different AI models',
14
+ 'Use /resume to load a previous conversation',
15
+ 'Use /search to find messages across all sessions',
16
+ 'Use /mcp to manage MCP servers',
17
+ 'Use /tools to see all available tools',
18
+ 'Use /prompts to browse, add and delete custom prompts',
19
+ 'Use /log to change logging verbosity',
20
+ 'Use /clear to clear the screen',
21
+ 'Use /exit or /quit to close dexto',
22
+ 'Use /docs to access documentation',
23
+ 'Use /copy to copy the previous response',
24
+ 'Use /shortcuts to see all available shortcuts',
25
+ 'Use /sysprompt to see the current system prompt',
26
26
  // Keyboard shortcut tips
27
- 'Press Escape to cancel the current request...',
28
- 'Press Ctrl+C twice to exit dexto...',
29
- 'Press Escape to close overlays and menus...',
30
- 'Use Up/Down arrows to navigate command history...',
31
- 'Press Enter to submit your message...',
32
- 'Press Ctrl+T to collapse/expand large pastes...',
33
- 'Press Ctrl+R to search previous prompts...',
27
+ 'Press Escape to cancel the current request',
28
+ 'Press Ctrl+C twice to exit dexto',
29
+ 'Press Escape to close overlays and menus',
30
+ 'Use Up/Down arrows to navigate command history',
31
+ 'Press Enter to submit your message',
32
+ 'Press Ctrl+T to collapse/expand large pastes',
33
+ 'Press Ctrl+R to search previous prompts',
34
34
  // Feature tips
35
- 'Paste copied images with Ctrl+V...',
36
- 'Large pastes are automatically collapsed - press Ctrl+T to toggle...',
37
- 'Use @ to reference files and resources...',
38
- 'Type / to see available slash commands...',
39
- 'MCP servers extend dexto with custom tools...',
40
- 'Sessions are automatically saved for later...',
41
- 'You can create custom commands with /prompts...',
42
- 'You can submit messages while Dexto is processing...',
43
- 'Use /stream to toggle streaming mode...',
35
+ 'Start with ! to run bash commands directly…',
36
+ 'Paste copied images with Ctrl+V…',
37
+ 'Large pastes are automatically collapsed - press Ctrl+T to toggle…',
38
+ 'Use @ to reference files and resources…',
39
+ 'Type / to see available slash commands…',
40
+ 'MCP servers extend dexto with custom tools…',
41
+ 'Sessions are automatically saved for later…',
42
+ 'You can create custom commands with /prompts…',
43
+ 'You can submit messages while Dexto is processing…',
44
+ 'Use /stream to toggle streaming mode…',
44
45
  // Platform tips
45
- 'On Mac, use Option+Up/Down to jump to start/end of input...',
46
+ 'On Mac, use Option+Up/Down to jump to start/end of input',
46
47
  ];
47
48
  /**
48
49
  * Get a random tip from the list
49
50
  */
50
51
  export function getRandomTip() {
51
52
  const index = Math.floor(Math.random() * tips.length);
52
- return tips[index] ?? 'Type /help to see available commands...';
53
+ return tips[index] ?? 'Type /help to see available commands';
53
54
  }
@@ -1 +1 @@
1
- {"version":3,"file":"InputContainer.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/containers/InputContainer.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAA0E,MAAM,OAAO,CAAC;AAC/F,OAAO,KAAK,EAAE,UAAU,EAAoC,aAAa,EAAE,MAAM,aAAa,CAAC;AAE/F,OAAO,EAAE,YAAY,EAAiB,MAAM,sBAAsB,CAAC;AACnE,OAAO,KAAK,EACR,OAAO,EACP,OAAO,EACP,UAAU,EACV,YAAY,EAGf,MAAM,mBAAmB,CAAC;AAG3B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AAMtE,qDAAqD;AACrD,MAAM,WAAW,oBAAoB;IACjC,yEAAyE;IACzE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C;AAED,UAAU,mBAAmB;IACzB,yCAAyC;IACzC,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,UAAU,CAAC;IAClB,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,YAAY,CAAC;IACtB,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,8CAA8C;IAC9C,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;IAC3D,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/D,2DAA2D;IAC3D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC7D,mEAAmE;IACnE,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACpE,iFAAiF;IACjF,iBAAiB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACnE,iCAAiC;IACjC,iBAAiB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;IACzE,8EAA8E;IAC9E,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1E,yEAAyE;IACzE,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;IAC1E,KAAK,EAAE,UAAU,CAAC;IAClB,YAAY,EAAE,YAAY,CAAC;IAC3B,mEAAmE;IACnE,gBAAgB,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC;IACtD,6EAA6E;IAC7E,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,kGA8qB1B,CAAC"}
1
+ {"version":3,"file":"InputContainer.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/containers/InputContainer.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAA0E,MAAM,OAAO,CAAC;AAC/F,OAAO,KAAK,EAAE,UAAU,EAAoC,aAAa,EAAE,MAAM,aAAa,CAAC;AAE/F,OAAO,EAAE,YAAY,EAAiB,MAAM,sBAAsB,CAAC;AACnE,OAAO,KAAK,EACR,OAAO,EACP,OAAO,EACP,UAAU,EACV,YAAY,EAGf,MAAM,mBAAmB,CAAC;AAG3B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AAMtE,qDAAqD;AACrD,MAAM,WAAW,oBAAoB;IACjC,yEAAyE;IACzE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C;AAED,UAAU,mBAAmB;IACzB,yCAAyC;IACzC,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,UAAU,CAAC;IAClB,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,YAAY,CAAC;IACtB,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,8CAA8C;IAC9C,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;IAC3D,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/D,2DAA2D;IAC3D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC7D,mEAAmE;IACnE,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACpE,iFAAiF;IACjF,iBAAiB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACnE,iCAAiC;IACjC,iBAAiB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;IACzE,8EAA8E;IAC9E,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1E,yEAAyE;IACzE,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;IAC1E,KAAK,EAAE,UAAU,CAAC;IAClB,YAAY,EAAE,YAAY,CAAC;IAC3B,mEAAmE;IACnE,gBAAgB,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC;IACtD,6EAA6E;IAC7E,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,kGAqrB1B,CAAC"}
@@ -126,18 +126,25 @@ export const InputContainer = forwardRef(function InputContainer({ buffer, input
126
126
  ui.isProcessing,
127
127
  ]);
128
128
  // Handle overlay triggers
129
+ // Allow triggers while processing (for queuing), but not during approval
130
+ // IMPORTANT: Use functional updates to check prev.activeOverlay, not the closure value.
131
+ // This avoids race conditions when open/close happen in quick succession (React batching).
129
132
  const handleTriggerOverlay = useCallback((trigger) => {
130
- if (ui.isProcessing || approval)
133
+ if (approval)
131
134
  return;
132
135
  if (trigger === 'close') {
133
- if (ui.activeOverlay === 'slash-autocomplete' ||
134
- ui.activeOverlay === 'resource-autocomplete') {
135
- setUi((prev) => ({
136
- ...prev,
137
- activeOverlay: 'none',
138
- mcpWizardServerType: null,
139
- }));
140
- }
136
+ // Use functional update to check the ACTUAL current state, not stale closure
137
+ setUi((prev) => {
138
+ if (prev.activeOverlay === 'slash-autocomplete' ||
139
+ prev.activeOverlay === 'resource-autocomplete') {
140
+ return {
141
+ ...prev,
142
+ activeOverlay: 'none',
143
+ mcpWizardServerType: null,
144
+ };
145
+ }
146
+ return prev;
147
+ });
141
148
  }
142
149
  else if (trigger === 'slash-autocomplete') {
143
150
  setUi((prev) => ({ ...prev, activeOverlay: 'slash-autocomplete' }));
@@ -145,7 +152,7 @@ export const InputContainer = forwardRef(function InputContainer({ buffer, input
145
152
  else if (trigger === 'resource-autocomplete') {
146
153
  setUi((prev) => ({ ...prev, activeOverlay: 'resource-autocomplete' }));
147
154
  }
148
- }, [setUi, ui.isProcessing, ui.activeOverlay, approval]);
155
+ }, [setUi, approval]);
149
156
  // Handle image paste from clipboard
150
157
  const handleImagePaste = useCallback((image) => {
151
158
  // Track image attachment analytics (only if session exists)
@@ -558,7 +565,7 @@ export const InputContainer = forwardRef(function InputContainer({ buffer, input
558
565
  // Note: slash-autocomplete handles its own Enter key (either executes command or submits raw text)
559
566
  const shouldHandleSubmit = ui.activeOverlay === 'none' || ui.activeOverlay === 'approval';
560
567
  // Allow history navigation when not blocked by approval/overlay
561
- // When processing: handler allows queue editing but blocks history navigation
568
+ // Allow during processing so users can browse previous prompts while agent runs
562
569
  const canNavigateHistory = !approval && ui.activeOverlay === 'none';
563
570
  const placeholder = approval
564
571
  ? 'Approval required above...'
@@ -1 +1 @@
1
- {"version":3,"file":"OverlayContainer.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/containers/OverlayContainer.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAyE,MAAM,OAAO,CAAC;AAE9F,OAAO,KAAK,EAAE,UAAU,EAAmD,MAAM,aAAa,CAAC;AAC/F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AACtE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kCAAkC,CAAC;AAE5D,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACpF,OAAO,EAGH,KAAK,eAAe,EACvB,MAAM,iCAAiC,CAAC;AA0EzC,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAK3D,MAAM,WAAW,sBAAsB;IACnC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED,UAAU,qBAAqB;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE,YAAY,CAAC;IACtB,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;IAC3D,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC7D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1E,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;IAC1E,KAAK,EAAE,UAAU,CAAC;IAClB,YAAY,EAAE,YAAY,CAAC;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,4EAA4E;IAC5E,qBAAqB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,sGAm1D5B,CAAC"}
1
+ {"version":3,"file":"OverlayContainer.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/containers/OverlayContainer.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAyE,MAAM,OAAO,CAAC;AAE9F,OAAO,KAAK,EAAE,UAAU,EAAmD,MAAM,aAAa,CAAC;AAC/F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AACtE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kCAAkC,CAAC;AAE5D,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACpF,OAAO,EAGH,KAAK,eAAe,EACvB,MAAM,iCAAiC,CAAC;AA0EzC,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAK3D,MAAM,WAAW,sBAAsB;IACnC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED,UAAU,qBAAqB;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE,YAAY,CAAC;IACtB,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;IAC3D,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC7D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1E,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;IAC1E,KAAK,EAAE,UAAU,CAAC;IAClB,YAAY,EAAE,YAAY,CAAC;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,4EAA4E;IAC5E,qBAAqB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,sGA+2D5B,CAAC"}
@@ -5,7 +5,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
5
5
  */
6
6
  import { useCallback, useRef, useImperativeHandle, forwardRef, useState } from 'react';
7
7
  import { Box } from 'ink';
8
- import { ApprovalStatus, DenialReason } from '@dexto/core';
8
+ import { ApprovalStatus, DenialReason, isUserMessage } from '@dexto/core';
9
9
  import { ApprovalPrompt, } from '../components/ApprovalPrompt.js';
10
10
  import { SlashCommandAutocomplete, } from '../components/SlashCommandAutocomplete.js';
11
11
  import ResourceAutocomplete from '../components/ResourceAutocomplete.js';
@@ -446,6 +446,29 @@ export const OverlayContainer = forwardRef(function OverlayContainer({ ui, input
446
446
  if (history && history.length > 0) {
447
447
  const historyMessages = convertHistoryToUIMessages(history, newSessionId);
448
448
  setMessages(historyMessages);
449
+ // Extract user messages for input history (arrow up navigation)
450
+ const userInputHistory = history
451
+ .filter(isUserMessage)
452
+ .map((msg) => {
453
+ // Extract text content from user message
454
+ if (typeof msg.content === 'string') {
455
+ return msg.content;
456
+ }
457
+ // Handle array content (text parts)
458
+ if (Array.isArray(msg.content)) {
459
+ return msg.content
460
+ .filter((part) => typeof part === 'object' && part.type === 'text')
461
+ .map((part) => part.text)
462
+ .join('\n');
463
+ }
464
+ return '';
465
+ })
466
+ .filter((text) => text.trim().length > 0);
467
+ setInput((prev) => ({
468
+ ...prev,
469
+ history: userInputHistory,
470
+ historyIndex: -1,
471
+ }));
449
472
  }
450
473
  setMessages((prev) => [
451
474
  ...prev,
@@ -1 +1 @@
1
- {"version":3,"file":"useCLIState.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/hooks/useCLIState.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useCLIState.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/hooks/useCLIState.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import { useState, useMemo, useEffect, useRef, useCallback } from 'react';
8
8
  import { useStdout } from 'ink';
9
- import { getModelDisplayName } from '@dexto/core';
9
+ import { getModelDisplayName, isUserMessage, } from '@dexto/core';
10
10
  import { useAgentEvents } from './useAgentEvents.js';
11
11
  import { useInputOrchestrator } from './useInputOrchestrator.js';
12
12
  import { InputService, MessageService } from '../services/index.js';
@@ -140,6 +140,19 @@ export function useCLIState({ agent, initialSessionId, startupInfo, onKeyboardSc
140
140
  return;
141
141
  const historyMessages = convertHistoryToUIMessages(history, initialSessionId);
142
142
  setMessages(historyMessages);
143
+ // Extract user messages for input history (arrow up navigation)
144
+ const userInputHistory = history
145
+ .filter(isUserMessage)
146
+ .map((msg) => msg.content
147
+ .filter((part) => part.type === 'text')
148
+ .map((part) => part.text)
149
+ .join('\n'))
150
+ .filter((text) => text.trim().length > 0);
151
+ setInput((prev) => ({
152
+ ...prev,
153
+ history: userInputHistory,
154
+ historyIndex: -1,
155
+ }));
143
156
  }
144
157
  catch (error) {
145
158
  if (cancelled)
@@ -1 +1 @@
1
- {"version":3,"file":"messageFormatting.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/utils/messageFormatting.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAe,MAAM,aAAa,CAAC;AAE5E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAGjD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,GAAE,MAAsB,GAAG,MAAM,CAmB1F;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAChC,YAAY,EAAE,MAAM,EACpB,QAAQ,GAAE,MAAW,EACrB,GAAG,GAAE,MAAsB,GAC5B,MAAM,CAWR;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAiC7E;AAyED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA6B3D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA8BzD;AA8BD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CA0EhG;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,SAAS,GAAE,MAAW,GACvB,MAAM,CAeR;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAO1D;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CASvF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAO5D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAQjE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAO3D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAQhD;AAgBD;;GAEG;AACH,wBAAgB,0BAA0B,CACtC,OAAO,EAAE,eAAe,EAAE,EAC1B,SAAS,EAAE,MAAM,GAClB,OAAO,EAAE,CAmBX;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,UAAU;;;;;;;;GAiBrD"}
1
+ {"version":3,"file":"messageFormatting.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/utils/messageFormatting.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAyB,MAAM,aAAa,CAAC;AAEtF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAGjD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,GAAE,MAAsB,GAAG,MAAM,CAmB1F;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAChC,YAAY,EAAE,MAAM,EACpB,QAAQ,GAAE,MAAW,EACrB,GAAG,GAAE,MAAsB,GAC5B,MAAM,CAWR;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAiC7E;AAyED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA6B3D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA8BzD;AA8BD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CA0EhG;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,SAAS,GAAE,MAAW,GACvB,MAAM,CAeR;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAO1D;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CASvF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAO5D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAQjE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAO3D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAQhD;AAqCD;;GAEG;AACH,wBAAgB,0BAA0B,CACtC,OAAO,EAAE,eAAe,EAAE,EAC1B,SAAS,EAAE,MAAM,GAClB,OAAO,EAAE,CAgGX;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,UAAU;;;;;;;;GAiBrD"}
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import path from 'path';
6
6
  import os from 'os';
7
- import { isTextPart } from '@dexto/core';
7
+ import { isTextPart, isAssistantMessage, isToolMessage } from '@dexto/core';
8
8
  import { generateMessageId } from './idGenerator.js';
9
9
  /**
10
10
  * Convert absolute path to display-friendly relative path.
@@ -245,9 +245,9 @@ export function formatToolArgsForDisplay(toolName, args) {
245
245
  */
246
246
  const formatArgValue = (argName, value) => {
247
247
  const strValue = typeof value === 'string' ? value : JSON.stringify(value);
248
- // File paths: use relative path + center-truncation
248
+ // File paths: use relative path (no truncation)
249
249
  if (PATH_ARGS.has(argName)) {
250
- return formatPathForDisplay(strValue);
250
+ return makeRelativePath(strValue);
251
251
  }
252
252
  // Commands: show single-line in full, truncate multi-line (heredocs) to first line
253
253
  if (argName === 'command') {
@@ -407,13 +407,100 @@ function extractTextContent(content) {
407
407
  .map((part) => part.text)
408
408
  .join('\n');
409
409
  }
410
+ /**
411
+ * Generates a preview of tool result content for display
412
+ */
413
+ function generateToolResultPreview(content) {
414
+ const textContent = extractTextContent(content);
415
+ if (!textContent)
416
+ return '';
417
+ const lines = textContent.split('\n');
418
+ const previewLines = lines.slice(0, 5);
419
+ let preview = previewLines.join('\n');
420
+ // Truncate if too long
421
+ if (preview.length > 400) {
422
+ preview = preview.slice(0, 397) + '...';
423
+ }
424
+ else if (lines.length > 5) {
425
+ preview += '\n...';
426
+ }
427
+ return preview;
428
+ }
410
429
  /**
411
430
  * Converts session history messages to UI messages
412
431
  */
413
432
  export function convertHistoryToUIMessages(history, sessionId) {
414
433
  const uiMessages = [];
434
+ // Build a map of toolCallId -> ToolCall for looking up tool call args
435
+ const toolCallMap = new Map();
436
+ for (const msg of history) {
437
+ if (isAssistantMessage(msg) && msg.toolCalls) {
438
+ for (const toolCall of msg.toolCalls) {
439
+ toolCallMap.set(toolCall.id, toolCall);
440
+ }
441
+ }
442
+ }
415
443
  history.forEach((msg, index) => {
416
- // Extract text content properly
444
+ const timestamp = new Date(msg.timestamp ?? Date.now() - (history.length - index) * 1000);
445
+ // Handle tool messages specially
446
+ if (isToolMessage(msg)) {
447
+ // Look up the original tool call to get args
448
+ const toolCall = toolCallMap.get(msg.toolCallId);
449
+ // Format tool name
450
+ const displayName = getToolDisplayName(msg.name);
451
+ // Format args if we have them
452
+ let toolContent = displayName;
453
+ if (toolCall) {
454
+ try {
455
+ const args = JSON.parse(toolCall.function.arguments || '{}');
456
+ const argsFormatted = formatToolArgsForDisplay(msg.name, args);
457
+ if (argsFormatted) {
458
+ toolContent = `${displayName}(${argsFormatted})`;
459
+ }
460
+ }
461
+ catch {
462
+ // Ignore JSON parse errors
463
+ }
464
+ }
465
+ // Add tool type badge
466
+ const badge = getToolTypeBadge(msg.name);
467
+ if (badge) {
468
+ toolContent = `${toolContent} [${badge}]`;
469
+ }
470
+ // Generate result preview
471
+ const resultPreview = generateToolResultPreview(msg.content);
472
+ uiMessages.push({
473
+ id: `session-${sessionId}-${index}`,
474
+ role: 'tool',
475
+ content: toolContent,
476
+ timestamp,
477
+ toolStatus: 'finished',
478
+ toolResult: resultPreview,
479
+ isError: msg.success === false,
480
+ // Store content parts for potential rich rendering
481
+ toolContent: msg.content,
482
+ // Restore structured display data for rich rendering (diffs, shell output, etc.)
483
+ ...(msg.displayData !== undefined && {
484
+ toolDisplayData: msg.displayData,
485
+ }),
486
+ });
487
+ return;
488
+ }
489
+ // Handle assistant messages - skip those with only tool calls (no text content)
490
+ if (isAssistantMessage(msg)) {
491
+ const textContent = extractTextContent(msg.content);
492
+ // Skip if no text content (message was just tool calls)
493
+ if (!textContent)
494
+ return;
495
+ uiMessages.push({
496
+ id: `session-${sessionId}-${index}`,
497
+ role: 'assistant',
498
+ content: textContent,
499
+ timestamp,
500
+ });
501
+ return;
502
+ }
503
+ // Handle other messages (user, system)
417
504
  const textContent = extractTextContent(msg.content);
418
505
  // Skip empty messages
419
506
  if (!textContent)
@@ -422,7 +509,7 @@ export function convertHistoryToUIMessages(history, sessionId) {
422
509
  id: `session-${sessionId}-${index}`,
423
510
  role: msg.role,
424
511
  content: textContent,
425
- timestamp: new Date(msg.timestamp ?? Date.now() - (history.length - index) * 1000),
512
+ timestamp,
426
513
  });
427
514
  });
428
515
  return uiMessages;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dexto",
3
- "version": "1.5.1",
3
+ "version": "1.5.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "dexto": "./dist/index.js"
@@ -40,12 +40,12 @@
40
40
  "ws": "^8.18.1",
41
41
  "yaml": "^2.7.1",
42
42
  "zod": "^3.25.0",
43
- "@dexto/agent-management": "1.5.1",
44
- "@dexto/analytics": "1.5.1",
45
- "@dexto/core": "1.5.1",
46
- "@dexto/image-local": "1.5.1",
47
- "@dexto/registry": "1.5.1",
48
- "@dexto/server": "1.5.1"
43
+ "@dexto/agent-management": "1.5.2",
44
+ "@dexto/analytics": "1.5.2",
45
+ "@dexto/core": "1.5.2",
46
+ "@dexto/image-local": "1.5.2",
47
+ "@dexto/registry": "1.5.2",
48
+ "@dexto/server": "1.5.2"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/react": "^19.0.0",