@hailer/mcp 0.0.4 → 0.0.5
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/hooks/post-scaffold-hook.cjs +125 -0
- package/.claude/hooks/prompt-skill-loader.cjs +106 -5
- package/.claude/hooks/publish-template-guard.cjs +123 -0
- package/.claude/hooks/skill-loader.cjs +1 -1
- package/.claude/settings.json +22 -0
- package/.claude/skills/MCP-build-data-app-skill/SKILL.md +372 -0
- package/.claude/skills/MCP-publish-template-skill/SKILL.md +278 -0
- package/.claude/skills/MCP-scaffold-hailer-app-skill/SKILL.md +250 -47
- package/.claude/skills/building-hailer-apps-skill/SKILL.md +274 -9
- package/.claude/skills/hailer-app-builder/SKILL.md +340 -0
- package/.claude/skills/spawn-app-builder/SKILL.md +366 -0
- package/CHANGELOG.md +24 -0
- package/dist/app.js +8 -0
- package/dist/mcp/tools/app.d.ts +7 -0
- package/dist/mcp/tools/app.js +997 -1
- package/package.json +1 -1
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Spawn App Builder Agent
|
|
3
|
+
description: Spawns an isolated general-purpose agent pre-loaded with Hailer app building skills and TypeScript standards (project)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Spawn App Builder Agent
|
|
7
|
+
|
|
8
|
+
This skill spawns an **isolated general-purpose agent** pre-loaded with all the TypeScript standards and Hailer SDK patterns needed to build Hailer apps.
|
|
9
|
+
|
|
10
|
+
## When to Use
|
|
11
|
+
|
|
12
|
+
Use this skill when:
|
|
13
|
+
- App has been scaffolded and needs components built
|
|
14
|
+
- User wants to build a data manager, dashboard, or workflow-specific app
|
|
15
|
+
- Complex multi-component work that benefits from isolated context
|
|
16
|
+
|
|
17
|
+
## Prerequisites
|
|
18
|
+
|
|
19
|
+
Before spawning the agent, gather:
|
|
20
|
+
1. **Project path** - Where the scaffolded app is located
|
|
21
|
+
2. **Workflows info** - Run `list_workflows_minimal()` to get workflow names/IDs
|
|
22
|
+
3. **Task description** - What the user wants built (dashboard, CRUD, etc.)
|
|
23
|
+
|
|
24
|
+
## How to Spawn
|
|
25
|
+
|
|
26
|
+
Use the **Task tool** with `subagent_type: "general-purpose"` and the template below.
|
|
27
|
+
|
|
28
|
+
### Step 1: Gather Context
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
// Get workflows first
|
|
32
|
+
list_workflows_minimal()
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Step 2: Ask User What to Build (if not already specified)
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"questions": [
|
|
40
|
+
{
|
|
41
|
+
"question": "What should the app builder create?",
|
|
42
|
+
"header": "Task",
|
|
43
|
+
"options": [
|
|
44
|
+
{ "label": "Full data manager", "description": "CRUD for all workflows with tabs/navigation" },
|
|
45
|
+
{ "label": "Dashboard with charts", "description": "Overview stats and visualizations" },
|
|
46
|
+
{ "label": "Specific component", "description": "I'll describe what I need" }
|
|
47
|
+
],
|
|
48
|
+
"multiSelect": false
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Step 3: Spawn the Agent
|
|
55
|
+
|
|
56
|
+
Use the Task tool with this template (replace variables marked with ${...}):
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
Task({
|
|
60
|
+
subagent_type: "general-purpose",
|
|
61
|
+
description: "Build Hailer app components",
|
|
62
|
+
prompt: `
|
|
63
|
+
You are a **Hailer App Builder** - a specialized TypeScript developer building React apps that integrate with Hailer.
|
|
64
|
+
|
|
65
|
+
## YOUR TASK
|
|
66
|
+
|
|
67
|
+
Build components for the Hailer app at: **\${PROJECT_PATH}**
|
|
68
|
+
|
|
69
|
+
**Requirements:** \${TASK_DESCRIPTION}
|
|
70
|
+
|
|
71
|
+
## WORKSPACE DATA
|
|
72
|
+
|
|
73
|
+
Available workflows in this Hailer workspace:
|
|
74
|
+
|
|
75
|
+
\${WORKFLOWS_INFO}
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## MANDATORY: TYPESCRIPT STANDARDS
|
|
80
|
+
|
|
81
|
+
### Rule 1: Never Use any
|
|
82
|
+
|
|
83
|
+
// ❌ WRONG
|
|
84
|
+
const data: any = await hailer.activity.list(...);
|
|
85
|
+
function process(item: any) { ... }
|
|
86
|
+
|
|
87
|
+
// ✅ CORRECT
|
|
88
|
+
interface Activity {
|
|
89
|
+
_id: string;
|
|
90
|
+
name: string;
|
|
91
|
+
fields: Record<string, FieldValue>;
|
|
92
|
+
created: number;
|
|
93
|
+
updated: number;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const data: Activity[] = await hailer.activity.list(...);
|
|
97
|
+
function process(item: Activity) { ... }
|
|
98
|
+
|
|
99
|
+
### Rule 2: Type All Function Parameters and Returns
|
|
100
|
+
|
|
101
|
+
// ❌ WRONG
|
|
102
|
+
function formatDate(timestamp) {
|
|
103
|
+
return new Date(timestamp).toLocaleDateString();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ✅ CORRECT
|
|
107
|
+
function formatDate(timestamp: number): string {
|
|
108
|
+
return new Date(timestamp).toLocaleDateString();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
### Rule 3: Use Interface Definitions
|
|
112
|
+
|
|
113
|
+
// Core Hailer types - put in src/types/activity.ts
|
|
114
|
+
interface FieldValue {
|
|
115
|
+
type: string;
|
|
116
|
+
value: unknown;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface Activity {
|
|
120
|
+
_id: string;
|
|
121
|
+
name: string;
|
|
122
|
+
fields: Record<string, FieldValue>;
|
|
123
|
+
currentPhase: string;
|
|
124
|
+
process: string;
|
|
125
|
+
created: number;
|
|
126
|
+
updated?: number;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
interface Workflow {
|
|
130
|
+
_id: string;
|
|
131
|
+
name: string;
|
|
132
|
+
fields: Record<string, WorkflowField>;
|
|
133
|
+
phases: Record<string, Phase>;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
interface WorkflowField {
|
|
137
|
+
_id: string;
|
|
138
|
+
label: string;
|
|
139
|
+
type: 'text' | 'numeric' | 'date' | 'datetime' | 'textpredefinedoptions' | 'activitylink' | 'country' | 'numericunit' | 'textarea';
|
|
140
|
+
key?: string;
|
|
141
|
+
data?: string[];
|
|
142
|
+
required?: boolean;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
interface Phase {
|
|
146
|
+
_id: string;
|
|
147
|
+
name: string;
|
|
148
|
+
fields?: string[];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## MANDATORY: HAILER SDK PATTERNS
|
|
154
|
+
|
|
155
|
+
### Loading Workflow Schema
|
|
156
|
+
|
|
157
|
+
// ✅ CORRECT - Use hailer.workflow.get()
|
|
158
|
+
const workflow = await hailer.workflow.get(workflowId);
|
|
159
|
+
// workflow.fields = { fieldId: { label, type, key, ... } }
|
|
160
|
+
// workflow.phases = { phaseId: { name, fields: [...] } }
|
|
161
|
+
|
|
162
|
+
// ❌ WRONG - These methods don't exist!
|
|
163
|
+
// await hailer.process.getFields(workflowId, phaseId);
|
|
164
|
+
// await hailer.workflow.getFields(workflowId);
|
|
165
|
+
// await hailer.workflow.getSchema(workflowId);
|
|
166
|
+
|
|
167
|
+
### Loading Activities
|
|
168
|
+
|
|
169
|
+
// ✅ CORRECT - Use 3 positional parameters
|
|
170
|
+
const activities = await hailer.activity.list(
|
|
171
|
+
workflowId, // 1st: workflow ID string
|
|
172
|
+
phaseId, // 2nd: phase ID string
|
|
173
|
+
{ limit: 100 } // 3rd: options object
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
// ❌ WRONG - Don't use object with named params
|
|
177
|
+
// await hailer.activity.list({ process: workflowId, phase: phaseId });
|
|
178
|
+
|
|
179
|
+
### Field Access Pattern
|
|
180
|
+
|
|
181
|
+
**CRITICAL:** Activity fields are keyed by FIELD IDs, not readable keys!
|
|
182
|
+
|
|
183
|
+
// Activity structure from API (field IDs are 24-char hex strings)
|
|
184
|
+
{
|
|
185
|
+
_id: "abc123",
|
|
186
|
+
name: "Example Activity",
|
|
187
|
+
fields: {
|
|
188
|
+
"<field-id-1>": { type: "text", value: "Some text" },
|
|
189
|
+
"<field-id-2>": { type: "numeric", value: 10 }
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ✅ CORRECT - Create field ID constants from workflow schema
|
|
194
|
+
// Get IDs from hailer.workflow.get(workflowId).fields
|
|
195
|
+
const FIELD_IDS = {
|
|
196
|
+
// Map readable names to actual field IDs from your workspace
|
|
197
|
+
FIELD_NAME: '<actual-field-id-from-workflow>',
|
|
198
|
+
ANOTHER_FIELD: '<another-field-id>',
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// ✅ CORRECT - Helper function to extract field values
|
|
202
|
+
function getFieldValue<T>(
|
|
203
|
+
fields: Record<string, FieldValue> | undefined,
|
|
204
|
+
fieldId: string
|
|
205
|
+
): T | undefined {
|
|
206
|
+
return fields?.[fieldId]?.value as T;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const value1 = getFieldValue<string>(activity.fields, FIELD_IDS.FIELD_NAME);
|
|
210
|
+
const value2 = getFieldValue<number>(activity.fields, FIELD_IDS.ANOTHER_FIELD);
|
|
211
|
+
|
|
212
|
+
// ❌ WRONG - Don't use readable keys directly
|
|
213
|
+
// activity.fields.playerName.value // undefined!
|
|
214
|
+
// activity.fields['playerName'] // undefined!
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## CLEAN CODE PRACTICES
|
|
219
|
+
|
|
220
|
+
1. **Small, Focused Functions** - Each function does one thing
|
|
221
|
+
2. **Early Returns** - Avoid deep nesting
|
|
222
|
+
3. **Const by Default** - Only use let when reassignment is needed
|
|
223
|
+
4. **Handle All States** - Loading, error, empty, success
|
|
224
|
+
|
|
225
|
+
### Example Component Pattern
|
|
226
|
+
|
|
227
|
+
function ActivityList({ hailer, workflowId, phaseId }: Props) {
|
|
228
|
+
const [activities, setActivities] = useState<Activity[]>([]);
|
|
229
|
+
const [loading, setLoading] = useState(true);
|
|
230
|
+
const [error, setError] = useState<string | null>(null);
|
|
231
|
+
|
|
232
|
+
useEffect(() => {
|
|
233
|
+
loadData();
|
|
234
|
+
}, [workflowId, phaseId]);
|
|
235
|
+
|
|
236
|
+
async function loadData() {
|
|
237
|
+
try {
|
|
238
|
+
setLoading(true);
|
|
239
|
+
setError(null);
|
|
240
|
+
const result = await hailer.activity.list(workflowId, phaseId, { limit: 100 });
|
|
241
|
+
setActivities(result || []);
|
|
242
|
+
} catch (err) {
|
|
243
|
+
setError(err instanceof Error ? err.message : 'Failed to load');
|
|
244
|
+
} finally {
|
|
245
|
+
setLoading(false);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Loading state
|
|
250
|
+
if (loading) return <Spinner />;
|
|
251
|
+
|
|
252
|
+
// Error state
|
|
253
|
+
if (error) return <Alert status="error">{error}</Alert>;
|
|
254
|
+
|
|
255
|
+
// Empty state
|
|
256
|
+
if (activities.length === 0) return <Text>No activities found</Text>;
|
|
257
|
+
|
|
258
|
+
// Success state
|
|
259
|
+
return <ActivityTable data={activities} />;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## WHAT TO BUILD
|
|
265
|
+
|
|
266
|
+
Based on the task requirements, create these files:
|
|
267
|
+
|
|
268
|
+
1. **src/types/activity.ts** - All TypeScript interfaces
|
|
269
|
+
2. **src/config/workflows.ts** - Workflow IDs, field IDs, phase IDs
|
|
270
|
+
3. **src/utils/fieldHelpers.ts** - getFieldValue() and other helpers
|
|
271
|
+
4. **src/components/** - React components based on task
|
|
272
|
+
5. **Update src/App.tsx** - Wire everything together
|
|
273
|
+
|
|
274
|
+
## DO NOT
|
|
275
|
+
|
|
276
|
+
- ❌ Use any type anywhere
|
|
277
|
+
- ❌ Use hailer.process.getFields() (doesn't exist)
|
|
278
|
+
- ❌ Access fields by readable keys (use field IDs)
|
|
279
|
+
- ❌ Skip loading/error/empty states
|
|
280
|
+
- ❌ Forget to type function parameters
|
|
281
|
+
|
|
282
|
+
## DO
|
|
283
|
+
|
|
284
|
+
- ✅ Define interfaces for all data structures
|
|
285
|
+
- ✅ Use hailer.workflow.get(workflowId) for schema
|
|
286
|
+
- ✅ Use hailer.activity.list(workflowId, phaseId, options) for data
|
|
287
|
+
- ✅ Access fields via field IDs with helper function
|
|
288
|
+
- ✅ Handle all UI states (loading, error, empty, success)
|
|
289
|
+
- ✅ Use Chakra UI components (already installed)
|
|
290
|
+
- ✅ Use Chart.js + react-chartjs-2 for charts (already installed)
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## REPORT BACK
|
|
295
|
+
|
|
296
|
+
When done, report:
|
|
297
|
+
1. What files you created/modified
|
|
298
|
+
2. What components are available
|
|
299
|
+
3. How to use them
|
|
300
|
+
4. Any issues or next steps needed
|
|
301
|
+
`
|
|
302
|
+
})
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Template Variables
|
|
306
|
+
|
|
307
|
+
Replace these placeholders in the prompt:
|
|
308
|
+
|
|
309
|
+
| Variable | Description | Example |
|
|
310
|
+
|----------|-------------|---------|
|
|
311
|
+
| `${PROJECT_PATH}` | Absolute path to scaffolded app | `/home/user/dev-apps/fc-dashboard` |
|
|
312
|
+
| `${TASK_DESCRIPTION}` | What user wants built | `Full data manager with View + Edit + Create for all workflows` |
|
|
313
|
+
| `${WORKFLOWS_INFO}` | Formatted workflow list | `- Players (abc123): 31 activities\n- Teams (def456): 15 activities` |
|
|
314
|
+
|
|
315
|
+
## Complete Example
|
|
316
|
+
|
|
317
|
+
```javascript
|
|
318
|
+
// After scaffold completes and user confirms they want to spawn agent:
|
|
319
|
+
|
|
320
|
+
// 1. Gather workflow context from list_workflows_minimal()
|
|
321
|
+
// Format the output into a string for the agent prompt
|
|
322
|
+
const workflowsInfo = `
|
|
323
|
+
- Workflow Name 1 (<workflow-id-1>): X activities
|
|
324
|
+
- Workflow Name 2 (<workflow-id-2>): Y activities
|
|
325
|
+
- Workflow Name 3 (<workflow-id-3>): Z activities
|
|
326
|
+
`;
|
|
327
|
+
// Replace with actual data from list_workflows_minimal()
|
|
328
|
+
|
|
329
|
+
// 2. Spawn the agent with Task tool
|
|
330
|
+
Task({
|
|
331
|
+
subagent_type: "general-purpose",
|
|
332
|
+
description: "Build app-name components",
|
|
333
|
+
prompt: `
|
|
334
|
+
You are a **Hailer App Builder**...
|
|
335
|
+
|
|
336
|
+
## YOUR TASK
|
|
337
|
+
Build components for: /path/to/your/scaffolded/app
|
|
338
|
+
Requirements: Dashboard/Data manager/Custom (based on user request)
|
|
339
|
+
|
|
340
|
+
## WORKSPACE DATA
|
|
341
|
+
${workflowsInfo}
|
|
342
|
+
|
|
343
|
+
... (rest of embedded template from above) ...
|
|
344
|
+
`
|
|
345
|
+
})
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## Why Spawn an Agent?
|
|
349
|
+
|
|
350
|
+
| Benefit | Description |
|
|
351
|
+
|---------|-------------|
|
|
352
|
+
| **Isolated context** | Agent gets fresh context without conversation noise |
|
|
353
|
+
| **Pre-loaded knowledge** | All TypeScript + SDK patterns embedded in prompt |
|
|
354
|
+
| **Focused execution** | Single task without distractions |
|
|
355
|
+
| **Better code quality** | Standards enforced from the start |
|
|
356
|
+
|
|
357
|
+
## Alternative: Load Skills in Current Session
|
|
358
|
+
|
|
359
|
+
If user prefers not to spawn an agent, load both skills in the current session:
|
|
360
|
+
|
|
361
|
+
```javascript
|
|
362
|
+
Skill("building-hailer-apps-skill")
|
|
363
|
+
Skill("hailer-app-builder")
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Then continue building in the current conversation.
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,30 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.0.5] - 01-12-2025
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **New Skills**:
|
|
12
|
+
- `hailer-app-builder` - TypeScript standards and clean code patterns
|
|
13
|
+
- `spawn-app-builder` - Isolated agent spawning for app development
|
|
14
|
+
- `MCP-build-data-app-skill` - Data app building patterns
|
|
15
|
+
- `MCP-publish-template-skill` - Marketplace template publishing guide
|
|
16
|
+
|
|
17
|
+
- **New Hooks**:
|
|
18
|
+
- `post-scaffold-hook.cjs` - Auto-spawns builder agent after scaffolding
|
|
19
|
+
- `publish-template-guard.cjs` - Validates required fields before publishing
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- Updated `scaffold-hailer-app-skill` with mandatory agent spawn documentation
|
|
24
|
+
- Updated `building-hailer-apps-skill` with improved field access patterns
|
|
25
|
+
- Enhanced `prompt-skill-loader` with new skill keywords
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- App tools configuration imports
|
|
30
|
+
|
|
7
31
|
## [0.0.4] - 28-11-2025
|
|
8
32
|
|
|
9
33
|
### Added
|
package/dist/app.js
CHANGED
|
@@ -61,6 +61,14 @@ core.addTool(app_1.addAppMemberTool);
|
|
|
61
61
|
core.addTool(app_1.removeAppMemberTool);
|
|
62
62
|
core.addTool(app_1.scaffoldHailerAppTool);
|
|
63
63
|
core.addTool(app_1.publishHailerAppTool);
|
|
64
|
+
// Marketplace template tools
|
|
65
|
+
core.addTool(app_1.listTemplatesTool);
|
|
66
|
+
core.addTool(app_1.createTemplateTool);
|
|
67
|
+
core.addTool(app_1.installTemplateTool);
|
|
68
|
+
core.addTool(app_1.getTemplateTool);
|
|
69
|
+
core.addTool(app_1.publishTemplateTool);
|
|
70
|
+
core.addTool(app_1.getProductTool);
|
|
71
|
+
core.addTool(app_1.getProductManifestTool);
|
|
64
72
|
core.addTool(skill_1.listSkillsTool);
|
|
65
73
|
core.addTool(skill_1.getSkillTool);
|
|
66
74
|
console.log('All tools registered successfully!');
|
package/dist/mcp/tools/app.d.ts
CHANGED
|
@@ -17,4 +17,11 @@ export declare const addAppMemberTool: Tool;
|
|
|
17
17
|
export declare const removeAppMemberTool: Tool;
|
|
18
18
|
export declare const scaffoldHailerAppTool: Tool;
|
|
19
19
|
export declare const publishHailerAppTool: Tool;
|
|
20
|
+
export declare const listTemplatesTool: Tool;
|
|
21
|
+
export declare const createTemplateTool: Tool;
|
|
22
|
+
export declare const installTemplateTool: Tool;
|
|
23
|
+
export declare const getTemplateTool: Tool;
|
|
24
|
+
export declare const publishTemplateTool: Tool;
|
|
25
|
+
export declare const getProductTool: Tool;
|
|
26
|
+
export declare const getProductManifestTool: Tool;
|
|
20
27
|
//# sourceMappingURL=app.d.ts.map
|