@codemcp/agentskills-cli 0.0.7 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/install-agentskills-mcp.test.d.ts +12 -0
- package/dist/__tests__/install-agentskills-mcp.test.d.ts.map +1 -0
- package/dist/__tests__/install-agentskills-mcp.test.js +848 -0
- package/dist/__tests__/install-agentskills-mcp.test.js.map +1 -0
- package/dist/__tests__/install-mcp-validation.test.d.ts +9 -0
- package/dist/__tests__/install-mcp-validation.test.d.ts.map +1 -0
- package/dist/__tests__/install-mcp-validation.test.js +1495 -0
- package/dist/__tests__/install-mcp-validation.test.js.map +1 -0
- package/dist/__tests__/install-with-mcp.test.d.ts +12 -0
- package/dist/__tests__/install-with-mcp.test.d.ts.map +1 -0
- package/dist/__tests__/install-with-mcp.test.js +2258 -0
- package/dist/__tests__/install-with-mcp.test.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +8 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/install.d.ts +4 -0
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +305 -9
- package/dist/commands/install.js.map +1 -1
- package/package.json +7 -2
|
@@ -0,0 +1,848 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for auto-install agentskills-mcp server feature
|
|
3
|
+
*
|
|
4
|
+
* TDD RED PHASE - Tests written before implementation
|
|
5
|
+
* These tests define the expected behavior of automatically installing
|
|
6
|
+
* the @codemcp/agentskills-mcp server when running install command.
|
|
7
|
+
*
|
|
8
|
+
* Task: agent-skills-2.3.19
|
|
9
|
+
* Phase: TDD RED - Writing failing tests
|
|
10
|
+
*/
|
|
11
|
+
import { describe, it, expect, beforeEach, vi, afterEach } from "vitest";
|
|
12
|
+
import { installCommand } from "../commands/install.js";
|
|
13
|
+
import { PackageConfigManager, SkillInstaller, MCPConfigManager, MCPDependencyChecker } from "@codemcp/agentskills-core";
|
|
14
|
+
// Mock fs module
|
|
15
|
+
vi.mock("fs", () => ({
|
|
16
|
+
promises: {
|
|
17
|
+
mkdir: vi.fn().mockResolvedValue(undefined),
|
|
18
|
+
access: vi.fn().mockResolvedValue(undefined),
|
|
19
|
+
readFile: vi.fn(),
|
|
20
|
+
writeFile: vi.fn(),
|
|
21
|
+
stat: vi.fn(),
|
|
22
|
+
rm: vi.fn()
|
|
23
|
+
}
|
|
24
|
+
}));
|
|
25
|
+
// Mock inquirer
|
|
26
|
+
vi.mock("inquirer", () => ({
|
|
27
|
+
default: {
|
|
28
|
+
prompt: vi.fn()
|
|
29
|
+
}
|
|
30
|
+
}));
|
|
31
|
+
// Mock all dependencies
|
|
32
|
+
vi.mock("@codemcp/agentskills-core", async () => {
|
|
33
|
+
const actualCore = await vi.importActual("@codemcp/agentskills-core");
|
|
34
|
+
return {
|
|
35
|
+
...actualCore,
|
|
36
|
+
PackageConfigManager: vi.fn(),
|
|
37
|
+
SkillInstaller: vi.fn(),
|
|
38
|
+
MCPConfigManager: vi.fn(),
|
|
39
|
+
MCPDependencyChecker: vi.fn()
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
vi.mock("ora", () => ({
|
|
43
|
+
default: vi.fn(() => ({
|
|
44
|
+
start: vi.fn().mockReturnThis(),
|
|
45
|
+
stop: vi.fn().mockReturnThis(),
|
|
46
|
+
succeed: vi.fn().mockReturnThis(),
|
|
47
|
+
fail: vi.fn().mockReturnThis()
|
|
48
|
+
}))
|
|
49
|
+
}));
|
|
50
|
+
describe("Install Command - Auto-install agentskills-mcp Server", () => {
|
|
51
|
+
let mockConfigManager;
|
|
52
|
+
let mockInstaller;
|
|
53
|
+
let mockMCPConfigManager;
|
|
54
|
+
let mockMCPDependencyChecker;
|
|
55
|
+
let consoleLogSpy;
|
|
56
|
+
let consoleErrorSpy;
|
|
57
|
+
let processExitSpy;
|
|
58
|
+
let processCwdSpy;
|
|
59
|
+
beforeEach(() => {
|
|
60
|
+
// Setup mocks
|
|
61
|
+
mockConfigManager = {
|
|
62
|
+
loadConfig: vi.fn()
|
|
63
|
+
};
|
|
64
|
+
mockInstaller = {
|
|
65
|
+
install: vi.fn(),
|
|
66
|
+
generateLockFile: vi.fn(),
|
|
67
|
+
loadInstalledSkills: vi.fn()
|
|
68
|
+
};
|
|
69
|
+
mockMCPConfigManager = {
|
|
70
|
+
isServerConfigured: vi.fn(),
|
|
71
|
+
addServer: vi.fn()
|
|
72
|
+
};
|
|
73
|
+
mockMCPDependencyChecker = {
|
|
74
|
+
collectDependencies: vi.fn(),
|
|
75
|
+
checkDependencies: vi.fn()
|
|
76
|
+
};
|
|
77
|
+
vi.mocked(PackageConfigManager).mockImplementation(() => mockConfigManager);
|
|
78
|
+
vi.mocked(SkillInstaller).mockImplementation(() => mockInstaller);
|
|
79
|
+
vi.mocked(MCPConfigManager).mockImplementation(() => mockMCPConfigManager);
|
|
80
|
+
vi.mocked(MCPDependencyChecker).mockImplementation(() => mockMCPDependencyChecker);
|
|
81
|
+
// Setup spies
|
|
82
|
+
consoleLogSpy = vi.spyOn(console, "log").mockImplementation(() => { });
|
|
83
|
+
consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => { });
|
|
84
|
+
processExitSpy = vi
|
|
85
|
+
.spyOn(process, "exit")
|
|
86
|
+
.mockImplementation((() => { }));
|
|
87
|
+
processCwdSpy = vi.spyOn(process, "cwd").mockReturnValue("/test/project");
|
|
88
|
+
// Default mock implementations
|
|
89
|
+
mockConfigManager.loadConfig.mockResolvedValue({
|
|
90
|
+
skills: {},
|
|
91
|
+
config: {
|
|
92
|
+
skillsDirectory: ".agentskills/skills",
|
|
93
|
+
autoDiscover: [],
|
|
94
|
+
maxSkillSize: 5000,
|
|
95
|
+
logLevel: "info"
|
|
96
|
+
},
|
|
97
|
+
source: {
|
|
98
|
+
type: "file",
|
|
99
|
+
path: "/test/project/package.json"
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
afterEach(() => {
|
|
104
|
+
vi.clearAllMocks();
|
|
105
|
+
consoleLogSpy.mockRestore();
|
|
106
|
+
consoleErrorSpy.mockRestore();
|
|
107
|
+
processExitSpy.mockRestore();
|
|
108
|
+
processCwdSpy.mockRestore();
|
|
109
|
+
});
|
|
110
|
+
describe("First install - agentskills server not configured", () => {
|
|
111
|
+
it("should add agentskills server after successful skill installation", async () => {
|
|
112
|
+
// Setup
|
|
113
|
+
const config = {
|
|
114
|
+
skills: {
|
|
115
|
+
"test-skill": "github:user/test-skill#v1.0.0"
|
|
116
|
+
},
|
|
117
|
+
config: {
|
|
118
|
+
skillsDirectory: ".agentskills/skills",
|
|
119
|
+
autoDiscover: [],
|
|
120
|
+
maxSkillSize: 5000,
|
|
121
|
+
logLevel: "info"
|
|
122
|
+
},
|
|
123
|
+
source: {
|
|
124
|
+
type: "file",
|
|
125
|
+
path: "/test/project/package.json"
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const checkResult = {
|
|
129
|
+
allConfigured: true,
|
|
130
|
+
missing: [],
|
|
131
|
+
configured: []
|
|
132
|
+
};
|
|
133
|
+
mockConfigManager.loadConfig.mockResolvedValue(config);
|
|
134
|
+
mockMCPConfigManager.isServerConfigured.mockResolvedValue(false); // agentskills NOT configured
|
|
135
|
+
mockInstaller.install.mockResolvedValue({
|
|
136
|
+
success: true,
|
|
137
|
+
name: "test-skill",
|
|
138
|
+
spec: "github:user/test-skill#v1.0.0",
|
|
139
|
+
resolvedVersion: "1.0.0",
|
|
140
|
+
integrity: "sha512-abc123",
|
|
141
|
+
installPath: "/test/.agentskills/skills/test-skill"
|
|
142
|
+
});
|
|
143
|
+
mockInstaller.loadInstalledSkills.mockResolvedValue([
|
|
144
|
+
{
|
|
145
|
+
metadata: { name: "test-skill", description: "Test skill" },
|
|
146
|
+
body: "Skill content"
|
|
147
|
+
}
|
|
148
|
+
]);
|
|
149
|
+
mockMCPDependencyChecker.collectDependencies.mockReturnValue([]);
|
|
150
|
+
mockMCPDependencyChecker.checkDependencies.mockResolvedValue(checkResult);
|
|
151
|
+
// Execute
|
|
152
|
+
await installCommand({ cwd: "/test/project", agent: "claude" });
|
|
153
|
+
// Verify agentskills server was added
|
|
154
|
+
expect(mockMCPConfigManager.isServerConfigured).toHaveBeenCalledWith("claude-desktop", "agentskills", "/test/project");
|
|
155
|
+
expect(mockMCPConfigManager.addServer).toHaveBeenCalledWith("claude-desktop", "agentskills", {
|
|
156
|
+
command: "npx",
|
|
157
|
+
args: ["-y", "@codemcp/agentskills-mcp"],
|
|
158
|
+
env: {}
|
|
159
|
+
}, "/test/project");
|
|
160
|
+
expect(processExitSpy).toHaveBeenCalledWith(0);
|
|
161
|
+
});
|
|
162
|
+
it("should show success message when agentskills server is added", async () => {
|
|
163
|
+
// Setup
|
|
164
|
+
const config = {
|
|
165
|
+
skills: {
|
|
166
|
+
"test-skill": "github:user/test-skill#v1.0.0"
|
|
167
|
+
},
|
|
168
|
+
config: {
|
|
169
|
+
skillsDirectory: ".agentskills/skills",
|
|
170
|
+
autoDiscover: [],
|
|
171
|
+
maxSkillSize: 5000,
|
|
172
|
+
logLevel: "info"
|
|
173
|
+
},
|
|
174
|
+
source: {
|
|
175
|
+
type: "file",
|
|
176
|
+
path: "/test/project/package.json"
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
const checkResult = {
|
|
180
|
+
allConfigured: true,
|
|
181
|
+
missing: [],
|
|
182
|
+
configured: []
|
|
183
|
+
};
|
|
184
|
+
mockConfigManager.loadConfig.mockResolvedValue(config);
|
|
185
|
+
mockMCPConfigManager.isServerConfigured.mockResolvedValue(false);
|
|
186
|
+
mockInstaller.install.mockResolvedValue({
|
|
187
|
+
success: true,
|
|
188
|
+
name: "test-skill",
|
|
189
|
+
spec: "github:user/test-skill#v1.0.0",
|
|
190
|
+
resolvedVersion: "1.0.0",
|
|
191
|
+
integrity: "sha512-abc123",
|
|
192
|
+
installPath: "/test/.agentskills/skills/test-skill"
|
|
193
|
+
});
|
|
194
|
+
mockInstaller.loadInstalledSkills.mockResolvedValue([
|
|
195
|
+
{
|
|
196
|
+
metadata: { name: "test-skill", description: "Test skill" },
|
|
197
|
+
body: "Skill content"
|
|
198
|
+
}
|
|
199
|
+
]);
|
|
200
|
+
mockMCPDependencyChecker.collectDependencies.mockReturnValue([]);
|
|
201
|
+
mockMCPDependencyChecker.checkDependencies.mockResolvedValue(checkResult);
|
|
202
|
+
// Execute
|
|
203
|
+
await installCommand({ cwd: "/test/project", agent: "claude" });
|
|
204
|
+
// Verify success message
|
|
205
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining("agentskills"));
|
|
206
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringMatching(/added|configured/i));
|
|
207
|
+
});
|
|
208
|
+
it("should use process.cwd() as working directory when server runs", async () => {
|
|
209
|
+
// Setup
|
|
210
|
+
const config = {
|
|
211
|
+
skills: {
|
|
212
|
+
"test-skill": "github:user/test-skill#v1.0.0"
|
|
213
|
+
},
|
|
214
|
+
config: {
|
|
215
|
+
skillsDirectory: ".agentskills/skills",
|
|
216
|
+
autoDiscover: [],
|
|
217
|
+
maxSkillSize: 5000,
|
|
218
|
+
logLevel: "info"
|
|
219
|
+
},
|
|
220
|
+
source: {
|
|
221
|
+
type: "file",
|
|
222
|
+
path: "/test/project/package.json"
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
const checkResult = {
|
|
226
|
+
allConfigured: true,
|
|
227
|
+
missing: [],
|
|
228
|
+
configured: []
|
|
229
|
+
};
|
|
230
|
+
processCwdSpy.mockReturnValue("/custom/working/directory");
|
|
231
|
+
mockConfigManager.loadConfig.mockResolvedValue(config);
|
|
232
|
+
mockMCPConfigManager.isServerConfigured.mockResolvedValue(false);
|
|
233
|
+
mockInstaller.install.mockResolvedValue({
|
|
234
|
+
success: true,
|
|
235
|
+
name: "test-skill",
|
|
236
|
+
spec: "github:user/test-skill#v1.0.0",
|
|
237
|
+
resolvedVersion: "1.0.0",
|
|
238
|
+
integrity: "sha512-abc123",
|
|
239
|
+
installPath: "/test/.agentskills/skills/test-skill"
|
|
240
|
+
});
|
|
241
|
+
mockInstaller.loadInstalledSkills.mockResolvedValue([
|
|
242
|
+
{
|
|
243
|
+
metadata: { name: "test-skill", description: "Test skill" },
|
|
244
|
+
body: "Skill content"
|
|
245
|
+
}
|
|
246
|
+
]);
|
|
247
|
+
mockMCPDependencyChecker.collectDependencies.mockReturnValue([]);
|
|
248
|
+
mockMCPDependencyChecker.checkDependencies.mockResolvedValue(checkResult);
|
|
249
|
+
// Execute with explicit cwd
|
|
250
|
+
await installCommand({
|
|
251
|
+
cwd: "/custom/working/directory",
|
|
252
|
+
agent: "claude"
|
|
253
|
+
});
|
|
254
|
+
// Verify server config without cwd (working directory inherited)
|
|
255
|
+
expect(mockMCPConfigManager.addServer).toHaveBeenCalledWith("claude-desktop", "agentskills", expect.objectContaining({
|
|
256
|
+
command: "npx",
|
|
257
|
+
args: ["-y", "@codemcp/agentskills-mcp"],
|
|
258
|
+
env: {}
|
|
259
|
+
}), "/custom/working/directory");
|
|
260
|
+
});
|
|
261
|
+
it("should work with --with-mcp flag", async () => {
|
|
262
|
+
// Setup
|
|
263
|
+
const config = {
|
|
264
|
+
skills: {
|
|
265
|
+
"test-skill": "github:user/test-skill#v1.0.0"
|
|
266
|
+
},
|
|
267
|
+
config: {
|
|
268
|
+
skillsDirectory: ".agentskills/skills",
|
|
269
|
+
autoDiscover: [],
|
|
270
|
+
maxSkillSize: 5000,
|
|
271
|
+
logLevel: "info"
|
|
272
|
+
},
|
|
273
|
+
source: {
|
|
274
|
+
type: "file",
|
|
275
|
+
path: "/test/project/package.json"
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
const checkResult = {
|
|
279
|
+
allConfigured: true,
|
|
280
|
+
missing: [],
|
|
281
|
+
configured: []
|
|
282
|
+
};
|
|
283
|
+
mockConfigManager.loadConfig.mockResolvedValue(config);
|
|
284
|
+
mockMCPConfigManager.isServerConfigured.mockResolvedValue(false);
|
|
285
|
+
mockInstaller.install.mockResolvedValue({
|
|
286
|
+
success: true,
|
|
287
|
+
name: "test-skill",
|
|
288
|
+
spec: "github:user/test-skill#v1.0.0",
|
|
289
|
+
resolvedVersion: "1.0.0",
|
|
290
|
+
integrity: "sha512-abc123",
|
|
291
|
+
installPath: "/test/.agentskills/skills/test-skill"
|
|
292
|
+
});
|
|
293
|
+
mockInstaller.loadInstalledSkills.mockResolvedValue([
|
|
294
|
+
{
|
|
295
|
+
metadata: { name: "test-skill", description: "Test skill" },
|
|
296
|
+
body: "Skill content"
|
|
297
|
+
}
|
|
298
|
+
]);
|
|
299
|
+
mockMCPDependencyChecker.collectDependencies.mockReturnValue([]);
|
|
300
|
+
mockMCPDependencyChecker.checkDependencies.mockResolvedValue(checkResult);
|
|
301
|
+
// Execute with --with-mcp
|
|
302
|
+
await installCommand({
|
|
303
|
+
cwd: "/test/project",
|
|
304
|
+
withMcp: true,
|
|
305
|
+
agent: "claude"
|
|
306
|
+
});
|
|
307
|
+
// Verify agentskills server was added
|
|
308
|
+
expect(mockMCPConfigManager.addServer).toHaveBeenCalledWith("claude-desktop", "agentskills", expect.objectContaining({
|
|
309
|
+
command: "npx",
|
|
310
|
+
args: ["-y", "@codemcp/agentskills-mcp"]
|
|
311
|
+
}), "/test/project");
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
describe("Subsequent install - agentskills server already configured", () => {
|
|
315
|
+
it("should skip adding agentskills server when already configured", async () => {
|
|
316
|
+
// Setup
|
|
317
|
+
const config = {
|
|
318
|
+
skills: {
|
|
319
|
+
"test-skill": "github:user/test-skill#v1.0.0"
|
|
320
|
+
},
|
|
321
|
+
config: {
|
|
322
|
+
skillsDirectory: ".agentskills/skills",
|
|
323
|
+
autoDiscover: [],
|
|
324
|
+
maxSkillSize: 5000,
|
|
325
|
+
logLevel: "info"
|
|
326
|
+
},
|
|
327
|
+
source: {
|
|
328
|
+
type: "file",
|
|
329
|
+
path: "/test/project/package.json"
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
const checkResult = {
|
|
333
|
+
allConfigured: true,
|
|
334
|
+
missing: [],
|
|
335
|
+
configured: []
|
|
336
|
+
};
|
|
337
|
+
mockConfigManager.loadConfig.mockResolvedValue(config);
|
|
338
|
+
mockMCPConfigManager.isServerConfigured.mockResolvedValue(true); // ALREADY configured
|
|
339
|
+
mockInstaller.install.mockResolvedValue({
|
|
340
|
+
success: true,
|
|
341
|
+
name: "test-skill",
|
|
342
|
+
spec: "github:user/test-skill#v1.0.0",
|
|
343
|
+
resolvedVersion: "1.0.0",
|
|
344
|
+
integrity: "sha512-abc123",
|
|
345
|
+
installPath: "/test/.agentskills/skills/test-skill"
|
|
346
|
+
});
|
|
347
|
+
mockInstaller.loadInstalledSkills.mockResolvedValue([
|
|
348
|
+
{
|
|
349
|
+
metadata: { name: "test-skill", description: "Test skill" },
|
|
350
|
+
body: "Skill content"
|
|
351
|
+
}
|
|
352
|
+
]);
|
|
353
|
+
mockMCPDependencyChecker.collectDependencies.mockReturnValue([]);
|
|
354
|
+
mockMCPDependencyChecker.checkDependencies.mockResolvedValue(checkResult);
|
|
355
|
+
// Execute
|
|
356
|
+
await installCommand({ cwd: "/test/project", agent: "claude" });
|
|
357
|
+
// Verify agentskills server was NOT added
|
|
358
|
+
expect(mockMCPConfigManager.isServerConfigured, "/test/project").toHaveBeenCalledWith("claude-desktop", "agentskills", "/test/project");
|
|
359
|
+
expect(mockMCPConfigManager.addServer).not.toHaveBeenCalled();
|
|
360
|
+
expect(processExitSpy).toHaveBeenCalledWith(0);
|
|
361
|
+
});
|
|
362
|
+
it("should not show message when skipping already configured server", async () => {
|
|
363
|
+
// Setup
|
|
364
|
+
const config = {
|
|
365
|
+
skills: {
|
|
366
|
+
"test-skill": "github:user/test-skill#v1.0.0"
|
|
367
|
+
},
|
|
368
|
+
config: {
|
|
369
|
+
skillsDirectory: ".agentskills/skills",
|
|
370
|
+
autoDiscover: [],
|
|
371
|
+
maxSkillSize: 5000,
|
|
372
|
+
logLevel: "info"
|
|
373
|
+
},
|
|
374
|
+
source: {
|
|
375
|
+
type: "file",
|
|
376
|
+
path: "/test/project/package.json"
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
const checkResult = {
|
|
380
|
+
allConfigured: true,
|
|
381
|
+
missing: [],
|
|
382
|
+
configured: []
|
|
383
|
+
};
|
|
384
|
+
mockConfigManager.loadConfig.mockResolvedValue(config);
|
|
385
|
+
mockMCPConfigManager.isServerConfigured.mockResolvedValue(true);
|
|
386
|
+
mockInstaller.install.mockResolvedValue({
|
|
387
|
+
success: true,
|
|
388
|
+
name: "test-skill",
|
|
389
|
+
spec: "github:user/test-skill#v1.0.0",
|
|
390
|
+
resolvedVersion: "1.0.0",
|
|
391
|
+
integrity: "sha512-abc123",
|
|
392
|
+
installPath: "/test/.agentskills/skills/test-skill"
|
|
393
|
+
});
|
|
394
|
+
mockInstaller.loadInstalledSkills.mockResolvedValue([
|
|
395
|
+
{
|
|
396
|
+
metadata: { name: "test-skill", description: "Test skill" },
|
|
397
|
+
body: "Skill content"
|
|
398
|
+
}
|
|
399
|
+
]);
|
|
400
|
+
mockMCPDependencyChecker.collectDependencies.mockReturnValue([]);
|
|
401
|
+
mockMCPDependencyChecker.checkDependencies.mockResolvedValue(checkResult);
|
|
402
|
+
consoleLogSpy.mockClear(); // Clear previous calls
|
|
403
|
+
// Execute
|
|
404
|
+
await installCommand({ cwd: "/test/project", agent: "claude" });
|
|
405
|
+
// Verify NO message about agentskills MCP server (silently skip)
|
|
406
|
+
const agentskillsMCPMessages = consoleLogSpy.mock.calls.filter((call) => call.some((arg) => {
|
|
407
|
+
const str = String(arg).toLowerCase();
|
|
408
|
+
return (str.includes("agentskills") &&
|
|
409
|
+
(str.includes("mcp") ||
|
|
410
|
+
str.includes("server") ||
|
|
411
|
+
str.includes("added") ||
|
|
412
|
+
str.includes("configured")));
|
|
413
|
+
}));
|
|
414
|
+
expect(agentskillsMCPMessages.length).toBe(0);
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
describe("Works with all MCP client types", () => {
|
|
418
|
+
it("should add agentskills server for claude-desktop client", async () => {
|
|
419
|
+
// Setup
|
|
420
|
+
const config = {
|
|
421
|
+
skills: {
|
|
422
|
+
"test-skill": "github:user/test-skill#v1.0.0"
|
|
423
|
+
},
|
|
424
|
+
config: {
|
|
425
|
+
skillsDirectory: ".agentskills/skills",
|
|
426
|
+
autoDiscover: [],
|
|
427
|
+
maxSkillSize: 5000,
|
|
428
|
+
logLevel: "info"
|
|
429
|
+
},
|
|
430
|
+
source: {
|
|
431
|
+
type: "file",
|
|
432
|
+
path: "/test/project/package.json"
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
const checkResult = {
|
|
436
|
+
allConfigured: true,
|
|
437
|
+
missing: [],
|
|
438
|
+
configured: []
|
|
439
|
+
};
|
|
440
|
+
mockConfigManager.loadConfig.mockResolvedValue(config);
|
|
441
|
+
mockMCPConfigManager.isServerConfigured.mockResolvedValue(false);
|
|
442
|
+
mockInstaller.install.mockResolvedValue({
|
|
443
|
+
success: true,
|
|
444
|
+
name: "test-skill",
|
|
445
|
+
spec: "github:user/test-skill#v1.0.0",
|
|
446
|
+
resolvedVersion: "1.0.0",
|
|
447
|
+
integrity: "sha512-abc123",
|
|
448
|
+
installPath: "/test/.agentskills/skills/test-skill"
|
|
449
|
+
});
|
|
450
|
+
mockInstaller.loadInstalledSkills.mockResolvedValue([
|
|
451
|
+
{
|
|
452
|
+
metadata: { name: "test-skill", description: "Test skill" },
|
|
453
|
+
body: "Skill content"
|
|
454
|
+
}
|
|
455
|
+
]);
|
|
456
|
+
mockMCPDependencyChecker.collectDependencies.mockReturnValue([]);
|
|
457
|
+
mockMCPDependencyChecker.checkDependencies.mockResolvedValue(checkResult);
|
|
458
|
+
// Execute
|
|
459
|
+
await installCommand({ cwd: "/test/project", agent: "claude" });
|
|
460
|
+
// Verify
|
|
461
|
+
expect(mockMCPConfigManager.addServer).toHaveBeenCalledWith("claude-desktop", "agentskills", expect.any(Object), "/test/project");
|
|
462
|
+
});
|
|
463
|
+
it("should add agentskills server for cline client", async () => {
|
|
464
|
+
// Setup
|
|
465
|
+
const config = {
|
|
466
|
+
skills: {
|
|
467
|
+
"test-skill": "github:user/test-skill#v1.0.0"
|
|
468
|
+
},
|
|
469
|
+
config: {
|
|
470
|
+
skillsDirectory: ".agentskills/skills",
|
|
471
|
+
autoDiscover: [],
|
|
472
|
+
maxSkillSize: 5000,
|
|
473
|
+
logLevel: "info"
|
|
474
|
+
},
|
|
475
|
+
source: {
|
|
476
|
+
type: "file",
|
|
477
|
+
path: "/test/project/package.json"
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
const checkResult = {
|
|
481
|
+
allConfigured: true,
|
|
482
|
+
missing: [],
|
|
483
|
+
configured: []
|
|
484
|
+
};
|
|
485
|
+
mockConfigManager.loadConfig.mockResolvedValue(config);
|
|
486
|
+
mockMCPConfigManager.isServerConfigured.mockResolvedValue(false);
|
|
487
|
+
mockInstaller.install.mockResolvedValue({
|
|
488
|
+
success: true,
|
|
489
|
+
name: "test-skill",
|
|
490
|
+
spec: "github:user/test-skill#v1.0.0",
|
|
491
|
+
resolvedVersion: "1.0.0",
|
|
492
|
+
integrity: "sha512-abc123",
|
|
493
|
+
installPath: "/test/.agentskills/skills/test-skill"
|
|
494
|
+
});
|
|
495
|
+
mockInstaller.loadInstalledSkills.mockResolvedValue([
|
|
496
|
+
{
|
|
497
|
+
metadata: { name: "test-skill", description: "Test skill" },
|
|
498
|
+
body: "Skill content"
|
|
499
|
+
}
|
|
500
|
+
]);
|
|
501
|
+
mockMCPDependencyChecker.collectDependencies.mockReturnValue([]);
|
|
502
|
+
mockMCPDependencyChecker.checkDependencies.mockResolvedValue(checkResult);
|
|
503
|
+
// Execute with cline agent
|
|
504
|
+
await installCommand({ cwd: "/test/project", agent: "cline" });
|
|
505
|
+
// Verify
|
|
506
|
+
expect(mockMCPConfigManager.addServer).toHaveBeenCalledWith("cline", "agentskills", expect.any(Object), "/test/project");
|
|
507
|
+
});
|
|
508
|
+
it("should add agentskills server for zed client", async () => {
|
|
509
|
+
// Setup
|
|
510
|
+
const config = {
|
|
511
|
+
skills: {
|
|
512
|
+
"test-skill": "github:user/test-skill#v1.0.0"
|
|
513
|
+
},
|
|
514
|
+
config: {
|
|
515
|
+
skillsDirectory: ".agentskills/skills",
|
|
516
|
+
autoDiscover: [],
|
|
517
|
+
maxSkillSize: 5000,
|
|
518
|
+
logLevel: "info"
|
|
519
|
+
},
|
|
520
|
+
source: {
|
|
521
|
+
type: "file",
|
|
522
|
+
path: "/test/project/package.json"
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
const checkResult = {
|
|
526
|
+
allConfigured: true,
|
|
527
|
+
missing: [],
|
|
528
|
+
configured: []
|
|
529
|
+
};
|
|
530
|
+
mockConfigManager.loadConfig.mockResolvedValue(config);
|
|
531
|
+
mockMCPConfigManager.isServerConfigured.mockResolvedValue(false);
|
|
532
|
+
mockInstaller.install.mockResolvedValue({
|
|
533
|
+
success: true,
|
|
534
|
+
name: "test-skill",
|
|
535
|
+
spec: "github:user/test-skill#v1.0.0",
|
|
536
|
+
resolvedVersion: "1.0.0",
|
|
537
|
+
integrity: "sha512-abc123",
|
|
538
|
+
installPath: "/test/.agentskills/skills/test-skill"
|
|
539
|
+
});
|
|
540
|
+
mockInstaller.loadInstalledSkills.mockResolvedValue([
|
|
541
|
+
{
|
|
542
|
+
metadata: { name: "test-skill", description: "Test skill" },
|
|
543
|
+
body: "Skill content"
|
|
544
|
+
}
|
|
545
|
+
]);
|
|
546
|
+
mockMCPDependencyChecker.collectDependencies.mockReturnValue([]);
|
|
547
|
+
mockMCPDependencyChecker.checkDependencies.mockResolvedValue(checkResult);
|
|
548
|
+
// Execute with zed agent
|
|
549
|
+
await installCommand({ cwd: "/test/project", agent: "zed" });
|
|
550
|
+
// Verify
|
|
551
|
+
expect(mockMCPConfigManager.addServer).toHaveBeenCalledWith("zed", "agentskills", expect.any(Object), "/test/project");
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
describe("Handle no agent specified", () => {
|
|
555
|
+
it("should skip MCP configuration when no agent is specified", async () => {
|
|
556
|
+
// Setup
|
|
557
|
+
const config = {
|
|
558
|
+
skills: {
|
|
559
|
+
"test-skill": "github:user/test-skill#v1.0.0"
|
|
560
|
+
},
|
|
561
|
+
config: {
|
|
562
|
+
skillsDirectory: ".agentskills/skills",
|
|
563
|
+
autoDiscover: [],
|
|
564
|
+
maxSkillSize: 5000,
|
|
565
|
+
logLevel: "info"
|
|
566
|
+
},
|
|
567
|
+
source: {
|
|
568
|
+
type: "file",
|
|
569
|
+
path: "/test/project/package.json"
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
mockConfigManager.loadConfig.mockResolvedValue(config);
|
|
573
|
+
mockInstaller.install.mockResolvedValue({
|
|
574
|
+
success: true,
|
|
575
|
+
name: "test-skill",
|
|
576
|
+
spec: "github:user/test-skill#v1.0.0",
|
|
577
|
+
resolvedVersion: "1.0.0",
|
|
578
|
+
integrity: "sha512-abc123",
|
|
579
|
+
installPath: "/test/.agentskills/skills/test-skill"
|
|
580
|
+
});
|
|
581
|
+
mockInstaller.generateLockFile.mockResolvedValue(undefined);
|
|
582
|
+
mockInstaller.loadInstalledSkills.mockResolvedValue([]);
|
|
583
|
+
mockMCPDependencyChecker.collectDependencies.mockReturnValue([]);
|
|
584
|
+
// Execute without agent parameter
|
|
585
|
+
await installCommand({ cwd: "/test/project" });
|
|
586
|
+
// Verify graceful skip - no MCP operations
|
|
587
|
+
expect(mockMCPConfigManager.isServerConfigured).not.toHaveBeenCalled();
|
|
588
|
+
expect(mockMCPConfigManager.addServer).not.toHaveBeenCalled();
|
|
589
|
+
expect(processExitSpy).toHaveBeenCalledWith(0); // Still successful
|
|
590
|
+
});
|
|
591
|
+
it("should not show error when no agent is specified", async () => {
|
|
592
|
+
// Setup
|
|
593
|
+
const config = {
|
|
594
|
+
skills: {
|
|
595
|
+
"test-skill": "github:user/test-skill#v1.0.0"
|
|
596
|
+
},
|
|
597
|
+
config: {
|
|
598
|
+
skillsDirectory: ".agentskills/skills",
|
|
599
|
+
autoDiscover: [],
|
|
600
|
+
maxSkillSize: 5000,
|
|
601
|
+
logLevel: "info"
|
|
602
|
+
},
|
|
603
|
+
source: {
|
|
604
|
+
type: "file",
|
|
605
|
+
path: "/test/project/package.json"
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
mockConfigManager.loadConfig.mockResolvedValue(config);
|
|
609
|
+
mockInstaller.install.mockResolvedValue({
|
|
610
|
+
success: true,
|
|
611
|
+
name: "test-skill",
|
|
612
|
+
spec: "github:user/test-skill#v1.0.0",
|
|
613
|
+
resolvedVersion: "1.0.0",
|
|
614
|
+
integrity: "sha512-abc123",
|
|
615
|
+
installPath: "/test/.agentskills/skills/test-skill"
|
|
616
|
+
});
|
|
617
|
+
mockInstaller.generateLockFile.mockResolvedValue(undefined);
|
|
618
|
+
mockInstaller.loadInstalledSkills.mockResolvedValue([]);
|
|
619
|
+
mockMCPDependencyChecker.collectDependencies.mockReturnValue([]);
|
|
620
|
+
// Execute without agent parameter
|
|
621
|
+
await installCommand({ cwd: "/test/project" });
|
|
622
|
+
// Verify no error
|
|
623
|
+
expect(consoleErrorSpy).not.toHaveBeenCalledWith(expect.stringContaining("agentskills"));
|
|
624
|
+
});
|
|
625
|
+
});
|
|
626
|
+
describe("Handle addServer errors gracefully", () => {
|
|
627
|
+
it("should not fail installation if addServer throws error", async () => {
|
|
628
|
+
// Setup
|
|
629
|
+
const config = {
|
|
630
|
+
skills: {
|
|
631
|
+
"test-skill": "github:user/test-skill#v1.0.0"
|
|
632
|
+
},
|
|
633
|
+
config: {
|
|
634
|
+
skillsDirectory: ".agentskills/skills",
|
|
635
|
+
autoDiscover: [],
|
|
636
|
+
maxSkillSize: 5000,
|
|
637
|
+
logLevel: "info"
|
|
638
|
+
},
|
|
639
|
+
source: {
|
|
640
|
+
type: "file",
|
|
641
|
+
path: "/test/project/package.json"
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
const checkResult = {
|
|
645
|
+
allConfigured: true,
|
|
646
|
+
missing: [],
|
|
647
|
+
configured: []
|
|
648
|
+
};
|
|
649
|
+
mockConfigManager.loadConfig.mockResolvedValue(config);
|
|
650
|
+
mockMCPConfigManager.isServerConfigured.mockResolvedValue(false);
|
|
651
|
+
mockMCPConfigManager.addServer.mockRejectedValue(new Error("Failed to write config"));
|
|
652
|
+
mockInstaller.install.mockResolvedValue({
|
|
653
|
+
success: true,
|
|
654
|
+
name: "test-skill",
|
|
655
|
+
spec: "github:user/test-skill#v1.0.0",
|
|
656
|
+
resolvedVersion: "1.0.0",
|
|
657
|
+
integrity: "sha512-abc123",
|
|
658
|
+
installPath: "/test/.agentskills/skills/test-skill"
|
|
659
|
+
});
|
|
660
|
+
mockInstaller.loadInstalledSkills.mockResolvedValue([
|
|
661
|
+
{
|
|
662
|
+
metadata: { name: "test-skill", description: "Test skill" },
|
|
663
|
+
body: "Skill content"
|
|
664
|
+
}
|
|
665
|
+
]);
|
|
666
|
+
mockMCPDependencyChecker.collectDependencies.mockReturnValue([]);
|
|
667
|
+
mockMCPDependencyChecker.checkDependencies.mockResolvedValue(checkResult);
|
|
668
|
+
// Execute
|
|
669
|
+
await installCommand({ cwd: "/test/project", agent: "claude" });
|
|
670
|
+
// Verify installation still succeeds
|
|
671
|
+
expect(processExitSpy).toHaveBeenCalledWith(0);
|
|
672
|
+
});
|
|
673
|
+
it("should log warning when addServer fails", async () => {
|
|
674
|
+
// Setup
|
|
675
|
+
const config = {
|
|
676
|
+
skills: {
|
|
677
|
+
"test-skill": "github:user/test-skill#v1.0.0"
|
|
678
|
+
},
|
|
679
|
+
config: {
|
|
680
|
+
skillsDirectory: ".agentskills/skills",
|
|
681
|
+
autoDiscover: [],
|
|
682
|
+
maxSkillSize: 5000,
|
|
683
|
+
logLevel: "info"
|
|
684
|
+
},
|
|
685
|
+
source: {
|
|
686
|
+
type: "file",
|
|
687
|
+
path: "/test/project/package.json"
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
const checkResult = {
|
|
691
|
+
allConfigured: true,
|
|
692
|
+
missing: [],
|
|
693
|
+
configured: []
|
|
694
|
+
};
|
|
695
|
+
mockConfigManager.loadConfig.mockResolvedValue(config);
|
|
696
|
+
mockMCPConfigManager.isServerConfigured.mockResolvedValue(false);
|
|
697
|
+
mockMCPConfigManager.addServer.mockRejectedValue(new Error("Failed to write config"));
|
|
698
|
+
mockInstaller.install.mockResolvedValue({
|
|
699
|
+
success: true,
|
|
700
|
+
name: "test-skill",
|
|
701
|
+
spec: "github:user/test-skill#v1.0.0",
|
|
702
|
+
resolvedVersion: "1.0.0",
|
|
703
|
+
integrity: "sha512-abc123",
|
|
704
|
+
installPath: "/test/.agentskills/skills/test-skill"
|
|
705
|
+
});
|
|
706
|
+
mockInstaller.loadInstalledSkills.mockResolvedValue([
|
|
707
|
+
{
|
|
708
|
+
metadata: { name: "test-skill", description: "Test skill" },
|
|
709
|
+
body: "Skill content"
|
|
710
|
+
}
|
|
711
|
+
]);
|
|
712
|
+
mockMCPDependencyChecker.collectDependencies.mockReturnValue([]);
|
|
713
|
+
mockMCPDependencyChecker.checkDependencies.mockResolvedValue(checkResult);
|
|
714
|
+
// Execute
|
|
715
|
+
await installCommand({ cwd: "/test/project", agent: "claude" });
|
|
716
|
+
// Verify warning was logged
|
|
717
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringMatching(/warning|failed|could not/i));
|
|
718
|
+
});
|
|
719
|
+
});
|
|
720
|
+
describe("Server config format", () => {
|
|
721
|
+
it("should auto-install even when no skills configured", async () => {
|
|
722
|
+
// Setup: Config with NO skills
|
|
723
|
+
const config = {
|
|
724
|
+
skills: {}, // Empty skills object
|
|
725
|
+
config: {
|
|
726
|
+
skillsDirectory: ".agentskills/skills",
|
|
727
|
+
autoDiscover: [],
|
|
728
|
+
maxSkillSize: 5000,
|
|
729
|
+
logLevel: "info"
|
|
730
|
+
},
|
|
731
|
+
source: {
|
|
732
|
+
type: "file",
|
|
733
|
+
path: "/test/project/package.json"
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
mockConfigManager.loadConfig.mockResolvedValue(config);
|
|
737
|
+
mockMCPConfigManager.isServerConfigured.mockResolvedValue(false);
|
|
738
|
+
// Execute
|
|
739
|
+
await installCommand({ cwd: "/test/project", agent: "claude" });
|
|
740
|
+
// Verify agentskills server was added even though no skills
|
|
741
|
+
expect(mockMCPConfigManager.isServerConfigured, "/test/project").toHaveBeenCalledWith("claude-desktop", "agentskills", "/test/project");
|
|
742
|
+
expect(mockMCPConfigManager.addServer).toHaveBeenCalledWith("claude-desktop", "agentskills", {
|
|
743
|
+
command: "npx",
|
|
744
|
+
args: ["-y", "@codemcp/agentskills-mcp"],
|
|
745
|
+
env: {}
|
|
746
|
+
}, "/test/project");
|
|
747
|
+
// Should exit with 0 (no skills to install message, but still success)
|
|
748
|
+
expect(processExitSpy).toHaveBeenCalledWith(0);
|
|
749
|
+
});
|
|
750
|
+
it("should use correct command format with npx", async () => {
|
|
751
|
+
// Setup
|
|
752
|
+
const config = {
|
|
753
|
+
skills: {
|
|
754
|
+
"test-skill": "github:user/test-skill#v1.0.0"
|
|
755
|
+
},
|
|
756
|
+
config: {
|
|
757
|
+
skillsDirectory: ".agentskills/skills",
|
|
758
|
+
autoDiscover: [],
|
|
759
|
+
maxSkillSize: 5000,
|
|
760
|
+
logLevel: "info"
|
|
761
|
+
},
|
|
762
|
+
source: {
|
|
763
|
+
type: "file",
|
|
764
|
+
path: "/test/project/package.json"
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
const checkResult = {
|
|
768
|
+
allConfigured: true,
|
|
769
|
+
missing: [],
|
|
770
|
+
configured: []
|
|
771
|
+
};
|
|
772
|
+
mockConfigManager.loadConfig.mockResolvedValue(config);
|
|
773
|
+
mockMCPConfigManager.isServerConfigured.mockResolvedValue(false);
|
|
774
|
+
mockInstaller.install.mockResolvedValue({
|
|
775
|
+
success: true,
|
|
776
|
+
name: "test-skill",
|
|
777
|
+
spec: "github:user/test-skill#v1.0.0",
|
|
778
|
+
resolvedVersion: "1.0.0",
|
|
779
|
+
integrity: "sha512-abc123",
|
|
780
|
+
installPath: "/test/.agentskills/skills/test-skill"
|
|
781
|
+
});
|
|
782
|
+
mockInstaller.loadInstalledSkills.mockResolvedValue([
|
|
783
|
+
{
|
|
784
|
+
metadata: { name: "test-skill", description: "Test skill" },
|
|
785
|
+
body: "Skill content"
|
|
786
|
+
}
|
|
787
|
+
]);
|
|
788
|
+
mockMCPDependencyChecker.collectDependencies.mockReturnValue([]);
|
|
789
|
+
mockMCPDependencyChecker.checkDependencies.mockResolvedValue(checkResult);
|
|
790
|
+
// Execute
|
|
791
|
+
await installCommand({ cwd: "/test/project", agent: "claude" });
|
|
792
|
+
// Verify exact format
|
|
793
|
+
expect(mockMCPConfigManager.addServer).toHaveBeenCalledWith("claude-desktop", "agentskills", {
|
|
794
|
+
command: "npx",
|
|
795
|
+
args: ["-y", "@codemcp/agentskills-mcp"],
|
|
796
|
+
env: {}
|
|
797
|
+
}, "/test/project");
|
|
798
|
+
});
|
|
799
|
+
it("should include empty env object in config", async () => {
|
|
800
|
+
// Setup
|
|
801
|
+
const config = {
|
|
802
|
+
skills: {
|
|
803
|
+
"test-skill": "github:user/test-skill#v1.0.0"
|
|
804
|
+
},
|
|
805
|
+
config: {
|
|
806
|
+
skillsDirectory: ".agentskills/skills",
|
|
807
|
+
autoDiscover: [],
|
|
808
|
+
maxSkillSize: 5000,
|
|
809
|
+
logLevel: "info"
|
|
810
|
+
},
|
|
811
|
+
source: {
|
|
812
|
+
type: "file",
|
|
813
|
+
path: "/test/project/package.json"
|
|
814
|
+
}
|
|
815
|
+
};
|
|
816
|
+
const checkResult = {
|
|
817
|
+
allConfigured: true,
|
|
818
|
+
missing: [],
|
|
819
|
+
configured: []
|
|
820
|
+
};
|
|
821
|
+
mockConfigManager.loadConfig.mockResolvedValue(config);
|
|
822
|
+
mockMCPConfigManager.isServerConfigured.mockResolvedValue(false);
|
|
823
|
+
mockInstaller.install.mockResolvedValue({
|
|
824
|
+
success: true,
|
|
825
|
+
name: "test-skill",
|
|
826
|
+
spec: "github:user/test-skill#v1.0.0",
|
|
827
|
+
resolvedVersion: "1.0.0",
|
|
828
|
+
integrity: "sha512-abc123",
|
|
829
|
+
installPath: "/test/.agentskills/skills/test-skill"
|
|
830
|
+
});
|
|
831
|
+
mockInstaller.loadInstalledSkills.mockResolvedValue([
|
|
832
|
+
{
|
|
833
|
+
metadata: { name: "test-skill", description: "Test skill" },
|
|
834
|
+
body: "Skill content"
|
|
835
|
+
}
|
|
836
|
+
]);
|
|
837
|
+
mockMCPDependencyChecker.collectDependencies.mockReturnValue([]);
|
|
838
|
+
mockMCPDependencyChecker.checkDependencies.mockResolvedValue(checkResult);
|
|
839
|
+
// Execute
|
|
840
|
+
await installCommand({ cwd: "/test/project", agent: "claude" });
|
|
841
|
+
// Verify env is empty object
|
|
842
|
+
expect(mockMCPConfigManager.addServer).toHaveBeenCalledWith("claude-desktop", "agentskills", expect.objectContaining({
|
|
843
|
+
env: {}
|
|
844
|
+
}), "/test/project");
|
|
845
|
+
});
|
|
846
|
+
});
|
|
847
|
+
});
|
|
848
|
+
//# sourceMappingURL=install-agentskills-mcp.test.js.map
|