@codemcp/agentskills-cli 0.0.9 → 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.
@@ -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