@patze/code-cli 0.43.3 → 0.55.1
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 +238 -0
- package/README.md +84 -57
- package/VERSION +1 -1
- package/dist/backend/auth-token-kind.d.ts +3 -0
- package/dist/backend/auth-token-kind.d.ts.map +1 -0
- package/dist/backend/auth-token-kind.js +6 -0
- package/dist/backend/auth-token-kind.js.map +1 -0
- package/dist/backend/client.d.ts.map +1 -1
- package/dist/backend/client.js +7 -1
- package/dist/backend/client.js.map +1 -1
- package/dist/backend/execute-client.d.ts +1 -1
- package/dist/backend/execute-client.d.ts.map +1 -1
- package/dist/backend/execute-client.js +26 -0
- package/dist/backend/execute-client.js.map +1 -1
- package/dist/backend/plugin-session-client.d.ts +33 -0
- package/dist/backend/plugin-session-client.d.ts.map +1 -0
- package/dist/backend/plugin-session-client.js +108 -0
- package/dist/backend/plugin-session-client.js.map +1 -0
- package/dist/backend/runs-client.d.ts.map +1 -1
- package/dist/backend/runs-client.js +2 -1
- package/dist/backend/runs-client.js.map +1 -1
- package/dist/cli/auth/pairing-login.d.ts +10 -0
- package/dist/cli/auth/pairing-login.d.ts.map +1 -0
- package/dist/cli/auth/pairing-login.js +78 -0
- package/dist/cli/auth/pairing-login.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +10 -4
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/exec.d.ts.map +1 -1
- package/dist/cli/commands/exec.js +33 -8
- package/dist/cli/commands/exec.js.map +1 -1
- package/dist/cli/commands/login.d.ts +1 -0
- package/dist/cli/commands/login.d.ts.map +1 -1
- package/dist/cli/commands/login.js +36 -5
- package/dist/cli/commands/login.js.map +1 -1
- package/dist/cli/commands/runs-format.d.ts.map +1 -1
- package/dist/cli/commands/runs-format.js +2 -1
- package/dist/cli/commands/runs-format.js.map +1 -1
- package/dist/cli/commands/runs.d.ts.map +1 -1
- package/dist/cli/commands/runs.js +63 -5
- package/dist/cli/commands/runs.js.map +1 -1
- package/dist/cli/commands/status-format.d.ts.map +1 -1
- package/dist/cli/commands/status-format.js +4 -3
- package/dist/cli/commands/status-format.js.map +1 -1
- package/dist/cli/doctor/e2e-readiness.d.ts +12 -0
- package/dist/cli/doctor/e2e-readiness.d.ts.map +1 -0
- package/dist/cli/doctor/e2e-readiness.js +95 -0
- package/dist/cli/doctor/e2e-readiness.js.map +1 -0
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +2 -0
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/interactive/agent-execute-turn.d.ts.map +1 -1
- package/dist/cli/interactive/agent-execute-turn.js +3 -1
- package/dist/cli/interactive/agent-execute-turn.js.map +1 -1
- package/dist/cli/interactive/agent-turn-context.d.ts +2 -2
- package/dist/cli/interactive/agent-turn-context.d.ts.map +1 -1
- package/dist/cli/interactive/agent-turn-context.js +23 -8
- package/dist/cli/interactive/agent-turn-context.js.map +1 -1
- package/dist/cli/interactive/agent-turn.d.ts.map +1 -1
- package/dist/cli/interactive/agent-turn.js +8 -7
- package/dist/cli/interactive/agent-turn.js.map +1 -1
- package/dist/cli/interactive/auth-credential-resolve.d.ts +20 -0
- package/dist/cli/interactive/auth-credential-resolve.d.ts.map +1 -0
- package/dist/cli/interactive/auth-credential-resolve.js +65 -0
- package/dist/cli/interactive/auth-credential-resolve.js.map +1 -0
- package/dist/cli/interactive/auth-gate.d.ts +1 -1
- package/dist/cli/interactive/auth-gate.d.ts.map +1 -1
- package/dist/cli/interactive/auth-gate.js +12 -3
- package/dist/cli/interactive/auth-gate.js.map +1 -1
- package/dist/cli/interactive/auth-readiness.d.ts +16 -0
- package/dist/cli/interactive/auth-readiness.d.ts.map +1 -0
- package/dist/cli/interactive/auth-readiness.js +50 -0
- package/dist/cli/interactive/auth-readiness.js.map +1 -0
- package/dist/cli/interactive/codex-feed-writer.d.ts.map +1 -1
- package/dist/cli/interactive/codex-feed-writer.js +4 -0
- package/dist/cli/interactive/codex-feed-writer.js.map +1 -1
- package/dist/cli/interactive/composer-chrome.d.ts +1 -1
- package/dist/cli/interactive/composer-chrome.d.ts.map +1 -1
- package/dist/cli/interactive/composer-chrome.js +1 -1
- package/dist/cli/interactive/composer-chrome.js.map +1 -1
- package/dist/cli/interactive/composer-edit-keys.d.ts +21 -0
- package/dist/cli/interactive/composer-edit-keys.d.ts.map +1 -0
- package/dist/cli/interactive/composer-edit-keys.js +57 -0
- package/dist/cli/interactive/composer-edit-keys.js.map +1 -0
- package/dist/cli/interactive/ensure-execute-auth.d.ts.map +1 -1
- package/dist/cli/interactive/ensure-execute-auth.js +8 -13
- package/dist/cli/interactive/ensure-execute-auth.js.map +1 -1
- package/dist/cli/interactive/header-refresh-triggers.d.ts +3 -0
- package/dist/cli/interactive/header-refresh-triggers.d.ts.map +1 -0
- package/dist/cli/interactive/header-refresh-triggers.js +16 -0
- package/dist/cli/interactive/header-refresh-triggers.js.map +1 -0
- package/dist/cli/interactive/header-refresh.d.ts +4 -0
- package/dist/cli/interactive/header-refresh.d.ts.map +1 -0
- package/dist/cli/interactive/header-refresh.js +33 -0
- package/dist/cli/interactive/header-refresh.js.map +1 -0
- package/dist/cli/interactive/header.d.ts +3 -2
- package/dist/cli/interactive/header.d.ts.map +1 -1
- package/dist/cli/interactive/header.js +23 -14
- package/dist/cli/interactive/header.js.map +1 -1
- package/dist/cli/interactive/interactive-footer-hints.d.ts +12 -0
- package/dist/cli/interactive/interactive-footer-hints.d.ts.map +1 -0
- package/dist/cli/interactive/interactive-footer-hints.js +32 -0
- package/dist/cli/interactive/interactive-footer-hints.js.map +1 -0
- package/dist/cli/interactive/interactive-model-apply.d.ts +15 -0
- package/dist/cli/interactive/interactive-model-apply.d.ts.map +1 -0
- package/dist/cli/interactive/interactive-model-apply.js +28 -0
- package/dist/cli/interactive/interactive-model-apply.js.map +1 -0
- package/dist/cli/interactive/interactive-ui.d.ts +13 -0
- package/dist/cli/interactive/interactive-ui.d.ts.map +1 -0
- package/dist/cli/interactive/interactive-ui.js +64 -0
- package/dist/cli/interactive/interactive-ui.js.map +1 -0
- package/dist/cli/interactive/launcher.d.ts.map +1 -1
- package/dist/cli/interactive/launcher.js +24 -17
- package/dist/cli/interactive/launcher.js.map +1 -1
- package/dist/cli/interactive/line-editor.d.ts +5 -4
- package/dist/cli/interactive/line-editor.d.ts.map +1 -1
- package/dist/cli/interactive/line-editor.js +102 -30
- package/dist/cli/interactive/line-editor.js.map +1 -1
- package/dist/cli/interactive/model-catalog-resolve.d.ts +11 -0
- package/dist/cli/interactive/model-catalog-resolve.d.ts.map +1 -0
- package/dist/cli/interactive/model-catalog-resolve.js +49 -0
- package/dist/cli/interactive/model-catalog-resolve.js.map +1 -0
- package/dist/cli/interactive/model-picker-panel.d.ts +22 -2
- package/dist/cli/interactive/model-picker-panel.d.ts.map +1 -1
- package/dist/cli/interactive/model-picker-panel.js +99 -16
- package/dist/cli/interactive/model-picker-panel.js.map +1 -1
- package/dist/cli/interactive/model-selector.d.ts.map +1 -1
- package/dist/cli/interactive/model-selector.js +3 -2
- package/dist/cli/interactive/model-selector.js.map +1 -1
- package/dist/cli/interactive/model-tty-picker.d.ts +3 -1
- package/dist/cli/interactive/model-tty-picker.d.ts.map +1 -1
- package/dist/cli/interactive/model-tty-picker.js +154 -24
- package/dist/cli/interactive/model-tty-picker.js.map +1 -1
- package/dist/cli/interactive/plain-codex-feed.d.ts.map +1 -1
- package/dist/cli/interactive/plain-codex-feed.js +48 -0
- package/dist/cli/interactive/plain-codex-feed.js.map +1 -1
- package/dist/cli/interactive/run-cancel.d.ts +23 -0
- package/dist/cli/interactive/run-cancel.d.ts.map +1 -0
- package/dist/cli/interactive/run-cancel.js +67 -0
- package/dist/cli/interactive/run-cancel.js.map +1 -0
- package/dist/cli/interactive/select-list-nav.d.ts +10 -0
- package/dist/cli/interactive/select-list-nav.d.ts.map +1 -0
- package/dist/cli/interactive/select-list-nav.js +45 -0
- package/dist/cli/interactive/select-list-nav.js.map +1 -0
- package/dist/cli/interactive/session-controller.d.ts +1 -0
- package/dist/cli/interactive/session-controller.d.ts.map +1 -1
- package/dist/cli/interactive/session-controller.js +5 -1
- package/dist/cli/interactive/session-controller.js.map +1 -1
- package/dist/cli/interactive/session-hints.d.ts +4 -1
- package/dist/cli/interactive/session-hints.d.ts.map +1 -1
- package/dist/cli/interactive/session-hints.js +23 -16
- package/dist/cli/interactive/session-hints.js.map +1 -1
- package/dist/cli/interactive/shell.d.ts.map +1 -1
- package/dist/cli/interactive/shell.js +17 -7
- package/dist/cli/interactive/shell.js.map +1 -1
- package/dist/cli/interactive/slash-dispatch.d.ts +2 -0
- package/dist/cli/interactive/slash-dispatch.d.ts.map +1 -1
- package/dist/cli/interactive/slash-dispatch.js +55 -28
- package/dist/cli/interactive/slash-dispatch.js.map +1 -1
- package/dist/cli/interactive/slash-menu.d.ts.map +1 -1
- package/dist/cli/interactive/slash-menu.js +29 -2
- package/dist/cli/interactive/slash-menu.js.map +1 -1
- package/dist/cli/interactive/slash-registry.js +2 -2
- package/dist/cli/interactive/slash-registry.js.map +1 -1
- package/dist/cli/interactive/smoke-trust-fixture.d.ts +10 -0
- package/dist/cli/interactive/smoke-trust-fixture.d.ts.map +1 -0
- package/dist/cli/interactive/smoke-trust-fixture.js +66 -0
- package/dist/cli/interactive/smoke-trust-fixture.js.map +1 -0
- package/dist/cli/interactive/tools-catalog.d.ts.map +1 -1
- package/dist/cli/interactive/tools-catalog.js +17 -12
- package/dist/cli/interactive/tools-catalog.js.map +1 -1
- package/dist/config/config.d.ts +2 -0
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +8 -0
- package/dist/config/config.js.map +1 -1
- package/dist/config/web-ui.d.ts +5 -0
- package/dist/config/web-ui.d.ts.map +1 -0
- package/dist/config/web-ui.js +12 -0
- package/dist/config/web-ui.js.map +1 -0
- package/docs/beta-release-handoff.md +56 -29
- package/docs/internal-beta.md +6 -3
- package/docs/known-limitations.md +32 -19
- package/docs/release-checklist.md +11 -3
- package/opentui/src/App.tsx +626 -218
- package/package.json +6 -2
package/opentui/src/App.tsx
CHANGED
|
@@ -24,14 +24,34 @@ import { classifyPlainOutput, createTranscriptSink, inferErrorKind } from "./tui
|
|
|
24
24
|
import { upsertTranscriptEntry } from "../../dist/cli/interactive/transcript-upsert.js"
|
|
25
25
|
import { applyOpenTuiAgentEvent } from "../../dist/cli/interactive/opentui-agent-event.js"
|
|
26
26
|
import type { AgentEvent } from "../../dist/cli/interactive/agent-events.js"
|
|
27
|
-
import {
|
|
28
|
-
dedupeModelIds,
|
|
29
|
-
formatModelDisplayName,
|
|
30
|
-
getModelCatalogEntry,
|
|
31
|
-
isPatzeDefaultModel,
|
|
27
|
+
import { formatModelDisplayName,
|
|
32
28
|
PATZE_MODEL_CATALOG,
|
|
29
|
+
type PatzeModelCatalogEntry,
|
|
33
30
|
} from "../../dist/config/models.js"
|
|
34
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
buildFilteredModelSelectOptions,
|
|
33
|
+
filterModelCatalogEntries,
|
|
34
|
+
formatCodexListItemLabel,
|
|
35
|
+
formatModelPickerPreferenceHint,
|
|
36
|
+
indexForFilteredCatalogEntry,
|
|
37
|
+
indexForReasoningLevel,
|
|
38
|
+
} from "../../dist/cli/interactive/model-picker-panel.js"
|
|
39
|
+
import { isComposerNewlineKey } from "../../dist/cli/interactive/composer-keys.js"
|
|
40
|
+
import { isHistorySafe } from "../../dist/cli/interactive/redact-input.js"
|
|
41
|
+
import {
|
|
42
|
+
getReasoningLevelsForModelId,
|
|
43
|
+
getDefaultReasoningLevel,
|
|
44
|
+
type ModelReasoningLevel,
|
|
45
|
+
} from "../../dist/config/model-reasoning.js"
|
|
46
|
+
import {
|
|
47
|
+
buildInteractiveFooterHint,
|
|
48
|
+
} from "../../dist/cli/interactive/interactive-footer-hints.js"
|
|
49
|
+
import {
|
|
50
|
+
applySimpleComposerBufferEdit,
|
|
51
|
+
jumpListSelection,
|
|
52
|
+
mapListNavKey,
|
|
53
|
+
moveListSelection,
|
|
54
|
+
} from "../../dist/cli/interactive/select-list-nav.js"
|
|
35
55
|
|
|
36
56
|
extend({ "tui-input": InputRenderable })
|
|
37
57
|
|
|
@@ -47,12 +67,13 @@ function TuiInput(props: TuiInputProps) {
|
|
|
47
67
|
return createElement("tui-input", props)
|
|
48
68
|
}
|
|
49
69
|
|
|
50
|
-
type ViewMode = "
|
|
70
|
+
type ViewMode = "input" | "model" | "model-reasoning"
|
|
51
71
|
|
|
52
72
|
type CommandSelectItem = SelectOption & {
|
|
53
73
|
name: string
|
|
54
74
|
description: string
|
|
55
75
|
value: string
|
|
76
|
+
commandName?: string
|
|
56
77
|
}
|
|
57
78
|
|
|
58
79
|
type ModelSelectItem = SelectOption & {
|
|
@@ -69,6 +90,8 @@ type InteractiveController = {
|
|
|
69
90
|
setModelThinking: (value: boolean | null) => void
|
|
70
91
|
getModelFast: () => boolean | null
|
|
71
92
|
setModelFast: (value: boolean | null) => void
|
|
93
|
+
getModelReasoningLevel: () => ModelReasoningLevel | null
|
|
94
|
+
setModelReasoningLevel: (value: ModelReasoningLevel | null) => void
|
|
72
95
|
getExecutionMode: () => "local" | "cloud"
|
|
73
96
|
snapshot: () => Array<{ input: string; lines: string[] }>
|
|
74
97
|
}
|
|
@@ -106,6 +129,15 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
106
129
|
const streamingIdRef = useRef<string | null>(null)
|
|
107
130
|
const scrollPinnedRef = useRef(true)
|
|
108
131
|
const activeAbortRef = useRef<AbortController | null>(null)
|
|
132
|
+
const inputHistoryRef = useRef<string[]>([])
|
|
133
|
+
const historyIndexRef = useRef(-1)
|
|
134
|
+
const stashedInputRef = useRef("")
|
|
135
|
+
const commandSelectIndexRef = useRef(0)
|
|
136
|
+
const [commandSelectIndex, setCommandSelectIndex] = useState(0)
|
|
137
|
+
const [modelSelectIndex, setModelSelectIndex] = useState(0)
|
|
138
|
+
const [modelSearch, setModelSearch] = useState("")
|
|
139
|
+
const [reasoningSelectIndex, setReasoningSelectIndex] = useState(0)
|
|
140
|
+
const [menuDismissed, setMenuDismissed] = useState(false)
|
|
109
141
|
|
|
110
142
|
const [ready, setReady] = useState(false)
|
|
111
143
|
const [bootError, setBootError] = useState<string | null>(null)
|
|
@@ -114,18 +146,21 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
114
146
|
const [activityFrame, setActivityFrame] = useState(0)
|
|
115
147
|
const [input, setInput] = useState("")
|
|
116
148
|
const [mode, setMode] = useState<ViewMode>("input")
|
|
117
|
-
const [modelSearch, setModelSearch] = useState("")
|
|
118
149
|
const [scrollOffset, setScrollOffset] = useState(0)
|
|
119
150
|
const [transcript, setTranscript] = useState<TranscriptEntry[]>([])
|
|
120
151
|
const [currentModel, setCurrentModel] = useState("composer-2.5-fast")
|
|
121
152
|
const [modelThinking, setModelThinking] = useState<boolean | null>(null)
|
|
122
153
|
const [modelFast, setModelFast] = useState<boolean | null>(null)
|
|
154
|
+
const [modelReasoningLevel, setModelReasoningLevel] = useState<ModelReasoningLevel | null>(null)
|
|
155
|
+
const [pendingModelEntry, setPendingModelEntry] = useState<PatzeModelCatalogEntry | null>(null)
|
|
123
156
|
const [executionMode, setExecutionMode] = useState<"local" | "cloud">("local")
|
|
124
157
|
const [executionTarget, setExecutionTarget] = useState<string | null>(null)
|
|
125
|
-
const [
|
|
158
|
+
const [pickerCatalog, setPickerCatalog] = useState<PatzeModelCatalogEntry[]>([
|
|
159
|
+
...PATZE_MODEL_CATALOG,
|
|
160
|
+
])
|
|
126
161
|
const [slashMenuReady, setSlashMenuReady] = useState(false)
|
|
127
162
|
const [startupHeader, setStartupHeader] = useState<string[]>([])
|
|
128
|
-
const [composerPlaceholder, setComposerPlaceholder] = useState("
|
|
163
|
+
const [composerPlaceholder, setComposerPlaceholder] = useState("")
|
|
129
164
|
const [emptySessionHints, setEmptySessionHints] = useState<string[]>([])
|
|
130
165
|
|
|
131
166
|
const followTranscript = useCallback(() => {
|
|
@@ -143,6 +178,53 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
143
178
|
setScrollOffset(0)
|
|
144
179
|
}, [])
|
|
145
180
|
|
|
181
|
+
const resetInputHistoryNav = useCallback(() => {
|
|
182
|
+
historyIndexRef.current = -1
|
|
183
|
+
stashedInputRef.current = ""
|
|
184
|
+
}, [])
|
|
185
|
+
|
|
186
|
+
const pushInputHistory = useCallback((value: string) => {
|
|
187
|
+
if (!isHistorySafe(value)) {
|
|
188
|
+
return
|
|
189
|
+
}
|
|
190
|
+
const history = inputHistoryRef.current
|
|
191
|
+
if (history[history.length - 1] !== value) {
|
|
192
|
+
inputHistoryRef.current = [...history, value]
|
|
193
|
+
}
|
|
194
|
+
resetInputHistoryNav()
|
|
195
|
+
}, [resetInputHistoryNav])
|
|
196
|
+
|
|
197
|
+
const historyPrev = useCallback((): string | null => {
|
|
198
|
+
const history = inputHistoryRef.current
|
|
199
|
+
if (!history.length) {
|
|
200
|
+
return null
|
|
201
|
+
}
|
|
202
|
+
if (historyIndexRef.current === -1) {
|
|
203
|
+
stashedInputRef.current = input
|
|
204
|
+
historyIndexRef.current = history.length - 1
|
|
205
|
+
} else {
|
|
206
|
+
historyIndexRef.current = Math.max(0, historyIndexRef.current - 1)
|
|
207
|
+
}
|
|
208
|
+
return history[historyIndexRef.current] ?? null
|
|
209
|
+
}, [input])
|
|
210
|
+
|
|
211
|
+
const historyNext = useCallback((): string | null => {
|
|
212
|
+
if (historyIndexRef.current === -1) {
|
|
213
|
+
return null
|
|
214
|
+
}
|
|
215
|
+
const history = inputHistoryRef.current
|
|
216
|
+
if (historyIndexRef.current >= history.length - 1) {
|
|
217
|
+
historyIndexRef.current = -1
|
|
218
|
+
return stashedInputRef.current
|
|
219
|
+
}
|
|
220
|
+
historyIndexRef.current += 1
|
|
221
|
+
return history[historyIndexRef.current] ?? null
|
|
222
|
+
}, [])
|
|
223
|
+
|
|
224
|
+
const transcriptScrollActive = useCallback(() => {
|
|
225
|
+
return !scrollPinnedRef.current || scrollOffset > 0
|
|
226
|
+
}, [scrollOffset])
|
|
227
|
+
|
|
146
228
|
const nextId = useCallback(() => {
|
|
147
229
|
const id = nextIdRef.current
|
|
148
230
|
nextIdRef.current += 1
|
|
@@ -239,24 +321,12 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
239
321
|
setExecutionMode(controller.session.getExecutionMode())
|
|
240
322
|
setModelThinking(controller.session.getModelThinking())
|
|
241
323
|
setModelFast(controller.session.getModelFast())
|
|
324
|
+
setModelReasoningLevel(controller.session.getModelReasoningLevel())
|
|
242
325
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
const { Cursor } = await import("@cursor/sdk")
|
|
248
|
-
const listed = await Cursor.models.list({ apiKey })
|
|
249
|
-
const ids = listed
|
|
250
|
-
.map((item) => String(item.id ?? "").trim())
|
|
251
|
-
.filter(Boolean)
|
|
252
|
-
if (ids.length > 0) {
|
|
253
|
-
modelIds = ids
|
|
254
|
-
}
|
|
255
|
-
} catch {
|
|
256
|
-
// keep bundled fallback list
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
setSupportedModels(modelIds)
|
|
326
|
+
const catalogResolve = await importPatzeDist<
|
|
327
|
+
typeof import("../../dist/cli/interactive/model-catalog-resolve.js")
|
|
328
|
+
>("cli/interactive/model-catalog-resolve.js")
|
|
329
|
+
setPickerCatalog(await catalogResolve.resolvePickerModelCatalog())
|
|
260
330
|
|
|
261
331
|
const cloudGit = await importPatzeDist<
|
|
262
332
|
typeof import("../../dist/cli/interactive/cloud-git.js")
|
|
@@ -278,7 +348,13 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
278
348
|
const sessionHints = await importPatzeDist<
|
|
279
349
|
typeof import("../../dist/cli/interactive/session-hints.js")
|
|
280
350
|
>("cli/interactive/session-hints.js")
|
|
281
|
-
setEmptySessionHints(
|
|
351
|
+
setEmptySessionHints(
|
|
352
|
+
sessionHints.shouldShowEmptySessionHints(controller.cwd)
|
|
353
|
+
? sessionHints.buildEmptySessionHints(controller.cwd)
|
|
354
|
+
: []
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
setStartupHeader(controller.renderHeaderLines())
|
|
282
358
|
}, [])
|
|
283
359
|
|
|
284
360
|
useEffect(() => {
|
|
@@ -387,7 +463,7 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
387
463
|
const result = await controller.processLine(value, sink, {
|
|
388
464
|
signal: abort.signal,
|
|
389
465
|
onActivity: setActivityLabel,
|
|
390
|
-
useActivitySpinner:
|
|
466
|
+
useActivitySpinner: false,
|
|
391
467
|
onAgentEvent: (event) => {
|
|
392
468
|
setActivityLabel(null)
|
|
393
469
|
setTranscript((items) =>
|
|
@@ -403,6 +479,9 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
403
479
|
setTranscript([])
|
|
404
480
|
markScrollPinned()
|
|
405
481
|
}
|
|
482
|
+
if (result.refreshHeader) {
|
|
483
|
+
setStartupHeader(controller.renderHeaderLines())
|
|
484
|
+
}
|
|
406
485
|
await refreshStatusMeta()
|
|
407
486
|
if (result.exitShell) {
|
|
408
487
|
exitApp()
|
|
@@ -430,32 +509,44 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
430
509
|
)
|
|
431
510
|
|
|
432
511
|
const openModelPicker = useCallback(() => {
|
|
512
|
+
setPendingModelEntry(null)
|
|
433
513
|
setModelSearch("")
|
|
514
|
+
const filtered = filterModelCatalogEntries("", pickerCatalog)
|
|
515
|
+
setModelSelectIndex(indexForFilteredCatalogEntry(filtered, currentModel))
|
|
434
516
|
setMode("model")
|
|
435
|
-
}, [])
|
|
517
|
+
}, [currentModel, pickerCatalog])
|
|
436
518
|
|
|
437
|
-
const
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
519
|
+
const toggleModelPickerPreference = useCallback(
|
|
520
|
+
(preference: "thinking" | "fast") => {
|
|
521
|
+
const controller = controllerRef.current
|
|
522
|
+
if (!controller) {
|
|
523
|
+
return
|
|
524
|
+
}
|
|
525
|
+
void (async () => {
|
|
526
|
+
const prefs = await importPatzeDist<
|
|
527
|
+
typeof import("../../dist/config/model-preferences.js")
|
|
528
|
+
>("config/model-preferences.js")
|
|
529
|
+
const current = preference === "thinking" ? modelThinking : modelFast
|
|
530
|
+
const next = prefs.resolveModelPreferenceToggle(current, "toggle")
|
|
531
|
+
if (preference === "thinking") {
|
|
532
|
+
controller.session.setModelThinking(next)
|
|
533
|
+
setModelThinking(next)
|
|
534
|
+
} else {
|
|
535
|
+
controller.session.setModelFast(next)
|
|
536
|
+
setModelFast(next)
|
|
537
|
+
}
|
|
538
|
+
controller.persistSession()
|
|
539
|
+
})()
|
|
540
|
+
},
|
|
541
|
+
[modelFast, modelThinking]
|
|
542
|
+
)
|
|
453
543
|
|
|
454
544
|
const runCommand = useCallback(
|
|
455
545
|
async (rawCommand: string) => {
|
|
456
546
|
const trimmed = rawCommand.trim()
|
|
457
547
|
setMode("input")
|
|
458
548
|
setInput("")
|
|
549
|
+
setMenuDismissed(false)
|
|
459
550
|
|
|
460
551
|
if (!trimmed) {
|
|
461
552
|
return
|
|
@@ -492,7 +583,30 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
492
583
|
const submitInput = useCallback(
|
|
493
584
|
(value: string) => {
|
|
494
585
|
const prompt = trimComposerPrompt(value)
|
|
586
|
+
|
|
587
|
+
if (
|
|
588
|
+
prompt.startsWith("/") &&
|
|
589
|
+
!prompt.includes(" ") &&
|
|
590
|
+
!menuDismissed &&
|
|
591
|
+
slashMenuModule &&
|
|
592
|
+
commandMenu.items.length > 0
|
|
593
|
+
) {
|
|
594
|
+
const command = commandMenu.items[commandSelectIndexRef.current]
|
|
595
|
+
if (command) {
|
|
596
|
+
setInput("")
|
|
597
|
+
resetInputHistoryNav()
|
|
598
|
+
if (slashMenuModule.commandRequiresArg(command)) {
|
|
599
|
+
setInput(`/${command.name} `)
|
|
600
|
+
setMenuDismissed(false)
|
|
601
|
+
return
|
|
602
|
+
}
|
|
603
|
+
void runCommand(`/${command.name}`)
|
|
604
|
+
return
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
495
608
|
setInput("")
|
|
609
|
+
resetInputHistoryNav()
|
|
496
610
|
|
|
497
611
|
if (!prompt || busy) {
|
|
498
612
|
return
|
|
@@ -503,9 +617,19 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
503
617
|
return
|
|
504
618
|
}
|
|
505
619
|
|
|
620
|
+
pushInputHistory(prompt)
|
|
506
621
|
void submitToController(prompt)
|
|
507
622
|
},
|
|
508
|
-
[
|
|
623
|
+
[
|
|
624
|
+
busy,
|
|
625
|
+
commandMenu.items,
|
|
626
|
+
menuDismissed,
|
|
627
|
+
pushInputHistory,
|
|
628
|
+
resetInputHistoryNav,
|
|
629
|
+
runCommand,
|
|
630
|
+
slashMenuModule,
|
|
631
|
+
submitToController,
|
|
632
|
+
]
|
|
509
633
|
)
|
|
510
634
|
|
|
511
635
|
const selectModel = useCallback(
|
|
@@ -515,46 +639,116 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
515
639
|
return
|
|
516
640
|
}
|
|
517
641
|
|
|
518
|
-
const
|
|
519
|
-
typeof import("../../dist/cli/interactive/model-
|
|
520
|
-
>("cli/interactive/model-
|
|
642
|
+
const modelApply = await importPatzeDist<
|
|
643
|
+
typeof import("../../dist/cli/interactive/interactive-model-apply.js")
|
|
644
|
+
>("cli/interactive/interactive-model-apply.js")
|
|
645
|
+
|
|
646
|
+
const catalogEntry =
|
|
647
|
+
pickerCatalog.find((entry) => entry.id.toLowerCase() === modelId.toLowerCase()) ?? null
|
|
648
|
+
|
|
649
|
+
const result = modelApply.applyInteractiveModelChoice(
|
|
650
|
+
controller.session as never,
|
|
651
|
+
modelId,
|
|
652
|
+
undefined,
|
|
653
|
+
catalogEntry
|
|
654
|
+
)
|
|
655
|
+
if (result.pendingReasoning) {
|
|
656
|
+
setPendingModelEntry(result.pendingReasoning)
|
|
657
|
+
setReasoningSelectIndex(
|
|
658
|
+
indexForReasoningLevel(
|
|
659
|
+
getReasoningLevelsForModelId(result.pendingReasoning.id),
|
|
660
|
+
currentModel.toLowerCase() === result.pendingReasoning.id.toLowerCase()
|
|
661
|
+
? modelReasoningLevel
|
|
662
|
+
: null,
|
|
663
|
+
result.pendingReasoning.id
|
|
664
|
+
)
|
|
665
|
+
)
|
|
666
|
+
setMode("model-reasoning")
|
|
667
|
+
return
|
|
668
|
+
}
|
|
521
669
|
|
|
522
|
-
const result = modelCommand.dispatchModelCommand(controller.session as never, modelId)
|
|
523
670
|
for (const line of result.lines) {
|
|
524
671
|
addEntry(inferErrorKind(line), "model", line)
|
|
525
672
|
}
|
|
673
|
+
controller.persistSession()
|
|
674
|
+
setStartupHeader(controller.renderHeaderLines())
|
|
526
675
|
await refreshStatusMeta()
|
|
527
676
|
setMode("input")
|
|
528
677
|
},
|
|
529
|
-
[addEntry, refreshStatusMeta]
|
|
678
|
+
[addEntry, currentModel, modelReasoningLevel, pickerCatalog, refreshStatusMeta]
|
|
679
|
+
)
|
|
680
|
+
|
|
681
|
+
const selectReasoning = useCallback(
|
|
682
|
+
async (level: ModelReasoningLevel) => {
|
|
683
|
+
const controller = controllerRef.current
|
|
684
|
+
if (!controller || !pendingModelEntry) {
|
|
685
|
+
return
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
const modelApply = await importPatzeDist<
|
|
689
|
+
typeof import("../../dist/cli/interactive/interactive-model-apply.js")
|
|
690
|
+
>("cli/interactive/interactive-model-apply.js")
|
|
691
|
+
|
|
692
|
+
const result = modelApply.applyInteractiveModelReasoning(
|
|
693
|
+
controller.session as never,
|
|
694
|
+
pendingModelEntry,
|
|
695
|
+
level
|
|
696
|
+
)
|
|
697
|
+
for (const line of result.lines) {
|
|
698
|
+
addEntry(inferErrorKind(line), "model", line)
|
|
699
|
+
}
|
|
700
|
+
controller.persistSession()
|
|
701
|
+
setPendingModelEntry(null)
|
|
702
|
+
setStartupHeader(controller.renderHeaderLines())
|
|
703
|
+
await refreshStatusMeta()
|
|
704
|
+
setMode("input")
|
|
705
|
+
},
|
|
706
|
+
[addEntry, pendingModelEntry, refreshStatusMeta]
|
|
530
707
|
)
|
|
531
708
|
|
|
532
709
|
const commandSelectRows = Math.min(6, Math.max(3, rows - 10))
|
|
533
710
|
const modelSelectRows = Math.min(8, Math.max(3, rows - 10))
|
|
534
711
|
const commandPanelRows = 2 + commandSelectRows
|
|
535
|
-
const modelPanelRows =
|
|
712
|
+
const modelPanelRows = 4 + modelSelectRows
|
|
536
713
|
const headerRows = Math.max(1, startupHeader.length)
|
|
537
|
-
const composerChromeRows =
|
|
714
|
+
const composerChromeRows = 4
|
|
538
715
|
const bottomHintRows = 1
|
|
539
716
|
|
|
717
|
+
const commandMenu = useMemo(() => {
|
|
718
|
+
if (!slashMenuReady || !slashMenuModule || !input.startsWith("/") || input.includes(" ") || menuDismissed) {
|
|
719
|
+
return { items: [], advancedCount: 0 }
|
|
720
|
+
}
|
|
721
|
+
return slashMenuModule.computeSlashMenuItems(input)
|
|
722
|
+
}, [input, menuDismissed, slashMenuReady])
|
|
723
|
+
|
|
724
|
+
const commandItems = useMemo(() => {
|
|
725
|
+
return commandMenu.items.map((command) => ({
|
|
726
|
+
name: `/${command.name}`,
|
|
727
|
+
description: command.description,
|
|
728
|
+
value: `/${command.name}`,
|
|
729
|
+
commandName: command.name,
|
|
730
|
+
}))
|
|
731
|
+
}, [commandMenu.items])
|
|
732
|
+
|
|
733
|
+
const slashPaletteOpen =
|
|
734
|
+
mode === "input" &&
|
|
735
|
+
input.startsWith("/") &&
|
|
736
|
+
!input.includes(" ") &&
|
|
737
|
+
!menuDismissed &&
|
|
738
|
+
commandMenu.items.length > 0
|
|
739
|
+
|
|
540
740
|
const transcriptViewportRows = Math.max(
|
|
541
741
|
4,
|
|
542
742
|
rows -
|
|
543
743
|
headerRows -
|
|
544
744
|
bottomHintRows -
|
|
545
|
-
(mode === "model"
|
|
745
|
+
(mode === "model-reasoning"
|
|
546
746
|
? modelPanelRows + 2
|
|
547
|
-
: mode === "
|
|
548
|
-
?
|
|
549
|
-
: composerChromeRows)
|
|
747
|
+
: mode === "model"
|
|
748
|
+
? modelPanelRows + 2
|
|
749
|
+
: composerChromeRows + (slashPaletteOpen ? commandPanelRows + 2 : 0))
|
|
550
750
|
)
|
|
551
751
|
|
|
552
|
-
const composerModelLabel = formatModelDisplay(currentModel, {
|
|
553
|
-
thinking: modelThinking,
|
|
554
|
-
fast: modelFast,
|
|
555
|
-
})
|
|
556
|
-
const composerPathLabel = shortenComposerPath(cwd)
|
|
557
|
-
|
|
558
752
|
const scrollableEntries = useMemo(() => transcript, [transcript])
|
|
559
753
|
|
|
560
754
|
const transcriptLines = useMemo(
|
|
@@ -575,40 +769,53 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
575
769
|
setScrollOffset((offset) => Math.min(offset, maxScrollOffset))
|
|
576
770
|
}, [maxScrollOffset])
|
|
577
771
|
|
|
578
|
-
|
|
579
|
-
|
|
772
|
+
useEffect(() => {
|
|
773
|
+
setCommandSelectIndex(0)
|
|
774
|
+
commandSelectIndexRef.current = 0
|
|
775
|
+
}, [commandMenu.items])
|
|
776
|
+
|
|
777
|
+
const modelItems = useMemo(
|
|
778
|
+
() => buildFilteredModelSelectOptions(currentModel, modelSearch, pickerCatalog),
|
|
779
|
+
[currentModel, modelSearch, pickerCatalog]
|
|
780
|
+
)
|
|
781
|
+
|
|
782
|
+
useEffect(() => {
|
|
783
|
+
if (mode !== "model") {
|
|
784
|
+
return
|
|
785
|
+
}
|
|
786
|
+
setModelSelectIndex((index) => {
|
|
787
|
+
if (modelItems.length === 0) {
|
|
788
|
+
return 0
|
|
789
|
+
}
|
|
790
|
+
return Math.min(index, modelItems.length - 1)
|
|
791
|
+
})
|
|
792
|
+
}, [mode, modelItems.length, modelSearch])
|
|
793
|
+
|
|
794
|
+
const reasoningItems = useMemo(() => {
|
|
795
|
+
if (!pendingModelEntry) {
|
|
580
796
|
return []
|
|
581
797
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
const
|
|
588
|
-
const
|
|
589
|
-
return
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
.map((model) => {
|
|
604
|
-
const entry = getModelCatalogEntry(model)
|
|
605
|
-
return {
|
|
606
|
-
name: formatModelDisplayName(model),
|
|
607
|
-
description: entry?.description ?? "",
|
|
608
|
-
value: model,
|
|
609
|
-
}
|
|
610
|
-
})
|
|
611
|
-
}, [currentModel, modelSearch, supportedModels])
|
|
798
|
+
const levels = getReasoningLevelsForModelId(pendingModelEntry.id)
|
|
799
|
+
const currentLevel =
|
|
800
|
+
pendingModelEntry.id.toLowerCase() === currentModel.toLowerCase()
|
|
801
|
+
? modelReasoningLevel
|
|
802
|
+
: null
|
|
803
|
+
const highlightLevel = currentLevel ?? getDefaultReasoningLevel(pendingModelEntry.id)
|
|
804
|
+
const defaultLevel = getDefaultReasoningLevel(pendingModelEntry.id)
|
|
805
|
+
return levels.map((option) => {
|
|
806
|
+
const item = {
|
|
807
|
+
label: option.label,
|
|
808
|
+
description: option.description,
|
|
809
|
+
isDefault: option.id === defaultLevel,
|
|
810
|
+
isCurrent: option.id === highlightLevel,
|
|
811
|
+
}
|
|
812
|
+
return {
|
|
813
|
+
name: formatCodexListItemLabel(item),
|
|
814
|
+
description: option.description,
|
|
815
|
+
value: option.id,
|
|
816
|
+
}
|
|
817
|
+
})
|
|
818
|
+
}, [currentModel, modelReasoningLevel, pendingModelEntry])
|
|
612
819
|
|
|
613
820
|
const toggleFeedExpand = useCallback((target: "think" | "diff") => {
|
|
614
821
|
setTranscript((items) => {
|
|
@@ -631,21 +838,25 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
631
838
|
}, [followTranscript])
|
|
632
839
|
|
|
633
840
|
useKeyboard((key: KeyEvent) => {
|
|
634
|
-
const character = getInputCharacter(key)
|
|
635
|
-
|
|
636
841
|
if (key.ctrl && key.name === "c") {
|
|
637
842
|
if (busy && activeAbortRef.current) {
|
|
638
843
|
activeAbortRef.current.abort()
|
|
639
844
|
addEntry("status", "run", "cancelled · stream stopped locally")
|
|
640
845
|
return
|
|
641
846
|
}
|
|
847
|
+
if (!busy && mode === "input" && input.trim()) {
|
|
848
|
+
setInput("")
|
|
849
|
+
return
|
|
850
|
+
}
|
|
642
851
|
if (!busy) {
|
|
643
852
|
exitApp()
|
|
644
853
|
}
|
|
645
854
|
return
|
|
646
855
|
}
|
|
647
856
|
|
|
648
|
-
if (mode === "model" && key.name === "escape") {
|
|
857
|
+
if ((mode === "model" || mode === "model-reasoning") && key.name === "escape") {
|
|
858
|
+
setPendingModelEntry(null)
|
|
859
|
+
setModelSearch("")
|
|
649
860
|
setMode("input")
|
|
650
861
|
return
|
|
651
862
|
}
|
|
@@ -653,24 +864,86 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
653
864
|
if (mode === "model") {
|
|
654
865
|
if (key.name === "backspace" || key.name === "delete") {
|
|
655
866
|
setModelSearch((value) => value.slice(0, -1))
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
867
|
+
return
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
const searchCharacter = getModelPickerSearchCharacter(key)
|
|
871
|
+
if (searchCharacter === "T") {
|
|
872
|
+
toggleModelPickerPreference("thinking")
|
|
873
|
+
return
|
|
874
|
+
}
|
|
875
|
+
if (searchCharacter === "F") {
|
|
876
|
+
toggleModelPickerPreference("fast")
|
|
877
|
+
return
|
|
878
|
+
}
|
|
879
|
+
if (isModelPickerSearchInput(searchCharacter)) {
|
|
880
|
+
setModelSearch((value) => `${value}${searchCharacter}`)
|
|
881
|
+
return
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
if (modelItems.length > 0) {
|
|
885
|
+
if (
|
|
886
|
+
applyOpenTuiListNav(key, {
|
|
887
|
+
count: modelItems.length,
|
|
888
|
+
index: modelSelectIndex,
|
|
889
|
+
setIndex: setModelSelectIndex,
|
|
890
|
+
onEnter: () => {
|
|
891
|
+
const item = modelItems[modelSelectIndex]
|
|
892
|
+
if (item) {
|
|
893
|
+
void selectModel(item.value)
|
|
894
|
+
}
|
|
895
|
+
},
|
|
896
|
+
})
|
|
897
|
+
) {
|
|
898
|
+
return
|
|
899
|
+
}
|
|
662
900
|
}
|
|
663
901
|
return
|
|
664
902
|
}
|
|
665
903
|
|
|
666
|
-
if (mode === "
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
904
|
+
if (mode === "model-reasoning" && reasoningItems.length > 0) {
|
|
905
|
+
if (
|
|
906
|
+
applyOpenTuiListNav(key, {
|
|
907
|
+
count: reasoningItems.length,
|
|
908
|
+
index: reasoningSelectIndex,
|
|
909
|
+
setIndex: setReasoningSelectIndex,
|
|
910
|
+
onEnter: () => {
|
|
911
|
+
const item = reasoningItems[reasoningSelectIndex]
|
|
912
|
+
if (item) {
|
|
913
|
+
void selectReasoning(item.value as ModelReasoningLevel)
|
|
914
|
+
}
|
|
915
|
+
},
|
|
916
|
+
})
|
|
917
|
+
) {
|
|
918
|
+
return
|
|
919
|
+
}
|
|
670
920
|
}
|
|
671
921
|
|
|
672
922
|
if (mode === "input") {
|
|
673
923
|
const pageSize = Math.max(1, transcriptViewportRows - 1)
|
|
924
|
+
const paletteOpen =
|
|
925
|
+
input.startsWith("/") && !input.includes(" ") && !menuDismissed && commandMenu.items.length > 0
|
|
926
|
+
|
|
927
|
+
if (paletteOpen && commandItems.length > 0) {
|
|
928
|
+
if (
|
|
929
|
+
applyOpenTuiListNav(key, {
|
|
930
|
+
count: commandItems.length,
|
|
931
|
+
index: commandSelectIndex,
|
|
932
|
+
setIndex: (index) => {
|
|
933
|
+
setCommandSelectIndex(index)
|
|
934
|
+
commandSelectIndexRef.current = index
|
|
935
|
+
},
|
|
936
|
+
onEnter: () => {
|
|
937
|
+
const item = commandItems[commandSelectIndexRef.current]
|
|
938
|
+
if (item) {
|
|
939
|
+
selectCommandOption(commandSelectIndexRef.current, item)
|
|
940
|
+
}
|
|
941
|
+
},
|
|
942
|
+
})
|
|
943
|
+
) {
|
|
944
|
+
return
|
|
945
|
+
}
|
|
946
|
+
}
|
|
674
947
|
|
|
675
948
|
if (key.ctrl && key.name === "t") {
|
|
676
949
|
toggleFeedExpand("think")
|
|
@@ -682,22 +955,64 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
682
955
|
return
|
|
683
956
|
}
|
|
684
957
|
|
|
685
|
-
if (key.
|
|
958
|
+
if (key.ctrl && (key.name === "u" || key.name === "k")) {
|
|
959
|
+
const edit = applySimpleComposerBufferEdit(input, key)
|
|
960
|
+
if (edit.handled) {
|
|
961
|
+
setInput(edit.value)
|
|
962
|
+
resetInputHistoryNav()
|
|
963
|
+
return
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
if (isComposerNewlineKey(key.sequence, key)) {
|
|
686
968
|
setInput((value) => `${value}\n`)
|
|
969
|
+
resetInputHistoryNav()
|
|
687
970
|
return
|
|
688
971
|
}
|
|
689
972
|
|
|
690
|
-
if (key.name === "
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
973
|
+
if (key.name === "escape") {
|
|
974
|
+
if (paletteOpen) {
|
|
975
|
+
setMenuDismissed(true)
|
|
976
|
+
}
|
|
977
|
+
return
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
if (key.name === "tab" && paletteOpen) {
|
|
981
|
+
const command = commandMenu.items[commandSelectIndexRef.current]
|
|
982
|
+
if (command) {
|
|
983
|
+
setInput(`/${command.name} `)
|
|
984
|
+
setMenuDismissed(false)
|
|
985
|
+
}
|
|
986
|
+
return
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
const scrollTranscript = transcriptScrollActive() && !paletteOpen
|
|
990
|
+
|
|
991
|
+
if (key.name === "up" || (key.name === "k" && !key.ctrl && !key.meta && !paletteOpen)) {
|
|
992
|
+
if (scrollTranscript) {
|
|
993
|
+
markScrollUnpinned()
|
|
994
|
+
setScrollOffset((offset) => Math.min(maxScrollOffset, offset + 1))
|
|
995
|
+
} else {
|
|
996
|
+
const previous = historyPrev()
|
|
997
|
+
if (previous !== null) {
|
|
998
|
+
setInput(previous)
|
|
698
999
|
}
|
|
699
|
-
|
|
700
|
-
|
|
1000
|
+
}
|
|
1001
|
+
} else if (key.name === "down" || (key.name === "j" && !key.ctrl && !key.meta && !paletteOpen)) {
|
|
1002
|
+
if (scrollTranscript) {
|
|
1003
|
+
setScrollOffset((offset) => {
|
|
1004
|
+
const next = Math.max(0, offset - 1)
|
|
1005
|
+
if (next === 0) {
|
|
1006
|
+
scrollPinnedRef.current = true
|
|
1007
|
+
}
|
|
1008
|
+
return next
|
|
1009
|
+
})
|
|
1010
|
+
} else {
|
|
1011
|
+
const next = historyNext()
|
|
1012
|
+
if (next !== null) {
|
|
1013
|
+
setInput(next)
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
701
1016
|
} else if (key.name === "pageup") {
|
|
702
1017
|
markScrollUnpinned()
|
|
703
1018
|
setScrollOffset((offset) => Math.min(maxScrollOffset, offset + pageSize))
|
|
@@ -710,19 +1025,36 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
710
1025
|
return next
|
|
711
1026
|
})
|
|
712
1027
|
} else if (key.name === "home") {
|
|
713
|
-
|
|
714
|
-
|
|
1028
|
+
if (scrollTranscript) {
|
|
1029
|
+
markScrollUnpinned()
|
|
1030
|
+
setScrollOffset(maxScrollOffset)
|
|
1031
|
+
}
|
|
715
1032
|
} else if (key.name === "end") {
|
|
716
|
-
|
|
1033
|
+
if (scrollTranscript) {
|
|
1034
|
+
markScrollPinned()
|
|
1035
|
+
}
|
|
717
1036
|
}
|
|
718
1037
|
}
|
|
719
1038
|
})
|
|
720
1039
|
|
|
721
1040
|
const selectCommandOption = (_index: number, option: SelectOption | null) => {
|
|
722
1041
|
const item = option as CommandSelectItem | null
|
|
723
|
-
if (item?.value) {
|
|
724
|
-
|
|
1042
|
+
if (!item?.value || !slashMenuModule) {
|
|
1043
|
+
return
|
|
1044
|
+
}
|
|
1045
|
+
const command = commandMenu.items.find((entry) => entry.name === item.commandName)
|
|
1046
|
+
if (command && slashMenuModule.commandRequiresArg(command)) {
|
|
1047
|
+
setInput(`/${command.name} `)
|
|
1048
|
+
setMenuDismissed(false)
|
|
1049
|
+
setMode("input")
|
|
1050
|
+
return
|
|
725
1051
|
}
|
|
1052
|
+
void runCommand(item.value)
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
const trackCommandSelection = (index: number) => {
|
|
1056
|
+
commandSelectIndexRef.current = index
|
|
1057
|
+
setCommandSelectIndex(index)
|
|
726
1058
|
}
|
|
727
1059
|
|
|
728
1060
|
const selectModelOption = (_index: number, option: SelectOption | null) => {
|
|
@@ -732,6 +1064,28 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
732
1064
|
}
|
|
733
1065
|
}
|
|
734
1066
|
|
|
1067
|
+
const selectReasoningOption = (_index: number, option: SelectOption | null) => {
|
|
1068
|
+
const item = option as ModelSelectItem | null
|
|
1069
|
+
if (item?.value) {
|
|
1070
|
+
void selectReasoning(item.value as ModelReasoningLevel)
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
const footerHint = buildInteractiveFooterHint({
|
|
1075
|
+
busy,
|
|
1076
|
+
slashMenuOpen: slashPaletteOpen,
|
|
1077
|
+
pickerMode:
|
|
1078
|
+
mode === "model"
|
|
1079
|
+
? "model"
|
|
1080
|
+
: mode === "model-reasoning"
|
|
1081
|
+
? "model-reasoning"
|
|
1082
|
+
: null,
|
|
1083
|
+
modelPickerPreferences:
|
|
1084
|
+
mode === "model"
|
|
1085
|
+
? { thinking: modelThinking, fast: modelFast }
|
|
1086
|
+
: undefined,
|
|
1087
|
+
})
|
|
1088
|
+
|
|
735
1089
|
if (bootError) {
|
|
736
1090
|
return createElement(
|
|
737
1091
|
"box",
|
|
@@ -788,32 +1142,36 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
788
1142
|
attributes: TextAttributes.DIM,
|
|
789
1143
|
})
|
|
790
1144
|
: null,
|
|
791
|
-
mode === "
|
|
1145
|
+
mode === "model-reasoning"
|
|
792
1146
|
? createElement(
|
|
793
1147
|
"box",
|
|
794
1148
|
{
|
|
795
1149
|
border: true,
|
|
796
1150
|
borderStyle: "single",
|
|
797
|
-
borderColor: "
|
|
1151
|
+
borderColor: "magenta",
|
|
798
1152
|
flexDirection: "column",
|
|
799
1153
|
marginTop: 1,
|
|
800
1154
|
paddingX: 1,
|
|
801
1155
|
},
|
|
802
1156
|
createElement("text", {
|
|
803
|
-
content: "
|
|
1157
|
+
content: "Select reasoning effort",
|
|
804
1158
|
attributes: TextAttributes.BOLD,
|
|
805
1159
|
}),
|
|
806
1160
|
createElement("text", {
|
|
807
|
-
content:
|
|
1161
|
+
content: `${formatModelDisplayName(pendingModelEntry?.id ?? currentModel)} · ↑↓ or j/k · 1-9 · Enter · Esc`,
|
|
808
1162
|
fg: "gray",
|
|
809
1163
|
}),
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
1164
|
+
reasoningItems.length === 0
|
|
1165
|
+
? createElement("text", { content: "No reasoning levels.", fg: "yellow" })
|
|
1166
|
+
: createElement("select", {
|
|
1167
|
+
focused: mode === "model-reasoning",
|
|
1168
|
+
height: modelSelectRows,
|
|
1169
|
+
options: reasoningItems,
|
|
1170
|
+
selectedIndex: reasoningSelectIndex,
|
|
1171
|
+
onSelect: selectReasoningOption,
|
|
1172
|
+
showDescription: true,
|
|
1173
|
+
showScrollIndicator: true,
|
|
1174
|
+
})
|
|
817
1175
|
)
|
|
818
1176
|
: mode === "model"
|
|
819
1177
|
? createElement(
|
|
@@ -827,100 +1185,173 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
827
1185
|
paddingX: 1,
|
|
828
1186
|
},
|
|
829
1187
|
createElement("text", {
|
|
830
|
-
content: "Select
|
|
1188
|
+
content: "Select Model and Effort",
|
|
831
1189
|
attributes: TextAttributes.BOLD,
|
|
832
1190
|
}),
|
|
833
1191
|
createElement("text", {
|
|
834
|
-
content: `Type to search ·
|
|
1192
|
+
content: `Type to search · ${formatModelPickerPreferenceHint(modelThinking, modelFast)} · ↑↓ or j/k · 1-9 · Enter · Esc`,
|
|
835
1193
|
fg: "gray",
|
|
836
1194
|
}),
|
|
837
1195
|
createElement("text", {
|
|
838
1196
|
content: `Search: ${modelSearch || "all models"}`,
|
|
839
1197
|
fg: "gray",
|
|
840
1198
|
}),
|
|
1199
|
+
createElement("text", {
|
|
1200
|
+
content: "Access other models by running patze -m <model> or in config",
|
|
1201
|
+
fg: "gray",
|
|
1202
|
+
}),
|
|
841
1203
|
modelItems.length === 0
|
|
842
1204
|
? createElement("text", { content: "No matching models.", fg: "yellow" })
|
|
843
1205
|
: createElement("select", {
|
|
844
1206
|
focused: mode === "model",
|
|
845
1207
|
height: modelSelectRows,
|
|
846
1208
|
options: modelItems,
|
|
1209
|
+
selectedIndex: modelSelectIndex,
|
|
847
1210
|
onSelect: selectModelOption,
|
|
1211
|
+
showDescription: true,
|
|
848
1212
|
showScrollIndicator: true,
|
|
849
1213
|
})
|
|
850
1214
|
)
|
|
851
1215
|
: createElement(
|
|
852
1216
|
"box",
|
|
853
|
-
{
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
1217
|
+
{ flexDirection: "column", flexShrink: 0 },
|
|
1218
|
+
slashPaletteOpen && commandItems.length > 0
|
|
1219
|
+
? createElement(
|
|
1220
|
+
"box",
|
|
1221
|
+
{
|
|
1222
|
+
border: true,
|
|
1223
|
+
borderStyle: "single",
|
|
1224
|
+
borderColor: "cyan",
|
|
1225
|
+
flexDirection: "column",
|
|
1226
|
+
marginTop: 1,
|
|
1227
|
+
paddingX: 1,
|
|
1228
|
+
},
|
|
1229
|
+
createElement("text", {
|
|
1230
|
+
content: "Commands",
|
|
1231
|
+
attributes: TextAttributes.BOLD,
|
|
1232
|
+
}),
|
|
1233
|
+
createElement("text", {
|
|
1234
|
+
content:
|
|
1235
|
+
commandMenu.advancedCount > 0
|
|
1236
|
+
? `${commandMenu.advancedCount} advanced · Tab · ↑↓ or j/k · 1-9 · Enter · Esc`
|
|
1237
|
+
: "Tab · ↑↓ or j/k · 1-9 jump · Enter · Esc dismiss",
|
|
1238
|
+
fg: "gray",
|
|
1239
|
+
}),
|
|
1240
|
+
createElement("select", {
|
|
1241
|
+
focused: false,
|
|
1242
|
+
height: commandSelectRows,
|
|
1243
|
+
options: commandItems,
|
|
1244
|
+
selectedIndex: commandSelectIndex,
|
|
1245
|
+
onChange: trackCommandSelection,
|
|
1246
|
+
onSelect: selectCommandOption,
|
|
1247
|
+
showDescription: true,
|
|
1248
|
+
showScrollIndicator: true,
|
|
1249
|
+
})
|
|
1250
|
+
)
|
|
1251
|
+
: null,
|
|
861
1252
|
createElement(
|
|
862
1253
|
"box",
|
|
863
|
-
{
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
1254
|
+
{
|
|
1255
|
+
border: true,
|
|
1256
|
+
borderStyle: "single",
|
|
1257
|
+
borderColor: busy ? "yellow" : "green",
|
|
1258
|
+
flexDirection: "column",
|
|
1259
|
+
marginTop: 1,
|
|
1260
|
+
paddingX: 1,
|
|
1261
|
+
},
|
|
1262
|
+
createElement(
|
|
1263
|
+
"box",
|
|
1264
|
+
{ flexDirection: "row", alignItems: "center" },
|
|
1265
|
+
createElement("text", {
|
|
1266
|
+
content: "> ",
|
|
1267
|
+
attributes: TextAttributes.BOLD,
|
|
1268
|
+
}),
|
|
1269
|
+
createElement(TuiInput, {
|
|
1270
|
+
focused: ready && !busy,
|
|
1271
|
+
placeholder: busy
|
|
1272
|
+
? activityLabel
|
|
1273
|
+
? `${["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"][activityFrame % 10]} ${activityLabel}… · Ctrl+C cancel`
|
|
1274
|
+
: "Ctrl+C cancel · waiting for agent…"
|
|
1275
|
+
: ready
|
|
1276
|
+
? composerPlaceholder
|
|
1277
|
+
: "Loading Patze Code…",
|
|
1278
|
+
value: input,
|
|
1279
|
+
onInput: (value: string) => {
|
|
1280
|
+
setInput(value)
|
|
1281
|
+
resetInputHistoryNav()
|
|
1282
|
+
if (busy) {
|
|
1283
|
+
return
|
|
1284
|
+
}
|
|
1285
|
+
if (!value.startsWith("/")) {
|
|
1286
|
+
setMenuDismissed(false)
|
|
1287
|
+
}
|
|
1288
|
+
if (value.startsWith("/") && !value.includes(" ") && !menuDismissed) {
|
|
1289
|
+
setCommandSelectIndex(0)
|
|
1290
|
+
commandSelectIndexRef.current = 0
|
|
1291
|
+
}
|
|
1292
|
+
},
|
|
1293
|
+
onSubmit: submitInput,
|
|
1294
|
+
})
|
|
1295
|
+
)
|
|
1296
|
+
)
|
|
896
1297
|
),
|
|
897
1298
|
createElement("text", {
|
|
898
|
-
content:
|
|
899
|
-
? "Esc exit · Ctrl+C cancel run · agent executing"
|
|
900
|
-
: "Esc exit · Ctrl+C quit · / commands · ctrl+t thought · ctrl+d diff · /status",
|
|
1299
|
+
content: footerHint,
|
|
901
1300
|
attributes: TextAttributes.DIM,
|
|
902
1301
|
})
|
|
903
1302
|
)
|
|
904
1303
|
}
|
|
905
1304
|
|
|
1305
|
+
function applyOpenTuiListNav(
|
|
1306
|
+
key: KeyEvent,
|
|
1307
|
+
input: {
|
|
1308
|
+
count: number
|
|
1309
|
+
index: number
|
|
1310
|
+
setIndex: (index: number) => void
|
|
1311
|
+
onEnter: () => void
|
|
1312
|
+
}
|
|
1313
|
+
): boolean {
|
|
1314
|
+
if (input.count <= 0) {
|
|
1315
|
+
return false
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
const digitSource =
|
|
1319
|
+
key.sequence && key.sequence.length === 1 ? key.sequence : (key.name ?? "")
|
|
1320
|
+
const jump = jumpListSelection(digitSource, input.count)
|
|
1321
|
+
if (jump !== null) {
|
|
1322
|
+
input.setIndex(jump)
|
|
1323
|
+
return true
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
const nav = mapListNavKey(key.name, key.sequence)
|
|
1327
|
+
if (nav === "up") {
|
|
1328
|
+
input.setIndex(moveListSelection(input.index, -1, input.count))
|
|
1329
|
+
return true
|
|
1330
|
+
}
|
|
1331
|
+
if (nav === "down") {
|
|
1332
|
+
input.setIndex(moveListSelection(input.index, 1, input.count))
|
|
1333
|
+
return true
|
|
1334
|
+
}
|
|
1335
|
+
if (nav === "enter") {
|
|
1336
|
+
input.onEnter()
|
|
1337
|
+
return true
|
|
1338
|
+
}
|
|
1339
|
+
return false
|
|
1340
|
+
}
|
|
1341
|
+
|
|
906
1342
|
function trimComposerPrompt(value: string): string {
|
|
907
1343
|
return value.replace(/^\s+|\s+$/g, "")
|
|
908
1344
|
}
|
|
909
1345
|
|
|
910
|
-
function
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
return normalized
|
|
914
|
-
}
|
|
915
|
-
const parts = normalized.split("/").filter(Boolean)
|
|
916
|
-
if (parts.length >= 2) {
|
|
917
|
-
const tail = `${parts[parts.length - 2]}/${parts[parts.length - 1]}`
|
|
918
|
-
const candidate = `…/${tail}`
|
|
919
|
-
if ([...candidate].length <= 36) {
|
|
920
|
-
return candidate
|
|
921
|
-
}
|
|
1346
|
+
function getModelPickerSearchCharacter(key: KeyEvent): string {
|
|
1347
|
+
if (key.ctrl || key.meta || key.name.length !== 1) {
|
|
1348
|
+
return ""
|
|
922
1349
|
}
|
|
923
|
-
return
|
|
1350
|
+
return key.shift ? key.name.toUpperCase() : key.name
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
function isModelPickerSearchInput(input: string): boolean {
|
|
1354
|
+
return input.length > 0 && !/[\u0000-\u001F\u007F]/.test(input)
|
|
924
1355
|
}
|
|
925
1356
|
|
|
926
1357
|
function HeaderLine({ line }: { line: string }) {
|
|
@@ -985,31 +1416,8 @@ function partToAttributes(part: TranscriptPart) {
|
|
|
985
1416
|
return attributes
|
|
986
1417
|
}
|
|
987
1418
|
|
|
988
|
-
function getInputCharacter(key: KeyEvent): string {
|
|
989
|
-
if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta) {
|
|
990
|
-
return key.sequence
|
|
991
|
-
}
|
|
992
|
-
return ""
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
function isSearchInput(character: string): boolean {
|
|
996
|
-
return Boolean(character && character !== " " && character.charCodeAt(0) >= 32)
|
|
997
|
-
}
|
|
998
|
-
|
|
999
1419
|
type SlashMenuModule = typeof import("../../dist/cli/interactive/slash-menu.js")
|
|
1000
1420
|
|
|
1001
1421
|
let slashMenuModule: SlashMenuModule | null = null
|
|
1002
1422
|
|
|
1003
|
-
function getCommandItems(input: string): CommandSelectItem[] {
|
|
1004
|
-
if (!slashMenuModule) {
|
|
1005
|
-
return []
|
|
1006
|
-
}
|
|
1007
|
-
const menu = slashMenuModule.computeSlashMenuItems(input)
|
|
1008
|
-
return menu.items.map((command) => ({
|
|
1009
|
-
name: `/${command.name}`,
|
|
1010
|
-
description: command.description,
|
|
1011
|
-
value: `/${command.name}`,
|
|
1012
|
-
}))
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
1423
|
export { parseCwdArg }
|