@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.
Files changed (163) hide show
  1. package/.claude/commands/tool-builder.md +37 -0
  2. package/.claude/commands/ws-pull.md +44 -0
  3. package/.claude/settings.json +8 -0
  4. package/.claude/settings.local.json +49 -0
  5. package/.claude/skills/activity-api/SKILL.md +96 -0
  6. package/.claude/skills/activity-api/references/activity-endpoints.md +845 -0
  7. package/.claude/skills/add-app-member-skill/SKILL.md +977 -0
  8. package/.claude/skills/agent-building/SKILL.md +243 -0
  9. package/.claude/skills/agent-building/references/architecture-patterns.md +446 -0
  10. package/.claude/skills/agent-building/references/code-examples.md +587 -0
  11. package/.claude/skills/agent-building/references/implementation-guide.md +619 -0
  12. package/.claude/skills/app-api/SKILL.md +219 -0
  13. package/.claude/skills/app-api/references/app-endpoints.md +759 -0
  14. package/.claude/skills/building-hailer-apps-skill/SKILL.md +548 -0
  15. package/.claude/skills/create-app-skill/SKILL.md +1101 -0
  16. package/.claude/skills/create-insight-skill/SKILL.md +1317 -0
  17. package/.claude/skills/get-insight-data-skill/SKILL.md +1053 -0
  18. package/.claude/skills/hailer-api/SKILL.md +283 -0
  19. package/.claude/skills/hailer-api/references/activities.md +620 -0
  20. package/.claude/skills/hailer-api/references/authentication.md +216 -0
  21. package/.claude/skills/hailer-api/references/datasets.md +437 -0
  22. package/.claude/skills/hailer-api/references/files.md +301 -0
  23. package/.claude/skills/hailer-api/references/insights.md +469 -0
  24. package/.claude/skills/hailer-api/references/workflows.md +720 -0
  25. package/.claude/skills/hailer-api/references/workspaces-users.md +445 -0
  26. package/.claude/skills/insight-api/SKILL.md +185 -0
  27. package/.claude/skills/insight-api/references/insight-endpoints.md +514 -0
  28. package/.claude/skills/install-workflow-skill/SKILL.md +1056 -0
  29. package/.claude/skills/list-apps-skill/SKILL.md +1010 -0
  30. package/.claude/skills/list-workflows-minimal-skill/SKILL.md +992 -0
  31. package/.claude/skills/local-first-skill/SKILL.md +570 -0
  32. package/.claude/skills/mcp-tools/SKILL.md +419 -0
  33. package/.claude/skills/mcp-tools/references/api-endpoints.md +499 -0
  34. package/.claude/skills/mcp-tools/references/data-structures.md +554 -0
  35. package/.claude/skills/mcp-tools/references/implementation-patterns.md +717 -0
  36. package/.claude/skills/preview-insight-skill/SKILL.md +1290 -0
  37. package/.claude/skills/publish-hailer-app-skill/SKILL.md +453 -0
  38. package/.claude/skills/remove-app-member-skill/SKILL.md +671 -0
  39. package/.claude/skills/remove-app-skill/SKILL.md +985 -0
  40. package/.claude/skills/remove-insight-skill/SKILL.md +1011 -0
  41. package/.claude/skills/remove-workflow-skill/SKILL.md +920 -0
  42. package/.claude/skills/scaffold-hailer-app-skill/SKILL.md +1034 -0
  43. package/.claude/skills/skill-testing/README.md +137 -0
  44. package/.claude/skills/skill-testing/SKILL.md +348 -0
  45. package/.claude/skills/skill-testing/references/test-patterns.md +705 -0
  46. package/.claude/skills/skill-testing/references/testing-guide.md +603 -0
  47. package/.claude/skills/skill-testing/references/validation-checklist.md +537 -0
  48. package/.claude/skills/tool-builder/SKILL.md +328 -0
  49. package/.claude/skills/update-app-skill/SKILL.md +970 -0
  50. package/.claude/skills/update-workflow-field-skill/SKILL.md +1098 -0
  51. package/.env.example +81 -0
  52. package/.mcp.json +13 -0
  53. package/README.md +297 -0
  54. package/dist/app.d.ts +4 -0
  55. package/dist/app.js +74 -0
  56. package/dist/cli.d.ts +3 -0
  57. package/dist/cli.js +5 -0
  58. package/dist/client/adaptive-documentation-bot.d.ts +108 -0
  59. package/dist/client/adaptive-documentation-bot.js +475 -0
  60. package/dist/client/adaptive-documentation-types.d.ts +66 -0
  61. package/dist/client/adaptive-documentation-types.js +9 -0
  62. package/dist/client/agent-activity-bot.d.ts +51 -0
  63. package/dist/client/agent-activity-bot.js +166 -0
  64. package/dist/client/agent-tracker.d.ts +499 -0
  65. package/dist/client/agent-tracker.js +659 -0
  66. package/dist/client/description-updater.d.ts +56 -0
  67. package/dist/client/description-updater.js +259 -0
  68. package/dist/client/log-parser.d.ts +72 -0
  69. package/dist/client/log-parser.js +387 -0
  70. package/dist/client/mcp-client.d.ts +50 -0
  71. package/dist/client/mcp-client.js +532 -0
  72. package/dist/client/message-processor.d.ts +35 -0
  73. package/dist/client/message-processor.js +352 -0
  74. package/dist/client/multi-bot-manager.d.ts +24 -0
  75. package/dist/client/multi-bot-manager.js +74 -0
  76. package/dist/client/providers/anthropic-provider.d.ts +19 -0
  77. package/dist/client/providers/anthropic-provider.js +631 -0
  78. package/dist/client/providers/llm-provider.d.ts +47 -0
  79. package/dist/client/providers/llm-provider.js +367 -0
  80. package/dist/client/providers/openai-provider.d.ts +23 -0
  81. package/dist/client/providers/openai-provider.js +621 -0
  82. package/dist/client/simple-llm-caller.d.ts +19 -0
  83. package/dist/client/simple-llm-caller.js +100 -0
  84. package/dist/client/skill-generator.d.ts +81 -0
  85. package/dist/client/skill-generator.js +386 -0
  86. package/dist/client/test-adaptive-bot.d.ts +9 -0
  87. package/dist/client/test-adaptive-bot.js +82 -0
  88. package/dist/client/token-pricing.d.ts +38 -0
  89. package/dist/client/token-pricing.js +127 -0
  90. package/dist/client/token-tracker.d.ts +232 -0
  91. package/dist/client/token-tracker.js +457 -0
  92. package/dist/client/token-usage-bot.d.ts +53 -0
  93. package/dist/client/token-usage-bot.js +153 -0
  94. package/dist/client/tool-executor.d.ts +69 -0
  95. package/dist/client/tool-executor.js +159 -0
  96. package/dist/client/tool-schema-loader.d.ts +60 -0
  97. package/dist/client/tool-schema-loader.js +178 -0
  98. package/dist/client/types.d.ts +69 -0
  99. package/dist/client/types.js +7 -0
  100. package/dist/config.d.ts +162 -0
  101. package/dist/config.js +296 -0
  102. package/dist/core.d.ts +26 -0
  103. package/dist/core.js +147 -0
  104. package/dist/lib/context-manager.d.ts +111 -0
  105. package/dist/lib/context-manager.js +431 -0
  106. package/dist/lib/logger.d.ts +74 -0
  107. package/dist/lib/logger.js +277 -0
  108. package/dist/lib/materialize.d.ts +3 -0
  109. package/dist/lib/materialize.js +101 -0
  110. package/dist/lib/normalizedName.d.ts +7 -0
  111. package/dist/lib/normalizedName.js +48 -0
  112. package/dist/lib/prompt-length-manager.d.ts +81 -0
  113. package/dist/lib/prompt-length-manager.js +457 -0
  114. package/dist/lib/terminal-prompt.d.ts +9 -0
  115. package/dist/lib/terminal-prompt.js +108 -0
  116. package/dist/mcp/UserContextCache.d.ts +56 -0
  117. package/dist/mcp/UserContextCache.js +163 -0
  118. package/dist/mcp/auth.d.ts +2 -0
  119. package/dist/mcp/auth.js +29 -0
  120. package/dist/mcp/hailer-clients.d.ts +42 -0
  121. package/dist/mcp/hailer-clients.js +246 -0
  122. package/dist/mcp/signal-handler.d.ts +45 -0
  123. package/dist/mcp/signal-handler.js +317 -0
  124. package/dist/mcp/tool-registry.d.ts +100 -0
  125. package/dist/mcp/tool-registry.js +306 -0
  126. package/dist/mcp/tools/activity.d.ts +15 -0
  127. package/dist/mcp/tools/activity.js +955 -0
  128. package/dist/mcp/tools/app.d.ts +20 -0
  129. package/dist/mcp/tools/app.js +1488 -0
  130. package/dist/mcp/tools/discussion.d.ts +19 -0
  131. package/dist/mcp/tools/discussion.js +950 -0
  132. package/dist/mcp/tools/file.d.ts +15 -0
  133. package/dist/mcp/tools/file.js +119 -0
  134. package/dist/mcp/tools/insight.d.ts +17 -0
  135. package/dist/mcp/tools/insight.js +806 -0
  136. package/dist/mcp/tools/skill.d.ts +10 -0
  137. package/dist/mcp/tools/skill.js +279 -0
  138. package/dist/mcp/tools/user.d.ts +10 -0
  139. package/dist/mcp/tools/user.js +108 -0
  140. package/dist/mcp/tools/workflow-template.d.ts +19 -0
  141. package/dist/mcp/tools/workflow-template.js +822 -0
  142. package/dist/mcp/tools/workflow.d.ts +18 -0
  143. package/dist/mcp/tools/workflow.js +1362 -0
  144. package/dist/mcp/utils/api-errors.d.ts +45 -0
  145. package/dist/mcp/utils/api-errors.js +160 -0
  146. package/dist/mcp/utils/data-transformers.d.ts +102 -0
  147. package/dist/mcp/utils/data-transformers.js +194 -0
  148. package/dist/mcp/utils/file-upload.d.ts +33 -0
  149. package/dist/mcp/utils/file-upload.js +148 -0
  150. package/dist/mcp/utils/hailer-api-client.d.ts +120 -0
  151. package/dist/mcp/utils/hailer-api-client.js +323 -0
  152. package/dist/mcp/utils/index.d.ts +13 -0
  153. package/dist/mcp/utils/index.js +39 -0
  154. package/dist/mcp/utils/logger.d.ts +42 -0
  155. package/dist/mcp/utils/logger.js +103 -0
  156. package/dist/mcp/utils/types.d.ts +286 -0
  157. package/dist/mcp/utils/types.js +7 -0
  158. package/dist/mcp/workspace-cache.d.ts +42 -0
  159. package/dist/mcp/workspace-cache.js +97 -0
  160. package/dist/mcp-server.d.ts +42 -0
  161. package/dist/mcp-server.js +280 -0
  162. package/package.json +56 -0
  163. package/tsconfig.json +23 -0
