@hailer/mcp 0.0.5 โ†’ 0.1.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.
Files changed (120) hide show
  1. package/.claude/agents/ada.md +127 -0
  2. package/.claude/agents/agent-builder.md +151 -0
  3. package/.claude/agents/alejandro.md +66 -0
  4. package/.claude/agents/bjorn.md +305 -0
  5. package/.claude/agents/dmitri.md +61 -0
  6. package/.claude/agents/giuseppe.md +66 -0
  7. package/.claude/agents/gunther.md +355 -0
  8. package/.claude/agents/helga.md +68 -0
  9. package/.claude/agents/kenji.md +58 -0
  10. package/.claude/agents/svetlana.md +394 -0
  11. package/.claude/agents/viktor.md +63 -0
  12. package/.claude/agents/yevgeni.md +60 -0
  13. package/.claude/hooks/agent-failure-detector.cjs +286 -0
  14. package/.claude/hooks/app-edit-guard.cjs +462 -0
  15. package/.claude/hooks/interactive-mode.cjs +59 -0
  16. package/.claude/hooks/mcp-server-guard.cjs +92 -0
  17. package/.claude/hooks/post-scaffold-hook.cjs +31 -0
  18. package/.claude/hooks/src-edit-guard.cjs +208 -0
  19. package/.claude/settings.json +47 -2
  20. package/.claude/skills/insight-join-patterns/SKILL.md +209 -0
  21. package/.env.example +13 -1
  22. package/CLAUDE.md +134 -0
  23. package/dist/app.js +4 -3
  24. package/dist/cli.js +0 -0
  25. package/dist/client/adaptive-documentation-bot.d.ts +0 -2
  26. package/dist/client/adaptive-documentation-bot.js +5 -16
  27. package/dist/client/message-processor.js +5 -0
  28. package/dist/client/providers/anthropic-provider.js +21 -7
  29. package/dist/mcp/UserContextCache.d.ts +14 -0
  30. package/dist/mcp/UserContextCache.js +49 -24
  31. package/dist/mcp/auth.d.ts +7 -0
  32. package/dist/mcp/auth.js +13 -5
  33. package/dist/mcp/hailer-clients.d.ts +5 -2
  34. package/dist/mcp/signal-handler.d.ts +28 -2
  35. package/dist/mcp/signal-handler.js +4 -2
  36. package/dist/mcp/tool-registry.d.ts +55 -2
  37. package/dist/mcp/tool-registry.js +197 -2
  38. package/dist/mcp/tools/app-core.d.ts +15 -0
  39. package/dist/mcp/tools/app-core.js +609 -0
  40. package/dist/mcp/tools/app-marketplace.d.ts +21 -0
  41. package/dist/mcp/tools/app-marketplace.js +1284 -0
  42. package/dist/mcp/tools/app-member.d.ts +11 -0
  43. package/dist/mcp/tools/app-member.js +258 -0
  44. package/dist/mcp/tools/app-scaffold.d.ts +11 -0
  45. package/dist/mcp/tools/app-scaffold.js +743 -0
  46. package/dist/mcp/tools/app.d.ts +13 -22
  47. package/dist/mcp/tools/app.js +17 -2466
  48. package/dist/mcp/tools/file.js +6 -6
  49. package/dist/mcp/tools/insight.d.ts +1 -0
  50. package/dist/mcp/tools/insight.js +203 -64
  51. package/dist/mcp/tools/user.js +3 -9
  52. package/dist/mcp/tools/workflow.js +49 -38
  53. package/dist/mcp/utils/hailer-api-client.js +4 -13
  54. package/dist/mcp/utils/tool-helpers.d.ts +102 -0
  55. package/dist/mcp/utils/tool-helpers.js +179 -0
  56. package/dist/mcp/utils/types.d.ts +6 -0
  57. package/dist/mcp/workspace-cache.d.ts +5 -5
  58. package/dist/mcp/workspace-cache.js +4 -3
  59. package/package.json +1 -1
  60. package/.claude/hooks/PreToolUse.sh +0 -52
  61. package/.claude/hooks/prompt-skill-loader.cjs +0 -553
  62. package/.claude/hooks/skill-loader.cjs +0 -142
  63. package/.claude/settings.local.json +0 -49
  64. package/.claude/skills/MCP-add-app-member-skill/SKILL.md +0 -977
  65. package/.claude/skills/MCP-build-data-app-skill/SKILL.md +0 -372
  66. package/.claude/skills/MCP-create-app-skill/SKILL.md +0 -1101
  67. package/.claude/skills/MCP-create-insight-skill/SKILL.md +0 -1317
  68. package/.claude/skills/MCP-get-insight-data-skill/SKILL.md +0 -1053
  69. package/.claude/skills/MCP-insight-api/SKILL.md +0 -185
  70. package/.claude/skills/MCP-insight-api/references/insight-endpoints.md +0 -514
  71. package/.claude/skills/MCP-install-workflow-skill/SKILL.md +0 -1056
  72. package/.claude/skills/MCP-list-apps-skill/SKILL.md +0 -1010
  73. package/.claude/skills/MCP-list-workflows-minimal-skill/SKILL.md +0 -992
  74. package/.claude/skills/MCP-local-first-skill/SKILL.md +0 -570
  75. package/.claude/skills/MCP-populate-workflow-data-skill/SKILL.md +0 -395
  76. package/.claude/skills/MCP-preview-insight-skill/SKILL.md +0 -1290
  77. package/.claude/skills/MCP-publish-hailer-app-skill/SKILL.md +0 -453
  78. package/.claude/skills/MCP-publish-template-skill/SKILL.md +0 -278
  79. package/.claude/skills/MCP-remove-app-member-skill/SKILL.md +0 -671
  80. package/.claude/skills/MCP-remove-app-skill/SKILL.md +0 -985
  81. package/.claude/skills/MCP-remove-insight-skill/SKILL.md +0 -1011
  82. package/.claude/skills/MCP-remove-workflow-skill/SKILL.md +0 -920
  83. package/.claude/skills/MCP-scaffold-hailer-app-skill/SKILL.md +0 -1237
  84. package/.claude/skills/MCP-update-app-skill/SKILL.md +0 -970
  85. package/.claude/skills/MCP-update-workflow-field-skill/SKILL.md +0 -1098
  86. package/.claude/skills/SDK-create-function-field-skill/SKILL.md +0 -313
  87. package/.claude/skills/SDK-generate-skill/SKILL.md +0 -223
  88. package/.claude/skills/SDK-init-skill/SKILL.md +0 -177
  89. package/.claude/skills/SDK-workspace-setup-skill/SKILL.md +0 -605
  90. package/.claude/skills/SDK-ws-config-skill/SKILL.md +0 -435
  91. package/.claude/skills/activity-api/SKILL.md +0 -96
  92. package/.claude/skills/activity-api/references/activity-endpoints.md +0 -845
  93. package/.claude/skills/agent-building/SKILL.md +0 -243
  94. package/.claude/skills/agent-building/references/architecture-patterns.md +0 -446
  95. package/.claude/skills/agent-building/references/code-examples.md +0 -587
  96. package/.claude/skills/agent-building/references/implementation-guide.md +0 -619
  97. package/.claude/skills/app-api/SKILL.md +0 -219
  98. package/.claude/skills/app-api/references/app-endpoints.md +0 -759
  99. package/.claude/skills/building-hailer-apps-skill/SKILL.md +0 -813
  100. package/.claude/skills/hailer-api/SKILL.md +0 -283
  101. package/.claude/skills/hailer-api/references/activities.md +0 -620
  102. package/.claude/skills/hailer-api/references/authentication.md +0 -216
  103. package/.claude/skills/hailer-api/references/datasets.md +0 -437
  104. package/.claude/skills/hailer-api/references/files.md +0 -301
  105. package/.claude/skills/hailer-api/references/insights.md +0 -469
  106. package/.claude/skills/hailer-api/references/workflows.md +0 -720
  107. package/.claude/skills/hailer-api/references/workspaces-users.md +0 -445
  108. package/.claude/skills/hailer-app-builder/SKILL.md +0 -340
  109. package/.claude/skills/mcp-tools/SKILL.md +0 -419
  110. package/.claude/skills/mcp-tools/references/api-endpoints.md +0 -499
  111. package/.claude/skills/mcp-tools/references/data-structures.md +0 -554
  112. package/.claude/skills/mcp-tools/references/implementation-patterns.md +0 -717
  113. package/.claude/skills/skill-testing/README.md +0 -137
  114. package/.claude/skills/skill-testing/SKILL.md +0 -348
  115. package/.claude/skills/skill-testing/references/test-patterns.md +0 -705
  116. package/.claude/skills/skill-testing/references/testing-guide.md +0 -603
  117. package/.claude/skills/skill-testing/references/validation-checklist.md +0 -537
  118. package/.claude/skills/spawn-app-builder/SKILL.md +0 -366
  119. package/.claude/skills/tool-builder/SKILL.md +0 -328
  120. package/tsconfig.json +0 -23
