claudeup 3.17.0 → 4.0.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/package.json +1 -1
- package/src/ui/adapters/pluginsAdapter.js +139 -0
- package/src/ui/adapters/pluginsAdapter.ts +202 -0
- package/src/ui/adapters/settingsAdapter.js +111 -0
- package/src/ui/adapters/settingsAdapter.ts +165 -0
- package/src/ui/components/ScrollableList.js +4 -4
- package/src/ui/components/ScrollableList.tsx +4 -4
- package/src/ui/components/SearchInput.js +2 -2
- package/src/ui/components/SearchInput.tsx +3 -3
- package/src/ui/components/StyledText.js +1 -1
- package/src/ui/components/StyledText.tsx +5 -1
- package/src/ui/components/layout/ProgressBar.js +1 -1
- package/src/ui/components/layout/ProgressBar.tsx +1 -5
- package/src/ui/components/modals/InputModal.tsx +1 -6
- package/src/ui/components/modals/LoadingModal.js +1 -1
- package/src/ui/components/modals/LoadingModal.tsx +1 -3
- package/src/ui/hooks/index.js +3 -3
- package/src/ui/hooks/index.ts +3 -3
- package/src/ui/hooks/useKeyboard.ts +1 -3
- package/src/ui/hooks/useKeyboardHandler.js +9 -9
- package/src/ui/hooks/useKeyboardHandler.ts +9 -9
- package/src/ui/renderers/cliToolRenderers.js +33 -0
- package/src/ui/renderers/cliToolRenderers.tsx +153 -0
- package/src/ui/renderers/mcpRenderers.js +26 -0
- package/src/ui/renderers/mcpRenderers.tsx +145 -0
- package/src/ui/renderers/pluginRenderers.js +124 -0
- package/src/ui/renderers/pluginRenderers.tsx +362 -0
- package/src/ui/renderers/profileRenderers.js +172 -0
- package/src/ui/renderers/profileRenderers.tsx +410 -0
- package/src/ui/renderers/settingsRenderers.js +69 -0
- package/src/ui/renderers/settingsRenderers.tsx +205 -0
- package/src/ui/screens/CliToolsScreen.js +14 -58
- package/src/ui/screens/CliToolsScreen.tsx +36 -196
- package/src/ui/screens/EnvVarsScreen.js +12 -168
- package/src/ui/screens/EnvVarsScreen.tsx +16 -327
- package/src/ui/screens/McpScreen.js +12 -62
- package/src/ui/screens/McpScreen.tsx +21 -190
- package/src/ui/screens/PluginsScreen.js +52 -425
- package/src/ui/screens/PluginsScreen.tsx +70 -758
- package/src/ui/screens/ProfilesScreen.js +32 -97
- package/src/ui/screens/ProfilesScreen.tsx +58 -328
- package/src/ui/screens/SkillsScreen.js +16 -16
- package/src/ui/screens/SkillsScreen.tsx +20 -23
|
@@ -8,7 +8,9 @@ import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
|
8
8
|
import { ScreenLayout } from "../components/layout/index.js";
|
|
9
9
|
import { ScrollableList } from "../components/ScrollableList.js";
|
|
10
10
|
import { cliTools } from "../../data/cli-tools.js";
|
|
11
|
+
import { renderCliToolRow, renderCliToolDetail, } from "../renderers/cliToolRenderers.js";
|
|
11
12
|
const execAsync = promisify(exec);
|
|
13
|
+
// ─── Version helpers ───────────────────────────────────────────────────────────
|
|
12
14
|
// Session-level cache
|
|
13
15
|
let cachedToolStatuses = null;
|
|
14
16
|
let cacheInitialized = false;
|
|
@@ -53,10 +55,7 @@ async function getLatestNpmVersion(packageName) {
|
|
|
53
55
|
}
|
|
54
56
|
async function getLatestPipVersion(packageName) {
|
|
55
57
|
try {
|
|
56
|
-
const { stdout } = await execAsync(`pip index versions ${packageName} 2>/dev/null | head -1`, {
|
|
57
|
-
timeout: 10000,
|
|
58
|
-
shell: "/bin/bash",
|
|
59
|
-
});
|
|
58
|
+
const { stdout } = await execAsync(`pip index versions ${packageName} 2>/dev/null | head -1`, { timeout: 10000, shell: "/bin/bash" });
|
|
60
59
|
const match = stdout.trim().match(/\(([^)]+)\)/);
|
|
61
60
|
return match ? match[1] : undefined;
|
|
62
61
|
}
|
|
@@ -65,12 +64,9 @@ async function getLatestPipVersion(packageName) {
|
|
|
65
64
|
}
|
|
66
65
|
}
|
|
67
66
|
async function getLatestVersion(tool) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
else {
|
|
72
|
-
return getLatestPipVersion(tool.packageName);
|
|
73
|
-
}
|
|
67
|
+
return tool.packageManager === "npm"
|
|
68
|
+
? getLatestNpmVersion(tool.packageName)
|
|
69
|
+
: getLatestPipVersion(tool.packageName);
|
|
74
70
|
}
|
|
75
71
|
function compareVersions(v1, v2) {
|
|
76
72
|
const parts1 = v1.split(/[-.]/).map((p) => parseInt(p, 10) || 0);
|
|
@@ -85,6 +81,7 @@ function compareVersions(v1, v2) {
|
|
|
85
81
|
}
|
|
86
82
|
return 0;
|
|
87
83
|
}
|
|
84
|
+
// ─── Component ─────────────────────────────────────────────────────────────────
|
|
88
85
|
export function CliToolsScreen() {
|
|
89
86
|
const { state, dispatch } = useApp();
|
|
90
87
|
const { cliTools: cliToolsState } = state;
|
|
@@ -101,7 +98,6 @@ export function CliToolsScreen() {
|
|
|
101
98
|
});
|
|
102
99
|
const statusesRef = useRef(toolStatuses);
|
|
103
100
|
statusesRef.current = toolStatuses;
|
|
104
|
-
// Update single tool status
|
|
105
101
|
const updateToolStatus = useCallback((index, updates) => {
|
|
106
102
|
setToolStatuses((prev) => {
|
|
107
103
|
const newStatuses = [...prev];
|
|
@@ -110,9 +106,7 @@ export function CliToolsScreen() {
|
|
|
110
106
|
return newStatuses;
|
|
111
107
|
});
|
|
112
108
|
}, []);
|
|
113
|
-
// Fetch all version info
|
|
114
109
|
const fetchVersionInfo = useCallback(async () => {
|
|
115
|
-
// Check installed versions
|
|
116
110
|
for (let i = 0; i < cliTools.length; i++) {
|
|
117
111
|
const tool = cliTools[i];
|
|
118
112
|
getInstalledVersion(tool).then((version) => {
|
|
@@ -140,7 +134,6 @@ export function CliToolsScreen() {
|
|
|
140
134
|
fetchVersionInfo();
|
|
141
135
|
}
|
|
142
136
|
}, [fetchVersionInfo, toolStatuses]);
|
|
143
|
-
// Keyboard handling
|
|
144
137
|
useKeyboard((event) => {
|
|
145
138
|
if (state.isSearching || state.modal)
|
|
146
139
|
return;
|
|
@@ -178,24 +171,15 @@ export function CliToolsScreen() {
|
|
|
178
171
|
if (!status)
|
|
179
172
|
return;
|
|
180
173
|
const { tool, installed, hasUpdate } = status;
|
|
181
|
-
const action = !installed
|
|
182
|
-
? "Installing"
|
|
183
|
-
: hasUpdate
|
|
184
|
-
? "Updating"
|
|
185
|
-
: "Reinstalling";
|
|
174
|
+
const action = !installed ? "Installing" : hasUpdate ? "Updating" : "Reinstalling";
|
|
186
175
|
const viaHomebrew = installed
|
|
187
176
|
? await isInstalledViaHomebrew(tool.name)
|
|
188
177
|
: false;
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
command = `brew upgrade ${tool.name}`;
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
command = tool.installCommand;
|
|
198
|
-
}
|
|
178
|
+
const command = !installed
|
|
179
|
+
? tool.installCommand
|
|
180
|
+
: viaHomebrew
|
|
181
|
+
? `brew upgrade ${tool.name}`
|
|
182
|
+
: tool.installCommand;
|
|
199
183
|
modal.loading(`${action} ${tool.displayName}...`);
|
|
200
184
|
try {
|
|
201
185
|
execSync(command, {
|
|
@@ -237,38 +221,10 @@ export function CliToolsScreen() {
|
|
|
237
221
|
modal.hideModal();
|
|
238
222
|
handleRefresh();
|
|
239
223
|
};
|
|
240
|
-
// Get selected item
|
|
241
224
|
const selectedStatus = toolStatuses[cliToolsState.selectedIndex];
|
|
242
|
-
const renderDetail = () => {
|
|
243
|
-
if (!selectedStatus) {
|
|
244
|
-
return (_jsx("box", { flexDirection: "column", alignItems: "center", justifyContent: "center", flexGrow: 1, children: _jsx("text", { fg: "gray", children: "Select a tool to see details" }) }));
|
|
245
|
-
}
|
|
246
|
-
const { tool, installed, installedVersion, latestVersion, hasUpdate, checking, } = selectedStatus;
|
|
247
|
-
return (_jsxs("box", { flexDirection: "column", children: [_jsxs("box", { marginBottom: 1, children: [_jsx("text", { fg: "cyan", children: _jsxs("strong", { children: ["\u2699 ", tool.displayName] }) }), hasUpdate && _jsx("text", { fg: "yellow", children: " \u2B06" })] }), _jsx("text", { fg: "gray", children: tool.description }), _jsxs("box", { marginTop: 1, flexDirection: "column", children: [_jsxs("box", { children: [_jsx("text", { fg: "gray", children: "Status " }), !installed ? (_jsx("text", { fg: "gray", children: "\u25CB Not installed" })) : checking ? (_jsx("text", { fg: "green", children: "\u25CF Checking..." })) : hasUpdate ? (_jsx("text", { fg: "yellow", children: "\u25CF Update available" })) : (_jsx("text", { fg: "green", children: "\u25CF Up to date" }))] }), installedVersion && (_jsxs("box", { children: [_jsx("text", { fg: "gray", children: "Installed " }), _jsxs("text", { fg: "green", children: ["v", installedVersion] })] })), latestVersion && (_jsxs("box", { children: [_jsx("text", { fg: "gray", children: "Latest " }), _jsxs("text", { fg: "white", children: ["v", latestVersion] })] })), _jsxs("box", { children: [_jsx("text", { fg: "gray", children: "Website " }), _jsx("text", { fg: "#5c9aff", children: tool.website })] })] }), _jsx("box", { marginTop: 2, children: !installed ? (_jsxs("box", { children: [_jsxs("text", { bg: "green", fg: "black", children: [" ", "Enter", " "] }), _jsx("text", { fg: "gray", children: " Install" })] })) : hasUpdate ? (_jsxs("box", { children: [_jsxs("text", { bg: "yellow", fg: "black", children: [" ", "Enter", " "] }), _jsxs("text", { fg: "gray", children: [" Update to v", latestVersion] })] })) : (_jsxs("box", { children: [_jsxs("text", { bg: "gray", fg: "white", children: [" ", "Enter", " "] }), _jsx("text", { fg: "gray", children: " Reinstall" })] })) })] }));
|
|
248
|
-
};
|
|
249
|
-
const renderListItem = (status, _idx, isSelected) => {
|
|
250
|
-
const { tool, installed, installedVersion, hasUpdate, checking } = status;
|
|
251
|
-
let icon;
|
|
252
|
-
let iconColor;
|
|
253
|
-
if (!installed) {
|
|
254
|
-
icon = "○";
|
|
255
|
-
iconColor = "gray";
|
|
256
|
-
}
|
|
257
|
-
else if (hasUpdate) {
|
|
258
|
-
icon = "⬆";
|
|
259
|
-
iconColor = "yellow";
|
|
260
|
-
}
|
|
261
|
-
else {
|
|
262
|
-
icon = "●";
|
|
263
|
-
iconColor = "green";
|
|
264
|
-
}
|
|
265
|
-
const versionText = installedVersion ? `v${installedVersion}` : "";
|
|
266
|
-
return isSelected ? (_jsxs("text", { bg: "magenta", fg: "white", children: [" ", icon, " ", tool.displayName, " ", versionText, checking ? "..." : "", " "] })) : (_jsxs("text", { children: [_jsx("span", { fg: iconColor, children: icon }), _jsxs("span", { fg: "white", children: [" ", tool.displayName] }), versionText && _jsxs("span", { fg: "green", children: [" ", versionText] }), checking && _jsx("span", { fg: "gray", children: "..." })] }));
|
|
267
|
-
};
|
|
268
|
-
// Calculate stats for status line
|
|
269
225
|
const installedCount = toolStatuses.filter((s) => s.installed).length;
|
|
270
226
|
const updateCount = toolStatuses.filter((s) => s.hasUpdate).length;
|
|
271
227
|
const statusContent = (_jsxs("text", { children: [_jsx("span", { fg: "gray", children: "Installed: " }), _jsxs("span", { fg: "cyan", children: [installedCount, "/", toolStatuses.length] }), updateCount > 0 && (_jsxs(_Fragment, { children: [_jsx("span", { fg: "gray", children: " \u2502 Updates: " }), _jsx("span", { fg: "yellow", children: updateCount })] }))] }));
|
|
272
|
-
return (_jsx(ScreenLayout, { title: "claudeup CLI Tools", currentScreen: "cli-tools", statusLine: statusContent, footerHints: "\u2191\u2193:nav \u2502 Enter:install \u2502 a:update all \u2502 r:refresh", listPanel: _jsx(ScrollableList, { items: toolStatuses, selectedIndex: cliToolsState.selectedIndex, renderItem:
|
|
228
|
+
return (_jsx(ScreenLayout, { title: "claudeup CLI Tools", currentScreen: "cli-tools", statusLine: statusContent, footerHints: "\u2191\u2193:nav \u2502 Enter:install \u2502 a:update all \u2502 r:refresh", listPanel: _jsx(ScrollableList, { items: toolStatuses, selectedIndex: cliToolsState.selectedIndex, renderItem: renderCliToolRow, maxHeight: dimensions.listPanelHeight }), detailPanel: renderCliToolDetail(selectedStatus) }));
|
|
273
229
|
}
|
|
274
230
|
export default CliToolsScreen;
|
|
@@ -6,21 +6,19 @@ import { useDimensions } from "../state/DimensionsContext.js";
|
|
|
6
6
|
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
7
7
|
import { ScreenLayout } from "../components/layout/index.js";
|
|
8
8
|
import { ScrollableList } from "../components/ScrollableList.js";
|
|
9
|
-
import { cliTools
|
|
9
|
+
import { cliTools } from "../../data/cli-tools.js";
|
|
10
|
+
import {
|
|
11
|
+
renderCliToolRow,
|
|
12
|
+
renderCliToolDetail,
|
|
13
|
+
type CliToolStatus,
|
|
14
|
+
} from "../renderers/cliToolRenderers.js";
|
|
10
15
|
|
|
11
16
|
const execAsync = promisify(exec);
|
|
12
17
|
|
|
13
|
-
|
|
14
|
-
tool: CliTool;
|
|
15
|
-
installed: boolean;
|
|
16
|
-
installedVersion?: string;
|
|
17
|
-
latestVersion?: string;
|
|
18
|
-
hasUpdate?: boolean;
|
|
19
|
-
checking: boolean;
|
|
20
|
-
}
|
|
18
|
+
// ─── Version helpers ───────────────────────────────────────────────────────────
|
|
21
19
|
|
|
22
20
|
// Session-level cache
|
|
23
|
-
let cachedToolStatuses:
|
|
21
|
+
let cachedToolStatuses: CliToolStatus[] | null = null;
|
|
24
22
|
let cacheInitialized = false;
|
|
25
23
|
|
|
26
24
|
function clearCliToolsCache(): void {
|
|
@@ -46,7 +44,9 @@ async function isInstalledViaHomebrew(toolName: string): Promise<boolean> {
|
|
|
46
44
|
}
|
|
47
45
|
}
|
|
48
46
|
|
|
49
|
-
async function getInstalledVersion(
|
|
47
|
+
async function getInstalledVersion(
|
|
48
|
+
tool: import("../../data/cli-tools.js").CliTool,
|
|
49
|
+
): Promise<string | undefined> {
|
|
50
50
|
try {
|
|
51
51
|
const { stdout } = await execAsync(tool.checkCommand, { timeout: 5000 });
|
|
52
52
|
return parseVersion(stdout.trim());
|
|
@@ -55,9 +55,7 @@ async function getInstalledVersion(tool: CliTool): Promise<string | undefined> {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
async function getLatestNpmVersion(
|
|
59
|
-
packageName: string,
|
|
60
|
-
): Promise<string | undefined> {
|
|
58
|
+
async function getLatestNpmVersion(packageName: string): Promise<string | undefined> {
|
|
61
59
|
try {
|
|
62
60
|
const { stdout } = await execAsync(
|
|
63
61
|
`npm view ${packageName} version 2>/dev/null`,
|
|
@@ -69,16 +67,11 @@ async function getLatestNpmVersion(
|
|
|
69
67
|
}
|
|
70
68
|
}
|
|
71
69
|
|
|
72
|
-
async function getLatestPipVersion(
|
|
73
|
-
packageName: string,
|
|
74
|
-
): Promise<string | undefined> {
|
|
70
|
+
async function getLatestPipVersion(packageName: string): Promise<string | undefined> {
|
|
75
71
|
try {
|
|
76
72
|
const { stdout } = await execAsync(
|
|
77
73
|
`pip index versions ${packageName} 2>/dev/null | head -1`,
|
|
78
|
-
{
|
|
79
|
-
timeout: 10000,
|
|
80
|
-
shell: "/bin/bash",
|
|
81
|
-
},
|
|
74
|
+
{ timeout: 10000, shell: "/bin/bash" },
|
|
82
75
|
);
|
|
83
76
|
const match = stdout.trim().match(/\(([^)]+)\)/);
|
|
84
77
|
return match ? match[1] : undefined;
|
|
@@ -87,18 +80,17 @@ async function getLatestPipVersion(
|
|
|
87
80
|
}
|
|
88
81
|
}
|
|
89
82
|
|
|
90
|
-
async function getLatestVersion(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
83
|
+
async function getLatestVersion(
|
|
84
|
+
tool: import("../../data/cli-tools.js").CliTool,
|
|
85
|
+
): Promise<string | undefined> {
|
|
86
|
+
return tool.packageManager === "npm"
|
|
87
|
+
? getLatestNpmVersion(tool.packageName)
|
|
88
|
+
: getLatestPipVersion(tool.packageName);
|
|
96
89
|
}
|
|
97
90
|
|
|
98
91
|
function compareVersions(v1: string, v2: string): number {
|
|
99
92
|
const parts1 = v1.split(/[-.]/).map((p) => parseInt(p, 10) || 0);
|
|
100
93
|
const parts2 = v2.split(/[-.]/).map((p) => parseInt(p, 10) || 0);
|
|
101
|
-
|
|
102
94
|
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
103
95
|
const p1 = parts1[i] || 0;
|
|
104
96
|
const p2 = parts2[i] || 0;
|
|
@@ -108,13 +100,15 @@ function compareVersions(v1: string, v2: string): number {
|
|
|
108
100
|
return 0;
|
|
109
101
|
}
|
|
110
102
|
|
|
103
|
+
// ─── Component ─────────────────────────────────────────────────────────────────
|
|
104
|
+
|
|
111
105
|
export function CliToolsScreen() {
|
|
112
106
|
const { state, dispatch } = useApp();
|
|
113
107
|
const { cliTools: cliToolsState } = state;
|
|
114
108
|
const modal = useModal();
|
|
115
109
|
const dimensions = useDimensions();
|
|
116
110
|
|
|
117
|
-
const [toolStatuses, setToolStatuses] = useState<
|
|
111
|
+
const [toolStatuses, setToolStatuses] = useState<CliToolStatus[]>(() => {
|
|
118
112
|
return (
|
|
119
113
|
cachedToolStatuses ||
|
|
120
114
|
cliTools.map((tool) => ({
|
|
@@ -129,12 +123,11 @@ export function CliToolsScreen() {
|
|
|
129
123
|
const statusesRef = useRef(toolStatuses);
|
|
130
124
|
statusesRef.current = toolStatuses;
|
|
131
125
|
|
|
132
|
-
// Update single tool status
|
|
133
126
|
const updateToolStatus = useCallback(
|
|
134
|
-
(index: number, updates: Partial<
|
|
127
|
+
(index: number, updates: Partial<CliToolStatus>) => {
|
|
135
128
|
setToolStatuses((prev) => {
|
|
136
129
|
const newStatuses = [...prev];
|
|
137
|
-
newStatuses[index] = { ...newStatuses[index]
|
|
130
|
+
newStatuses[index] = { ...newStatuses[index]!, ...updates };
|
|
138
131
|
cachedToolStatuses = newStatuses;
|
|
139
132
|
return newStatuses;
|
|
140
133
|
});
|
|
@@ -142,11 +135,9 @@ export function CliToolsScreen() {
|
|
|
142
135
|
[],
|
|
143
136
|
);
|
|
144
137
|
|
|
145
|
-
// Fetch all version info
|
|
146
138
|
const fetchVersionInfo = useCallback(async () => {
|
|
147
|
-
// Check installed versions
|
|
148
139
|
for (let i = 0; i < cliTools.length; i++) {
|
|
149
|
-
const tool = cliTools[i]
|
|
140
|
+
const tool = cliTools[i]!;
|
|
150
141
|
getInstalledVersion(tool).then((version) => {
|
|
151
142
|
updateToolStatus(i, {
|
|
152
143
|
installedVersion: version,
|
|
@@ -155,7 +146,7 @@ export function CliToolsScreen() {
|
|
|
155
146
|
});
|
|
156
147
|
|
|
157
148
|
getLatestVersion(tool).then((latest) => {
|
|
158
|
-
const current = statusesRef.current[i]
|
|
149
|
+
const current = statusesRef.current[i]!;
|
|
159
150
|
updateToolStatus(i, {
|
|
160
151
|
latestVersion: latest,
|
|
161
152
|
checking: false,
|
|
@@ -176,7 +167,6 @@ export function CliToolsScreen() {
|
|
|
176
167
|
}
|
|
177
168
|
}, [fetchVersionInfo, toolStatuses]);
|
|
178
169
|
|
|
179
|
-
// Keyboard handling
|
|
180
170
|
useKeyboard((event) => {
|
|
181
171
|
if (state.isSearching || state.modal) return;
|
|
182
172
|
|
|
@@ -217,27 +207,19 @@ export function CliToolsScreen() {
|
|
|
217
207
|
if (!status) return;
|
|
218
208
|
|
|
219
209
|
const { tool, installed, hasUpdate } = status;
|
|
220
|
-
const action = !installed
|
|
221
|
-
? "Installing"
|
|
222
|
-
: hasUpdate
|
|
223
|
-
? "Updating"
|
|
224
|
-
: "Reinstalling";
|
|
210
|
+
const action = !installed ? "Installing" : hasUpdate ? "Updating" : "Reinstalling";
|
|
225
211
|
|
|
226
212
|
const viaHomebrew = installed
|
|
227
213
|
? await isInstalledViaHomebrew(tool.name)
|
|
228
214
|
: false;
|
|
229
215
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
} else {
|
|
236
|
-
command = tool.installCommand;
|
|
237
|
-
}
|
|
216
|
+
const command = !installed
|
|
217
|
+
? tool.installCommand
|
|
218
|
+
: viaHomebrew
|
|
219
|
+
? `brew upgrade ${tool.name}`
|
|
220
|
+
: tool.installCommand;
|
|
238
221
|
|
|
239
222
|
modal.loading(`${action} ${tool.displayName}...`);
|
|
240
|
-
|
|
241
223
|
try {
|
|
242
224
|
execSync(command, {
|
|
243
225
|
encoding: "utf-8",
|
|
@@ -259,11 +241,7 @@ export function CliToolsScreen() {
|
|
|
259
241
|
const handleUpdateAll = async () => {
|
|
260
242
|
const updatable = toolStatuses.filter((s) => s.hasUpdate);
|
|
261
243
|
if (updatable.length === 0) {
|
|
262
|
-
await modal.message(
|
|
263
|
-
"Up to Date",
|
|
264
|
-
"All tools are already up to date.",
|
|
265
|
-
"info",
|
|
266
|
-
);
|
|
244
|
+
await modal.message("Up to Date", "All tools are already up to date.", "info");
|
|
267
245
|
return;
|
|
268
246
|
}
|
|
269
247
|
|
|
@@ -274,7 +252,6 @@ export function CliToolsScreen() {
|
|
|
274
252
|
const command = viaHomebrew
|
|
275
253
|
? `brew upgrade ${status.tool.name}`
|
|
276
254
|
: status.tool.installCommand;
|
|
277
|
-
|
|
278
255
|
try {
|
|
279
256
|
execSync(command, {
|
|
280
257
|
encoding: "utf-8",
|
|
@@ -290,145 +267,8 @@ export function CliToolsScreen() {
|
|
|
290
267
|
handleRefresh();
|
|
291
268
|
};
|
|
292
269
|
|
|
293
|
-
// Get selected item
|
|
294
270
|
const selectedStatus = toolStatuses[cliToolsState.selectedIndex];
|
|
295
271
|
|
|
296
|
-
const renderDetail = () => {
|
|
297
|
-
if (!selectedStatus) {
|
|
298
|
-
return (
|
|
299
|
-
<box
|
|
300
|
-
flexDirection="column"
|
|
301
|
-
alignItems="center"
|
|
302
|
-
justifyContent="center"
|
|
303
|
-
flexGrow={1}
|
|
304
|
-
>
|
|
305
|
-
<text fg="gray">Select a tool to see details</text>
|
|
306
|
-
</box>
|
|
307
|
-
);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const {
|
|
311
|
-
tool,
|
|
312
|
-
installed,
|
|
313
|
-
installedVersion,
|
|
314
|
-
latestVersion,
|
|
315
|
-
hasUpdate,
|
|
316
|
-
checking,
|
|
317
|
-
} = selectedStatus;
|
|
318
|
-
|
|
319
|
-
return (
|
|
320
|
-
<box flexDirection="column">
|
|
321
|
-
<box marginBottom={1}>
|
|
322
|
-
<text fg="cyan">
|
|
323
|
-
<strong>⚙ {tool.displayName}</strong>
|
|
324
|
-
</text>
|
|
325
|
-
{hasUpdate && <text fg="yellow"> ⬆</text>}
|
|
326
|
-
</box>
|
|
327
|
-
|
|
328
|
-
<text fg="gray">{tool.description}</text>
|
|
329
|
-
|
|
330
|
-
<box marginTop={1} flexDirection="column">
|
|
331
|
-
<box>
|
|
332
|
-
<text fg="gray">Status </text>
|
|
333
|
-
{!installed ? (
|
|
334
|
-
<text fg="gray">○ Not installed</text>
|
|
335
|
-
) : checking ? (
|
|
336
|
-
<text fg="green">● Checking...</text>
|
|
337
|
-
) : hasUpdate ? (
|
|
338
|
-
<text fg="yellow">● Update available</text>
|
|
339
|
-
) : (
|
|
340
|
-
<text fg="green">● Up to date</text>
|
|
341
|
-
)}
|
|
342
|
-
</box>
|
|
343
|
-
{installedVersion && (
|
|
344
|
-
<box>
|
|
345
|
-
<text fg="gray">Installed </text>
|
|
346
|
-
<text fg="green">v{installedVersion}</text>
|
|
347
|
-
</box>
|
|
348
|
-
)}
|
|
349
|
-
{latestVersion && (
|
|
350
|
-
<box>
|
|
351
|
-
<text fg="gray">Latest </text>
|
|
352
|
-
<text fg="white">v{latestVersion}</text>
|
|
353
|
-
</box>
|
|
354
|
-
)}
|
|
355
|
-
<box>
|
|
356
|
-
<text fg="gray">Website </text>
|
|
357
|
-
<text fg="#5c9aff">{tool.website}</text>
|
|
358
|
-
</box>
|
|
359
|
-
</box>
|
|
360
|
-
|
|
361
|
-
<box marginTop={2}>
|
|
362
|
-
{!installed ? (
|
|
363
|
-
<box>
|
|
364
|
-
<text bg="green" fg="black">
|
|
365
|
-
{" "}
|
|
366
|
-
Enter{" "}
|
|
367
|
-
</text>
|
|
368
|
-
<text fg="gray"> Install</text>
|
|
369
|
-
</box>
|
|
370
|
-
) : hasUpdate ? (
|
|
371
|
-
<box>
|
|
372
|
-
<text bg="yellow" fg="black">
|
|
373
|
-
{" "}
|
|
374
|
-
Enter{" "}
|
|
375
|
-
</text>
|
|
376
|
-
<text fg="gray"> Update to v{latestVersion}</text>
|
|
377
|
-
</box>
|
|
378
|
-
) : (
|
|
379
|
-
<box>
|
|
380
|
-
<text bg="gray" fg="white">
|
|
381
|
-
{" "}
|
|
382
|
-
Enter{" "}
|
|
383
|
-
</text>
|
|
384
|
-
<text fg="gray"> Reinstall</text>
|
|
385
|
-
</box>
|
|
386
|
-
)}
|
|
387
|
-
</box>
|
|
388
|
-
</box>
|
|
389
|
-
);
|
|
390
|
-
};
|
|
391
|
-
|
|
392
|
-
const renderListItem = (
|
|
393
|
-
status: ToolStatus,
|
|
394
|
-
_idx: number,
|
|
395
|
-
isSelected: boolean,
|
|
396
|
-
) => {
|
|
397
|
-
const { tool, installed, installedVersion, hasUpdate, checking } = status;
|
|
398
|
-
|
|
399
|
-
let icon: string;
|
|
400
|
-
let iconColor: string;
|
|
401
|
-
|
|
402
|
-
if (!installed) {
|
|
403
|
-
icon = "○";
|
|
404
|
-
iconColor = "gray";
|
|
405
|
-
} else if (hasUpdate) {
|
|
406
|
-
icon = "⬆";
|
|
407
|
-
iconColor = "yellow";
|
|
408
|
-
} else {
|
|
409
|
-
icon = "●";
|
|
410
|
-
iconColor = "green";
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
const versionText = installedVersion ? `v${installedVersion}` : "";
|
|
414
|
-
|
|
415
|
-
return isSelected ? (
|
|
416
|
-
<text bg="magenta" fg="white">
|
|
417
|
-
{" "}
|
|
418
|
-
{icon} {tool.displayName} {versionText}
|
|
419
|
-
{checking ? "..." : ""}{" "}
|
|
420
|
-
</text>
|
|
421
|
-
) : (
|
|
422
|
-
<text>
|
|
423
|
-
<span fg={iconColor}>{icon}</span>
|
|
424
|
-
<span fg="white"> {tool.displayName}</span>
|
|
425
|
-
{versionText && <span fg="green"> {versionText}</span>}
|
|
426
|
-
{checking && <span fg="gray">...</span>}
|
|
427
|
-
</text>
|
|
428
|
-
);
|
|
429
|
-
};
|
|
430
|
-
|
|
431
|
-
// Calculate stats for status line
|
|
432
272
|
const installedCount = toolStatuses.filter((s) => s.installed).length;
|
|
433
273
|
const updateCount = toolStatuses.filter((s) => s.hasUpdate).length;
|
|
434
274
|
const statusContent = (
|
|
@@ -456,11 +296,11 @@ export function CliToolsScreen() {
|
|
|
456
296
|
<ScrollableList
|
|
457
297
|
items={toolStatuses}
|
|
458
298
|
selectedIndex={cliToolsState.selectedIndex}
|
|
459
|
-
renderItem={
|
|
299
|
+
renderItem={renderCliToolRow}
|
|
460
300
|
maxHeight={dimensions.listPanelHeight}
|
|
461
301
|
/>
|
|
462
302
|
}
|
|
463
|
-
detailPanel={
|
|
303
|
+
detailPanel={renderCliToolDetail(selectedStatus)}
|
|
464
304
|
/>
|
|
465
305
|
);
|
|
466
306
|
}
|