claude-ai-switcher 1.1.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/AGENTS.md +265 -0
- package/ARCHITECTURE.md +162 -0
- package/CLAUDE.md +267 -0
- package/LICENSE +21 -0
- package/QWEN.md +429 -0
- package/README.md +833 -0
- package/dist/clients/claude-code.d.ts +92 -0
- package/dist/clients/claude-code.d.ts.map +1 -0
- package/dist/clients/claude-code.js +312 -0
- package/dist/clients/claude-code.js.map +1 -0
- package/dist/clients/opencode.d.ts +71 -0
- package/dist/clients/opencode.d.ts.map +1 -0
- package/dist/clients/opencode.js +604 -0
- package/dist/clients/opencode.js.map +1 -0
- package/dist/config.d.ts +37 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +122 -0
- package/dist/config.js.map +1 -0
- package/dist/display.d.ts +51 -0
- package/dist/display.d.ts.map +1 -0
- package/dist/display.js +118 -0
- package/dist/display.js.map +1 -0
- package/dist/hooks/index.d.ts +60 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +223 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/token-tracker.js +280 -0
- package/dist/hooks/visual-enhancements.js +364 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1091 -0
- package/dist/index.js.map +1 -0
- package/dist/models.d.ts +34 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +343 -0
- package/dist/models.js.map +1 -0
- package/dist/providers/alibaba.d.ts +25 -0
- package/dist/providers/alibaba.d.ts.map +1 -0
- package/dist/providers/alibaba.js +37 -0
- package/dist/providers/alibaba.js.map +1 -0
- package/dist/providers/anthropic.d.ts +14 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +19 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/gemini.d.ts +44 -0
- package/dist/providers/gemini.d.ts.map +1 -0
- package/dist/providers/gemini.js +156 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/providers/glm.d.ts +25 -0
- package/dist/providers/glm.d.ts.map +1 -0
- package/dist/providers/glm.js +89 -0
- package/dist/providers/glm.js.map +1 -0
- package/dist/providers/ollama.d.ts +48 -0
- package/dist/providers/ollama.d.ts.map +1 -0
- package/dist/providers/ollama.js +174 -0
- package/dist/providers/ollama.js.map +1 -0
- package/dist/providers/openrouter.d.ts +24 -0
- package/dist/providers/openrouter.d.ts.map +1 -0
- package/dist/providers/openrouter.js +36 -0
- package/dist/providers/openrouter.js.map +1 -0
- package/dist/verify.d.ts +24 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +262 -0
- package/dist/verify.js.map +1 -0
- package/package.json +57 -0
- package/scripts/copy-hooks.js +15 -0
- package/src/clients/claude-code.ts +340 -0
- package/src/clients/opencode.ts +618 -0
- package/src/config.ts +101 -0
- package/src/display.ts +151 -0
- package/src/hooks/index.ts +208 -0
- package/src/hooks/token-tracker.js +280 -0
- package/src/hooks/visual-enhancements.js +364 -0
- package/src/index.ts +1263 -0
- package/src/models.ts +366 -0
- package/src/providers/alibaba.ts +43 -0
- package/src/providers/anthropic.ts +23 -0
- package/src/providers/gemini.ts +136 -0
- package/src/providers/glm.ts +60 -0
- package/src/providers/ollama.ts +146 -0
- package/src/providers/openrouter.ts +42 -0
- package/src/verify.ts +258 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code Visual Enhancements
|
|
3
|
+
*
|
|
4
|
+
* Provides visual enhancements for Claude Code including:
|
|
5
|
+
* - Active model display with provider info
|
|
6
|
+
* - Context usage bar (percentage)
|
|
7
|
+
* - Provider endpoint display
|
|
8
|
+
* - Custom system prompt injection
|
|
9
|
+
*
|
|
10
|
+
* Installed to: ~/.claude/visual-enhancements.js
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const os = require('os');
|
|
16
|
+
|
|
17
|
+
const SETTINGS_FILE = path.join(os.homedir(), '.claude', 'settings.json');
|
|
18
|
+
const PROMPT_FILE = path.join(os.homedir(), '.claude', 'prompt.json');
|
|
19
|
+
|
|
20
|
+
// Provider information
|
|
21
|
+
const PROVIDER_INFO = {
|
|
22
|
+
anthropic: {
|
|
23
|
+
name: 'Anthropic',
|
|
24
|
+
endpoint: 'api.anthropic.com',
|
|
25
|
+
icon: '🎭'
|
|
26
|
+
},
|
|
27
|
+
alibaba: {
|
|
28
|
+
name: 'Alibaba Model Studio',
|
|
29
|
+
endpoint: 'coding-intl.dashscope.aliyuncs.com',
|
|
30
|
+
icon: ''
|
|
31
|
+
},
|
|
32
|
+
glm: {
|
|
33
|
+
name: 'GLM/Z.AI',
|
|
34
|
+
endpoint: 'z.ai',
|
|
35
|
+
icon: '🤖'
|
|
36
|
+
},
|
|
37
|
+
openrouter: {
|
|
38
|
+
name: 'OpenRouter',
|
|
39
|
+
endpoint: 'openrouter.ai',
|
|
40
|
+
icon: '🌐'
|
|
41
|
+
},
|
|
42
|
+
ollama: {
|
|
43
|
+
name: 'Ollama (Local)',
|
|
44
|
+
endpoint: 'localhost:4000',
|
|
45
|
+
icon: ''
|
|
46
|
+
},
|
|
47
|
+
gemini: {
|
|
48
|
+
name: 'Gemini (Google)',
|
|
49
|
+
endpoint: 'localhost:4001',
|
|
50
|
+
icon: '💎'
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Model capabilities for display
|
|
55
|
+
const MODEL_CAPABILITIES = {
|
|
56
|
+
// Alibaba Models
|
|
57
|
+
'qwen3.7-plus': ['Text Generation', 'Deep Thinking', 'Visual Understanding'],
|
|
58
|
+
'qwen3.6-plus': ['Text Generation', 'Deep Thinking', 'Visual Understanding'],
|
|
59
|
+
'qwen3-max-2026-01-23': ['Text Generation', 'Deep Thinking'],
|
|
60
|
+
'qwen3-coder-next': ['Text Generation', 'Coding Agent'],
|
|
61
|
+
'qwen3-coder-plus': ['Text Generation', 'Coding', '1M Context'],
|
|
62
|
+
'glm-5': ['Text Generation', 'Deep Thinking'],
|
|
63
|
+
'glm-4.7': ['Text Generation', 'Deep Thinking'],
|
|
64
|
+
'glm-4.7-flash': ['Text Generation', 'Fast Inference'],
|
|
65
|
+
'kimi-k2.5': ['Text Generation', 'Deep Thinking', 'Visual Understanding'],
|
|
66
|
+
'MiniMax-M2.5': ['Text Generation', 'Deep Thinking'],
|
|
67
|
+
|
|
68
|
+
// GLM Models
|
|
69
|
+
'glm-5.1': ['Text Generation', 'Deep Thinking'],
|
|
70
|
+
'glm-5.2[1m]': ['Text Generation', 'Deep Thinking', '1M Context'],
|
|
71
|
+
'glm-5v-turbo': ['Text Generation', 'Deep Thinking', 'Multimodal'],
|
|
72
|
+
'glm-5-turbo': ['Text Generation', 'Deep Thinking', 'Fast'],
|
|
73
|
+
|
|
74
|
+
// OpenRouter Models
|
|
75
|
+
'qwen/qwen3.6-plus:free': ['Text Generation', 'Deep Thinking'],
|
|
76
|
+
'openrouter/free': ['Text Generation'],
|
|
77
|
+
|
|
78
|
+
// Ollama Models
|
|
79
|
+
'deepseek-r1:latest': ['Text Generation', 'Deep Thinking', 'Reasoning'],
|
|
80
|
+
'qwen2.5-coder:latest': ['Text Generation', 'Coding', 'Tool Calling'],
|
|
81
|
+
'llama3.1:latest': ['Text Generation', 'Code', 'Vision'],
|
|
82
|
+
'codellama:latest': ['Text Generation', 'Coding'],
|
|
83
|
+
|
|
84
|
+
// Gemini Models
|
|
85
|
+
'gemini-2.5-pro': ['Text Generation', 'Deep Thinking', 'Code', 'Vision'],
|
|
86
|
+
'gemini-2.5-flash': ['Text Generation', 'Fast Responses', 'Code'],
|
|
87
|
+
'gemini-2.5-flash-lite': ['Text Generation', 'Cost-optimized'],
|
|
88
|
+
|
|
89
|
+
// Anthropic Models
|
|
90
|
+
'claude-opus-4-6-20250205': ['Text Generation', 'Code', 'Vision', 'Complex Reasoning'],
|
|
91
|
+
'claude-opus-4-5-20251101': ['Text Generation', 'Code', 'Vision', 'Complex Reasoning'],
|
|
92
|
+
'claude-sonnet-4-6-20250219': ['Text Generation', 'Code', 'Vision'],
|
|
93
|
+
'claude-sonnet-4-5-20250814': ['Text Generation', 'Code', 'Vision'],
|
|
94
|
+
'claude-haiku-4-5-20251015': ['Text Generation', 'Fast Responses'],
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Detect current provider from settings
|
|
99
|
+
*/
|
|
100
|
+
function detectProvider() {
|
|
101
|
+
try {
|
|
102
|
+
if (!fs.existsSync(SETTINGS_FILE)) {
|
|
103
|
+
return 'anthropic';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const settings = JSON.parse(fs.readFileSync(SETTINGS_FILE, 'utf-8'));
|
|
107
|
+
const baseUrl = settings.env?.ANTHROPIC_BASE_URL || '';
|
|
108
|
+
|
|
109
|
+
if (baseUrl.includes('coding-intl.dashscope.aliyuncs.com')) {
|
|
110
|
+
return 'alibaba';
|
|
111
|
+
}
|
|
112
|
+
if (baseUrl.includes('openrouter.ai')) {
|
|
113
|
+
return 'openrouter';
|
|
114
|
+
}
|
|
115
|
+
if (baseUrl.includes('localhost:4000')) {
|
|
116
|
+
return 'ollama';
|
|
117
|
+
}
|
|
118
|
+
if (baseUrl.includes('localhost:4001')) {
|
|
119
|
+
return 'gemini';
|
|
120
|
+
}
|
|
121
|
+
if (baseUrl.includes('z.ai')) {
|
|
122
|
+
return 'glm';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Check tier map aliases (GLM indicator)
|
|
126
|
+
if (settings.env?.ANTHROPIC_DEFAULT_OPUS_MODEL && !baseUrl) {
|
|
127
|
+
return 'glm';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return 'anthropic';
|
|
131
|
+
} catch (error) {
|
|
132
|
+
return 'anthropic';
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Get current model from settings
|
|
138
|
+
*/
|
|
139
|
+
function getCurrentModel() {
|
|
140
|
+
try {
|
|
141
|
+
if (!fs.existsSync(SETTINGS_FILE)) {
|
|
142
|
+
return 'claude-opus-4-6-20250205';
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const settings = JSON.parse(fs.readFileSync(SETTINGS_FILE, 'utf-8'));
|
|
146
|
+
|
|
147
|
+
if (settings.env?.ANTHROPIC_MODEL) {
|
|
148
|
+
return settings.env.ANTHROPIC_MODEL;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (settings.env?.ANTHROPIC_DEFAULT_OPUS_MODEL) {
|
|
152
|
+
return settings.env.ANTHROPIC_DEFAULT_OPUS_MODEL;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return 'claude-opus-4-6-20250205';
|
|
156
|
+
} catch (error) {
|
|
157
|
+
return 'claude-opus-4-6-20250205';
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Get context window for model
|
|
163
|
+
*/
|
|
164
|
+
function getContextWindow(modelId) {
|
|
165
|
+
const CONTEXT_WINDOWS = {
|
|
166
|
+
'qwen3.7-plus': 1000000,
|
|
167
|
+
'qwen3.6-plus': 1000000,
|
|
168
|
+
'qwen3-max-2026-01-23': 262144,
|
|
169
|
+
'qwen3-coder-next': 262144,
|
|
170
|
+
'qwen3-coder-plus': 1000000,
|
|
171
|
+
'glm-5': 200000,
|
|
172
|
+
'glm-4.7': 256000,
|
|
173
|
+
'glm-4.7-flash': 256000,
|
|
174
|
+
'kimi-k2.5': 200000,
|
|
175
|
+
'MiniMax-M2.5': 200000,
|
|
176
|
+
'glm-5.1': 200000,
|
|
177
|
+
'glm-5.2[1m]': 1000000,
|
|
178
|
+
'glm-5v-turbo': 200000,
|
|
179
|
+
'glm-5-turbo': 200000,
|
|
180
|
+
'qwen/qwen3.6-plus:free': 131072,
|
|
181
|
+
'openrouter/free': 131072,
|
|
182
|
+
'deepseek-r1:latest': 128000,
|
|
183
|
+
'qwen2.5-coder:latest': 128000,
|
|
184
|
+
'llama3.1:latest': 128000,
|
|
185
|
+
'codellama:latest': 100000,
|
|
186
|
+
'gemini-2.5-pro': 1000000,
|
|
187
|
+
'gemini-2.5-flash': 1000000,
|
|
188
|
+
'gemini-2.5-flash-lite': 1000000,
|
|
189
|
+
'claude-opus-4-6-20250205': 200000,
|
|
190
|
+
'claude-opus-4-5-20251101': 200000,
|
|
191
|
+
'claude-sonnet-4-6-20250219': 200000,
|
|
192
|
+
'claude-sonnet-4-5-20250814': 200000,
|
|
193
|
+
'claude-haiku-4-5-20251015': 200000,
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
return CONTEXT_WINDOWS[modelId] || 200000;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Format context number
|
|
201
|
+
*/
|
|
202
|
+
function formatContext(tokens) {
|
|
203
|
+
if (tokens >= 1000000) {
|
|
204
|
+
return `${(tokens / 1000000).toFixed(1)}M`;
|
|
205
|
+
}
|
|
206
|
+
if (tokens >= 1000) {
|
|
207
|
+
return `${(tokens / 1000).toFixed(0)}K`;
|
|
208
|
+
}
|
|
209
|
+
return tokens.toString();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Create visual model card
|
|
214
|
+
*/
|
|
215
|
+
function createModelCard() {
|
|
216
|
+
const provider = detectProvider();
|
|
217
|
+
const model = getCurrentModel();
|
|
218
|
+
const contextWindow = getContextWindow(model);
|
|
219
|
+
const providerInfo = PROVIDER_INFO[provider];
|
|
220
|
+
const capabilities = MODEL_CAPABILITIES[model] || [];
|
|
221
|
+
|
|
222
|
+
const reset = '\x1b[0m';
|
|
223
|
+
const bold = '\x1b[1m';
|
|
224
|
+
const dim = '\x1b[2m';
|
|
225
|
+
|
|
226
|
+
// Colors
|
|
227
|
+
const headerColor = '\x1b[36m'; // Cyan
|
|
228
|
+
const accentColor = '\x1b[33m'; // Yellow
|
|
229
|
+
const infoColor = '\x1b[37m'; // White
|
|
230
|
+
const dimColor = '\x1b[90m'; // Gray
|
|
231
|
+
|
|
232
|
+
const modelName = model.includes('claude-') ? model.replace('claude-', 'Claude ').replace(/-/g, ' ') : model;
|
|
233
|
+
|
|
234
|
+
let output = '';
|
|
235
|
+
output += `${dimColor}┌─────────────────────────────────────────────────────────────┐${reset}\n`;
|
|
236
|
+
output += `${dimColor}│${reset} ${headerColor}${bold}${providerInfo.icon} ${providerInfo.name}${reset}${dimColor}${' '.repeat(48)}│${reset}\n`;
|
|
237
|
+
output += `${dimColor}├─────────────────────────────────────────────────────────────┤${reset}\n`;
|
|
238
|
+
output += `${dimColor}│${reset} ${accentColor}Model:${reset} ${infoColor}${modelName}${' '.repeat(Math.max(0, 35 - modelName.length))}${dimColor}│${reset}\n`;
|
|
239
|
+
output += `${dimColor}│${reset} ${accentColor}Context:${reset} ${infoColor}${formatContext(contextWindow)} tokens${' '.repeat(Math.max(0, 31 - formatContext(contextWindow).length - 8))}${dimColor}│${reset}\n`;
|
|
240
|
+
|
|
241
|
+
if (capabilities.length > 0) {
|
|
242
|
+
const capStr = capabilities.slice(0, 3).join(' • ');
|
|
243
|
+
output += `${dimColor}│${reset} ${accentColor}Capabilities:${reset}${' '.repeat(Math.max(0, 38 - capStr.length - 14))}${dimColor}│${reset}\n`;
|
|
244
|
+
output += `${dimColor}│${reset} ${dimColor}${capStr}${' '.repeat(Math.max(0, 52 - capStr.length))}${dimColor}│${reset}\n`;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
output += `${dimColor}└─────────────────────────────────────────────────────────────┘${reset}\n`;
|
|
248
|
+
|
|
249
|
+
return output;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Create context usage bar
|
|
254
|
+
*/
|
|
255
|
+
function createContextBar(usedTokens, totalTokens) {
|
|
256
|
+
if (!totalTokens || totalTokens <= 0) {
|
|
257
|
+
return '\x1b[32m' + '░'.repeat(30) + '\x1b[0m 0.0%';
|
|
258
|
+
}
|
|
259
|
+
const percentage = Math.min((usedTokens / totalTokens) * 100, 100);
|
|
260
|
+
const barWidth = 30;
|
|
261
|
+
const filled = Math.round((percentage / 100) * barWidth);
|
|
262
|
+
const empty = barWidth - filled;
|
|
263
|
+
|
|
264
|
+
const reset = '\x1b[0m';
|
|
265
|
+
const filledChar = '█';
|
|
266
|
+
const emptyChar = '░';
|
|
267
|
+
|
|
268
|
+
// Color based on usage
|
|
269
|
+
let color;
|
|
270
|
+
if (percentage < 50) {
|
|
271
|
+
color = '\x1b[32m'; // Green
|
|
272
|
+
} else if (percentage < 75) {
|
|
273
|
+
color = '\x1b[33m'; // Yellow
|
|
274
|
+
} else if (percentage < 90) {
|
|
275
|
+
color = '\x1b[31m'; // Red
|
|
276
|
+
} else {
|
|
277
|
+
color = '\x1b[35m'; // Magenta
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const bar = filledChar.repeat(filled) + emptyChar.repeat(empty);
|
|
281
|
+
|
|
282
|
+
return `${color}${bar}${reset} ${percentage.toFixed(1).padStart(5)}%`;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Display full status (called on session start)
|
|
287
|
+
*/
|
|
288
|
+
function displayStatus() {
|
|
289
|
+
console.log('');
|
|
290
|
+
console.log(createModelCard());
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Generate custom system prompt based on provider and model
|
|
295
|
+
*/
|
|
296
|
+
function generateSystemPrompt() {
|
|
297
|
+
const provider = detectProvider();
|
|
298
|
+
const model = getCurrentModel();
|
|
299
|
+
const providerInfo = PROVIDER_INFO[provider];
|
|
300
|
+
|
|
301
|
+
const prompt = {
|
|
302
|
+
system: [
|
|
303
|
+
`You are running on ${providerInfo.name} using the ${model} model.`,
|
|
304
|
+
`Provider endpoint: ${providerInfo.endpoint}`,
|
|
305
|
+
``,
|
|
306
|
+
`Current configuration:`,
|
|
307
|
+
`- Provider: ${providerInfo.name}`,
|
|
308
|
+
`- Model: ${model}`,
|
|
309
|
+
`- Context Window: ${formatContext(getContextWindow(model))} tokens`,
|
|
310
|
+
].join('\n')
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
return prompt;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Write custom prompt to prompt.json
|
|
318
|
+
*/
|
|
319
|
+
function writeCustomPrompt() {
|
|
320
|
+
try {
|
|
321
|
+
const prompt = generateSystemPrompt();
|
|
322
|
+
fs.writeFileSync(PROMPT_FILE, JSON.stringify(prompt, null, 2), 'utf-8');
|
|
323
|
+
return true;
|
|
324
|
+
} catch (error) {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Initialize visual enhancements
|
|
331
|
+
*/
|
|
332
|
+
function init() {
|
|
333
|
+
displayStatus();
|
|
334
|
+
writeCustomPrompt();
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Update visual enhancements (called after provider switch)
|
|
339
|
+
*/
|
|
340
|
+
function update() {
|
|
341
|
+
displayStatus();
|
|
342
|
+
writeCustomPrompt();
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Export functions
|
|
347
|
+
*/
|
|
348
|
+
module.exports = {
|
|
349
|
+
init,
|
|
350
|
+
update,
|
|
351
|
+
displayStatus,
|
|
352
|
+
createModelCard,
|
|
353
|
+
createContextBar,
|
|
354
|
+
generateSystemPrompt,
|
|
355
|
+
writeCustomPrompt,
|
|
356
|
+
detectProvider,
|
|
357
|
+
getCurrentModel,
|
|
358
|
+
getContextWindow
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// Auto-run if called directly
|
|
362
|
+
if (require.main === module) {
|
|
363
|
+
init();
|
|
364
|
+
}
|