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.
Files changed (83) hide show
  1. package/AGENTS.md +265 -0
  2. package/ARCHITECTURE.md +162 -0
  3. package/CLAUDE.md +267 -0
  4. package/LICENSE +21 -0
  5. package/QWEN.md +429 -0
  6. package/README.md +833 -0
  7. package/dist/clients/claude-code.d.ts +92 -0
  8. package/dist/clients/claude-code.d.ts.map +1 -0
  9. package/dist/clients/claude-code.js +312 -0
  10. package/dist/clients/claude-code.js.map +1 -0
  11. package/dist/clients/opencode.d.ts +71 -0
  12. package/dist/clients/opencode.d.ts.map +1 -0
  13. package/dist/clients/opencode.js +604 -0
  14. package/dist/clients/opencode.js.map +1 -0
  15. package/dist/config.d.ts +37 -0
  16. package/dist/config.d.ts.map +1 -0
  17. package/dist/config.js +122 -0
  18. package/dist/config.js.map +1 -0
  19. package/dist/display.d.ts +51 -0
  20. package/dist/display.d.ts.map +1 -0
  21. package/dist/display.js +118 -0
  22. package/dist/display.js.map +1 -0
  23. package/dist/hooks/index.d.ts +60 -0
  24. package/dist/hooks/index.d.ts.map +1 -0
  25. package/dist/hooks/index.js +223 -0
  26. package/dist/hooks/index.js.map +1 -0
  27. package/dist/hooks/token-tracker.js +280 -0
  28. package/dist/hooks/visual-enhancements.js +364 -0
  29. package/dist/index.d.ts +9 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +1091 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/models.d.ts +34 -0
  34. package/dist/models.d.ts.map +1 -0
  35. package/dist/models.js +343 -0
  36. package/dist/models.js.map +1 -0
  37. package/dist/providers/alibaba.d.ts +25 -0
  38. package/dist/providers/alibaba.d.ts.map +1 -0
  39. package/dist/providers/alibaba.js +37 -0
  40. package/dist/providers/alibaba.js.map +1 -0
  41. package/dist/providers/anthropic.d.ts +14 -0
  42. package/dist/providers/anthropic.d.ts.map +1 -0
  43. package/dist/providers/anthropic.js +19 -0
  44. package/dist/providers/anthropic.js.map +1 -0
  45. package/dist/providers/gemini.d.ts +44 -0
  46. package/dist/providers/gemini.d.ts.map +1 -0
  47. package/dist/providers/gemini.js +156 -0
  48. package/dist/providers/gemini.js.map +1 -0
  49. package/dist/providers/glm.d.ts +25 -0
  50. package/dist/providers/glm.d.ts.map +1 -0
  51. package/dist/providers/glm.js +89 -0
  52. package/dist/providers/glm.js.map +1 -0
  53. package/dist/providers/ollama.d.ts +48 -0
  54. package/dist/providers/ollama.d.ts.map +1 -0
  55. package/dist/providers/ollama.js +174 -0
  56. package/dist/providers/ollama.js.map +1 -0
  57. package/dist/providers/openrouter.d.ts +24 -0
  58. package/dist/providers/openrouter.d.ts.map +1 -0
  59. package/dist/providers/openrouter.js +36 -0
  60. package/dist/providers/openrouter.js.map +1 -0
  61. package/dist/verify.d.ts +24 -0
  62. package/dist/verify.d.ts.map +1 -0
  63. package/dist/verify.js +262 -0
  64. package/dist/verify.js.map +1 -0
  65. package/package.json +57 -0
  66. package/scripts/copy-hooks.js +15 -0
  67. package/src/clients/claude-code.ts +340 -0
  68. package/src/clients/opencode.ts +618 -0
  69. package/src/config.ts +101 -0
  70. package/src/display.ts +151 -0
  71. package/src/hooks/index.ts +208 -0
  72. package/src/hooks/token-tracker.js +280 -0
  73. package/src/hooks/visual-enhancements.js +364 -0
  74. package/src/index.ts +1263 -0
  75. package/src/models.ts +366 -0
  76. package/src/providers/alibaba.ts +43 -0
  77. package/src/providers/anthropic.ts +23 -0
  78. package/src/providers/gemini.ts +136 -0
  79. package/src/providers/glm.ts +60 -0
  80. package/src/providers/ollama.ts +146 -0
  81. package/src/providers/openrouter.ts +42 -0
  82. package/src/verify.ts +258 -0
  83. 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
+ }