claudeup 4.19.0 → 4.22.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/bin/claudeup.js +43 -19
- package/package.json +14 -19
- package/scripts/build-binaries.ts +77 -0
- package/scripts/check-optional-deps.ts +49 -0
- package/scripts/test-isolated.ts +43 -0
- package/src/__tests__/cli-router.test.ts +65 -0
- package/src/__tests__/conventions-integration.test.ts +745 -0
- package/src/__tests__/conventions-manager.test.ts +1172 -0
- package/src/__tests__/doctor-bins.test.ts +35 -0
- package/src/__tests__/doctor.test.ts +450 -0
- package/src/__tests__/dual-write-prevention.test.ts +14 -4
- package/src/__tests__/file-locking.test.ts +208 -0
- package/src/__tests__/gitignore-prerun.test.ts +1 -0
- package/src/__tests__/install-command.test.ts +81 -0
- package/src/__tests__/install-plan.test.ts +98 -0
- package/src/__tests__/manifest.test.ts +136 -0
- package/src/__tests__/marketplace-refresh.test.ts +267 -0
- package/src/__tests__/plugin-requires.test.ts +133 -0
- package/src/__tests__/plugin-setup.test.ts +7 -0
- package/src/__tests__/profile-command.test.ts +116 -0
- package/src/__tests__/profile-materializer.test.ts +82 -0
- package/src/__tests__/profile-sync.test.ts +136 -0
- package/src/__tests__/registry-version-resolution.test.ts +74 -0
- package/src/__tests__/resolve-executable.test.ts +42 -0
- package/src/__tests__/resolver.test.ts +218 -0
- package/src/__tests__/symlink-manager.test.ts +99 -0
- package/src/__tests__/toolchain.test.ts +56 -0
- package/src/cli/claude.ts +21 -0
- package/src/cli/doctor.ts +132 -0
- package/src/cli/install.ts +360 -0
- package/src/cli/profile.ts +166 -0
- package/src/cli/router.ts +107 -0
- package/src/cli/update.ts +85 -0
- package/src/data/cli-tools.ts +7 -7
- package/src/data/gitignore-defaults.ts +4 -0
- package/src/data/marketplaces.ts +12 -0
- package/src/data/predefined-profiles.ts +3 -4
- package/src/main.tsx +11 -154
- package/src/prerunner/index.ts +62 -27
- package/src/services/claude-cli.ts +19 -22
- package/src/services/claude-settings.ts +99 -26
- package/src/services/conventions-manager.ts +865 -0
- package/src/services/doctor-bins.ts +49 -0
- package/src/services/doctor.ts +509 -0
- package/src/services/install-plan.ts +107 -0
- package/src/services/manifest.ts +166 -0
- package/src/services/marketplace-refresh.ts +173 -0
- package/src/services/plugin-manager.ts +7 -1
- package/src/services/plugin-requires.ts +215 -0
- package/src/services/plugin-version-check.ts +9 -4
- package/src/services/profile-materializer.ts +61 -0
- package/src/services/profile-sync.ts +150 -0
- package/src/services/resolver.ts +293 -0
- package/src/services/symlink-manager.ts +146 -0
- package/src/services/toolchain.ts +97 -0
- package/src/services/version-check.ts +10 -11
- package/src/types/index.ts +138 -31
- package/src/ui/App.tsx +0 -4
- package/src/ui/screens/CliToolsScreen.tsx +11 -3
- package/src/ui/screens/index.ts +0 -1
- package/src/ui/state/reducer.ts +0 -101
- package/src/ui/state/types.ts +0 -35
- package/src/utils/command-utils.ts +23 -0
- package/src/utils/file-locking.ts +144 -0
- package/src/data/alias-flags.js +0 -205
- package/src/data/cli-tools.js +0 -123
- package/src/data/gitignore-defaults.js +0 -24
- package/src/data/gitignore-reasons.js +0 -97
- package/src/data/gitignore-templates.js +0 -21
- package/src/data/marketplaces.js +0 -138
- package/src/data/mcp-servers.js +0 -509
- package/src/data/predefined-profiles.js +0 -248
- package/src/data/settings-catalog.js +0 -625
- package/src/data/skill-repos.js +0 -160
- package/src/data/statuslines.js +0 -159
- package/src/data/statuslines.ts +0 -188
- package/src/index.js +0 -4
- package/src/index.ts +0 -5
- package/src/main.js +0 -170
- package/src/prerunner/index.js +0 -252
- package/src/services/alias-settings.js +0 -51
- package/src/services/alias-shell-writer.js +0 -1018
- package/src/services/alias-store.js +0 -129
- package/src/services/claude-cli.js +0 -189
- package/src/services/claude-runner.js +0 -28
- package/src/services/claude-settings.js +0 -1223
- package/src/services/gitignore-detector.js +0 -155
- package/src/services/gitignore-fixer.js +0 -231
- package/src/services/gitignore-prerun.js +0 -46
- package/src/services/gitignore-resolver.js +0 -143
- package/src/services/gitignore-service.js +0 -117
- package/src/services/local-marketplace.js +0 -339
- package/src/services/marketplace-fetcher.js +0 -96
- package/src/services/marketplace-sync.js +0 -94
- package/src/services/mcp-registry.js +0 -87
- package/src/services/plugin-manager.js +0 -473
- package/src/services/plugin-mcp-config.js +0 -177
- package/src/services/plugin-setup.js +0 -499
- package/src/services/plugin-version-check.js +0 -262
- package/src/services/profiles.js +0 -161
- package/src/services/settings-manager.js +0 -243
- package/src/services/skills-manager.js +0 -393
- package/src/services/skillsmp-client.js +0 -87
- package/src/services/update-cache.js +0 -52
- package/src/services/version-check.js +0 -99
- package/src/types/gitignore.js +0 -6
- package/src/types/index.js +0 -1
- package/src/ui/App.js +0 -355
- package/src/ui/adapters/pluginsAdapter.js +0 -139
- package/src/ui/adapters/settingsAdapter.js +0 -111
- package/src/ui/adapters/skillsAdapter.js +0 -154
- package/src/ui/components/CategoryHeader.js +0 -9
- package/src/ui/components/EmptyFilterState.js +0 -4
- package/src/ui/components/FlagDetailEditor.js +0 -0
- package/src/ui/components/ScopeIndicator.js +0 -30
- package/src/ui/components/ScrollableList.js +0 -36
- package/src/ui/components/SearchInput.js +0 -19
- package/src/ui/components/StyledText.js +0 -39
- package/src/ui/components/TabBar.js +0 -19
- package/src/ui/components/layout/FooterHints.js +0 -29
- package/src/ui/components/layout/Panel.js +0 -6
- package/src/ui/components/layout/ProgressBar.js +0 -14
- package/src/ui/components/layout/ScopeTabs.js +0 -6
- package/src/ui/components/layout/ScreenLayout.js +0 -16
- package/src/ui/components/layout/index.js +0 -5
- package/src/ui/components/modals/ConfirmModal.js +0 -14
- package/src/ui/components/modals/InputModal.js +0 -5
- package/src/ui/components/modals/LoadingModal.js +0 -17
- package/src/ui/components/modals/MessageModal.js +0 -16
- package/src/ui/components/modals/ModalContainer.js +0 -137
- package/src/ui/components/modals/SelectModal.js +0 -18
- package/src/ui/components/modals/VersionMismatchModal.js +0 -36
- package/src/ui/components/modals/index.js +0 -6
- package/src/ui/components/primitives/ActionHints.js +0 -13
- package/src/ui/components/primitives/DetailSection.js +0 -7
- package/src/ui/components/primitives/KeyValueLine.js +0 -8
- package/src/ui/components/primitives/ListCategoryRow.js +0 -8
- package/src/ui/components/primitives/MetaText.js +0 -8
- package/src/ui/components/primitives/ScopeDetail.js +0 -32
- package/src/ui/components/primitives/ScopeSquares.js +0 -11
- package/src/ui/components/primitives/SelectableRow.js +0 -5
- package/src/ui/components/primitives/index.js +0 -8
- package/src/ui/hooks/index.js +0 -4
- package/src/ui/hooks/useAsyncData.js +0 -77
- package/src/ui/hooks/useGitignoreModal.js +0 -74
- package/src/ui/hooks/useKeyboard.js +0 -13
- package/src/ui/hooks/useKeyboardHandler.js +0 -39
- package/src/ui/hooks/useMismatchModal.js +0 -77
- package/src/ui/registry.js +0 -1
- package/src/ui/renderers/cliToolRenderers.js +0 -54
- package/src/ui/renderers/gitignoreRenderers.js +0 -46
- package/src/ui/renderers/mcpRenderers.js +0 -26
- package/src/ui/renderers/pluginRenderers.js +0 -124
- package/src/ui/renderers/profileRenderers.js +0 -177
- package/src/ui/renderers/settingsRenderers.js +0 -73
- package/src/ui/renderers/skillRenderers.js +0 -138
- package/src/ui/screens/AliasScreen.js +0 -1111
- package/src/ui/screens/CliToolsScreen.js +0 -338
- package/src/ui/screens/EnvVarsScreen.js +0 -152
- package/src/ui/screens/GitignoreScreen.js +0 -328
- package/src/ui/screens/McpRegistryScreen.js +0 -238
- package/src/ui/screens/McpScreen.js +0 -176
- package/src/ui/screens/ModelSelectorScreen.js +0 -296
- package/src/ui/screens/ModelSelectorScreen.tsx +0 -444
- package/src/ui/screens/PluginsScreen.js +0 -769
- package/src/ui/screens/ProfilesScreen.js +0 -298
- package/src/ui/screens/SkillsScreen.js +0 -549
- package/src/ui/screens/StatusLineScreen.js +0 -206
- package/src/ui/screens/StatusLineScreen.tsx +0 -416
- package/src/ui/screens/index.js +0 -10
- package/src/ui/state/AnimationContext.js +0 -34
- package/src/ui/state/AppContext.js +0 -162
- package/src/ui/state/DimensionsContext.js +0 -71
- package/src/ui/state/reducer.js +0 -547
- package/src/ui/state/types.js +0 -1
- package/src/ui/theme.js +0 -47
- package/src/utils/clipboard.js +0 -56
- package/src/utils/command-utils.js +0 -19
- package/src/utils/fuzzy-search.js +0 -101
- package/src/utils/string-utils.js +0 -62
package/src/ui/App.js
DELETED
|
@@ -1,355 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
3
|
-
import { useRenderer } from "@opentui/react";
|
|
4
|
-
import fs from "node:fs";
|
|
5
|
-
import { AppProvider, useApp, useNavigation, useModal, } from "./state/AppContext.js";
|
|
6
|
-
import { DimensionsProvider, useDimensions, } from "./state/DimensionsContext.js";
|
|
7
|
-
import { ModalContainer } from "./components/modals/index.js";
|
|
8
|
-
import { PluginsScreen, McpScreen, McpRegistryScreen, SettingsScreen, CliToolsScreen, ModelSelectorScreen, ProfilesScreen, SkillsScreen, GitignoreScreen, AliasScreen, } from "./screens/index.js";
|
|
9
|
-
import { checkGitignore } from "../services/gitignore-prerun.js";
|
|
10
|
-
import { loadGitignoreState, applyAllSafeFixes, } from "../services/gitignore-service.js";
|
|
11
|
-
import { useGitignoreModal } from "./hooks/useGitignoreModal.js";
|
|
12
|
-
import { repairAllMarketplaces } from "../services/local-marketplace.js";
|
|
13
|
-
import { migrateMarketplaceRename, recoverMarketplaceSettings, } from "../services/claude-settings.js";
|
|
14
|
-
import { autoAddMissingMarketplaces } from "../services/marketplace-sync.js";
|
|
15
|
-
import { isClaudeAvailable } from "../services/claude-cli.js";
|
|
16
|
-
import { checkPluginVersionMismatches, } from "../services/plugin-version-check.js";
|
|
17
|
-
import { useMismatchModal } from "./hooks/useMismatchModal.js";
|
|
18
|
-
import { checkForUpdates, getCurrentVersion, } from "../services/version-check.js";
|
|
19
|
-
import { useKeyboardHandler } from "./hooks/useKeyboardHandler.js";
|
|
20
|
-
import { ProgressBar } from "./components/layout/ProgressBar.js";
|
|
21
|
-
export const VERSION = getCurrentVersion();
|
|
22
|
-
/**
|
|
23
|
-
* Router Component
|
|
24
|
-
* Renders the current screen based on route
|
|
25
|
-
*/
|
|
26
|
-
function Router() {
|
|
27
|
-
const { state } = useApp();
|
|
28
|
-
const { currentRoute } = state;
|
|
29
|
-
switch (currentRoute.screen) {
|
|
30
|
-
case "plugins":
|
|
31
|
-
return _jsx(PluginsScreen, {});
|
|
32
|
-
case "mcp":
|
|
33
|
-
return _jsx(McpScreen, {});
|
|
34
|
-
case "mcp-registry":
|
|
35
|
-
return _jsx(McpRegistryScreen, {});
|
|
36
|
-
case "settings":
|
|
37
|
-
return _jsx(SettingsScreen, {});
|
|
38
|
-
case "cli-tools":
|
|
39
|
-
return _jsx(CliToolsScreen, {});
|
|
40
|
-
case "model-selector":
|
|
41
|
-
return _jsx(ModelSelectorScreen, {});
|
|
42
|
-
case "profiles":
|
|
43
|
-
return _jsx(ProfilesScreen, {});
|
|
44
|
-
case "skills":
|
|
45
|
-
return _jsx(SkillsScreen, {});
|
|
46
|
-
case "gitignore":
|
|
47
|
-
return _jsx(GitignoreScreen, {});
|
|
48
|
-
case "alias":
|
|
49
|
-
return _jsx(AliasScreen, {});
|
|
50
|
-
default:
|
|
51
|
-
return _jsx(PluginsScreen, {});
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* GlobalKeyHandler Component
|
|
56
|
-
* Handles global keyboard shortcuts (1-8, Tab, Escape, q, Ctrl+C, ?, Shift+D)
|
|
57
|
-
* Does not render anything (returns null)
|
|
58
|
-
*/
|
|
59
|
-
function GlobalKeyHandler({ onDebugToggle, onExit, }) {
|
|
60
|
-
const { state } = useApp();
|
|
61
|
-
const { navigateToScreen } = useNavigation();
|
|
62
|
-
const modal = useModal();
|
|
63
|
-
const renderer = useRenderer();
|
|
64
|
-
useKeyboardHandler((input, key) => {
|
|
65
|
-
// Debug key (Shift+D) - always available
|
|
66
|
-
if (input === "D" && key.shift) {
|
|
67
|
-
onDebugToggle();
|
|
68
|
-
// Write debug info to file
|
|
69
|
-
const debugInfo = {
|
|
70
|
-
timestamp: new Date().toISOString(),
|
|
71
|
-
terminal: { rows: renderer.height, columns: renderer.width },
|
|
72
|
-
state: {
|
|
73
|
-
currentRoute: state.currentRoute,
|
|
74
|
-
isSearching: state.isSearching,
|
|
75
|
-
modal: state.modal ? { type: state.modal.type } : null,
|
|
76
|
-
plugins: {
|
|
77
|
-
scope: state.plugins.scope,
|
|
78
|
-
selectedIndex: state.plugins.selectedIndex,
|
|
79
|
-
searchQuery: state.plugins.searchQuery,
|
|
80
|
-
marketplacesStatus: state.plugins.marketplaces.status,
|
|
81
|
-
pluginsStatus: state.plugins.plugins.status,
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
};
|
|
85
|
-
fs.writeFileSync("/tmp/claudeup-debug.json", JSON.stringify(debugInfo, null, 2));
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
// Don't handle keys when modal is open or searching
|
|
89
|
-
if (state.modal || state.isSearching)
|
|
90
|
-
return;
|
|
91
|
-
// Global navigation shortcuts (1-8) - include mcp-registry as it's a sub-screen of mcp
|
|
92
|
-
const isTopLevel = [
|
|
93
|
-
"plugins",
|
|
94
|
-
"mcp",
|
|
95
|
-
"mcp-registry",
|
|
96
|
-
"settings",
|
|
97
|
-
"cli-tools",
|
|
98
|
-
"model-selector",
|
|
99
|
-
"profiles",
|
|
100
|
-
"skills",
|
|
101
|
-
"gitignore",
|
|
102
|
-
"alias",
|
|
103
|
-
].includes(state.currentRoute.screen);
|
|
104
|
-
if (isTopLevel) {
|
|
105
|
-
if (input === "1")
|
|
106
|
-
navigateToScreen("plugins");
|
|
107
|
-
else if (input === "2")
|
|
108
|
-
navigateToScreen("skills");
|
|
109
|
-
else if (input === "3")
|
|
110
|
-
navigateToScreen("mcp");
|
|
111
|
-
else if (input === "4")
|
|
112
|
-
navigateToScreen("settings");
|
|
113
|
-
else if (input === "5")
|
|
114
|
-
navigateToScreen("profiles");
|
|
115
|
-
else if (input === "6")
|
|
116
|
-
navigateToScreen("cli-tools");
|
|
117
|
-
else if (input === "7")
|
|
118
|
-
navigateToScreen("gitignore");
|
|
119
|
-
else if (input === "8")
|
|
120
|
-
navigateToScreen("alias");
|
|
121
|
-
// Tab navigation cycling
|
|
122
|
-
if (key.tab) {
|
|
123
|
-
const screens = [
|
|
124
|
-
"plugins",
|
|
125
|
-
"skills",
|
|
126
|
-
"mcp",
|
|
127
|
-
"settings",
|
|
128
|
-
"profiles",
|
|
129
|
-
"cli-tools",
|
|
130
|
-
"gitignore",
|
|
131
|
-
"alias",
|
|
132
|
-
];
|
|
133
|
-
const currentIndex = screens.indexOf(state.currentRoute.screen);
|
|
134
|
-
if (currentIndex !== -1) {
|
|
135
|
-
const nextIndex = key.shift
|
|
136
|
-
? (currentIndex - 1 + screens.length) % screens.length
|
|
137
|
-
: (currentIndex + 1) % screens.length;
|
|
138
|
-
navigateToScreen(screens[nextIndex]);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
// q or Ctrl+C quits from anywhere — no need to bounce to home first.
|
|
143
|
-
if (input === "q" || (key.ctrl && input === "c")) {
|
|
144
|
-
onExit();
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
// Escape goes back: registry → mcp, any other tab → plugins home,
|
|
148
|
-
// already-home → exit. But when a screen has a focused sub-view open
|
|
149
|
-
// (e.g. the Alias flag-detail editor), that sub-view owns Escape and
|
|
150
|
-
// backs out to itself — don't let the global handler jump to home.
|
|
151
|
-
// Scoped to the Escape branch ONLY so q/Ctrl+C still quit from anywhere.
|
|
152
|
-
if (key.escape) {
|
|
153
|
-
if (state.detailActive)
|
|
154
|
-
return;
|
|
155
|
-
if (state.currentRoute.screen === "plugins") {
|
|
156
|
-
onExit();
|
|
157
|
-
}
|
|
158
|
-
else if (state.currentRoute.screen === "mcp-registry") {
|
|
159
|
-
navigateToScreen("mcp");
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
navigateToScreen("plugins");
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
// ? for help
|
|
166
|
-
if (input === "?") {
|
|
167
|
-
modal.message("claudeup Help", `Navigation
|
|
168
|
-
↑/↓ or j/k Move selection
|
|
169
|
-
Enter Select / Toggle
|
|
170
|
-
Escape Back (to home)
|
|
171
|
-
q / Ctrl+C Quit (from anywhere)
|
|
172
|
-
? This help
|
|
173
|
-
|
|
174
|
-
Quick Navigation
|
|
175
|
-
1 Plugins 4 Settings 7 Git State
|
|
176
|
-
2 Skills 5 Profiles 8 Alias
|
|
177
|
-
3 MCP Servers 6 CLI Tools
|
|
178
|
-
|
|
179
|
-
Plugin Actions
|
|
180
|
-
u Update d Uninstall
|
|
181
|
-
a Update All r Refresh
|
|
182
|
-
|
|
183
|
-
MCP Servers
|
|
184
|
-
/ Search local + remote
|
|
185
|
-
r Browse MCP registry`, "info");
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
return null;
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* UpdateBanner Component
|
|
192
|
-
* Shows version update notification
|
|
193
|
-
*/
|
|
194
|
-
function UpdateBanner({ result }) {
|
|
195
|
-
if (!result.updateAvailable)
|
|
196
|
-
return null;
|
|
197
|
-
return (_jsxs("box", { paddingLeft: 1, paddingRight: 1, children: [_jsx("text", { bg: "yellow", fg: "black", children: _jsx("strong", { children: " UPDATE " }) }), _jsxs("text", { fg: "yellow", children: [" ", "v", result.currentVersion, " \u2192 v", result.latestVersion] }), _jsx("text", { fg: "gray", children: " Run: " }), _jsx("text", { fg: "cyan", children: "claudeup update" })] }));
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* ProgressIndicator Component
|
|
201
|
-
* Shows progress bar when operations are running
|
|
202
|
-
*/
|
|
203
|
-
function ProgressIndicator({ message, current, total, }) {
|
|
204
|
-
return (_jsx("box", { paddingLeft: 1, paddingRight: 1, children: _jsx(ProgressBar, { message: message, current: current, total: total }) }));
|
|
205
|
-
}
|
|
206
|
-
function AppContentInner({ showDebug, onDebugToggle, updateInfo, onExit, recoveryReport, }) {
|
|
207
|
-
const { state } = useApp();
|
|
208
|
-
const { progress } = state;
|
|
209
|
-
const dimensions = useDimensions();
|
|
210
|
-
return (_jsxs("box", { flexDirection: "column", height: dimensions.terminalHeight, children: [updateInfo?.updateAvailable && _jsx(UpdateBanner, { result: updateInfo }), recoveryReport && (_jsx("box", { paddingLeft: 1, paddingRight: 1, children: _jsxs("text", { fg: "green", children: ["\u2713 Fixed: ", recoveryReport] }) })), showDebug && (_jsx("box", { paddingLeft: 1, paddingRight: 1, children: _jsxs("text", { fg: "#888888", children: ["DEBUG: ", dimensions.terminalWidth, "x", dimensions.terminalHeight, " | content=", dimensions.contentHeight, " | screen=", state.currentRoute.screen] }) })), progress && _jsx(ProgressIndicator, { ...progress }), _jsx("box", { flexDirection: "column", height: dimensions.contentHeight, paddingLeft: 1, paddingRight: 1, children: _jsx(Router, {}) }), _jsx(GlobalKeyHandler, { onDebugToggle: onDebugToggle, onExit: onExit }), _jsx(ModalContainer, {})] }));
|
|
211
|
-
}
|
|
212
|
-
function AppContent({ onExit }) {
|
|
213
|
-
const { state, dispatch } = useApp();
|
|
214
|
-
const { progress } = state;
|
|
215
|
-
const [showDebug, setShowDebug] = useState(false);
|
|
216
|
-
const [updateInfo, setUpdateInfo] = useState(null);
|
|
217
|
-
const [recoveryReport, setRecoveryReport] = useState(null);
|
|
218
|
-
const [mismatchData, setMismatchData] = useState(null);
|
|
219
|
-
const mismatchModal = useMismatchModal();
|
|
220
|
-
const [gitignoreData, setGitignoreData] = useState(null);
|
|
221
|
-
const gitignoreModal = useGitignoreModal();
|
|
222
|
-
// Check for updates on startup (non-blocking)
|
|
223
|
-
useEffect(() => {
|
|
224
|
-
checkForUpdates()
|
|
225
|
-
.then(setUpdateInfo)
|
|
226
|
-
.catch(() => { });
|
|
227
|
-
}, []);
|
|
228
|
-
// Auto-dismiss recovery banner after 5 seconds
|
|
229
|
-
useEffect(() => {
|
|
230
|
-
if (!recoveryReport)
|
|
231
|
-
return;
|
|
232
|
-
const timer = setTimeout(() => setRecoveryReport(null), 5000);
|
|
233
|
-
return () => clearTimeout(timer);
|
|
234
|
-
}, [recoveryReport]);
|
|
235
|
-
// Show mismatch modal when data arrives
|
|
236
|
-
useEffect(() => {
|
|
237
|
-
if (!mismatchData || mismatchData.length === 0)
|
|
238
|
-
return;
|
|
239
|
-
mismatchModal.show(mismatchData);
|
|
240
|
-
setMismatchData(null);
|
|
241
|
-
}, [mismatchData, mismatchModal]);
|
|
242
|
-
// Show gitignore modal — wait for the modal slot to be free so we don't
|
|
243
|
-
// stomp on the mismatch modal that may have just been shown.
|
|
244
|
-
useEffect(() => {
|
|
245
|
-
if (!gitignoreData)
|
|
246
|
-
return;
|
|
247
|
-
if (state.modal)
|
|
248
|
-
return;
|
|
249
|
-
gitignoreModal.show({
|
|
250
|
-
violationCount: gitignoreData.violationCount,
|
|
251
|
-
safeCount: gitignoreData.safeCount,
|
|
252
|
-
onFixSafe: async () => {
|
|
253
|
-
const s = await loadGitignoreState(state.projectPath);
|
|
254
|
-
await applyAllSafeFixes(state.projectPath, s.violations);
|
|
255
|
-
},
|
|
256
|
-
});
|
|
257
|
-
setGitignoreData(null);
|
|
258
|
-
}, [gitignoreData, gitignoreModal, state.modal, state.projectPath]);
|
|
259
|
-
// Auto-refresh marketplaces on startup
|
|
260
|
-
useEffect(() => {
|
|
261
|
-
const noRefresh = process.argv.includes("--no-refresh");
|
|
262
|
-
if (noRefresh)
|
|
263
|
-
return;
|
|
264
|
-
dispatch({
|
|
265
|
-
type: "SHOW_PROGRESS",
|
|
266
|
-
state: { message: "Scanning marketplaces..." },
|
|
267
|
-
});
|
|
268
|
-
// Migrate old marketplace names -> magus (idempotent), then repair plugin.json files
|
|
269
|
-
migrateMarketplaceRename().catch(() => { }); // non-blocking, best-effort
|
|
270
|
-
// Auto-add marketplaces referenced by enabledPlugins but missing from
|
|
271
|
-
// the local registry. Fixes the "fresh clone of a project whose
|
|
272
|
-
// .claude/settings.json enables @magus plugins but this machine has
|
|
273
|
-
// never run `claude plugin marketplace add`" case.
|
|
274
|
-
(async () => {
|
|
275
|
-
try {
|
|
276
|
-
if (!(await isClaudeAvailable()))
|
|
277
|
-
return;
|
|
278
|
-
const added = await autoAddMissingMarketplaces(process.cwd());
|
|
279
|
-
if (added.length > 0) {
|
|
280
|
-
setRecoveryReport(`added marketplace(s): ${added.join(", ")}`);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
catch {
|
|
284
|
-
// non-fatal: user can still install marketplaces manually
|
|
285
|
-
}
|
|
286
|
-
})();
|
|
287
|
-
// Recover stale marketplace registry entries (e.g. "directory" -> "github")
|
|
288
|
-
recoverMarketplaceSettings()
|
|
289
|
-
.then(async (recovery) => {
|
|
290
|
-
const parts = [];
|
|
291
|
-
if (recovery.reregistered.length > 0) {
|
|
292
|
-
// Update the marketplace clone now that the source is fixed
|
|
293
|
-
const { updateMarketplace } = await import("../services/claude-cli.js");
|
|
294
|
-
for (const mp of recovery.reregistered) {
|
|
295
|
-
try {
|
|
296
|
-
await updateMarketplace(mp);
|
|
297
|
-
parts.push(`${mp} refreshed`);
|
|
298
|
-
}
|
|
299
|
-
catch {
|
|
300
|
-
parts.push(`${mp} (update failed)`);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
if (recovery.enabledAutoUpdate.length > 0) {
|
|
305
|
-
parts.push(`auto-update: ${recovery.enabledAutoUpdate.join(", ")}`);
|
|
306
|
-
}
|
|
307
|
-
if (recovery.removed.length > 0) {
|
|
308
|
-
parts.push(`removed: ${recovery.removed.join(", ")}`);
|
|
309
|
-
}
|
|
310
|
-
if (parts.length > 0) {
|
|
311
|
-
setRecoveryReport(parts.join(" | "));
|
|
312
|
-
}
|
|
313
|
-
})
|
|
314
|
-
.catch(() => { }); // non-fatal
|
|
315
|
-
// Check for plugin version mismatches (the [0] index bug)
|
|
316
|
-
checkPluginVersionMismatches(process.cwd())
|
|
317
|
-
.then((mismatches) => {
|
|
318
|
-
if (mismatches.length > 0) {
|
|
319
|
-
setMismatchData(mismatches);
|
|
320
|
-
}
|
|
321
|
-
})
|
|
322
|
-
.catch(() => { }); // non-fatal
|
|
323
|
-
// Check for gitignore manifest violations and surface a popup if any.
|
|
324
|
-
// Loads twice (once for count, once when user picks "fix safe") because
|
|
325
|
-
// the second load happens after edits — keeps detection cheap on startup.
|
|
326
|
-
(async () => {
|
|
327
|
-
try {
|
|
328
|
-
const result = await checkGitignore(process.cwd());
|
|
329
|
-
if (result.violationCount > 0) {
|
|
330
|
-
const s = await loadGitignoreState(process.cwd());
|
|
331
|
-
const safeCount = s.violations.filter((v) => v.severity === "safe").length;
|
|
332
|
-
setGitignoreData({ violationCount: result.violationCount, safeCount });
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
catch {
|
|
336
|
-
// non-fatal
|
|
337
|
-
}
|
|
338
|
-
})();
|
|
339
|
-
repairAllMarketplaces()
|
|
340
|
-
.then(async () => {
|
|
341
|
-
dispatch({ type: "HIDE_PROGRESS" });
|
|
342
|
-
dispatch({ type: "DATA_REFRESH_COMPLETE" });
|
|
343
|
-
})
|
|
344
|
-
.catch(() => {
|
|
345
|
-
dispatch({ type: "HIDE_PROGRESS" });
|
|
346
|
-
});
|
|
347
|
-
}, [dispatch]);
|
|
348
|
-
// Count transient banners for dimension calculation
|
|
349
|
-
const transientBannerCount = recoveryReport ? 1 : 0;
|
|
350
|
-
return (_jsx(DimensionsProvider, { showProgress: !!progress, showDebug: showDebug, showUpdateBanner: !!updateInfo?.updateAvailable, transientBannerCount: transientBannerCount, children: _jsx(AppContentInner, { showDebug: showDebug, onDebugToggle: () => setShowDebug((s) => !s), updateInfo: updateInfo, onExit: onExit, recoveryReport: recoveryReport }) }));
|
|
351
|
-
}
|
|
352
|
-
export function App({ onExit }) {
|
|
353
|
-
return (_jsx(AppProvider, { children: _jsx(AppContent, { onExit: onExit }) }));
|
|
354
|
-
}
|
|
355
|
-
export default App;
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
// Virtual marketplace name for the community sub-section of claude-plugins-official
|
|
2
|
-
export const COMMUNITY_VIRTUAL_MARKETPLACE = "claude-plugins-official:community";
|
|
3
|
-
// The marketplace that gets split into Anthropic Official + Community sections
|
|
4
|
-
export const SPLIT_MARKETPLACE = "claude-plugins-official";
|
|
5
|
-
/**
|
|
6
|
-
* Derives tone and badge for a category item based on marketplace identity.
|
|
7
|
-
*/
|
|
8
|
-
function categoryStyling(mp, isCommunitySection, marketplaceEnabled) {
|
|
9
|
-
if (!marketplaceEnabled) {
|
|
10
|
-
return { tone: "gray" };
|
|
11
|
-
}
|
|
12
|
-
if (isCommunitySection) {
|
|
13
|
-
return { tone: "gray", badge: "3rd Party" };
|
|
14
|
-
}
|
|
15
|
-
if (mp.name === "claude-plugins-official") {
|
|
16
|
-
return { tone: "yellow", badge: "★ Official" };
|
|
17
|
-
}
|
|
18
|
-
if (mp.name === "claude-code-plugins") {
|
|
19
|
-
return { tone: "red", badge: "⚠ Deprecated" };
|
|
20
|
-
}
|
|
21
|
-
if (mp.official) {
|
|
22
|
-
return { tone: "yellow", badge: "★ Official" };
|
|
23
|
-
}
|
|
24
|
-
return { tone: "green", badge: "✓ Added" };
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Builds the flat list of items for the PluginsScreen list panel.
|
|
28
|
-
* Extracted from PluginsScreen so it can be tested independently.
|
|
29
|
-
*/
|
|
30
|
-
export function buildPluginBrowserItems({ marketplaces, plugins, collapsedMarketplaces, }) {
|
|
31
|
-
const pluginsByMarketplace = new Map();
|
|
32
|
-
for (const plugin of plugins) {
|
|
33
|
-
const existing = pluginsByMarketplace.get(plugin.marketplace) || [];
|
|
34
|
-
existing.push(plugin);
|
|
35
|
-
pluginsByMarketplace.set(plugin.marketplace, existing);
|
|
36
|
-
}
|
|
37
|
-
// Sort marketplaces: deprecated ones go to the bottom
|
|
38
|
-
const sortedMarketplaces = [...marketplaces].sort((a, b) => {
|
|
39
|
-
const aDeprecated = a.name === "claude-code-plugins" ? 1 : 0;
|
|
40
|
-
const bDeprecated = b.name === "claude-code-plugins" ? 1 : 0;
|
|
41
|
-
return aDeprecated - bDeprecated;
|
|
42
|
-
});
|
|
43
|
-
const items = [];
|
|
44
|
-
for (const marketplace of sortedMarketplaces) {
|
|
45
|
-
const marketplacePlugins = pluginsByMarketplace.get(marketplace.name) || [];
|
|
46
|
-
const isCollapsed = collapsedMarketplaces.has(marketplace.name);
|
|
47
|
-
const isEnabled = marketplacePlugins.length > 0 || !!marketplace.official;
|
|
48
|
-
const hasPlugins = marketplacePlugins.length > 0;
|
|
49
|
-
// Special handling: split claude-plugins-official into two sub-sections
|
|
50
|
-
if (marketplace.name === SPLIT_MARKETPLACE && hasPlugins) {
|
|
51
|
-
const anthropicPlugins = marketplacePlugins.filter((p) => p.author?.name?.toLowerCase() === "anthropic");
|
|
52
|
-
const communityPlugins = marketplacePlugins.filter((p) => p.author?.name?.toLowerCase() !== "anthropic");
|
|
53
|
-
// Sub-section 1: Anthropic Official (plugins by Anthropic)
|
|
54
|
-
const anthropicCollapsed = collapsedMarketplaces.has(marketplace.name);
|
|
55
|
-
const anthropicHasPlugins = anthropicPlugins.length > 0;
|
|
56
|
-
const anthropicStyle = categoryStyling(marketplace, false, isEnabled);
|
|
57
|
-
items.push({
|
|
58
|
-
id: `mp:${marketplace.name}`,
|
|
59
|
-
kind: "category",
|
|
60
|
-
label: marketplace.displayName,
|
|
61
|
-
marketplace,
|
|
62
|
-
marketplaceEnabled: isEnabled,
|
|
63
|
-
pluginCount: anthropicPlugins.length,
|
|
64
|
-
isExpanded: !anthropicCollapsed && anthropicHasPlugins,
|
|
65
|
-
tone: anthropicStyle.tone,
|
|
66
|
-
badge: anthropicStyle.badge,
|
|
67
|
-
});
|
|
68
|
-
if (isEnabled && anthropicHasPlugins && !anthropicCollapsed) {
|
|
69
|
-
for (const plugin of anthropicPlugins) {
|
|
70
|
-
items.push({
|
|
71
|
-
id: `pl:${plugin.id}`,
|
|
72
|
-
kind: "plugin",
|
|
73
|
-
label: plugin.name,
|
|
74
|
-
plugin,
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
// Sub-section 2: Community (third-party plugins in same marketplace)
|
|
79
|
-
if (communityPlugins.length > 0) {
|
|
80
|
-
const communityVirtualMp = {
|
|
81
|
-
name: COMMUNITY_VIRTUAL_MARKETPLACE,
|
|
82
|
-
displayName: "Anthropic Official — 3rd Party",
|
|
83
|
-
source: marketplace.source,
|
|
84
|
-
description: "Third-party plugins in the Anthropic Official marketplace",
|
|
85
|
-
};
|
|
86
|
-
const communityCollapsed = collapsedMarketplaces.has(COMMUNITY_VIRTUAL_MARKETPLACE);
|
|
87
|
-
const communityStyle = categoryStyling(communityVirtualMp, true, true);
|
|
88
|
-
items.push({
|
|
89
|
-
id: `mp:${COMMUNITY_VIRTUAL_MARKETPLACE}`,
|
|
90
|
-
kind: "category",
|
|
91
|
-
label: "Anthropic Official — 3rd Party",
|
|
92
|
-
marketplace: communityVirtualMp,
|
|
93
|
-
marketplaceEnabled: true,
|
|
94
|
-
pluginCount: communityPlugins.length,
|
|
95
|
-
isExpanded: !communityCollapsed,
|
|
96
|
-
isCommunitySection: true,
|
|
97
|
-
tone: communityStyle.tone,
|
|
98
|
-
badge: communityStyle.badge,
|
|
99
|
-
});
|
|
100
|
-
if (!communityCollapsed) {
|
|
101
|
-
for (const plugin of communityPlugins) {
|
|
102
|
-
items.push({
|
|
103
|
-
id: `pl:${plugin.id}`,
|
|
104
|
-
kind: "plugin",
|
|
105
|
-
label: plugin.name,
|
|
106
|
-
plugin,
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
// Category header (marketplace)
|
|
114
|
-
const style = categoryStyling(marketplace, false, isEnabled);
|
|
115
|
-
items.push({
|
|
116
|
-
id: `mp:${marketplace.name}`,
|
|
117
|
-
kind: "category",
|
|
118
|
-
label: marketplace.displayName,
|
|
119
|
-
marketplace,
|
|
120
|
-
marketplaceEnabled: isEnabled,
|
|
121
|
-
pluginCount: marketplacePlugins.length,
|
|
122
|
-
isExpanded: !isCollapsed && hasPlugins,
|
|
123
|
-
tone: style.tone,
|
|
124
|
-
badge: style.badge,
|
|
125
|
-
});
|
|
126
|
-
// Plugins under this marketplace (if expanded)
|
|
127
|
-
if (isEnabled && hasPlugins && !isCollapsed) {
|
|
128
|
-
for (const plugin of marketplacePlugins) {
|
|
129
|
-
items.push({
|
|
130
|
-
id: `pl:${plugin.id}`,
|
|
131
|
-
kind: "plugin",
|
|
132
|
-
label: plugin.name,
|
|
133
|
-
plugin,
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
return items;
|
|
139
|
-
}
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { SETTINGS_CATALOG, } from "../../data/settings-catalog.js";
|
|
2
|
-
// ─── Constants ─────────────────────────────────────────────────────────────────
|
|
3
|
-
export const CATEGORY_LABELS = {
|
|
4
|
-
recommended: "Recommended",
|
|
5
|
-
agents: "Agents & Teams",
|
|
6
|
-
models: "Models & Thinking",
|
|
7
|
-
workflow: "Workflow",
|
|
8
|
-
terminal: "Terminal & UI",
|
|
9
|
-
performance: "Performance",
|
|
10
|
-
advanced: "Advanced",
|
|
11
|
-
};
|
|
12
|
-
export const CATEGORY_ORDER = [
|
|
13
|
-
"recommended",
|
|
14
|
-
"agents",
|
|
15
|
-
"models",
|
|
16
|
-
"workflow",
|
|
17
|
-
"terminal",
|
|
18
|
-
"performance",
|
|
19
|
-
"advanced",
|
|
20
|
-
];
|
|
21
|
-
export const CATEGORY_BG = {
|
|
22
|
-
recommended: "#2e7d32",
|
|
23
|
-
agents: "#00838f",
|
|
24
|
-
models: "#4527a0",
|
|
25
|
-
workflow: "#1565c0",
|
|
26
|
-
terminal: "#4e342e",
|
|
27
|
-
performance: "#6a1b9a",
|
|
28
|
-
advanced: "#e65100",
|
|
29
|
-
};
|
|
30
|
-
export const CATEGORY_COLOR = {
|
|
31
|
-
recommended: "green",
|
|
32
|
-
agents: "cyan",
|
|
33
|
-
models: "cyan",
|
|
34
|
-
workflow: "blue",
|
|
35
|
-
terminal: "blue",
|
|
36
|
-
performance: "magentaBright",
|
|
37
|
-
advanced: "yellow",
|
|
38
|
-
};
|
|
39
|
-
export const CATEGORY_DESCRIPTIONS = {
|
|
40
|
-
recommended: "Most impactful settings every user should know.",
|
|
41
|
-
agents: "Agent teams, task lists, and subagent configuration.",
|
|
42
|
-
models: "Model selection, extended thinking, and effort.",
|
|
43
|
-
workflow: "Git, plans, permissions, output style, and languages.",
|
|
44
|
-
terminal: "Shell, spinners, progress bars, voice, and UI behavior.",
|
|
45
|
-
performance: "Compaction, token limits, timeouts, and caching.",
|
|
46
|
-
advanced: "Telemetry, updates, debugging, and internal controls.",
|
|
47
|
-
};
|
|
48
|
-
// ─── Helpers ───────────────────────────────────────────────────────────────────
|
|
49
|
-
/** Get the effective value (project overrides user) */
|
|
50
|
-
export function getEffectiveValue(scoped) {
|
|
51
|
-
return scoped.project !== undefined ? scoped.project : scoped.user;
|
|
52
|
-
}
|
|
53
|
-
export function formatValue(setting, value) {
|
|
54
|
-
if (value === undefined || value === "") {
|
|
55
|
-
if (setting.type === "boolean" && setting.defaultValue !== undefined) {
|
|
56
|
-
return setting.defaultValue === "true" ? "on" : "off";
|
|
57
|
-
}
|
|
58
|
-
if (setting.type === "select" && setting.defaultValue !== undefined) {
|
|
59
|
-
const opt = setting.options?.find((o) => o.value === setting.defaultValue);
|
|
60
|
-
return opt ? opt.label : setting.defaultValue;
|
|
61
|
-
}
|
|
62
|
-
return "—";
|
|
63
|
-
}
|
|
64
|
-
if (setting.type === "boolean") {
|
|
65
|
-
return value === "true" || value === "1" ? "on" : "off";
|
|
66
|
-
}
|
|
67
|
-
if (setting.type === "select" && setting.options) {
|
|
68
|
-
const opt = setting.options.find((o) => o.value === value);
|
|
69
|
-
return opt ? opt.label : value;
|
|
70
|
-
}
|
|
71
|
-
if (value.length > 12) {
|
|
72
|
-
return value.slice(0, 12) + "…";
|
|
73
|
-
}
|
|
74
|
-
return value;
|
|
75
|
-
}
|
|
76
|
-
// ─── Adapter ───────────────────────────────────────────────────────────────────
|
|
77
|
-
/**
|
|
78
|
-
* Builds the flat list of items for the SettingsScreen list panel.
|
|
79
|
-
* Extracted from SettingsScreen so it can be tested independently.
|
|
80
|
-
*/
|
|
81
|
-
export function buildSettingsBrowserItems(values) {
|
|
82
|
-
const items = [];
|
|
83
|
-
for (const category of CATEGORY_ORDER) {
|
|
84
|
-
items.push({
|
|
85
|
-
id: `cat:${category}`,
|
|
86
|
-
kind: "category",
|
|
87
|
-
label: CATEGORY_LABELS[category],
|
|
88
|
-
category,
|
|
89
|
-
isDefault: true,
|
|
90
|
-
});
|
|
91
|
-
const categorySettings = SETTINGS_CATALOG.filter((s) => s.category === category);
|
|
92
|
-
for (const setting of categorySettings) {
|
|
93
|
-
const scoped = values.get(setting.id) || {
|
|
94
|
-
user: undefined,
|
|
95
|
-
project: undefined,
|
|
96
|
-
};
|
|
97
|
-
const effective = getEffectiveValue(scoped);
|
|
98
|
-
items.push({
|
|
99
|
-
id: `setting:${setting.id}`,
|
|
100
|
-
kind: "setting",
|
|
101
|
-
label: setting.name,
|
|
102
|
-
category,
|
|
103
|
-
setting,
|
|
104
|
-
scopedValues: scoped,
|
|
105
|
-
effectiveValue: effective,
|
|
106
|
-
isDefault: effective === undefined || effective === "",
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return items;
|
|
111
|
-
}
|