@patze/code-cli 0.43.3 → 0.55.0
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 +231 -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-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 +6 -0
- package/dist/cli/interactive/line-editor.d.ts.map +1 -1
- package/dist/cli/interactive/line-editor.js +102 -8
- 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 +1 -0
- package/dist/cli/interactive/session-hints.d.ts.map +1 -1
- package/dist/cli/interactive/session-hints.js +24 -8
- 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 -5
- 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 +636 -213
- 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,23 @@ 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
163
|
const [composerPlaceholder, setComposerPlaceholder] = useState("Describe a task… · / for commands")
|
|
164
|
+
const [composerChromeLeft, setComposerChromeLeft] = useState("Patze")
|
|
165
|
+
const [composerChromeRight, setComposerChromeRight] = useState("")
|
|
129
166
|
const [emptySessionHints, setEmptySessionHints] = useState<string[]>([])
|
|
130
167
|
|
|
131
168
|
const followTranscript = useCallback(() => {
|
|
@@ -143,6 +180,53 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
143
180
|
setScrollOffset(0)
|
|
144
181
|
}, [])
|
|
145
182
|
|
|
183
|
+
const resetInputHistoryNav = useCallback(() => {
|
|
184
|
+
historyIndexRef.current = -1
|
|
185
|
+
stashedInputRef.current = ""
|
|
186
|
+
}, [])
|
|
187
|
+
|
|
188
|
+
const pushInputHistory = useCallback((value: string) => {
|
|
189
|
+
if (!isHistorySafe(value)) {
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
const history = inputHistoryRef.current
|
|
193
|
+
if (history[history.length - 1] !== value) {
|
|
194
|
+
inputHistoryRef.current = [...history, value]
|
|
195
|
+
}
|
|
196
|
+
resetInputHistoryNav()
|
|
197
|
+
}, [resetInputHistoryNav])
|
|
198
|
+
|
|
199
|
+
const historyPrev = useCallback((): string | null => {
|
|
200
|
+
const history = inputHistoryRef.current
|
|
201
|
+
if (!history.length) {
|
|
202
|
+
return null
|
|
203
|
+
}
|
|
204
|
+
if (historyIndexRef.current === -1) {
|
|
205
|
+
stashedInputRef.current = input
|
|
206
|
+
historyIndexRef.current = history.length - 1
|
|
207
|
+
} else {
|
|
208
|
+
historyIndexRef.current = Math.max(0, historyIndexRef.current - 1)
|
|
209
|
+
}
|
|
210
|
+
return history[historyIndexRef.current] ?? null
|
|
211
|
+
}, [input])
|
|
212
|
+
|
|
213
|
+
const historyNext = useCallback((): string | null => {
|
|
214
|
+
if (historyIndexRef.current === -1) {
|
|
215
|
+
return null
|
|
216
|
+
}
|
|
217
|
+
const history = inputHistoryRef.current
|
|
218
|
+
if (historyIndexRef.current >= history.length - 1) {
|
|
219
|
+
historyIndexRef.current = -1
|
|
220
|
+
return stashedInputRef.current
|
|
221
|
+
}
|
|
222
|
+
historyIndexRef.current += 1
|
|
223
|
+
return history[historyIndexRef.current] ?? null
|
|
224
|
+
}, [])
|
|
225
|
+
|
|
226
|
+
const transcriptScrollActive = useCallback(() => {
|
|
227
|
+
return !scrollPinnedRef.current || scrollOffset > 0
|
|
228
|
+
}, [scrollOffset])
|
|
229
|
+
|
|
146
230
|
const nextId = useCallback(() => {
|
|
147
231
|
const id = nextIdRef.current
|
|
148
232
|
nextIdRef.current += 1
|
|
@@ -239,24 +323,12 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
239
323
|
setExecutionMode(controller.session.getExecutionMode())
|
|
240
324
|
setModelThinking(controller.session.getModelThinking())
|
|
241
325
|
setModelFast(controller.session.getModelFast())
|
|
326
|
+
setModelReasoningLevel(controller.session.getModelReasoningLevel())
|
|
242
327
|
|
|
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)
|
|
328
|
+
const catalogResolve = await importPatzeDist<
|
|
329
|
+
typeof import("../../dist/cli/interactive/model-catalog-resolve.js")
|
|
330
|
+
>("cli/interactive/model-catalog-resolve.js")
|
|
331
|
+
setPickerCatalog(await catalogResolve.resolvePickerModelCatalog())
|
|
260
332
|
|
|
261
333
|
const cloudGit = await importPatzeDist<
|
|
262
334
|
typeof import("../../dist/cli/interactive/cloud-git.js")
|
|
@@ -278,7 +350,19 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
278
350
|
const sessionHints = await importPatzeDist<
|
|
279
351
|
typeof import("../../dist/cli/interactive/session-hints.js")
|
|
280
352
|
>("cli/interactive/session-hints.js")
|
|
281
|
-
setEmptySessionHints(
|
|
353
|
+
setEmptySessionHints(
|
|
354
|
+
sessionHints.shouldShowEmptySessionHints(controller.cwd)
|
|
355
|
+
? sessionHints.buildEmptySessionHints(controller.cwd)
|
|
356
|
+
: []
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
const composerChrome = await importPatzeDist<
|
|
360
|
+
typeof import("../../dist/cli/interactive/composer-session-chrome.js")
|
|
361
|
+
>("cli/interactive/composer-session-chrome.js")
|
|
362
|
+
const chrome = composerChrome.buildComposerChromeForSession(controller as never)
|
|
363
|
+
setComposerChromeLeft(chrome.left)
|
|
364
|
+
setComposerChromeRight(chrome.right)
|
|
365
|
+
setStartupHeader(controller.renderHeaderLines())
|
|
282
366
|
}, [])
|
|
283
367
|
|
|
284
368
|
useEffect(() => {
|
|
@@ -387,7 +471,7 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
387
471
|
const result = await controller.processLine(value, sink, {
|
|
388
472
|
signal: abort.signal,
|
|
389
473
|
onActivity: setActivityLabel,
|
|
390
|
-
useActivitySpinner:
|
|
474
|
+
useActivitySpinner: false,
|
|
391
475
|
onAgentEvent: (event) => {
|
|
392
476
|
setActivityLabel(null)
|
|
393
477
|
setTranscript((items) =>
|
|
@@ -403,6 +487,9 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
403
487
|
setTranscript([])
|
|
404
488
|
markScrollPinned()
|
|
405
489
|
}
|
|
490
|
+
if (result.refreshHeader) {
|
|
491
|
+
setStartupHeader(controller.renderHeaderLines())
|
|
492
|
+
}
|
|
406
493
|
await refreshStatusMeta()
|
|
407
494
|
if (result.exitShell) {
|
|
408
495
|
exitApp()
|
|
@@ -430,32 +517,44 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
430
517
|
)
|
|
431
518
|
|
|
432
519
|
const openModelPicker = useCallback(() => {
|
|
520
|
+
setPendingModelEntry(null)
|
|
433
521
|
setModelSearch("")
|
|
522
|
+
const filtered = filterModelCatalogEntries("", pickerCatalog)
|
|
523
|
+
setModelSelectIndex(indexForFilteredCatalogEntry(filtered, currentModel))
|
|
434
524
|
setMode("model")
|
|
435
|
-
}, [])
|
|
525
|
+
}, [currentModel, pickerCatalog])
|
|
436
526
|
|
|
437
|
-
const
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
527
|
+
const toggleModelPickerPreference = useCallback(
|
|
528
|
+
(preference: "thinking" | "fast") => {
|
|
529
|
+
const controller = controllerRef.current
|
|
530
|
+
if (!controller) {
|
|
531
|
+
return
|
|
532
|
+
}
|
|
533
|
+
void (async () => {
|
|
534
|
+
const prefs = await importPatzeDist<
|
|
535
|
+
typeof import("../../dist/config/model-preferences.js")
|
|
536
|
+
>("config/model-preferences.js")
|
|
537
|
+
const current = preference === "thinking" ? modelThinking : modelFast
|
|
538
|
+
const next = prefs.resolveModelPreferenceToggle(current, "toggle")
|
|
539
|
+
if (preference === "thinking") {
|
|
540
|
+
controller.session.setModelThinking(next)
|
|
541
|
+
setModelThinking(next)
|
|
542
|
+
} else {
|
|
543
|
+
controller.session.setModelFast(next)
|
|
544
|
+
setModelFast(next)
|
|
545
|
+
}
|
|
546
|
+
controller.persistSession()
|
|
547
|
+
})()
|
|
548
|
+
},
|
|
549
|
+
[modelFast, modelThinking]
|
|
550
|
+
)
|
|
453
551
|
|
|
454
552
|
const runCommand = useCallback(
|
|
455
553
|
async (rawCommand: string) => {
|
|
456
554
|
const trimmed = rawCommand.trim()
|
|
457
555
|
setMode("input")
|
|
458
556
|
setInput("")
|
|
557
|
+
setMenuDismissed(false)
|
|
459
558
|
|
|
460
559
|
if (!trimmed) {
|
|
461
560
|
return
|
|
@@ -492,7 +591,30 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
492
591
|
const submitInput = useCallback(
|
|
493
592
|
(value: string) => {
|
|
494
593
|
const prompt = trimComposerPrompt(value)
|
|
594
|
+
|
|
595
|
+
if (
|
|
596
|
+
prompt.startsWith("/") &&
|
|
597
|
+
!prompt.includes(" ") &&
|
|
598
|
+
!menuDismissed &&
|
|
599
|
+
slashMenuModule &&
|
|
600
|
+
commandMenu.items.length > 0
|
|
601
|
+
) {
|
|
602
|
+
const command = commandMenu.items[commandSelectIndexRef.current]
|
|
603
|
+
if (command) {
|
|
604
|
+
setInput("")
|
|
605
|
+
resetInputHistoryNav()
|
|
606
|
+
if (slashMenuModule.commandRequiresArg(command)) {
|
|
607
|
+
setInput(`/${command.name} `)
|
|
608
|
+
setMenuDismissed(false)
|
|
609
|
+
return
|
|
610
|
+
}
|
|
611
|
+
void runCommand(`/${command.name}`)
|
|
612
|
+
return
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
495
616
|
setInput("")
|
|
617
|
+
resetInputHistoryNav()
|
|
496
618
|
|
|
497
619
|
if (!prompt || busy) {
|
|
498
620
|
return
|
|
@@ -503,9 +625,19 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
503
625
|
return
|
|
504
626
|
}
|
|
505
627
|
|
|
628
|
+
pushInputHistory(prompt)
|
|
506
629
|
void submitToController(prompt)
|
|
507
630
|
},
|
|
508
|
-
[
|
|
631
|
+
[
|
|
632
|
+
busy,
|
|
633
|
+
commandMenu.items,
|
|
634
|
+
menuDismissed,
|
|
635
|
+
pushInputHistory,
|
|
636
|
+
resetInputHistoryNav,
|
|
637
|
+
runCommand,
|
|
638
|
+
slashMenuModule,
|
|
639
|
+
submitToController,
|
|
640
|
+
]
|
|
509
641
|
)
|
|
510
642
|
|
|
511
643
|
const selectModel = useCallback(
|
|
@@ -515,45 +647,118 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
515
647
|
return
|
|
516
648
|
}
|
|
517
649
|
|
|
518
|
-
const
|
|
519
|
-
typeof import("../../dist/cli/interactive/model-
|
|
520
|
-
>("cli/interactive/model-
|
|
650
|
+
const modelApply = await importPatzeDist<
|
|
651
|
+
typeof import("../../dist/cli/interactive/interactive-model-apply.js")
|
|
652
|
+
>("cli/interactive/interactive-model-apply.js")
|
|
653
|
+
|
|
654
|
+
const catalogEntry =
|
|
655
|
+
pickerCatalog.find((entry) => entry.id.toLowerCase() === modelId.toLowerCase()) ?? null
|
|
656
|
+
|
|
657
|
+
const result = modelApply.applyInteractiveModelChoice(
|
|
658
|
+
controller.session as never,
|
|
659
|
+
modelId,
|
|
660
|
+
undefined,
|
|
661
|
+
catalogEntry
|
|
662
|
+
)
|
|
663
|
+
if (result.pendingReasoning) {
|
|
664
|
+
setPendingModelEntry(result.pendingReasoning)
|
|
665
|
+
setReasoningSelectIndex(
|
|
666
|
+
indexForReasoningLevel(
|
|
667
|
+
getReasoningLevelsForModelId(result.pendingReasoning.id),
|
|
668
|
+
currentModel.toLowerCase() === result.pendingReasoning.id.toLowerCase()
|
|
669
|
+
? modelReasoningLevel
|
|
670
|
+
: null,
|
|
671
|
+
result.pendingReasoning.id
|
|
672
|
+
)
|
|
673
|
+
)
|
|
674
|
+
setMode("model-reasoning")
|
|
675
|
+
return
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
for (const line of result.lines) {
|
|
679
|
+
addEntry(inferErrorKind(line), "model", line)
|
|
680
|
+
}
|
|
681
|
+
controller.persistSession()
|
|
682
|
+
setStartupHeader(controller.renderHeaderLines())
|
|
683
|
+
await refreshStatusMeta()
|
|
684
|
+
setMode("input")
|
|
685
|
+
},
|
|
686
|
+
[addEntry, currentModel, modelReasoningLevel, pickerCatalog, refreshStatusMeta]
|
|
687
|
+
)
|
|
688
|
+
|
|
689
|
+
const selectReasoning = useCallback(
|
|
690
|
+
async (level: ModelReasoningLevel) => {
|
|
691
|
+
const controller = controllerRef.current
|
|
692
|
+
if (!controller || !pendingModelEntry) {
|
|
693
|
+
return
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
const modelApply = await importPatzeDist<
|
|
697
|
+
typeof import("../../dist/cli/interactive/interactive-model-apply.js")
|
|
698
|
+
>("cli/interactive/interactive-model-apply.js")
|
|
521
699
|
|
|
522
|
-
const result =
|
|
700
|
+
const result = modelApply.applyInteractiveModelReasoning(
|
|
701
|
+
controller.session as never,
|
|
702
|
+
pendingModelEntry,
|
|
703
|
+
level
|
|
704
|
+
)
|
|
523
705
|
for (const line of result.lines) {
|
|
524
706
|
addEntry(inferErrorKind(line), "model", line)
|
|
525
707
|
}
|
|
708
|
+
controller.persistSession()
|
|
709
|
+
setPendingModelEntry(null)
|
|
710
|
+
setStartupHeader(controller.renderHeaderLines())
|
|
526
711
|
await refreshStatusMeta()
|
|
527
712
|
setMode("input")
|
|
528
713
|
},
|
|
529
|
-
[addEntry, refreshStatusMeta]
|
|
714
|
+
[addEntry, pendingModelEntry, refreshStatusMeta]
|
|
530
715
|
)
|
|
531
716
|
|
|
532
717
|
const commandSelectRows = Math.min(6, Math.max(3, rows - 10))
|
|
533
718
|
const modelSelectRows = Math.min(8, Math.max(3, rows - 10))
|
|
534
719
|
const commandPanelRows = 2 + commandSelectRows
|
|
535
|
-
const modelPanelRows =
|
|
720
|
+
const modelPanelRows = 4 + modelSelectRows
|
|
536
721
|
const headerRows = Math.max(1, startupHeader.length)
|
|
537
722
|
const composerChromeRows = 5
|
|
538
723
|
const bottomHintRows = 1
|
|
539
724
|
|
|
725
|
+
const commandMenu = useMemo(() => {
|
|
726
|
+
if (!slashMenuReady || !slashMenuModule || !input.startsWith("/") || input.includes(" ") || menuDismissed) {
|
|
727
|
+
return { items: [], advancedCount: 0 }
|
|
728
|
+
}
|
|
729
|
+
return slashMenuModule.computeSlashMenuItems(input)
|
|
730
|
+
}, [input, menuDismissed, slashMenuReady])
|
|
731
|
+
|
|
732
|
+
const commandItems = useMemo(() => {
|
|
733
|
+
return commandMenu.items.map((command) => ({
|
|
734
|
+
name: `/${command.name}`,
|
|
735
|
+
description: command.description,
|
|
736
|
+
value: `/${command.name}`,
|
|
737
|
+
commandName: command.name,
|
|
738
|
+
}))
|
|
739
|
+
}, [commandMenu.items])
|
|
740
|
+
|
|
741
|
+
const slashPaletteOpen =
|
|
742
|
+
mode === "input" &&
|
|
743
|
+
input.startsWith("/") &&
|
|
744
|
+
!input.includes(" ") &&
|
|
745
|
+
!menuDismissed &&
|
|
746
|
+
commandMenu.items.length > 0
|
|
747
|
+
|
|
540
748
|
const transcriptViewportRows = Math.max(
|
|
541
749
|
4,
|
|
542
750
|
rows -
|
|
543
751
|
headerRows -
|
|
544
752
|
bottomHintRows -
|
|
545
|
-
(mode === "model"
|
|
753
|
+
(mode === "model-reasoning"
|
|
546
754
|
? modelPanelRows + 2
|
|
547
|
-
: mode === "
|
|
548
|
-
?
|
|
549
|
-
: composerChromeRows)
|
|
755
|
+
: mode === "model"
|
|
756
|
+
? modelPanelRows + 2
|
|
757
|
+
: composerChromeRows + (slashPaletteOpen ? commandPanelRows + 2 : 0))
|
|
550
758
|
)
|
|
551
759
|
|
|
552
|
-
const composerModelLabel =
|
|
553
|
-
|
|
554
|
-
fast: modelFast,
|
|
555
|
-
})
|
|
556
|
-
const composerPathLabel = shortenComposerPath(cwd)
|
|
760
|
+
const composerModelLabel = composerChromeLeft
|
|
761
|
+
const composerPathLabel = composerChromeRight
|
|
557
762
|
|
|
558
763
|
const scrollableEntries = useMemo(() => transcript, [transcript])
|
|
559
764
|
|
|
@@ -575,40 +780,53 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
575
780
|
setScrollOffset((offset) => Math.min(offset, maxScrollOffset))
|
|
576
781
|
}, [maxScrollOffset])
|
|
577
782
|
|
|
578
|
-
|
|
579
|
-
|
|
783
|
+
useEffect(() => {
|
|
784
|
+
setCommandSelectIndex(0)
|
|
785
|
+
commandSelectIndexRef.current = 0
|
|
786
|
+
}, [commandMenu.items])
|
|
787
|
+
|
|
788
|
+
const modelItems = useMemo(
|
|
789
|
+
() => buildFilteredModelSelectOptions(currentModel, modelSearch, pickerCatalog),
|
|
790
|
+
[currentModel, modelSearch, pickerCatalog]
|
|
791
|
+
)
|
|
792
|
+
|
|
793
|
+
useEffect(() => {
|
|
794
|
+
if (mode !== "model") {
|
|
795
|
+
return
|
|
796
|
+
}
|
|
797
|
+
setModelSelectIndex((index) => {
|
|
798
|
+
if (modelItems.length === 0) {
|
|
799
|
+
return 0
|
|
800
|
+
}
|
|
801
|
+
return Math.min(index, modelItems.length - 1)
|
|
802
|
+
})
|
|
803
|
+
}, [mode, modelItems.length, modelSearch])
|
|
804
|
+
|
|
805
|
+
const reasoningItems = useMemo(() => {
|
|
806
|
+
if (!pendingModelEntry) {
|
|
580
807
|
return []
|
|
581
808
|
}
|
|
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])
|
|
809
|
+
const levels = getReasoningLevelsForModelId(pendingModelEntry.id)
|
|
810
|
+
const currentLevel =
|
|
811
|
+
pendingModelEntry.id.toLowerCase() === currentModel.toLowerCase()
|
|
812
|
+
? modelReasoningLevel
|
|
813
|
+
: null
|
|
814
|
+
const highlightLevel = currentLevel ?? getDefaultReasoningLevel(pendingModelEntry.id)
|
|
815
|
+
const defaultLevel = getDefaultReasoningLevel(pendingModelEntry.id)
|
|
816
|
+
return levels.map((option) => {
|
|
817
|
+
const item = {
|
|
818
|
+
label: option.label,
|
|
819
|
+
description: option.description,
|
|
820
|
+
isDefault: option.id === defaultLevel,
|
|
821
|
+
isCurrent: option.id === highlightLevel,
|
|
822
|
+
}
|
|
823
|
+
return {
|
|
824
|
+
name: formatCodexListItemLabel(item),
|
|
825
|
+
description: option.description,
|
|
826
|
+
value: option.id,
|
|
827
|
+
}
|
|
828
|
+
})
|
|
829
|
+
}, [currentModel, modelReasoningLevel, pendingModelEntry])
|
|
612
830
|
|
|
613
831
|
const toggleFeedExpand = useCallback((target: "think" | "diff") => {
|
|
614
832
|
setTranscript((items) => {
|
|
@@ -631,21 +849,25 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
631
849
|
}, [followTranscript])
|
|
632
850
|
|
|
633
851
|
useKeyboard((key: KeyEvent) => {
|
|
634
|
-
const character = getInputCharacter(key)
|
|
635
|
-
|
|
636
852
|
if (key.ctrl && key.name === "c") {
|
|
637
853
|
if (busy && activeAbortRef.current) {
|
|
638
854
|
activeAbortRef.current.abort()
|
|
639
855
|
addEntry("status", "run", "cancelled · stream stopped locally")
|
|
640
856
|
return
|
|
641
857
|
}
|
|
858
|
+
if (!busy && mode === "input" && input.trim()) {
|
|
859
|
+
setInput("")
|
|
860
|
+
return
|
|
861
|
+
}
|
|
642
862
|
if (!busy) {
|
|
643
863
|
exitApp()
|
|
644
864
|
}
|
|
645
865
|
return
|
|
646
866
|
}
|
|
647
867
|
|
|
648
|
-
if (mode === "model" && key.name === "escape") {
|
|
868
|
+
if ((mode === "model" || mode === "model-reasoning") && key.name === "escape") {
|
|
869
|
+
setPendingModelEntry(null)
|
|
870
|
+
setModelSearch("")
|
|
649
871
|
setMode("input")
|
|
650
872
|
return
|
|
651
873
|
}
|
|
@@ -653,24 +875,86 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
653
875
|
if (mode === "model") {
|
|
654
876
|
if (key.name === "backspace" || key.name === "delete") {
|
|
655
877
|
setModelSearch((value) => value.slice(0, -1))
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
878
|
+
return
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
const searchCharacter = getModelPickerSearchCharacter(key)
|
|
882
|
+
if (searchCharacter === "T") {
|
|
883
|
+
toggleModelPickerPreference("thinking")
|
|
884
|
+
return
|
|
885
|
+
}
|
|
886
|
+
if (searchCharacter === "F") {
|
|
887
|
+
toggleModelPickerPreference("fast")
|
|
888
|
+
return
|
|
889
|
+
}
|
|
890
|
+
if (isModelPickerSearchInput(searchCharacter)) {
|
|
891
|
+
setModelSearch((value) => `${value}${searchCharacter}`)
|
|
892
|
+
return
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
if (modelItems.length > 0) {
|
|
896
|
+
if (
|
|
897
|
+
applyOpenTuiListNav(key, {
|
|
898
|
+
count: modelItems.length,
|
|
899
|
+
index: modelSelectIndex,
|
|
900
|
+
setIndex: setModelSelectIndex,
|
|
901
|
+
onEnter: () => {
|
|
902
|
+
const item = modelItems[modelSelectIndex]
|
|
903
|
+
if (item) {
|
|
904
|
+
void selectModel(item.value)
|
|
905
|
+
}
|
|
906
|
+
},
|
|
907
|
+
})
|
|
908
|
+
) {
|
|
909
|
+
return
|
|
910
|
+
}
|
|
662
911
|
}
|
|
663
912
|
return
|
|
664
913
|
}
|
|
665
914
|
|
|
666
|
-
if (mode === "
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
915
|
+
if (mode === "model-reasoning" && reasoningItems.length > 0) {
|
|
916
|
+
if (
|
|
917
|
+
applyOpenTuiListNav(key, {
|
|
918
|
+
count: reasoningItems.length,
|
|
919
|
+
index: reasoningSelectIndex,
|
|
920
|
+
setIndex: setReasoningSelectIndex,
|
|
921
|
+
onEnter: () => {
|
|
922
|
+
const item = reasoningItems[reasoningSelectIndex]
|
|
923
|
+
if (item) {
|
|
924
|
+
void selectReasoning(item.value as ModelReasoningLevel)
|
|
925
|
+
}
|
|
926
|
+
},
|
|
927
|
+
})
|
|
928
|
+
) {
|
|
929
|
+
return
|
|
930
|
+
}
|
|
670
931
|
}
|
|
671
932
|
|
|
672
933
|
if (mode === "input") {
|
|
673
934
|
const pageSize = Math.max(1, transcriptViewportRows - 1)
|
|
935
|
+
const paletteOpen =
|
|
936
|
+
input.startsWith("/") && !input.includes(" ") && !menuDismissed && commandMenu.items.length > 0
|
|
937
|
+
|
|
938
|
+
if (paletteOpen && commandItems.length > 0) {
|
|
939
|
+
if (
|
|
940
|
+
applyOpenTuiListNav(key, {
|
|
941
|
+
count: commandItems.length,
|
|
942
|
+
index: commandSelectIndex,
|
|
943
|
+
setIndex: (index) => {
|
|
944
|
+
setCommandSelectIndex(index)
|
|
945
|
+
commandSelectIndexRef.current = index
|
|
946
|
+
},
|
|
947
|
+
onEnter: () => {
|
|
948
|
+
const item = commandItems[commandSelectIndexRef.current]
|
|
949
|
+
if (item) {
|
|
950
|
+
selectCommandOption(commandSelectIndexRef.current, item)
|
|
951
|
+
}
|
|
952
|
+
},
|
|
953
|
+
})
|
|
954
|
+
) {
|
|
955
|
+
return
|
|
956
|
+
}
|
|
957
|
+
}
|
|
674
958
|
|
|
675
959
|
if (key.ctrl && key.name === "t") {
|
|
676
960
|
toggleFeedExpand("think")
|
|
@@ -682,22 +966,64 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
682
966
|
return
|
|
683
967
|
}
|
|
684
968
|
|
|
685
|
-
if (key.
|
|
969
|
+
if (key.ctrl && (key.name === "u" || key.name === "k")) {
|
|
970
|
+
const edit = applySimpleComposerBufferEdit(input, key)
|
|
971
|
+
if (edit.handled) {
|
|
972
|
+
setInput(edit.value)
|
|
973
|
+
resetInputHistoryNav()
|
|
974
|
+
return
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
if (isComposerNewlineKey(key.sequence, key)) {
|
|
686
979
|
setInput((value) => `${value}\n`)
|
|
980
|
+
resetInputHistoryNav()
|
|
687
981
|
return
|
|
688
982
|
}
|
|
689
983
|
|
|
690
|
-
if (key.name === "
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
984
|
+
if (key.name === "escape") {
|
|
985
|
+
if (paletteOpen) {
|
|
986
|
+
setMenuDismissed(true)
|
|
987
|
+
}
|
|
988
|
+
return
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
if (key.name === "tab" && paletteOpen) {
|
|
992
|
+
const command = commandMenu.items[commandSelectIndexRef.current]
|
|
993
|
+
if (command) {
|
|
994
|
+
setInput(`/${command.name} `)
|
|
995
|
+
setMenuDismissed(false)
|
|
996
|
+
}
|
|
997
|
+
return
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
const scrollTranscript = transcriptScrollActive() && !paletteOpen
|
|
1001
|
+
|
|
1002
|
+
if (key.name === "up" || (key.name === "k" && !key.ctrl && !key.meta && !paletteOpen)) {
|
|
1003
|
+
if (scrollTranscript) {
|
|
1004
|
+
markScrollUnpinned()
|
|
1005
|
+
setScrollOffset((offset) => Math.min(maxScrollOffset, offset + 1))
|
|
1006
|
+
} else {
|
|
1007
|
+
const previous = historyPrev()
|
|
1008
|
+
if (previous !== null) {
|
|
1009
|
+
setInput(previous)
|
|
698
1010
|
}
|
|
699
|
-
|
|
700
|
-
|
|
1011
|
+
}
|
|
1012
|
+
} else if (key.name === "down" || (key.name === "j" && !key.ctrl && !key.meta && !paletteOpen)) {
|
|
1013
|
+
if (scrollTranscript) {
|
|
1014
|
+
setScrollOffset((offset) => {
|
|
1015
|
+
const next = Math.max(0, offset - 1)
|
|
1016
|
+
if (next === 0) {
|
|
1017
|
+
scrollPinnedRef.current = true
|
|
1018
|
+
}
|
|
1019
|
+
return next
|
|
1020
|
+
})
|
|
1021
|
+
} else {
|
|
1022
|
+
const next = historyNext()
|
|
1023
|
+
if (next !== null) {
|
|
1024
|
+
setInput(next)
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
701
1027
|
} else if (key.name === "pageup") {
|
|
702
1028
|
markScrollUnpinned()
|
|
703
1029
|
setScrollOffset((offset) => Math.min(maxScrollOffset, offset + pageSize))
|
|
@@ -710,19 +1036,36 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
710
1036
|
return next
|
|
711
1037
|
})
|
|
712
1038
|
} else if (key.name === "home") {
|
|
713
|
-
|
|
714
|
-
|
|
1039
|
+
if (scrollTranscript) {
|
|
1040
|
+
markScrollUnpinned()
|
|
1041
|
+
setScrollOffset(maxScrollOffset)
|
|
1042
|
+
}
|
|
715
1043
|
} else if (key.name === "end") {
|
|
716
|
-
|
|
1044
|
+
if (scrollTranscript) {
|
|
1045
|
+
markScrollPinned()
|
|
1046
|
+
}
|
|
717
1047
|
}
|
|
718
1048
|
}
|
|
719
1049
|
})
|
|
720
1050
|
|
|
721
1051
|
const selectCommandOption = (_index: number, option: SelectOption | null) => {
|
|
722
1052
|
const item = option as CommandSelectItem | null
|
|
723
|
-
if (item?.value) {
|
|
724
|
-
|
|
1053
|
+
if (!item?.value || !slashMenuModule) {
|
|
1054
|
+
return
|
|
1055
|
+
}
|
|
1056
|
+
const command = commandMenu.items.find((entry) => entry.name === item.commandName)
|
|
1057
|
+
if (command && slashMenuModule.commandRequiresArg(command)) {
|
|
1058
|
+
setInput(`/${command.name} `)
|
|
1059
|
+
setMenuDismissed(false)
|
|
1060
|
+
setMode("input")
|
|
1061
|
+
return
|
|
725
1062
|
}
|
|
1063
|
+
void runCommand(item.value)
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
const trackCommandSelection = (index: number) => {
|
|
1067
|
+
commandSelectIndexRef.current = index
|
|
1068
|
+
setCommandSelectIndex(index)
|
|
726
1069
|
}
|
|
727
1070
|
|
|
728
1071
|
const selectModelOption = (_index: number, option: SelectOption | null) => {
|
|
@@ -732,6 +1075,28 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
732
1075
|
}
|
|
733
1076
|
}
|
|
734
1077
|
|
|
1078
|
+
const selectReasoningOption = (_index: number, option: SelectOption | null) => {
|
|
1079
|
+
const item = option as ModelSelectItem | null
|
|
1080
|
+
if (item?.value) {
|
|
1081
|
+
void selectReasoning(item.value as ModelReasoningLevel)
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
const footerHint = buildInteractiveFooterHint({
|
|
1086
|
+
busy,
|
|
1087
|
+
slashMenuOpen: slashPaletteOpen,
|
|
1088
|
+
pickerMode:
|
|
1089
|
+
mode === "model"
|
|
1090
|
+
? "model"
|
|
1091
|
+
: mode === "model-reasoning"
|
|
1092
|
+
? "model-reasoning"
|
|
1093
|
+
: null,
|
|
1094
|
+
modelPickerPreferences:
|
|
1095
|
+
mode === "model"
|
|
1096
|
+
? { thinking: modelThinking, fast: modelFast }
|
|
1097
|
+
: undefined,
|
|
1098
|
+
})
|
|
1099
|
+
|
|
735
1100
|
if (bootError) {
|
|
736
1101
|
return createElement(
|
|
737
1102
|
"box",
|
|
@@ -788,32 +1153,36 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
788
1153
|
attributes: TextAttributes.DIM,
|
|
789
1154
|
})
|
|
790
1155
|
: null,
|
|
791
|
-
mode === "
|
|
1156
|
+
mode === "model-reasoning"
|
|
792
1157
|
? createElement(
|
|
793
1158
|
"box",
|
|
794
1159
|
{
|
|
795
1160
|
border: true,
|
|
796
1161
|
borderStyle: "single",
|
|
797
|
-
borderColor: "
|
|
1162
|
+
borderColor: "magenta",
|
|
798
1163
|
flexDirection: "column",
|
|
799
1164
|
marginTop: 1,
|
|
800
1165
|
paddingX: 1,
|
|
801
1166
|
},
|
|
802
1167
|
createElement("text", {
|
|
803
|
-
content: "
|
|
1168
|
+
content: "Select reasoning effort",
|
|
804
1169
|
attributes: TextAttributes.BOLD,
|
|
805
1170
|
}),
|
|
806
1171
|
createElement("text", {
|
|
807
|
-
content:
|
|
1172
|
+
content: `${formatModelDisplayName(pendingModelEntry?.id ?? currentModel)} · ↑↓ or j/k · 1-9 · Enter · Esc`,
|
|
808
1173
|
fg: "gray",
|
|
809
1174
|
}),
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
1175
|
+
reasoningItems.length === 0
|
|
1176
|
+
? createElement("text", { content: "No reasoning levels.", fg: "yellow" })
|
|
1177
|
+
: createElement("select", {
|
|
1178
|
+
focused: mode === "model-reasoning",
|
|
1179
|
+
height: modelSelectRows,
|
|
1180
|
+
options: reasoningItems,
|
|
1181
|
+
selectedIndex: reasoningSelectIndex,
|
|
1182
|
+
onSelect: selectReasoningOption,
|
|
1183
|
+
showDescription: true,
|
|
1184
|
+
showScrollIndicator: true,
|
|
1185
|
+
})
|
|
817
1186
|
)
|
|
818
1187
|
: mode === "model"
|
|
819
1188
|
? createElement(
|
|
@@ -827,100 +1196,177 @@ export function App({ cwd: cwdArg }: { cwd?: string }) {
|
|
|
827
1196
|
paddingX: 1,
|
|
828
1197
|
},
|
|
829
1198
|
createElement("text", {
|
|
830
|
-
content: "Select
|
|
1199
|
+
content: "Select Model and Effort",
|
|
831
1200
|
attributes: TextAttributes.BOLD,
|
|
832
1201
|
}),
|
|
833
1202
|
createElement("text", {
|
|
834
|
-
content: `Type to search ·
|
|
1203
|
+
content: `Type to search · ${formatModelPickerPreferenceHint(modelThinking, modelFast)} · ↑↓ or j/k · 1-9 · Enter · Esc`,
|
|
835
1204
|
fg: "gray",
|
|
836
1205
|
}),
|
|
837
1206
|
createElement("text", {
|
|
838
1207
|
content: `Search: ${modelSearch || "all models"}`,
|
|
839
1208
|
fg: "gray",
|
|
840
1209
|
}),
|
|
1210
|
+
createElement("text", {
|
|
1211
|
+
content: "Access other models by running patze -m <model> or in config",
|
|
1212
|
+
fg: "gray",
|
|
1213
|
+
}),
|
|
841
1214
|
modelItems.length === 0
|
|
842
1215
|
? createElement("text", { content: "No matching models.", fg: "yellow" })
|
|
843
1216
|
: createElement("select", {
|
|
844
1217
|
focused: mode === "model",
|
|
845
1218
|
height: modelSelectRows,
|
|
846
1219
|
options: modelItems,
|
|
1220
|
+
selectedIndex: modelSelectIndex,
|
|
847
1221
|
onSelect: selectModelOption,
|
|
1222
|
+
showDescription: true,
|
|
848
1223
|
showScrollIndicator: true,
|
|
849
1224
|
})
|
|
850
1225
|
)
|
|
851
1226
|
: createElement(
|
|
852
1227
|
"box",
|
|
853
|
-
{
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
1228
|
+
{ flexDirection: "column", flexShrink: 0 },
|
|
1229
|
+
slashPaletteOpen && commandItems.length > 0
|
|
1230
|
+
? createElement(
|
|
1231
|
+
"box",
|
|
1232
|
+
{
|
|
1233
|
+
border: true,
|
|
1234
|
+
borderStyle: "single",
|
|
1235
|
+
borderColor: "cyan",
|
|
1236
|
+
flexDirection: "column",
|
|
1237
|
+
marginTop: 1,
|
|
1238
|
+
paddingX: 1,
|
|
1239
|
+
},
|
|
1240
|
+
createElement("text", {
|
|
1241
|
+
content: "Commands",
|
|
1242
|
+
attributes: TextAttributes.BOLD,
|
|
1243
|
+
}),
|
|
1244
|
+
createElement("text", {
|
|
1245
|
+
content:
|
|
1246
|
+
commandMenu.advancedCount > 0
|
|
1247
|
+
? `${commandMenu.advancedCount} advanced · Tab · ↑↓ or j/k · 1-9 · Enter · Esc`
|
|
1248
|
+
: "Tab · ↑↓ or j/k · 1-9 jump · Enter · Esc dismiss",
|
|
1249
|
+
fg: "gray",
|
|
1250
|
+
}),
|
|
1251
|
+
createElement("select", {
|
|
1252
|
+
focused: false,
|
|
1253
|
+
height: commandSelectRows,
|
|
1254
|
+
options: commandItems,
|
|
1255
|
+
selectedIndex: commandSelectIndex,
|
|
1256
|
+
onChange: trackCommandSelection,
|
|
1257
|
+
onSelect: selectCommandOption,
|
|
1258
|
+
showDescription: true,
|
|
1259
|
+
showScrollIndicator: true,
|
|
1260
|
+
})
|
|
1261
|
+
)
|
|
1262
|
+
: null,
|
|
861
1263
|
createElement(
|
|
862
1264
|
"box",
|
|
863
|
-
{
|
|
1265
|
+
{
|
|
1266
|
+
border: true,
|
|
1267
|
+
borderStyle: "single",
|
|
1268
|
+
borderColor: busy ? "yellow" : "green",
|
|
1269
|
+
flexDirection: "column",
|
|
1270
|
+
marginTop: 1,
|
|
1271
|
+
paddingX: 1,
|
|
1272
|
+
},
|
|
1273
|
+
createElement(
|
|
1274
|
+
"box",
|
|
1275
|
+
{ flexDirection: "row", alignItems: "center" },
|
|
1276
|
+
createElement("text", {
|
|
1277
|
+
content: "> ",
|
|
1278
|
+
attributes: TextAttributes.BOLD,
|
|
1279
|
+
}),
|
|
1280
|
+
createElement(TuiInput, {
|
|
1281
|
+
focused: ready && !busy,
|
|
1282
|
+
placeholder: busy
|
|
1283
|
+
? activityLabel
|
|
1284
|
+
? `${["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"][activityFrame % 10]} ${activityLabel}… · Ctrl+C cancel`
|
|
1285
|
+
: "Ctrl+C cancel · waiting for agent…"
|
|
1286
|
+
: ready
|
|
1287
|
+
? composerPlaceholder
|
|
1288
|
+
: "Loading Patze Code…",
|
|
1289
|
+
value: input,
|
|
1290
|
+
onInput: (value: string) => {
|
|
1291
|
+
setInput(value)
|
|
1292
|
+
resetInputHistoryNav()
|
|
1293
|
+
if (busy) {
|
|
1294
|
+
return
|
|
1295
|
+
}
|
|
1296
|
+
if (!value.startsWith("/")) {
|
|
1297
|
+
setMenuDismissed(false)
|
|
1298
|
+
}
|
|
1299
|
+
if (value.startsWith("/") && !value.includes(" ") && !menuDismissed) {
|
|
1300
|
+
setCommandSelectIndex(0)
|
|
1301
|
+
commandSelectIndexRef.current = 0
|
|
1302
|
+
}
|
|
1303
|
+
},
|
|
1304
|
+
onSubmit: submitInput,
|
|
1305
|
+
})
|
|
1306
|
+
),
|
|
864
1307
|
createElement("text", {
|
|
865
|
-
content:
|
|
866
|
-
attributes: TextAttributes.
|
|
867
|
-
}),
|
|
868
|
-
createElement(TuiInput, {
|
|
869
|
-
focused: ready && !busy,
|
|
870
|
-
placeholder: busy
|
|
871
|
-
? activityLabel
|
|
872
|
-
? `${["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"][activityFrame % 10]} ${activityLabel}… · Ctrl+C cancel`
|
|
873
|
-
: "Ctrl+C cancel · waiting for agent…"
|
|
874
|
-
: ready
|
|
875
|
-
? composerPlaceholder
|
|
876
|
-
: "Loading Patze Code…",
|
|
877
|
-
value: input,
|
|
878
|
-
onInput: (value: string) => {
|
|
879
|
-
setInput(value)
|
|
880
|
-
if (busy) {
|
|
881
|
-
return
|
|
882
|
-
}
|
|
883
|
-
if (value.startsWith("/") && !value.includes(" ")) {
|
|
884
|
-
setMode("command")
|
|
885
|
-
} else {
|
|
886
|
-
setMode("input")
|
|
887
|
-
}
|
|
888
|
-
},
|
|
889
|
-
onSubmit: submitInput,
|
|
1308
|
+
content: `${composerModelLabel} · ${composerPathLabel}`,
|
|
1309
|
+
attributes: TextAttributes.DIM,
|
|
890
1310
|
})
|
|
891
|
-
)
|
|
892
|
-
createElement("text", {
|
|
893
|
-
content: `${composerModelLabel} · ${composerPathLabel}`,
|
|
894
|
-
attributes: TextAttributes.DIM,
|
|
895
|
-
})
|
|
1311
|
+
)
|
|
896
1312
|
),
|
|
897
1313
|
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",
|
|
1314
|
+
content: footerHint,
|
|
901
1315
|
attributes: TextAttributes.DIM,
|
|
902
1316
|
})
|
|
903
1317
|
)
|
|
904
1318
|
}
|
|
905
1319
|
|
|
1320
|
+
function applyOpenTuiListNav(
|
|
1321
|
+
key: KeyEvent,
|
|
1322
|
+
input: {
|
|
1323
|
+
count: number
|
|
1324
|
+
index: number
|
|
1325
|
+
setIndex: (index: number) => void
|
|
1326
|
+
onEnter: () => void
|
|
1327
|
+
}
|
|
1328
|
+
): boolean {
|
|
1329
|
+
if (input.count <= 0) {
|
|
1330
|
+
return false
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
const digitSource =
|
|
1334
|
+
key.sequence && key.sequence.length === 1 ? key.sequence : (key.name ?? "")
|
|
1335
|
+
const jump = jumpListSelection(digitSource, input.count)
|
|
1336
|
+
if (jump !== null) {
|
|
1337
|
+
input.setIndex(jump)
|
|
1338
|
+
return true
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
const nav = mapListNavKey(key.name, key.sequence)
|
|
1342
|
+
if (nav === "up") {
|
|
1343
|
+
input.setIndex(moveListSelection(input.index, -1, input.count))
|
|
1344
|
+
return true
|
|
1345
|
+
}
|
|
1346
|
+
if (nav === "down") {
|
|
1347
|
+
input.setIndex(moveListSelection(input.index, 1, input.count))
|
|
1348
|
+
return true
|
|
1349
|
+
}
|
|
1350
|
+
if (nav === "enter") {
|
|
1351
|
+
input.onEnter()
|
|
1352
|
+
return true
|
|
1353
|
+
}
|
|
1354
|
+
return false
|
|
1355
|
+
}
|
|
1356
|
+
|
|
906
1357
|
function trimComposerPrompt(value: string): string {
|
|
907
1358
|
return value.replace(/^\s+|\s+$/g, "")
|
|
908
1359
|
}
|
|
909
1360
|
|
|
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
|
-
}
|
|
1361
|
+
function getModelPickerSearchCharacter(key: KeyEvent): string {
|
|
1362
|
+
if (key.ctrl || key.meta || key.name.length !== 1) {
|
|
1363
|
+
return ""
|
|
922
1364
|
}
|
|
923
|
-
return
|
|
1365
|
+
return key.shift ? key.name.toUpperCase() : key.name
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
function isModelPickerSearchInput(input: string): boolean {
|
|
1369
|
+
return input.length > 0 && !/[\u0000-\u001F\u007F]/.test(input)
|
|
924
1370
|
}
|
|
925
1371
|
|
|
926
1372
|
function HeaderLine({ line }: { line: string }) {
|
|
@@ -985,31 +1431,8 @@ function partToAttributes(part: TranscriptPart) {
|
|
|
985
1431
|
return attributes
|
|
986
1432
|
}
|
|
987
1433
|
|
|
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
1434
|
type SlashMenuModule = typeof import("../../dist/cli/interactive/slash-menu.js")
|
|
1000
1435
|
|
|
1001
1436
|
let slashMenuModule: SlashMenuModule | null = null
|
|
1002
1437
|
|
|
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
1438
|
export { parseCwdArg }
|