@kikkimo/claude-launcher 2.3.0 → 2.5.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/CHANGELOG.md +70 -0
- package/README.md +35 -13
- package/claude-launcher +425 -48
- package/docs/README-zh.md +35 -13
- package/docs/superpowers/plans/2026-03-31-update-models-and-auto-mode.md +1414 -0
- package/docs/superpowers/specs/2026-03-31-update-models-and-auto-mode-design.md +187 -0
- package/lib/api-manager.js +135 -1
- package/lib/i18n/locales/de.js +74 -3
- package/lib/i18n/locales/en.js +72 -2
- package/lib/i18n/locales/es.js +74 -3
- package/lib/i18n/locales/fr.js +74 -3
- package/lib/i18n/locales/it.js +72 -2
- package/lib/i18n/locales/ja.js +74 -3
- package/lib/i18n/locales/ko.js +74 -3
- package/lib/i18n/locales/pt.js +72 -2
- package/lib/i18n/locales/ru.js +72 -2
- package/lib/i18n/locales/zh-TW.js +74 -3
- package/lib/i18n/locales/zh.js +74 -3
- package/lib/launcher.js +10 -0
- package/lib/presets/providers.js +86 -15
- package/lib/ui/menu.js +17 -5
- package/lib/utils/model-upgrade-checker.js +103 -0
- package/lib/utils/version-checker.js +22 -3
- package/package.json +2 -2
package/lib/ui/menu.js
CHANGED
|
@@ -60,8 +60,9 @@ class Menu {
|
|
|
60
60
|
* Display menu with current selection
|
|
61
61
|
* @param {boolean} clearScreen - Whether to clear screen before displaying (default: true)
|
|
62
62
|
* @param {string} versionInfo - Optional version info to display between banner and navigation
|
|
63
|
+
* @param {Function|null} hintCallback - Optional sync callback(selectedIndex) returning hint string or null
|
|
63
64
|
*/
|
|
64
|
-
displayMenu(clearScreen = true, versionInfo = null) {
|
|
65
|
+
displayMenu(clearScreen = true, versionInfo = null, hintCallback = null) {
|
|
65
66
|
// Clear screen and display header + menu together (like old version)
|
|
66
67
|
if (clearScreen) {
|
|
67
68
|
console.clear();
|
|
@@ -92,6 +93,15 @@ class Menu {
|
|
|
92
93
|
}
|
|
93
94
|
});
|
|
94
95
|
|
|
96
|
+
// Render dynamic hint if callback provided
|
|
97
|
+
if (hintCallback) {
|
|
98
|
+
const hintText = hintCallback(this.selectedIndex);
|
|
99
|
+
if (hintText) {
|
|
100
|
+
console.log('');
|
|
101
|
+
console.log(colors.green + ' \u2139 ' + hintText + colors.reset);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
95
105
|
console.log('');
|
|
96
106
|
}
|
|
97
107
|
|
|
@@ -107,8 +117,9 @@ class Menu {
|
|
|
107
117
|
* Handle keyboard navigation
|
|
108
118
|
* @param {boolean} clearScreen - Whether to clear screen on initial display (default: true)
|
|
109
119
|
* @param {string} versionInfo - Optional version info to display
|
|
120
|
+
* @param {Function|null} hintCallback - Optional sync callback(selectedIndex) returning hint string or null
|
|
110
121
|
*/
|
|
111
|
-
async navigate(clearScreen = true, versionInfo = null) {
|
|
122
|
+
async navigate(clearScreen = true, versionInfo = null, hintCallback = null) {
|
|
112
123
|
// Guard against empty menu to prevent NaN from modulo operations
|
|
113
124
|
if (!this.menuOptions || this.menuOptions.length === 0) {
|
|
114
125
|
console.log(colors.yellow + ' Warning: No menu options available' + colors.reset);
|
|
@@ -116,9 +127,10 @@ class Menu {
|
|
|
116
127
|
}
|
|
117
128
|
|
|
118
129
|
this.versionInfo = versionInfo; // Store for redrawing
|
|
130
|
+
this.hintCallback = hintCallback; // Store for redrawing
|
|
119
131
|
|
|
120
132
|
return new Promise((resolve, reject) => {
|
|
121
|
-
this.displayMenu(clearScreen, versionInfo);
|
|
133
|
+
this.displayMenu(clearScreen, versionInfo, hintCallback);
|
|
122
134
|
|
|
123
135
|
if (process.stdin.isTTY) {
|
|
124
136
|
const scope = stdinManager.acquire('raw', {
|
|
@@ -171,12 +183,12 @@ class Menu {
|
|
|
171
183
|
switch (key) {
|
|
172
184
|
case '\u001b[A': // Up arrow
|
|
173
185
|
this.selectedIndex = (this.selectedIndex - 1 + this.menuOptions.length) % this.menuOptions.length;
|
|
174
|
-
this.displayMenu(true, this.versionInfo);
|
|
186
|
+
this.displayMenu(true, this.versionInfo, this.hintCallback);
|
|
175
187
|
break;
|
|
176
188
|
|
|
177
189
|
case '\u001b[B': // Down arrow
|
|
178
190
|
this.selectedIndex = (this.selectedIndex + 1) % this.menuOptions.length;
|
|
179
|
-
this.displayMenu(true, this.versionInfo);
|
|
191
|
+
this.displayMenu(true, this.versionInfo, this.hintCallback);
|
|
180
192
|
break;
|
|
181
193
|
|
|
182
194
|
case '\r': // Enter
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model Upgrade Checker
|
|
3
|
+
* Detects available model upgrades with time-based caching (same pattern as version-checker)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { getLatestModel } = require('../presets/providers');
|
|
7
|
+
const versionChecker = require('./version-checker');
|
|
8
|
+
|
|
9
|
+
const CHECK_INTERVAL_HOURS = 12;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Check all APIs for available upgrades
|
|
13
|
+
* @param {ApiManager} apiManager - ApiManager instance (dependency injection)
|
|
14
|
+
* @returns {Array} Array of upgrade info objects
|
|
15
|
+
*/
|
|
16
|
+
function checkAllApiUpgrades(apiManager) {
|
|
17
|
+
const apis = apiManager.getApis();
|
|
18
|
+
const upgrades = [];
|
|
19
|
+
|
|
20
|
+
for (const api of apis) {
|
|
21
|
+
const latestModel = getLatestModel(api.model, api.provider);
|
|
22
|
+
if (latestModel) {
|
|
23
|
+
upgrades.push({
|
|
24
|
+
apiId: api.id,
|
|
25
|
+
apiName: api.name,
|
|
26
|
+
providerId: api.provider,
|
|
27
|
+
currentModel: api.model,
|
|
28
|
+
latestModel: latestModel
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return upgrades;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Check for model upgrades with caching
|
|
38
|
+
* @param {ApiManager} apiManager - ApiManager instance
|
|
39
|
+
* @param {boolean} force - Force check regardless of cache
|
|
40
|
+
* @returns {Promise<{upgrades: Array, needsCheck: boolean}>}
|
|
41
|
+
*/
|
|
42
|
+
async function checkForModelUpgrades(apiManager, force = false) {
|
|
43
|
+
const config = await versionChecker.loadConfig();
|
|
44
|
+
const now = Date.now();
|
|
45
|
+
const cacheDuration = CHECK_INTERVAL_HOURS * 60 * 60 * 1000;
|
|
46
|
+
|
|
47
|
+
const needsCheck = force ||
|
|
48
|
+
!config.lastModelUpgradeCheck ||
|
|
49
|
+
(now - config.lastModelUpgradeCheck > cacheDuration);
|
|
50
|
+
|
|
51
|
+
if (!needsCheck) {
|
|
52
|
+
return { upgrades: [], needsCheck: false };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const upgrades = checkAllApiUpgrades(apiManager);
|
|
56
|
+
|
|
57
|
+
// Update last check time
|
|
58
|
+
config.lastModelUpgradeCheck = now;
|
|
59
|
+
await versionChecker.saveConfig(config);
|
|
60
|
+
|
|
61
|
+
return { upgrades, needsCheck: true };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get auto upgrade setting
|
|
66
|
+
* @returns {Promise<boolean>} Whether auto upgrade is enabled
|
|
67
|
+
*/
|
|
68
|
+
async function isAutoUpgradeEnabled() {
|
|
69
|
+
const config = await versionChecker.loadConfig();
|
|
70
|
+
return config.autoModelUpgrade === true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Perform auto upgrade for all APIs with available upgrades
|
|
75
|
+
* @param {ApiManager} apiManager - ApiManager instance
|
|
76
|
+
* @param {Array} upgrades - Array of upgrade info from checkForModelUpgrades
|
|
77
|
+
* @returns {Array} Array of upgraded API info
|
|
78
|
+
*/
|
|
79
|
+
function performAutoUpgrade(apiManager, upgrades) {
|
|
80
|
+
const upgraded = [];
|
|
81
|
+
|
|
82
|
+
for (const upgrade of upgrades) {
|
|
83
|
+
try {
|
|
84
|
+
apiManager.updateApiModel(upgrade.apiId, upgrade.latestModel);
|
|
85
|
+
upgraded.push({
|
|
86
|
+
apiName: upgrade.apiName,
|
|
87
|
+
from: upgrade.currentModel,
|
|
88
|
+
to: upgrade.latestModel
|
|
89
|
+
});
|
|
90
|
+
} catch (error) {
|
|
91
|
+
// Skip failed upgrades silently
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return upgraded;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = {
|
|
99
|
+
checkAllApiUpgrades,
|
|
100
|
+
checkForModelUpgrades,
|
|
101
|
+
isAutoUpgradeEnabled,
|
|
102
|
+
performAutoUpgrade
|
|
103
|
+
};
|
|
@@ -22,13 +22,21 @@ async function loadConfig() {
|
|
|
22
22
|
try {
|
|
23
23
|
const configPath = getConfigPath();
|
|
24
24
|
const data = await fs.readFile(configPath, 'utf8');
|
|
25
|
-
|
|
25
|
+
const config = JSON.parse(data);
|
|
26
|
+
|
|
27
|
+
// Add new fields for backward compatibility
|
|
28
|
+
if (config.autoModelUpgrade === undefined) config.autoModelUpgrade = false;
|
|
29
|
+
if (config.lastModelUpgradeCheck === undefined) config.lastModelUpgradeCheck = 0;
|
|
30
|
+
|
|
31
|
+
return config;
|
|
26
32
|
} catch (error) {
|
|
27
33
|
// Return default config if file doesn't exist
|
|
28
34
|
return {
|
|
29
35
|
language: 'zh',
|
|
30
36
|
lastVersionCheck: 0,
|
|
31
|
-
cachedLatestVersion: null
|
|
37
|
+
cachedLatestVersion: null,
|
|
38
|
+
autoModelUpgrade: false,
|
|
39
|
+
lastModelUpgradeCheck: 0
|
|
32
40
|
};
|
|
33
41
|
}
|
|
34
42
|
}
|
|
@@ -231,10 +239,21 @@ async function forceCheckForUpdates(timeoutMs = 15000) {
|
|
|
231
239
|
}
|
|
232
240
|
}
|
|
233
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Set auto model upgrade setting
|
|
244
|
+
* @param {boolean} enabled - Whether to enable auto model upgrade
|
|
245
|
+
*/
|
|
246
|
+
async function setAutoModelUpgrade(enabled) {
|
|
247
|
+
const config = await loadConfig();
|
|
248
|
+
config.autoModelUpgrade = enabled;
|
|
249
|
+
await saveConfig(config);
|
|
250
|
+
}
|
|
251
|
+
|
|
234
252
|
module.exports = {
|
|
235
253
|
checkForUpdates,
|
|
236
254
|
forceCheckForUpdates,
|
|
237
255
|
clearCache,
|
|
238
256
|
loadConfig,
|
|
239
|
-
saveConfig
|
|
257
|
+
saveConfig,
|
|
258
|
+
setAutoModelUpgrade
|
|
240
259
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kikkimo/claude-launcher",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "Interactive launcher for Claude Code with beautiful Claude-style interface",
|
|
5
5
|
"main": "claude-launcher",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "node claude-launcher",
|
|
11
|
-
"test": "
|
|
11
|
+
"test": "node test/providers.test.js && node test/menu-hints.test.js",
|
|
12
12
|
"prepublishOnly": "echo \"Publishing claude-launcher...\"",
|
|
13
13
|
"postinstall": "echo \"Claude Launcher installed successfully! Run 'claude-launcher' to start.\"",
|
|
14
14
|
"publish:public": "npm publish --access public"
|