@qwen-code/qwen-code 0.0.13 → 0.0.14-nightly.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/dist/package.json +2 -2
- package/dist/qwen-code-qwen-code-0.0.13.tgz +0 -0
- package/dist/src/config/config.js +40 -20
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/settingsSchema.d.ts +9 -0
- package/dist/src/config/settingsSchema.js +9 -0
- package/dist/src/config/settingsSchema.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/generated/git-commit.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.js +6 -4
- package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.test.js +10 -0
- package/dist/src/services/BuiltinCommandLoader.test.js.map +1 -1
- package/dist/src/test-utils/mockCommandContext.js +4 -1
- package/dist/src/test-utils/mockCommandContext.js.map +1 -1
- package/dist/src/ui/commands/approvalModeCommand.d.ts +7 -0
- package/dist/src/ui/commands/approvalModeCommand.js +340 -0
- package/dist/src/ui/commands/approvalModeCommand.js.map +1 -0
- package/dist/src/ui/commands/approvalModeCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/approvalModeCommand.test.js +293 -0
- package/dist/src/ui/commands/approvalModeCommand.test.js.map +1 -0
- package/dist/src/ui/components/AutoAcceptIndicator.js +8 -3
- package/dist/src/ui/components/AutoAcceptIndicator.js.map +1 -1
- package/dist/src/ui/components/Help.js +1 -1
- package/dist/src/ui/components/Help.js.map +1 -1
- package/dist/src/ui/components/PlanSummaryDisplay.d.ts +14 -0
- package/dist/src/ui/components/PlanSummaryDisplay.js +9 -0
- package/dist/src/ui/components/PlanSummaryDisplay.js.map +1 -0
- package/dist/src/ui/components/messages/ToolConfirmationMessage.js +18 -0
- package/dist/src/ui/components/messages/ToolConfirmationMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolConfirmationMessage.test.js +15 -0
- package/dist/src/ui/components/messages/ToolConfirmationMessage.test.js.map +1 -1
- package/dist/src/ui/components/messages/ToolMessage.js +12 -1
- package/dist/src/ui/components/messages/ToolMessage.js.map +1 -1
- package/dist/src/ui/components/subagents/manage/AgentsManagerDialog.js +1 -1
- package/dist/src/ui/components/subagents/manage/AgentsManagerDialog.js.map +1 -1
- package/dist/src/ui/hooks/useAutoAcceptIndicator.d.ts +1 -1
- package/dist/src/ui/hooks/useAutoAcceptIndicator.js +16 -24
- package/dist/src/ui/hooks/useAutoAcceptIndicator.js.map +1 -1
- package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js +41 -89
- package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js.map +1 -1
- package/dist/src/ui/hooks/useVisionAutoSwitch.test.js +15 -0
- package/dist/src/ui/hooks/useVisionAutoSwitch.test.js.map +1 -1
- package/dist/src/zed-integration/zedIntegration.js +18 -0
- package/dist/src/zed-integration/zedIntegration.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/dist/qwen-code-qwen-code-0.0.12.tgz +0 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Qwen
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { CommandKind } from './types.js';
|
|
7
|
+
import { ApprovalMode, APPROVAL_MODES } from '@qwen-code/qwen-code-core';
|
|
8
|
+
import { SettingScope } from '../../config/settings.js';
|
|
9
|
+
const USAGE_MESSAGE = 'Usage: /approval-mode <mode> [--session|--user|--project]';
|
|
10
|
+
const normalizeInputMode = (value) => value.trim().toLowerCase();
|
|
11
|
+
const tokenizeArgs = (args) => {
|
|
12
|
+
const matches = args.match(/(?:"[^"]*"|'[^']*'|[^\s"']+)/g);
|
|
13
|
+
if (!matches) {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
return matches.map((token) => {
|
|
17
|
+
if ((token.startsWith('"') && token.endsWith('"')) ||
|
|
18
|
+
(token.startsWith("'") && token.endsWith("'"))) {
|
|
19
|
+
return token.slice(1, -1);
|
|
20
|
+
}
|
|
21
|
+
return token;
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
const parseApprovalMode = (value) => {
|
|
25
|
+
if (!value) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const normalized = normalizeInputMode(value).replace(/_/g, '-');
|
|
29
|
+
const matchIndex = APPROVAL_MODES.findIndex((candidate) => candidate === normalized);
|
|
30
|
+
return matchIndex === -1 ? null : APPROVAL_MODES[matchIndex];
|
|
31
|
+
};
|
|
32
|
+
const formatModeDescription = (mode) => {
|
|
33
|
+
switch (mode) {
|
|
34
|
+
case ApprovalMode.PLAN:
|
|
35
|
+
return 'Plan mode - Analyze only, do not modify files or execute commands';
|
|
36
|
+
case ApprovalMode.DEFAULT:
|
|
37
|
+
return 'Default mode - Require approval for file edits or shell commands';
|
|
38
|
+
case ApprovalMode.AUTO_EDIT:
|
|
39
|
+
return 'Auto-edit mode - Automatically approve file edits';
|
|
40
|
+
case ApprovalMode.YOLO:
|
|
41
|
+
return 'YOLO mode - Automatically approve all tools';
|
|
42
|
+
default:
|
|
43
|
+
return `${mode} mode`;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const parseApprovalArgs = (args) => {
|
|
47
|
+
const trimmedArgs = args.trim();
|
|
48
|
+
if (!trimmedArgs) {
|
|
49
|
+
return { mode: null, scope: 'session' };
|
|
50
|
+
}
|
|
51
|
+
const tokens = tokenizeArgs(trimmedArgs);
|
|
52
|
+
let mode = null;
|
|
53
|
+
let scope = 'session';
|
|
54
|
+
let scopeFlag = null;
|
|
55
|
+
// Find scope flag and mode
|
|
56
|
+
for (const token of tokens) {
|
|
57
|
+
if (token === '--session' || token === '--user' || token === '--project') {
|
|
58
|
+
if (scopeFlag) {
|
|
59
|
+
return {
|
|
60
|
+
mode: null,
|
|
61
|
+
scope: 'session',
|
|
62
|
+
error: 'Multiple scope flags provided',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
scopeFlag = token;
|
|
66
|
+
scope = token.substring(2);
|
|
67
|
+
}
|
|
68
|
+
else if (!mode) {
|
|
69
|
+
mode = token;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
return {
|
|
73
|
+
mode: null,
|
|
74
|
+
scope: 'session',
|
|
75
|
+
error: 'Invalid arguments provided',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (!mode) {
|
|
80
|
+
return { mode: null, scope: 'session', error: 'Missing approval mode' };
|
|
81
|
+
}
|
|
82
|
+
return { mode, scope };
|
|
83
|
+
};
|
|
84
|
+
const setApprovalModeWithScope = async (context, mode, scope) => {
|
|
85
|
+
const { services } = context;
|
|
86
|
+
const { config } = services;
|
|
87
|
+
if (!config) {
|
|
88
|
+
return {
|
|
89
|
+
type: 'message',
|
|
90
|
+
messageType: 'error',
|
|
91
|
+
content: 'Configuration not available.',
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
// Always set the mode in the current session
|
|
96
|
+
config.setApprovalMode(mode);
|
|
97
|
+
// If scope is not session, also persist to settings
|
|
98
|
+
if (scope !== 'session') {
|
|
99
|
+
const { settings } = context.services;
|
|
100
|
+
if (!settings || typeof settings.setValue !== 'function') {
|
|
101
|
+
return {
|
|
102
|
+
type: 'message',
|
|
103
|
+
messageType: 'error',
|
|
104
|
+
content: 'Settings service is not available; unable to persist the approval mode.',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const settingScope = scope === 'user' ? SettingScope.User : SettingScope.Workspace;
|
|
108
|
+
const scopeLabel = scope === 'user' ? 'user' : 'project';
|
|
109
|
+
let settingsPath;
|
|
110
|
+
try {
|
|
111
|
+
if (typeof settings.forScope === 'function') {
|
|
112
|
+
settingsPath = settings.forScope(settingScope)?.path;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (_error) {
|
|
116
|
+
settingsPath = undefined;
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
settings.setValue(settingScope, 'approvalMode', mode);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
return {
|
|
123
|
+
type: 'message',
|
|
124
|
+
messageType: 'error',
|
|
125
|
+
content: `Failed to save approval mode: ${error.message}`,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const locationSuffix = settingsPath ? ` at ${settingsPath}` : '';
|
|
129
|
+
const scopeSuffix = ` (saved to ${scopeLabel} settings${locationSuffix})`;
|
|
130
|
+
return {
|
|
131
|
+
type: 'message',
|
|
132
|
+
messageType: 'info',
|
|
133
|
+
content: `Approval mode changed to: ${mode}${scopeSuffix}`,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
type: 'message',
|
|
138
|
+
messageType: 'info',
|
|
139
|
+
content: `Approval mode changed to: ${mode}`,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
return {
|
|
144
|
+
type: 'message',
|
|
145
|
+
messageType: 'error',
|
|
146
|
+
content: `Failed to change approval mode: ${error.message}`,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
export const approvalModeCommand = {
|
|
151
|
+
name: 'approval-mode',
|
|
152
|
+
description: 'View or change the approval mode for tool usage',
|
|
153
|
+
kind: CommandKind.BUILT_IN,
|
|
154
|
+
action: async (context, args) => {
|
|
155
|
+
const { config } = context.services;
|
|
156
|
+
if (!config) {
|
|
157
|
+
return {
|
|
158
|
+
type: 'message',
|
|
159
|
+
messageType: 'error',
|
|
160
|
+
content: 'Configuration not available.',
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
// If no arguments provided, show current mode and available options
|
|
164
|
+
if (!args || args.trim() === '') {
|
|
165
|
+
const currentMode = typeof config.getApprovalMode === 'function'
|
|
166
|
+
? config.getApprovalMode()
|
|
167
|
+
: null;
|
|
168
|
+
const messageLines = [];
|
|
169
|
+
if (currentMode) {
|
|
170
|
+
messageLines.push(`Current approval mode: ${currentMode}`);
|
|
171
|
+
messageLines.push('');
|
|
172
|
+
}
|
|
173
|
+
messageLines.push('Available approval modes:');
|
|
174
|
+
for (const mode of APPROVAL_MODES) {
|
|
175
|
+
messageLines.push(` - ${mode}: ${formatModeDescription(mode)}`);
|
|
176
|
+
}
|
|
177
|
+
messageLines.push('');
|
|
178
|
+
messageLines.push(USAGE_MESSAGE);
|
|
179
|
+
return {
|
|
180
|
+
type: 'message',
|
|
181
|
+
messageType: 'info',
|
|
182
|
+
content: messageLines.join('\n'),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
// Parse arguments flexibly
|
|
186
|
+
const parsed = parseApprovalArgs(args);
|
|
187
|
+
if (parsed.error) {
|
|
188
|
+
return {
|
|
189
|
+
type: 'message',
|
|
190
|
+
messageType: 'error',
|
|
191
|
+
content: `${parsed.error}. ${USAGE_MESSAGE}`,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
if (!parsed.mode) {
|
|
195
|
+
return {
|
|
196
|
+
type: 'message',
|
|
197
|
+
messageType: 'info',
|
|
198
|
+
content: USAGE_MESSAGE,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
const requestedMode = parseApprovalMode(parsed.mode);
|
|
202
|
+
if (!requestedMode) {
|
|
203
|
+
let message = `Invalid approval mode: ${parsed.mode}\n\n`;
|
|
204
|
+
message += 'Available approval modes:\n';
|
|
205
|
+
for (const mode of APPROVAL_MODES) {
|
|
206
|
+
message += ` - ${mode}: ${formatModeDescription(mode)}\n`;
|
|
207
|
+
}
|
|
208
|
+
message += `\n${USAGE_MESSAGE}`;
|
|
209
|
+
return {
|
|
210
|
+
type: 'message',
|
|
211
|
+
messageType: 'error',
|
|
212
|
+
content: message,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
return setApprovalModeWithScope(context, requestedMode, parsed.scope);
|
|
216
|
+
},
|
|
217
|
+
subCommands: APPROVAL_MODES.map((mode) => ({
|
|
218
|
+
name: mode,
|
|
219
|
+
description: formatModeDescription(mode),
|
|
220
|
+
kind: CommandKind.BUILT_IN,
|
|
221
|
+
subCommands: [
|
|
222
|
+
{
|
|
223
|
+
name: '--session',
|
|
224
|
+
description: 'Apply to current session only (temporary)',
|
|
225
|
+
kind: CommandKind.BUILT_IN,
|
|
226
|
+
action: async (context, args) => {
|
|
227
|
+
if (args.trim().length > 0) {
|
|
228
|
+
return {
|
|
229
|
+
type: 'message',
|
|
230
|
+
messageType: 'error',
|
|
231
|
+
content: 'Scope subcommands do not accept additional arguments.',
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
return setApprovalModeWithScope(context, mode, 'session');
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
name: '--project',
|
|
239
|
+
description: 'Persist for this project/workspace',
|
|
240
|
+
kind: CommandKind.BUILT_IN,
|
|
241
|
+
action: async (context, args) => {
|
|
242
|
+
if (args.trim().length > 0) {
|
|
243
|
+
return {
|
|
244
|
+
type: 'message',
|
|
245
|
+
messageType: 'error',
|
|
246
|
+
content: 'Scope subcommands do not accept additional arguments.',
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
return setApprovalModeWithScope(context, mode, 'project');
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
name: '--user',
|
|
254
|
+
description: 'Persist for this user on this machine',
|
|
255
|
+
kind: CommandKind.BUILT_IN,
|
|
256
|
+
action: async (context, args) => {
|
|
257
|
+
if (args.trim().length > 0) {
|
|
258
|
+
return {
|
|
259
|
+
type: 'message',
|
|
260
|
+
messageType: 'error',
|
|
261
|
+
content: 'Scope subcommands do not accept additional arguments.',
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
return setApprovalModeWithScope(context, mode, 'user');
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
],
|
|
268
|
+
action: async (context, args) => {
|
|
269
|
+
if (args.trim().length > 0) {
|
|
270
|
+
// Allow users who type `/approval-mode plan --user` via the subcommand path
|
|
271
|
+
const parsed = parseApprovalArgs(`${mode} ${args}`);
|
|
272
|
+
if (parsed.error) {
|
|
273
|
+
return {
|
|
274
|
+
type: 'message',
|
|
275
|
+
messageType: 'error',
|
|
276
|
+
content: `${parsed.error}. ${USAGE_MESSAGE}`,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
const normalizedMode = parseApprovalMode(parsed.mode);
|
|
280
|
+
if (!normalizedMode) {
|
|
281
|
+
return {
|
|
282
|
+
type: 'message',
|
|
283
|
+
messageType: 'error',
|
|
284
|
+
content: `Invalid approval mode: ${parsed.mode}. ${USAGE_MESSAGE}`,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
return setApprovalModeWithScope(context, normalizedMode, parsed.scope);
|
|
288
|
+
}
|
|
289
|
+
return setApprovalModeWithScope(context, mode, 'session');
|
|
290
|
+
},
|
|
291
|
+
})),
|
|
292
|
+
completion: async (_context, partialArg) => {
|
|
293
|
+
const tokens = tokenizeArgs(partialArg);
|
|
294
|
+
const hasTrailingSpace = /\s$/.test(partialArg);
|
|
295
|
+
const currentSegment = hasTrailingSpace
|
|
296
|
+
? ''
|
|
297
|
+
: tokens.length > 0
|
|
298
|
+
? tokens[tokens.length - 1]
|
|
299
|
+
: '';
|
|
300
|
+
const normalizedCurrent = normalizeInputMode(currentSegment).replace(/_/g, '-');
|
|
301
|
+
const scopeValues = ['--session', '--project', '--user'];
|
|
302
|
+
const normalizeToken = (token) => normalizeInputMode(token).replace(/_/g, '-');
|
|
303
|
+
const normalizedTokens = tokens.map(normalizeToken);
|
|
304
|
+
if (tokens.length === 0) {
|
|
305
|
+
if (currentSegment.startsWith('-')) {
|
|
306
|
+
return scopeValues.filter((scope) => scope.startsWith(currentSegment));
|
|
307
|
+
}
|
|
308
|
+
return APPROVAL_MODES;
|
|
309
|
+
}
|
|
310
|
+
if (tokens.length === 1 && !hasTrailingSpace) {
|
|
311
|
+
const originalToken = tokens[0];
|
|
312
|
+
if (originalToken.startsWith('-')) {
|
|
313
|
+
return scopeValues.filter((scope) => scope.startsWith(normalizedCurrent));
|
|
314
|
+
}
|
|
315
|
+
return APPROVAL_MODES.filter((mode) => mode.startsWith(normalizedCurrent));
|
|
316
|
+
}
|
|
317
|
+
if (tokens.length === 1 && hasTrailingSpace) {
|
|
318
|
+
const normalizedFirst = normalizedTokens[0];
|
|
319
|
+
if (scopeValues.includes(tokens[0])) {
|
|
320
|
+
return APPROVAL_MODES;
|
|
321
|
+
}
|
|
322
|
+
if (APPROVAL_MODES.includes(normalizedFirst)) {
|
|
323
|
+
return scopeValues;
|
|
324
|
+
}
|
|
325
|
+
return APPROVAL_MODES;
|
|
326
|
+
}
|
|
327
|
+
if (tokens.length === 2 && !hasTrailingSpace) {
|
|
328
|
+
const normalizedFirst = normalizedTokens[0];
|
|
329
|
+
if (scopeValues.includes(tokens[0])) {
|
|
330
|
+
return APPROVAL_MODES.filter((mode) => mode.startsWith(normalizedCurrent));
|
|
331
|
+
}
|
|
332
|
+
if (APPROVAL_MODES.includes(normalizedFirst)) {
|
|
333
|
+
return scopeValues.filter((scope) => scope.startsWith(normalizedCurrent));
|
|
334
|
+
}
|
|
335
|
+
return [];
|
|
336
|
+
}
|
|
337
|
+
return [];
|
|
338
|
+
},
|
|
339
|
+
};
|
|
340
|
+
//# sourceMappingURL=approvalModeCommand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approvalModeCommand.js","sourceRoot":"","sources":["../../../../src/ui/commands/approvalModeCommand.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Qwen
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
7
|
+
import { approvalModeCommand } from './approvalModeCommand.js';
|
|
8
|
+
import { CommandKind, } from './types.js';
|
|
9
|
+
import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
|
|
10
|
+
import { ApprovalMode } from '@qwen-code/qwen-code-core';
|
|
11
|
+
import { SettingScope } from '../../config/settings.js';
|
|
12
|
+
describe('approvalModeCommand', () => {
|
|
13
|
+
let mockContext;
|
|
14
|
+
let setApprovalModeMock;
|
|
15
|
+
let setSettingsValueMock;
|
|
16
|
+
const originalEnv = { ...process.env };
|
|
17
|
+
const userSettingsPath = '/mock/user/settings.json';
|
|
18
|
+
const projectSettingsPath = '/mock/project/settings.json';
|
|
19
|
+
const userSettingsFile = { path: userSettingsPath, settings: {} };
|
|
20
|
+
const projectSettingsFile = { path: projectSettingsPath, settings: {} };
|
|
21
|
+
const getModeSubCommand = (mode) => approvalModeCommand.subCommands?.find((cmd) => cmd.name === mode);
|
|
22
|
+
const getScopeSubCommand = (mode, scope) => getModeSubCommand(mode)?.subCommands?.find((cmd) => cmd.name === scope);
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
setApprovalModeMock = vi.fn();
|
|
25
|
+
setSettingsValueMock = vi.fn();
|
|
26
|
+
mockContext = createMockCommandContext({
|
|
27
|
+
services: {
|
|
28
|
+
config: {
|
|
29
|
+
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
|
|
30
|
+
setApprovalMode: setApprovalModeMock,
|
|
31
|
+
},
|
|
32
|
+
settings: {
|
|
33
|
+
merged: {},
|
|
34
|
+
setValue: setSettingsValueMock,
|
|
35
|
+
forScope: vi
|
|
36
|
+
.fn()
|
|
37
|
+
.mockImplementation((scope) => scope === SettingScope.User
|
|
38
|
+
? userSettingsFile
|
|
39
|
+
: scope === SettingScope.Workspace
|
|
40
|
+
? projectSettingsFile
|
|
41
|
+
: { path: '', settings: {} }),
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
afterEach(() => {
|
|
47
|
+
process.env = { ...originalEnv };
|
|
48
|
+
vi.clearAllMocks();
|
|
49
|
+
});
|
|
50
|
+
it('should have the correct command properties', () => {
|
|
51
|
+
expect(approvalModeCommand.name).toBe('approval-mode');
|
|
52
|
+
expect(approvalModeCommand.kind).toBe(CommandKind.BUILT_IN);
|
|
53
|
+
expect(approvalModeCommand.description).toBe('View or change the approval mode for tool usage');
|
|
54
|
+
});
|
|
55
|
+
it('should show current mode, options, and usage when no arguments provided', async () => {
|
|
56
|
+
if (!approvalModeCommand.action) {
|
|
57
|
+
throw new Error('approvalModeCommand must have an action.');
|
|
58
|
+
}
|
|
59
|
+
const result = (await approvalModeCommand.action(mockContext, ''));
|
|
60
|
+
expect(result.type).toBe('message');
|
|
61
|
+
expect(result.messageType).toBe('info');
|
|
62
|
+
const expectedMessage = [
|
|
63
|
+
'Current approval mode: default',
|
|
64
|
+
'',
|
|
65
|
+
'Available approval modes:',
|
|
66
|
+
' - plan: Plan mode - Analyze only, do not modify files or execute commands',
|
|
67
|
+
' - default: Default mode - Require approval for file edits or shell commands',
|
|
68
|
+
' - auto-edit: Auto-edit mode - Automatically approve file edits',
|
|
69
|
+
' - yolo: YOLO mode - Automatically approve all tools',
|
|
70
|
+
'',
|
|
71
|
+
'Usage: /approval-mode <mode> [--session|--user|--project]',
|
|
72
|
+
].join('\n');
|
|
73
|
+
expect(result.content).toBe(expectedMessage);
|
|
74
|
+
});
|
|
75
|
+
it('should display error when config is not available', async () => {
|
|
76
|
+
if (!approvalModeCommand.action) {
|
|
77
|
+
throw new Error('approvalModeCommand must have an action.');
|
|
78
|
+
}
|
|
79
|
+
const nullConfigContext = createMockCommandContext({
|
|
80
|
+
services: {
|
|
81
|
+
config: null,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
const result = (await approvalModeCommand.action(nullConfigContext, ''));
|
|
85
|
+
expect(result.type).toBe('message');
|
|
86
|
+
expect(result.messageType).toBe('error');
|
|
87
|
+
expect(result.content).toBe('Configuration not available.');
|
|
88
|
+
});
|
|
89
|
+
it('should change approval mode when valid mode is provided', async () => {
|
|
90
|
+
if (!approvalModeCommand.action) {
|
|
91
|
+
throw new Error('approvalModeCommand must have an action.');
|
|
92
|
+
}
|
|
93
|
+
const result = (await approvalModeCommand.action(mockContext, 'plan'));
|
|
94
|
+
expect(setApprovalModeMock).toHaveBeenCalledWith(ApprovalMode.PLAN);
|
|
95
|
+
expect(setSettingsValueMock).not.toHaveBeenCalled();
|
|
96
|
+
expect(result.type).toBe('message');
|
|
97
|
+
expect(result.messageType).toBe('info');
|
|
98
|
+
expect(result.content).toBe('Approval mode changed to: plan');
|
|
99
|
+
});
|
|
100
|
+
it('should accept canonical auto-edit mode value', async () => {
|
|
101
|
+
if (!approvalModeCommand.action) {
|
|
102
|
+
throw new Error('approvalModeCommand must have an action.');
|
|
103
|
+
}
|
|
104
|
+
const result = (await approvalModeCommand.action(mockContext, 'auto-edit'));
|
|
105
|
+
expect(setApprovalModeMock).toHaveBeenCalledWith(ApprovalMode.AUTO_EDIT);
|
|
106
|
+
expect(setSettingsValueMock).not.toHaveBeenCalled();
|
|
107
|
+
expect(result.type).toBe('message');
|
|
108
|
+
expect(result.messageType).toBe('info');
|
|
109
|
+
expect(result.content).toBe('Approval mode changed to: auto-edit');
|
|
110
|
+
});
|
|
111
|
+
it('should accept auto-edit alias for compatibility', async () => {
|
|
112
|
+
if (!approvalModeCommand.action) {
|
|
113
|
+
throw new Error('approvalModeCommand must have an action.');
|
|
114
|
+
}
|
|
115
|
+
const result = (await approvalModeCommand.action(mockContext, 'auto-edit'));
|
|
116
|
+
expect(setApprovalModeMock).toHaveBeenCalledWith(ApprovalMode.AUTO_EDIT);
|
|
117
|
+
expect(setSettingsValueMock).not.toHaveBeenCalled();
|
|
118
|
+
expect(result.content).toBe('Approval mode changed to: auto-edit');
|
|
119
|
+
});
|
|
120
|
+
it('should display error when invalid mode is provided', async () => {
|
|
121
|
+
if (!approvalModeCommand.action) {
|
|
122
|
+
throw new Error('approvalModeCommand must have an action.');
|
|
123
|
+
}
|
|
124
|
+
const result = (await approvalModeCommand.action(mockContext, 'invalid'));
|
|
125
|
+
expect(result.type).toBe('message');
|
|
126
|
+
expect(result.messageType).toBe('error');
|
|
127
|
+
expect(result.content).toContain('Invalid approval mode: invalid');
|
|
128
|
+
expect(result.content).toContain('Available approval modes:');
|
|
129
|
+
expect(result.content).toContain('Usage: /approval-mode <mode> [--session|--user|--project]');
|
|
130
|
+
});
|
|
131
|
+
it('should display error when setApprovalMode throws an error', async () => {
|
|
132
|
+
if (!approvalModeCommand.action) {
|
|
133
|
+
throw new Error('approvalModeCommand must have an action.');
|
|
134
|
+
}
|
|
135
|
+
const errorMessage = 'Failed to set approval mode';
|
|
136
|
+
mockContext.services.config.setApprovalMode = vi
|
|
137
|
+
.fn()
|
|
138
|
+
.mockImplementation(() => {
|
|
139
|
+
throw new Error(errorMessage);
|
|
140
|
+
});
|
|
141
|
+
const result = (await approvalModeCommand.action(mockContext, 'plan'));
|
|
142
|
+
expect(result.type).toBe('message');
|
|
143
|
+
expect(result.messageType).toBe('error');
|
|
144
|
+
expect(result.content).toBe(`Failed to change approval mode: ${errorMessage}`);
|
|
145
|
+
});
|
|
146
|
+
it('should allow selecting auto-edit with user scope via nested subcommands', async () => {
|
|
147
|
+
if (!approvalModeCommand.subCommands) {
|
|
148
|
+
throw new Error('approvalModeCommand must have subCommands.');
|
|
149
|
+
}
|
|
150
|
+
const userSubCommand = getScopeSubCommand(ApprovalMode.AUTO_EDIT, '--user');
|
|
151
|
+
if (!userSubCommand?.action) {
|
|
152
|
+
throw new Error('--user scope subcommand must have an action.');
|
|
153
|
+
}
|
|
154
|
+
const result = (await userSubCommand.action(mockContext, ''));
|
|
155
|
+
expect(setApprovalModeMock).toHaveBeenCalledWith(ApprovalMode.AUTO_EDIT);
|
|
156
|
+
expect(setSettingsValueMock).toHaveBeenCalledWith(SettingScope.User, 'approvalMode', 'auto-edit');
|
|
157
|
+
expect(result.content).toBe(`Approval mode changed to: auto-edit (saved to user settings at ${userSettingsPath})`);
|
|
158
|
+
});
|
|
159
|
+
it('should allow selecting plan with project scope via nested subcommands', async () => {
|
|
160
|
+
if (!approvalModeCommand.subCommands) {
|
|
161
|
+
throw new Error('approvalModeCommand must have subCommands.');
|
|
162
|
+
}
|
|
163
|
+
const projectSubCommand = getScopeSubCommand(ApprovalMode.PLAN, '--project');
|
|
164
|
+
if (!projectSubCommand?.action) {
|
|
165
|
+
throw new Error('--project scope subcommand must have an action.');
|
|
166
|
+
}
|
|
167
|
+
const result = (await projectSubCommand.action(mockContext, ''));
|
|
168
|
+
expect(setApprovalModeMock).toHaveBeenCalledWith(ApprovalMode.PLAN);
|
|
169
|
+
expect(setSettingsValueMock).toHaveBeenCalledWith(SettingScope.Workspace, 'approvalMode', 'plan');
|
|
170
|
+
expect(result.content).toBe(`Approval mode changed to: plan (saved to project settings at ${projectSettingsPath})`);
|
|
171
|
+
});
|
|
172
|
+
it('should allow selecting plan with session scope via nested subcommands', async () => {
|
|
173
|
+
if (!approvalModeCommand.subCommands) {
|
|
174
|
+
throw new Error('approvalModeCommand must have subCommands.');
|
|
175
|
+
}
|
|
176
|
+
const sessionSubCommand = getScopeSubCommand(ApprovalMode.PLAN, '--session');
|
|
177
|
+
if (!sessionSubCommand?.action) {
|
|
178
|
+
throw new Error('--session scope subcommand must have an action.');
|
|
179
|
+
}
|
|
180
|
+
const result = (await sessionSubCommand.action(mockContext, ''));
|
|
181
|
+
expect(setApprovalModeMock).toHaveBeenCalledWith(ApprovalMode.PLAN);
|
|
182
|
+
expect(setSettingsValueMock).not.toHaveBeenCalled();
|
|
183
|
+
expect(result.content).toBe('Approval mode changed to: plan');
|
|
184
|
+
});
|
|
185
|
+
it('should allow providing a scope argument after selecting a mode subcommand', async () => {
|
|
186
|
+
if (!approvalModeCommand.subCommands) {
|
|
187
|
+
throw new Error('approvalModeCommand must have subCommands.');
|
|
188
|
+
}
|
|
189
|
+
const planSubCommand = getModeSubCommand(ApprovalMode.PLAN);
|
|
190
|
+
if (!planSubCommand?.action) {
|
|
191
|
+
throw new Error('plan subcommand must have an action.');
|
|
192
|
+
}
|
|
193
|
+
const result = (await planSubCommand.action(mockContext, '--user'));
|
|
194
|
+
expect(setApprovalModeMock).toHaveBeenCalledWith(ApprovalMode.PLAN);
|
|
195
|
+
expect(setSettingsValueMock).toHaveBeenCalledWith(SettingScope.User, 'approvalMode', 'plan');
|
|
196
|
+
expect(result.content).toBe(`Approval mode changed to: plan (saved to user settings at ${userSettingsPath})`);
|
|
197
|
+
});
|
|
198
|
+
it('should support --user plan pattern (scope first)', async () => {
|
|
199
|
+
if (!approvalModeCommand.action) {
|
|
200
|
+
throw new Error('approvalModeCommand must have an action.');
|
|
201
|
+
}
|
|
202
|
+
const result = (await approvalModeCommand.action(mockContext, '--user plan'));
|
|
203
|
+
expect(setApprovalModeMock).toHaveBeenCalledWith(ApprovalMode.PLAN);
|
|
204
|
+
expect(setSettingsValueMock).toHaveBeenCalledWith(SettingScope.User, 'approvalMode', 'plan');
|
|
205
|
+
expect(result.content).toBe(`Approval mode changed to: plan (saved to user settings at ${userSettingsPath})`);
|
|
206
|
+
});
|
|
207
|
+
it('should support plan --user pattern (mode first)', async () => {
|
|
208
|
+
if (!approvalModeCommand.action) {
|
|
209
|
+
throw new Error('approvalModeCommand must have an action.');
|
|
210
|
+
}
|
|
211
|
+
const result = (await approvalModeCommand.action(mockContext, 'plan --user'));
|
|
212
|
+
expect(setApprovalModeMock).toHaveBeenCalledWith(ApprovalMode.PLAN);
|
|
213
|
+
expect(setSettingsValueMock).toHaveBeenCalledWith(SettingScope.User, 'approvalMode', 'plan');
|
|
214
|
+
expect(result.content).toBe(`Approval mode changed to: plan (saved to user settings at ${userSettingsPath})`);
|
|
215
|
+
});
|
|
216
|
+
it('should support --project auto-edit pattern', async () => {
|
|
217
|
+
if (!approvalModeCommand.action) {
|
|
218
|
+
throw new Error('approvalModeCommand must have an action.');
|
|
219
|
+
}
|
|
220
|
+
const result = (await approvalModeCommand.action(mockContext, '--project auto-edit'));
|
|
221
|
+
expect(setApprovalModeMock).toHaveBeenCalledWith(ApprovalMode.AUTO_EDIT);
|
|
222
|
+
expect(setSettingsValueMock).toHaveBeenCalledWith(SettingScope.Workspace, 'approvalMode', 'auto-edit');
|
|
223
|
+
expect(result.content).toBe(`Approval mode changed to: auto-edit (saved to project settings at ${projectSettingsPath})`);
|
|
224
|
+
});
|
|
225
|
+
it('should display error when only scope flag is provided', async () => {
|
|
226
|
+
if (!approvalModeCommand.action) {
|
|
227
|
+
throw new Error('approvalModeCommand must have an action.');
|
|
228
|
+
}
|
|
229
|
+
const result = (await approvalModeCommand.action(mockContext, '--user'));
|
|
230
|
+
expect(result.type).toBe('message');
|
|
231
|
+
expect(result.messageType).toBe('error');
|
|
232
|
+
expect(result.content).toContain('Missing approval mode');
|
|
233
|
+
expect(setApprovalModeMock).not.toHaveBeenCalled();
|
|
234
|
+
expect(setSettingsValueMock).not.toHaveBeenCalled();
|
|
235
|
+
});
|
|
236
|
+
it('should display error when multiple scope flags are provided', async () => {
|
|
237
|
+
if (!approvalModeCommand.action) {
|
|
238
|
+
throw new Error('approvalModeCommand must have an action.');
|
|
239
|
+
}
|
|
240
|
+
const result = (await approvalModeCommand.action(mockContext, '--user --project plan'));
|
|
241
|
+
expect(result.type).toBe('message');
|
|
242
|
+
expect(result.messageType).toBe('error');
|
|
243
|
+
expect(result.content).toContain('Multiple scope flags provided');
|
|
244
|
+
expect(setApprovalModeMock).not.toHaveBeenCalled();
|
|
245
|
+
expect(setSettingsValueMock).not.toHaveBeenCalled();
|
|
246
|
+
});
|
|
247
|
+
it('should surface a helpful error when scope subcommands receive extra arguments', async () => {
|
|
248
|
+
if (!approvalModeCommand.subCommands) {
|
|
249
|
+
throw new Error('approvalModeCommand must have subCommands.');
|
|
250
|
+
}
|
|
251
|
+
const userSubCommand = getScopeSubCommand(ApprovalMode.DEFAULT, '--user');
|
|
252
|
+
if (!userSubCommand?.action) {
|
|
253
|
+
throw new Error('--user scope subcommand must have an action.');
|
|
254
|
+
}
|
|
255
|
+
const result = (await userSubCommand.action(mockContext, 'extra'));
|
|
256
|
+
expect(result.type).toBe('message');
|
|
257
|
+
expect(result.messageType).toBe('error');
|
|
258
|
+
expect(result.content).toBe('Scope subcommands do not accept additional arguments.');
|
|
259
|
+
expect(setApprovalModeMock).not.toHaveBeenCalled();
|
|
260
|
+
expect(setSettingsValueMock).not.toHaveBeenCalled();
|
|
261
|
+
});
|
|
262
|
+
it('should provide completion for approval modes', async () => {
|
|
263
|
+
if (!approvalModeCommand.completion) {
|
|
264
|
+
throw new Error('approvalModeCommand must have a completion function.');
|
|
265
|
+
}
|
|
266
|
+
// Test partial mode completion
|
|
267
|
+
const result = await approvalModeCommand.completion(mockContext, 'p');
|
|
268
|
+
expect(result).toEqual(['plan']);
|
|
269
|
+
const result2 = await approvalModeCommand.completion(mockContext, 'a');
|
|
270
|
+
expect(result2).toEqual(['auto-edit']);
|
|
271
|
+
// Test empty completion - should suggest available modes first
|
|
272
|
+
const result3 = await approvalModeCommand.completion(mockContext, '');
|
|
273
|
+
expect(result3).toEqual(['plan', 'default', 'auto-edit', 'yolo']);
|
|
274
|
+
const result4 = await approvalModeCommand.completion(mockContext, 'AUTO');
|
|
275
|
+
expect(result4).toEqual(['auto-edit']);
|
|
276
|
+
// Test mode first pattern: 'plan ' should suggest scope flags
|
|
277
|
+
const result5 = await approvalModeCommand.completion(mockContext, 'plan ');
|
|
278
|
+
expect(result5).toEqual(['--session', '--project', '--user']);
|
|
279
|
+
const result6 = await approvalModeCommand.completion(mockContext, 'plan --u');
|
|
280
|
+
expect(result6).toEqual(['--user']);
|
|
281
|
+
// Test scope first pattern: '--user ' should suggest modes
|
|
282
|
+
const result7 = await approvalModeCommand.completion(mockContext, '--user ');
|
|
283
|
+
expect(result7).toEqual(['plan', 'default', 'auto-edit', 'yolo']);
|
|
284
|
+
const result8 = await approvalModeCommand.completion(mockContext, '--user p');
|
|
285
|
+
expect(result8).toEqual(['plan']);
|
|
286
|
+
// Test completed patterns should return empty
|
|
287
|
+
const result9 = await approvalModeCommand.completion(mockContext, 'plan --user ');
|
|
288
|
+
expect(result9).toEqual([]);
|
|
289
|
+
const result10 = await approvalModeCommand.completion(mockContext, '--user plan ');
|
|
290
|
+
expect(result10).toEqual([]);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
//# sourceMappingURL=approvalModeCommand.test.js.map
|