File size: 5,502 Bytes
52efc7b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ActivateSkillTool } from './activate-skill.js';
import type { Config } from '../config/config.js';
import type { MessageBus } from '../confirmation-bus/message-bus.js';
import { createMockMessageBus } from '../test-utils/mock-message-bus.js';

vi.mock('../utils/getFolderStructure.js', () => ({
  getFolderStructure: vi.fn().mockResolvedValue('Mock folder structure'),
}));

describe('ActivateSkillTool', () => {
  let mockConfig: Config;
  let tool: ActivateSkillTool;
  let mockMessageBus: MessageBus;

  beforeEach(() => {
    mockMessageBus = createMockMessageBus();
    const skills = [
      {
        name: 'test-skill',
        description: 'A test skill',
        location: '/path/to/test-skill/SKILL.md',
      },
    ];
    mockConfig = {
      getWorkspaceContext: vi.fn().mockReturnValue({
        addDirectory: vi.fn(),
      }),
      getSkillManager: vi.fn().mockReturnValue({
        getSkills: vi.fn().mockReturnValue(skills),
        getAllSkills: vi.fn().mockReturnValue(skills),
        getSkill: vi.fn().mockImplementation((name: string) => {
          if (name === 'test-skill') {
            return {
              name: 'test-skill',
              description: 'A test skill',
              location: '/path/to/test-skill/SKILL.md',
              body: 'Skill instructions content.',
            };
          }
          return null;
        }),
        activateSkill: vi.fn(),
      }),
    } as unknown as Config;
    tool = new ActivateSkillTool(mockConfig, mockMessageBus);
  });

  it('should return enhanced description', () => {
    const params = { name: 'test-skill' };
    const invocation = tool.build(params);
    expect(invocation.getDescription()).toBe('"test-skill": A test skill');
  });

  it('should return enhanced confirmation details', async () => {
    const params = { name: 'test-skill' };
    const invocation = tool.build(params);
    const details = await (
      invocation as unknown as {
        getConfirmationDetails: (signal: AbortSignal) => Promise<{
          prompt: string;
          title: string;
        }>;
      }
    ).getConfirmationDetails(new AbortController().signal);

    expect(details.title).toBe('Activate Skill: test-skill');
    expect(details.prompt).toContain('enable the specialized agent skill');
    expect(details.prompt).toContain('A test skill');
    expect(details.prompt).toContain('Mock folder structure');
  });

  it('should skip confirmation for built-in skills', async () => {
    const builtinSkill = {
      name: 'builtin-skill',
      description: 'A built-in skill',
      location: '/path/to/builtin/SKILL.md',
      isBuiltin: true,
      body: 'Built-in instructions',
    };
    vi.mocked(mockConfig.getSkillManager().getSkill).mockReturnValue(
      builtinSkill,
    );
    vi.mocked(mockConfig.getSkillManager().getSkills).mockReturnValue([
      builtinSkill,
    ]);

    const params = { name: 'builtin-skill' };
    const toolWithBuiltin = new ActivateSkillTool(mockConfig, mockMessageBus);
    const invocation = toolWithBuiltin.build(params);

    const details = await (
      invocation as unknown as {
        getConfirmationDetails: (signal: AbortSignal) => Promise<unknown>;
      }
    ).getConfirmationDetails(new AbortController().signal);

    expect(details).toBe(false);
  });

  it('should activate a valid skill and return its content in XML tags', async () => {
    const params = { name: 'test-skill' };
    const invocation = tool.build(params);
    const result = await invocation.execute({
      abortSignal: new AbortController().signal,
    });

    expect(mockConfig.getSkillManager().activateSkill).toHaveBeenCalledWith(
      'test-skill',
    );
    expect(mockConfig.getWorkspaceContext().addDirectory).toHaveBeenCalledWith(
      '/path/to/test-skill',
    );
    expect(result.llmContent).toContain('<activated_skill name="test-skill">');
    expect(result.llmContent).toContain('<instructions>');
    expect(result.llmContent).toContain('Skill instructions content.');
    expect(result.llmContent).toContain('</instructions>');
    expect(result.llmContent).toContain('<available_resources>');
    expect(result.llmContent).toContain('Mock folder structure');
    expect(result.llmContent).toContain('</available_resources>');
    expect(result.llmContent).toContain('</activated_skill>');
    expect(result.returnDisplay).toContain('Skill **test-skill** activated');
    expect(result.returnDisplay).toContain('Mock folder structure');
  });

  it('should throw error if skill is not in enum', async () => {
    const params = { name: 'non-existent' };
    expect(() => tool.build(params as { name: string })).toThrow();
  });

  it('should return an error if skill content cannot be read', async () => {
    vi.mocked(mockConfig.getSkillManager().getSkill).mockReturnValue(null);
    const params = { name: 'test-skill' };
    const invocation = tool.build(params);
    const result = await invocation.execute({
      abortSignal: new AbortController().signal,
    });

    expect(result.llmContent).toContain('Error: Skill "test-skill" not found.');
    expect(mockConfig.getSkillManager().activateSkill).not.toHaveBeenCalled();
  });

  it('should validate that name is provided', () => {
    expect(() =>
      tool.build({ name: '' } as unknown as { name: string }),
    ).toThrow();
  });
});