@@ -0,0 +1,548 @@
1
+ ---
2
+ name: Building Hailer Apps
3
+ description: Complete guide for building Hailer apps with the @hailer/app-sdk - covers data loading, field access, API patterns, and common pitfalls
4
+ ---
5
+
6
+ # Building Hailer Apps Skill
7
+
8
+ Complete guide for building Hailer apps using the `@hailer/app-sdk` package. This skill covers critical patterns and gotchas that prevent data loading issues.
9
+
10
+ ## ⚠️ CRITICAL: Hailer App SDK vs MCP API
11
+
12
+ **IMPORTANT:** The Hailer App SDK (used in React/frontend apps) is DIFFERENT from the MCP API (used by Claude Code tools).
13
+
14
+ ### Hailer App SDK (Frontend Apps)
15
+ ```typescript
16
+ // Available on window.Hailer in browser apps
17
+ const hailer = await window.Hailer.init({ appId: '...' });
18
+
19
+ // NO hailer.api - access directly:
20
+ hailer.activity.list(workflowId, phaseId, { limit: 100 })
21
+ hailer.activity.get(activityId)
22
+ hailer.activity.create(workflowId, data)
23
+ ```
24
+
25
+ ### MCP API (Claude Code Tools)
26
+ ```typescript
27
+ // Used by Claude Code MCP tools
28
+ await list_activities({ workflowId, phaseId, fields: [...] })
29
+ await show_activity_by_id({ activityId })
30
+ await create_activity({ workflowId, fields: {...} })
31
+ ```
32
+
33
+ **Key Difference:** Frontend apps use `hailer.activity.list()` directly, NOT `hailer.api.activity.list()`!
34
+
35
+ ---
36
+
37
+ ## 1. Data Loading Pattern
38
+
39
+ ### ✅ CORRECT Pattern
40
+
41
+ ```typescript
42
+ import { useEffect, useState } from 'react';
43
+
44
+ function MyComponent({ hailer }) {
45
+ const [data, setData] = useState([]);
46
+
47
+ useEffect(() => {
48
+ loadData();
49
+ }, []);
50
+
51
+ async function loadData() {
52
+ // ✅ Call with 3 POSITIONAL PARAMETERS
53
+ const result = await hailer.activity.list(
54
+ '691ffdf84217e9e8434e5693', // 1st: workflowId
55
+ '691ffdf84217e9e8434e569f', // 2nd: phaseId
56
+ { limit: 100 } // 3rd: options object
57
+ );
58
+
59
+ setData(result || []);
60
+ }
61
+ }
62
+ ```
63
+
64
+ ### ❌ WRONG Patterns
65
+
66
+ ```typescript
67
+ // ❌ WRONG: Using object with named params
68
+ await hailer.activity.list({
69
+ process: workflowId,
70
+ phase: phaseId,
71
+ limit: 100
72
+ });
73
+
74
+ // ❌ WRONG: Using hailer.api (doesn't exist in frontend SDK)
75
+ await hailer.api.activity.list(workflowId, phaseId, options);
76
+
77
+ // ❌ WRONG: Missing options object (3rd param)
78
+ await hailer.activity.list(workflowId, phaseId);
79
+ ```
80
+
81
+ ### SDK Method Signature
82
+
83
+ ```typescript
84
+ hailer.activity.list(
85
+ workflowId: string,
86
+ phaseId: string,
87
+ options?: {
88
+ limit?: number,
89
+ skip?: number,
90
+ filters?: object
91
+ }
92
+ ): Promise<Activity[]>
93
+ ```
94
+
95
+ ---
96
+
97
+ ## 2. Field Access - The Critical Issue
98
+
99
+ ### Understanding Field Structure
100
+
101
+ **Activities returned from API have fields keyed by FIELD IDs, NOT readable keys!**
102
+
103
+ ```typescript
104
+ // ❌ WRONG: Fields are NOT keyed by readable names
105
+ {
106
+ _id: "691ffe874217e9e8434e5800",
107
+ name: "Jude Bellingham Jr",
108
+ fields: {
109
+ playerName: { value: "Jude" }, // ❌ WRONG - This doesn't exist!
110
+ jerseyNumber: { value: 26 } // ❌ WRONG - This doesn't exist!
111
+ }
112
+ }
113
+
114
+ // ✅ CORRECT: Fields are keyed by field IDs
115
+ {
116
+ _id: "691ffe874217e9e8434e5800",
117
+ name: "Jude Bellingham Jr",
118
+ fields: {
119
+ "691ffdf84217e9e8434e5694": { type: "text", value: "Jude Bellingham Jr" },
120
+ "691ffdf84217e9e8434e5695": { type: "numeric", value: 26 },
121
+ "691ffdf84217e9e8434e5696": { type: "textpredefinedoptions", value: "Midfielder" }
122
+ }
123
+ }
124
+ ```
125
+
126
+ ### Step 1: Get Field IDs Using MCP Tools
127
+
128
+ **ALWAYS use MCP tools to get field schema BEFORE building components:**
129
+
130
+ ```typescript
131
+ // In Claude Code, use MCP tool:
132
+ await get_workflow_schema({
133
+ workflowId: "691ffdf84217e9e8434e5693",
134
+ phaseId: "691ffdf84217e9e8434e569f"
135
+ });
136
+
137
+ // Returns field mappings:
138
+ // Player Name → 691ffdf84217e9e8434e5694
139
+ // Jersey Number → 691ffdf84217e9e8434e5695
140
+ // Position → 691ffdf84217e9e8434e5696
141
+ ```
142
+
143
+ ### Step 2: Create Field ID Constants
144
+
145
+ ```typescript
146
+ // src/fieldIds.ts
147
+ export const PLAYER_FIELDS = {
148
+ PLAYER_NAME: '691ffdf84217e9e8434e5694',
149
+ JERSEY_NUMBER: '691ffdf84217e9e8434e5695',
150
+ POSITION: '691ffdf84217e9e8434e5696',
151
+ DATE_OF_BIRTH: '691ffdf84217e9e8434e5697',
152
+ NATIONALITY: '691ffdf84217e9e8434e5698',
153
+ HEIGHT: '691ffdf84217e9e8434e5699',
154
+ WEIGHT: '691ffdf84217e9e8434e569a',
155
+ } as const;
156
+
157
+ // Helper function to safely get field value
158
+ export function getFieldValue<T = any>(
159
+ fields: Record<string, { type: string; value: T }>,
160
+ fieldId: string
161
+ ): T | undefined {
162
+ return fields[fieldId]?.value;
163
+ }
164
+ ```
165
+
166
+ ### Step 3: Use Field IDs in Components
167
+
168
+ ```typescript
169
+ import { PLAYER_FIELDS, getFieldValue } from './fieldIds';
170
+
171
+ function PlayerCard({ player }) {
172
+ return (
173
+ <div>
174
+ {/* ✅ CORRECT: Use getFieldValue with field ID */}
175
+ <h3>{getFieldValue(player.fields, PLAYER_FIELDS.PLAYER_NAME)}</h3>
176
+ <div>#{getFieldValue(player.fields, PLAYER_FIELDS.JERSEY_NUMBER)}</div>
177
+ <div>{getFieldValue(player.fields, PLAYER_FIELDS.POSITION)}</div>
178
+
179
+ {/* ❌ WRONG: This won't work! */}
180
+ <h3>{player.fields.playerName?.value}</h3>
181
+ <div>#{player.fields.jerseyNumber?.value}</div>
182
+ </div>
183
+ );
184
+ }
185
+ ```
186
+
187
+ ---
188
+
189
+ ## 3. Complete Working Example
190
+
191
+ ### Full Component with Proper Data Loading
192
+
193
+ ```typescript
194
+ import { useState, useEffect } from 'react';
195
+ import { PLAYER_FIELDS, getFieldValue } from '../fieldIds';
196
+
197
+ const WORKFLOW_IDS = {
198
+ PLAYERS: '691ffdf84217e9e8434e5693',
199
+ ACTIVE_PHASE: '691ffdf84217e9e8434e569f',
200
+ };
201
+
202
+ export default function PlayerList({ hailer }) {
203
+ const [players, setPlayers] = useState([]);
204
+ const [loading, setLoading] = useState(true);
205
+
206
+ useEffect(() => {
207
+ loadPlayers();
208
+ }, []);
209
+
210
+ async function loadPlayers() {
211
+ try {
212
+ setLoading(true);
213
+
214
+ // ✅ Call with 3 positional parameters
215
+ const result = await hailer.activity.list(
216
+ WORKFLOW_IDS.PLAYERS,
217
+ WORKFLOW_IDS.ACTIVE_PHASE,
218
+ { limit: 100 }
219
+ );
220
+
221
+ console.log(`Loaded ${result?.length || 0} players`);
222
+ setPlayers(result || []);
223
+ } catch (error) {
224
+ console.error('Failed to load players:', error);
225
+ } finally {
226
+ setLoading(false);
227
+ }
228
+ }
229
+
230
+ if (loading) {
231
+ return <div>Loading...</div>;
232
+ }
233
+
234
+ return (
235
+ <div className="player-list">
236
+ {players.map(player => (
237
+ <div key={player._id} className="player-card">
238
+ {/* ✅ Use getFieldValue with field IDs */}
239
+ <h3>{getFieldValue(player.fields, PLAYER_FIELDS.PLAYER_NAME) || player.name}</h3>
240
+ <div className="jersey">
241
+ #{getFieldValue(player.fields, PLAYER_FIELDS.JERSEY_NUMBER) || '?'}
242
+ </div>
243
+ <div className="position">
244
+ {getFieldValue(player.fields, PLAYER_FIELDS.POSITION)}
245
+ </div>
246
+ <div className="stats">
247
+ <span>Height: {getFieldValue(player.fields, PLAYER_FIELDS.HEIGHT)} cm</span>
248
+ <span>Weight: {getFieldValue(player.fields, PLAYER_FIELDS.WEIGHT)} kg</span>
249
+ </div>
250
+ </div>
251
+ ))}
252
+ </div>
253
+ );
254
+ }
255
+ ```
256
+
257
+ ---
258
+
259
+ ## 4. Common Field Types
260
+
261
+ ### Field Type Reference
262
+
263
+ ```typescript
264
+ // text
265
+ { type: "text", value: "John Doe" }
266
+
267
+ // numeric
268
+ { type: "numeric", value: 42 }
269
+
270
+ // numericunit
271
+ { type: "numericunit", value: 180 } // e.g., 180 cm
272
+
273
+ // date
274
+ { type: "date", value: 1036454400000 } // timestamp
275
+
276
+ // textpredefinedoptions (dropdown)
277
+ { type: "textpredefinedoptions", value: "Active" }
278
+
279
+ // country
280
+ { type: "country", value: { code: "US", name: "United States" } }
281
+
282
+ // activitylink (reference to another activity)
283
+ { type: "activitylink", value: { _id: "...", name: "..." } }
284
+ ```
285
+
286
+ ### Type-Safe Field Access
287
+
288
+ ```typescript
289
+ // For simple types
290
+ const name = getFieldValue<string>(fields, PLAYER_FIELDS.PLAYER_NAME);
291
+ const age = getFieldValue<number>(fields, PLAYER_FIELDS.AGE);
292
+
293
+ // For complex types (country)
294
+ const country = getFieldValue<{ code: string; name: string }>(
295
+ fields,
296
+ PLAYER_FIELDS.NATIONALITY
297
+ );
298
+ console.log(country?.name);
299
+
300
+ // For activity links
301
+ const team = getFieldValue<{ _id: string; name: string }>(
302
+ fields,
303
+ PLAYER_FIELDS.CURRENT_TEAM
304
+ );
305
+ console.log(team?.name);
306
+ ```
307
+
308
+ ---
309
+
310
+ ## 5. Development Workflow
311
+
312
+ ### Step-by-Step: Building a Hailer App Component
313
+
314
+ 1. **Use MCP tool to get schema**
315
+ ```typescript
316
+ await get_workflow_schema({
317
+ workflowId: "...",
318
+ phaseId: "..."
319
+ });
320
+ ```
321
+
322
+ 2. **Create field ID constants**
323
+ ```typescript
324
+ // src/fieldIds.ts
325
+ export const WORKFLOW_FIELDS = {
326
+ FIELD_NAME: 'field-id-from-schema',
327
+ };
328
+ ```
329
+
330
+ 3. **Create component with proper data loading**
331
+ ```typescript
332
+ // Use 3-param signature for hailer.activity.list()
333
+ const data = await hailer.activity.list(workflowId, phaseId, options);
334
+ ```
335
+
336
+ 4. **Access fields using field IDs**
337
+ ```typescript
338
+ // Use getFieldValue(fields, FIELD_ID)
339
+ const value = getFieldValue(item.fields, WORKFLOW_FIELDS.FIELD_NAME);
340
+ ```
341
+
342
+ 5. **Test and verify data loads**
343
+ ```typescript
344
+ console.log('Loaded data:', data);
345
+ console.log('First item fields:', data[0]?.fields);
346
+ ```
347
+
348
+ ---
349
+
350
+ ## 6. Debugging Data Loading Issues
351
+
352
+ ### Quick Diagnostics
353
+
354
+ ```typescript
355
+ async function loadPlayers() {
356
+ try {
357
+ console.log('=== DEBUG DATA LOADING ===');
358
+
359
+ // 1. Check hailer object
360
+ console.log('hailer:', hailer);
361
+ console.log('hailer.activity:', hailer.activity);
362
+ console.log('hailer.activity.list:', typeof hailer.activity.list);
363
+
364
+ // 2. Call API
365
+ const result = await hailer.activity.list(workflowId, phaseId, { limit: 10 });
366
+
367
+ // 3. Check result structure
368
+ console.log('Result:', result);
369
+ console.log('Result length:', result?.length);
370
+ console.log('First item:', result?.[0]);
371
+ console.log('First item fields:', result?.[0]?.fields);
372
+ console.log('Field keys:', Object.keys(result?.[0]?.fields || {}));
373
+
374
+ return result;
375
+ } catch (error) {
376
+ console.error('Load failed:', error);
377
+ }
378
+ }
379
+ ```
380
+
381
+ ### Common Issues and Solutions
382
+
383
+ | Issue | Symptom | Solution |
384
+ |-------|---------|----------|
385
+ | `Cannot read properties of undefined (reading 'activity')` | `hailer.api` doesn't exist | Use `hailer.activity`, NOT `hailer.api.activity` |
386
+ | `"processId" must be a string` | Passing object instead of positional params | Use 3 positional params: `list(workflowId, phaseId, options)` |
387
+ | Fields showing as `undefined` | Using readable keys like `fields.playerName` | Use field IDs: `fields['691ffdf...']` or `getFieldValue()` |
388
+ | Empty data displayed | Data loads but doesn't render | Check field access - use correct field IDs |
389
+
390
+ ---
391
+
392
+ ## 7. Loading Data from Multiple Phases
393
+
394
+ ```typescript
395
+ async function loadAllPlayers() {
396
+ const allPlayers = [];
397
+
398
+ // Load from multiple phases
399
+ const phases = [
400
+ { id: '691ffdf84217e9e8434e569f', name: 'Active' },
401
+ { id: '691ffdf84217e9e8434e56a0', name: 'On Loan' },
402
+ { id: '691ffdf84217e9e8434e56a1', name: 'Injured' },
403
+ ];
404
+
405
+ for (const phase of phases) {
406
+ try {
407
+ const result = await hailer.activity.list(
408
+ WORKFLOW_IDS.PLAYERS,
409
+ phase.id,
410
+ { limit: 100 }
411
+ );
412
+
413
+ console.log(`Loaded ${result?.length || 0} players from ${phase.name}`);
414
+
415
+ if (result && Array.isArray(result)) {
416
+ allPlayers.push(...result);
417
+ }
418
+ } catch (error) {
419
+ console.error(`Failed to load ${phase.name}:`, error);
420
+ }
421
+ }
422
+
423
+ return allPlayers;
424
+ }
425
+ ```
426
+
427
+ ---
428
+
429
+ ## 8. Best Practices
430
+
431
+ ### ✅ DO
432
+
433
+ 1. **Always get schema first** - Use MCP `get_workflow_schema` tool
434
+ 2. **Create field ID constants** - Store in `src/fieldIds.ts`
435
+ 3. **Use helper functions** - `getFieldValue()` for safe access
436
+ 4. **Log during development** - Verify data structure
437
+ 5. **Handle errors** - Wrap API calls in try-catch
438
+ 6. **Use TypeScript** - Type your field values
439
+
440
+ ### ❌ DON'T
441
+
442
+ 1. **Don't use readable keys** - `fields.playerName` won't work
443
+ 2. **Don't use hailer.api** - It doesn't exist in frontend SDK
444
+ 3. **Don't pass objects to list()** - Use positional params
445
+ 4. **Don't assume field structure** - Always check schema first
446
+ 5. **Don't forget error handling** - API calls can fail
447
+ 6. **Don't hardcode field IDs inline** - Use constants
448
+
449
+ ---
450
+
451
+ ## 9. TypeScript Definitions
452
+
453
+ ```typescript
454
+ // src/types.ts
455
+ export interface Activity {
456
+ _id: string;
457
+ name: string;
458
+ fields: Record<string, FieldValue>;
459
+ currentPhase: string;
460
+ process: string;
461
+ created: number;
462
+ updated: number;
463
+ active: boolean;
464
+ discussion?: string;
465
+ files?: any[];
466
+ followers?: string[];
467
+ }
468
+
469
+ export interface FieldValue<T = any> {
470
+ type: string;
471
+ value: T;
472
+ }
473
+
474
+ // Hailer client type
475
+ export interface HailerClient {
476
+ activity: {
477
+ list(workflowId: string, phaseId: string, options?: {
478
+ limit?: number;
479
+ skip?: number;
480
+ filters?: any;
481
+ }): Promise<Activity[]>;
482
+
483
+ get(activityId: string): Promise<Activity>;
484
+
485
+ create(workflowId: string, data: {
486
+ name: string;
487
+ fields?: Record<string, any>;
488
+ phaseId?: string;
489
+ }): Promise<Activity>;
490
+
491
+ update(activityId: string, data: Partial<Activity>): Promise<Activity>;
492
+
493
+ remove(activityId: string): Promise<void>;
494
+ };
495
+
496
+ // Other modules...
497
+ workflow: any;
498
+ insight: any;
499
+ user: any;
500
+ }
501
+ ```
502
+
503
+ ---
504
+
505
+ ## 10. Testing Checklist
506
+
507
+ Before deploying your Hailer app:
508
+
509
+ - [ ] Data loads successfully (`hailer.activity.list()` works)
510
+ - [ ] Field IDs are correct (checked with `get_workflow_schema`)
511
+ - [ ] Field values display properly (using `getFieldValue()`)
512
+ - [ ] All required fields are accessible
513
+ - [ ] Error handling is in place
514
+ - [ ] Loading states are handled
515
+ - [ ] Console has no errors
516
+ - [ ] Data refreshes correctly
517
+ - [ ] Multiple phases work (if applicable)
518
+ - [ ] TypeScript types are correct
519
+
520
+ ---
521
+
522
+ ## Summary
523
+
524
+ **Key Points to Remember:**
525
+
526
+ 1. **SDK vs MCP API** - Frontend uses `hailer.activity`, NOT `hailer.api.activity`
527
+ 2. **3 Positional Params** - `list(workflowId, phaseId, options)`
528
+ 3. **Field IDs, Not Keys** - Fields are keyed by IDs, not readable names
529
+ 4. **Get Schema First** - Always use MCP `get_workflow_schema` tool
530
+ 5. **Use Constants** - Store field IDs in `src/fieldIds.ts`
531
+ 6. **Helper Functions** - Use `getFieldValue()` for safe access
532
+
533
+ **Critical Pattern:**
534
+ ```typescript
535
+ // 1. Get schema (in Claude Code)
536
+ await get_workflow_schema({ workflowId, phaseId });
537
+
538
+ // 2. Create constants
539
+ export const FIELDS = { NAME: 'field-id-from-schema' };
540
+
541
+ // 3. Load data
542
+ const data = await hailer.activity.list(workflowId, phaseId, { limit: 100 });
543
+
544
+ // 4. Access fields
545
+ const name = getFieldValue(data[0].fields, FIELDS.NAME);
546
+ ```
547
+
548
+ Follow these patterns and your Hailer app will load data correctly from the start!