@iowarp/clio-coder 0.4.5 → 0.4.6
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/CHANGELOG.md +24 -0
- package/CONTRIBUTING.md +14 -9
- package/NOTICE +16 -0
- package/README.md +312 -685
- package/dist/{acp-H3CU2HBQ.js → acp-UVEVWSDL.js} +6 -6
- package/dist/{agents-IXQQZ7BD.js → agents-VWY37DTK.js} +31 -30
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-R7KYF7N3.js → auth-PCV2XQDV.js} +6 -6
- package/dist/{builtins-CRFDQUVP.js → builtins-PD652MTK.js} +3 -3
- package/dist/{chunk-SDWNDICN.js → chunk-24T2YZ7A.js} +58 -56
- package/dist/{chunk-FSTIBAGM.js → chunk-2AKYFREM.js} +7 -7
- package/dist/{chunk-SE4ZKILO.js → chunk-3BJGMT2D.js} +30 -9
- package/dist/{chunk-GVYWZNRK.js → chunk-3HQVBIKD.js} +2 -2
- package/dist/{chunk-EWD5P3GK.js → chunk-3JLFK5MW.js} +4 -4
- package/dist/{chunk-O2RQ7LCK.js → chunk-3O2ZAUGQ.js} +2 -2
- package/dist/{chunk-GXCCHKN6.js → chunk-3QKXHCGS.js} +4 -4
- package/dist/{chunk-YEJIG7LN.js → chunk-3WBALRQH.js} +7 -7
- package/dist/{chunk-I7VLWRK2.js → chunk-4DU2TVI4.js} +5 -5
- package/dist/{chunk-TEG3RYVZ.js → chunk-4U4TKZFR.js} +3 -3
- package/dist/{chunk-EHCPSZJF.js → chunk-4VMV6OS6.js} +2 -2
- package/dist/{chunk-T7FDXNRU.js → chunk-536HXRFE.js} +4 -4
- package/dist/{chunk-ZMZQEBKI.js → chunk-5QRX7SLS.js} +8 -8
- package/dist/{chunk-RXIOCVBZ.js → chunk-6HAO6X5B.js} +4 -4
- package/dist/{chunk-HGSLRG33.js → chunk-6OII3LOK.js} +2 -2
- package/dist/{chunk-U2JOLB7O.js → chunk-6RYDAVQT.js} +32 -32
- package/dist/{chunk-T53XI6I7.js → chunk-6VRRJT7L.js} +2 -2
- package/dist/{chunk-RLTT5GVY.js → chunk-7JSSKXG2.js} +4 -4
- package/dist/{chunk-633EXGZA.js → chunk-ACCKVSHU.js} +3 -3
- package/dist/{chunk-6SYEGZI3.js → chunk-B3MOMLJ4.js} +2 -2
- package/dist/{chunk-UXAC2FX4.js → chunk-BLTEAUCB.js} +3 -3
- package/dist/{chunk-DRD2A54W.js → chunk-BT7HGDOX.js} +15 -15
- package/dist/{chunk-F34AR6O3.js → chunk-DXLUVUD4.js} +5 -5
- package/dist/{chunk-HR3HPQ76.js → chunk-E7RF4KBO.js} +2 -2
- package/dist/chunk-EM2WNQS2.js +93 -0
- package/dist/{chunk-KYKNBD4Y.js → chunk-F5Z7EWMN.js} +953 -233
- package/dist/{chunk-VLPTKCKT.js → chunk-GLYBJ4RQ.js} +3 -3
- package/dist/{chunk-KDWLXVMN.js → chunk-HPTJF6DG.js} +16 -39
- package/dist/{chunk-6CDNRHWH.js → chunk-HZ2T2YED.js} +2 -2
- package/dist/{chunk-UTUNT5QL.js → chunk-HZFBVU5X.js} +7 -7
- package/dist/{chunk-KF6GFW6O.js → chunk-I2K2NCZQ.js} +7 -7
- package/dist/{chunk-5DDOTTG4.js → chunk-IGLVVNEG.js} +2 -2
- package/dist/{chunk-4SVIVOMC.js → chunk-IKRWLEDA.js} +5 -5
- package/dist/{chunk-R6MAYTMS.js → chunk-JMPL7NUB.js} +3 -3
- package/dist/{chunk-5ZMXGW7P.js → chunk-KAFMW7WM.js} +4 -85
- package/dist/{chunk-R6MICQYU.js → chunk-LIN2K3EL.js} +4 -4
- package/dist/{chunk-QEQISF75.js → chunk-LK2FN5JY.js} +1 -1
- package/dist/{chunk-YYB3MC65.js → chunk-N2OKBREN.js} +2 -2
- package/dist/{chunk-XXBQKW32.js → chunk-NNWJYUNO.js} +4 -4
- package/dist/{chunk-TZ7MFRG4.js → chunk-P5JMBPDJ.js} +22 -20
- package/dist/{chunk-WFKDPU7U.js → chunk-P6M7RKMI.js} +2 -2
- package/dist/{chunk-MDU3C27C.js → chunk-PZN2LQY6.js} +5 -5
- package/dist/{chunk-S32UQ5GL.js → chunk-Q7NNUHQV.js} +15 -15
- package/dist/{chunk-U7VQ4NI4.js → chunk-QCOV5DFY.js} +5 -5
- package/dist/{chunk-MC3JSVR6.js → chunk-QJMRYTCX.js} +7 -7
- package/dist/{chunk-DIMRLNIC.js → chunk-QNWRHOMF.js} +3 -3
- package/dist/{chunk-ILJ7DWGE.js → chunk-QXFBABFJ.js} +4 -4
- package/dist/chunk-R2CT5YDO.js +66 -0
- package/dist/{chunk-E3BQTQUP.js → chunk-RGZNQ5CT.js} +3 -3
- package/dist/{chunk-SBZX6ARC.js → chunk-SXQKNKRA.js} +7 -7
- package/dist/{chunk-WLMSOT73.js → chunk-TJFDL26W.js} +3 -3
- package/dist/{chunk-EQEJJ53P.js → chunk-VAHLPXPY.js} +2 -2
- package/dist/{chunk-7TKWWPD7.js → chunk-VANN3GDP.js} +3 -23
- package/dist/{chunk-GNQRWTMY.js → chunk-VHMX7SXF.js} +2 -2
- package/dist/{chunk-UL36QJX6.js → chunk-WKUYQBCG.js} +6 -6
- package/dist/{chunk-FKIOX6DS.js → chunk-WNGF5AJX.js} +9 -4
- package/dist/{chunk-ICO4TQ2F.js → chunk-WV6TDAPN.js} +36 -20
- package/dist/{chunk-K6RDB7IA.js → chunk-X3BI7HTV.js} +9 -9
- package/dist/{chunk-NY6IWDVU.js → chunk-XWMSPJYN.js} +2 -2
- package/dist/{chunk-7HBHHVCK.js → chunk-YPRSNUTC.js} +2 -2
- package/dist/{chunk-ONRTXOZY.js → chunk-ZS43Y2FT.js} +2 -2
- package/dist/cli/index.js +27 -27
- package/dist/{clio-5I2IBFYL.js → clio-QQCMTUZE.js} +6 -6
- package/dist/{code-nav-YXIAEZVS.js → code-nav-GUPM6766.js} +8 -8
- package/dist/{config-Y2JDJ3JJ.js → config-IIM7YOD3.js} +42 -41
- package/dist/{configure-SML6UBZ5.js → configure-4ZM5HWJX.js} +28 -10
- package/dist/{context-S2EEC3EJ.js → context-44BZLJ3J.js} +12 -12
- package/dist/{context-P5YSEO55.js → context-AIDVQKMW.js} +34 -33
- package/dist/{context-QQ53HSGJ.js → context-OL5P3HEP.js} +25 -24
- package/dist/{context-clear-TMEQCOTH.js → context-clear-OQV76MYP.js} +34 -33
- package/dist/{context-working-set-CNLSQN5K.js → context-working-set-ZUW3EGLS.js} +10 -9
- package/dist/{detail-5572VLBO.js → detail-UXPWRANG.js} +35 -34
- package/dist/{dispatch-runner-FRHD6P2O.js → dispatch-runner-P3ZW6G77.js} +38 -37
- package/dist/{doctor-N3LCM2LG.js → doctor-JJVGPZWT.js} +18 -17
- package/dist/{eval-BUU3ZOUZ.js → eval-V2RTN3UH.js} +26 -25
- package/dist/{evidence-DTGEN456.js → evidence-35PYWGSO.js} +34 -33
- package/dist/{evidence-SQ5DNFEN.js → evidence-CIGGKI6J.js} +36 -35
- package/dist/{evolve-IJNUFZDS.js → evolve-33SM3NJP.js} +34 -33
- package/dist/{fleet-ZNNVSYFO.js → fleet-LFO7SIPC.js} +62 -61
- package/dist/{fleet-commands-5YE7NPJL.js → fleet-commands-RLK72GU3.js} +9 -9
- package/dist/fleet-decisions-3SBSCTCW.js +1 -1
- package/dist/{fleet-graph-6HHPWF5T.js → fleet-graph-JZIBCGMW.js} +11 -11
- package/dist/{fleet-inspect-JEYMGCGC.js → fleet-inspect-2L5YGGZJ.js} +35 -34
- package/dist/{fleet-validate-4PM4GDDL.js → fleet-validate-LIAVKDUU.js} +12 -12
- package/dist/{fleet-verify-NJDTHTO3.js → fleet-verify-JIK3OQZR.js} +34 -33
- package/dist/{fleet-view-UP2BWQQY.js → fleet-view-IWHYGQKK.js} +35 -34
- package/dist/{init-S3GZQ5ZF.js → init-NJEDOTQW.js} +46 -45
- package/dist/{interop-YOTGXFJM.js → interop-RVCAYECG.js} +4 -4
- package/dist/{inventory-K2OZ36YJ.js → inventory-74UL7UHU.js} +35 -34
- package/dist/{library-GJI6W6OK.js → library-CTKZA7K3.js} +10 -10
- package/dist/{memory-OQOXQGYD.js → memory-I76I5VUH.js} +34 -33
- package/dist/{models-3IK2Y5WE.js → models-D4YDIQZQ.js} +19 -18
- package/dist/{monitor-FTJLTKCQ.js → monitor-NKVD55HX.js} +40 -39
- package/dist/{orchestrator-N47O7SFD.js → orchestrator-I6OATVPK.js} +658 -1092
- package/dist/{preload-65U34NG6.js → preload-EJAWJEIO.js} +34 -33
- package/dist/{reset-MUVQDLNL.js → reset-4XMQKDNZ.js} +5 -3
- package/dist/{resources-LM76COAO.js → resources-TVYVXK3Q.js} +10 -10
- package/dist/{run-3VFZQ33N.js → run-OWQNNSJU.js} +61 -60
- package/dist/{share-4JUB4BL7.js → share-NPZZMUXG.js} +11 -11
- package/dist/{skills-S36AAJHU.js → skills-OXFEAWMT.js} +12 -12
- package/dist/{skills-eval-G6T57L7M.js → skills-eval-J5TCKSJA.js} +38 -37
- package/dist/{skills-inventory-WLM46PJH.js → skills-inventory-7II4DM57.js} +10 -10
- package/dist/{slash-commands-GBARG3Y7.js → slash-commands-MDSDSWYF.js} +26 -25
- package/dist/{targets-N5REUETU.js → targets-KILZEOCE.js} +36 -20
- package/dist/{tasks-6BVY5YZF.js → tasks-R6P6OV22.js} +7 -7
- package/dist/{terminal-lease-57UEOJKS.js → terminal-lease-LZCG2KN3.js} +2 -2
- package/dist/{upgrade-4HD5HAIV.js → upgrade-D2OZH6IQ.js} +9 -9
- package/dist/{usage-4GWM2WWP.js → usage-PIASNUS4.js} +40 -39
- package/dist/{verifiers-GSKWKWVG.js → verifiers-LTHUY2PX.js} +9 -9
- package/dist/{verify-3AS3ULCY.js → verify-HYF7KBVE.js} +7 -7
- package/dist/{wiki-generate-7BV7XWIY.js → wiki-generate-RK7JQ5NP.js} +51 -50
- package/dist/worker/entry.js +41 -39
- package/docs/architecture/context-engine.md +1 -1
- package/docs/architecture/tui-design.md +45 -30
- package/docs/guide/commands-and-modes.md +26 -25
- package/docs/guide/configuration-and-targets.md +122 -27
- package/docs/guide/configuration-reference.md +9 -4
- package/docs/guide/fleet-dispatch.md +1 -6
- package/docs/guide/glossary.md +1 -1
- package/docs/guide/installation-and-lifecycle.md +44 -11
- package/docs/process/development-pipeline.md +1 -1
- package/docs/process/documentation-coverage.md +1 -1
- package/docs/process/release-cut-checklist.md +18 -6
- package/package.json +1 -1
- package/src/cli/ask.ts +10 -10
- package/src/cli/configure-editor.ts +51 -0
- package/src/cli/configure-interop.ts +1 -1
- package/src/cli/configure-oauth.ts +1 -1
- package/src/cli/configure-onboarding.ts +232 -96
- package/src/cli/configure-prompts.ts +76 -0
- package/src/cli/configure-quick.ts +274 -0
- package/src/cli/configure-target.ts +14 -2
- package/src/cli/configure.ts +413 -174
- package/src/cli/oauth-manual-input.ts +1 -1
- package/src/cli/reset.ts +2 -0
- package/src/cli/select.ts +35 -4
- package/src/cli/upgrade.ts +2 -2
- package/src/core/config.ts +6 -1
- package/src/core/defaults.ts +33 -11
- package/src/{interactive → core}/external-editor.ts +4 -4
- package/src/domains/config/classify.ts +1 -1
- package/src/domains/config/keybindings.ts +3 -31
- package/src/domains/providers/probe/fingerprint.ts +13 -6
- package/src/domains/providers/probe/reasoning.ts +2 -0
- package/src/domains/providers/runtimes/common/probe-helpers.ts +23 -7
- package/src/domains/providers/runtimes/protocol/anthropic-compat.ts +7 -2
- package/src/domains/providers/runtimes/protocol/openai-compat.ts +10 -4
- package/src/interactive/application-controller.ts +1 -31
- package/src/interactive/chat-panel.ts +163 -637
- package/src/interactive/chat-renderer.ts +13 -30
- package/src/interactive/dispatch-board.ts +1 -3
- package/src/interactive/editor-submit.ts +8 -31
- package/src/interactive/footer/dashboard.ts +8 -3
- package/src/interactive/footer/widgets.ts +50 -91
- package/src/interactive/interactive-application.ts +16 -42
- package/src/interactive/interactive-event-projection.ts +2 -44
- package/src/interactive/interactive-input-runtime.ts +3 -28
- package/src/interactive/interactive-presentation.ts +30 -9
- package/src/interactive/interactive-slash-runtime.ts +3 -40
- package/src/interactive/layout.ts +29 -4
- package/src/interactive/overlay-general-openers.ts +2 -0
- package/src/interactive/overlay-lifecycle.ts +1 -0
- package/src/interactive/overlay-session-lifecycle.ts +1 -1
- package/src/interactive/overlays/settings.ts +9 -8
- package/src/interactive/renderers/preview.ts +16 -0
- package/src/interactive/renderers/tool-execution.ts +67 -160
- package/src/interactive/renderers/worker-entry.ts +57 -41
- package/src/interactive/slash-autocomplete.ts +1 -6
- package/src/interactive/slash-commands.ts +7 -39
- package/src/interactive/status/controller.ts +0 -5
- package/src/interactive/status/state-machine.ts +44 -26
- package/src/interactive/status/types.ts +3 -0
- package/src/interactive/status/verbs.ts +13 -12
- package/src/interactive/transcript-detail.ts +50 -112
- package/src/interactive/view/artifacts.ts +4 -0
- package/src/interactive/view/view-overlay.ts +44 -34
- package/src/tools/presentation.ts +1 -1
- package/src/tools/registry.ts +1 -1
- package/src/tools/verify/numeric.ts +4 -4
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
import type { ClioSettings } from "../core/config.js";
|
|
12
12
|
import type { SafeEventBus } from "../core/event-bus.js";
|
|
13
13
|
import { routingChangeNotices } from "../core/session-routing.js";
|
|
14
|
-
import { visibleWidth } from "../engine/tui.js";
|
|
15
14
|
import {
|
|
16
15
|
budgetAlertNotice,
|
|
17
16
|
middlewareHookFailedSessionNotice,
|
|
@@ -26,27 +25,11 @@ import {
|
|
|
26
25
|
toolBudgetAuditReason,
|
|
27
26
|
toolBudgetStopReason,
|
|
28
27
|
} from "./loop-guard-interrupt.js";
|
|
29
|
-
import {
|
|
30
|
-
type AgentStatus,
|
|
31
|
-
INLINE_STATUS_INDENT_COLS,
|
|
32
|
-
type ReasoningUsageView,
|
|
33
|
-
reasoningFromTally,
|
|
34
|
-
resolveInlineVerb,
|
|
35
|
-
type StatusPhase,
|
|
36
|
-
spinnerFrame,
|
|
37
|
-
type TurnSummary,
|
|
38
|
-
type VerbRender,
|
|
39
|
-
} from "./status/index.js";
|
|
28
|
+
import type { AgentStatus, TurnSummary } from "./status/index.js";
|
|
40
29
|
|
|
41
30
|
export type InteractiveProjectionNoticeLevel = "info" | "success" | "warning" | "error";
|
|
42
31
|
export type InteractiveTranscriptNoticeLevel = "info" | "success" | "warn" | "error";
|
|
43
32
|
|
|
44
|
-
export interface InteractiveStatusLine {
|
|
45
|
-
phase: StatusPhase;
|
|
46
|
-
verb: string;
|
|
47
|
-
toneHint: VerbRender["toneHint"];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
33
|
export interface InteractiveEventProjectionDeps {
|
|
51
34
|
bus: SafeEventBus;
|
|
52
35
|
chat: {
|
|
@@ -69,12 +52,10 @@ export interface InteractiveEventProjectionDeps {
|
|
|
69
52
|
resetAskUserCancellation: () => void;
|
|
70
53
|
recordToolStart: (toolName: string, toolCallId: string) => void;
|
|
71
54
|
recordToolEnd: (toolName: string, toolCallId: string, isError: boolean, truncated: boolean) => void;
|
|
72
|
-
setStatusLine: (line: InteractiveStatusLine | null) => void;
|
|
73
55
|
/**
|
|
74
56
|
* Publish the live turn's reasoning projection to the transcript. Optional so
|
|
75
57
|
* a host without a chat panel still gets every other projection.
|
|
76
58
|
*/
|
|
77
|
-
setLiveReasoning?: (view: ReasoningUsageView | null) => void;
|
|
78
59
|
setLastTurnSummary: (summary: TurnSummary) => void;
|
|
79
60
|
startTerminalProgress: () => void;
|
|
80
61
|
stopTerminalProgress: () => void;
|
|
@@ -126,8 +107,6 @@ function askUserInterviewClosedByToolResult(event: {
|
|
|
126
107
|
* subscriptions.
|
|
127
108
|
*/
|
|
128
109
|
export function createInteractiveEventProjection(deps: InteractiveEventProjectionDeps): InteractiveEventProjection {
|
|
129
|
-
const now = deps.now ?? Date.now;
|
|
130
|
-
|
|
131
110
|
for (const notice of deps.initialNotices ?? []) {
|
|
132
111
|
const text = notice.trim();
|
|
133
112
|
if (text.length === 0) continue;
|
|
@@ -187,30 +166,9 @@ export function createInteractiveEventProjection(deps: InteractiveEventProjectio
|
|
|
187
166
|
}),
|
|
188
167
|
);
|
|
189
168
|
|
|
190
|
-
let statusInlineFrame = 0;
|
|
191
169
|
primaryUnsubscribers.push(
|
|
192
170
|
deps.status.subscribe((status) => {
|
|
193
|
-
|
|
194
|
-
// line, the turn receipt, and the footer state the same number.
|
|
195
|
-
deps.setLiveReasoning?.(
|
|
196
|
-
status.phase === "idle" || status.phase === "ended" ? null : reasoningFromTally(status.runTally),
|
|
197
|
-
);
|
|
198
|
-
if (status.phase === "idle") {
|
|
199
|
-
deps.setStatusLine(null);
|
|
200
|
-
} else if (status.phase === "ended") {
|
|
201
|
-
deps.setStatusLine(null);
|
|
202
|
-
if (status.summary) deps.setLastTurnSummary(status.summary);
|
|
203
|
-
} else {
|
|
204
|
-
const cols = deps.getTerminalColumns();
|
|
205
|
-
const frame = cols < 30 ? "" : `${spinnerFrame(statusInlineFrame)} `;
|
|
206
|
-
const verb = resolveInlineVerb(status, now(), cols, INLINE_STATUS_INDENT_COLS + visibleWidth(frame));
|
|
207
|
-
if (verb) {
|
|
208
|
-
deps.setStatusLine({ phase: status.phase, verb: `${frame}${verb.text}`, toneHint: verb.toneHint });
|
|
209
|
-
statusInlineFrame = (statusInlineFrame + 1) % 10;
|
|
210
|
-
} else {
|
|
211
|
-
deps.setStatusLine(null);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
171
|
+
if (status.phase === "ended" && status.summary) deps.setLastTurnSummary(status.summary);
|
|
214
172
|
deps.refreshFooter();
|
|
215
173
|
deps.requestRender();
|
|
216
174
|
}),
|
|
@@ -30,11 +30,7 @@ export interface InteractiveInputKeyActionDeps {
|
|
|
30
30
|
cycleScopedModelForward: () => void;
|
|
31
31
|
cycleScopedModelBackward: () => void;
|
|
32
32
|
dismissNotifications: () => void;
|
|
33
|
-
|
|
34
|
-
toggleAllToolExpansion: () => void;
|
|
35
|
-
toggleLiveToolOutput: () => void;
|
|
36
|
-
toggleThinkingExpansion: () => void;
|
|
37
|
-
toggleAllThinkingExpansion: () => void;
|
|
33
|
+
cycleOutputStyle: () => void;
|
|
38
34
|
openExternalEditor: () => void;
|
|
39
35
|
queueFollowUp: () => void;
|
|
40
36
|
interruptWithMessage: () => void;
|
|
@@ -49,6 +45,7 @@ export interface InteractiveInputRuntimeDeps {
|
|
|
49
45
|
dispatchAction: (id: ClioKeybinding, deps: InteractiveInputKeyActionDeps) => boolean;
|
|
50
46
|
actions: {
|
|
51
47
|
canExit(): boolean;
|
|
48
|
+
cycleOutputStyle(): void;
|
|
52
49
|
availableThinkingLevels(): ReadonlyArray<string>;
|
|
53
50
|
onCycleThinking(): void;
|
|
54
51
|
cycleScopedModelForward(): void;
|
|
@@ -105,13 +102,6 @@ export interface InteractiveInputRuntimeDeps {
|
|
|
105
102
|
dismiss(id: string): void;
|
|
106
103
|
dismissAll(): void;
|
|
107
104
|
};
|
|
108
|
-
chatPanel: {
|
|
109
|
-
toggleLastToolExpanded(): boolean;
|
|
110
|
-
toggleAllToolsExpanded(): boolean;
|
|
111
|
-
toggleLiveToolOutput(): void;
|
|
112
|
-
toggleLastThinking(): boolean;
|
|
113
|
-
toggleAllThinking(): boolean;
|
|
114
|
-
};
|
|
115
105
|
shutdown: {
|
|
116
106
|
stopTickers(): void;
|
|
117
107
|
disposeInteractiveTickers(): void;
|
|
@@ -171,18 +161,7 @@ export function createInteractiveInputRuntime(deps: InteractiveInputRuntimeDeps)
|
|
|
171
161
|
deps.requestRender();
|
|
172
162
|
},
|
|
173
163
|
dismissNotifications: () => controller.dismissNotifications(),
|
|
174
|
-
|
|
175
|
-
toggleAllToolExpansion: () => {
|
|
176
|
-
if (deps.chatPanel.toggleAllToolsExpanded()) deps.requestRender();
|
|
177
|
-
},
|
|
178
|
-
toggleLiveToolOutput: () => {
|
|
179
|
-
deps.chatPanel.toggleLiveToolOutput();
|
|
180
|
-
deps.requestRender();
|
|
181
|
-
},
|
|
182
|
-
toggleThinkingExpansion: () => controller.toggleThinkingExpansion(),
|
|
183
|
-
toggleAllThinkingExpansion: () => {
|
|
184
|
-
if (deps.chatPanel.toggleAllThinking()) deps.requestRender();
|
|
185
|
-
},
|
|
164
|
+
cycleOutputStyle: deps.actions.cycleOutputStyle,
|
|
186
165
|
openExternalEditor: deps.editorSubmit.openExternalEditorForInput,
|
|
187
166
|
queueFollowUp: deps.editorSubmit.queueFollowUpFromEditor,
|
|
188
167
|
interruptWithMessage: deps.editorSubmit.interruptFromEditor,
|
|
@@ -277,10 +256,6 @@ export function createInteractiveInputRuntime(deps: InteractiveInputRuntimeDeps)
|
|
|
277
256
|
listNotifications: () => deps.notifications.list(),
|
|
278
257
|
dismissNotification: (id) => deps.notifications.dismiss(id),
|
|
279
258
|
dismissAllNotifications: () => deps.notifications.dismissAll(),
|
|
280
|
-
toggleLastToolExpanded: () => deps.chatPanel.toggleLastToolExpanded(),
|
|
281
|
-
toggleAllToolsExpanded: () => deps.chatPanel.toggleAllToolsExpanded(),
|
|
282
|
-
toggleLastThinking: () => deps.chatPanel.toggleLastThinking(),
|
|
283
|
-
toggleAllThinking: () => deps.chatPanel.toggleAllThinking(),
|
|
284
259
|
shutdownDisposers: [
|
|
285
260
|
() => deps.shutdown.stopTickers(),
|
|
286
261
|
() => leaderKeys.dispose(),
|
|
@@ -17,7 +17,7 @@ import type { ResourcesContract } from "../domains/resources/index.js";
|
|
|
17
17
|
import { ceilChars, contentChars } from "../domains/session/context-accounting.js";
|
|
18
18
|
import type { SessionContract, TaskBoardSnapshot } from "../domains/session/index.js";
|
|
19
19
|
import type { UserTasksStore } from "../domains/user-tasks/store.js";
|
|
20
|
-
import type { Component, TUI } from "../engine/tui.js";
|
|
20
|
+
import type { Component, ScrollView, TUI } from "../engine/tui.js";
|
|
21
21
|
import type { ChatLoop, ChatLoopEvent } from "./chat-loop.js";
|
|
22
22
|
import { type ChatPanel, createChatPanel } from "./chat-panel.js";
|
|
23
23
|
import { type CoalescingChatRenderer, createCoalescingChatRenderer } from "./chat-renderer.js";
|
|
@@ -33,7 +33,7 @@ import { createNotificationCenter, type NotificationCenter } from "./footer/noti
|
|
|
33
33
|
import { getActiveRenderTrace } from "./interactive-shell.js";
|
|
34
34
|
import type { InteractiveNoticeLevel } from "./interactive-subscriptions.js";
|
|
35
35
|
import { type ClioKeybindingManager, createKeybindingManager } from "./keybinding-manager.js";
|
|
36
|
-
import { buildLayout } from "./layout.js";
|
|
36
|
+
import { buildLayout, preserveTranscriptScroll } from "./layout.js";
|
|
37
37
|
import type { SessionTranscript } from "./session-transcript.js";
|
|
38
38
|
import { createSlashCommandAutocompleteProvider } from "./slash-autocomplete.js";
|
|
39
39
|
import { parseSlashCommand, type RunIo } from "./slash-commands.js";
|
|
@@ -146,6 +146,8 @@ export interface InteractivePresentation {
|
|
|
146
146
|
chatRenderer: CoalescingChatRenderer;
|
|
147
147
|
io: RunIo;
|
|
148
148
|
root: Component;
|
|
149
|
+
changeOutputStyle(mutation: () => void): void;
|
|
150
|
+
setLocalBashRunning(running: boolean): void;
|
|
149
151
|
getObservabilitySnapshot(): ObservabilitySnapshot;
|
|
150
152
|
/** Fold one raw chat event into the ephemeral throughput shown only while this turn is active. */
|
|
151
153
|
recordChatEvent(event: ChatLoopEvent): void;
|
|
@@ -233,11 +235,8 @@ export function createInteractivePresentation(deps: InteractivePresentationDeps)
|
|
|
233
235
|
});
|
|
234
236
|
const renderTrace = getActiveRenderTrace();
|
|
235
237
|
const chatPanel = factories.createChatPanel({
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
return typeof first === "string" && first.length > 0 ? first : undefined;
|
|
239
|
-
},
|
|
240
|
-
getOutputVerbosity: () => deps.getSettings?.().interface.outputDetail ?? "default",
|
|
238
|
+
getOutputStyle: () => deps.getSettings?.().interface.outputDetail ?? "standard",
|
|
239
|
+
getTerminalRows: () => process.stdout.rows ?? 40,
|
|
241
240
|
...(renderTrace ? { onRenderMetrics: (metrics) => renderTrace.recordPanelRender(metrics) } : {}),
|
|
242
241
|
});
|
|
243
242
|
const followUpQueuePanel = factories.createFollowUpQueuePanel({
|
|
@@ -359,10 +358,21 @@ export function createInteractivePresentation(deps: InteractivePresentationDeps)
|
|
|
359
358
|
);
|
|
360
359
|
};
|
|
361
360
|
|
|
361
|
+
let localBashStartedAt: number | null = null;
|
|
362
362
|
const footerDeps: FooterDashboardDeps = {
|
|
363
363
|
providers: deps.providers,
|
|
364
364
|
...(deps.getSettings ? { getSettings: deps.getSettings } : {}),
|
|
365
|
-
getAgentStatus: () =>
|
|
365
|
+
getAgentStatus: () => {
|
|
366
|
+
const status = statusController.current();
|
|
367
|
+
if (localBashStartedAt === null || (status.phase !== "idle" && status.phase !== "ended")) return status;
|
|
368
|
+
return {
|
|
369
|
+
...status,
|
|
370
|
+
phase: "tool_running",
|
|
371
|
+
since: localBashStartedAt,
|
|
372
|
+
toolStartedAt: localBashStartedAt,
|
|
373
|
+
tool: { toolName: "bash", toolPreview: "local shell" },
|
|
374
|
+
};
|
|
375
|
+
},
|
|
366
376
|
getTerminalColumns: () => deps.terminal.columns,
|
|
367
377
|
getSessionTokens: () => observabilitySnapshot.session.tokens,
|
|
368
378
|
getTokenThroughput: () =>
|
|
@@ -533,6 +543,7 @@ export function createInteractivePresentation(deps: InteractivePresentationDeps)
|
|
|
533
543
|
},
|
|
534
544
|
}
|
|
535
545
|
: followUpQueuePanel;
|
|
546
|
+
let transcriptView: ScrollView | undefined;
|
|
536
547
|
const root = factories.buildLayout(
|
|
537
548
|
{
|
|
538
549
|
banner,
|
|
@@ -544,6 +555,9 @@ export function createInteractivePresentation(deps: InteractivePresentationDeps)
|
|
|
544
555
|
{
|
|
545
556
|
mode: settings.interface?.mode ?? "regular",
|
|
546
557
|
fullscreenScrollbar: settings.interface?.fullscreenScrollbar ?? "auto",
|
|
558
|
+
onTranscript: (view) => {
|
|
559
|
+
transcriptView = view;
|
|
560
|
+
},
|
|
547
561
|
},
|
|
548
562
|
);
|
|
549
563
|
deps.mount?.(root, editor);
|
|
@@ -553,7 +567,7 @@ export function createInteractivePresentation(deps: InteractivePresentationDeps)
|
|
|
553
567
|
deps.clearScheduledInterval ??
|
|
554
568
|
((handle: PresentationTickerHandle): void => clearInterval(handle as ReturnType<typeof setInterval>));
|
|
555
569
|
const footerTicker = scheduleInterval(() => {
|
|
556
|
-
const statusActive = statusController.current().phase !== "idle";
|
|
570
|
+
const statusActive = statusController.current().phase !== "idle" || localBashStartedAt !== null;
|
|
557
571
|
if (!deps.chat.isStreaming() && !statusActive && !footer.isExpanded()) return;
|
|
558
572
|
footer.refresh();
|
|
559
573
|
requestRender();
|
|
@@ -618,6 +632,13 @@ export function createInteractivePresentation(deps: InteractivePresentationDeps)
|
|
|
618
632
|
chatRenderer,
|
|
619
633
|
io,
|
|
620
634
|
root,
|
|
635
|
+
setLocalBashRunning(running) {
|
|
636
|
+
localBashStartedAt = running ? Date.now() : null;
|
|
637
|
+
footer.refresh();
|
|
638
|
+
requestRender();
|
|
639
|
+
},
|
|
640
|
+
changeOutputStyle: (mutation) =>
|
|
641
|
+
chatRenderer.mutate(() => preserveTranscriptScroll(transcriptView, deps.terminal.columns, mutation), "output-style"),
|
|
621
642
|
getObservabilitySnapshot: () => observabilitySnapshot,
|
|
622
643
|
recordChatEvent,
|
|
623
644
|
recordToolStart: (toolCallId, toolName) => {
|
|
@@ -64,7 +64,7 @@ export interface InteractiveSlashSubmitExpansion {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
type SlashChat = Pick<ChatLoop, "clearSkillSurface" | "getSessionId" | "isStreaming" | "submit">;
|
|
67
|
-
type SlashChatPanel = Pick<ChatPanel, "appendReplayBlock" | "appendUser"
|
|
67
|
+
type SlashChatPanel = Pick<ChatPanel, "appendReplayBlock" | "appendUser">;
|
|
68
68
|
type UserTurnStatus = import("./chat-panel.js").UserTurnStatus;
|
|
69
69
|
type SlashResources = Pick<ResourcesContract, "prompts" | "expandPromptTemplate" | "reload">;
|
|
70
70
|
type SlashExtensions = Pick<ExtensionsContract, "list">;
|
|
@@ -178,7 +178,6 @@ export interface InteractiveSlashRuntime {
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
/** Owns slash-command context construction and its asynchronous command state. */
|
|
181
|
-
const OUTPUT_VERBOSITIES: ReadonlyArray<ClioSettings["interface"]["outputDetail"]> = ["minimal", "default", "verbose"];
|
|
182
181
|
|
|
183
182
|
/** Thinking capability of the active orchestrator target and model, or null when unresolved. */
|
|
184
183
|
export function resolveThinkingCapability(
|
|
@@ -466,7 +465,7 @@ export function createInteractiveSlashRuntime(deps: InteractiveSlashRuntimeDeps)
|
|
|
466
465
|
if (!match) {
|
|
467
466
|
return { status: "unsupported", level, supported: supported.map(labelFor) };
|
|
468
467
|
}
|
|
469
|
-
// Session scope, like
|
|
468
|
+
// Session scope, like Alt+O: onSetThinkingLevel writes the level
|
|
470
469
|
// through to settings.yaml as the new default, which a slash command
|
|
471
470
|
// has no mandate to do.
|
|
472
471
|
if (deps.commitSetting) {
|
|
@@ -508,42 +507,6 @@ export function createInteractiveSlashRuntime(deps: InteractiveSlashRuntimeDeps)
|
|
|
508
507
|
if (selected) dispatchSlashCommand({ kind: "thinking-set", level: selected.value }, context);
|
|
509
508
|
});
|
|
510
509
|
},
|
|
511
|
-
setOutputVerbosity: (verbosity) => {
|
|
512
|
-
if (!deps.getSettings || !(deps.commitSetting || deps.writeSettings)) return { status: "unavailable" };
|
|
513
|
-
const requested = verbosity.trim().toLowerCase();
|
|
514
|
-
const match = OUTPUT_VERBOSITIES.find((candidate) => candidate === requested);
|
|
515
|
-
if (!match) return { status: "unsupported", verbosity, supported: OUTPUT_VERBOSITIES };
|
|
516
|
-
const next = structuredClone(deps.getSettings()) as ClioSettings;
|
|
517
|
-
next.interface.outputDetail = match;
|
|
518
|
-
// Session scope: /output changes this session and leaves settings.yaml
|
|
519
|
-
// alone. Saving it as the default is what Settings → Terminal is for.
|
|
520
|
-
if (deps.commitSetting) deps.commitSetting("interface.outputDetail", next, "session");
|
|
521
|
-
else deps.writeSettings?.(next);
|
|
522
|
-
// `/output` is also the explicit reset action for transcript folds. Do
|
|
523
|
-
// this even when the requested level already matches the current one;
|
|
524
|
-
// the render-time change detector cannot observe a same-value command.
|
|
525
|
-
deps.chatPanel.clearFoldOverrides();
|
|
526
|
-
deps.refreshFooter();
|
|
527
|
-
return { status: "applied", verbosity: match };
|
|
528
|
-
},
|
|
529
|
-
openOutputPicker: () => {
|
|
530
|
-
void deps
|
|
531
|
-
.openAskUser([
|
|
532
|
-
{
|
|
533
|
-
question: "Choose transcript detail for this session.",
|
|
534
|
-
header: "Output",
|
|
535
|
-
options: OUTPUT_VERBOSITIES.map((value) => ({
|
|
536
|
-
label: `${value[0]?.toUpperCase()}${value.slice(1)}`,
|
|
537
|
-
})),
|
|
538
|
-
},
|
|
539
|
-
])
|
|
540
|
-
.then((result) => {
|
|
541
|
-
if (result.cancelled) return;
|
|
542
|
-
const answer = result.answers[0]?.answer.toLowerCase();
|
|
543
|
-
const selected = OUTPUT_VERBOSITIES.find((value) => value === answer);
|
|
544
|
-
if (selected) dispatchSlashCommand({ kind: "output-set", verbosity: selected }, context);
|
|
545
|
-
});
|
|
546
|
-
},
|
|
547
510
|
openModel: deps.openModel,
|
|
548
511
|
providers: deps.providers,
|
|
549
512
|
applyModelRef: (ref) => {
|
|
@@ -595,7 +558,7 @@ export function createInteractiveSlashRuntime(deps: InteractiveSlashRuntimeDeps)
|
|
|
595
558
|
// operator overrides, and rendered at a stable width. HTML converts
|
|
596
559
|
// the resulting ANSI presentation to inline styles; Markdown keeps the
|
|
597
560
|
// prior plain-text fenced transcript.
|
|
598
|
-
const exportPanel = createChatPanel({ unboundedToolBodies: true,
|
|
561
|
+
const exportPanel = createChatPanel({ unboundedToolBodies: true, getOutputStyle: () => "detailed" });
|
|
599
562
|
// Scoped to the leaf the session is on, the same way /resume replays
|
|
600
563
|
// (issue #107): with a /tree pin persisted and not yet extended, the
|
|
601
564
|
// file still holds the abandoned branch after the pin, and an unscoped
|
|
@@ -13,6 +13,7 @@ export interface LayoutParts {
|
|
|
13
13
|
export interface LayoutOptions {
|
|
14
14
|
mode?: TuiMode;
|
|
15
15
|
fullscreenScrollbar?: ScrollViewScrollbar;
|
|
16
|
+
onTranscript?: (view: ScrollView) => void;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export interface FullscreenLayout {
|
|
@@ -20,10 +21,7 @@ export interface FullscreenLayout {
|
|
|
20
21
|
transcript: ScrollView;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
function buildFullscreenLayout(
|
|
24
|
-
parts: LayoutParts,
|
|
25
|
-
options: Pick<LayoutOptions, "fullscreenScrollbar"> = {},
|
|
26
|
-
): FullscreenLayout {
|
|
24
|
+
function buildFullscreenLayout(parts: LayoutParts, options: LayoutOptions = {}): FullscreenLayout {
|
|
27
25
|
const document = new Container();
|
|
28
26
|
document.addChild(parts.banner);
|
|
29
27
|
document.addChild(parts.chat);
|
|
@@ -36,6 +34,7 @@ function buildFullscreenLayout(
|
|
|
36
34
|
scrollbarTrackStyle: (text) => theme.fg("frame", text),
|
|
37
35
|
scrollbarThumbStyle: () => theme.fg("frameStrong", GLYPH.barFull),
|
|
38
36
|
});
|
|
37
|
+
options.onTranscript?.(transcript);
|
|
39
38
|
const dock = new VStack();
|
|
40
39
|
if (parts.pending) dock.addChild(parts.pending, { shrink: 1, minSize: 0 });
|
|
41
40
|
dock.addChild(parts.editor, { shrink: 1, minSize: 3 });
|
|
@@ -56,3 +55,29 @@ export function buildLayout(parts: LayoutParts, options: LayoutOptions = {}): Co
|
|
|
56
55
|
root.addChild(parts.footer);
|
|
57
56
|
return root;
|
|
58
57
|
}
|
|
58
|
+
|
|
59
|
+
/** Keep the nearest surviving text at the viewport when a preset changes row counts. */
|
|
60
|
+
export function preserveTranscriptScroll(view: ScrollView | undefined, width: number, mutation: () => void): void {
|
|
61
|
+
if (!view || view.isFollowingEnd) {
|
|
62
|
+
mutation();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const top = view.scrollTop;
|
|
66
|
+
const before = view.render(width);
|
|
67
|
+
mutation();
|
|
68
|
+
const after = view.render(width);
|
|
69
|
+
for (let row = top; row < Math.min(before.length, top + view.viewportHeight); row++) {
|
|
70
|
+
const anchor = before[row];
|
|
71
|
+
if (!anchor?.trim()) continue;
|
|
72
|
+
let match = -1;
|
|
73
|
+
for (let index = 0; index < after.length; index++) {
|
|
74
|
+
if (after[index] === anchor && (match < 0 || Math.abs(index - row) < Math.abs(match - row))) match = index;
|
|
75
|
+
}
|
|
76
|
+
if (match >= 0) {
|
|
77
|
+
view.updateLayout(after.length, view.viewportHeight, () => {});
|
|
78
|
+
view.scrollTo(Math.max(0, match - (row - top)), { disableFollow: true });
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
view.scrollTo(top, { disableFollow: true });
|
|
83
|
+
}
|
|
@@ -47,6 +47,7 @@ import { type ArtifactProviderDeps, createDefaultArtifactProviders } from "./vie
|
|
|
47
47
|
import { openViewOverlay } from "./view/view-overlay.js";
|
|
48
48
|
|
|
49
49
|
export interface OverlayGeneralOpenersDeps {
|
|
50
|
+
readTranscript?: ArtifactProviderDeps["readTranscript"];
|
|
50
51
|
tui: TUI;
|
|
51
52
|
transitions: OverlayTransitions;
|
|
52
53
|
observability: ObservabilityContract;
|
|
@@ -319,6 +320,7 @@ export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): Ov
|
|
|
319
320
|
dispatch: deps.dispatch,
|
|
320
321
|
sessionMeta: deps.getSessionMeta(),
|
|
321
322
|
readSessionEntries: deps.readSessionEntries,
|
|
323
|
+
readTranscript: deps.readTranscript,
|
|
322
324
|
}),
|
|
323
325
|
...(initialFilter ? { initialFilter } : {}),
|
|
324
326
|
notice: deps.notify,
|
|
@@ -399,6 +399,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
|
|
|
399
399
|
|
|
400
400
|
const scheduling = deps.app.scheduling;
|
|
401
401
|
const overlayGeneralOpeners = createOverlayGeneralOpeners({
|
|
402
|
+
readTranscript: () => chatPanel.inspectionArtifacts(),
|
|
402
403
|
tui,
|
|
403
404
|
transitions: overlayTransitions,
|
|
404
405
|
observability: deps.app.observability,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { editTextExternally, resolveExternalEditor } from "../core/external-editor.js";
|
|
1
2
|
import { resolveSessionCwd } from "../domains/session/cwd-fallback.js";
|
|
2
3
|
import type { DecisionLedgerEntry } from "../domains/session/entries.js";
|
|
3
4
|
import {
|
|
@@ -18,7 +19,6 @@ import type { ChatLoop } from "./chat-loop.js";
|
|
|
18
19
|
import type { ChatPanel } from "./chat-panel.js";
|
|
19
20
|
import { rehydrateChatPanelFromTurns } from "./chat-renderer.js";
|
|
20
21
|
import { emitCommandNotice } from "./command-fallbacks.js";
|
|
21
|
-
import { editTextExternally, resolveExternalEditor } from "./external-editor.js";
|
|
22
22
|
import type { InteractiveNoticeLevel } from "./interactive-subscriptions.js";
|
|
23
23
|
import { buildModelReplayAgentMessagesFromTurns } from "./model-session-replay.js";
|
|
24
24
|
import type { OverlayTransitions } from "./overlay-transitions.js";
|
|
@@ -229,7 +229,7 @@ export const SETTINGS_LABELS_BY_ID = {
|
|
|
229
229
|
"retry.maxDelayMs": "Max delay (ms)",
|
|
230
230
|
"retry.streamStallMs": "Stream stall timeout (ms)",
|
|
231
231
|
"terminal.showTerminalProgress": "Terminal progress badges",
|
|
232
|
-
"terminal.outputVerbosity": "Output
|
|
232
|
+
"terminal.outputVerbosity": "Output style",
|
|
233
233
|
"terminal.tuiMode": "TUI mode",
|
|
234
234
|
"terminal.fullscreenScrollbar": "Fullscreen scrollbar",
|
|
235
235
|
"terminal.smoothStreaming": "Smooth streaming",
|
|
@@ -514,7 +514,8 @@ const SETTINGS_HELP_BY_ID: Partial<Record<EditableSettingId, string>> = {
|
|
|
514
514
|
"Written by the confirm flow, not by hand, because confirming a remote here would be the record confirming itself. Sync refuses with library_remote_unconfirmed until this equals library.remote. Default: blank.",
|
|
515
515
|
"library.sync":
|
|
516
516
|
"Off means `clio-coder library sync` and `push` refuse before touching the network, whatever the remote says. Legal values: true, false · default: false.",
|
|
517
|
-
"budget.concurrency":
|
|
517
|
+
"budget.concurrency":
|
|
518
|
+
"auto allows four local workers. A fixed number caps how many workers run at once; the default is one.",
|
|
518
519
|
"skills.trustProjectCompatRoots":
|
|
519
520
|
"Project roots like .claude/skills and .codex/skills are untrusted by default; enabling exposes them to the model.",
|
|
520
521
|
"attribution.gitCommits":
|
|
@@ -550,7 +551,7 @@ const SETTINGS_HELP_BY_ID: Partial<Record<EditableSettingId, string>> = {
|
|
|
550
551
|
"watchdog.cadenceToolCalls":
|
|
551
552
|
"Mid-turn firing is how scope drift becomes visible before the turn ends. Leave blank and the watchdog fires at turn end only.",
|
|
552
553
|
keybindings:
|
|
553
|
-
"
|
|
554
|
+
"Alt+O cycles Output style: Compact, Standard, Detailed. Use /view for full reasoning and action details. Shift+Tab changes model thinking effort.",
|
|
554
555
|
};
|
|
555
556
|
|
|
556
557
|
/** Per-value meaning, surfaced for the current value of an enum knob. */
|
|
@@ -623,9 +624,9 @@ const SETTINGS_VALUE_HELP_BY_ID: Partial<Record<EditableSettingId, Record<string
|
|
|
623
624
|
false: "no terminal progress badges",
|
|
624
625
|
},
|
|
625
626
|
"terminal.outputVerbosity": {
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
627
|
+
compact: "quiet action summaries, answers, and visible failures",
|
|
628
|
+
standard: "short reasoning and change previews, clear action outcomes",
|
|
629
|
+
detailed: "larger bounded previews; full content is available in /view",
|
|
629
630
|
},
|
|
630
631
|
"terminal.tuiMode": {
|
|
631
632
|
regular: "preserve terminal scrollback and render the composer below the transcript",
|
|
@@ -1923,7 +1924,7 @@ function buildSettingItems(settings: Readonly<ClioSettings>, options?: BuildSett
|
|
|
1923
1924
|
values: ["false", "true"],
|
|
1924
1925
|
}),
|
|
1925
1926
|
settingItem("terminal.outputVerbosity", terminal.outputDetail, {
|
|
1926
|
-
values: ["
|
|
1927
|
+
values: ["compact", "standard", "detailed"],
|
|
1927
1928
|
}),
|
|
1928
1929
|
settingItem("terminal.tuiMode", terminal.mode, {
|
|
1929
1930
|
values: ["regular", "fullscreen"],
|
|
@@ -2555,7 +2556,7 @@ function applySettingChange(settings: ClioSettings, id: string, value: string):
|
|
|
2555
2556
|
if (value === "true" || value === "false") settings.interface.terminalProgress = value === "true";
|
|
2556
2557
|
return;
|
|
2557
2558
|
case "terminal.outputVerbosity":
|
|
2558
|
-
if (value === "
|
|
2559
|
+
if (value === "compact" || value === "standard" || value === "detailed") settings.interface.outputDetail = value;
|
|
2559
2560
|
return;
|
|
2560
2561
|
case "terminal.tuiMode":
|
|
2561
2562
|
if (value === "regular" || value === "fullscreen") settings.interface.mode = value;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { truncateToWidth } from "../../engine/tui.js";
|
|
2
|
+
import { clioTheme } from "../theme/index.js";
|
|
3
|
+
|
|
4
|
+
/** Count terminal rows after wrapping, including the inspection hint. */
|
|
5
|
+
export function previewRows(rows: readonly string[], limit: number, width: number, tail = false): string[] {
|
|
6
|
+
if (rows.length <= limit) return [...rows];
|
|
7
|
+
if (limit <= 0) return [];
|
|
8
|
+
const count = Math.max(0, limit - 1);
|
|
9
|
+
const hint = clioTheme().fg("dim", truncateToWidth(`… ${rows.length - count} rows · /view`, Math.max(1, width)));
|
|
10
|
+
return tail ? [hint, ...rows.slice(rows.length - count)] : [...rows.slice(0, count), hint];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Leave room for the composer and action identity in a short terminal. */
|
|
14
|
+
export function previewBudget(limit: number, terminalRows = 40): number {
|
|
15
|
+
return Math.min(limit, Math.max(2, Math.floor((terminalRows - 8) / 2)));
|
|
16
|
+
}
|