claudeup 3.17.0 → 4.0.1
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/data/predefined-profiles.js +69 -12
- package/src/data/predefined-profiles.ts +73 -14
- package/src/services/claude-cli.js +8 -1
- package/src/services/claude-cli.ts +10 -7
- package/src/services/plugin-manager.js +40 -4
- package/src/services/plugin-manager.ts +57 -6
- 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/ScrollableDetail.js +23 -0
- package/src/ui/components/ScrollableDetail.tsx +55 -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/layout/ScreenLayout.js +1 -1
- package/src/ui/components/layout/ScreenLayout.tsx +11 -8
- 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 +177 -0
- package/src/ui/renderers/profileRenderers.tsx +361 -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
package/package.json
CHANGED
|
@@ -1,9 +1,73 @@
|
|
|
1
1
|
export const PREDEFINED_PROFILES = [
|
|
2
|
+
{
|
|
3
|
+
id: "must-have",
|
|
4
|
+
name: "Must Have",
|
|
5
|
+
description: "Essential plugins every Claude Code user should have",
|
|
6
|
+
icon: "★",
|
|
7
|
+
magusPlugins: ["statusline", "multimodel"],
|
|
8
|
+
anthropicPlugins: [
|
|
9
|
+
"claude-code-setup",
|
|
10
|
+
"claude-md-management",
|
|
11
|
+
"code-simplifier",
|
|
12
|
+
"explanatory-output-style",
|
|
13
|
+
"playground",
|
|
14
|
+
"skill-creator",
|
|
15
|
+
],
|
|
16
|
+
skills: ["Find Skills"],
|
|
17
|
+
settings: {
|
|
18
|
+
effortLevel: "high",
|
|
19
|
+
alwaysThinkingEnabled: true,
|
|
20
|
+
outputStyle: "explanatory",
|
|
21
|
+
env: { CLAUDE_CODE_ENABLE_TASKS: "true" },
|
|
22
|
+
respectGitignore: true,
|
|
23
|
+
enableAllProjectMcpServers: true,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: "developer-essentials",
|
|
28
|
+
name: "Developer Essentials",
|
|
29
|
+
description: "Must Have + full dev toolkit: code analysis, browser, terminal, design, review",
|
|
30
|
+
icon: "▶",
|
|
31
|
+
magusPlugins: [
|
|
32
|
+
"statusline",
|
|
33
|
+
"multimodel",
|
|
34
|
+
"code-analysis",
|
|
35
|
+
"dev",
|
|
36
|
+
"browser-use",
|
|
37
|
+
"designer",
|
|
38
|
+
"terminal",
|
|
39
|
+
"kanban",
|
|
40
|
+
],
|
|
41
|
+
anthropicPlugins: [
|
|
42
|
+
"claude-code-setup",
|
|
43
|
+
"claude-md-management",
|
|
44
|
+
"code-simplifier",
|
|
45
|
+
"explanatory-output-style",
|
|
46
|
+
"playground",
|
|
47
|
+
"skill-creator",
|
|
48
|
+
"code-review",
|
|
49
|
+
"commit-commands",
|
|
50
|
+
"feature-dev",
|
|
51
|
+
],
|
|
52
|
+
skills: ["Find Skills", "Systematic Debugging"],
|
|
53
|
+
settings: {
|
|
54
|
+
effortLevel: "high",
|
|
55
|
+
alwaysThinkingEnabled: true,
|
|
56
|
+
outputStyle: "explanatory",
|
|
57
|
+
env: {
|
|
58
|
+
CLAUDE_CODE_ENABLE_TASKS: "true",
|
|
59
|
+
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "true",
|
|
60
|
+
},
|
|
61
|
+
includeGitInstructions: true,
|
|
62
|
+
respectGitignore: true,
|
|
63
|
+
enableAllProjectMcpServers: true,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
2
66
|
{
|
|
3
67
|
id: "frontend-pro",
|
|
4
68
|
name: "Frontend Pro",
|
|
5
69
|
description: "UI implementation, design fidelity, browser-driven workflows",
|
|
6
|
-
icon: "
|
|
70
|
+
icon: "★",
|
|
7
71
|
magusPlugins: [
|
|
8
72
|
"dev",
|
|
9
73
|
"code-analysis",
|
|
@@ -30,7 +94,6 @@ export const PREDEFINED_PROFILES = [
|
|
|
30
94
|
settings: {
|
|
31
95
|
effortLevel: "high",
|
|
32
96
|
alwaysThinkingEnabled: true,
|
|
33
|
-
model: "claude-sonnet-4-6",
|
|
34
97
|
outputStyle: "explanatory",
|
|
35
98
|
env: {
|
|
36
99
|
CLAUDE_CODE_ENABLE_TASKS: "true",
|
|
@@ -45,7 +108,7 @@ export const PREDEFINED_PROFILES = [
|
|
|
45
108
|
id: "backend-forge",
|
|
46
109
|
name: "Backend Forge",
|
|
47
110
|
description: "API development, debugging, code quality, data workflows",
|
|
48
|
-
icon: "
|
|
111
|
+
icon: "★",
|
|
49
112
|
magusPlugins: [
|
|
50
113
|
"dev",
|
|
51
114
|
"code-analysis",
|
|
@@ -61,13 +124,11 @@ export const PREDEFINED_PROFILES = [
|
|
|
61
124
|
"code-simplifier",
|
|
62
125
|
"commit-commands",
|
|
63
126
|
"security-guidance",
|
|
64
|
-
"agent-sdk-dev",
|
|
65
127
|
],
|
|
66
128
|
skills: ["Systematic Debugging", "Neon Postgres", "Find Skills"],
|
|
67
129
|
settings: {
|
|
68
130
|
effortLevel: "high",
|
|
69
131
|
alwaysThinkingEnabled: true,
|
|
70
|
-
model: "claude-sonnet-4-6",
|
|
71
132
|
outputStyle: "concise",
|
|
72
133
|
env: {
|
|
73
134
|
CLAUDE_CODE_ENABLE_TASKS: "true",
|
|
@@ -82,7 +143,7 @@ export const PREDEFINED_PROFILES = [
|
|
|
82
143
|
id: "infra-ops",
|
|
83
144
|
name: "Infra Ops",
|
|
84
145
|
description: "Infrastructure, operational debugging, automation, terminal-first",
|
|
85
|
-
icon: "
|
|
146
|
+
icon: "★",
|
|
86
147
|
magusPlugins: [
|
|
87
148
|
"dev",
|
|
88
149
|
"code-analysis",
|
|
@@ -104,7 +165,6 @@ export const PREDEFINED_PROFILES = [
|
|
|
104
165
|
settings: {
|
|
105
166
|
effortLevel: "high",
|
|
106
167
|
alwaysThinkingEnabled: true,
|
|
107
|
-
model: "claude-opus-4-6",
|
|
108
168
|
outputStyle: "concise",
|
|
109
169
|
env: {
|
|
110
170
|
CLAUDE_CODE_ENABLE_TASKS: "true",
|
|
@@ -119,7 +179,7 @@ export const PREDEFINED_PROFILES = [
|
|
|
119
179
|
id: "growth-marketer",
|
|
120
180
|
name: "Growth Marketer",
|
|
121
181
|
description: "SEO, website audits, content production, marketing automation",
|
|
122
|
-
icon: "
|
|
182
|
+
icon: "★",
|
|
123
183
|
magusPlugins: [
|
|
124
184
|
"dev",
|
|
125
185
|
"code-analysis",
|
|
@@ -143,8 +203,6 @@ export const PREDEFINED_PROFILES = [
|
|
|
143
203
|
],
|
|
144
204
|
settings: {
|
|
145
205
|
effortLevel: "medium",
|
|
146
|
-
alwaysThinkingEnabled: false,
|
|
147
|
-
model: "claude-sonnet-4-6",
|
|
148
206
|
outputStyle: "explanatory",
|
|
149
207
|
env: { CLAUDE_CODE_ENABLE_TASKS: "true" },
|
|
150
208
|
respectGitignore: true,
|
|
@@ -155,7 +213,7 @@ export const PREDEFINED_PROFILES = [
|
|
|
155
213
|
id: "team-lead",
|
|
156
214
|
name: "Team Lead",
|
|
157
215
|
description: "Planning, code review, coordination, and broad repo visibility",
|
|
158
|
-
icon: "
|
|
216
|
+
icon: "★",
|
|
159
217
|
magusPlugins: [
|
|
160
218
|
"dev",
|
|
161
219
|
"code-analysis",
|
|
@@ -177,7 +235,6 @@ export const PREDEFINED_PROFILES = [
|
|
|
177
235
|
settings: {
|
|
178
236
|
effortLevel: "medium",
|
|
179
237
|
alwaysThinkingEnabled: true,
|
|
180
|
-
model: "claude-sonnet-4-6",
|
|
181
238
|
outputStyle: "explanatory",
|
|
182
239
|
env: {
|
|
183
240
|
CLAUDE_CODE_ENABLE_TASKS: "true",
|
|
@@ -2,7 +2,7 @@ export interface PredefinedProfile {
|
|
|
2
2
|
id: string;
|
|
3
3
|
name: string;
|
|
4
4
|
description: string;
|
|
5
|
-
icon: string;
|
|
5
|
+
icon: string;
|
|
6
6
|
magusPlugins: string[];
|
|
7
7
|
anthropicPlugins: string[];
|
|
8
8
|
skills: string[];
|
|
@@ -10,11 +10,77 @@ export interface PredefinedProfile {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export const PREDEFINED_PROFILES: PredefinedProfile[] = [
|
|
13
|
+
{
|
|
14
|
+
id: "must-have",
|
|
15
|
+
name: "Must Have",
|
|
16
|
+
description: "Essential plugins every Claude Code user should have",
|
|
17
|
+
icon: "★",
|
|
18
|
+
magusPlugins: ["statusline", "multimodel"],
|
|
19
|
+
anthropicPlugins: [
|
|
20
|
+
"claude-code-setup",
|
|
21
|
+
"claude-md-management",
|
|
22
|
+
"code-simplifier",
|
|
23
|
+
"explanatory-output-style",
|
|
24
|
+
"playground",
|
|
25
|
+
"skill-creator",
|
|
26
|
+
],
|
|
27
|
+
skills: ["Find Skills"],
|
|
28
|
+
settings: {
|
|
29
|
+
effortLevel: "high",
|
|
30
|
+
alwaysThinkingEnabled: true,
|
|
31
|
+
outputStyle: "explanatory",
|
|
32
|
+
env: { CLAUDE_CODE_ENABLE_TASKS: "true" },
|
|
33
|
+
respectGitignore: true,
|
|
34
|
+
enableAllProjectMcpServers: true,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "developer-essentials",
|
|
39
|
+
name: "Developer Essentials",
|
|
40
|
+
description:
|
|
41
|
+
"Must Have + full dev toolkit: code analysis, browser, terminal, design, review",
|
|
42
|
+
icon: "▶",
|
|
43
|
+
magusPlugins: [
|
|
44
|
+
"statusline",
|
|
45
|
+
"multimodel",
|
|
46
|
+
"code-analysis",
|
|
47
|
+
"dev",
|
|
48
|
+
"browser-use",
|
|
49
|
+
"designer",
|
|
50
|
+
"terminal",
|
|
51
|
+
"kanban",
|
|
52
|
+
],
|
|
53
|
+
anthropicPlugins: [
|
|
54
|
+
"claude-code-setup",
|
|
55
|
+
"claude-md-management",
|
|
56
|
+
"code-simplifier",
|
|
57
|
+
"explanatory-output-style",
|
|
58
|
+
"playground",
|
|
59
|
+
"skill-creator",
|
|
60
|
+
"code-review",
|
|
61
|
+
"commit-commands",
|
|
62
|
+
"feature-dev",
|
|
63
|
+
],
|
|
64
|
+
skills: ["Find Skills", "Systematic Debugging"],
|
|
65
|
+
settings: {
|
|
66
|
+
effortLevel: "high",
|
|
67
|
+
alwaysThinkingEnabled: true,
|
|
68
|
+
outputStyle: "explanatory",
|
|
69
|
+
env: {
|
|
70
|
+
CLAUDE_CODE_ENABLE_TASKS: "true",
|
|
71
|
+
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "true",
|
|
72
|
+
},
|
|
73
|
+
includeGitInstructions: true,
|
|
74
|
+
respectGitignore: true,
|
|
75
|
+
enableAllProjectMcpServers: true,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
13
78
|
{
|
|
14
79
|
id: "frontend-pro",
|
|
15
80
|
name: "Frontend Pro",
|
|
16
|
-
description:
|
|
17
|
-
|
|
81
|
+
description:
|
|
82
|
+
"UI implementation, design fidelity, browser-driven workflows",
|
|
83
|
+
icon: "★",
|
|
18
84
|
magusPlugins: [
|
|
19
85
|
"dev",
|
|
20
86
|
"code-analysis",
|
|
@@ -41,7 +107,6 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
|
|
|
41
107
|
settings: {
|
|
42
108
|
effortLevel: "high",
|
|
43
109
|
alwaysThinkingEnabled: true,
|
|
44
|
-
model: "claude-sonnet-4-6",
|
|
45
110
|
outputStyle: "explanatory",
|
|
46
111
|
env: {
|
|
47
112
|
CLAUDE_CODE_ENABLE_TASKS: "true",
|
|
@@ -56,7 +121,7 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
|
|
|
56
121
|
id: "backend-forge",
|
|
57
122
|
name: "Backend Forge",
|
|
58
123
|
description: "API development, debugging, code quality, data workflows",
|
|
59
|
-
icon: "
|
|
124
|
+
icon: "★",
|
|
60
125
|
magusPlugins: [
|
|
61
126
|
"dev",
|
|
62
127
|
"code-analysis",
|
|
@@ -72,13 +137,11 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
|
|
|
72
137
|
"code-simplifier",
|
|
73
138
|
"commit-commands",
|
|
74
139
|
"security-guidance",
|
|
75
|
-
"agent-sdk-dev",
|
|
76
140
|
],
|
|
77
141
|
skills: ["Systematic Debugging", "Neon Postgres", "Find Skills"],
|
|
78
142
|
settings: {
|
|
79
143
|
effortLevel: "high",
|
|
80
144
|
alwaysThinkingEnabled: true,
|
|
81
|
-
model: "claude-sonnet-4-6",
|
|
82
145
|
outputStyle: "concise",
|
|
83
146
|
env: {
|
|
84
147
|
CLAUDE_CODE_ENABLE_TASKS: "true",
|
|
@@ -94,7 +157,7 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
|
|
|
94
157
|
name: "Infra Ops",
|
|
95
158
|
description:
|
|
96
159
|
"Infrastructure, operational debugging, automation, terminal-first",
|
|
97
|
-
icon: "
|
|
160
|
+
icon: "★",
|
|
98
161
|
magusPlugins: [
|
|
99
162
|
"dev",
|
|
100
163
|
"code-analysis",
|
|
@@ -116,7 +179,6 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
|
|
|
116
179
|
settings: {
|
|
117
180
|
effortLevel: "high",
|
|
118
181
|
alwaysThinkingEnabled: true,
|
|
119
|
-
model: "claude-opus-4-6",
|
|
120
182
|
outputStyle: "concise",
|
|
121
183
|
env: {
|
|
122
184
|
CLAUDE_CODE_ENABLE_TASKS: "true",
|
|
@@ -132,7 +194,7 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
|
|
|
132
194
|
name: "Growth Marketer",
|
|
133
195
|
description:
|
|
134
196
|
"SEO, website audits, content production, marketing automation",
|
|
135
|
-
icon: "
|
|
197
|
+
icon: "★",
|
|
136
198
|
magusPlugins: [
|
|
137
199
|
"dev",
|
|
138
200
|
"code-analysis",
|
|
@@ -156,8 +218,6 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
|
|
|
156
218
|
],
|
|
157
219
|
settings: {
|
|
158
220
|
effortLevel: "medium",
|
|
159
|
-
alwaysThinkingEnabled: false,
|
|
160
|
-
model: "claude-sonnet-4-6",
|
|
161
221
|
outputStyle: "explanatory",
|
|
162
222
|
env: { CLAUDE_CODE_ENABLE_TASKS: "true" },
|
|
163
223
|
respectGitignore: true,
|
|
@@ -169,7 +229,7 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
|
|
|
169
229
|
name: "Team Lead",
|
|
170
230
|
description:
|
|
171
231
|
"Planning, code review, coordination, and broad repo visibility",
|
|
172
|
-
icon: "
|
|
232
|
+
icon: "★",
|
|
173
233
|
magusPlugins: [
|
|
174
234
|
"dev",
|
|
175
235
|
"code-analysis",
|
|
@@ -191,7 +251,6 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
|
|
|
191
251
|
settings: {
|
|
192
252
|
effortLevel: "medium",
|
|
193
253
|
alwaysThinkingEnabled: true,
|
|
194
|
-
model: "claude-sonnet-4-6",
|
|
195
254
|
outputStyle: "explanatory",
|
|
196
255
|
env: {
|
|
197
256
|
CLAUDE_CODE_ENABLE_TASKS: "true",
|
|
@@ -12,7 +12,7 @@ import { execFile } from "node:child_process";
|
|
|
12
12
|
import { promisify } from "node:util";
|
|
13
13
|
import { which } from "../utils/command-utils.js";
|
|
14
14
|
import { removeGlobalInstalledPluginVersion, removeLocalInstalledPluginVersion, } from "./claude-settings.js";
|
|
15
|
-
import { removeInstalledPluginVersion
|
|
15
|
+
import { removeInstalledPluginVersion } from "./plugin-manager.js";
|
|
16
16
|
const execFileAsync = promisify(execFile);
|
|
17
17
|
/**
|
|
18
18
|
* Get the path to the claude CLI binary
|
|
@@ -107,6 +107,13 @@ export async function updatePlugin(pluginId, scope = "user") {
|
|
|
107
107
|
export async function addMarketplace(repo) {
|
|
108
108
|
await execClaude(["plugin", "marketplace", "add", repo], 60000);
|
|
109
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Update marketplace cache by running git pull via Claude CLI
|
|
112
|
+
* Uses longer timeout since this involves git network operations
|
|
113
|
+
*/
|
|
114
|
+
export async function updateMarketplace(name) {
|
|
115
|
+
await execClaude(["plugin", "marketplace", "update", name], 60000);
|
|
116
|
+
}
|
|
110
117
|
/**
|
|
111
118
|
* Check if the claude CLI is available
|
|
112
119
|
* @returns true if claude CLI is found in PATH
|
|
@@ -16,9 +16,7 @@ import {
|
|
|
16
16
|
removeGlobalInstalledPluginVersion,
|
|
17
17
|
removeLocalInstalledPluginVersion,
|
|
18
18
|
} from "./claude-settings.js";
|
|
19
|
-
import {
|
|
20
|
-
removeInstalledPluginVersion,
|
|
21
|
-
} from "./plugin-manager.js";
|
|
19
|
+
import { removeInstalledPluginVersion } from "./plugin-manager.js";
|
|
22
20
|
|
|
23
21
|
const execFileAsync = promisify(execFile);
|
|
24
22
|
|
|
@@ -44,10 +42,7 @@ async function getClaudePath(): Promise<string> {
|
|
|
44
42
|
* @param timeoutMs - Timeout in milliseconds (default: 30s)
|
|
45
43
|
* @returns stdout from the command
|
|
46
44
|
*/
|
|
47
|
-
async function execClaude(
|
|
48
|
-
args: string[],
|
|
49
|
-
timeoutMs = 30000,
|
|
50
|
-
): Promise<string> {
|
|
45
|
+
async function execClaude(args: string[], timeoutMs = 30000): Promise<string> {
|
|
51
46
|
const claudePath = await getClaudePath();
|
|
52
47
|
try {
|
|
53
48
|
const { stdout } = await execFileAsync(claudePath, args, {
|
|
@@ -148,6 +143,14 @@ export async function addMarketplace(repo: string): Promise<void> {
|
|
|
148
143
|
await execClaude(["plugin", "marketplace", "add", repo], 60000);
|
|
149
144
|
}
|
|
150
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Update marketplace cache by running git pull via Claude CLI
|
|
148
|
+
* Uses longer timeout since this involves git network operations
|
|
149
|
+
*/
|
|
150
|
+
export async function updateMarketplace(name: string): Promise<void> {
|
|
151
|
+
await execClaude(["plugin", "marketplace", "update", name], 60000);
|
|
152
|
+
}
|
|
153
|
+
|
|
151
154
|
/**
|
|
152
155
|
* Check if the claude CLI is available
|
|
153
156
|
* @returns true if claude CLI is found in PATH
|
|
@@ -5,6 +5,7 @@ import { getConfiguredMarketplaces, getEnabledPlugins, readSettings, writeSettin
|
|
|
5
5
|
import { defaultMarketplaces } from "../data/marketplaces.js";
|
|
6
6
|
import { scanLocalMarketplaces, repairAllMarketplaces, } from "./local-marketplace.js";
|
|
7
7
|
import { formatMarketplaceName, isValidGitHubRepo, parsePluginId, } from "../utils/string-utils.js";
|
|
8
|
+
import { updateMarketplace } from "./claude-cli.js";
|
|
8
9
|
// Cache for local marketplaces (session-level) - Promise-based to prevent race conditions
|
|
9
10
|
let localMarketplacesPromise = null;
|
|
10
11
|
// Session-level cache for fetched marketplace data (no TTL - persists until explicit refresh)
|
|
@@ -104,12 +105,16 @@ export async function getAvailablePlugins(projectPath) {
|
|
|
104
105
|
marketplaceNames.add(mp.name);
|
|
105
106
|
}
|
|
106
107
|
}
|
|
108
|
+
// Fetch local marketplace caches up front so we can detect stale caches
|
|
109
|
+
let localMarketplaces = await getLocalMarketplaces();
|
|
107
110
|
// Fetch plugins from each configured marketplace
|
|
108
111
|
for (const mpName of marketplaceNames) {
|
|
109
112
|
const marketplace = defaultMarketplaces.find((m) => m.name === mpName);
|
|
110
113
|
if (!marketplace)
|
|
111
114
|
continue;
|
|
112
115
|
const marketplacePlugins = await fetchMarketplacePlugins(mpName, marketplace.source.repo);
|
|
116
|
+
// Auto-sync local cache if remote has plugins the local cache doesn't
|
|
117
|
+
localMarketplaces = await autoSyncIfStale(mpName, marketplacePlugins.map((p) => p.name), localMarketplaces);
|
|
113
118
|
for (const plugin of marketplacePlugins) {
|
|
114
119
|
const pluginId = `${plugin.name}@${mpName}`;
|
|
115
120
|
const installedVersion = installedVersions[pluginId];
|
|
@@ -136,8 +141,6 @@ export async function getAvailablePlugins(projectPath) {
|
|
|
136
141
|
});
|
|
137
142
|
}
|
|
138
143
|
}
|
|
139
|
-
// Fetch ALL plugins from local marketplace caches (for marketplaces not in defaults)
|
|
140
|
-
const localMarketplaces = await getLocalMarketplaces();
|
|
141
144
|
for (const [mpName, localMp] of localMarketplaces) {
|
|
142
145
|
// Skip if already fetched from defaults
|
|
143
146
|
if (marketplaceNames.has(mpName))
|
|
@@ -256,12 +259,16 @@ export async function getGlobalAvailablePlugins() {
|
|
|
256
259
|
marketplaceNames.add(mp.name);
|
|
257
260
|
}
|
|
258
261
|
}
|
|
262
|
+
// Fetch local marketplace caches up front so we can detect stale caches
|
|
263
|
+
let localMarketplaces = await getLocalMarketplaces();
|
|
259
264
|
// Fetch plugins from each configured marketplace
|
|
260
265
|
for (const mpName of marketplaceNames) {
|
|
261
266
|
const marketplace = defaultMarketplaces.find((m) => m.name === mpName);
|
|
262
267
|
if (!marketplace)
|
|
263
268
|
continue;
|
|
264
269
|
const marketplacePlugins = await fetchMarketplacePlugins(mpName, marketplace.source.repo);
|
|
270
|
+
// Auto-sync local cache if remote has plugins the local cache doesn't
|
|
271
|
+
localMarketplaces = await autoSyncIfStale(mpName, marketplacePlugins.map((p) => p.name), localMarketplaces);
|
|
265
272
|
for (const plugin of marketplacePlugins) {
|
|
266
273
|
const pluginId = `${plugin.name}@${mpName}`;
|
|
267
274
|
const installedVersion = installedVersions[pluginId];
|
|
@@ -288,8 +295,6 @@ export async function getGlobalAvailablePlugins() {
|
|
|
288
295
|
});
|
|
289
296
|
}
|
|
290
297
|
}
|
|
291
|
-
// Fetch ALL plugins from local marketplace caches (for marketplaces not in defaults)
|
|
292
|
-
const localMarketplaces = await getLocalMarketplaces();
|
|
293
298
|
for (const [mpName, localMp] of localMarketplaces) {
|
|
294
299
|
// Skip if already fetched from defaults
|
|
295
300
|
if (marketplaceNames.has(mpName))
|
|
@@ -424,6 +429,37 @@ async function getLocalMarketplaces() {
|
|
|
424
429
|
export async function getLocalMarketplacesInfo() {
|
|
425
430
|
return getLocalMarketplaces();
|
|
426
431
|
}
|
|
432
|
+
// Track which marketplaces have already been auto-synced this session
|
|
433
|
+
// so we only attempt the update once per marketplace per claudeup run.
|
|
434
|
+
const autoSyncedMarketplaces = new Set();
|
|
435
|
+
/**
|
|
436
|
+
* If the remote manifest lists plugins that aren't in the local cache,
|
|
437
|
+
* the local clone is stale. Silently run `claude plugin marketplace update`
|
|
438
|
+
* to pull the latest, then invalidate the local cache so the next scan
|
|
439
|
+
* picks up the new plugins.
|
|
440
|
+
*/
|
|
441
|
+
async function autoSyncIfStale(mpName, remotePluginNames, localMarketplaces) {
|
|
442
|
+
if (autoSyncedMarketplaces.has(mpName))
|
|
443
|
+
return localMarketplaces;
|
|
444
|
+
const localMp = localMarketplaces.get(mpName);
|
|
445
|
+
if (!localMp)
|
|
446
|
+
return localMarketplaces;
|
|
447
|
+
const localNames = new Set(localMp.plugins.map((p) => p.name));
|
|
448
|
+
const hasMissing = remotePluginNames.some((name) => !localNames.has(name));
|
|
449
|
+
if (!hasMissing)
|
|
450
|
+
return localMarketplaces;
|
|
451
|
+
autoSyncedMarketplaces.add(mpName);
|
|
452
|
+
try {
|
|
453
|
+
await updateMarketplace(mpName);
|
|
454
|
+
// Invalidate local cache so re-scan picks up new plugins
|
|
455
|
+
localMarketplacesPromise = null;
|
|
456
|
+
return getLocalMarketplaces();
|
|
457
|
+
}
|
|
458
|
+
catch {
|
|
459
|
+
// Update failed (no network, CLI missing, etc.) — continue with stale data
|
|
460
|
+
return localMarketplaces;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
427
463
|
/**
|
|
428
464
|
* Refresh claudeup's internal cache
|
|
429
465
|
* Note: Marketplace updates should be done via Claude Code's /plugin marketplace update command
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
isValidGitHubRepo,
|
|
28
28
|
parsePluginId,
|
|
29
29
|
} from "../utils/string-utils.js";
|
|
30
|
+
import { updateMarketplace } from "./claude-cli.js";
|
|
30
31
|
|
|
31
32
|
// Cache for local marketplaces (session-level) - Promise-based to prevent race conditions
|
|
32
33
|
let localMarketplacesPromise: Promise<Map<string, LocalMarketplace>> | null =
|
|
@@ -206,6 +207,9 @@ export async function getAvailablePlugins(
|
|
|
206
207
|
}
|
|
207
208
|
}
|
|
208
209
|
|
|
210
|
+
// Fetch local marketplace caches up front so we can detect stale caches
|
|
211
|
+
let localMarketplaces = await getLocalMarketplaces();
|
|
212
|
+
|
|
209
213
|
// Fetch plugins from each configured marketplace
|
|
210
214
|
for (const mpName of marketplaceNames) {
|
|
211
215
|
const marketplace = defaultMarketplaces.find((m) => m.name === mpName);
|
|
@@ -216,6 +220,13 @@ export async function getAvailablePlugins(
|
|
|
216
220
|
marketplace.source.repo,
|
|
217
221
|
);
|
|
218
222
|
|
|
223
|
+
// Auto-sync local cache if remote has plugins the local cache doesn't
|
|
224
|
+
localMarketplaces = await autoSyncIfStale(
|
|
225
|
+
mpName,
|
|
226
|
+
marketplacePlugins.map((p) => p.name),
|
|
227
|
+
localMarketplaces,
|
|
228
|
+
);
|
|
229
|
+
|
|
219
230
|
for (const plugin of marketplacePlugins) {
|
|
220
231
|
const pluginId = `${plugin.name}@${mpName}`;
|
|
221
232
|
const installedVersion = installedVersions[pluginId];
|
|
@@ -245,9 +256,6 @@ export async function getAvailablePlugins(
|
|
|
245
256
|
}
|
|
246
257
|
}
|
|
247
258
|
|
|
248
|
-
// Fetch ALL plugins from local marketplace caches (for marketplaces not in defaults)
|
|
249
|
-
const localMarketplaces = await getLocalMarketplaces();
|
|
250
|
-
|
|
251
259
|
for (const [mpName, localMp] of localMarketplaces) {
|
|
252
260
|
// Skip if already fetched from defaults
|
|
253
261
|
if (marketplaceNames.has(mpName)) continue;
|
|
@@ -383,6 +391,9 @@ export async function getGlobalAvailablePlugins(): Promise<PluginInfo[]> {
|
|
|
383
391
|
}
|
|
384
392
|
}
|
|
385
393
|
|
|
394
|
+
// Fetch local marketplace caches up front so we can detect stale caches
|
|
395
|
+
let localMarketplaces = await getLocalMarketplaces();
|
|
396
|
+
|
|
386
397
|
// Fetch plugins from each configured marketplace
|
|
387
398
|
for (const mpName of marketplaceNames) {
|
|
388
399
|
const marketplace = defaultMarketplaces.find((m) => m.name === mpName);
|
|
@@ -393,6 +404,13 @@ export async function getGlobalAvailablePlugins(): Promise<PluginInfo[]> {
|
|
|
393
404
|
marketplace.source.repo,
|
|
394
405
|
);
|
|
395
406
|
|
|
407
|
+
// Auto-sync local cache if remote has plugins the local cache doesn't
|
|
408
|
+
localMarketplaces = await autoSyncIfStale(
|
|
409
|
+
mpName,
|
|
410
|
+
marketplacePlugins.map((p) => p.name),
|
|
411
|
+
localMarketplaces,
|
|
412
|
+
);
|
|
413
|
+
|
|
396
414
|
for (const plugin of marketplacePlugins) {
|
|
397
415
|
const pluginId = `${plugin.name}@${mpName}`;
|
|
398
416
|
const installedVersion = installedVersions[pluginId];
|
|
@@ -422,9 +440,6 @@ export async function getGlobalAvailablePlugins(): Promise<PluginInfo[]> {
|
|
|
422
440
|
}
|
|
423
441
|
}
|
|
424
442
|
|
|
425
|
-
// Fetch ALL plugins from local marketplace caches (for marketplaces not in defaults)
|
|
426
|
-
const localMarketplaces = await getLocalMarketplaces();
|
|
427
|
-
|
|
428
443
|
for (const [mpName, localMp] of localMarketplaces) {
|
|
429
444
|
// Skip if already fetched from defaults
|
|
430
445
|
if (marketplaceNames.has(mpName)) continue;
|
|
@@ -598,6 +613,42 @@ export async function getLocalMarketplacesInfo(): Promise<
|
|
|
598
613
|
return getLocalMarketplaces();
|
|
599
614
|
}
|
|
600
615
|
|
|
616
|
+
// Track which marketplaces have already been auto-synced this session
|
|
617
|
+
// so we only attempt the update once per marketplace per claudeup run.
|
|
618
|
+
const autoSyncedMarketplaces = new Set<string>();
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* If the remote manifest lists plugins that aren't in the local cache,
|
|
622
|
+
* the local clone is stale. Silently run `claude plugin marketplace update`
|
|
623
|
+
* to pull the latest, then invalidate the local cache so the next scan
|
|
624
|
+
* picks up the new plugins.
|
|
625
|
+
*/
|
|
626
|
+
async function autoSyncIfStale(
|
|
627
|
+
mpName: string,
|
|
628
|
+
remotePluginNames: string[],
|
|
629
|
+
localMarketplaces: Map<string, LocalMarketplace>,
|
|
630
|
+
): Promise<Map<string, LocalMarketplace>> {
|
|
631
|
+
if (autoSyncedMarketplaces.has(mpName)) return localMarketplaces;
|
|
632
|
+
|
|
633
|
+
const localMp = localMarketplaces.get(mpName);
|
|
634
|
+
if (!localMp) return localMarketplaces;
|
|
635
|
+
|
|
636
|
+
const localNames = new Set(localMp.plugins.map((p) => p.name));
|
|
637
|
+
const hasMissing = remotePluginNames.some((name) => !localNames.has(name));
|
|
638
|
+
if (!hasMissing) return localMarketplaces;
|
|
639
|
+
|
|
640
|
+
autoSyncedMarketplaces.add(mpName);
|
|
641
|
+
try {
|
|
642
|
+
await updateMarketplace(mpName);
|
|
643
|
+
// Invalidate local cache so re-scan picks up new plugins
|
|
644
|
+
localMarketplacesPromise = null;
|
|
645
|
+
return getLocalMarketplaces();
|
|
646
|
+
} catch {
|
|
647
|
+
// Update failed (no network, CLI missing, etc.) — continue with stale data
|
|
648
|
+
return localMarketplaces;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
601
652
|
export interface RefreshAndRepairResult {
|
|
602
653
|
refresh: never[];
|
|
603
654
|
repair: RepairMarketplaceResult[];
|