@prestyj/cli 4.6.4 → 4.10.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.
- package/dist/cli/auth.d.ts.map +1 -1
- package/dist/cli/auth.js +11 -10
- package/dist/cli/auth.js.map +1 -1
- package/dist/cli/mcp.js +1 -1
- package/dist/cli/shared.d.ts +12 -0
- package/dist/cli/shared.d.ts.map +1 -1
- package/dist/cli/shared.js +31 -6
- package/dist/cli/shared.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +63 -62
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +7 -14
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +13 -17
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-compaction.test.js +1 -1
- package/dist/core/agent-session-compaction.test.js.map +1 -1
- package/dist/core/agent-session.d.ts +1 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +6 -2
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +1 -58
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +3 -254
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/auto-update.d.ts +4 -26
- package/dist/core/auto-update.d.ts.map +1 -1
- package/dist/core/auto-update.js +12 -237
- package/dist/core/auto-update.js.map +1 -1
- package/dist/core/claude-code-version.d.ts +1 -9
- package/dist/core/claude-code-version.d.ts.map +1 -1
- package/dist/core/claude-code-version.js +2 -105
- package/dist/core/claude-code-version.js.map +1 -1
- package/dist/core/compaction/compactor.test.js +2 -2
- package/dist/core/compaction/compactor.test.js.map +1 -1
- package/dist/core/file-lock.d.ts +1 -5
- package/dist/core/file-lock.d.ts.map +1 -1
- package/dist/core/file-lock.js +2 -75
- package/dist/core/file-lock.js.map +1 -1
- package/dist/core/logger.d.ts +4 -17
- package/dist/core/logger.d.ts.map +1 -1
- package/dist/core/logger.js +21 -110
- package/dist/core/logger.js.map +1 -1
- package/dist/core/lsp/client.d.ts +63 -0
- package/dist/core/lsp/client.d.ts.map +1 -0
- package/dist/core/lsp/client.js +215 -0
- package/dist/core/lsp/client.js.map +1 -0
- package/dist/core/lsp/format.d.ts +12 -0
- package/dist/core/lsp/format.d.ts.map +1 -0
- package/dist/core/lsp/format.js +30 -0
- package/dist/core/lsp/format.js.map +1 -0
- package/dist/core/lsp/format.test.d.ts +2 -0
- package/dist/core/lsp/format.test.d.ts.map +1 -0
- package/dist/core/lsp/format.test.js +56 -0
- package/dist/core/lsp/format.test.js.map +1 -0
- package/dist/core/lsp/integration.test.d.ts +2 -0
- package/dist/core/lsp/integration.test.d.ts.map +1 -0
- package/dist/core/lsp/integration.test.js +60 -0
- package/dist/core/lsp/integration.test.js.map +1 -0
- package/dist/core/lsp/jsonrpc.d.ts +32 -0
- package/dist/core/lsp/jsonrpc.d.ts.map +1 -0
- package/dist/core/lsp/jsonrpc.js +141 -0
- package/dist/core/lsp/jsonrpc.js.map +1 -0
- package/dist/core/lsp/jsonrpc.test.d.ts +2 -0
- package/dist/core/lsp/jsonrpc.test.d.ts.map +1 -0
- package/dist/core/lsp/jsonrpc.test.js +127 -0
- package/dist/core/lsp/jsonrpc.test.js.map +1 -0
- package/dist/core/lsp/manager.d.ts +40 -0
- package/dist/core/lsp/manager.d.ts.map +1 -0
- package/dist/core/lsp/manager.js +135 -0
- package/dist/core/lsp/manager.js.map +1 -0
- package/dist/core/lsp/manager.test.d.ts +2 -0
- package/dist/core/lsp/manager.test.d.ts.map +1 -0
- package/dist/core/lsp/manager.test.js +119 -0
- package/dist/core/lsp/manager.test.js.map +1 -0
- package/dist/core/lsp/servers.d.ts +45 -0
- package/dist/core/lsp/servers.d.ts.map +1 -0
- package/dist/core/lsp/servers.js +247 -0
- package/dist/core/lsp/servers.js.map +1 -0
- package/dist/core/mcp/defaults.d.ts +1 -1
- package/dist/core/mcp/defaults.js +1 -1
- package/dist/core/mcp/store.d.ts +1 -1
- package/dist/core/mcp/store.test.js +1 -1
- package/dist/core/mcp/store.test.js.map +1 -1
- package/dist/core/model-registry.d.ts +1 -75
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +4 -344
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/oauth/anthropic.d.ts +1 -3
- package/dist/core/oauth/anthropic.d.ts.map +1 -1
- package/dist/core/oauth/anthropic.js +2 -96
- package/dist/core/oauth/anthropic.js.map +1 -1
- package/dist/core/oauth/gemini.d.ts +1 -3
- package/dist/core/oauth/gemini.d.ts.map +1 -1
- package/dist/core/oauth/gemini.js +2 -385
- package/dist/core/oauth/gemini.js.map +1 -1
- package/dist/core/oauth/kimi.d.ts +1 -45
- package/dist/core/oauth/kimi.d.ts.map +1 -1
- package/dist/core/oauth/kimi.js +2 -277
- package/dist/core/oauth/kimi.js.map +1 -1
- package/dist/core/oauth/openai.d.ts +1 -3
- package/dist/core/oauth/openai.d.ts.map +1 -1
- package/dist/core/oauth/openai.js +2 -187
- package/dist/core/oauth/openai.js.map +1 -1
- package/dist/core/oauth/pkce.d.ts +1 -4
- package/dist/core/oauth/pkce.d.ts.map +1 -1
- package/dist/core/oauth/pkce.js +2 -16
- package/dist/core/oauth/pkce.js.map +1 -1
- package/dist/core/oauth/types.d.ts +1 -13
- package/dist/core/oauth/types.d.ts.map +1 -1
- package/dist/core/process-manager.d.ts +10 -0
- package/dist/core/process-manager.d.ts.map +1 -1
- package/dist/core/process-manager.js +45 -1
- package/dist/core/process-manager.js.map +1 -1
- package/dist/core/prompt-commands.d.ts.map +1 -1
- package/dist/core/prompt-commands.js +20 -7
- package/dist/core/prompt-commands.js.map +1 -1
- package/dist/core/session-manager.d.ts +26 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +99 -2
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-manager.test.d.ts +2 -0
- package/dist/core/session-manager.test.d.ts.map +1 -0
- package/dist/core/session-manager.test.js +113 -0
- package/dist/core/session-manager.test.js.map +1 -0
- package/dist/core/settings-manager.d.ts +2 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +6 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/telegram.d.ts +1 -112
- package/dist/core/telegram.d.ts.map +1 -1
- package/dist/core/telegram.js +2 -251
- package/dist/core/telegram.js.map +1 -1
- package/dist/core/thinking-level.d.ts +1 -4
- package/dist/core/thinking-level.d.ts.map +1 -1
- package/dist/core/thinking-level.js +3 -58
- package/dist/core/thinking-level.js.map +1 -1
- package/dist/core/voice-transcriber.d.ts +1 -32
- package/dist/core/voice-transcriber.d.ts.map +1 -1
- package/dist/core/voice-transcriber.js +3 -112
- package/dist/core/voice-transcriber.js.map +1 -1
- package/dist/interactive.d.ts.map +1 -1
- package/dist/interactive.js +5 -2
- package/dist/interactive.js.map +1 -1
- package/dist/modes/agent-home-mode.d.ts.map +1 -1
- package/dist/modes/agent-home-mode.js +10 -38
- package/dist/modes/agent-home-mode.js.map +1 -1
- package/dist/modes/serve-mode.d.ts.map +1 -1
- package/dist/modes/serve-mode.js +11 -39
- package/dist/modes/serve-mode.js.map +1 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +13 -6
- package/dist/session.js.map +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +6 -3
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/edit.d.ts +3 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +15 -3
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/edit.test.js +70 -0
- package/dist/tools/edit.test.js.map +1 -1
- package/dist/tools/goals.d.ts +1 -1
- package/dist/tools/index.d.ts +15 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +16 -3
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/task-send.d.ts +12 -0
- package/dist/tools/task-send.d.ts.map +1 -0
- package/dist/tools/task-send.js +34 -0
- package/dist/tools/task-send.js.map +1 -0
- package/dist/tools/task-send.test.d.ts +2 -0
- package/dist/tools/task-send.test.d.ts.map +1 -0
- package/dist/tools/task-send.test.js +95 -0
- package/dist/tools/task-send.test.js.map +1 -0
- package/dist/tools/write.d.ts +3 -1
- package/dist/tools/write.d.ts.map +1 -1
- package/dist/tools/write.js +13 -2
- package/dist/tools/write.js.map +1 -1
- package/dist/tools/write.test.js +33 -0
- package/dist/tools/write.test.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/App.d.ts +16 -0
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +126 -12
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/anchor-pad-reclaim.test.d.ts +2 -0
- package/dist/ui/anchor-pad-reclaim.test.d.ts.map +1 -0
- package/dist/ui/anchor-pad-reclaim.test.js +146 -0
- package/dist/ui/anchor-pad-reclaim.test.js.map +1 -0
- package/dist/ui/app-items.d.ts +14 -0
- package/dist/ui/app-items.d.ts.map +1 -1
- package/dist/ui/app-items.js +13 -0
- package/dist/ui/app-items.js.map +1 -1
- package/dist/ui/app-items.test.js +17 -1
- package/dist/ui/app-items.test.js.map +1 -1
- package/dist/ui/atomic-flush-regression.test.d.ts +2 -0
- package/dist/ui/atomic-flush-regression.test.d.ts.map +1 -0
- package/dist/ui/atomic-flush-regression.test.js +218 -0
- package/dist/ui/atomic-flush-regression.test.js.map +1 -0
- package/dist/ui/components/AssistantMessage.test.js +32 -1
- package/dist/ui/components/AssistantMessage.test.js.map +1 -1
- package/dist/ui/components/Footer.d.ts.map +1 -1
- package/dist/ui/components/Footer.js +1 -0
- package/dist/ui/components/Footer.js.map +1 -1
- package/dist/ui/components/InputArea.test.js +11 -5
- package/dist/ui/components/InputArea.test.js.map +1 -1
- package/dist/ui/components/Markdown.d.ts.map +1 -1
- package/dist/ui/components/Markdown.js +9 -1
- package/dist/ui/components/Markdown.js.map +1 -1
- package/dist/ui/footer-jump-regression.test.js +56 -9
- package/dist/ui/footer-jump-regression.test.js.map +1 -1
- package/dist/ui/frame-clip-regression.test.d.ts +2 -0
- package/dist/ui/frame-clip-regression.test.d.ts.map +1 -0
- package/dist/ui/frame-clip-regression.test.js +215 -0
- package/dist/ui/frame-clip-regression.test.js.map +1 -0
- package/dist/ui/hooks/useTranscriptHistory.d.ts +9 -1
- package/dist/ui/hooks/useTranscriptHistory.d.ts.map +1 -1
- package/dist/ui/hooks/useTranscriptHistory.js +51 -19
- package/dist/ui/hooks/useTranscriptHistory.js.map +1 -1
- package/dist/ui/hooks/useTranscriptHistory.sessionstore.test.d.ts +2 -0
- package/dist/ui/hooks/useTranscriptHistory.sessionstore.test.d.ts.map +1 -0
- package/dist/ui/hooks/useTranscriptHistory.sessionstore.test.js +81 -0
- package/dist/ui/hooks/useTranscriptHistory.sessionstore.test.js.map +1 -0
- package/dist/ui/live-area-clamp.test.js +37 -0
- package/dist/ui/live-area-clamp.test.js.map +1 -1
- package/dist/ui/live-area-height.d.ts.map +1 -1
- package/dist/ui/live-area-height.js +13 -6
- package/dist/ui/live-area-height.js.map +1 -1
- package/dist/ui/live-area-height.test.js +9 -0
- package/dist/ui/live-area-height.test.js.map +1 -1
- package/dist/ui/live-item-flush.d.ts +28 -0
- package/dist/ui/live-item-flush.d.ts.map +1 -1
- package/dist/ui/live-item-flush.js +44 -0
- package/dist/ui/live-item-flush.js.map +1 -1
- package/dist/ui/live-item-flush.test.js +83 -1
- package/dist/ui/live-item-flush.test.js.map +1 -1
- package/dist/ui/login.d.ts.map +1 -1
- package/dist/ui/login.js +12 -30
- package/dist/ui/login.js.map +1 -1
- package/dist/ui/mcp.d.ts +1 -2
- package/dist/ui/mcp.d.ts.map +1 -1
- package/dist/ui/mcp.js +8 -43
- package/dist/ui/mcp.js.map +1 -1
- package/dist/ui/pixel.d.ts.map +1 -1
- package/dist/ui/pixel.js +10 -38
- package/dist/ui/pixel.js.map +1 -1
- package/dist/ui/queued-message.test.js +7 -1
- package/dist/ui/queued-message.test.js.map +1 -1
- package/dist/ui/render.d.ts.map +1 -1
- package/dist/ui/render.js +154 -5
- package/dist/ui/render.js.map +1 -1
- package/dist/ui/sessions.d.ts.map +1 -1
- package/dist/ui/sessions.js +10 -42
- package/dist/ui/sessions.js.map +1 -1
- package/dist/ui/startup-banner-order.test.d.ts +2 -0
- package/dist/ui/startup-banner-order.test.d.ts.map +1 -0
- package/dist/ui/startup-banner-order.test.js +105 -0
- package/dist/ui/startup-banner-order.test.js.map +1 -0
- package/dist/ui/streaming-flush-bounce.test.js +41 -20
- package/dist/ui/streaming-flush-bounce.test.js.map +1 -1
- package/dist/ui/submit-boundary-gap.test.d.ts +2 -0
- package/dist/ui/submit-boundary-gap.test.d.ts.map +1 -0
- package/dist/ui/submit-boundary-gap.test.js +180 -0
- package/dist/ui/submit-boundary-gap.test.js.map +1 -0
- package/dist/ui/terminal-history.d.ts.map +1 -1
- package/dist/ui/terminal-history.js +28 -9
- package/dist/ui/terminal-history.js.map +1 -1
- package/dist/ui/terminal-history.test.js +10 -3
- package/dist/ui/terminal-history.test.js.map +1 -1
- package/dist/ui/testing/screen-recorder.d.ts.map +1 -1
- package/dist/ui/testing/screen-recorder.js +11 -1
- package/dist/ui/testing/screen-recorder.js.map +1 -1
- package/dist/ui/tui-history-parity.test.js +4 -1
- package/dist/ui/tui-history-parity.test.js.map +1 -1
- package/dist/ui/tui-simulation.test.js +7 -1
- package/dist/ui/tui-simulation.test.js.map +1 -1
- package/dist/ui/utils/assistant-stream-split.d.ts +7 -0
- package/dist/ui/utils/assistant-stream-split.d.ts.map +1 -1
- package/dist/ui/utils/assistant-stream-split.js +22 -1
- package/dist/ui/utils/assistant-stream-split.js.map +1 -1
- package/dist/ui/utils/assistant-stream-split.test.js +14 -0
- package/dist/ui/utils/assistant-stream-split.test.js.map +1 -1
- package/dist/ui/utils/markdown-renderer.d.ts.map +1 -1
- package/dist/ui/utils/markdown-renderer.js +46 -3
- package/dist/ui/utils/markdown-renderer.js.map +1 -1
- package/dist/ui/utils/markdown-renderer.test.d.ts +2 -0
- package/dist/ui/utils/markdown-renderer.test.d.ts.map +1 -0
- package/dist/ui/utils/markdown-renderer.test.js +63 -0
- package/dist/ui/utils/markdown-renderer.test.js.map +1 -0
- package/dist/ui/utils/terminal-graphics.d.ts +28 -1
- package/dist/ui/utils/terminal-graphics.d.ts.map +1 -1
- package/dist/ui/utils/terminal-graphics.js +44 -3
- package/dist/ui/utils/terminal-graphics.js.map +1 -1
- package/dist/ui/utils/terminal-graphics.test.js +40 -1
- package/dist/ui/utils/terminal-graphics.test.js.map +1 -1
- package/dist/utils/session-title.js +1 -1
- package/dist/utils/session-title.js.map +1 -1
- package/package.json +7 -6
- package/dist/core/model-registry.test.d.ts +0 -2
- package/dist/core/model-registry.test.d.ts.map +0 -1
- package/dist/core/model-registry.test.js +0 -95
- package/dist/core/model-registry.test.js.map +0 -1
- package/dist/core/oauth/gemini.test.d.ts +0 -2
- package/dist/core/oauth/gemini.test.d.ts.map +0 -1
- package/dist/core/oauth/gemini.test.js +0 -154
- package/dist/core/oauth/gemini.test.js.map +0 -1
- package/dist/core/thinking-level.test.d.ts +0 -2
- package/dist/core/thinking-level.test.d.ts.map +0 -1
- package/dist/core/thinking-level.test.js +0 -51
- package/dist/core/thinking-level.test.js.map +0 -1
- package/dist/core/voice-transcriber.test.d.ts +0 -2
- package/dist/core/voice-transcriber.test.d.ts.map +0 -1
- package/dist/core/voice-transcriber.test.js +0 -88
- package/dist/core/voice-transcriber.test.js.map +0 -1
|
@@ -2,14 +2,15 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
// Regression for the per-chunk footer "bounce" during streaming.
|
|
3
3
|
//
|
|
4
4
|
// The assistant response is flushed to native scrollback paragraph-by-paragraph
|
|
5
|
-
// while it streams (see assistant-stream-split). The
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
5
|
+
// while it streams (see assistant-stream-split). The flush effect calls
|
|
6
|
+
// queueFlush, which renders the paragraph to ANSI and enqueues the bytes
|
|
7
|
+
// through the patched Ink `insertBeforeFrame` (a passive buffer — no terminal
|
|
8
|
+
// write), then advances `flushedChars` via the same React batch. The commit
|
|
9
|
+
// that shrinks the live text therefore carries the compensating scrollback
|
|
10
|
+
// bytes in ONE frame write: erase tall frame + paragraph bytes + shorter
|
|
11
|
+
// frame. No frame ever shows the paragraph in both scrollback and the live
|
|
12
|
+
// region, so the footer offset from the bottom stays constant. This test
|
|
13
|
+
// models that flow and asserts exactly that.
|
|
13
14
|
import React, { useEffect, useRef, useState } from "react";
|
|
14
15
|
import { render, Text, useStdout } from "ink";
|
|
15
16
|
import { describe, expect, it } from "vitest";
|
|
@@ -41,18 +42,24 @@ const getId = () => `chunk-${idCounter++}`;
|
|
|
41
42
|
function Controls() {
|
|
42
43
|
return (_jsxs(ChatControls, { controlsRef: () => { }, children: [_jsx(Text, { children: "SIM_STATUS" }), _jsx(Text, { children: "SIM_INPUT" }), _jsx(Text, { children: "SIM_FOOTER" }), _jsx(Text, { children: FOOTER_BOTTOM })] }));
|
|
43
44
|
}
|
|
44
|
-
// Mirrors App.tsx:
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
|
|
45
|
+
// Mirrors App.tsx: the live text is sliced by the COMMITTED `flushedChars`
|
|
46
|
+
// only. The flush effect enqueues the paragraph bytes (passive) and advances
|
|
47
|
+
// `flushedChars`; the resulting commit's single frame write both shrinks the
|
|
48
|
+
// live frame and carries the scrollback bytes.
|
|
49
|
+
function Driver({ rawText, enqueueStdout, }) {
|
|
48
50
|
const { write: writeStdout } = useStdout();
|
|
49
51
|
const [history, setHistory] = useState([]);
|
|
50
52
|
const [liveItems, setLiveItems] = useState([]);
|
|
51
53
|
const flushRef = useRef({ flushedChars: 0, text: "" });
|
|
54
|
+
// Stable printer across renders — mirrors renderApp, where one printer
|
|
55
|
+
// instance (and its printed-id dedup set) lives for the whole app lifetime.
|
|
56
|
+
const printerRef = useRef(null);
|
|
57
|
+
printerRef.current ??= createTerminalHistoryPrinter();
|
|
52
58
|
const { queueFlush } = useTranscriptHistory({
|
|
53
|
-
terminalHistoryPrinter:
|
|
59
|
+
terminalHistoryPrinter: printerRef.current,
|
|
54
60
|
terminalHistoryContext: terminalContext,
|
|
55
61
|
writeStdout,
|
|
62
|
+
enqueueStdout,
|
|
56
63
|
sessionPathRef: { current: undefined },
|
|
57
64
|
sessionManagerRef: { current: null },
|
|
58
65
|
history,
|
|
@@ -60,9 +67,6 @@ function Driver({ rawText }) {
|
|
|
60
67
|
setLiveItems,
|
|
61
68
|
});
|
|
62
69
|
const alreadyFlushed = flushRef.current.flushedChars;
|
|
63
|
-
const pendingFlushChars = rawText
|
|
64
|
-
? splitAssistantStreamingText(rawText.slice(alreadyFlushed)).flushedText.length
|
|
65
|
-
: 0;
|
|
66
70
|
useEffect(() => {
|
|
67
71
|
if (!rawText) {
|
|
68
72
|
flushRef.current = { flushedChars: 0, text: "" };
|
|
@@ -88,7 +92,7 @@ function Driver({ rawText }) {
|
|
|
88
92
|
}
|
|
89
93
|
flushRef.current = { ...flushRef.current, text: rawText };
|
|
90
94
|
}, [rawText, queueFlush]);
|
|
91
|
-
const visibleStreamingText = stripDoneMarkers(rawText.slice(alreadyFlushed
|
|
95
|
+
const visibleStreamingText = stripDoneMarkers(rawText.slice(alreadyFlushed));
|
|
92
96
|
const renderItem = (item, index, items) => renderTranscriptItem({
|
|
93
97
|
item,
|
|
94
98
|
index,
|
|
@@ -102,7 +106,10 @@ function Driver({ rawText }) {
|
|
|
102
106
|
renderMarkdown: true,
|
|
103
107
|
measuredLiveAreaRows: ROWS,
|
|
104
108
|
});
|
|
105
|
-
return (_jsx(ThemeContext.Provider, { value: theme, children: _jsx(TerminalSizeProvider, { children: _jsxs(ChatLayout, { columns: COLUMNS, children: [_jsx(ChatLivePane, { liveItems: liveItems, renderItem: renderItem, isRunning: true, visibleStreamingText: visibleStreamingText, streamingThinking: "", thinkingMs: 0, reserveStreamingSpacing: false, renderMarkdown: true, measuredLiveAreaRows: ROWS,
|
|
109
|
+
return (_jsx(ThemeContext.Provider, { value: theme, children: _jsx(TerminalSizeProvider, { children: _jsxs(ChatLayout, { columns: COLUMNS, children: [_jsx(ChatLivePane, { liveItems: liveItems, renderItem: renderItem, isRunning: true, visibleStreamingText: visibleStreamingText, streamingThinking: "", thinkingMs: 0, reserveStreamingSpacing: false, renderMarkdown: true, measuredLiveAreaRows: ROWS,
|
|
110
|
+
// Mirrors App: a live continuation of an already-flushed stream
|
|
111
|
+
// re-inserts the blank separator line above the live remainder.
|
|
112
|
+
assistantMarginTop: alreadyFlushed > 0 ? 1 : 0, streamingContinuation: alreadyFlushed > 0 }), _jsx(Controls, {})] }) }) }));
|
|
106
113
|
}
|
|
107
114
|
async function tick() {
|
|
108
115
|
await new Promise((resolve) => setTimeout(resolve, 45));
|
|
@@ -114,6 +121,19 @@ describe("streaming flush footer bounce", () => {
|
|
|
114
121
|
it("keeps the footer at a constant offset across paragraph flush boundaries", async () => {
|
|
115
122
|
const recorder = new ScreenRecorder({ columns: COLUMNS, rows: ROWS });
|
|
116
123
|
const stdout = makeRecordingStdout(recorder);
|
|
124
|
+
// Mirrors renderApp's enqueueHistoryWrite: reads the (patched) instance at
|
|
125
|
+
// call time, falling back to a raw write when the API is unavailable.
|
|
126
|
+
const instanceRef = {
|
|
127
|
+
current: null,
|
|
128
|
+
};
|
|
129
|
+
const enqueueStdout = (data) => {
|
|
130
|
+
if (instanceRef.current?.insertBeforeFrame) {
|
|
131
|
+
instanceRef.current.insertBeforeFrame(data);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
stdout.write(data);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
117
137
|
const footerOffsets = [];
|
|
118
138
|
const capture = () => {
|
|
119
139
|
const lines = recorder.viewportLines().map((line) => stripAnsi(line));
|
|
@@ -130,17 +150,18 @@ describe("streaming flush footer bounce", () => {
|
|
|
130
150
|
`${paragraph(1)}\n\n${paragraph(2)}\n\n`,
|
|
131
151
|
`${paragraph(1)}\n\n${paragraph(2)}\n\n${paragraph(3)}`,
|
|
132
152
|
];
|
|
133
|
-
const mounted = render(_jsx(Driver, { rawText: steps[0] }), {
|
|
153
|
+
const mounted = render(_jsx(Driver, { rawText: steps[0], enqueueStdout: enqueueStdout }), {
|
|
134
154
|
stdout,
|
|
135
155
|
columns: COLUMNS,
|
|
136
156
|
rows: ROWS,
|
|
137
157
|
patchConsole: false,
|
|
138
158
|
maxFps: 1000,
|
|
139
159
|
});
|
|
160
|
+
instanceRef.current = mounted;
|
|
140
161
|
await tick();
|
|
141
162
|
capture();
|
|
142
163
|
for (let i = 1; i < steps.length; i++) {
|
|
143
|
-
mounted.rerender(_jsx(Driver, { rawText: steps[i] }));
|
|
164
|
+
mounted.rerender(_jsx(Driver, { rawText: steps[i], enqueueStdout: enqueueStdout }));
|
|
144
165
|
await tick();
|
|
145
166
|
await tick();
|
|
146
167
|
capture();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streaming-flush-bounce.test.js","sourceRoot":"","sources":["../../src/ui/streaming-flush-bounce.test.tsx"],"names":[],"mappings":";AAAA,iEAAiE;AACjE,EAAE;AACF,gFAAgF;AAChF,
|
|
1
|
+
{"version":3,"file":"streaming-flush-bounce.test.js","sourceRoot":"","sources":["../../src/ui/streaming-flush-bounce.test.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"submit-boundary-gap.test.d.ts","sourceRoot":"","sources":["../../src/ui/submit-boundary-gap.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Self-test for the "white space before the LLM responds" gap that appears
|
|
3
|
+
// when submitting a slash command (e.g. /commit).
|
|
4
|
+
//
|
|
5
|
+
// The submit boundary stacks several frame SHRINKS into the moment the agent
|
|
6
|
+
// is (re)starting, while the patched ink bottom-anchor is ACTIVE:
|
|
7
|
+
// 1. the prior turn's live items flush to scrollback + the live frame clears
|
|
8
|
+
// (setLiveItems([])),
|
|
9
|
+
// 2. the open slash menu closes (the controls block gets shorter).
|
|
10
|
+
// Each shrink-while-active becomes blank "pad debt" above the frame so the
|
|
11
|
+
// footer can't jump up. The new turn then enters a quiet thinking phase with
|
|
12
|
+
// NO frame growth, so nothing consumes the pads — they sit on screen as a blank
|
|
13
|
+
// gap before any LLM output, exactly what the user reported.
|
|
14
|
+
//
|
|
15
|
+
// This drives the REAL patched ink through that byte sequence and asserts:
|
|
16
|
+
// A) the gap genuinely forms at the submit boundary (the bug), and
|
|
17
|
+
// B) the mid-run off→on reclaim pulse (App's fix) collapses it while keeping
|
|
18
|
+
// the footer bottom-pinned.
|
|
19
|
+
import React from "react";
|
|
20
|
+
import { Box, render, Text } from "ink";
|
|
21
|
+
import { describe, expect, it } from "vitest";
|
|
22
|
+
import stripAnsi from "strip-ansi";
|
|
23
|
+
import { ScreenRecorder, makeRecordingStdout } from "./testing/screen-recorder.js";
|
|
24
|
+
const COLUMNS = 80;
|
|
25
|
+
const ROWS = 24;
|
|
26
|
+
const FOOTER = "SIM_FOOTER_BOTTOM";
|
|
27
|
+
const FRAME_TOP = "SIM_FRAME_TOP";
|
|
28
|
+
// Controls block: the slash menu adds rows ABOVE the input while open, so
|
|
29
|
+
// closing it is a real controls-height shrink (same shape as the menu close
|
|
30
|
+
// that fires on submit).
|
|
31
|
+
function Controls({ liveRows, menuOpen }) {
|
|
32
|
+
return (_jsxs(Box, { flexDirection: "column", width: COLUMNS, children: [_jsx(Text, { children: FRAME_TOP }), Array.from({ length: liveRows }, (_, i) => (_jsxs(Text, { children: ["SIM_LIVE_", i + 1] }, i))), menuOpen
|
|
33
|
+
? Array.from({ length: 6 }, (_, i) => _jsxs(Text, { children: ["SIM_MENU_ITEM_", i + 1] }, `m${i}`))
|
|
34
|
+
: null, _jsx(Text, { children: "SIM_INPUT" }), _jsx(Text, { children: FOOTER })] }));
|
|
35
|
+
}
|
|
36
|
+
async function tick(ms = 60) {
|
|
37
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
38
|
+
}
|
|
39
|
+
function blanksAboveFrame(recorder) {
|
|
40
|
+
const lines = recorder.viewportLines().map((line) => stripAnsi(line));
|
|
41
|
+
const frameTopIdx = lines.findIndex((line) => line.includes(FRAME_TOP));
|
|
42
|
+
if (frameTopIdx < 0)
|
|
43
|
+
return -1;
|
|
44
|
+
return lines.slice(0, frameTopIdx).filter((line) => line.trim() === "").length;
|
|
45
|
+
}
|
|
46
|
+
describe("submit-boundary gap", () => {
|
|
47
|
+
it("forms the gap at submit then the reclaim pulse collapses it (footer stays pinned)", async () => {
|
|
48
|
+
const recorder = new ScreenRecorder({ columns: COLUMNS, rows: ROWS });
|
|
49
|
+
const stdout = makeRecordingStdout(recorder);
|
|
50
|
+
const transcriptRows = Array.from({ length: ROWS }, (_, i) => `SIM_HISTORY_${i + 1}`);
|
|
51
|
+
const mounted = render(_jsx(Controls, { liveRows: 2, menuOpen: false }), {
|
|
52
|
+
stdout,
|
|
53
|
+
patchConsole: false,
|
|
54
|
+
maxFps: 1000,
|
|
55
|
+
anchorFrameToBottom: true,
|
|
56
|
+
clipFrameToTerminalHeight: true,
|
|
57
|
+
});
|
|
58
|
+
// Skip cleanly on unpatched ink (fork-only APIs).
|
|
59
|
+
if (!mounted.setFrameAnchorActive || !mounted.insertBeforeFrame) {
|
|
60
|
+
mounted.unmount();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
mounted.setFrameShrinkBackfill?.((needRows) => {
|
|
64
|
+
const lines = transcriptRows.slice(-needRows);
|
|
65
|
+
while (lines.length < needRows)
|
|
66
|
+
lines.unshift("");
|
|
67
|
+
return `${lines.join("\n")}\n`;
|
|
68
|
+
});
|
|
69
|
+
await tick();
|
|
70
|
+
// Prior turn finished streaming a tall response; the slash menu is open
|
|
71
|
+
// because the user typed "/commit". Anchor is ACTIVE (agent considered
|
|
72
|
+
// running through the submit). Flush the scrollback history first.
|
|
73
|
+
mounted.setFrameAnchorActive(true);
|
|
74
|
+
mounted.insertBeforeFrame(`${transcriptRows.join("\n")}\n`);
|
|
75
|
+
mounted.rerender(_jsx(Controls, { liveRows: 10, menuOpen: true }));
|
|
76
|
+
await tick();
|
|
77
|
+
// SUBMIT: prior live rows flush + clear (live shrinks to 1) AND the slash
|
|
78
|
+
// menu closes — two stacked shrinks with NO compensating scrollback insert,
|
|
79
|
+
// while the anchor is active. Pad debt forms.
|
|
80
|
+
mounted.rerender(_jsx(Controls, { liveRows: 1, menuOpen: false }));
|
|
81
|
+
await tick();
|
|
82
|
+
// Quiet thinking phase: nothing grows the frame. The gap is now on screen
|
|
83
|
+
// BEFORE any LLM output — the reported bug.
|
|
84
|
+
const gapAtSubmit = blanksAboveFrame(recorder);
|
|
85
|
+
expect(gapAtSubmit, "gap genuinely forms at the submit boundary").toBeGreaterThan(0);
|
|
86
|
+
// App's mid-run reclaim pulse (fires after the frame is stable): off→on.
|
|
87
|
+
mounted.setFrameAnchorActive(false);
|
|
88
|
+
mounted.setFrameAnchorActive(true);
|
|
89
|
+
await tick(150);
|
|
90
|
+
const lines = recorder.viewportLines().map((line) => stripAnsi(line));
|
|
91
|
+
const frameTopIdx = lines.findIndex((line) => line.includes(FRAME_TOP));
|
|
92
|
+
const footerIdx = lines.findIndex((line) => line.includes(FOOTER));
|
|
93
|
+
expect(frameTopIdx, "live frame visible").toBeGreaterThanOrEqual(0);
|
|
94
|
+
expect(footerIdx, "footer visible").toBeGreaterThan(frameTopIdx);
|
|
95
|
+
// The gap is gone.
|
|
96
|
+
expect(blanksAboveFrame(recorder), "reclaim pulse collapses the submit-boundary gap").toBe(0);
|
|
97
|
+
// Footer still bottom-pinned (nothing below it).
|
|
98
|
+
const lastNonBlank = lines.reduce((acc, line, i) => (line.trim().length > 0 ? i : acc), -1);
|
|
99
|
+
expect(lastNonBlank, "nothing below the footer after reclaim").toBe(footerIdx);
|
|
100
|
+
mounted.unmount();
|
|
101
|
+
});
|
|
102
|
+
it("reclaims a finishing tool-batch collapse mid-run (no menu involved)", async () => {
|
|
103
|
+
const recorder = new ScreenRecorder({ columns: COLUMNS, rows: ROWS });
|
|
104
|
+
const stdout = makeRecordingStdout(recorder);
|
|
105
|
+
const transcriptRows = Array.from({ length: ROWS }, (_, i) => `SIM_HISTORY_${i + 1}`);
|
|
106
|
+
const mounted = render(_jsx(Controls, { liveRows: 2, menuOpen: false }), {
|
|
107
|
+
stdout,
|
|
108
|
+
patchConsole: false,
|
|
109
|
+
maxFps: 1000,
|
|
110
|
+
anchorFrameToBottom: true,
|
|
111
|
+
clipFrameToTerminalHeight: true,
|
|
112
|
+
});
|
|
113
|
+
if (!mounted.setFrameAnchorActive || !mounted.insertBeforeFrame) {
|
|
114
|
+
mounted.unmount();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
mounted.setFrameShrinkBackfill?.((needRows) => {
|
|
118
|
+
const lines = transcriptRows.slice(-needRows);
|
|
119
|
+
while (lines.length < needRows)
|
|
120
|
+
lines.unshift("");
|
|
121
|
+
return `${lines.join("\n")}\n`;
|
|
122
|
+
});
|
|
123
|
+
await tick();
|
|
124
|
+
// Mid-run: a batch of tool panels expands the live frame, then collapses
|
|
125
|
+
// when the batch finishes — a shrink with no compensating insert. Debt forms.
|
|
126
|
+
mounted.setFrameAnchorActive(true);
|
|
127
|
+
mounted.insertBeforeFrame(`${transcriptRows.join("\n")}\n`);
|
|
128
|
+
mounted.rerender(_jsx(Controls, { liveRows: 11, menuOpen: false }));
|
|
129
|
+
await tick();
|
|
130
|
+
mounted.rerender(_jsx(Controls, { liveRows: 2, menuOpen: false }));
|
|
131
|
+
await tick();
|
|
132
|
+
expect(blanksAboveFrame(recorder), "tool-batch collapse forms a gap").toBeGreaterThan(0);
|
|
133
|
+
// Reclaim pulse collapses it.
|
|
134
|
+
mounted.setFrameAnchorActive(false);
|
|
135
|
+
mounted.setFrameAnchorActive(true);
|
|
136
|
+
await tick(150);
|
|
137
|
+
expect(blanksAboveFrame(recorder), "tool-batch gap reclaimed").toBe(0);
|
|
138
|
+
mounted.unmount();
|
|
139
|
+
});
|
|
140
|
+
it("is a harmless no-op when there is no pad debt (steady streaming)", async () => {
|
|
141
|
+
const recorder = new ScreenRecorder({ columns: COLUMNS, rows: ROWS });
|
|
142
|
+
const stdout = makeRecordingStdout(recorder);
|
|
143
|
+
const transcriptRows = Array.from({ length: ROWS }, (_, i) => `SIM_HISTORY_${i + 1}`);
|
|
144
|
+
const mounted = render(_jsx(Controls, { liveRows: 2, menuOpen: false }), {
|
|
145
|
+
stdout,
|
|
146
|
+
patchConsole: false,
|
|
147
|
+
maxFps: 1000,
|
|
148
|
+
anchorFrameToBottom: true,
|
|
149
|
+
clipFrameToTerminalHeight: true,
|
|
150
|
+
});
|
|
151
|
+
if (!mounted.setFrameAnchorActive || !mounted.insertBeforeFrame) {
|
|
152
|
+
mounted.unmount();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
mounted.setFrameShrinkBackfill?.((needRows) => {
|
|
156
|
+
const lines = transcriptRows.slice(-needRows);
|
|
157
|
+
while (lines.length < needRows)
|
|
158
|
+
lines.unshift("");
|
|
159
|
+
return `${lines.join("\n")}\n`;
|
|
160
|
+
});
|
|
161
|
+
await tick();
|
|
162
|
+
// Anchor active, frame only GROWS (no shrink) — no debt is ever created.
|
|
163
|
+
mounted.setFrameAnchorActive(true);
|
|
164
|
+
mounted.rerender(_jsx(Controls, { liveRows: 4, menuOpen: false }));
|
|
165
|
+
await tick();
|
|
166
|
+
expect(blanksAboveFrame(recorder), "no gap during pure growth").toBe(0);
|
|
167
|
+
// A reclaim pulse here must change nothing: footer pinned, no gap, frame
|
|
168
|
+
// unchanged — proving the pulse is a safe no-op when debt is zero.
|
|
169
|
+
mounted.setFrameAnchorActive(false);
|
|
170
|
+
mounted.setFrameAnchorActive(true);
|
|
171
|
+
await tick(150);
|
|
172
|
+
const lines = recorder.viewportLines().map((line) => stripAnsi(line));
|
|
173
|
+
const footerIdx = lines.findIndex((line) => line.includes(FOOTER));
|
|
174
|
+
const lastNonBlank = lines.reduce((acc, line, i) => (line.trim().length > 0 ? i : acc), -1);
|
|
175
|
+
expect(blanksAboveFrame(recorder), "still no gap after no-op pulse").toBe(0);
|
|
176
|
+
expect(lastNonBlank, "footer still pinned after no-op pulse").toBe(footerIdx);
|
|
177
|
+
mounted.unmount();
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
//# sourceMappingURL=submit-boundary-gap.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"submit-boundary-gap.test.js","sourceRoot":"","sources":["../../src/ui/submit-boundary-gap.test.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-history.d.ts","sourceRoot":"","sources":["../../src/ui/terminal-history.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAK9C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"terminal-history.d.ts","sourceRoot":"","sources":["../../src/ui/terminal-history.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAK9C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AA8F9C,MAAM,WAAW,sBAAsB;IACrC,KAAK,CACH,KAAK,EAAE,SAAS,aAAa,EAAE,EAC/B,OAAO,EAAE,sBAAsB,EAC/B,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,GAC5D,IAAI,CAAC;IACR,KAAK,IAAI,IAAI,CAAC;IACd,YAAY,IAAI,IAAI,CAAC;IACrB,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,6BAA6B;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC,WAAW,CAAC;CAC7B;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,QAAQ,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;CACb;AASD,wBAAgB,4BAA4B,CAAC,EAC3C,MAAuB,GACxB,GAAE,6BAAkC,GAAG,sBAAsB,CAsH7D;AAED,wBAAgB,uCAAuC,CACrD,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,sBAAsB,GAC9B,MAAM,CA+IR"}
|
|
@@ -10,7 +10,7 @@ import { buildToolGroupSummary } from "./tool-group-summary.js";
|
|
|
10
10
|
import path from "node:path";
|
|
11
11
|
import { pathToFileURL } from "node:url";
|
|
12
12
|
import { renderMarkdownToAnsiLines } from "./utils/markdown-renderer.js";
|
|
13
|
-
import { detectGraphicsProtocol,
|
|
13
|
+
import { detectGraphicsProtocol, encodeInlineImageBlock } from "./utils/terminal-graphics.js";
|
|
14
14
|
import { createHyperlink } from "./utils/hyperlink.js";
|
|
15
15
|
import { supportsHyperlinks } from "./utils/supports-hyperlinks.js";
|
|
16
16
|
import { shouldSeparateTranscriptItems } from "./transcript/spacing.js";
|
|
@@ -51,6 +51,9 @@ const GRADIENT = [
|
|
|
51
51
|
const GAP = " ";
|
|
52
52
|
const LOGO_WIDTH = 9;
|
|
53
53
|
const SIDE_BY_SIDE_MIN = LOGO_WIDTH + GAP.length + 62;
|
|
54
|
+
// Row index in the logo block where each info line is placed. The EZ art is
|
|
55
|
+
// 3 rows tall, so info lines sit one-per-row beside it from the top.
|
|
56
|
+
const INFO_ANCHOR_ROW = 0;
|
|
54
57
|
const COMPACT_TOOLS = new Set(["read", "grep", "find", "ls", "source_path"]);
|
|
55
58
|
const STATE_TOOLS = new Set(["tasks"]);
|
|
56
59
|
const SERVER_STYLE_TOOLS = new Set(["web_search"]);
|
|
@@ -142,7 +145,10 @@ export function createTerminalHistoryPrinter({ stream = process.stdout, } = {})
|
|
|
142
145
|
const canLink = supportsHyperlinks();
|
|
143
146
|
for (const preview of previews) {
|
|
144
147
|
if (protocol !== "none") {
|
|
145
|
-
|
|
148
|
+
// Fixed-height block whose newline count equals its visual rows —
|
|
149
|
+
// a raw escape (many rows, zero newlines) desyncs Ink's live-frame
|
|
150
|
+
// erase math and strands orphaned rows around the image.
|
|
151
|
+
writeOutput(`\n${encodeInlineImageBlock(preview.base64, protocol, { leftPad: imageLeftPad })}\n`);
|
|
146
152
|
}
|
|
147
153
|
// Clickable "open" affordance — Cmd/Ctrl-click opens the file in the
|
|
148
154
|
// OS default viewer. The pixels themselves aren't clickable, so the
|
|
@@ -311,13 +317,19 @@ function renderBanner(context) {
|
|
|
311
317
|
"",
|
|
312
318
|
]);
|
|
313
319
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
`${
|
|
318
|
-
`${
|
|
319
|
-
|
|
320
|
-
]
|
|
320
|
+
// Info lines rendered beside the (taller) logo. They're anchored starting at
|
|
321
|
+
// INFO_ANCHOR_ROW so the text column sits vertically centered next to the art.
|
|
322
|
+
const infoLines = [
|
|
323
|
+
`${color(context.theme.primary, "EZ Coder", true)}${dim(context, ` v${context.version} · By `)}${color(context.theme.text, "Nolan Grout", true)}`,
|
|
324
|
+
`${color(context.theme.secondary, modelName)} ${dim(context, truncatePlain(displayPath, Math.max(10, context.columns - LOGO_WIDTH - GAP.length - stringWidth(modelName) - 2)))}`,
|
|
325
|
+
shortcuts,
|
|
326
|
+
];
|
|
327
|
+
const rows = logo.map((logoLine, i) => {
|
|
328
|
+
const infoIndex = i - INFO_ANCHOR_ROW;
|
|
329
|
+
const info = infoIndex >= 0 && infoIndex < infoLines.length ? infoLines[infoIndex] : undefined;
|
|
330
|
+
return info === undefined ? logoLine : `${logoLine}${GAP}${info}`;
|
|
331
|
+
});
|
|
332
|
+
return block(["", ...rows, ""]);
|
|
321
333
|
}
|
|
322
334
|
function renderGoalProgress(item, context) {
|
|
323
335
|
const isError = item.status === "failed" || item.status === "fail" || item.status === "blocked";
|
|
@@ -860,6 +872,13 @@ function getServerToolHeaderParts(name, input) {
|
|
|
860
872
|
function getBashExitCode(result) {
|
|
861
873
|
return result.match(/^Exit code: (.+)/)?.[1]?.trim() ?? "0";
|
|
862
874
|
}
|
|
875
|
+
/**
|
|
876
|
+
* At-a-glance header chip mirroring the live ToolExecution chips:
|
|
877
|
+
* - bash → exit-code chip (`✓ 0` / `✗ N`)
|
|
878
|
+
* - edit → diff-stat chip (`+a −r`)
|
|
879
|
+
* Returns "" when no chip applies. Spacing (two leading spaces per segment)
|
|
880
|
+
* matches the live renderer so live/history parity holds.
|
|
881
|
+
*/
|
|
863
882
|
function renderHeaderChip(name, result, isError, context) {
|
|
864
883
|
if (name === "bash" && !isError) {
|
|
865
884
|
const exitMatch = result.split("\n")[0]?.match(/^Exit code: (.+)/);
|