@hailer/mcp 0.0.1
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/.claude/commands/tool-builder.md +37 -0
- package/.claude/commands/ws-pull.md +44 -0
- package/.claude/settings.json +8 -0
- package/.claude/settings.local.json +49 -0
- package/.claude/skills/activity-api/SKILL.md +96 -0
- package/.claude/skills/activity-api/references/activity-endpoints.md +845 -0
- package/.claude/skills/add-app-member-skill/SKILL.md +977 -0
- package/.claude/skills/agent-building/SKILL.md +243 -0
- package/.claude/skills/agent-building/references/architecture-patterns.md +446 -0
- package/.claude/skills/agent-building/references/code-examples.md +587 -0
- package/.claude/skills/agent-building/references/implementation-guide.md +619 -0
- package/.claude/skills/app-api/SKILL.md +219 -0
- package/.claude/skills/app-api/references/app-endpoints.md +759 -0
- package/.claude/skills/building-hailer-apps-skill/SKILL.md +548 -0
- package/.claude/skills/create-app-skill/SKILL.md +1101 -0
- package/.claude/skills/create-insight-skill/SKILL.md +1317 -0
- package/.claude/skills/get-insight-data-skill/SKILL.md +1053 -0
- package/.claude/skills/hailer-api/SKILL.md +283 -0
- package/.claude/skills/hailer-api/references/activities.md +620 -0
- package/.claude/skills/hailer-api/references/authentication.md +216 -0
- package/.claude/skills/hailer-api/references/datasets.md +437 -0
- package/.claude/skills/hailer-api/references/files.md +301 -0
- package/.claude/skills/hailer-api/references/insights.md +469 -0
- package/.claude/skills/hailer-api/references/workflows.md +720 -0
- package/.claude/skills/hailer-api/references/workspaces-users.md +445 -0
- package/.claude/skills/insight-api/SKILL.md +185 -0
- package/.claude/skills/insight-api/references/insight-endpoints.md +514 -0
- package/.claude/skills/install-workflow-skill/SKILL.md +1056 -0
- package/.claude/skills/list-apps-skill/SKILL.md +1010 -0
- package/.claude/skills/list-workflows-minimal-skill/SKILL.md +992 -0
- package/.claude/skills/local-first-skill/SKILL.md +570 -0
- package/.claude/skills/mcp-tools/SKILL.md +419 -0
- package/.claude/skills/mcp-tools/references/api-endpoints.md +499 -0
- package/.claude/skills/mcp-tools/references/data-structures.md +554 -0
- package/.claude/skills/mcp-tools/references/implementation-patterns.md +717 -0
- package/.claude/skills/preview-insight-skill/SKILL.md +1290 -0
- package/.claude/skills/publish-hailer-app-skill/SKILL.md +453 -0
- package/.claude/skills/remove-app-member-skill/SKILL.md +671 -0
- package/.claude/skills/remove-app-skill/SKILL.md +985 -0
- package/.claude/skills/remove-insight-skill/SKILL.md +1011 -0
- package/.claude/skills/remove-workflow-skill/SKILL.md +920 -0
- package/.claude/skills/scaffold-hailer-app-skill/SKILL.md +1034 -0
- package/.claude/skills/skill-testing/README.md +137 -0
- package/.claude/skills/skill-testing/SKILL.md +348 -0
- package/.claude/skills/skill-testing/references/test-patterns.md +705 -0
- package/.claude/skills/skill-testing/references/testing-guide.md +603 -0
- package/.claude/skills/skill-testing/references/validation-checklist.md +537 -0
- package/.claude/skills/tool-builder/SKILL.md +328 -0
- package/.claude/skills/update-app-skill/SKILL.md +970 -0
- package/.claude/skills/update-workflow-field-skill/SKILL.md +1098 -0
- package/.env.example +81 -0
- package/.mcp.json +13 -0
- package/README.md +297 -0
- package/dist/app.d.ts +4 -0
- package/dist/app.js +74 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +5 -0
- package/dist/client/adaptive-documentation-bot.d.ts +108 -0
- package/dist/client/adaptive-documentation-bot.js +475 -0
- package/dist/client/adaptive-documentation-types.d.ts +66 -0
- package/dist/client/adaptive-documentation-types.js +9 -0
- package/dist/client/agent-activity-bot.d.ts +51 -0
- package/dist/client/agent-activity-bot.js +166 -0
- package/dist/client/agent-tracker.d.ts +499 -0
- package/dist/client/agent-tracker.js +659 -0
- package/dist/client/description-updater.d.ts +56 -0
- package/dist/client/description-updater.js +259 -0
- package/dist/client/log-parser.d.ts +72 -0
- package/dist/client/log-parser.js +387 -0
- package/dist/client/mcp-client.d.ts +50 -0
- package/dist/client/mcp-client.js +532 -0
- package/dist/client/message-processor.d.ts +35 -0
- package/dist/client/message-processor.js +352 -0
- package/dist/client/multi-bot-manager.d.ts +24 -0
- package/dist/client/multi-bot-manager.js +74 -0
- package/dist/client/providers/anthropic-provider.d.ts +19 -0
- package/dist/client/providers/anthropic-provider.js +631 -0
- package/dist/client/providers/llm-provider.d.ts +47 -0
- package/dist/client/providers/llm-provider.js +367 -0
- package/dist/client/providers/openai-provider.d.ts +23 -0
- package/dist/client/providers/openai-provider.js +621 -0
- package/dist/client/simple-llm-caller.d.ts +19 -0
- package/dist/client/simple-llm-caller.js +100 -0
- package/dist/client/skill-generator.d.ts +81 -0
- package/dist/client/skill-generator.js +386 -0
- package/dist/client/test-adaptive-bot.d.ts +9 -0
- package/dist/client/test-adaptive-bot.js +82 -0
- package/dist/client/token-pricing.d.ts +38 -0
- package/dist/client/token-pricing.js +127 -0
- package/dist/client/token-tracker.d.ts +232 -0
- package/dist/client/token-tracker.js +457 -0
- package/dist/client/token-usage-bot.d.ts +53 -0
- package/dist/client/token-usage-bot.js +153 -0
- package/dist/client/tool-executor.d.ts +69 -0
- package/dist/client/tool-executor.js +159 -0
- package/dist/client/tool-schema-loader.d.ts +60 -0
- package/dist/client/tool-schema-loader.js +178 -0
- package/dist/client/types.d.ts +69 -0
- package/dist/client/types.js +7 -0
- package/dist/config.d.ts +162 -0
- package/dist/config.js +296 -0
- package/dist/core.d.ts +26 -0
- package/dist/core.js +147 -0
- package/dist/lib/context-manager.d.ts +111 -0
- package/dist/lib/context-manager.js +431 -0
- package/dist/lib/logger.d.ts +74 -0
- package/dist/lib/logger.js +277 -0
- package/dist/lib/materialize.d.ts +3 -0
- package/dist/lib/materialize.js +101 -0
- package/dist/lib/normalizedName.d.ts +7 -0
- package/dist/lib/normalizedName.js +48 -0
- package/dist/lib/prompt-length-manager.d.ts +81 -0
- package/dist/lib/prompt-length-manager.js +457 -0
- package/dist/lib/terminal-prompt.d.ts +9 -0
- package/dist/lib/terminal-prompt.js +108 -0
- package/dist/mcp/UserContextCache.d.ts +56 -0
- package/dist/mcp/UserContextCache.js +163 -0
- package/dist/mcp/auth.d.ts +2 -0
- package/dist/mcp/auth.js +29 -0
- package/dist/mcp/hailer-clients.d.ts +42 -0
- package/dist/mcp/hailer-clients.js +246 -0
- package/dist/mcp/signal-handler.d.ts +45 -0
- package/dist/mcp/signal-handler.js +317 -0
- package/dist/mcp/tool-registry.d.ts +100 -0
- package/dist/mcp/tool-registry.js +306 -0
- package/dist/mcp/tools/activity.d.ts +15 -0
- package/dist/mcp/tools/activity.js +955 -0
- package/dist/mcp/tools/app.d.ts +20 -0
- package/dist/mcp/tools/app.js +1488 -0
- package/dist/mcp/tools/discussion.d.ts +19 -0
- package/dist/mcp/tools/discussion.js +950 -0
- package/dist/mcp/tools/file.d.ts +15 -0
- package/dist/mcp/tools/file.js +119 -0
- package/dist/mcp/tools/insight.d.ts +17 -0
- package/dist/mcp/tools/insight.js +806 -0
- package/dist/mcp/tools/skill.d.ts +10 -0
- package/dist/mcp/tools/skill.js +279 -0
- package/dist/mcp/tools/user.d.ts +10 -0
- package/dist/mcp/tools/user.js +108 -0
- package/dist/mcp/tools/workflow-template.d.ts +19 -0
- package/dist/mcp/tools/workflow-template.js +822 -0
- package/dist/mcp/tools/workflow.d.ts +18 -0
- package/dist/mcp/tools/workflow.js +1362 -0
- package/dist/mcp/utils/api-errors.d.ts +45 -0
- package/dist/mcp/utils/api-errors.js +160 -0
- package/dist/mcp/utils/data-transformers.d.ts +102 -0
- package/dist/mcp/utils/data-transformers.js +194 -0
- package/dist/mcp/utils/file-upload.d.ts +33 -0
- package/dist/mcp/utils/file-upload.js +148 -0
- package/dist/mcp/utils/hailer-api-client.d.ts +120 -0
- package/dist/mcp/utils/hailer-api-client.js +323 -0
- package/dist/mcp/utils/index.d.ts +13 -0
- package/dist/mcp/utils/index.js +39 -0
- package/dist/mcp/utils/logger.d.ts +42 -0
- package/dist/mcp/utils/logger.js +103 -0
- package/dist/mcp/utils/types.d.ts +286 -0
- package/dist/mcp/utils/types.js +7 -0
- package/dist/mcp/workspace-cache.d.ts +42 -0
- package/dist/mcp/workspace-cache.js +97 -0
- package/dist/mcp-server.d.ts +42 -0
- package/dist/mcp-server.js +280 -0
- package/package.json +56 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
# Implementation Guide: Skill-Based Agent System
|
|
2
|
+
|
|
3
|
+
This document provides step-by-step instructions for implementing a skill-based agent system in the Hailer MCP server.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Existing Hailer MCP server running
|
|
8
|
+
- Node.js and TypeScript environment
|
|
9
|
+
- Basic understanding of async/await patterns
|
|
10
|
+
- Access to `.claude/skills/` directory with existing skills
|
|
11
|
+
|
|
12
|
+
## Implementation Overview
|
|
13
|
+
|
|
14
|
+
**Total Time**: ~1 hour
|
|
15
|
+
**Difficulty**: Medium
|
|
16
|
+
**Files to Create**: 2 new files
|
|
17
|
+
**Files to Modify**: 3 existing files
|
|
18
|
+
|
|
19
|
+
## Step 1: Create Skill Loader
|
|
20
|
+
|
|
21
|
+
**File**: `server/src/client/skill-loader.ts`
|
|
22
|
+
**Time**: ~15 minutes
|
|
23
|
+
**Purpose**: Load skill files from disk and cache them
|
|
24
|
+
|
|
25
|
+
### Implementation
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import * as fs from 'fs/promises';
|
|
29
|
+
import * as path from 'path';
|
|
30
|
+
import { createLogger } from '../lib/logger';
|
|
31
|
+
|
|
32
|
+
export interface LoadedSkill {
|
|
33
|
+
name: string;
|
|
34
|
+
content: string;
|
|
35
|
+
description: string;
|
|
36
|
+
loadedAt: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class SkillLoader {
|
|
40
|
+
private skillsPath: string;
|
|
41
|
+
private cache = new Map<string, LoadedSkill>();
|
|
42
|
+
private logger = createLogger({ component: 'skill-loader' });
|
|
43
|
+
|
|
44
|
+
constructor(skillsPath: string = '.claude/skills/') {
|
|
45
|
+
this.skillsPath = skillsPath;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Load multiple skills at once
|
|
50
|
+
*/
|
|
51
|
+
async loadMultiple(skillNames: string[]): Promise<LoadedSkill[]> {
|
|
52
|
+
return Promise.all(skillNames.map(name => this.load(name)));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Load a single skill by name
|
|
57
|
+
*/
|
|
58
|
+
async load(skillName: string): Promise<LoadedSkill> {
|
|
59
|
+
// Check cache first
|
|
60
|
+
if (this.cache.has(skillName)) {
|
|
61
|
+
this.logger.debug('Skill loaded from cache', { skillName });
|
|
62
|
+
return this.cache.get(skillName)!;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const skillPath = path.join(this.skillsPath, skillName);
|
|
67
|
+
|
|
68
|
+
// Load main SKILL.md
|
|
69
|
+
const mainPath = path.join(skillPath, 'SKILL.md');
|
|
70
|
+
let content = await fs.readFile(mainPath, 'utf-8');
|
|
71
|
+
|
|
72
|
+
// Load reference files if they exist
|
|
73
|
+
const referencesPath = path.join(skillPath, 'references');
|
|
74
|
+
try {
|
|
75
|
+
const files = await fs.readdir(referencesPath);
|
|
76
|
+
for (const file of files.filter(f => f.endsWith('.md'))) {
|
|
77
|
+
const refContent = await fs.readFile(
|
|
78
|
+
path.join(referencesPath, file),
|
|
79
|
+
'utf-8'
|
|
80
|
+
);
|
|
81
|
+
content += `\n\n## ${file.replace('.md', '')}\n\n${refContent}`;
|
|
82
|
+
}
|
|
83
|
+
} catch (error) {
|
|
84
|
+
// References directory doesn't exist or is empty - that's ok
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const skill: LoadedSkill = {
|
|
88
|
+
name: skillName,
|
|
89
|
+
content,
|
|
90
|
+
description: this.extractDescription(content),
|
|
91
|
+
loadedAt: Date.now()
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
this.cache.set(skillName, skill);
|
|
95
|
+
this.logger.info('Skill loaded successfully', {
|
|
96
|
+
skillName,
|
|
97
|
+
contentLength: content.length
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return skill;
|
|
101
|
+
} catch (error) {
|
|
102
|
+
this.logger.error('Failed to load skill', error, { skillName });
|
|
103
|
+
throw new Error(`Failed to load skill '${skillName}': ${error}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Extract description from skill content (first paragraph)
|
|
109
|
+
*/
|
|
110
|
+
private extractDescription(content: string): string {
|
|
111
|
+
const lines = content.split('\n');
|
|
112
|
+
for (const line of lines) {
|
|
113
|
+
const trimmed = line.trim();
|
|
114
|
+
if (trimmed && !trimmed.startsWith('#')) {
|
|
115
|
+
return trimmed.slice(0, 200);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return '';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Clear cache for a specific skill or all skills
|
|
123
|
+
*/
|
|
124
|
+
clearCache(skillName?: string): void {
|
|
125
|
+
if (skillName) {
|
|
126
|
+
this.cache.delete(skillName);
|
|
127
|
+
this.logger.debug('Skill cache cleared', { skillName });
|
|
128
|
+
} else {
|
|
129
|
+
this.cache.clear();
|
|
130
|
+
this.logger.debug('All skill caches cleared');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get list of available skills
|
|
136
|
+
*/
|
|
137
|
+
async getAvailableSkills(): Promise<string[]> {
|
|
138
|
+
try {
|
|
139
|
+
const entries = await fs.readdir(this.skillsPath, { withFileTypes: true });
|
|
140
|
+
return entries
|
|
141
|
+
.filter(entry => entry.isDirectory())
|
|
142
|
+
.map(entry => entry.name);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
this.logger.error('Failed to list available skills', error);
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Testing
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
// Quick test
|
|
155
|
+
const loader = new SkillLoader();
|
|
156
|
+
const skill = await loader.load('mcp-tools');
|
|
157
|
+
console.log(`Loaded skill: ${skill.name}`);
|
|
158
|
+
console.log(`Content length: ${skill.content.length}`);
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Step 2: Create Skill Manager
|
|
162
|
+
|
|
163
|
+
**File**: `server/src/client/skill-manager.ts`
|
|
164
|
+
**Time**: ~20 minutes
|
|
165
|
+
**Purpose**: Analyze requests and select appropriate skills
|
|
166
|
+
|
|
167
|
+
### Implementation
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
import { SkillLoader, LoadedSkill } from './skill-loader';
|
|
171
|
+
import { createLogger } from '../lib/logger';
|
|
172
|
+
|
|
173
|
+
export interface SkillGuidance {
|
|
174
|
+
skills: string[];
|
|
175
|
+
confidence: number;
|
|
176
|
+
guidance: string;
|
|
177
|
+
recommendedTools: string[];
|
|
178
|
+
skillContent?: string;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
interface SkillMapping {
|
|
182
|
+
keywords: string[];
|
|
183
|
+
tools: string[];
|
|
184
|
+
priority?: number;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export class SkillManager {
|
|
188
|
+
private skillLoader: SkillLoader;
|
|
189
|
+
private skillMappings: Map<string, SkillMapping>;
|
|
190
|
+
private logger = createLogger({ component: 'skill-manager' });
|
|
191
|
+
|
|
192
|
+
constructor(skillLoader: SkillLoader) {
|
|
193
|
+
this.skillLoader = skillLoader;
|
|
194
|
+
this.skillMappings = this.buildSkillMappings();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Analyze a user request and determine which skills to load
|
|
199
|
+
*/
|
|
200
|
+
async analyzeRequest(message: string): Promise<SkillGuidance> {
|
|
201
|
+
const lower = message.toLowerCase();
|
|
202
|
+
const scores = new Map<string, number>();
|
|
203
|
+
|
|
204
|
+
// Score each skill based on keyword matches
|
|
205
|
+
for (const [skillName, mapping] of this.skillMappings) {
|
|
206
|
+
let score = 0;
|
|
207
|
+
for (const keyword of mapping.keywords) {
|
|
208
|
+
if (lower.includes(keyword.toLowerCase())) {
|
|
209
|
+
score += 1;
|
|
210
|
+
// Bonus for exact matches
|
|
211
|
+
if (lower.includes(` ${keyword.toLowerCase()} `)) {
|
|
212
|
+
score += 0.5;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Apply priority multiplier
|
|
218
|
+
if (mapping.priority) {
|
|
219
|
+
score *= mapping.priority;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (score > 0) {
|
|
223
|
+
scores.set(skillName, score);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Get best matching skills (up to 2)
|
|
228
|
+
const sortedSkills = Array.from(scores.entries())
|
|
229
|
+
.sort(([, a], [, b]) => b - a)
|
|
230
|
+
.slice(0, 2);
|
|
231
|
+
|
|
232
|
+
if (sortedSkills.length === 0) {
|
|
233
|
+
// No specific skill matched - use general
|
|
234
|
+
return await this.getDefaultGuidance();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const primarySkill = sortedSkills[0];
|
|
238
|
+
const [skillName, score] = primarySkill;
|
|
239
|
+
const mapping = this.skillMappings.get(skillName)!;
|
|
240
|
+
|
|
241
|
+
// Load the primary skill
|
|
242
|
+
const loadedSkills = await this.skillLoader.loadMultiple([skillName]);
|
|
243
|
+
|
|
244
|
+
this.logger.info('Skills selected', {
|
|
245
|
+
message: message.slice(0, 50),
|
|
246
|
+
selectedSkills: [skillName],
|
|
247
|
+
confidence: score
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
skills: [skillName],
|
|
252
|
+
confidence: Math.min(score / 3, 1.0), // Normalize
|
|
253
|
+
guidance: this.buildGuidance(skillName, message),
|
|
254
|
+
recommendedTools: mapping.tools,
|
|
255
|
+
skillContent: loadedSkills[0]?.content
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Define keyword mappings for each skill
|
|
261
|
+
*/
|
|
262
|
+
private buildSkillMappings(): Map<string, SkillMapping> {
|
|
263
|
+
return new Map([
|
|
264
|
+
[
|
|
265
|
+
'mcp-tools',
|
|
266
|
+
{
|
|
267
|
+
keywords: [
|
|
268
|
+
'workflow', 'phase', 'field', 'schema',
|
|
269
|
+
'create workflow', 'install workflow',
|
|
270
|
+
'workflow template', 'workflow structure',
|
|
271
|
+
'activity field', 'workflow phase'
|
|
272
|
+
],
|
|
273
|
+
tools: [
|
|
274
|
+
'install_workflow',
|
|
275
|
+
'list_workflow_phases',
|
|
276
|
+
'get_workflow_schema',
|
|
277
|
+
'update_workflow_field'
|
|
278
|
+
],
|
|
279
|
+
priority: 1.2
|
|
280
|
+
}
|
|
281
|
+
],
|
|
282
|
+
[
|
|
283
|
+
'hailer-api',
|
|
284
|
+
{
|
|
285
|
+
keywords: [
|
|
286
|
+
'api', 'endpoint', 'activity', 'list activities',
|
|
287
|
+
'filter', 'search', 'show activity',
|
|
288
|
+
'discussion', 'message', 'fetch'
|
|
289
|
+
],
|
|
290
|
+
tools: [
|
|
291
|
+
'list_activities',
|
|
292
|
+
'show_activity_by_id',
|
|
293
|
+
'fetch_discussion_messages',
|
|
294
|
+
'add_discussion_message'
|
|
295
|
+
],
|
|
296
|
+
priority: 1.0
|
|
297
|
+
}
|
|
298
|
+
],
|
|
299
|
+
[
|
|
300
|
+
'data-analysis',
|
|
301
|
+
{
|
|
302
|
+
keywords: [
|
|
303
|
+
'analyze', 'report', 'statistics', 'trends',
|
|
304
|
+
'insights', 'metrics', 'summary',
|
|
305
|
+
'count', 'aggregate'
|
|
306
|
+
],
|
|
307
|
+
tools: ['list_activities'],
|
|
308
|
+
priority: 0.8
|
|
309
|
+
}
|
|
310
|
+
],
|
|
311
|
+
[
|
|
312
|
+
'agent-building',
|
|
313
|
+
{
|
|
314
|
+
keywords: [
|
|
315
|
+
'build agent', 'create agent', 'agent system',
|
|
316
|
+
'skill system', 'autonomous agent',
|
|
317
|
+
'agent architecture', 'multi-agent'
|
|
318
|
+
],
|
|
319
|
+
tools: [],
|
|
320
|
+
priority: 1.0
|
|
321
|
+
}
|
|
322
|
+
]
|
|
323
|
+
]);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Build guidance text for a skill
|
|
328
|
+
*/
|
|
329
|
+
private buildGuidance(skillName: string, message: string): string {
|
|
330
|
+
const guidanceMap: Record<string, string> = {
|
|
331
|
+
'mcp-tools': 'This request involves workflow/schema operations. Use the workflow management tools available.',
|
|
332
|
+
'hailer-api': 'This request involves Hailer API operations. Use the appropriate API endpoints.',
|
|
333
|
+
'data-analysis': 'This request involves data analysis. Gather data using list_activities and analyze it.',
|
|
334
|
+
'agent-building': 'This request is about building agent systems. Reference the skill system architecture.'
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
return guidanceMap[skillName] || 'Use available tools to help the user.';
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Get default guidance when no specific skill matches
|
|
342
|
+
*/
|
|
343
|
+
private async getDefaultGuidance(): Promise<SkillGuidance> {
|
|
344
|
+
this.logger.debug('No specific skill matched, using default');
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
skills: [],
|
|
348
|
+
confidence: 0.3,
|
|
349
|
+
guidance: 'General assistance request. Use available tools as needed.',
|
|
350
|
+
recommendedTools: [],
|
|
351
|
+
skillContent: undefined
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Add or update a skill mapping
|
|
357
|
+
*/
|
|
358
|
+
addSkillMapping(skillName: string, mapping: SkillMapping): void {
|
|
359
|
+
this.skillMappings.set(skillName, mapping);
|
|
360
|
+
this.logger.info('Skill mapping added', { skillName });
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Testing
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
// Quick test
|
|
369
|
+
const loader = new SkillLoader();
|
|
370
|
+
const manager = new SkillManager(loader);
|
|
371
|
+
|
|
372
|
+
const guidance1 = await manager.analyzeRequest("Help me create a workflow");
|
|
373
|
+
console.log('Guidance:', guidance1);
|
|
374
|
+
// Should select 'mcp-tools' skill
|
|
375
|
+
|
|
376
|
+
const guidance2 = await manager.analyzeRequest("List my activities");
|
|
377
|
+
console.log('Guidance:', guidance2);
|
|
378
|
+
// Should select 'hailer-api' skill
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
## Step 3: Modify Message Processor
|
|
382
|
+
|
|
383
|
+
**File**: `server/src/client/message-processor.ts`
|
|
384
|
+
**Time**: ~10 minutes
|
|
385
|
+
**Purpose**: Integrate skill manager into message flow
|
|
386
|
+
|
|
387
|
+
### Changes Needed
|
|
388
|
+
|
|
389
|
+
1. Import new classes
|
|
390
|
+
2. Add skill manager instance
|
|
391
|
+
3. Call skill manager before processing
|
|
392
|
+
4. Pass skill context to provider
|
|
393
|
+
|
|
394
|
+
### Implementation
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
// At top of file, add imports:
|
|
398
|
+
import { SkillLoader } from './skill-loader';
|
|
399
|
+
import { SkillManager } from './skill-manager';
|
|
400
|
+
|
|
401
|
+
// In class HailerMessageProcessor:
|
|
402
|
+
export class HailerMessageProcessor implements MessageProcessor {
|
|
403
|
+
// ... existing properties ...
|
|
404
|
+
|
|
405
|
+
// ADD these new properties:
|
|
406
|
+
private skillLoader: SkillLoader;
|
|
407
|
+
private skillManager: SkillManager;
|
|
408
|
+
|
|
409
|
+
constructor(
|
|
410
|
+
multiBotManager: MultiBotManager,
|
|
411
|
+
mcpAgentIds: string[],
|
|
412
|
+
enableDirectMessages: boolean
|
|
413
|
+
) {
|
|
414
|
+
// ... existing constructor code ...
|
|
415
|
+
|
|
416
|
+
// ADD skill system initialization:
|
|
417
|
+
this.skillLoader = new SkillLoader();
|
|
418
|
+
this.skillManager = new SkillManager(this.skillLoader);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// MODIFY the processHailerMessage method:
|
|
422
|
+
async processHailerMessage(
|
|
423
|
+
signal: HailerSignal,
|
|
424
|
+
botClient: BotClient
|
|
425
|
+
): Promise<void> {
|
|
426
|
+
// ... existing message extraction code ...
|
|
427
|
+
|
|
428
|
+
for (const message of messages) {
|
|
429
|
+
// ADD skill analysis before provider call:
|
|
430
|
+
const skillGuidance = await this.skillManager.analyzeRequest(
|
|
431
|
+
message.content
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
// Pass to provider with skill context
|
|
435
|
+
await this.processMessageWithProvider(
|
|
436
|
+
message,
|
|
437
|
+
botClient,
|
|
438
|
+
skillGuidance // NEW parameter
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// MODIFY to accept skill guidance:
|
|
444
|
+
private async processMessageWithProvider(
|
|
445
|
+
message: ChatMessage,
|
|
446
|
+
botClient: BotClient,
|
|
447
|
+
skillGuidance: SkillGuidance // NEW parameter
|
|
448
|
+
): Promise<void> {
|
|
449
|
+
// ... existing provider selection code ...
|
|
450
|
+
|
|
451
|
+
// Pass skill guidance to provider
|
|
452
|
+
await provider.processMessage(message, skillGuidance);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
## Step 4: Modify LLM Providers
|
|
458
|
+
|
|
459
|
+
**Files**:
|
|
460
|
+
- `server/src/client/providers/openai-provider.ts`
|
|
461
|
+
- `server/src/client/providers/anthropic-provider.ts`
|
|
462
|
+
|
|
463
|
+
**Time**: ~15 minutes total
|
|
464
|
+
**Purpose**: Use skill content in system prompts
|
|
465
|
+
|
|
466
|
+
### Changes for OpenAI Provider
|
|
467
|
+
|
|
468
|
+
```typescript
|
|
469
|
+
import { SkillGuidance } from '../skill-manager';
|
|
470
|
+
|
|
471
|
+
export class OpenAIProvider extends LlmProvider {
|
|
472
|
+
// MODIFY processMessage to accept skill guidance:
|
|
473
|
+
async processMessage(
|
|
474
|
+
message: ChatMessage,
|
|
475
|
+
skillGuidance?: SkillGuidance // NEW optional parameter
|
|
476
|
+
): Promise<void> {
|
|
477
|
+
// Build enhanced system prompt
|
|
478
|
+
const systemPrompt = this.buildSystemPrompt(skillGuidance);
|
|
479
|
+
|
|
480
|
+
// ... rest of existing code uses systemPrompt ...
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// ADD method to build enhanced system prompt:
|
|
484
|
+
private buildSystemPrompt(skillGuidance?: SkillGuidance): string {
|
|
485
|
+
let prompt = `You are an AI assistant for Hailer workflow management.
|
|
486
|
+
|
|
487
|
+
You have access to MCP tools that can interact with the Hailer platform.
|
|
488
|
+
Use these tools to help users effectively.`;
|
|
489
|
+
|
|
490
|
+
// Add skill content if available
|
|
491
|
+
if (skillGuidance?.skillContent) {
|
|
492
|
+
prompt += `\n\n**DOMAIN EXPERTISE FOR THIS REQUEST:**
|
|
493
|
+
${skillGuidance.skillContent}
|
|
494
|
+
|
|
495
|
+
**GUIDANCE:**
|
|
496
|
+
${skillGuidance.guidance}`;
|
|
497
|
+
|
|
498
|
+
if (skillGuidance.recommendedTools.length > 0) {
|
|
499
|
+
prompt += `\n\n**RECOMMENDED TOOLS:**
|
|
500
|
+
${skillGuidance.recommendedTools.join(', ')}
|
|
501
|
+
|
|
502
|
+
Consider using these tools to best address the user's request.`;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
return prompt;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### Changes for Anthropic Provider
|
|
512
|
+
|
|
513
|
+
Same pattern - add `skillGuidance` parameter and build enhanced prompt.
|
|
514
|
+
|
|
515
|
+
## Step 5: Testing
|
|
516
|
+
|
|
517
|
+
**Time**: ~10 minutes
|
|
518
|
+
|
|
519
|
+
### Unit Tests
|
|
520
|
+
|
|
521
|
+
```typescript
|
|
522
|
+
// test/skill-loader.test.ts
|
|
523
|
+
describe('SkillLoader', () => {
|
|
524
|
+
it('should load skill from disk', async () => {
|
|
525
|
+
const loader = new SkillLoader();
|
|
526
|
+
const skill = await loader.load('mcp-tools');
|
|
527
|
+
expect(skill.name).toBe('mcp-tools');
|
|
528
|
+
expect(skill.content).toContain('workflow');
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
it('should cache skills', async () => {
|
|
532
|
+
const loader = new SkillLoader();
|
|
533
|
+
const skill1 = await loader.load('mcp-tools');
|
|
534
|
+
const skill2 = await loader.load('mcp-tools');
|
|
535
|
+
expect(skill1.loadedAt).toBe(skill2.loadedAt);
|
|
536
|
+
});
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
// test/skill-manager.test.ts
|
|
540
|
+
describe('SkillManager', () => {
|
|
541
|
+
it('should select workflow skill for workflow requests', async () => {
|
|
542
|
+
const loader = new SkillLoader();
|
|
543
|
+
const manager = new SkillManager(loader);
|
|
544
|
+
|
|
545
|
+
const guidance = await manager.analyzeRequest("create a workflow");
|
|
546
|
+
expect(guidance.skills).toContain('mcp-tools');
|
|
547
|
+
});
|
|
548
|
+
});
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
### Integration Test
|
|
552
|
+
|
|
553
|
+
```bash
|
|
554
|
+
# Start server
|
|
555
|
+
cd server && npm run dev
|
|
556
|
+
|
|
557
|
+
# In another terminal, test with Hailer
|
|
558
|
+
# Send message: "Help me create a workflow"
|
|
559
|
+
# Verify response includes workflow expertise
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
## Rollback Plan
|
|
563
|
+
|
|
564
|
+
If something goes wrong:
|
|
565
|
+
|
|
566
|
+
```bash
|
|
567
|
+
# Remove new files
|
|
568
|
+
rm server/src/client/skill-loader.ts
|
|
569
|
+
rm server/src/client/skill-manager.ts
|
|
570
|
+
|
|
571
|
+
# Revert modified files
|
|
572
|
+
git checkout server/src/client/message-processor.ts
|
|
573
|
+
git checkout server/src/client/providers/
|
|
574
|
+
|
|
575
|
+
# Restart server
|
|
576
|
+
npm run dev
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
## Configuration
|
|
580
|
+
|
|
581
|
+
No configuration changes needed! The system works with existing `.env.local`.
|
|
582
|
+
|
|
583
|
+
Optional: Add skill configuration to `.env.local`:
|
|
584
|
+
|
|
585
|
+
```bash
|
|
586
|
+
# Optional: Configure available skills
|
|
587
|
+
AVAILABLE_SKILLS=mcp-tools,hailer-api,data-analysis,agent-building
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
## Deployment Checklist
|
|
591
|
+
|
|
592
|
+
- [ ] SkillLoader implemented and tested
|
|
593
|
+
- [ ] SkillManager implemented and tested
|
|
594
|
+
- [ ] Message processor modified
|
|
595
|
+
- [ ] LLM providers modified
|
|
596
|
+
- [ ] Unit tests passing
|
|
597
|
+
- [ ] Integration tests passing
|
|
598
|
+
- [ ] Skills exist in `.claude/skills/` directory
|
|
599
|
+
- [ ] Server starts without errors
|
|
600
|
+
- [ ] Test messages produce skill-enhanced responses
|
|
601
|
+
- [ ] Performance overhead < 100ms
|
|
602
|
+
- [ ] No breaking changes to existing functionality
|
|
603
|
+
|
|
604
|
+
## Next Steps After Implementation
|
|
605
|
+
|
|
606
|
+
1. Monitor skill selection accuracy
|
|
607
|
+
2. Add more keyword mappings based on usage
|
|
608
|
+
3. Create additional skills as needed
|
|
609
|
+
4. Implement skill usage analytics
|
|
610
|
+
5. A/B test responses with/without skills
|
|
611
|
+
|
|
612
|
+
## Support
|
|
613
|
+
|
|
614
|
+
If you encounter issues:
|
|
615
|
+
1. Check logs for skill loading errors
|
|
616
|
+
2. Verify skill files exist and are readable
|
|
617
|
+
3. Test skill loader and manager independently
|
|
618
|
+
4. Review keyword mappings
|
|
619
|
+
5. Check for TypeScript compilation errors
|