claude-code-workflow 6.3.24 → 6.3.26
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/.claude/commands/issue/discover-by-prompt.md +764 -0
- package/.claude/skills/text-formatter/SKILL.md +196 -0
- package/.claude/skills/text-formatter/phases/01-input-collection.md +111 -0
- package/.claude/skills/text-formatter/phases/02-content-analysis.md +248 -0
- package/.claude/skills/text-formatter/phases/03-format-transform.md +245 -0
- package/.claude/skills/text-formatter/phases/04-output-preview.md +183 -0
- package/.claude/skills/text-formatter/specs/callout-types.md +293 -0
- package/.claude/skills/text-formatter/specs/element-mapping.md +226 -0
- package/.claude/skills/text-formatter/specs/format-rules.md +273 -0
- package/.claude/skills/text-formatter/templates/bbcode-template.md +350 -0
- package/ccw/dist/core/routes/help-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/help-routes.js +43 -7
- package/ccw/dist/core/routes/help-routes.js.map +1 -1
- package/ccw/dist/core/routes/litellm-api-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/litellm-api-routes.js +31 -5
- package/ccw/dist/core/routes/litellm-api-routes.js.map +1 -1
- package/ccw/dist/core/routes/memory-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/memory-routes.js +73 -0
- package/ccw/dist/core/routes/memory-routes.js.map +1 -1
- package/ccw/dist/core/routes/status-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/status-routes.js +36 -4
- package/ccw/dist/core/routes/status-routes.js.map +1 -1
- package/ccw/dist/core/server.d.ts.map +1 -1
- package/ccw/dist/core/server.js +58 -0
- package/ccw/dist/core/server.js.map +1 -1
- package/ccw/dist/core/services/api-key-tester.d.ts.map +1 -1
- package/ccw/dist/core/services/api-key-tester.js +8 -3
- package/ccw/dist/core/services/api-key-tester.js.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.d.ts +7 -0
- package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.js +11 -1
- package/ccw/dist/tools/claude-cli-tools.js.map +1 -1
- package/ccw/dist/tools/cli-executor-core.d.ts +11 -0
- package/ccw/dist/tools/cli-executor-core.d.ts.map +1 -1
- package/ccw/dist/tools/cli-executor-core.js +89 -2
- package/ccw/dist/tools/cli-executor-core.js.map +1 -1
- package/ccw/dist/tools/codex-lens.d.ts +2 -1
- package/ccw/dist/tools/codex-lens.d.ts.map +1 -1
- package/ccw/dist/tools/codex-lens.js +51 -8
- package/ccw/dist/tools/codex-lens.js.map +1 -1
- package/ccw/dist/tools/index.d.ts.map +1 -1
- package/ccw/dist/tools/index.js +2 -0
- package/ccw/dist/tools/index.js.map +1 -1
- package/ccw/dist/tools/litellm-client.d.ts +6 -0
- package/ccw/dist/tools/litellm-client.d.ts.map +1 -1
- package/ccw/dist/tools/litellm-client.js +22 -1
- package/ccw/dist/tools/litellm-client.js.map +1 -1
- package/ccw/dist/tools/litellm-executor.js +2 -2
- package/ccw/dist/tools/litellm-executor.js.map +1 -1
- package/ccw/dist/tools/memory-update-queue.d.ts +172 -0
- package/ccw/dist/tools/memory-update-queue.d.ts.map +1 -0
- package/ccw/dist/tools/memory-update-queue.js +431 -0
- package/ccw/dist/tools/memory-update-queue.js.map +1 -0
- package/ccw/src/core/routes/help-routes.ts +46 -7
- package/ccw/src/core/routes/litellm-api-routes.ts +35 -4
- package/ccw/src/core/routes/memory-routes.ts +84 -0
- package/ccw/src/core/routes/status-routes.ts +39 -4
- package/ccw/src/core/server.ts +62 -0
- package/ccw/src/core/services/api-key-tester.ts +9 -3
- package/ccw/src/templates/dashboard-css/21-cli-toolmgmt.css +45 -0
- package/ccw/src/templates/dashboard-js/components/cli-status.js +36 -5
- package/ccw/src/templates/dashboard-js/components/hook-manager.js +42 -81
- package/ccw/src/templates/dashboard-js/components/mcp-manager.js +170 -28
- package/ccw/src/templates/dashboard-js/components/notifications.js +14 -4
- package/ccw/src/templates/dashboard-js/i18n.js +26 -0
- package/ccw/src/templates/dashboard-js/views/cli-manager.js +72 -2
- package/ccw/src/templates/dashboard-js/views/codexlens-manager.js +11 -1
- package/ccw/src/tools/claude-cli-tools.ts +17 -1
- package/ccw/src/tools/cli-executor-core.ts +103 -2
- package/ccw/src/tools/codex-lens.ts +63 -8
- package/ccw/src/tools/index.ts +2 -0
- package/ccw/src/tools/litellm-client.ts +25 -3
- package/ccw/src/tools/litellm-executor.ts +2 -2
- package/ccw/src/tools/memory-update-queue.js +499 -0
- package/package.json +91 -91
|
@@ -84,6 +84,147 @@ function getCliMode() {
|
|
|
84
84
|
return currentCliMode;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
// ========== Cross-Platform MCP Helpers ==========
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Build cross-platform MCP server configuration
|
|
91
|
+
* On Windows, wraps npx/node/python commands with cmd /c for proper execution
|
|
92
|
+
* @param {string} command - The command to run (e.g., 'npx', 'node', 'python')
|
|
93
|
+
* @param {string[]} args - Command arguments
|
|
94
|
+
* @param {object} [options] - Additional options (env, type, etc.)
|
|
95
|
+
* @returns {object} MCP server configuration
|
|
96
|
+
*/
|
|
97
|
+
function buildCrossPlatformMcpConfig(command, args = [], options = {}) {
|
|
98
|
+
const { env, type, ...rest } = options;
|
|
99
|
+
|
|
100
|
+
// Commands that need cmd /c wrapper on Windows
|
|
101
|
+
const windowsWrappedCommands = ['npx', 'npm', 'node', 'python', 'python3', 'pip', 'pip3', 'pnpm', 'yarn', 'bun'];
|
|
102
|
+
const needsWindowsWrapper = isWindowsPlatform && windowsWrappedCommands.includes(command.toLowerCase());
|
|
103
|
+
|
|
104
|
+
const config = needsWindowsWrapper
|
|
105
|
+
? { command: 'cmd', args: ['/c', command, ...args] }
|
|
106
|
+
: { command, args };
|
|
107
|
+
|
|
108
|
+
// Add optional fields
|
|
109
|
+
if (type) config.type = type;
|
|
110
|
+
if (env && Object.keys(env).length > 0) config.env = env;
|
|
111
|
+
Object.assign(config, rest);
|
|
112
|
+
|
|
113
|
+
return config;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Check if MCP config needs Windows cmd /c wrapper
|
|
118
|
+
* @param {object} serverConfig - MCP server configuration
|
|
119
|
+
* @returns {object} { needsWrapper: boolean, command: string }
|
|
120
|
+
*/
|
|
121
|
+
function checkWindowsMcpCompatibility(serverConfig) {
|
|
122
|
+
if (!isWindowsPlatform) return { needsWrapper: false };
|
|
123
|
+
|
|
124
|
+
const command = serverConfig.command?.toLowerCase() || '';
|
|
125
|
+
const windowsWrappedCommands = ['npx', 'npm', 'node', 'python', 'python3', 'pip', 'pip3', 'pnpm', 'yarn', 'bun'];
|
|
126
|
+
|
|
127
|
+
// Already wrapped with cmd
|
|
128
|
+
if (command === 'cmd') return { needsWrapper: false };
|
|
129
|
+
|
|
130
|
+
const needsWrapper = windowsWrappedCommands.includes(command);
|
|
131
|
+
return { needsWrapper, command: serverConfig.command };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Auto-fix MCP config for Windows platform
|
|
136
|
+
* @param {object} serverConfig - Original MCP server configuration
|
|
137
|
+
* @returns {object} Fixed configuration (or original if no fix needed)
|
|
138
|
+
*/
|
|
139
|
+
function autoFixWindowsMcpConfig(serverConfig) {
|
|
140
|
+
const { needsWrapper, command } = checkWindowsMcpCompatibility(serverConfig);
|
|
141
|
+
|
|
142
|
+
if (!needsWrapper) return serverConfig;
|
|
143
|
+
|
|
144
|
+
// Create new config with cmd /c wrapper
|
|
145
|
+
const fixedConfig = {
|
|
146
|
+
...serverConfig,
|
|
147
|
+
command: 'cmd',
|
|
148
|
+
args: ['/c', command, ...(serverConfig.args || [])]
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
return fixedConfig;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Show Windows compatibility warning for MCP config
|
|
156
|
+
* @param {string} serverName - Name of the MCP server
|
|
157
|
+
* @param {object} serverConfig - MCP server configuration
|
|
158
|
+
* @returns {Promise<boolean>} True if user confirms auto-fix, false to keep original
|
|
159
|
+
*/
|
|
160
|
+
async function showWindowsMcpCompatibilityWarning(serverName, serverConfig) {
|
|
161
|
+
const { needsWrapper, command } = checkWindowsMcpCompatibility(serverConfig);
|
|
162
|
+
|
|
163
|
+
if (!needsWrapper) return false;
|
|
164
|
+
|
|
165
|
+
// Show warning toast with auto-fix option
|
|
166
|
+
const message = t('mcp.windows.compatibilityWarning', {
|
|
167
|
+
name: serverName,
|
|
168
|
+
command: command
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
return new Promise((resolve) => {
|
|
172
|
+
// Create custom toast with action buttons
|
|
173
|
+
const toastContainer = document.getElementById('refreshToast') || createToastContainer();
|
|
174
|
+
const toastId = `windows-mcp-warning-${Date.now()}`;
|
|
175
|
+
|
|
176
|
+
const toastHtml = `
|
|
177
|
+
<div id="${toastId}" class="fixed bottom-4 right-4 bg-warning text-warning-foreground p-4 rounded-lg shadow-lg max-w-md z-50 animate-slide-up">
|
|
178
|
+
<div class="flex items-start gap-3">
|
|
179
|
+
<i data-lucide="alert-triangle" class="w-5 h-5 flex-shrink-0 mt-0.5"></i>
|
|
180
|
+
<div class="flex-1">
|
|
181
|
+
<p class="font-medium mb-2">${t('mcp.windows.title')}</p>
|
|
182
|
+
<p class="text-sm opacity-90 mb-3">${message}</p>
|
|
183
|
+
<div class="flex gap-2">
|
|
184
|
+
<button class="px-3 py-1.5 text-sm bg-background text-foreground rounded hover:opacity-90"
|
|
185
|
+
onclick="document.getElementById('${toastId}').remove(); window._mcpWindowsResolve && window._mcpWindowsResolve(true)">
|
|
186
|
+
${t('mcp.windows.autoFix')}
|
|
187
|
+
</button>
|
|
188
|
+
<button class="px-3 py-1.5 text-sm border border-current rounded hover:opacity-90"
|
|
189
|
+
onclick="document.getElementById('${toastId}').remove(); window._mcpWindowsResolve && window._mcpWindowsResolve(false)">
|
|
190
|
+
${t('mcp.windows.keepOriginal')}
|
|
191
|
+
</button>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
<button onclick="document.getElementById('${toastId}').remove(); window._mcpWindowsResolve && window._mcpWindowsResolve(false)"
|
|
195
|
+
class="text-current opacity-70 hover:opacity-100">
|
|
196
|
+
<i data-lucide="x" class="w-4 h-4"></i>
|
|
197
|
+
</button>
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
`;
|
|
201
|
+
|
|
202
|
+
// Store resolve function globally for button clicks
|
|
203
|
+
window._mcpWindowsResolve = (result) => {
|
|
204
|
+
delete window._mcpWindowsResolve;
|
|
205
|
+
resolve(result);
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
document.body.insertAdjacentHTML('beforeend', toastHtml);
|
|
209
|
+
|
|
210
|
+
// Initialize icons
|
|
211
|
+
if (typeof lucide !== 'undefined') {
|
|
212
|
+
lucide.createIcons();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Auto-dismiss after 15 seconds (keep original)
|
|
216
|
+
setTimeout(() => {
|
|
217
|
+
const toast = document.getElementById(toastId);
|
|
218
|
+
if (toast) {
|
|
219
|
+
toast.remove();
|
|
220
|
+
if (window._mcpWindowsResolve) {
|
|
221
|
+
window._mcpWindowsResolve(false);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}, 15000);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
87
228
|
// ========== Codex MCP Functions ==========
|
|
88
229
|
|
|
89
230
|
/**
|
|
@@ -847,6 +988,19 @@ async function submitMcpCreateFromJson() {
|
|
|
847
988
|
}
|
|
848
989
|
|
|
849
990
|
async function createMcpServerWithConfig(name, serverConfig, scope = 'project') {
|
|
991
|
+
// Check Windows compatibility and offer auto-fix if needed
|
|
992
|
+
const { needsWrapper } = checkWindowsMcpCompatibility(serverConfig);
|
|
993
|
+
let finalConfig = serverConfig;
|
|
994
|
+
|
|
995
|
+
if (needsWrapper) {
|
|
996
|
+
// Show warning and ask user whether to auto-fix
|
|
997
|
+
const shouldAutoFix = await showWindowsMcpCompatibilityWarning(name, serverConfig);
|
|
998
|
+
if (shouldAutoFix) {
|
|
999
|
+
finalConfig = autoFixWindowsMcpConfig(serverConfig);
|
|
1000
|
+
console.log('[MCP] Auto-fixed config for Windows:', finalConfig);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
850
1004
|
// Submit to API
|
|
851
1005
|
try {
|
|
852
1006
|
let response;
|
|
@@ -859,7 +1013,7 @@ async function createMcpServerWithConfig(name, serverConfig, scope = 'project')
|
|
|
859
1013
|
headers: { 'Content-Type': 'application/json' },
|
|
860
1014
|
body: JSON.stringify({
|
|
861
1015
|
serverName: name,
|
|
862
|
-
serverConfig:
|
|
1016
|
+
serverConfig: finalConfig
|
|
863
1017
|
})
|
|
864
1018
|
});
|
|
865
1019
|
scopeLabel = 'Codex';
|
|
@@ -869,7 +1023,7 @@ async function createMcpServerWithConfig(name, serverConfig, scope = 'project')
|
|
|
869
1023
|
headers: { 'Content-Type': 'application/json' },
|
|
870
1024
|
body: JSON.stringify({
|
|
871
1025
|
serverName: name,
|
|
872
|
-
serverConfig:
|
|
1026
|
+
serverConfig: finalConfig
|
|
873
1027
|
})
|
|
874
1028
|
});
|
|
875
1029
|
scopeLabel = 'global';
|
|
@@ -880,7 +1034,7 @@ async function createMcpServerWithConfig(name, serverConfig, scope = 'project')
|
|
|
880
1034
|
body: JSON.stringify({
|
|
881
1035
|
projectPath: projectPath,
|
|
882
1036
|
serverName: name,
|
|
883
|
-
serverConfig:
|
|
1037
|
+
serverConfig: finalConfig
|
|
884
1038
|
})
|
|
885
1039
|
});
|
|
886
1040
|
scopeLabel = 'project';
|
|
@@ -1231,16 +1385,14 @@ const RECOMMENDED_MCP_SERVERS = [
|
|
|
1231
1385
|
descKey: 'mcp.ace-tool.field.token.desc'
|
|
1232
1386
|
}
|
|
1233
1387
|
],
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
]
|
|
1243
|
-
})
|
|
1388
|
+
// Uses buildCrossPlatformMcpConfig for automatic Windows cmd /c wrapping
|
|
1389
|
+
buildConfig: (values) => buildCrossPlatformMcpConfig('npx', [
|
|
1390
|
+
'ace-tool',
|
|
1391
|
+
'--base-url',
|
|
1392
|
+
values.baseUrl || 'https://acemcp.heroman.wtf/relay/',
|
|
1393
|
+
'--token',
|
|
1394
|
+
values.token
|
|
1395
|
+
])
|
|
1244
1396
|
},
|
|
1245
1397
|
{
|
|
1246
1398
|
id: 'chrome-devtools',
|
|
@@ -1249,12 +1401,8 @@ const RECOMMENDED_MCP_SERVERS = [
|
|
|
1249
1401
|
icon: 'chrome',
|
|
1250
1402
|
category: 'browser',
|
|
1251
1403
|
fields: [],
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
command: 'npx',
|
|
1255
|
-
args: ['chrome-devtools-mcp@latest'],
|
|
1256
|
-
env: {}
|
|
1257
|
-
})
|
|
1404
|
+
// Uses buildCrossPlatformMcpConfig for automatic Windows cmd /c wrapping
|
|
1405
|
+
buildConfig: () => buildCrossPlatformMcpConfig('npx', ['chrome-devtools-mcp@latest'], { type: 'stdio' })
|
|
1258
1406
|
},
|
|
1259
1407
|
{
|
|
1260
1408
|
id: 'exa',
|
|
@@ -1273,16 +1421,10 @@ const RECOMMENDED_MCP_SERVERS = [
|
|
|
1273
1421
|
descKey: 'mcp.exa.field.apiKey.desc'
|
|
1274
1422
|
}
|
|
1275
1423
|
],
|
|
1424
|
+
// Uses buildCrossPlatformMcpConfig for automatic Windows cmd /c wrapping
|
|
1276
1425
|
buildConfig: (values) => {
|
|
1277
|
-
const
|
|
1278
|
-
|
|
1279
|
-
args: ['-y', 'exa-mcp-server']
|
|
1280
|
-
};
|
|
1281
|
-
// Only add env if API key is provided
|
|
1282
|
-
if (values.apiKey) {
|
|
1283
|
-
config.env = { EXA_API_KEY: values.apiKey };
|
|
1284
|
-
}
|
|
1285
|
-
return config;
|
|
1426
|
+
const env = values.apiKey ? { EXA_API_KEY: values.apiKey } : undefined;
|
|
1427
|
+
return buildCrossPlatformMcpConfig('npx', ['-y', 'exa-mcp-server'], { env });
|
|
1286
1428
|
}
|
|
1287
1429
|
}
|
|
1288
1430
|
];
|
|
@@ -415,10 +415,15 @@ function handleNotification(data) {
|
|
|
415
415
|
'CodexLens'
|
|
416
416
|
);
|
|
417
417
|
}
|
|
418
|
-
// Invalidate CodexLens
|
|
418
|
+
// Invalidate all CodexLens related caches to ensure fresh data on refresh
|
|
419
|
+
// Must clear both codexlens-specific cache AND global status cache
|
|
420
|
+
if (window.cacheManager) {
|
|
421
|
+
window.cacheManager.invalidate('all-status');
|
|
422
|
+
window.cacheManager.invalidate('dashboard-init');
|
|
423
|
+
}
|
|
419
424
|
if (typeof window.invalidateCodexLensCache === 'function') {
|
|
420
425
|
window.invalidateCodexLensCache();
|
|
421
|
-
console.log('[CodexLens]
|
|
426
|
+
console.log('[CodexLens] All caches invalidated after installation');
|
|
422
427
|
}
|
|
423
428
|
// Refresh CLI status if active
|
|
424
429
|
if (typeof loadCodexLensStatus === 'function') {
|
|
@@ -443,10 +448,15 @@ function handleNotification(data) {
|
|
|
443
448
|
'CodexLens'
|
|
444
449
|
);
|
|
445
450
|
}
|
|
446
|
-
// Invalidate CodexLens
|
|
451
|
+
// Invalidate all CodexLens related caches to ensure fresh data on refresh
|
|
452
|
+
// Must clear both codexlens-specific cache AND global status cache
|
|
453
|
+
if (window.cacheManager) {
|
|
454
|
+
window.cacheManager.invalidate('all-status');
|
|
455
|
+
window.cacheManager.invalidate('dashboard-init');
|
|
456
|
+
}
|
|
447
457
|
if (typeof window.invalidateCodexLensCache === 'function') {
|
|
448
458
|
window.invalidateCodexLensCache();
|
|
449
|
-
console.log('[CodexLens]
|
|
459
|
+
console.log('[CodexLens] All caches invalidated after uninstallation');
|
|
450
460
|
}
|
|
451
461
|
// Refresh CLI status if active
|
|
452
462
|
if (typeof loadCodexLensStatus === 'function') {
|
|
@@ -261,6 +261,13 @@ const i18n = {
|
|
|
261
261
|
'cli.wrapper': 'Wrapper',
|
|
262
262
|
'cli.customClaudeSettings': 'Custom Claude CLI settings',
|
|
263
263
|
'cli.updateFailed': 'Failed to update',
|
|
264
|
+
|
|
265
|
+
// CLI Tool Config - Environment File
|
|
266
|
+
'cli.envFile': 'Environment File',
|
|
267
|
+
'cli.envFileOptional': '(optional)',
|
|
268
|
+
'cli.envFilePlaceholder': 'Path to .env file (e.g., ~/.gemini-env or C:/Users/xxx/.env)',
|
|
269
|
+
'cli.envFileHint': 'Load environment variables (e.g., API keys) before CLI execution. Supports ~ for home directory.',
|
|
270
|
+
'cli.envFileBrowse': 'Browse',
|
|
264
271
|
|
|
265
272
|
// CodexLens Configuration
|
|
266
273
|
'codexlens.config': 'CodexLens Configuration',
|
|
@@ -995,6 +1002,12 @@ const i18n = {
|
|
|
995
1002
|
'mcp.clickToEdit': 'Click to edit',
|
|
996
1003
|
'mcp.clickToViewDetails': 'Click to view details',
|
|
997
1004
|
|
|
1005
|
+
// Windows MCP Compatibility
|
|
1006
|
+
'mcp.windows.title': 'Windows Compatibility Warning',
|
|
1007
|
+
'mcp.windows.compatibilityWarning': 'The MCP server "{name}" uses "{command}" which requires "cmd /c" wrapper on Windows to work properly with Claude Code.',
|
|
1008
|
+
'mcp.windows.autoFix': 'Auto-fix (Recommended)',
|
|
1009
|
+
'mcp.windows.keepOriginal': 'Keep Original',
|
|
1010
|
+
|
|
998
1011
|
// Hook Manager
|
|
999
1012
|
'hook.projectHooks': 'Project Hooks',
|
|
1000
1013
|
'hook.projectFile': '.claude/settings.json',
|
|
@@ -2415,6 +2428,13 @@ const i18n = {
|
|
|
2415
2428
|
'cli.wrapper': '封装',
|
|
2416
2429
|
'cli.customClaudeSettings': '自定义 Claude CLI 配置',
|
|
2417
2430
|
'cli.updateFailed': '更新失败',
|
|
2431
|
+
|
|
2432
|
+
// CLI 工具配置 - 环境文件
|
|
2433
|
+
'cli.envFile': '环境文件',
|
|
2434
|
+
'cli.envFileOptional': '(可选)',
|
|
2435
|
+
'cli.envFilePlaceholder': '.env 文件路径(如 ~/.gemini-env 或 C:/Users/xxx/.env)',
|
|
2436
|
+
'cli.envFileHint': '在 CLI 执行前加载环境变量(如 API 密钥)。支持 ~ 表示用户目录。',
|
|
2437
|
+
'cli.envFileBrowse': '浏览',
|
|
2418
2438
|
|
|
2419
2439
|
// CodexLens 配置
|
|
2420
2440
|
'codexlens.config': 'CodexLens 配置',
|
|
@@ -3128,6 +3148,12 @@ const i18n = {
|
|
|
3128
3148
|
'mcp.clickToEdit': '点击编辑',
|
|
3129
3149
|
'mcp.clickToViewDetails': '点击查看详情',
|
|
3130
3150
|
|
|
3151
|
+
// Windows MCP 兼容性
|
|
3152
|
+
'mcp.windows.title': 'Windows 兼容性警告',
|
|
3153
|
+
'mcp.windows.compatibilityWarning': 'MCP 服务器 "{name}" 使用的 "{command}" 命令需要在 Windows 上添加 "cmd /c" 包装才能与 Claude Code 正常工作。',
|
|
3154
|
+
'mcp.windows.autoFix': '自动修复(推荐)',
|
|
3155
|
+
'mcp.windows.keepOriginal': '保持原样',
|
|
3156
|
+
|
|
3131
3157
|
// Hook Manager
|
|
3132
3158
|
'hook.projectHooks': '项目钩子',
|
|
3133
3159
|
'hook.projectFile': '.claude/settings.json',
|
|
@@ -523,6 +523,27 @@ function buildToolConfigModalContent(tool, config, models, status) {
|
|
|
523
523
|
'</div>' +
|
|
524
524
|
'</div>' +
|
|
525
525
|
|
|
526
|
+
// Environment File Section (only for builtin tools: gemini, qwen)
|
|
527
|
+
(tool === 'gemini' || tool === 'qwen' ? (
|
|
528
|
+
'<div class="tool-config-section">' +
|
|
529
|
+
'<h4><i data-lucide="file-key" class="w-3.5 h-3.5"></i> ' + t('cli.envFile') + ' <span class="text-muted">' + t('cli.envFileOptional') + '</span></h4>' +
|
|
530
|
+
'<div class="env-file-input-group">' +
|
|
531
|
+
'<div class="env-file-input-row">' +
|
|
532
|
+
'<input type="text" id="envFileInput" class="tool-config-input" ' +
|
|
533
|
+
'placeholder="' + t('cli.envFilePlaceholder') + '" ' +
|
|
534
|
+
'value="' + (config.envFile ? escapeHtml(config.envFile) : '') + '" />' +
|
|
535
|
+
'<button type="button" class="btn-sm btn-outline" id="envFileBrowseBtn">' +
|
|
536
|
+
'<i data-lucide="folder-open" class="w-3.5 h-3.5"></i> ' + t('cli.envFileBrowse') +
|
|
537
|
+
'</button>' +
|
|
538
|
+
'</div>' +
|
|
539
|
+
'<p class="env-file-hint">' +
|
|
540
|
+
'<i data-lucide="info" class="w-3 h-3"></i> ' +
|
|
541
|
+
t('cli.envFileHint') +
|
|
542
|
+
'</p>' +
|
|
543
|
+
'</div>' +
|
|
544
|
+
'</div>'
|
|
545
|
+
) : '') +
|
|
546
|
+
|
|
526
547
|
// Footer
|
|
527
548
|
'<div class="tool-config-footer">' +
|
|
528
549
|
'<button class="btn btn-outline" onclick="closeModal()">' + t('common.cancel') + '</button>' +
|
|
@@ -701,12 +722,23 @@ function initToolConfigModalEvents(tool, currentConfig, models) {
|
|
|
701
722
|
return;
|
|
702
723
|
}
|
|
703
724
|
|
|
725
|
+
// Get envFile value (only for gemini/qwen)
|
|
726
|
+
var envFileInput = document.getElementById('envFileInput');
|
|
727
|
+
var envFile = envFileInput ? envFileInput.value.trim() : '';
|
|
728
|
+
|
|
704
729
|
try {
|
|
705
|
-
|
|
730
|
+
var updateData = {
|
|
706
731
|
primaryModel: primaryModel,
|
|
707
732
|
secondaryModel: secondaryModel,
|
|
708
733
|
tags: currentTags
|
|
709
|
-
}
|
|
734
|
+
};
|
|
735
|
+
|
|
736
|
+
// Only include envFile for gemini/qwen tools
|
|
737
|
+
if (tool === 'gemini' || tool === 'qwen') {
|
|
738
|
+
updateData.envFile = envFile || null;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
await updateCliToolConfig(tool, updateData);
|
|
710
742
|
// Reload config to reflect changes
|
|
711
743
|
await loadCliToolConfig();
|
|
712
744
|
showRefreshToast('Configuration saved', 'success');
|
|
@@ -719,6 +751,44 @@ function initToolConfigModalEvents(tool, currentConfig, models) {
|
|
|
719
751
|
};
|
|
720
752
|
}
|
|
721
753
|
|
|
754
|
+
// Environment file browse button (only for gemini/qwen)
|
|
755
|
+
var envFileBrowseBtn = document.getElementById('envFileBrowseBtn');
|
|
756
|
+
if (envFileBrowseBtn) {
|
|
757
|
+
envFileBrowseBtn.onclick = async function() {
|
|
758
|
+
try {
|
|
759
|
+
// Use file dialog API if available
|
|
760
|
+
var response = await fetch('/api/dialog/open-file', {
|
|
761
|
+
method: 'POST',
|
|
762
|
+
headers: { 'Content-Type': 'application/json' },
|
|
763
|
+
body: JSON.stringify({
|
|
764
|
+
title: t('cli.envFile'),
|
|
765
|
+
filters: [
|
|
766
|
+
{ name: 'Environment Files', extensions: ['env'] },
|
|
767
|
+
{ name: 'All Files', extensions: ['*'] }
|
|
768
|
+
],
|
|
769
|
+
defaultPath: ''
|
|
770
|
+
})
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
if (response.ok) {
|
|
774
|
+
var data = await response.json();
|
|
775
|
+
if (data.filePath) {
|
|
776
|
+
var envFileInput = document.getElementById('envFileInput');
|
|
777
|
+
if (envFileInput) {
|
|
778
|
+
envFileInput.value = data.filePath;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
} else {
|
|
782
|
+
// Fallback: prompt user to enter path manually
|
|
783
|
+
showRefreshToast('File dialog not available. Please enter path manually.', 'info');
|
|
784
|
+
}
|
|
785
|
+
} catch (err) {
|
|
786
|
+
console.error('Failed to open file dialog:', err);
|
|
787
|
+
showRefreshToast('File dialog not available. Please enter path manually.', 'info');
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
|
|
722
792
|
// Initialize lucide icons in modal
|
|
723
793
|
if (window.lucide) lucide.createIcons();
|
|
724
794
|
}
|
|
@@ -72,6 +72,10 @@ function invalidateCache(key) {
|
|
|
72
72
|
Object.values(CACHE_KEY_MAP).forEach(function(k) {
|
|
73
73
|
window.cacheManager.invalidate(k);
|
|
74
74
|
});
|
|
75
|
+
// 重要:同时清理包含 CodexLens 状态的全局缓存
|
|
76
|
+
// 这些缓存在 cli-status.js 中使用,包含 codexLens.ready 状态
|
|
77
|
+
window.cacheManager.invalidate('all-status');
|
|
78
|
+
window.cacheManager.invalidate('dashboard-init');
|
|
75
79
|
}
|
|
76
80
|
}
|
|
77
81
|
|
|
@@ -788,6 +792,12 @@ function initCodexLensConfigEvents(currentConfig) {
|
|
|
788
792
|
|
|
789
793
|
if (result.success) {
|
|
790
794
|
showRefreshToast(t('codexlens.configSaved'), 'success');
|
|
795
|
+
|
|
796
|
+
// Invalidate config cache to ensure fresh data on next load
|
|
797
|
+
if (window.cacheManager) {
|
|
798
|
+
window.cacheManager.invalidate('codexlens-config');
|
|
799
|
+
}
|
|
800
|
+
|
|
791
801
|
closeModal();
|
|
792
802
|
|
|
793
803
|
// Refresh CodexLens status
|
|
@@ -5385,7 +5395,7 @@ function initCodexLensManagerPageEvents(currentConfig) {
|
|
|
5385
5395
|
try {
|
|
5386
5396
|
var response = await csrfFetch('/api/codexlens/config', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ index_dir: newIndexDir }) });
|
|
5387
5397
|
var result = await response.json();
|
|
5388
|
-
if (result.success) { showRefreshToast(t('codexlens.configSaved'), 'success'); renderCodexLensManager(); }
|
|
5398
|
+
if (result.success) { if (window.cacheManager) { window.cacheManager.invalidate('codexlens-config'); } showRefreshToast(t('codexlens.configSaved'), 'success'); renderCodexLensManager(); }
|
|
5389
5399
|
else { showRefreshToast(t('common.saveFailed') + ': ' + result.error, 'error'); }
|
|
5390
5400
|
} catch (err) { showRefreshToast(t('common.error') + ': ' + err.message, 'error'); }
|
|
5391
5401
|
saveBtn.disabled = false;
|
|
@@ -34,6 +34,11 @@ export interface ClaudeCliTool {
|
|
|
34
34
|
* Used to lookup endpoint configuration in litellm-api-config.json
|
|
35
35
|
*/
|
|
36
36
|
id?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Path to .env file for loading environment variables before CLI execution
|
|
39
|
+
* Supports both absolute paths and paths relative to home directory (e.g., ~/.my-env)
|
|
40
|
+
*/
|
|
41
|
+
envFile?: string;
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
export type CliToolName = 'gemini' | 'qwen' | 'codex' | 'claude' | 'opencode' | string;
|
|
@@ -808,6 +813,7 @@ export function getToolConfig(projectDir: string, tool: string): {
|
|
|
808
813
|
primaryModel: string;
|
|
809
814
|
secondaryModel: string;
|
|
810
815
|
tags?: string[];
|
|
816
|
+
envFile?: string;
|
|
811
817
|
} {
|
|
812
818
|
const config = loadClaudeCliTools(projectDir);
|
|
813
819
|
const toolConfig = config.tools[tool];
|
|
@@ -826,7 +832,8 @@ export function getToolConfig(projectDir: string, tool: string): {
|
|
|
826
832
|
enabled: toolConfig.enabled,
|
|
827
833
|
primaryModel: toolConfig.primaryModel ?? '',
|
|
828
834
|
secondaryModel: toolConfig.secondaryModel ?? '',
|
|
829
|
-
tags: toolConfig.tags
|
|
835
|
+
tags: toolConfig.tags,
|
|
836
|
+
envFile: toolConfig.envFile
|
|
830
837
|
};
|
|
831
838
|
}
|
|
832
839
|
|
|
@@ -841,6 +848,7 @@ export function updateToolConfig(
|
|
|
841
848
|
primaryModel: string;
|
|
842
849
|
secondaryModel: string;
|
|
843
850
|
tags: string[];
|
|
851
|
+
envFile: string | null;
|
|
844
852
|
}>
|
|
845
853
|
): ClaudeCliToolsConfig {
|
|
846
854
|
const config = loadClaudeCliTools(projectDir);
|
|
@@ -858,6 +866,14 @@ export function updateToolConfig(
|
|
|
858
866
|
if (updates.tags !== undefined) {
|
|
859
867
|
config.tools[tool].tags = updates.tags;
|
|
860
868
|
}
|
|
869
|
+
// Handle envFile: set to undefined if null/empty, otherwise set value
|
|
870
|
+
if (updates.envFile !== undefined) {
|
|
871
|
+
if (updates.envFile === null || updates.envFile === '') {
|
|
872
|
+
delete config.tools[tool].envFile;
|
|
873
|
+
} else {
|
|
874
|
+
config.tools[tool].envFile = updates.envFile;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
861
877
|
saveClaudeCliTools(projectDir, config);
|
|
862
878
|
}
|
|
863
879
|
|