vskill 0.1.10 → 0.1.12
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/README.md +15 -1
- package/dist/commands/add-wizard.test.d.ts +1 -0
- package/dist/commands/add-wizard.test.js +285 -0
- package/dist/commands/add-wizard.test.js.map +1 -0
- package/dist/commands/add.d.ts +1 -0
- package/dist/commands/add.js +108 -22
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/add.test.js +6 -1
- package/dist/commands/add.test.js.map +1 -1
- package/dist/commands/audit.d.ts +2 -2
- package/dist/commands/audit.js +36 -49
- package/dist/commands/audit.js.map +1 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/installer/canonical.d.ts +9 -0
- package/dist/installer/canonical.js +63 -0
- package/dist/installer/canonical.js.map +1 -0
- package/dist/installer/canonical.test.d.ts +1 -0
- package/dist/installer/canonical.test.js +117 -0
- package/dist/installer/canonical.test.js.map +1 -0
- package/dist/scanner/patterns.js +2 -2
- package/dist/scanner/patterns.js.map +1 -1
- package/dist/utils/prompts.d.ts +20 -0
- package/dist/utils/prompts.js +103 -0
- package/dist/utils/prompts.js.map +1 -0
- package/dist/utils/prompts.test.d.ts +1 -0
- package/dist/utils/prompts.test.js +155 -0
- package/dist/utils/prompts.test.js.map +1 -0
- package/dist/utils/validation.js +1 -1
- package/dist/utils/validation.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,9 +3,21 @@
|
|
|
3
3
|
**Secure multi-platform AI skill installer.** Scan before you install.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
+
# npm
|
|
6
7
|
npx vskill find remotion # search the registry
|
|
7
8
|
npx vskill install google/remotion # install after security scan
|
|
8
|
-
|
|
9
|
+
|
|
10
|
+
# bun
|
|
11
|
+
bunx vskill find remotion
|
|
12
|
+
bunx vskill install google/remotion
|
|
13
|
+
|
|
14
|
+
# pnpm
|
|
15
|
+
pnpx vskill find remotion
|
|
16
|
+
pnpx vskill install google/remotion
|
|
17
|
+
|
|
18
|
+
# yarn
|
|
19
|
+
yarn dlx vskill find remotion
|
|
20
|
+
yarn dlx vskill install google/remotion
|
|
9
21
|
```
|
|
10
22
|
|
|
11
23
|
## Why?
|
|
@@ -27,6 +39,8 @@ vskill submit <source> # Submit for verification (owner/repo or GitHub URL
|
|
|
27
39
|
vskill update # Update with diff scanning
|
|
28
40
|
```
|
|
29
41
|
|
|
42
|
+
> Replace `vskill` with `npx vskill`, `bunx vskill`, `pnpx vskill`, or `yarn dlx vskill` if not installed globally.
|
|
43
|
+
|
|
30
44
|
## 39 Agent Platforms
|
|
31
45
|
|
|
32
46
|
Works across Claude Code, Cursor, GitHub Copilot, Windsurf, Codex, Gemini CLI, Cline, Amp, Roo Code, and 30 more.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// Mock node:fs
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
const mockMkdirSync = vi.fn();
|
|
6
|
+
const mockWriteFileSync = vi.fn();
|
|
7
|
+
const mockReadFileSync = vi.fn();
|
|
8
|
+
const mockExistsSync = vi.fn();
|
|
9
|
+
const mockChmodSync = vi.fn();
|
|
10
|
+
const mockReaddirSync = vi.fn();
|
|
11
|
+
const mockStatSync = vi.fn();
|
|
12
|
+
const mockCopyFileSync = vi.fn();
|
|
13
|
+
const mockRmSync = vi.fn();
|
|
14
|
+
vi.mock("node:fs", () => ({
|
|
15
|
+
mkdirSync: (...args) => mockMkdirSync(...args),
|
|
16
|
+
writeFileSync: (...args) => mockWriteFileSync(...args),
|
|
17
|
+
readFileSync: (...args) => mockReadFileSync(...args),
|
|
18
|
+
existsSync: (...args) => mockExistsSync(...args),
|
|
19
|
+
chmodSync: (...args) => mockChmodSync(...args),
|
|
20
|
+
readdirSync: (...args) => mockReaddirSync(...args),
|
|
21
|
+
statSync: (...args) => mockStatSync(...args),
|
|
22
|
+
copyFileSync: (...args) => mockCopyFileSync(...args),
|
|
23
|
+
rmSync: (...args) => mockRmSync(...args),
|
|
24
|
+
}));
|
|
25
|
+
vi.mock("node:path", async () => {
|
|
26
|
+
const actual = await vi.importActual("node:path");
|
|
27
|
+
return { ...actual };
|
|
28
|
+
});
|
|
29
|
+
const mockDigest = vi.fn().mockReturnValue("abcdef123456xxxx");
|
|
30
|
+
const mockUpdate = vi.fn().mockReturnValue({ digest: mockDigest });
|
|
31
|
+
vi.mock("node:crypto", () => ({
|
|
32
|
+
createHash: () => ({ update: mockUpdate }),
|
|
33
|
+
}));
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Mock agents registry
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
const mockDetectInstalledAgents = vi.fn();
|
|
38
|
+
vi.mock("../agents/agents-registry.js", () => ({
|
|
39
|
+
detectInstalledAgents: (...args) => mockDetectInstalledAgents(...args),
|
|
40
|
+
}));
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Mock lockfile
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
const mockEnsureLockfile = vi.fn();
|
|
45
|
+
const mockWriteLockfile = vi.fn();
|
|
46
|
+
vi.mock("../lockfile/index.js", () => ({
|
|
47
|
+
ensureLockfile: (...args) => mockEnsureLockfile(...args),
|
|
48
|
+
writeLockfile: (...args) => mockWriteLockfile(...args),
|
|
49
|
+
}));
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Mock scanner, blocklist, security, API client, discovery
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
const mockRunTier1Scan = vi.fn();
|
|
54
|
+
vi.mock("../scanner/index.js", () => ({
|
|
55
|
+
runTier1Scan: (...args) => mockRunTier1Scan(...args),
|
|
56
|
+
}));
|
|
57
|
+
const mockCheckBlocklist = vi.fn();
|
|
58
|
+
vi.mock("../blocklist/blocklist.js", () => ({
|
|
59
|
+
checkBlocklist: (...args) => mockCheckBlocklist(...args),
|
|
60
|
+
}));
|
|
61
|
+
const mockCheckPlatformSecurity = vi.fn();
|
|
62
|
+
vi.mock("../security/index.js", () => ({
|
|
63
|
+
checkPlatformSecurity: (...args) => mockCheckPlatformSecurity(...args),
|
|
64
|
+
}));
|
|
65
|
+
const mockGetSkill = vi.fn();
|
|
66
|
+
vi.mock("../api/client.js", () => ({
|
|
67
|
+
getSkill: (...args) => mockGetSkill(...args),
|
|
68
|
+
}));
|
|
69
|
+
const mockDiscoverSkills = vi.fn();
|
|
70
|
+
vi.mock("../discovery/github-tree.js", () => ({
|
|
71
|
+
discoverSkills: (...args) => mockDiscoverSkills(...args),
|
|
72
|
+
}));
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// Mock project root & agent filter
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
const mockFindProjectRoot = vi.fn();
|
|
77
|
+
vi.mock("../utils/project-root.js", () => ({
|
|
78
|
+
findProjectRoot: (...args) => mockFindProjectRoot(...args),
|
|
79
|
+
}));
|
|
80
|
+
const mockFilterAgents = vi.fn();
|
|
81
|
+
vi.mock("../utils/agent-filter.js", () => ({
|
|
82
|
+
filterAgents: (...args) => mockFilterAgents(...args),
|
|
83
|
+
}));
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Mock output (suppress ANSI output)
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
vi.mock("../utils/output.js", () => ({
|
|
88
|
+
bold: (s) => s,
|
|
89
|
+
green: (s) => s,
|
|
90
|
+
red: (s) => s,
|
|
91
|
+
yellow: (s) => s,
|
|
92
|
+
dim: (s) => s,
|
|
93
|
+
cyan: (s) => s,
|
|
94
|
+
spinner: () => ({ stop: vi.fn() }),
|
|
95
|
+
}));
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// Mock prompts (to test wizard integration without real readline)
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
const mockPromptCheckboxList = vi.fn();
|
|
100
|
+
const mockPromptChoice = vi.fn();
|
|
101
|
+
const mockPromptConfirm = vi.fn();
|
|
102
|
+
const mockIsTTY = vi.fn();
|
|
103
|
+
vi.mock("../utils/prompts.js", () => ({
|
|
104
|
+
isTTY: (...args) => mockIsTTY(...args),
|
|
105
|
+
createPrompter: () => ({
|
|
106
|
+
promptCheckboxList: (...args) => mockPromptCheckboxList(...args),
|
|
107
|
+
promptChoice: (...args) => mockPromptChoice(...args),
|
|
108
|
+
promptConfirm: (...args) => mockPromptConfirm(...args),
|
|
109
|
+
}),
|
|
110
|
+
}));
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// Mock canonical installer
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
const mockInstallSymlink = vi.fn();
|
|
115
|
+
const mockInstallCopy = vi.fn();
|
|
116
|
+
vi.mock("../installer/canonical.js", () => ({
|
|
117
|
+
installSymlink: (...args) => mockInstallSymlink(...args),
|
|
118
|
+
installCopy: (...args) => mockInstallCopy(...args),
|
|
119
|
+
}));
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
// Import module under test AFTER mocks
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
const { addCommand } = await import("./add.js");
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// Helpers
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
function makeScanResult(overrides = {}) {
|
|
128
|
+
return {
|
|
129
|
+
verdict: "PASS",
|
|
130
|
+
findings: [],
|
|
131
|
+
score: 100,
|
|
132
|
+
patternsChecked: 37,
|
|
133
|
+
criticalCount: 0,
|
|
134
|
+
highCount: 0,
|
|
135
|
+
mediumCount: 0,
|
|
136
|
+
lowCount: 0,
|
|
137
|
+
infoCount: 0,
|
|
138
|
+
durationMs: 1,
|
|
139
|
+
...overrides,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function makeAgent(overrides = {}) {
|
|
143
|
+
return {
|
|
144
|
+
id: "claude-code",
|
|
145
|
+
displayName: "Claude Code",
|
|
146
|
+
localSkillsDir: ".claude/commands",
|
|
147
|
+
globalSkillsDir: "~/.claude/commands",
|
|
148
|
+
...overrides,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const originalFetch = globalThis.fetch;
|
|
152
|
+
beforeEach(() => {
|
|
153
|
+
vi.clearAllMocks();
|
|
154
|
+
vi.spyOn(console, "log").mockImplementation(() => { });
|
|
155
|
+
vi.spyOn(console, "error").mockImplementation(() => { });
|
|
156
|
+
// Default: safe skill content
|
|
157
|
+
globalThis.fetch = vi.fn().mockResolvedValue({
|
|
158
|
+
ok: true,
|
|
159
|
+
text: async () => "# Safe Skill\nNormal content",
|
|
160
|
+
});
|
|
161
|
+
mockRunTier1Scan.mockReturnValue(makeScanResult());
|
|
162
|
+
mockCheckBlocklist.mockResolvedValue(null);
|
|
163
|
+
mockCheckPlatformSecurity.mockResolvedValue(null);
|
|
164
|
+
mockEnsureLockfile.mockReturnValue({ skills: {}, agents: [] });
|
|
165
|
+
mockFindProjectRoot.mockReturnValue("/projects/myapp");
|
|
166
|
+
mockFilterAgents.mockImplementation((agents) => agents);
|
|
167
|
+
// Default: TTY
|
|
168
|
+
mockIsTTY.mockReturnValue(true);
|
|
169
|
+
});
|
|
170
|
+
afterEach(() => {
|
|
171
|
+
globalThis.fetch = originalFetch;
|
|
172
|
+
});
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
// Tests: --yes flag and non-interactive mode
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
describe("wizard integration: --yes flag", () => {
|
|
177
|
+
it("skips all prompts when --yes is set", async () => {
|
|
178
|
+
const agents = [makeAgent(), makeAgent({ id: "cursor", localSkillsDir: ".cursor/skills" })];
|
|
179
|
+
mockDetectInstalledAgents.mockResolvedValue(agents);
|
|
180
|
+
mockDiscoverSkills.mockResolvedValue([
|
|
181
|
+
{ name: "skill-a", rawUrl: "https://raw.githubusercontent.com/o/r/main/skills/skill-a/SKILL.md" },
|
|
182
|
+
{ name: "skill-b", rawUrl: "https://raw.githubusercontent.com/o/r/main/skills/skill-b/SKILL.md" },
|
|
183
|
+
]);
|
|
184
|
+
await addCommand("owner/repo", { yes: true });
|
|
185
|
+
// No prompt functions should have been called
|
|
186
|
+
expect(mockPromptCheckboxList).not.toHaveBeenCalled();
|
|
187
|
+
expect(mockPromptChoice).not.toHaveBeenCalled();
|
|
188
|
+
expect(mockPromptConfirm).not.toHaveBeenCalled();
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
describe("wizard integration: non-TTY mode", () => {
|
|
192
|
+
it("skips prompts when stdin is not a TTY", async () => {
|
|
193
|
+
mockIsTTY.mockReturnValue(false);
|
|
194
|
+
const agents = [makeAgent()];
|
|
195
|
+
mockDetectInstalledAgents.mockResolvedValue(agents);
|
|
196
|
+
mockDiscoverSkills.mockResolvedValue([
|
|
197
|
+
{ name: "skill-a", rawUrl: "https://raw.githubusercontent.com/o/r/main/skills/skill-a/SKILL.md" },
|
|
198
|
+
{ name: "skill-b", rawUrl: "https://raw.githubusercontent.com/o/r/main/skills/skill-b/SKILL.md" },
|
|
199
|
+
]);
|
|
200
|
+
await addCommand("owner/repo", {});
|
|
201
|
+
expect(mockPromptCheckboxList).not.toHaveBeenCalled();
|
|
202
|
+
expect(mockPromptChoice).not.toHaveBeenCalled();
|
|
203
|
+
expect(mockPromptConfirm).not.toHaveBeenCalled();
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
describe("wizard integration: single skill skips wizard", () => {
|
|
207
|
+
it("does not show wizard for single-skill repos", async () => {
|
|
208
|
+
const agents = [makeAgent()];
|
|
209
|
+
mockDetectInstalledAgents.mockResolvedValue(agents);
|
|
210
|
+
mockDiscoverSkills.mockResolvedValue([
|
|
211
|
+
{ name: "only-skill", rawUrl: "https://raw.githubusercontent.com/o/r/main/SKILL.md" },
|
|
212
|
+
]);
|
|
213
|
+
await addCommand("owner/repo", {});
|
|
214
|
+
// Single skill: no wizard
|
|
215
|
+
expect(mockPromptCheckboxList).not.toHaveBeenCalled();
|
|
216
|
+
expect(mockPromptChoice).not.toHaveBeenCalled();
|
|
217
|
+
expect(mockPromptConfirm).not.toHaveBeenCalled();
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
describe("wizard integration: --agent flag skips agent selection", () => {
|
|
221
|
+
it("does not prompt for agents when --agent is provided", async () => {
|
|
222
|
+
const agents = [makeAgent()];
|
|
223
|
+
mockDetectInstalledAgents.mockResolvedValue(agents);
|
|
224
|
+
mockDiscoverSkills.mockResolvedValue([
|
|
225
|
+
{ name: "skill-a", rawUrl: "https://raw.githubusercontent.com/o/r/main/skills/skill-a/SKILL.md" },
|
|
226
|
+
{ name: "skill-b", rawUrl: "https://raw.githubusercontent.com/o/r/main/skills/skill-b/SKILL.md" },
|
|
227
|
+
]);
|
|
228
|
+
// Wizard prompts: select all skills, skip agent (because --agent), project scope, symlink method, confirm
|
|
229
|
+
mockPromptCheckboxList.mockResolvedValue([0, 1]); // select all skills
|
|
230
|
+
mockPromptChoice
|
|
231
|
+
.mockResolvedValueOnce(0) // scope: project
|
|
232
|
+
.mockResolvedValueOnce(0); // method: symlink
|
|
233
|
+
mockPromptConfirm.mockResolvedValue(true);
|
|
234
|
+
await addCommand("owner/repo", { agent: ["claude-code"] });
|
|
235
|
+
// Agent checkbox should NOT be called (--agent flag)
|
|
236
|
+
// Skill checkbox IS called (multi-skill)
|
|
237
|
+
expect(mockPromptCheckboxList).toHaveBeenCalledTimes(1);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
describe("wizard integration: --global flag skips scope selection", () => {
|
|
241
|
+
it("does not prompt for scope when --global is provided", async () => {
|
|
242
|
+
const agents = [makeAgent()];
|
|
243
|
+
mockDetectInstalledAgents.mockResolvedValue(agents);
|
|
244
|
+
mockDiscoverSkills.mockResolvedValue([
|
|
245
|
+
{ name: "skill-a", rawUrl: "https://raw.githubusercontent.com/o/r/main/skills/skill-a/SKILL.md" },
|
|
246
|
+
{ name: "skill-b", rawUrl: "https://raw.githubusercontent.com/o/r/main/skills/skill-b/SKILL.md" },
|
|
247
|
+
]);
|
|
248
|
+
// Wizard: skills, agents, (no scope), method, confirm
|
|
249
|
+
mockPromptCheckboxList
|
|
250
|
+
.mockResolvedValueOnce([0, 1]) // skills
|
|
251
|
+
.mockResolvedValueOnce([0]); // agents
|
|
252
|
+
mockPromptChoice.mockResolvedValueOnce(0); // method: symlink (no scope prompt)
|
|
253
|
+
mockPromptConfirm.mockResolvedValue(true);
|
|
254
|
+
await addCommand("owner/repo", { global: true });
|
|
255
|
+
// Only 1 promptChoice call (method), not 2 (scope + method)
|
|
256
|
+
expect(mockPromptChoice).toHaveBeenCalledTimes(1);
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
describe("wizard integration: abort at confirmation", () => {
|
|
260
|
+
it("exits cleanly when user declines confirmation", async () => {
|
|
261
|
+
const agents = [makeAgent()];
|
|
262
|
+
mockDetectInstalledAgents.mockResolvedValue(agents);
|
|
263
|
+
mockDiscoverSkills.mockResolvedValue([
|
|
264
|
+
{ name: "skill-a", rawUrl: "https://raw.githubusercontent.com/o/r/main/skills/skill-a/SKILL.md" },
|
|
265
|
+
{ name: "skill-b", rawUrl: "https://raw.githubusercontent.com/o/r/main/skills/skill-b/SKILL.md" },
|
|
266
|
+
]);
|
|
267
|
+
mockPromptCheckboxList
|
|
268
|
+
.mockResolvedValueOnce([0, 1])
|
|
269
|
+
.mockResolvedValueOnce([0]);
|
|
270
|
+
mockPromptChoice
|
|
271
|
+
.mockResolvedValueOnce(0)
|
|
272
|
+
.mockResolvedValueOnce(0);
|
|
273
|
+
mockPromptConfirm.mockResolvedValue(false); // user says NO
|
|
274
|
+
const mockExit = vi.spyOn(process, "exit").mockImplementation(() => {
|
|
275
|
+
throw new Error("process.exit");
|
|
276
|
+
});
|
|
277
|
+
await expect(addCommand("owner/repo", {})).rejects.toThrow("process.exit");
|
|
278
|
+
mockExit.mockRestore();
|
|
279
|
+
// No skills should have been installed
|
|
280
|
+
expect(mockWriteFileSync).not.toHaveBeenCalled();
|
|
281
|
+
expect(mockInstallSymlink).not.toHaveBeenCalled();
|
|
282
|
+
expect(mockInstallCopy).not.toHaveBeenCalled();
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
//# sourceMappingURL=add-wizard.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add-wizard.test.js","sourceRoot":"","sources":["../../src/commands/add-wizard.test.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/add.d.ts
CHANGED
package/dist/commands/add.js
CHANGED
|
@@ -16,7 +16,9 @@ import { checkBlocklist } from "../blocklist/blocklist.js";
|
|
|
16
16
|
import { getSkill } from "../api/client.js";
|
|
17
17
|
import { checkPlatformSecurity } from "../security/index.js";
|
|
18
18
|
import { discoverSkills } from "../discovery/github-tree.js";
|
|
19
|
+
import { parseGitHubSource } from "../utils/validation.js";
|
|
19
20
|
import { bold, green, red, yellow, dim, cyan, spinner, } from "../utils/output.js";
|
|
21
|
+
import { isTTY, createPrompter } from "../utils/prompts.js";
|
|
20
22
|
// ---------------------------------------------------------------------------
|
|
21
23
|
// Command file filter (prevents plugin internals leaking as slash commands)
|
|
22
24
|
// ---------------------------------------------------------------------------
|
|
@@ -263,12 +265,17 @@ async function installPluginDir(basePath, pluginName, opts) {
|
|
|
263
265
|
// Detect installed agents and apply --agent filter
|
|
264
266
|
let agents = await detectInstalledAgents();
|
|
265
267
|
if (agents.length === 0) {
|
|
266
|
-
console.error(red("No AI agents detected.
|
|
267
|
-
cyan("vskill init") +
|
|
268
|
-
red(" first."));
|
|
268
|
+
console.error(red("No AI agents detected. Install Claude Code, Cursor, or another supported agent and try again."));
|
|
269
269
|
process.exit(1);
|
|
270
270
|
}
|
|
271
|
-
|
|
271
|
+
try {
|
|
272
|
+
agents = filterAgents(agents, opts.agent);
|
|
273
|
+
}
|
|
274
|
+
catch (e) {
|
|
275
|
+
console.error(red(e.message));
|
|
276
|
+
process.exit(1);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
272
279
|
// Read version from marketplace.json
|
|
273
280
|
const marketplacePath = join(basePath, ".claude-plugin", "marketplace.json");
|
|
274
281
|
const marketplaceContent = readFileSync(marketplacePath, "utf-8");
|
|
@@ -311,7 +318,7 @@ async function installPluginDir(basePath, pluginName, opts) {
|
|
|
311
318
|
installedAt: new Date().toISOString(),
|
|
312
319
|
source: `local:${basePath}`,
|
|
313
320
|
};
|
|
314
|
-
lock.agents = agents.map((a) => a.id);
|
|
321
|
+
lock.agents = [...new Set([...(lock.agents || []), ...agents.map((a) => a.id)])];
|
|
315
322
|
writeLockfile(lock);
|
|
316
323
|
// Print summary
|
|
317
324
|
console.log(green(`\nInstalled ${bold(pluginName)} to ${locations.length} agent${locations.length === 1 ? "" : "s"}:\n`));
|
|
@@ -376,6 +383,11 @@ export async function addCommand(source, opts) {
|
|
|
376
383
|
if (opts.pluginDir && opts.plugin) {
|
|
377
384
|
return installPluginDir(opts.pluginDir, opts.plugin, opts);
|
|
378
385
|
}
|
|
386
|
+
// Normalize full GitHub URLs to owner/repo shorthand
|
|
387
|
+
const parsed = parseGitHubSource(source);
|
|
388
|
+
if (parsed) {
|
|
389
|
+
source = `${parsed.owner}/${parsed.repo}`;
|
|
390
|
+
}
|
|
379
391
|
// GitHub mode: owner/repo
|
|
380
392
|
const parts = source.split("/");
|
|
381
393
|
if (parts.length !== 2) {
|
|
@@ -393,17 +405,81 @@ export async function addCommand(source, opts) {
|
|
|
393
405
|
// Fallback: discovery returned nothing — try root SKILL.md directly
|
|
394
406
|
return installSingleSkillLegacy(owner, repo, undefined, opts);
|
|
395
407
|
}
|
|
396
|
-
//
|
|
408
|
+
// Detect agents
|
|
397
409
|
let agents = await detectInstalledAgents();
|
|
398
410
|
if (agents.length === 0) {
|
|
399
|
-
console.error(red("No AI agents detected.
|
|
411
|
+
console.error(red("No AI agents detected. Install Claude Code, Cursor, or another supported agent and try again."));
|
|
412
|
+
process.exit(1);
|
|
413
|
+
}
|
|
414
|
+
try {
|
|
415
|
+
agents = filterAgents(agents, opts.agent);
|
|
416
|
+
}
|
|
417
|
+
catch (e) {
|
|
418
|
+
console.error(red(e.message));
|
|
400
419
|
process.exit(1);
|
|
420
|
+
return;
|
|
401
421
|
}
|
|
402
|
-
|
|
422
|
+
// Determine selected skills, agents, scope, and method
|
|
423
|
+
let selectedSkills = discovered;
|
|
424
|
+
let selectedAgents = agents;
|
|
425
|
+
let useGlobal = !!opts.global;
|
|
426
|
+
let method = "symlink";
|
|
427
|
+
const interactive = discovered.length > 1 && isTTY() && !opts.yes;
|
|
428
|
+
if (interactive) {
|
|
429
|
+
const prompter = createPrompter();
|
|
430
|
+
// Step 1: Skill selection
|
|
431
|
+
const skillIndices = await prompter.promptCheckboxList(discovered.map((s) => ({ label: s.name, checked: true })), { title: "Select skills to install (space to toggle)" });
|
|
432
|
+
selectedSkills = skillIndices.map((i) => discovered[i]);
|
|
433
|
+
if (selectedSkills.length === 0) {
|
|
434
|
+
console.log(dim("No skills selected. Aborting."));
|
|
435
|
+
return process.exit(0);
|
|
436
|
+
}
|
|
437
|
+
// Step 2: Agent selection (skip if --agent flag provided or only 1 agent)
|
|
438
|
+
if (!opts.agent?.length && agents.length > 1) {
|
|
439
|
+
const prompter2 = createPrompter();
|
|
440
|
+
const agentIndices = await prompter2.promptCheckboxList(agents.map((a) => ({ label: a.displayName, description: a.parentCompany, checked: true })), { title: `Detected ${agents.length} agents` });
|
|
441
|
+
selectedAgents = agentIndices.map((i) => agents[i]);
|
|
442
|
+
if (selectedAgents.length === 0) {
|
|
443
|
+
console.log(dim("No agents selected. Aborting."));
|
|
444
|
+
return process.exit(0);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
// Step 3: Scope selection (skip if --global flag provided)
|
|
448
|
+
if (!opts.global) {
|
|
449
|
+
const prompter3 = createPrompter();
|
|
450
|
+
const scopeIdx = await prompter3.promptChoice("Installation scope:", [
|
|
451
|
+
{ label: "Project", hint: "Install in current directory (committed with your project)" },
|
|
452
|
+
{ label: "Global", hint: "Install to ~/.<agent>/ directories" },
|
|
453
|
+
]);
|
|
454
|
+
useGlobal = scopeIdx === 1;
|
|
455
|
+
}
|
|
456
|
+
// Step 4: Install method
|
|
457
|
+
const prompter4 = createPrompter();
|
|
458
|
+
const methodIdx = await prompter4.promptChoice("Installation method:", [
|
|
459
|
+
{ label: "Symlink", hint: "Single source of truth, easy updates" },
|
|
460
|
+
{ label: "Copy", hint: "Independent copies to all agents" },
|
|
461
|
+
]);
|
|
462
|
+
method = methodIdx === 0 ? "symlink" : "copy";
|
|
463
|
+
// Step 5: Summary and confirmation
|
|
464
|
+
console.log(dim("\n--- Installation Summary ---"));
|
|
465
|
+
console.log(` Skills: ${selectedSkills.map((s) => s.name).join(", ")}`);
|
|
466
|
+
console.log(` Agents: ${selectedAgents.map((a) => a.displayName).join(", ")}`);
|
|
467
|
+
console.log(` Scope: ${useGlobal ? "Global" : "Project"}`);
|
|
468
|
+
console.log(` Method: ${method}`);
|
|
469
|
+
const prompter5 = createPrompter();
|
|
470
|
+
const proceed = await prompter5.promptConfirm("\nProceed?", true);
|
|
471
|
+
if (!proceed) {
|
|
472
|
+
return process.exit(0);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
// Override global flag based on wizard choice
|
|
476
|
+
if (useGlobal)
|
|
477
|
+
opts.global = true;
|
|
478
|
+
// Install selected skills
|
|
403
479
|
const results = [];
|
|
404
|
-
for (const skill of
|
|
480
|
+
for (const skill of selectedSkills) {
|
|
405
481
|
console.log(dim(`\nInstalling skill: ${bold(skill.name)}...`));
|
|
406
|
-
const result = await installOneGitHubSkill(owner, repo, skill.name, skill.rawUrl, opts,
|
|
482
|
+
const result = await installOneGitHubSkill(owner, repo, skill.name, skill.rawUrl, opts, selectedAgents);
|
|
407
483
|
results.push(result);
|
|
408
484
|
}
|
|
409
485
|
// Update lockfile with all installed skills
|
|
@@ -419,12 +495,10 @@ export async function addCommand(source, opts) {
|
|
|
419
495
|
};
|
|
420
496
|
}
|
|
421
497
|
}
|
|
422
|
-
lock.agents = agents.map((a) => a.id);
|
|
498
|
+
lock.agents = [...new Set([...(lock.agents || []), ...selectedAgents.map((a) => a.id)])];
|
|
423
499
|
writeLockfile(lock);
|
|
424
500
|
// Summary
|
|
425
|
-
|
|
426
|
-
const skipped = results.filter((r) => !r.installed);
|
|
427
|
-
console.log(green(`\nInstalled ${bold(String(installed.length))} of ${results.length} skills:\n`));
|
|
501
|
+
console.log(green(`\nInstalled ${bold(String(results.filter((r) => r.installed).length))} of ${results.length} skills:\n`));
|
|
428
502
|
for (const r of results) {
|
|
429
503
|
const icon = r.installed ? green("✓") : red("✗");
|
|
430
504
|
const detail = r.installed ? dim(`(${r.verdict})`) : red(`(${r.verdict})`);
|
|
@@ -497,10 +571,17 @@ async function installFromRegistry(skillName, opts) {
|
|
|
497
571
|
// Detect installed agents and apply --agent filter
|
|
498
572
|
let agents = await detectInstalledAgents();
|
|
499
573
|
if (agents.length === 0) {
|
|
500
|
-
console.error(red("No AI agents detected.
|
|
574
|
+
console.error(red("No AI agents detected. Install Claude Code, Cursor, or another supported agent and try again."));
|
|
501
575
|
process.exit(1);
|
|
502
576
|
}
|
|
503
|
-
|
|
577
|
+
try {
|
|
578
|
+
agents = filterAgents(agents, opts.agent);
|
|
579
|
+
}
|
|
580
|
+
catch (e) {
|
|
581
|
+
console.error(red(e.message));
|
|
582
|
+
process.exit(1);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
504
585
|
// Install to each agent
|
|
505
586
|
const sha = createHash("sha256").update(content).digest("hex").slice(0, 12);
|
|
506
587
|
const locations = [];
|
|
@@ -526,7 +607,7 @@ async function installFromRegistry(skillName, opts) {
|
|
|
526
607
|
installedAt: new Date().toISOString(),
|
|
527
608
|
source: `registry:${skillName}`,
|
|
528
609
|
};
|
|
529
|
-
lock.agents = agents.map((a) => a.id);
|
|
610
|
+
lock.agents = [...new Set([...(lock.agents || []), ...agents.map((a) => a.id)])];
|
|
530
611
|
writeLockfile(lock);
|
|
531
612
|
console.log(green(`\nInstalled ${bold(skillName)} to ${locations.length} agent${locations.length === 1 ? "" : "s"}:\n`));
|
|
532
613
|
for (const loc of locations) {
|
|
@@ -612,12 +693,17 @@ async function installSingleSkillLegacy(owner, repo, skill, opts) {
|
|
|
612
693
|
// Detect installed agents and apply --agent filter
|
|
613
694
|
let agents = await detectInstalledAgents();
|
|
614
695
|
if (agents.length === 0) {
|
|
615
|
-
console.error(red("No AI agents detected.
|
|
616
|
-
|
|
617
|
-
|
|
696
|
+
console.error(red("No AI agents detected. Install Claude Code, Cursor, or another supported agent and try again."));
|
|
697
|
+
process.exit(1);
|
|
698
|
+
}
|
|
699
|
+
try {
|
|
700
|
+
agents = filterAgents(agents, opts.agent);
|
|
701
|
+
}
|
|
702
|
+
catch (e) {
|
|
703
|
+
console.error(red(e.message));
|
|
618
704
|
process.exit(1);
|
|
705
|
+
return;
|
|
619
706
|
}
|
|
620
|
-
agents = filterAgents(agents, opts.agent);
|
|
621
707
|
// Install to each agent
|
|
622
708
|
const sha = createHash("sha256").update(content).digest("hex").slice(0, 12);
|
|
623
709
|
const locations = [];
|
|
@@ -643,7 +729,7 @@ async function installSingleSkillLegacy(owner, repo, skill, opts) {
|
|
|
643
729
|
installedAt: new Date().toISOString(),
|
|
644
730
|
source: `github:${owner}/${repo}`,
|
|
645
731
|
};
|
|
646
|
-
lock.agents = agents.map((a) => a.id);
|
|
732
|
+
lock.agents = [...new Set([...(lock.agents || []), ...agents.map((a) => a.id)])];
|
|
647
733
|
writeLockfile(lock);
|
|
648
734
|
// Print summary
|
|
649
735
|
console.log(green(`\nInstalled ${bold(skillName)} to ${locations.length} agent${locations.length === 1 ? "" : "s"}:\n`));
|