claudeup 4.40.1 → 4.41.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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudeup",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.41.0",
|
|
4
4
|
"description": "TUI tool for managing Claude Code plugins, MCPs, and configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/main.tsx",
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
"typescript": "^5.6.3"
|
|
65
65
|
},
|
|
66
66
|
"optionalDependencies": {
|
|
67
|
-
"claudeup-darwin-arm64": "4.
|
|
68
|
-
"claudeup-darwin-x64": "4.
|
|
69
|
-
"claudeup-linux-x64": "4.
|
|
67
|
+
"claudeup-darwin-arm64": "4.41.0",
|
|
68
|
+
"claudeup-darwin-x64": "4.41.0",
|
|
69
|
+
"claudeup-linux-x64": "4.41.0"
|
|
70
70
|
}
|
|
71
71
|
}
|
|
@@ -38,3 +38,87 @@ describe("marketplaces", () => {
|
|
|
38
38
|
expect(names).not.toContain("superpowers");
|
|
39
39
|
});
|
|
40
40
|
});
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The community marketplaces added alongside the Magus channels.
|
|
44
|
+
*
|
|
45
|
+
* `name` is not cosmetic: plugin ids are built as `${plugin.name}@${mpName}`
|
|
46
|
+
* (plugin-manager.ts), so it MUST equal the upstream
|
|
47
|
+
* .claude-plugin/marketplace.json "name" field. A mismatch fails at install
|
|
48
|
+
* time with "not found in marketplace", nowhere near the typo that caused it.
|
|
49
|
+
*
|
|
50
|
+
* Verify against upstream with:
|
|
51
|
+
* gh api repos/<repo>/contents/.claude-plugin/marketplace.json \
|
|
52
|
+
* -H "Accept: application/vnd.github.raw" | jq -r '.name'
|
|
53
|
+
*/
|
|
54
|
+
const COMMUNITY_MARKETPLACES: ReadonlyArray<[name: string, repo: string]> = [
|
|
55
|
+
["mattpocock", "mattpocock/skills"],
|
|
56
|
+
["ecc", "affaan-m/everything-claude-code"],
|
|
57
|
+
["ponytail", "DietrichGebert/ponytail"],
|
|
58
|
+
["addy-agent-skills", "addyosmani/agent-skills"],
|
|
59
|
+
["humanizer", "blader/humanizer"],
|
|
60
|
+
["opendesign", "manalkaff/opendesign"],
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
describe("community marketplaces", () => {
|
|
64
|
+
it.each(COMMUNITY_MARKETPLACES)(
|
|
65
|
+
"%s points at the repo whose manifest declares that name",
|
|
66
|
+
(name, repo) => {
|
|
67
|
+
const mp = defaultMarketplaces.find((m) => m.name === name);
|
|
68
|
+
expect(mp).toBeDefined();
|
|
69
|
+
expect(mp?.source.repo).toBe(repo);
|
|
70
|
+
},
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
it.each(COMMUNITY_MARKETPLACES)(
|
|
74
|
+
"%s is visible by default via the always-show gate",
|
|
75
|
+
(name) => {
|
|
76
|
+
const mp = defaultMarketplaces.find((m) => m.name === name);
|
|
77
|
+
// plugin-manager's gate: configured || official || featured || owned.
|
|
78
|
+
// Without one of these the entry is only a name-to-repo mapping and
|
|
79
|
+
// never appears, which is a silent no-op rather than a failure.
|
|
80
|
+
expect(mp?.official || mp?.featured || mp?.owned).toBe(true);
|
|
81
|
+
},
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
it.each(COMMUNITY_MARKETPLACES)(
|
|
85
|
+
"%s claims neither Anthropic nor MadAppGang ownership",
|
|
86
|
+
(name) => {
|
|
87
|
+
const mp = defaultMarketplaces.find((m) => m.name === name);
|
|
88
|
+
// `official` badges it as Anthropic-managed; `owned` sorts it above
|
|
89
|
+
// everything as a MadAppGang channel. Both would be a false claim.
|
|
90
|
+
expect(mp?.official).toBeUndefined();
|
|
91
|
+
expect(mp?.owned).toBeUndefined();
|
|
92
|
+
},
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
it("introduces no duplicate marketplace name or repo", () => {
|
|
96
|
+
const names = defaultMarketplaces.map((m) => m.name);
|
|
97
|
+
const repos = defaultMarketplaces.map((m) => m.source.repo.toLowerCase());
|
|
98
|
+
|
|
99
|
+
expect(new Set(names).size).toBe(names.length);
|
|
100
|
+
expect(new Set(repos).size).toBe(repos.length);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("surfaces every community marketplace through getAllMarketplaces", () => {
|
|
104
|
+
const names = getAllMarketplaces().map((mp) => mp.name);
|
|
105
|
+
|
|
106
|
+
for (const [name] of COMMUNITY_MARKETPLACES) {
|
|
107
|
+
expect(names).toContain(name);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("keeps the Magus channels sorted above the community ones", () => {
|
|
112
|
+
const order = getAllMarketplaces().map((mp) => mp.name);
|
|
113
|
+
const lastMagus = Math.max(
|
|
114
|
+
order.indexOf("magus"),
|
|
115
|
+
order.indexOf("magus-marketing"),
|
|
116
|
+
order.indexOf("magus-alpha"),
|
|
117
|
+
);
|
|
118
|
+
const firstCommunity = Math.min(
|
|
119
|
+
...COMMUNITY_MARKETPLACES.map(([name]) => order.indexOf(name)),
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
expect(lastMagus).toBeLessThan(firstCommunity);
|
|
123
|
+
});
|
|
124
|
+
});
|
package/src/data/marketplaces.ts
CHANGED
|
@@ -87,6 +87,80 @@ export const defaultMarketplaces: Marketplace[] = [
|
|
|
87
87
|
description: "Curated Claude Code plugins for skills and workflows",
|
|
88
88
|
featured: true,
|
|
89
89
|
},
|
|
90
|
+
// Community marketplaces. Each `name` MUST equal the upstream
|
|
91
|
+
// .claude-plugin/marketplace.json "name" field — plugin ids are built as
|
|
92
|
+
// `${plugin.name}@${marketplaceName}`, so a mismatch here breaks install
|
|
93
|
+
// with a "not found in marketplace" error rather than anything obvious.
|
|
94
|
+
//
|
|
95
|
+
// All carry `featured` because that is the only flag that makes a
|
|
96
|
+
// third-party marketplace visible: the gate in plugin-manager is
|
|
97
|
+
// `configured || official || featured || owned`, and an unflagged entry is
|
|
98
|
+
// just a name→repo mapping for auto-recovery. `official` means Anthropic
|
|
99
|
+
// and `owned` means MadAppGang, so neither applies.
|
|
100
|
+
{
|
|
101
|
+
name: "mattpocock",
|
|
102
|
+
displayName: "Matt Pocock",
|
|
103
|
+
source: {
|
|
104
|
+
source: "github",
|
|
105
|
+
repo: "mattpocock/skills",
|
|
106
|
+
},
|
|
107
|
+
description:
|
|
108
|
+
"Agent skills for real engineering — grilling, spec and ticket flows, TDD, code review, domain modelling",
|
|
109
|
+
featured: true,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: "ecc",
|
|
113
|
+
displayName: "Everything Claude Code",
|
|
114
|
+
source: {
|
|
115
|
+
source: "github",
|
|
116
|
+
repo: "affaan-m/everything-claude-code",
|
|
117
|
+
},
|
|
118
|
+
description:
|
|
119
|
+
"Operator layer for agent harnesses — 68 agents, 286 skills, and command shims",
|
|
120
|
+
featured: true,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: "ponytail",
|
|
124
|
+
displayName: "Ponytail",
|
|
125
|
+
source: {
|
|
126
|
+
source: "github",
|
|
127
|
+
repo: "DietrichGebert/ponytail",
|
|
128
|
+
},
|
|
129
|
+
description:
|
|
130
|
+
"Pushes the agent toward the smallest solution that works — YAGNI, and the standard library before custom code",
|
|
131
|
+
featured: true,
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: "addy-agent-skills",
|
|
135
|
+
displayName: "Addy Osmani Skills",
|
|
136
|
+
source: {
|
|
137
|
+
source: "github",
|
|
138
|
+
repo: "addyosmani/agent-skills",
|
|
139
|
+
},
|
|
140
|
+
description: "Production-grade engineering skills for AI coding agents",
|
|
141
|
+
featured: true,
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: "humanizer",
|
|
145
|
+
displayName: "Humanizer",
|
|
146
|
+
source: {
|
|
147
|
+
source: "github",
|
|
148
|
+
repo: "blader/humanizer",
|
|
149
|
+
},
|
|
150
|
+
description: "Removes signs of AI-generated writing from text",
|
|
151
|
+
featured: true,
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: "opendesign",
|
|
155
|
+
displayName: "OpenDesign",
|
|
156
|
+
source: {
|
|
157
|
+
source: "github",
|
|
158
|
+
repo: "manalkaff/opendesign",
|
|
159
|
+
},
|
|
160
|
+
description:
|
|
161
|
+
"Designs and redesigns frontend UI and marketing graphics, routing to specialist skills per artifact type",
|
|
162
|
+
featured: true,
|
|
163
|
+
},
|
|
90
164
|
{
|
|
91
165
|
name: "claude-code-plugins",
|
|
92
166
|
displayName: "Anthropic Deprecated",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import { readFile, writeFile, stat, rename, unlink, open } from 'node:fs/promises';
|
|
17
|
+
import { existsSync } from 'node:fs';
|
|
17
18
|
import { randomUUID } from 'node:crypto';
|
|
18
19
|
import { dirname, basename, join, resolve } from 'node:path';
|
|
19
20
|
import { homedir } from 'node:os';
|
|
@@ -692,7 +693,11 @@ export async function resolveGlobalGitignorePath(): Promise<string | null> {
|
|
|
692
693
|
export async function resolvePluginConventions(
|
|
693
694
|
pluginPath: string,
|
|
694
695
|
): Promise<PluginConventions | null> {
|
|
695
|
-
|
|
696
|
+
// `.claude-plugin/plugin.json` is the location Claude Code reads; a root-level
|
|
697
|
+
// manifest is inert to the runtime but still worth reading for a third-party plugin
|
|
698
|
+
// whose author put it there.
|
|
699
|
+
const nested = join(pluginPath, '.claude-plugin', 'plugin.json');
|
|
700
|
+
const manifestPath = existsSync(nested) ? nested : join(pluginPath, 'plugin.json');
|
|
696
701
|
|
|
697
702
|
try {
|
|
698
703
|
const raw = await readFile(manifestPath, 'utf-8');
|
|
@@ -156,15 +156,19 @@ export function parsePluginRequires(pluginJson: unknown): PluginRequires {
|
|
|
156
156
|
export async function readPluginRequires(
|
|
157
157
|
pluginPath: string,
|
|
158
158
|
): Promise<PluginRequires> {
|
|
159
|
-
|
|
159
|
+
// `.claude-plugin/plugin.json` FIRST — that is the only location Claude Code's
|
|
160
|
+
// runtime loader reads, and the only one its documentation describes. The root
|
|
161
|
+
// fallback stays for third-party plugins whose authors put it there; such a manifest
|
|
162
|
+
// is inert as far as Claude Code is concerned, but claudeup can still read metadata
|
|
163
|
+
// out of it rather than reporting nothing.
|
|
164
|
+
const manifestPath = path.join(pluginPath, ".claude-plugin", "plugin.json");
|
|
160
165
|
try {
|
|
161
166
|
if (await fs.pathExists(manifestPath)) {
|
|
162
167
|
return parsePluginRequires(await fs.readJson(manifestPath));
|
|
163
168
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
return parsePluginRequires(await fs.readJson(nested));
|
|
169
|
+
const rootLevel = path.join(pluginPath, "plugin.json");
|
|
170
|
+
if (await fs.pathExists(rootLevel)) {
|
|
171
|
+
return parsePluginRequires(await fs.readJson(rootLevel));
|
|
168
172
|
}
|
|
169
173
|
} catch {
|
|
170
174
|
// Unreadable/malformed — treat as no requirements.
|