@@ -1,705 +0,0 @@
1
- # Reusable Test Patterns
2
-
3
- Copy-paste templates and patterns for common skill testing scenarios.
4
-
5
- ## Quick Test Script Template
6
-
7
- Save as `test-{skill-name}.ts`:
8
-
9
- ```typescript
10
- /**
11
- * Quick validation test for {skill-name}
12
- * Run: npx tsx test-{skill-name}.ts
13
- */
14
-
15
- import { SkillLoader } from './src/client/skill-loader';
16
- import { SkillManager } from './src/client/skill-manager';
17
- import * as path from 'path';
18
- import * as fs from 'fs/promises';
19
-
20
- const SKILL_NAME = 'my-skill';
21
- const TEST_QUERIES = [
22
- 'primary keyword test',
23
- 'secondary keyword test',
24
- 'edge case test',
25
- ];
26
-
27
- async function testSkill() {
28
- console.log(`\n๐Ÿงช Testing Skill: ${SKILL_NAME}\n`);
29
-
30
- const skillsPath = path.join(process.cwd(), '..', '.claude', 'skills');
31
- const skillPath = path.join(skillsPath, SKILL_NAME);
32
- const loader = new SkillLoader(skillsPath);
33
- const manager = new SkillManager(loader);
34
-
35
- let passed = 0;
36
- let failed = 0;
37
-
38
- // Test 1: File Structure
39
- console.log('๐Ÿ“ Test 1: File Structure');
40
- try {
41
- const skillMd = await fs.readFile(path.join(skillPath, 'SKILL.md'), 'utf-8');
42
- console.log(` โœ… SKILL.md exists (${skillMd.length} chars)`);
43
- passed++;
44
- } catch (error) {
45
- console.log(` โŒ SKILL.md not found`);
46
- failed++;
47
- }
48
-
49
- // Test 2: Load Skill
50
- console.log('\n๐Ÿ“š Test 2: Skill Loading');
51
- try {
52
- const start = Date.now();
53
- const skill = await loader.load(SKILL_NAME);
54
- const loadTime = Date.now() - start;
55
-
56
- console.log(` โœ… Loaded successfully in ${loadTime}ms`);
57
- console.log(` โœ… Content size: ${(skill.content.length / 1024).toFixed(1)} KB`);
58
- console.log(` โœ… Description: ${skill.description.substring(0, 60)}...`);
59
- passed += 3;
60
- } catch (error) {
61
- console.log(` โŒ Failed to load: ${error}`);
62
- failed++;
63
- }
64
-
65
- // Test 3: Caching
66
- console.log('\nโšก Test 3: Caching');
67
- try {
68
- const start = Date.now();
69
- await loader.load(SKILL_NAME);
70
- const cacheTime = Date.now() - start;
71
-
72
- if (cacheTime < 10) {
73
- console.log(` โœ… Cached load: ${cacheTime}ms (fast!)`);
74
- passed++;
75
- } else {
76
- console.log(` โš ๏ธ Cached load: ${cacheTime}ms (should be < 10ms)`);
77
- failed++;
78
- }
79
- } catch (error) {
80
- console.log(` โŒ Cache test failed: ${error}`);
81
- failed++;
82
- }
83
-
84
- // Test 4: Keyword Matching
85
- console.log('\n๐Ÿ” Test 4: Keyword Matching');
86
- for (const query of TEST_QUERIES) {
87
- try {
88
- const guidance = await manager.analyzeRequest(query);
89
-
90
- if (guidance.skills.includes(SKILL_NAME)) {
91
- console.log(` โœ… "${query}" โ†’ Matched (${(guidance.confidence * 100).toFixed(0)}%)`);
92
- passed++;
93
- } else {
94
- console.log(` โŒ "${query}" โ†’ No match`);
95
- failed++;
96
- }
97
- } catch (error) {
98
- console.log(` โŒ "${query}" โ†’ Error: ${error}`);
99
- failed++;
100
- }
101
- }
102
-
103
- // Test 5: Content Quality
104
- console.log('\n๐Ÿ“ Test 5: Content Quality');
105
- try {
106
- const skill = await loader.load(SKILL_NAME);
107
- const content = skill.content;
108
-
109
- const checks = [
110
- { name: 'Has headings', test: () => content.includes('#'), },
111
- { name: 'Has examples', test: () => content.includes('```') },
112
- { name: 'Not too short', test: () => content.length > 500 },
113
- { name: 'Not too long', test: () => content.length < 100000 },
114
- { name: 'Has structure', test: () => content.includes('##') },
115
- ];
116
-
117
- for (const check of checks) {
118
- if (check.test()) {
119
- console.log(` โœ… ${check.name}`);
120
- passed++;
121
- } else {
122
- console.log(` โŒ ${check.name}`);
123
- failed++;
124
- }
125
- }
126
- } catch (error) {
127
- console.log(` โŒ Quality checks failed: ${error}`);
128
- failed++;
129
- }
130
-
131
- // Summary
132
- console.log('\n' + '='.repeat(50));
133
- console.log(`Results: ${passed} passed, ${failed} failed`);
134
- console.log('='.repeat(50) + '\n');
135
-
136
- if (failed === 0) {
137
- console.log(`โœ… ${SKILL_NAME} is ready to use!\n`);
138
- process.exit(0);
139
- } else {
140
- console.log(`โŒ ${SKILL_NAME} needs fixes before deployment\n`);
141
- process.exit(1);
142
- }
143
- }
144
-
145
- testSkill().catch((error) => {
146
- console.error('\nโŒ Test crashed:', error);
147
- process.exit(1);
148
- });
149
- ```
150
-
151
- ## Pattern: Test All Skills
152
-
153
- Save as `test-all-skills.ts`:
154
-
155
- ```typescript
156
- /**
157
- * Test all skills in the system
158
- * Run: npx tsx test-all-skills.ts
159
- */
160
-
161
- import { SkillLoader } from './src/client/skill-loader';
162
- import { SkillManager } from './src/client/skill-manager';
163
- import * as path from 'path';
164
-
165
- interface SkillTestResult {
166
- skillName: string;
167
- passed: boolean;
168
- loadTime: number;
169
- cacheTime: number;
170
- sizeKB: number;
171
- errors: string[];
172
- }
173
-
174
- async function testSkill(
175
- skillName: string,
176
- loader: SkillLoader,
177
- manager: SkillManager
178
- ): Promise<SkillTestResult> {
179
- const result: SkillTestResult = {
180
- skillName,
181
- passed: true,
182
- loadTime: 0,
183
- cacheTime: 0,
184
- sizeKB: 0,
185
- errors: [],
186
- };
187
-
188
- try {
189
- // Test loading
190
- loader.clearCache(skillName);
191
- const start1 = Date.now();
192
- const skill = await loader.load(skillName);
193
- result.loadTime = Date.now() - start1;
194
- result.sizeKB = skill.content.length / 1024;
195
-
196
- // Test caching
197
- const start2 = Date.now();
198
- await loader.load(skillName);
199
- result.cacheTime = Date.now() - start2;
200
-
201
- // Validations
202
- if (result.loadTime > 150) {
203
- result.errors.push(`Load time too high: ${result.loadTime}ms`);
204
- result.passed = false;
205
- }
206
-
207
- if (result.cacheTime > 10) {
208
- result.errors.push(`Cache time too high: ${result.cacheTime}ms`);
209
- result.passed = false;
210
- }
211
-
212
- if (result.sizeKB > 100) {
213
- result.errors.push(`Size too large: ${result.sizeKB.toFixed(1)} KB`);
214
- result.passed = false;
215
- }
216
-
217
- if (skill.content.length < 500) {
218
- result.errors.push('Content too short');
219
- result.passed = false;
220
- }
221
- } catch (error) {
222
- result.passed = false;
223
- result.errors.push(String(error));
224
- }
225
-
226
- return result;
227
- }
228
-
229
- async function testAllSkills() {
230
- console.log('๐Ÿงช Testing All Skills\n');
231
-
232
- const skillsPath = path.join(process.cwd(), '..', '.claude', 'skills');
233
- const loader = new SkillLoader(skillsPath);
234
- const manager = new SkillManager(loader);
235
-
236
- const allSkills = await loader.getAvailableSkills();
237
- console.log(`Found ${allSkills.length} skills to test\n`);
238
-
239
- const results: SkillTestResult[] = [];
240
-
241
- for (const skillName of allSkills) {
242
- process.stdout.write(`Testing ${skillName}... `);
243
- const result = await testSkill(skillName, loader, manager);
244
- results.push(result);
245
-
246
- if (result.passed) {
247
- console.log('โœ…');
248
- } else {
249
- console.log('โŒ');
250
- result.errors.forEach(err => console.log(` - ${err}`));
251
- }
252
- }
253
-
254
- // Summary
255
- console.log('\n' + '='.repeat(70));
256
- console.log('Summary:');
257
- console.log('='.repeat(70));
258
-
259
- const passed = results.filter(r => r.passed).length;
260
- const failed = results.filter(r => !r.passed).length;
261
-
262
- console.log(`\nSkills: ${passed} passed, ${failed} failed\n`);
263
-
264
- console.log('Performance Metrics:');
265
- console.log('โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€');
266
- console.log('Skill Name'.padEnd(25) + 'Load'.padEnd(15) + 'Cache'.padEnd(15) + 'Size');
267
- console.log('โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€');
268
-
269
- for (const result of results) {
270
- const status = result.passed ? 'โœ…' : 'โŒ';
271
- const name = `${status} ${result.skillName}`.padEnd(25);
272
- const load = `${result.loadTime}ms`.padEnd(15);
273
- const cache = `${result.cacheTime}ms`.padEnd(15);
274
- const size = `${result.sizeKB.toFixed(1)} KB`;
275
-
276
- console.log(name + load + cache + size);
277
- }
278
-
279
- console.log('โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n');
280
-
281
- // Exit with error if any failed
282
- process.exit(failed > 0 ? 1 : 0);
283
- }
284
-
285
- testAllSkills().catch(console.error);
286
- ```
287
-
288
- ## Pattern: Keyword Matching Tests
289
-
290
- Save as `test-keywords.ts`:
291
-
292
- ```typescript
293
- /**
294
- * Test keyword matching accuracy
295
- * Run: npx tsx test-keywords.ts
296
- */
297
-
298
- import { SkillManager } from './src/client/skill-manager';
299
- import { SkillLoader } from './src/client/skill-loader';
300
- import * as path from 'path';
301
-
302
- interface KeywordTest {
303
- query: string;
304
- expectedSkill: string;
305
- minConfidence?: number;
306
- }
307
-
308
- const KEYWORD_TESTS: KeywordTest[] = [
309
- // MCP Tools
310
- { query: 'create a workflow', expectedSkill: 'mcp-tools', minConfidence: 0.6 },
311
- { query: 'list workflow phases', expectedSkill: 'mcp-tools', minConfidence: 0.6 },
312
- { query: 'update workflow field', expectedSkill: 'mcp-tools', minConfidence: 0.6 },
313
- { query: 'show me the schema', expectedSkill: 'mcp-tools', minConfidence: 0.5 },
314
-
315
- // Hailer API
316
- { query: 'list all activities', expectedSkill: 'hailer-api', minConfidence: 0.6 },
317
- { query: 'filter activities by date', expectedSkill: 'hailer-api', minConfidence: 0.6 },
318
- { query: 'search for tasks', expectedSkill: 'hailer-api', minConfidence: 0.5 },
319
- { query: 'show activity details', expectedSkill: 'hailer-api', minConfidence: 0.5 },
320
-
321
- // Agent Building
322
- { query: 'how do I build an agent?', expectedSkill: 'agent-building', minConfidence: 0.5 },
323
- { query: 'create skill system', expectedSkill: 'agent-building', minConfidence: 0.5 },
324
- { query: 'multi-agent architecture', expectedSkill: 'agent-building', minConfidence: 0.5 },
325
-
326
- // Skill Testing
327
- { query: 'how do I test skills?', expectedSkill: 'skill-testing', minConfidence: 0.5 },
328
- { query: 'validate skill quality', expectedSkill: 'skill-testing', minConfidence: 0.5 },
329
- { query: 'skill testing patterns', expectedSkill: 'skill-testing', minConfidence: 0.6 },
330
- ];
331
-
332
- async function testKeywordMatching() {
333
- console.log('๐Ÿ” Testing Keyword Matching\n');
334
-
335
- const skillsPath = path.join(process.cwd(), '..', '.claude', 'skills');
336
- const loader = new SkillLoader(skillsPath);
337
- const manager = new SkillManager(loader);
338
-
339
- let passed = 0;
340
- let failed = 0;
341
-
342
- for (const test of KEYWORD_TESTS) {
343
- process.stdout.write(`Testing: "${test.query.substring(0, 40)}"... `);
344
-
345
- try {
346
- const guidance = await manager.analyzeRequest(test.query);
347
- const matchedSkill = guidance.skills[0];
348
- const confidence = guidance.confidence;
349
- const minConf = test.minConfidence || 0.5;
350
-
351
- const skillMatches = matchedSkill === test.expectedSkill;
352
- const confEnough = confidence >= minConf;
353
-
354
- if (skillMatches && confEnough) {
355
- console.log(`โœ… ${test.expectedSkill} (${(confidence * 100).toFixed(0)}%)`);
356
- passed++;
357
- } else if (skillMatches && !confEnough) {
358
- console.log(`โš ๏ธ ${test.expectedSkill} but low confidence (${(confidence * 100).toFixed(0)}%)`);
359
- failed++;
360
- } else {
361
- console.log(`โŒ Got ${matchedSkill || 'none'}, expected ${test.expectedSkill}`);
362
- failed++;
363
- }
364
- } catch (error) {
365
- console.log(`โŒ Error: ${error}`);
366
- failed++;
367
- }
368
- }
369
-
370
- // Summary
371
- console.log('\n' + '='.repeat(50));
372
- const total = passed + failed;
373
- const accuracy = (passed / total * 100).toFixed(1);
374
- console.log(`Results: ${passed}/${total} passed (${accuracy}% accuracy)`);
375
- console.log('='.repeat(50) + '\n');
376
-
377
- process.exit(failed > 0 ? 1 : 0);
378
- }
379
-
380
- testKeywordMatching().catch(console.error);
381
- ```
382
-
383
- ## Pattern: Performance Benchmarks
384
-
385
- Save as `benchmark-skills.ts`:
386
-
387
- ```typescript
388
- /**
389
- * Benchmark skill system performance
390
- * Run: npx tsx benchmark-skills.ts
391
- */
392
-
393
- import { SkillLoader } from './src/client/skill-loader';
394
- import { SkillManager } from './src/client/skill-manager';
395
- import * as path from 'path';
396
-
397
- interface BenchmarkResult {
398
- operation: string;
399
- iterations: number;
400
- totalTime: number;
401
- avgTime: number;
402
- minTime: number;
403
- maxTime: number;
404
- }
405
-
406
- async function benchmark(
407
- name: string,
408
- fn: () => Promise<void>,
409
- iterations: number
410
- ): Promise<BenchmarkResult> {
411
- const times: number[] = [];
412
-
413
- // Warmup
414
- await fn();
415
-
416
- // Benchmark
417
- for (let i = 0; i < iterations; i++) {
418
- const start = performance.now();
419
- await fn();
420
- times.push(performance.now() - start);
421
- }
422
-
423
- const totalTime = times.reduce((a, b) => a + b, 0);
424
- const avgTime = totalTime / iterations;
425
- const minTime = Math.min(...times);
426
- const maxTime = Math.max(...times);
427
-
428
- return {
429
- operation: name,
430
- iterations,
431
- totalTime,
432
- avgTime,
433
- minTime,
434
- maxTime,
435
- };
436
- }
437
-
438
- function printResult(result: BenchmarkResult) {
439
- console.log(`\n${result.operation}`);
440
- console.log('โ”€'.repeat(50));
441
- console.log(`Iterations: ${result.iterations}`);
442
- console.log(`Total time: ${result.totalTime.toFixed(2)}ms`);
443
- console.log(`Average: ${result.avgTime.toFixed(2)}ms`);
444
- console.log(`Min: ${result.minTime.toFixed(2)}ms`);
445
- console.log(`Max: ${result.maxTime.toFixed(2)}ms`);
446
- }
447
-
448
- async function runBenchmarks() {
449
- console.log('โšก Skill System Performance Benchmarks\n');
450
-
451
- const skillsPath = path.join(process.cwd(), '..', '.claude', 'skills');
452
- const loader = new SkillLoader(skillsPath);
453
- const manager = new SkillManager(loader);
454
-
455
- // Benchmark 1: First load (uncached)
456
- const firstLoadResult = await benchmark(
457
- 'First Load (from disk)',
458
- async () => {
459
- loader.clearCache('mcp-tools');
460
- await loader.load('mcp-tools');
461
- },
462
- 10
463
- );
464
- printResult(firstLoadResult);
465
-
466
- // Benchmark 2: Cached load
467
- await loader.load('mcp-tools'); // Ensure cached
468
- const cachedLoadResult = await benchmark(
469
- 'Cached Load (from memory)',
470
- async () => {
471
- await loader.load('mcp-tools');
472
- },
473
- 100
474
- );
475
- printResult(cachedLoadResult);
476
-
477
- // Benchmark 3: Keyword analysis
478
- const analysisResult = await benchmark(
479
- 'Keyword Analysis',
480
- async () => {
481
- await manager.analyzeRequest('create a workflow with phases');
482
- },
483
- 100
484
- );
485
- printResult(analysisResult);
486
-
487
- // Benchmark 4: Full flow (analysis + load)
488
- const fullFlowResult = await benchmark(
489
- 'Full Flow (analysis + load)',
490
- async () => {
491
- const guidance = await manager.analyzeRequest('create workflow');
492
- // Skill is already loaded during analysis
493
- },
494
- 100
495
- );
496
- printResult(fullFlowResult);
497
-
498
- // Summary
499
- console.log('\n' + '='.repeat(50));
500
- console.log('Performance Summary:');
501
- console.log('='.repeat(50));
502
- console.log(`First load: ${firstLoadResult.avgTime.toFixed(2)}ms avg (target: < 100ms)`);
503
- console.log(`Cached load: ${cachedLoadResult.avgTime.toFixed(2)}ms avg (target: < 10ms)`);
504
- console.log(`Analysis: ${analysisResult.avgTime.toFixed(2)}ms avg (target: < 5ms)`);
505
- console.log(`Full flow: ${fullFlowResult.avgTime.toFixed(2)}ms avg (target: < 50ms)`);
506
- console.log('='.repeat(50) + '\n');
507
-
508
- // Check if targets are met
509
- const targets = [
510
- { name: 'First load', actual: firstLoadResult.avgTime, target: 100 },
511
- { name: 'Cached load', actual: cachedLoadResult.avgTime, target: 10 },
512
- { name: 'Analysis', actual: analysisResult.avgTime, target: 5 },
513
- { name: 'Full flow', actual: fullFlowResult.avgTime, target: 50 },
514
- ];
515
-
516
- const allPassed = targets.every(t => t.actual <= t.target);
517
-
518
- if (allPassed) {
519
- console.log('โœ… All performance targets met!\n');
520
- process.exit(0);
521
- } else {
522
- console.log('โŒ Some performance targets not met:\n');
523
- targets.forEach(t => {
524
- if (t.actual > t.target) {
525
- console.log(` โš ๏ธ ${t.name}: ${t.actual.toFixed(2)}ms (target: ${t.target}ms)`);
526
- }
527
- });
528
- console.log();
529
- process.exit(1);
530
- }
531
- }
532
-
533
- runBenchmarks().catch(console.error);
534
- ```
535
-
536
- ## Pattern: Integration with Jest
537
-
538
- If using Jest for testing, here's the pattern:
539
-
540
- ```typescript
541
- // skills.test.ts
542
- import { SkillLoader } from '../src/client/skill-loader';
543
- import { SkillManager } from '../src/client/skill-manager';
544
- import * as path from 'path';
545
-
546
- describe('Skill System', () => {
547
- let loader: SkillLoader;
548
- let manager: SkillManager;
549
-
550
- beforeAll(() => {
551
- const skillsPath = path.join(__dirname, '../../.claude/skills');
552
- loader = new SkillLoader(skillsPath);
553
- manager = new SkillManager(loader);
554
- });
555
-
556
- afterEach(() => {
557
- loader.clearCache();
558
- });
559
-
560
- describe('SkillLoader', () => {
561
- it('should load skill successfully', async () => {
562
- const skill = await loader.load('mcp-tools');
563
- expect(skill.name).toBe('mcp-tools');
564
- expect(skill.content.length).toBeGreaterThan(0);
565
- });
566
-
567
- it('should cache skills', async () => {
568
- await loader.load('mcp-tools');
569
- const start = performance.now();
570
- await loader.load('mcp-tools');
571
- const time = performance.now() - start;
572
- expect(time).toBeLessThan(10);
573
- });
574
-
575
- it('should handle errors', async () => {
576
- await expect(loader.load('invalid-skill')).rejects.toThrow();
577
- });
578
- });
579
-
580
- describe('SkillManager', () => {
581
- it('should match keywords', async () => {
582
- const guidance = await manager.analyzeRequest('create workflow');
583
- expect(guidance.skills).toContain('mcp-tools');
584
- });
585
-
586
- it('should return default for non-matching queries', async () => {
587
- const guidance = await manager.analyzeRequest('xyz123random');
588
- expect(guidance.skills).toHaveLength(0);
589
- expect(guidance.confidence).toBeLessThan(0.5);
590
- });
591
- });
592
- });
593
- ```
594
-
595
- ## Pattern: CI/CD Integration
596
-
597
- ```yaml
598
- # .github/workflows/test-skills.yml
599
- name: Test Skills
600
-
601
- on:
602
- push:
603
- paths:
604
- - '.claude/skills/**'
605
- - 'server/src/client/skill-*.ts'
606
- pull_request:
607
- paths:
608
- - '.claude/skills/**'
609
- - 'server/src/client/skill-*.ts'
610
-
611
- jobs:
612
- test-skills:
613
- runs-on: ubuntu-latest
614
- steps:
615
- - uses: actions/checkout@v3
616
-
617
- - uses: actions/setup-node@v3
618
- with:
619
- node-version: '18'
620
-
621
- - name: Install dependencies
622
- working-directory: ./server
623
- run: npm install
624
-
625
- - name: Run skill tests
626
- working-directory: ./server
627
- run: npx tsx test-all-skills.ts
628
-
629
- - name: Run keyword tests
630
- working-directory: ./server
631
- run: npx tsx test-keywords.ts
632
-
633
- - name: Run performance benchmarks
634
- working-directory: ./server
635
- run: npx tsx benchmark-skills.ts
636
- ```
637
-
638
- ## Pattern: Watch Mode for Development
639
-
640
- ```typescript
641
- /**
642
- * Watch mode for skill development
643
- * Run: npx tsx watch-skills.ts
644
- *
645
- * Automatically re-tests skills when files change
646
- */
647
-
648
- import * as chokidar from 'chokidar';
649
- import { exec } from 'child_process';
650
- import * as path from 'path';
651
-
652
- const skillsPath = path.join(process.cwd(), '..', '.claude', 'skills');
653
-
654
- console.log('๐Ÿ‘€ Watching skills directory for changes...\n');
655
-
656
- const watcher = chokidar.watch(skillsPath, {
657
- ignored: /(^|[\/\\])\../, // ignore dotfiles
658
- persistent: true,
659
- });
660
-
661
- let testTimeout: NodeJS.Timeout;
662
-
663
- function runTests() {
664
- console.log('\n๐Ÿงช Running tests...\n');
665
- exec('npx tsx test-all-skills.ts', (error, stdout, stderr) => {
666
- if (stdout) console.log(stdout);
667
- if (stderr) console.error(stderr);
668
- console.log('\n๐Ÿ‘€ Watching for changes...\n');
669
- });
670
- }
671
-
672
- watcher.on('change', (filePath) => {
673
- console.log(`\n๐Ÿ“ Changed: ${path.relative(skillsPath, filePath)}`);
674
-
675
- // Debounce: wait 500ms before running tests
676
- clearTimeout(testTimeout);
677
- testTimeout = setTimeout(runTests, 500);
678
- });
679
-
680
- watcher.on('add', (filePath) => {
681
- console.log(`\nโž• Added: ${path.relative(skillsPath, filePath)}`);
682
- clearTimeout(testTimeout);
683
- testTimeout = setTimeout(runTests, 500);
684
- });
685
-
686
- // Initial test run
687
- runTests();
688
- ```
689
-
690
- ## Usage Tips
691
-
692
- 1. **Start with the quick test script** - Fastest way to validate a new skill
693
- 2. **Use test-all-skills** - Before committing changes to the skill system
694
- 3. **Run keyword tests** - When adding new skills or modifying keyword mappings
695
- 4. **Benchmark regularly** - Catch performance regressions early
696
- 5. **Use watch mode** - During active skill development for instant feedback
697
-
698
- ## Next Steps
699
-
700
- After using these patterns:
701
- 1. Customize tests for your specific skills
702
- 2. Add skill-specific validation logic
703
- 3. Integrate with your CI/CD pipeline
704
- 4. Set up monitoring in production
705
- 5. Collect feedback and iterate