@golba98/codexa 1.0.2 → 1.0.4
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/README.md +396 -100
- package/bin/codexa.js +62 -144
- package/package.json +14 -8
- package/src/app.tsx +596 -306
- package/src/commands/handler.ts +6 -6
- package/src/config/buildInfo.ts +2 -2
- package/src/config/layeredConfig.ts +1 -1
- package/src/config/persistence.ts +10 -0
- package/src/config/runtimeConfig.ts +1 -1
- package/src/config/settings.ts +8 -16
- package/src/config/trustStore.ts +1 -1
- package/src/config/updateCheckCache.ts +19 -1
- package/src/core/README.md +52 -0
- package/src/core/agent/loop.ts +282 -0
- package/src/core/agent/protocol.ts +211 -0
- package/src/core/agent/tools.ts +414 -0
- package/src/core/{codexExecArgs.ts → codex/codexExecArgs.ts} +2 -2
- package/src/core/{codexLaunch.ts → codex/codexLaunch.ts} +3 -3
- package/src/core/{codexPrompt.ts → codex/codexPrompt.ts} +3 -3
- package/src/core/debug/modelStateDebug.ts +34 -0
- package/src/core/executables/antigravityExecutable.ts +48 -0
- package/src/core/executables/codexExecutable.ts +1 -0
- package/src/core/executables/executableResolver.ts +65 -43
- package/src/core/perf/renderDebug.ts +10 -6
- package/src/core/process/processValidation.ts +9 -5
- package/src/core/providerLauncher/launcher.ts +59 -42
- package/src/core/providerLauncher/registry.ts +30 -14
- package/src/core/providerLauncher/types.ts +11 -9
- package/src/core/providerLauncher/workspaceConfig.ts +41 -26
- package/src/core/providerRuntime/anthropic.ts +7 -1
- package/src/core/providerRuntime/antigravity.ts +305 -0
- package/src/core/providerRuntime/claudeCodeDiscovery.ts +268 -22
- package/src/core/providerRuntime/claudeCodeDiscoveryDebug.ts +55 -0
- package/src/core/providerRuntime/contextMetadata.ts +12 -24
- package/src/core/providerRuntime/local.ts +129 -51
- package/src/core/providerRuntime/models.ts +22 -11
- package/src/core/providerRuntime/registry.ts +58 -31
- package/src/core/providerRuntime/types.ts +19 -14
- package/src/core/providers/codexSubprocess.ts +2 -2
- package/src/core/providers/types.ts +1 -1
- package/src/core/{attachments.ts → shared/attachments.ts} +27 -4
- package/src/core/{cleanupFastFail.ts → shared/cleanupFastFail.ts} +1 -1
- package/src/core/{hollowResponseFormat.ts → shared/hollowResponseFormat.ts} +1 -1
- package/src/core/terminal/clearFrameBoundary.ts +814 -0
- package/src/core/terminal/frameLock.ts +109 -0
- package/src/core/terminal/inkRenderReset.ts +123 -0
- package/src/core/terminal/terminalControl.ts +22 -0
- package/src/core/terminal/terminalTitle.ts +16 -102
- package/src/core/version/channel.ts +23 -0
- package/src/core/version/updateCheck.ts +193 -0
- package/src/core/{launchContext.ts → workspace/launchContext.ts} +32 -39
- package/src/core/{planStorage.ts → workspace/planStorage.ts} +2 -2
- package/src/core/{workspaceGuard.ts → workspace/workspaceGuard.ts} +97 -13
- package/src/headless/execRunner.ts +2 -2
- package/src/index.tsx +43 -89
- package/src/session/appSession.ts +10 -7
- package/src/session/chatLifecycle.ts +1 -1
- package/src/session/liveRenderScheduler.ts +1 -1
- package/src/session/types.ts +3 -2
- package/src/ui/ActionRequiredBlock.tsx +5 -5
- package/src/ui/ActivityBars.tsx +3 -3
- package/src/ui/ActivityIndicator.tsx +6 -6
- package/src/ui/AgentBlock.tsx +6 -6
- package/src/ui/AnimatedStatusText.tsx +1 -1
- package/src/ui/AppShell.tsx +670 -719
- package/src/ui/AttachmentImportPanel.tsx +8 -8
- package/src/ui/AuthPanel.tsx +20 -20
- package/src/ui/BottomComposer.tsx +158 -118
- package/src/ui/DashCard.tsx +3 -3
- package/src/ui/Markdown.tsx +17 -17
- package/src/ui/ModelPickerScreen.tsx +222 -42
- package/src/ui/ModelReasoningPicker.tsx +15 -15
- package/src/ui/Panel.tsx +3 -3
- package/src/ui/PlanActionPicker.tsx +6 -6
- package/src/ui/PlanReviewPanel.tsx +9 -9
- package/src/ui/ProviderPicker.tsx +735 -321
- package/src/ui/RunFooter.tsx +3 -3
- package/src/ui/RuntimeStatusBar.tsx +108 -0
- package/src/ui/SelectionPanel.tsx +8 -4
- package/src/ui/SettingsPanel.tsx +9 -9
- package/src/ui/Spinner.tsx +1 -1
- package/src/ui/TextEntryPanel.tsx +11 -11
- package/src/ui/ThinkingBlock.tsx +8 -8
- package/src/ui/Timeline.tsx +1625 -1472
- package/src/ui/TopHeader.tsx +437 -293
- package/src/ui/TranscriptShell.tsx +322 -0
- package/src/ui/TurnGroup.tsx +33 -33
- package/src/ui/UpdateAvailableCard.tsx +41 -0
- package/src/ui/UpdatePromptPanel.tsx +197 -0
- package/src/ui/focus.ts +3 -0
- package/src/ui/layout.ts +299 -25
- package/src/ui/layoutListWindow.ts +145 -0
- package/src/ui/logoVariants.ts +103 -0
- package/src/ui/modeDisplay.ts +12 -12
- package/src/ui/runtimeDisplay.ts +112 -0
- package/src/ui/textLayout.ts +15 -4
- package/src/ui/theme.tsx +274 -395
- package/src/ui/timelineMeasure.ts +218 -136
- package/scripts/audit-codexa-capabilities.mjs +0 -466
- package/scripts/gen-build-info.mjs +0 -33
- package/scripts/smoke-terminal-bench.mjs +0 -35
- package/src/appRenderStability.test.ts +0 -131
- package/src/commands/handler.test.ts +0 -655
- package/src/config/launchArgs.test.ts +0 -189
- package/src/config/layeredConfig.test.ts +0 -143
- package/src/config/persistence.test.ts +0 -114
- package/src/config/runtimeConfig.test.ts +0 -218
- package/src/config/settings.test.ts +0 -155
- package/src/config/trustStore.test.ts +0 -29
- package/src/core/attachments.test.ts +0 -155
- package/src/core/auth/codexAuth.test.ts +0 -68
- package/src/core/cleanupFastFail.test.ts +0 -76
- package/src/core/codex.ts +0 -124
- package/src/core/codexExecArgs.test.ts +0 -195
- package/src/core/codexLaunch.test.ts +0 -205
- package/src/core/codexPrompt.test.ts +0 -252
- package/src/core/executables/codexExecutable.test.ts +0 -212
- package/src/core/executables/executableResolver.test.ts +0 -129
- package/src/core/executables/geminiExecutable.test.ts +0 -116
- package/src/core/executables/pathSanityScan.test.ts +0 -47
- package/src/core/githubDiagnostics.test.ts +0 -92
- package/src/core/hollowResponseFormat.test.ts +0 -58
- package/src/core/launchContext.test.ts +0 -157
- package/src/core/models/codexCapabilities.test.ts +0 -45
- package/src/core/models/codexModelCapabilities.test.ts +0 -246
- package/src/core/models/modelSpecs.test.ts +0 -283
- package/src/core/perf/renderDebug.test.ts +0 -230
- package/src/core/planStorage.test.ts +0 -143
- package/src/core/process/CommandRunner.test.ts +0 -105
- package/src/core/projectInstructions.test.ts +0 -50
- package/src/core/providerLauncher/launcher.test.ts +0 -238
- package/src/core/providerLauncher/registry.test.ts +0 -324
- package/src/core/providerLauncher/workspaceConfig.test.ts +0 -638
- package/src/core/providerRuntime/anthropic.test.ts +0 -1120
- package/src/core/providerRuntime/capabilityProfile.test.ts +0 -311
- package/src/core/providerRuntime/contextMetadata.test.ts +0 -468
- package/src/core/providerRuntime/gemini.test.ts +0 -437
- package/src/core/providerRuntime/lmstudio.test.ts +0 -168
- package/src/core/providerRuntime/local.test.ts +0 -787
- package/src/core/providerRuntime/registry.test.ts +0 -233
- package/src/core/providers/codexJsonStream.test.ts +0 -148
- package/src/core/providers/codexSubprocess.test.ts +0 -68
- package/src/core/providers/codexTranscript.test.ts +0 -284
- package/src/core/terminal/startupClear.test.ts +0 -55
- package/src/core/terminal/terminalCapabilities.test.ts +0 -93
- package/src/core/terminal/terminalControl.test.ts +0 -75
- package/src/core/terminal/terminalSanitize.test.ts +0 -22
- package/src/core/terminal/terminalSelection.test.ts +0 -42
- package/src/core/terminal/terminalTitle.test.ts +0 -328
- package/src/core/updateCheck.test.ts +0 -194
- package/src/core/updateCheck.ts +0 -172
- package/src/core/workspaceActivity.test.ts +0 -163
- package/src/core/workspaceGuard.test.ts +0 -151
- package/src/core/workspaceRoot.test.ts +0 -23
- package/src/exec.test.ts +0 -13
- package/src/headless/execArgs.test.ts +0 -147
- package/src/headless/execRunner.test.ts +0 -436
- package/src/index.test.tsx +0 -620
- package/src/session/appSession.test.ts +0 -897
- package/src/session/chatLifecycle.test.ts +0 -64
- package/src/session/liveRenderScheduler.test.ts +0 -201
- package/src/session/planFlow.test.ts +0 -103
- package/src/session/planTranscript.test.ts +0 -65
- package/src/session/promptRunSchedule.test.ts +0 -36
- package/src/ui/ActivityIndicator.test.tsx +0 -58
- package/src/ui/AgentBlock.test.ts +0 -6
- package/src/ui/AnimatedStatusText.test.ts +0 -16
- package/src/ui/AppShell.test.tsx +0 -1776
- package/src/ui/AttachmentImportPanel.test.tsx +0 -204
- package/src/ui/BottomComposer.test.ts +0 -674
- package/src/ui/CodexLogo.tsx +0 -55
- package/src/ui/Markdown.test.ts +0 -157
- package/src/ui/ModelPickerProviderScope.test.tsx +0 -411
- package/src/ui/ModelPickerScreen.test.tsx +0 -99
- package/src/ui/ModelPickerState.test.tsx +0 -151
- package/src/ui/ModelReasoningPicker.test.tsx +0 -447
- package/src/ui/PlanReviewPanel.test.tsx +0 -267
- package/src/ui/PromptCardBorder.test.tsx +0 -161
- package/src/ui/ProviderPicker.test.tsx +0 -289
- package/src/ui/ProviderShortcut.test.tsx +0 -143
- package/src/ui/SettingsPanel.test.tsx +0 -233
- package/src/ui/StaticTranscriptItem.tsx +0 -56
- package/src/ui/Timeline.test.ts +0 -2067
- package/src/ui/TimelineNavigation.test.tsx +0 -201
- package/src/ui/TopHeader.test.tsx +0 -254
- package/src/ui/TurnGroup.test.tsx +0 -365
- package/src/ui/busyStatusAnimation.test.ts +0 -30
- package/src/ui/commandNormalize.test.ts +0 -142
- package/src/ui/diffRenderer.test.ts +0 -102
- package/src/ui/focusFlow.test.tsx +0 -1098
- package/src/ui/inputBuffer.test.ts +0 -151
- package/src/ui/layout.test.ts +0 -146
- package/src/ui/modeDisplay.test.ts +0 -42
- package/src/ui/runActivityView.test.ts +0 -89
- package/src/ui/runLifecycleView.test.tsx +0 -237
- package/src/ui/statusRenderIsolation.test.tsx +0 -654
- package/src/ui/terminalAnswerFormat.test.ts +0 -19
- package/src/ui/textLayout.test.ts +0 -18
- package/src/ui/themeFlow.test.ts +0 -53
- package/src/ui/timelineMeasureCache.test.ts +0 -986
- /package/src/core/{inputDebug.ts → debug/inputDebug.ts} +0 -0
- /package/src/core/{clipboard.ts → shared/clipboard.ts} +0 -0
- /package/src/core/{githubDiagnostics.ts → shared/githubDiagnostics.ts} +0 -0
- /package/src/core/{projectInstructions.ts → workspace/projectInstructions.ts} +0 -0
- /package/src/core/{workspaceActivity.ts → workspace/workspaceActivity.ts} +0 -0
- /package/src/core/{workspaceRoot.ts → workspace/workspaceRoot.ts} +0 -0
|
@@ -1,655 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import type { LayeredConfigResult } from "../config/layeredConfig.js";
|
|
4
|
-
import { normalizeRuntimeConfig, resolveRuntimeConfig } from "../config/runtimeConfig.js";
|
|
5
|
-
import { normalizeCodexModelListResponses } from "../core/models/codexModelCapabilities.js";
|
|
6
|
-
import { handleCommand, type CommandContext } from "./handler.js";
|
|
7
|
-
|
|
8
|
-
const baseRuntime = normalizeRuntimeConfig({
|
|
9
|
-
provider: "codex-subprocess",
|
|
10
|
-
model: "gpt-5.4",
|
|
11
|
-
mode: "suggest",
|
|
12
|
-
reasoningLevel: "high",
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
const baseConfig: LayeredConfigResult = {
|
|
16
|
-
runtime: baseRuntime,
|
|
17
|
-
diagnostics: {
|
|
18
|
-
projectRoot: "C:\\Workspace",
|
|
19
|
-
projectTrusted: false,
|
|
20
|
-
selectedProfile: null,
|
|
21
|
-
selectedProfileSource: null,
|
|
22
|
-
cliOverrides: [],
|
|
23
|
-
layers: [
|
|
24
|
-
{ label: "Built-in defaults", status: "loaded" as const },
|
|
25
|
-
{ label: "User config", status: "missing" as const, path: "C:\\Users\\Test\\.codex\\config.toml" },
|
|
26
|
-
],
|
|
27
|
-
ignoredEntries: [],
|
|
28
|
-
fieldSources: {
|
|
29
|
-
provider: "Built-in defaults",
|
|
30
|
-
model: "Built-in defaults",
|
|
31
|
-
reasoningLevel: "Built-in defaults",
|
|
32
|
-
mode: "Built-in defaults",
|
|
33
|
-
planMode: "Built-in defaults",
|
|
34
|
-
geminiCommandPath: "Built-in defaults",
|
|
35
|
-
"policy.approvalPolicy": "Built-in defaults",
|
|
36
|
-
"policy.sandboxMode": "Built-in defaults",
|
|
37
|
-
"policy.networkAccess": "Built-in defaults",
|
|
38
|
-
"policy.writableRoots": "Built-in defaults",
|
|
39
|
-
"policy.serviceTier": "Built-in defaults",
|
|
40
|
-
"policy.personality": "Built-in defaults",
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const baseContext: CommandContext = {
|
|
46
|
-
config: baseConfig,
|
|
47
|
-
runtime: baseRuntime,
|
|
48
|
-
resolvedRuntime: resolveRuntimeConfig(baseRuntime),
|
|
49
|
-
settings: {
|
|
50
|
-
workspaceDisplayMode: "dir",
|
|
51
|
-
terminalTitleMode: "dir",
|
|
52
|
-
showBusyLoader: true,
|
|
53
|
-
},
|
|
54
|
-
workspace: {
|
|
55
|
-
root: "C:\\Workspace",
|
|
56
|
-
summaryMessage: [
|
|
57
|
-
"Active workspace:",
|
|
58
|
-
" C:\\Workspace",
|
|
59
|
-
"",
|
|
60
|
-
"Launch mode: installed codexa",
|
|
61
|
-
].join("\n"),
|
|
62
|
-
},
|
|
63
|
-
tokensUsed: 1200,
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
function runCommand(command: string, context: Partial<CommandContext> = {}) {
|
|
67
|
-
return handleCommand(command, {
|
|
68
|
-
...baseContext,
|
|
69
|
-
...context,
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const dynamicCapabilities = normalizeCodexModelListResponses([
|
|
74
|
-
{
|
|
75
|
-
data: [
|
|
76
|
-
{
|
|
77
|
-
id: "dynamic-four",
|
|
78
|
-
model: "dynamic-four",
|
|
79
|
-
displayName: "Dynamic Four",
|
|
80
|
-
hidden: false,
|
|
81
|
-
isDefault: true,
|
|
82
|
-
defaultReasoningEffort: "medium",
|
|
83
|
-
supportedReasoningEfforts: [
|
|
84
|
-
{ reasoningEffort: "low", description: "Low" },
|
|
85
|
-
{ reasoningEffort: "medium", description: "Medium" },
|
|
86
|
-
{ reasoningEffort: "high", description: "High" },
|
|
87
|
-
{ reasoningEffort: "xhigh", description: "Extra" },
|
|
88
|
-
],
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
id: "dynamic-two",
|
|
92
|
-
model: "dynamic-two",
|
|
93
|
-
displayName: "Dynamic Two",
|
|
94
|
-
hidden: false,
|
|
95
|
-
isDefault: false,
|
|
96
|
-
defaultReasoningEffort: "medium",
|
|
97
|
-
supportedReasoningEfforts: [
|
|
98
|
-
{ reasoningEffort: "medium", description: "Medium" },
|
|
99
|
-
{ reasoningEffort: "high", description: "High" },
|
|
100
|
-
],
|
|
101
|
-
},
|
|
102
|
-
],
|
|
103
|
-
},
|
|
104
|
-
]);
|
|
105
|
-
|
|
106
|
-
test("parses /login command", () => {
|
|
107
|
-
const result = runCommand("/login");
|
|
108
|
-
assert.equal(result?.action, "login");
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
test("parses /logout command", () => {
|
|
112
|
-
const result = runCommand("/logout");
|
|
113
|
-
assert.equal(result?.action, "logout");
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
test("parses /auth status as auth_status action", () => {
|
|
117
|
-
const result = runCommand("/auth status");
|
|
118
|
-
assert.equal(result?.action, "auth_status");
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
test("keeps existing /model command behavior", () => {
|
|
122
|
-
const result = runCommand("/model gpt-5.4-mini");
|
|
123
|
-
assert.equal(result?.action, "model");
|
|
124
|
-
assert.equal(result?.value, "gpt-5.4-mini");
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
test("accepts dynamically detected model ids", () => {
|
|
128
|
-
const result = runCommand("/model dynamic-two", {
|
|
129
|
-
modelCapabilities: dynamicCapabilities,
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
assert.equal(result?.action, "model");
|
|
133
|
-
assert.equal(result?.value, "dynamic-two");
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
test("resolves canonical and aliased /mode commands", () => {
|
|
137
|
-
const cases = [
|
|
138
|
-
["/mode suggest", "suggest"],
|
|
139
|
-
["/mode auto-edit", "auto-edit"],
|
|
140
|
-
["/mode full-auto", "full-auto"],
|
|
141
|
-
["/mode default", "full-auto"],
|
|
142
|
-
["/mode ask", "suggest"],
|
|
143
|
-
["/mode auto", "auto-edit"],
|
|
144
|
-
["/mode plan", "suggest"],
|
|
145
|
-
] as const;
|
|
146
|
-
|
|
147
|
-
for (const [command, expectedValue] of cases) {
|
|
148
|
-
const result = runCommand(command);
|
|
149
|
-
assert.equal(result?.action, "mode", command);
|
|
150
|
-
assert.equal(result?.value, expectedValue, command);
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
test("opens the reasoning picker when /reasoning has no argument", () => {
|
|
155
|
-
const result = runCommand("/reasoning");
|
|
156
|
-
assert.equal(result?.action, "open_reasoning_picker");
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
test("accepts explicit reasoning levels", () => {
|
|
160
|
-
const result = runCommand("/reasoning medium");
|
|
161
|
-
assert.equal(result?.action, "reasoning");
|
|
162
|
-
assert.equal(result?.value, "medium");
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
test("accepts extra high reasoning aliases", () => {
|
|
166
|
-
const result = runCommand("/reasoning extra high");
|
|
167
|
-
assert.equal(result?.action, "reasoning");
|
|
168
|
-
assert.equal(result?.value, "xhigh");
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
test("validates reasoning against detected levels for the active model", () => {
|
|
172
|
-
const runtime = normalizeRuntimeConfig({
|
|
173
|
-
model: "dynamic-two",
|
|
174
|
-
reasoningLevel: "medium",
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
const accepted = runCommand("/reasoning high", {
|
|
178
|
-
runtime,
|
|
179
|
-
resolvedRuntime: resolveRuntimeConfig(runtime),
|
|
180
|
-
modelCapabilities: dynamicCapabilities,
|
|
181
|
-
});
|
|
182
|
-
assert.equal(accepted?.action, "reasoning");
|
|
183
|
-
assert.equal(accepted?.value, "high");
|
|
184
|
-
|
|
185
|
-
const rejected = runCommand("/reasoning xhigh", {
|
|
186
|
-
runtime,
|
|
187
|
-
resolvedRuntime: resolveRuntimeConfig(runtime),
|
|
188
|
-
modelCapabilities: dynamicCapabilities,
|
|
189
|
-
});
|
|
190
|
-
assert.equal(rejected?.action, "unknown");
|
|
191
|
-
assert.match(rejected?.message ?? "", /Valid: medium, high/i);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
test("shows and toggles plan mode", () => {
|
|
195
|
-
const statusResult = runCommand("/plan");
|
|
196
|
-
assert.equal(statusResult?.action, "plan_mode");
|
|
197
|
-
assert.equal(statusResult?.message, "Plan mode: Disabled.");
|
|
198
|
-
|
|
199
|
-
const explicitStatusResult = runCommand("/plan status", {
|
|
200
|
-
runtime: normalizeRuntimeConfig({ planMode: true }),
|
|
201
|
-
});
|
|
202
|
-
assert.equal(explicitStatusResult?.action, "plan_mode");
|
|
203
|
-
assert.equal(explicitStatusResult?.message, "Plan mode: Enabled.");
|
|
204
|
-
|
|
205
|
-
const enableResult = runCommand("/plan on");
|
|
206
|
-
assert.equal(enableResult?.action, "plan_mode");
|
|
207
|
-
assert.equal(enableResult?.value, "on");
|
|
208
|
-
|
|
209
|
-
const disableResult = runCommand("/plan off");
|
|
210
|
-
assert.equal(disableResult?.action, "plan_mode");
|
|
211
|
-
assert.equal(disableResult?.value, "off");
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
test("rejects invalid /plan usage with a short hint", () => {
|
|
215
|
-
const result = runCommand("/plan maybe");
|
|
216
|
-
assert.equal(result?.action, "unknown");
|
|
217
|
-
assert.equal(result?.message, "Usage: /plan [on|off]");
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
test("opens the settings panel and updates workspace and busy loader settings", () => {
|
|
221
|
-
const statusResult = runCommand("/setting");
|
|
222
|
-
assert.equal(statusResult?.action, "open_settings_panel");
|
|
223
|
-
assert.equal(statusResult?.message, undefined);
|
|
224
|
-
|
|
225
|
-
const pluralStatusResult = runCommand("/settings");
|
|
226
|
-
assert.equal(pluralStatusResult?.action, "open_settings_panel");
|
|
227
|
-
assert.equal(pluralStatusResult?.message, undefined);
|
|
228
|
-
|
|
229
|
-
const workspaceResult = runCommand("/setting workspace", {
|
|
230
|
-
settings: {
|
|
231
|
-
workspaceDisplayMode: "simple",
|
|
232
|
-
terminalTitleMode: "name",
|
|
233
|
-
showBusyLoader: true,
|
|
234
|
-
},
|
|
235
|
-
});
|
|
236
|
-
assert.equal(workspaceResult?.action, "setting_workspace_display");
|
|
237
|
-
assert.match(workspaceResult?.message ?? "", /Simple \(simple\)/i);
|
|
238
|
-
assert.match(workspaceResult?.message ?? "", /Allowed values: dir, name, simple/i);
|
|
239
|
-
|
|
240
|
-
const setResult = runCommand("/setting workspace name");
|
|
241
|
-
assert.equal(setResult?.action, "setting_workspace_display");
|
|
242
|
-
assert.equal(setResult?.value, "name");
|
|
243
|
-
|
|
244
|
-
const terminalResult = runCommand("/setting terminal-title", {
|
|
245
|
-
settings: {
|
|
246
|
-
workspaceDisplayMode: "dir",
|
|
247
|
-
terminalTitleMode: "simple",
|
|
248
|
-
showBusyLoader: true,
|
|
249
|
-
},
|
|
250
|
-
});
|
|
251
|
-
assert.equal(terminalResult?.action, "setting_terminal_title");
|
|
252
|
-
assert.match(terminalResult?.message ?? "", /Simple \(simple\)/i);
|
|
253
|
-
|
|
254
|
-
const setTerminalResult = runCommand("/setting terminal-title name");
|
|
255
|
-
assert.equal(setTerminalResult?.action, "setting_terminal_title");
|
|
256
|
-
assert.equal(setTerminalResult?.value, "name");
|
|
257
|
-
|
|
258
|
-
const legacyResult = runCommand("/setting directory normal");
|
|
259
|
-
assert.equal(legacyResult?.action, "setting_workspace_display");
|
|
260
|
-
assert.equal(legacyResult?.value, "dir");
|
|
261
|
-
|
|
262
|
-
const busyResult = runCommand("/setting busy-loader false");
|
|
263
|
-
assert.equal(busyResult?.action, "setting_busy_loader");
|
|
264
|
-
assert.equal(busyResult?.value, "false");
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
test("recognizes /settings without falling through to unknown command handling", () => {
|
|
268
|
-
const result = runCommand("/settings");
|
|
269
|
-
|
|
270
|
-
assert.equal(result?.action, "open_settings_panel");
|
|
271
|
-
assert.notEqual(result?.action, "unknown");
|
|
272
|
-
assert.doesNotMatch(result?.message ?? "", /Unknown command/i);
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
test("shows busy loader setting status", () => {
|
|
276
|
-
const result = runCommand("/setting busy-loader", {
|
|
277
|
-
settings: {
|
|
278
|
-
workspaceDisplayMode: "dir",
|
|
279
|
-
terminalTitleMode: "dir",
|
|
280
|
-
showBusyLoader: false,
|
|
281
|
-
},
|
|
282
|
-
});
|
|
283
|
-
assert.equal(result?.action, "setting_busy_loader");
|
|
284
|
-
assert.equal(result?.message, "Busy loader: false");
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
test("rejects invalid /setting usage with a short hint", () => {
|
|
288
|
-
const invalidValue = runCommand("/setting directory compact");
|
|
289
|
-
assert.equal(invalidValue?.action, "unknown");
|
|
290
|
-
assert.equal(invalidValue?.message, "Usage: /setting workspace [dir|name|simple]");
|
|
291
|
-
|
|
292
|
-
const invalidTerminalValue = runCommand("/setting terminal-title compact");
|
|
293
|
-
assert.equal(invalidTerminalValue?.action, "unknown");
|
|
294
|
-
assert.equal(invalidTerminalValue?.message, "Usage: /setting terminal-title [dir|name|simple]");
|
|
295
|
-
|
|
296
|
-
const invalidBusyLoader = runCommand("/setting busy-loader maybe");
|
|
297
|
-
assert.equal(invalidBusyLoader?.action, "unknown");
|
|
298
|
-
assert.equal(invalidBusyLoader?.message, "Usage: /setting busy-loader [true|false]");
|
|
299
|
-
|
|
300
|
-
const invalidSetting = runCommand("/setting theme");
|
|
301
|
-
assert.equal(invalidSetting?.action, "unknown");
|
|
302
|
-
assert.equal(invalidSetting?.message, "Usage: /setting, /setting workspace [dir|name|simple], /setting terminal-title [dir|name|simple], or /setting busy-loader [true|false]");
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
test("shows effective runtime status", () => {
|
|
306
|
-
const result = runCommand("/status");
|
|
307
|
-
assert.equal(result?.action, "status");
|
|
308
|
-
assert.match(result?.message ?? "", /Plan mode: Disabled/i);
|
|
309
|
-
assert.match(result?.message ?? "", /Approval policy: On request/i);
|
|
310
|
-
assert.match(result?.message ?? "", /Sandbox mode: Read only/i);
|
|
311
|
-
assert.match(result?.message ?? "", /Tokens used: ~1,200/i);
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
test("/status includes active provider route status when supplied", () => {
|
|
315
|
-
const result = runCommand("/status", {
|
|
316
|
-
routeStatusMessage: [
|
|
317
|
-
"Route status:",
|
|
318
|
-
" Active chat route: Google / gemini-3-flash-preview",
|
|
319
|
-
" Backend kind: gemini-cli-auth",
|
|
320
|
-
].join("\n"),
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
assert.equal(result?.action, "status");
|
|
324
|
-
assert.match(result?.message ?? "", /Active chat route: Google \/ gemini-3-flash-preview/);
|
|
325
|
-
assert.match(result?.message ?? "", /Backend kind: gemini-cli-auth/);
|
|
326
|
-
assert.doesNotMatch(result?.message ?? "", /Antigravity/);
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
test("/status includes project instructions status when supplied", () => {
|
|
330
|
-
const result = runCommand("/status", {
|
|
331
|
-
projectInstructions: {
|
|
332
|
-
status: "loaded",
|
|
333
|
-
instructions: { path: "C:\\Workspace\\AGENTS.md", content: "" },
|
|
334
|
-
},
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
assert.equal(result?.action, "status");
|
|
338
|
-
assert.match(result?.message ?? "", /Project instructions: loaded/);
|
|
339
|
-
assert.match(result?.message ?? "", /AGENTS\.md/);
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
test("/models opens the model picker", () => {
|
|
343
|
-
const result = runCommand("/models", {
|
|
344
|
-
modelCapabilities: dynamicCapabilities,
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
assert.equal(result?.action, "open_model_picker");
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
test("shows layered config status", () => {
|
|
351
|
-
const result = runCommand("/config");
|
|
352
|
-
assert.equal(result?.action, "config_status");
|
|
353
|
-
assert.match(result?.message ?? "", /Config status:/i);
|
|
354
|
-
assert.match(result?.message ?? "", /Project trust: Untrusted/i);
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
test("parses config trust commands", () => {
|
|
358
|
-
const statusResult = runCommand("/config trust");
|
|
359
|
-
assert.equal(statusResult?.action, "config_trust_status");
|
|
360
|
-
assert.match(statusResult?.message ?? "", /Status: Untrusted/i);
|
|
361
|
-
|
|
362
|
-
const setResult = runCommand("/config trust on");
|
|
363
|
-
assert.equal(setResult?.action, "config_trust_set");
|
|
364
|
-
assert.equal(setResult?.value, "on");
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
test("opens the permissions panel when /permissions has no arguments", () => {
|
|
368
|
-
const result = runCommand("/permissions");
|
|
369
|
-
assert.equal(result?.action, "open_permissions_panel");
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
test("reports configured and effective permissions status", () => {
|
|
373
|
-
const result = runCommand("/permissions status");
|
|
374
|
-
assert.equal(result?.action, "permissions_status");
|
|
375
|
-
assert.match(result?.message ?? "", /Permissions status:/i);
|
|
376
|
-
assert.match(result?.message ?? "", /configured Inherit; effective On request/i);
|
|
377
|
-
assert.match(result?.message ?? "", /Network access: configured Inherit; effective Disabled/i);
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
test("parses runtime approval policy setters and status", () => {
|
|
381
|
-
const setResult = runCommand("/runtime approval-policy never");
|
|
382
|
-
assert.equal(setResult?.action, "runtime_approval_policy");
|
|
383
|
-
assert.equal(setResult?.value, "never");
|
|
384
|
-
|
|
385
|
-
const statusResult = runCommand("/runtime approval-policy status");
|
|
386
|
-
assert.equal(statusResult?.action, "runtime_approval_policy");
|
|
387
|
-
assert.match(statusResult?.message ?? "", /configured Inherit; effective On request/i);
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
test("parses runtime sandbox setters and status", () => {
|
|
391
|
-
const setResult = runCommand("/runtime sandbox danger-full-access");
|
|
392
|
-
assert.equal(setResult?.action, "runtime_sandbox_mode");
|
|
393
|
-
assert.equal(setResult?.value, "danger-full-access");
|
|
394
|
-
|
|
395
|
-
const statusResult = runCommand("/runtime sandbox status");
|
|
396
|
-
assert.equal(statusResult?.action, "runtime_sandbox_mode");
|
|
397
|
-
assert.match(statusResult?.message ?? "", /configured Inherit; effective Read only/i);
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
test("parses runtime network setters including inherit resets", () => {
|
|
401
|
-
const setResult = runCommand("/runtime network on");
|
|
402
|
-
assert.equal(setResult?.action, "runtime_network_access");
|
|
403
|
-
assert.equal(setResult?.value, "enabled");
|
|
404
|
-
|
|
405
|
-
const inheritResult = runCommand("/runtime network inherit");
|
|
406
|
-
assert.equal(inheritResult?.action, "runtime_network_access");
|
|
407
|
-
assert.equal(inheritResult?.value, "inherit");
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
test("parses permissions command setters through the runtime action path", () => {
|
|
411
|
-
const approvalResult = runCommand("/permissions approval-policy never");
|
|
412
|
-
assert.equal(approvalResult?.action, "runtime_approval_policy");
|
|
413
|
-
assert.equal(approvalResult?.value, "never");
|
|
414
|
-
|
|
415
|
-
const sandboxResult = runCommand("/permissions sandbox workspace-write");
|
|
416
|
-
assert.equal(sandboxResult?.action, "runtime_sandbox_mode");
|
|
417
|
-
assert.equal(sandboxResult?.value, "workspace-write");
|
|
418
|
-
|
|
419
|
-
const networkResult = runCommand("/permissions network on");
|
|
420
|
-
assert.equal(networkResult?.action, "runtime_network_access");
|
|
421
|
-
assert.equal(networkResult?.value, "enabled");
|
|
422
|
-
|
|
423
|
-
const rootsResult = runCommand("/permissions writable-roots add .\\tmp");
|
|
424
|
-
assert.equal(rootsResult?.action, "runtime_writable_roots_add");
|
|
425
|
-
assert.equal(rootsResult?.value, ".\\tmp");
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
test("status reports resolved overrides rather than raw inherit values", () => {
|
|
429
|
-
const runtime = normalizeRuntimeConfig({
|
|
430
|
-
mode: "suggest",
|
|
431
|
-
policy: {
|
|
432
|
-
approvalPolicy: "never",
|
|
433
|
-
sandboxMode: "workspace-write",
|
|
434
|
-
networkAccess: "enabled",
|
|
435
|
-
writableRoots: ["C:\\Extra\\"],
|
|
436
|
-
},
|
|
437
|
-
});
|
|
438
|
-
const result = runCommand("/status", {
|
|
439
|
-
runtime,
|
|
440
|
-
resolvedRuntime: resolveRuntimeConfig(runtime),
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
assert.equal(result?.action, "status");
|
|
444
|
-
assert.match(result?.message ?? "", /Approval policy: Never/i);
|
|
445
|
-
assert.match(result?.message ?? "", /Sandbox mode: Workspace write/i);
|
|
446
|
-
assert.match(result?.message ?? "", /Network access: Enabled/i);
|
|
447
|
-
assert.match(result?.message ?? "", /C:\\Extra/i);
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
test("parses writable root list add remove and clear commands", () => {
|
|
451
|
-
const listResult = runCommand("/runtime writable-roots list");
|
|
452
|
-
assert.equal(listResult?.action, "runtime_writable_roots_list");
|
|
453
|
-
assert.match(listResult?.message ?? "", /none/i);
|
|
454
|
-
|
|
455
|
-
const addResult = runCommand("/runtime writable-roots add .\\tmp");
|
|
456
|
-
assert.equal(addResult?.action, "runtime_writable_roots_add");
|
|
457
|
-
assert.equal(addResult?.value, ".\\tmp");
|
|
458
|
-
|
|
459
|
-
const removeResult = runCommand("/runtime writable-roots remove .\\tmp");
|
|
460
|
-
assert.equal(removeResult?.action, "runtime_writable_roots_remove");
|
|
461
|
-
assert.equal(removeResult?.value, ".\\tmp");
|
|
462
|
-
|
|
463
|
-
const clearResult = runCommand("/runtime writable-roots clear");
|
|
464
|
-
assert.equal(clearResult?.action, "runtime_writable_roots_clear");
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
test("parses runtime service tier and personality setters", () => {
|
|
468
|
-
const tierResult = runCommand("/runtime service-tier fast");
|
|
469
|
-
assert.equal(tierResult?.action, "runtime_service_tier");
|
|
470
|
-
assert.equal(tierResult?.value, "fast");
|
|
471
|
-
|
|
472
|
-
const personalityResult = runCommand("/runtime personality pragmatic");
|
|
473
|
-
assert.equal(personalityResult?.action, "runtime_personality");
|
|
474
|
-
assert.equal(personalityResult?.value, "pragmatic");
|
|
475
|
-
});
|
|
476
|
-
|
|
477
|
-
test("documents runtime commands in help", () => {
|
|
478
|
-
const result = runCommand("/help");
|
|
479
|
-
assert.equal(result?.action, "help");
|
|
480
|
-
assert.match(result?.message ?? "", /\/status\s+Show the effective runtime configuration/i);
|
|
481
|
-
assert.match(result?.message ?? "", /\/config\s+Show layered config sources/i);
|
|
482
|
-
assert.match(result?.message ?? "", /\/permissions\s+Open or update permissions and sandbox controls/i);
|
|
483
|
-
assert.match(result?.message ?? "", /\/permissions approval-policy/i);
|
|
484
|
-
assert.match(result?.message ?? "", /\/runtime approval-policy/i);
|
|
485
|
-
assert.match(result?.message ?? "", /\/runtime writable-roots/i);
|
|
486
|
-
assert.match(result?.message ?? "", /\/plan \[on\|off\]\s+Show or toggle session plan mode/i);
|
|
487
|
-
assert.match(result?.message ?? "", /\/setting, \/settings Open the settings picker/i);
|
|
488
|
-
assert.match(result?.message ?? "", /\/setting workspace \[dir\|name\|simple\]/i);
|
|
489
|
-
assert.match(result?.message ?? "", /\/setting busy-loader \[true\|false\]/i);
|
|
490
|
-
assert.match(result?.message ?? "", /\/mouse\s+Toggle SGR mouse capture for in-app wheel scroll/i);
|
|
491
|
-
assert.match(result?.message ?? "", /Current plan mode: Disabled/i);
|
|
492
|
-
assert.match(result?.message ?? "", /Shift\+Tab\s+Toggle plan mode/i);
|
|
493
|
-
assert.match(result?.message ?? "", /Ctrl\+Y\s+Cycle execution mode/i);
|
|
494
|
-
assert.match(result?.message ?? "", /Ctrl\+Alt\+P\s+Open provider picker/i);
|
|
495
|
-
});
|
|
496
|
-
|
|
497
|
-
test("shows the active locked workspace", () => {
|
|
498
|
-
const result = runCommand("/workspace");
|
|
499
|
-
assert.equal(result?.action, "workspace");
|
|
500
|
-
assert.equal(result?.message, baseContext.workspace.summaryMessage);
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
test("parses workspace relaunch commands", () => {
|
|
504
|
-
const result = runCommand("/workspace relaunch C:\\Next Workspace");
|
|
505
|
-
assert.equal(result?.action, "workspace_relaunch");
|
|
506
|
-
assert.equal(result?.value, "C:\\Next Workspace");
|
|
507
|
-
});
|
|
508
|
-
|
|
509
|
-
test("requires a target path for workspace relaunch", () => {
|
|
510
|
-
const result = runCommand("/workspace relaunch");
|
|
511
|
-
assert.equal(result?.action, "unknown");
|
|
512
|
-
assert.match(result?.message ?? "", /Usage: \/workspace relaunch <path>/i);
|
|
513
|
-
});
|
|
514
|
-
|
|
515
|
-
test("parses /theme and /themes commands", () => {
|
|
516
|
-
const themeResult = runCommand("/theme mono");
|
|
517
|
-
assert.equal(themeResult?.action, "theme");
|
|
518
|
-
assert.equal(themeResult?.value, "mono");
|
|
519
|
-
|
|
520
|
-
const themesResult = runCommand("/themes");
|
|
521
|
-
assert.equal(themesResult?.action, "open_theme_picker");
|
|
522
|
-
});
|
|
523
|
-
|
|
524
|
-
test("parses /clear command", () => {
|
|
525
|
-
const result = runCommand("/clear");
|
|
526
|
-
assert.equal(result?.action, "clear");
|
|
527
|
-
});
|
|
528
|
-
|
|
529
|
-
test("parses /model without args as open_model_picker", () => {
|
|
530
|
-
const result = runCommand("/model");
|
|
531
|
-
assert.equal(result?.action, "open_model_picker");
|
|
532
|
-
});
|
|
533
|
-
|
|
534
|
-
test("parses /providers as open_provider_picker", () => {
|
|
535
|
-
const result = runCommand("/providers");
|
|
536
|
-
assert.equal(result?.action, "open_provider_picker");
|
|
537
|
-
});
|
|
538
|
-
|
|
539
|
-
test("parses /provider alias as open_provider_picker", () => {
|
|
540
|
-
const result = runCommand("/provider");
|
|
541
|
-
assert.equal(result?.action, "open_provider_picker");
|
|
542
|
-
});
|
|
543
|
-
|
|
544
|
-
test("parses /route as distinct route status", () => {
|
|
545
|
-
const result = runCommand("/route", {
|
|
546
|
-
routeStatusMessage: [
|
|
547
|
-
"Route status:",
|
|
548
|
-
" Workspace default: Anthropic",
|
|
549
|
-
" Active chat route: OpenAI / gpt-5.4",
|
|
550
|
-
" Active model: gpt-5.4",
|
|
551
|
-
" Active provider mode: Usable inside Codexa",
|
|
552
|
-
" Anthropic in-Codexa routing: not configured yet",
|
|
553
|
-
].join("\n"),
|
|
554
|
-
});
|
|
555
|
-
|
|
556
|
-
assert.equal(result?.action, "route_status");
|
|
557
|
-
assert.match(result?.message ?? "", /Workspace default: Anthropic/);
|
|
558
|
-
assert.match(result?.message ?? "", /Active chat route: OpenAI \/ gpt-5\.4/);
|
|
559
|
-
});
|
|
560
|
-
|
|
561
|
-
test("/models is a model picker alias", () => {
|
|
562
|
-
const result = runCommand("/models", {
|
|
563
|
-
modelCapabilities: dynamicCapabilities,
|
|
564
|
-
activeRouteProviderLabel: "OpenAI",
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
assert.equal(result?.action, "open_model_picker");
|
|
568
|
-
});
|
|
569
|
-
|
|
570
|
-
test("parses /reasoning without args as open_reasoning_picker", () => {
|
|
571
|
-
const result = runCommand("/reasoning");
|
|
572
|
-
assert.equal(result?.action, "open_reasoning_picker");
|
|
573
|
-
});
|
|
574
|
-
|
|
575
|
-
test("parses /diagnose github command", () => {
|
|
576
|
-
const result = runCommand("/diagnose github");
|
|
577
|
-
assert.equal(result?.action, "diagnose_github");
|
|
578
|
-
});
|
|
579
|
-
|
|
580
|
-
test("parses /diagnose providers command", () => {
|
|
581
|
-
const result = runCommand("/diagnose providers");
|
|
582
|
-
assert.equal(result?.action, "diagnose_providers");
|
|
583
|
-
});
|
|
584
|
-
|
|
585
|
-
test("unknown commands show /help suggestion", () => {
|
|
586
|
-
const result = runCommand("/unknown-cmd-123");
|
|
587
|
-
assert.equal(result?.action, "unknown");
|
|
588
|
-
assert.match(result?.message ?? "", /Type \/help for available commands/i);
|
|
589
|
-
});
|
|
590
|
-
|
|
591
|
-
test("every command documented in help is recognized by the parser", () => {
|
|
592
|
-
const helpResult = runCommand("/help");
|
|
593
|
-
assert.equal(helpResult?.action, "help");
|
|
594
|
-
const helpText = helpResult?.message ?? "";
|
|
595
|
-
|
|
596
|
-
// Capture commands from line-start entries (" /cmd ...") and comma-listed aliases (", /alias")
|
|
597
|
-
// so that lines like " /exit, /quit Quit..." contribute both "exit" and "quit"
|
|
598
|
-
const fromLineStart = [...helpText.matchAll(/^\s{2}\/([a-z][a-z0-9-]+)/gm)].map((m) => m[1]!);
|
|
599
|
-
const fromCommaAlias = [...helpText.matchAll(/,\s*\/([a-z][a-z0-9-]+)/g)].map((m) => m[1]!);
|
|
600
|
-
const documentedCommands = new Set([...fromLineStart, ...fromCommaAlias]);
|
|
601
|
-
|
|
602
|
-
// /diagnose requires the "github" subcommand; bare /diagnose returns unknown by design
|
|
603
|
-
const requiresSubcommand = new Set(["diagnose"]);
|
|
604
|
-
|
|
605
|
-
for (const cmd of documentedCommands) {
|
|
606
|
-
if (requiresSubcommand.has(cmd)) continue;
|
|
607
|
-
const result = runCommand(`/${cmd}`);
|
|
608
|
-
assert.notEqual(result?.action, "unknown", `Command /${cmd} documented in /help but handler returned 'unknown'`);
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
// Verify specific multi-word commands mentioned in help
|
|
612
|
-
const multiWord = [
|
|
613
|
-
["diagnose github", "diagnose_github"],
|
|
614
|
-
["diagnose providers", "diagnose_providers"],
|
|
615
|
-
["config trust", "config_trust_status"],
|
|
616
|
-
["auth status", "auth_status"],
|
|
617
|
-
["workspace relaunch .", "workspace_relaunch"],
|
|
618
|
-
["providers", "open_provider_picker"],
|
|
619
|
-
["route", "route_status"],
|
|
620
|
-
["update check", "update"],
|
|
621
|
-
] as const;
|
|
622
|
-
for (const [cmd, expectedAction] of multiWord) {
|
|
623
|
-
const result = runCommand(`/${cmd}`);
|
|
624
|
-
assert.equal(result?.action, expectedAction, `Multi-word command /${cmd} mentioned in help but returned action ${result?.action} instead of ${expectedAction}`);
|
|
625
|
-
}
|
|
626
|
-
});
|
|
627
|
-
|
|
628
|
-
test("?-prefixed command-like input is treated as invalid command error", () => {
|
|
629
|
-
const result = runCommand("?clear");
|
|
630
|
-
assert.equal(result?.action, "unknown");
|
|
631
|
-
assert.match(result?.message ?? "", /Invalid command syntax.*\?clear/i);
|
|
632
|
-
assert.match(result?.message ?? "", /Did you mean \/clear/i);
|
|
633
|
-
});
|
|
634
|
-
|
|
635
|
-
test("?model shows command error not model picker", () => {
|
|
636
|
-
const result = runCommand("?model");
|
|
637
|
-
assert.equal(result?.action, "unknown");
|
|
638
|
-
assert.match(result?.message ?? "", /Invalid command syntax/i);
|
|
639
|
-
});
|
|
640
|
-
|
|
641
|
-
test("non-command text without / or ? prefix returns null", () => {
|
|
642
|
-
const result = runCommand("hello world");
|
|
643
|
-
assert.equal(result, null);
|
|
644
|
-
});
|
|
645
|
-
|
|
646
|
-
test("parses /update and /update check commands", () => {
|
|
647
|
-
const resultStatus = runCommand("/update");
|
|
648
|
-
assert.equal(resultStatus?.action, "update");
|
|
649
|
-
assert.equal(resultStatus?.value, "status");
|
|
650
|
-
|
|
651
|
-
const resultCheck = runCommand("/update check");
|
|
652
|
-
assert.equal(resultCheck?.action, "update");
|
|
653
|
-
assert.equal(resultCheck?.value, "check");
|
|
654
|
-
});
|
|
655
|
-
|