@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.
@@ -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!');
@@ -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