@hailer/mcp 0.1.6 → 0.1.9

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 (137) hide show
  1. package/.claude/agents/agent-dmitri-activity-crud.md +3 -1
  2. package/.claude/agents/agent-giuseppe-app-builder.md +11 -12
  3. package/.claude/agents/agent-kenji-data-reader.md +5 -3
  4. package/.claude/hooks/sync-marketplace-agents.cjs +117 -56
  5. package/.claude/skills/hailer-app-builder/SKILL.md +506 -0
  6. package/.claude/skills/publish-hailer-app/SKILL.md +169 -0
  7. package/.claude/skills/tool-parameter-usage/SKILL.md +112 -0
  8. package/CHANGELOG.md +20 -0
  9. package/CLAUDE.md +37 -16
  10. package/REFACTOR_STATUS.md +127 -0
  11. package/dist/cli.js +0 -0
  12. package/dist/client/agents/base.d.ts +202 -0
  13. package/dist/client/agents/base.js +737 -0
  14. package/dist/client/agents/definitions.d.ts +53 -0
  15. package/dist/client/agents/definitions.js +178 -0
  16. package/dist/client/agents/orchestrator.d.ts +119 -0
  17. package/dist/client/agents/orchestrator.js +760 -0
  18. package/dist/client/agents/specialist.d.ts +86 -0
  19. package/dist/client/agents/specialist.js +340 -0
  20. package/dist/client/bot-manager.d.ts +44 -0
  21. package/dist/client/bot-manager.js +173 -0
  22. package/dist/client/chat-agent-daemon.d.ts +464 -0
  23. package/dist/client/chat-agent-daemon.js +1774 -0
  24. package/dist/client/daemon-factory.d.ts +106 -0
  25. package/dist/client/daemon-factory.js +301 -0
  26. package/dist/client/factory.d.ts +107 -0
  27. package/dist/client/factory.js +304 -0
  28. package/dist/client/index.d.ts +17 -0
  29. package/dist/client/index.js +38 -0
  30. package/dist/client/multi-bot-manager.d.ts +18 -0
  31. package/dist/client/multi-bot-manager.js +88 -1
  32. package/dist/client/orchestrator-daemon.d.ts +87 -0
  33. package/dist/client/orchestrator-daemon.js +444 -0
  34. package/dist/client/services/agent-registry.d.ts +108 -0
  35. package/dist/client/services/agent-registry.js +630 -0
  36. package/dist/client/services/conversation-manager.d.ts +50 -0
  37. package/dist/client/services/conversation-manager.js +136 -0
  38. package/dist/client/services/mcp-client.d.ts +48 -0
  39. package/dist/client/services/mcp-client.js +105 -0
  40. package/dist/client/services/message-classifier.d.ts +37 -0
  41. package/dist/client/services/message-classifier.js +187 -0
  42. package/dist/client/services/message-formatter.d.ts +84 -0
  43. package/dist/client/services/message-formatter.js +353 -0
  44. package/dist/client/services/session-logger.d.ts +106 -0
  45. package/dist/client/services/session-logger.js +446 -0
  46. package/dist/client/services/tool-executor.d.ts +41 -0
  47. package/dist/client/services/tool-executor.js +169 -0
  48. package/dist/client/services/workspace-schema-cache.d.ts +149 -0
  49. package/dist/client/services/workspace-schema-cache.js +732 -0
  50. package/dist/client/specialist-daemon.d.ts +77 -0
  51. package/dist/client/specialist-daemon.js +197 -0
  52. package/dist/client/specialists.d.ts +53 -0
  53. package/dist/client/specialists.js +178 -0
  54. package/dist/client/tool-schema-loader.d.ts +4 -3
  55. package/dist/client/tool-schema-loader.js +54 -8
  56. package/dist/client/types.d.ts +283 -55
  57. package/dist/client/types.js +113 -2
  58. package/dist/config.d.ts +1 -1
  59. package/dist/config.js +1 -1
  60. package/dist/core.d.ts +10 -2
  61. package/dist/core.js +43 -27
  62. package/dist/lib/logger.js +15 -3
  63. package/dist/mcp/UserContextCache.js +2 -2
  64. package/dist/mcp/hailer-clients.js +5 -5
  65. package/dist/mcp/signal-handler.js +27 -5
  66. package/dist/mcp/tools/activity.js +137 -65
  67. package/dist/mcp/tools/app-core.js +4 -140
  68. package/dist/mcp/tools/app-marketplace.js +15 -260
  69. package/dist/mcp/tools/app-member.js +2 -73
  70. package/dist/mcp/tools/app-scaffold.js +146 -87
  71. package/dist/mcp/tools/discussion.js +348 -73
  72. package/dist/mcp/tools/insight.js +74 -190
  73. package/dist/mcp/tools/workflow.js +20 -94
  74. package/dist/mcp/utils/hailer-api-client.d.ts +4 -2
  75. package/dist/mcp/utils/hailer-api-client.js +24 -10
  76. package/dist/mcp-server.d.ts +4 -0
  77. package/dist/mcp-server.js +24 -4
  78. package/dist/routes/agents.d.ts +44 -0
  79. package/dist/routes/agents.js +311 -0
  80. package/dist/services/agent-credential-store.d.ts +73 -0
  81. package/dist/services/agent-credential-store.js +212 -0
  82. package/lineup-manager/dist/assets/index-8ce6041d.css +1 -0
  83. package/lineup-manager/dist/assets/index-e168f265.js +600 -0
  84. package/lineup-manager/dist/index.html +15 -0
  85. package/lineup-manager/dist/manifest.json +17 -0
  86. package/lineup-manager/dist/vite.svg +1 -0
  87. package/package.json +1 -1
  88. package/dist/client/adaptive-documentation-bot.d.ts +0 -106
  89. package/dist/client/adaptive-documentation-bot.js +0 -464
  90. package/dist/client/adaptive-documentation-types.d.ts +0 -66
  91. package/dist/client/adaptive-documentation-types.js +0 -9
  92. package/dist/client/agent-activity-bot.d.ts +0 -51
  93. package/dist/client/agent-activity-bot.js +0 -166
  94. package/dist/client/agent-tracker.d.ts +0 -499
  95. package/dist/client/agent-tracker.js +0 -659
  96. package/dist/client/description-updater.d.ts +0 -56
  97. package/dist/client/description-updater.js +0 -259
  98. package/dist/client/log-parser.d.ts +0 -72
  99. package/dist/client/log-parser.js +0 -387
  100. package/dist/client/mcp-assistant.d.ts +0 -21
  101. package/dist/client/mcp-assistant.js +0 -58
  102. package/dist/client/mcp-client.d.ts +0 -50
  103. package/dist/client/mcp-client.js +0 -538
  104. package/dist/client/message-processor.d.ts +0 -35
  105. package/dist/client/message-processor.js +0 -357
  106. package/dist/client/providers/anthropic-provider.d.ts +0 -19
  107. package/dist/client/providers/anthropic-provider.js +0 -645
  108. package/dist/client/providers/assistant-provider.d.ts +0 -17
  109. package/dist/client/providers/assistant-provider.js +0 -51
  110. package/dist/client/providers/llm-provider.d.ts +0 -47
  111. package/dist/client/providers/llm-provider.js +0 -367
  112. package/dist/client/providers/openai-provider.d.ts +0 -23
  113. package/dist/client/providers/openai-provider.js +0 -630
  114. package/dist/client/simple-llm-caller.d.ts +0 -19
  115. package/dist/client/simple-llm-caller.js +0 -100
  116. package/dist/client/skill-generator.d.ts +0 -81
  117. package/dist/client/skill-generator.js +0 -386
  118. package/dist/client/test-adaptive-bot.d.ts +0 -9
  119. package/dist/client/test-adaptive-bot.js +0 -82
  120. package/dist/client/token-pricing.d.ts +0 -38
  121. package/dist/client/token-pricing.js +0 -127
  122. package/dist/client/token-tracker.d.ts +0 -232
  123. package/dist/client/token-tracker.js +0 -457
  124. package/dist/client/token-usage-bot.d.ts +0 -53
  125. package/dist/client/token-usage-bot.js +0 -153
  126. package/dist/client/tool-executor.d.ts +0 -69
  127. package/dist/client/tool-executor.js +0 -159
  128. package/dist/lib/materialize.d.ts +0 -3
  129. package/dist/lib/materialize.js +0 -101
  130. package/dist/lib/normalizedName.d.ts +0 -7
  131. package/dist/lib/normalizedName.js +0 -48
  132. package/dist/lib/terminal-prompt.d.ts +0 -9
  133. package/dist/lib/terminal-prompt.js +0 -108
  134. package/dist/mcp/tools/skill.d.ts +0 -10
  135. package/dist/mcp/tools/skill.js +0 -279
  136. package/dist/mcp/tools/workflow-template.d.ts +0 -19
  137. package/dist/mcp/tools/workflow-template.js +0 -822
@@ -0,0 +1,506 @@
1
+ ---
2
+ name: hailer-app-builder
3
+ description: Patterns for building Hailer apps with @hailer/app-sdk
4
+ ---
5
+
6
+ # Hailer App Builder Skill
7
+
8
+ Patterns and templates for building Hailer apps with @hailer/app-sdk.
9
+
10
+ <sdk-setup>
11
+ ## Hook Import (CRITICAL)
12
+
13
+ ```typescript
14
+ // CORRECT - local default import
15
+ import useHailer from './hailer/use-hailer';
16
+
17
+ // WRONG - will fail build
18
+ import { useHailer } from '@hailer/app-sdk';
19
+ ```
20
+
21
+ ## Hook Usage
22
+
23
+ ```typescript
24
+ function App() {
25
+ const { inside, hailer } = useHailer();
26
+
27
+ // CORRECT dependency array
28
+ useEffect(() => {
29
+ // fetch data
30
+ }, [inside]); // Use [inside] NOT [hailer]
31
+
32
+ // Early return AFTER hooks
33
+ if (!inside) return <Text>Open this app inside Hailer</Text>;
34
+
35
+ return <Box>...</Box>;
36
+ }
37
+ ```
38
+ </sdk-setup>
39
+
40
+ <sdk-api>
41
+ ## Activity API
42
+
43
+ ```typescript
44
+ // List activities from workflow phase
45
+ const activities = await hailer.activity.list(workflowId, phaseId, {
46
+ limit: 100,
47
+ fields: ['fieldId1', 'fieldId2'], // Optional: specific fields only
48
+ });
49
+
50
+ // Get single activity
51
+ const activity = await hailer.activity.get(activityId);
52
+
53
+ // Activity structure
54
+ interface Activity {
55
+ _id: string;
56
+ name: string;
57
+ fields?: Record<string, { value: unknown }>;
58
+ created?: number;
59
+ updated?: number;
60
+ }
61
+ ```
62
+
63
+ ## Insight API
64
+
65
+ ```typescript
66
+ // Get insight data (SQL query results)
67
+ const data = await hailer.insight.get(insightId, { update: true });
68
+
69
+ // Response structure
70
+ interface InsightResponse {
71
+ columns: string[];
72
+ rows: any[][];
73
+ }
74
+ ```
75
+
76
+ ## Workflow API
77
+
78
+ ```typescript
79
+ // List all workflows
80
+ const workflows = await hailer.workflow.list();
81
+
82
+ // Get single workflow
83
+ const workflow = await hailer.workflow.get(workflowId);
84
+ ```
85
+ </sdk-api>
86
+
87
+ <field-patterns>
88
+ ## Extracting Field Values
89
+
90
+ ```typescript
91
+ // Fields are optional and nested
92
+ interface Activity {
93
+ fields?: Record<string, { value: unknown }>;
94
+ }
95
+
96
+ // Safe extraction helper
97
+ function getFieldValue<T>(activity: Activity, fieldId: string, defaultValue: T): T {
98
+ return (activity.fields?.[fieldId]?.value as T) ?? defaultValue;
99
+ }
100
+
101
+ // Usage
102
+ const name = getFieldValue(activity, 'fieldId123', '');
103
+ const count = getFieldValue(activity, 'fieldId456', 0);
104
+ const date = getFieldValue(activity, 'fieldId789', null);
105
+ ```
106
+
107
+ ## Field Types
108
+
109
+ ```typescript
110
+ // Text field
111
+ const text = activity.fields?.['fieldId']?.value as string;
112
+
113
+ // Number field
114
+ const num = activity.fields?.['fieldId']?.value as number;
115
+
116
+ // Date field (timestamp)
117
+ const date = activity.fields?.['fieldId']?.value as number;
118
+ const formatted = new Date(date).toLocaleDateString();
119
+
120
+ // Enum/Select field
121
+ const status = activity.fields?.['fieldId']?.value as string;
122
+
123
+ // ActivityLink field (reference to another activity)
124
+ interface ActivityLinkValue {
125
+ _id: string;
126
+ name: string;
127
+ }
128
+ const linked = activity.fields?.['fieldId']?.value as ActivityLinkValue;
129
+ const linkedName = linked?.name || 'Unknown';
130
+
131
+ // User field
132
+ interface UserValue {
133
+ _id: string;
134
+ firstname: string;
135
+ lastname: string;
136
+ }
137
+ const user = activity.fields?.['fieldId']?.value as UserValue;
138
+ const userName = user ? `${user.firstname} ${user.lastname}` : 'Unknown';
139
+ ```
140
+ </field-patterns>
141
+
142
+ <component-templates>
143
+ ## Activity Table
144
+
145
+ ```typescript
146
+ import { Table, Thead, Tbody, Tr, Th, Td, Box, Spinner, Text } from '@chakra-ui/react';
147
+
148
+ interface Activity {
149
+ _id: string;
150
+ name: string;
151
+ fields?: Record<string, { value: unknown }>;
152
+ }
153
+
154
+ interface Props {
155
+ activities: Activity[];
156
+ loading: boolean;
157
+ columns: { fieldId: string; label: string }[];
158
+ }
159
+
160
+ function ActivityTable({ activities, loading, columns }: Props) {
161
+ if (loading) return <Spinner />;
162
+ if (activities.length === 0) return <Text>No data</Text>;
163
+
164
+ return (
165
+ <Table variant="simple" size="sm">
166
+ <Thead>
167
+ <Tr>
168
+ <Th>Name</Th>
169
+ {columns.map(col => (
170
+ <Th key={col.fieldId}>{col.label}</Th>
171
+ ))}
172
+ </Tr>
173
+ </Thead>
174
+ <Tbody>
175
+ {activities.map(activity => (
176
+ <Tr key={activity._id}>
177
+ <Td>{activity.name}</Td>
178
+ {columns.map(col => (
179
+ <Td key={col.fieldId}>
180
+ {String(activity.fields?.[col.fieldId]?.value ?? '-')}
181
+ </Td>
182
+ ))}
183
+ </Tr>
184
+ ))}
185
+ </Tbody>
186
+ </Table>
187
+ );
188
+ }
189
+ ```
190
+
191
+ ## Activity Card
192
+
193
+ ```typescript
194
+ import { Box, Heading, Text, VStack, useColorModeValue } from '@chakra-ui/react';
195
+
196
+ interface Props {
197
+ activity: Activity;
198
+ fieldId: string;
199
+ fieldLabel: string;
200
+ }
201
+
202
+ function ActivityCard({ activity, fieldId, fieldLabel }: Props) {
203
+ const bg = useColorModeValue('white', 'gray.700');
204
+ const borderColor = useColorModeValue('gray.200', 'gray.600');
205
+
206
+ return (
207
+ <Box
208
+ p={4}
209
+ bg={bg}
210
+ borderRadius="md"
211
+ border="1px"
212
+ borderColor={borderColor}
213
+ >
214
+ <VStack align="start" spacing={2}>
215
+ <Heading size="sm">{activity.name}</Heading>
216
+ <Text fontSize="sm" color="gray.500">
217
+ {fieldLabel}: {String(activity.fields?.[fieldId]?.value ?? '-')}
218
+ </Text>
219
+ </VStack>
220
+ </Box>
221
+ );
222
+ }
223
+ ```
224
+
225
+ ## Stats Card
226
+
227
+ ```typescript
228
+ import { Stat, StatLabel, StatNumber, StatHelpText, Box, useColorModeValue } from '@chakra-ui/react';
229
+
230
+ interface Props {
231
+ label: string;
232
+ value: number | string;
233
+ helpText?: string;
234
+ }
235
+
236
+ function StatsCard({ label, value, helpText }: Props) {
237
+ const bg = useColorModeValue('white', 'gray.700');
238
+
239
+ return (
240
+ <Box p={4} bg={bg} borderRadius="md" shadow="sm">
241
+ <Stat>
242
+ <StatLabel>{label}</StatLabel>
243
+ <StatNumber>{value}</StatNumber>
244
+ {helpText && <StatHelpText>{helpText}</StatHelpText>}
245
+ </Stat>
246
+ </Box>
247
+ );
248
+ }
249
+ ```
250
+ </component-templates>
251
+
252
+ <app-template>
253
+ ## Full App Template
254
+
255
+ ```typescript
256
+ import { useEffect, useState } from 'react';
257
+ import {
258
+ Box,
259
+ Heading,
260
+ Text,
261
+ Spinner,
262
+ Table,
263
+ Thead,
264
+ Tbody,
265
+ Tr,
266
+ Th,
267
+ Td,
268
+ VStack,
269
+ useColorModeValue,
270
+ } from '@chakra-ui/react';
271
+ import useHailer from './hailer/use-hailer';
272
+
273
+ // Field IDs from workflow schema (provided by orchestrator)
274
+ const FIELDS = {
275
+ NAME_FIELD: 'fieldId123',
276
+ STATUS_FIELD: 'fieldId456',
277
+ } as const;
278
+
279
+ interface Activity {
280
+ _id: string;
281
+ name: string;
282
+ fields?: Record<string, { value: unknown }>;
283
+ }
284
+
285
+ function App() {
286
+ const { inside, hailer } = useHailer();
287
+ const [activities, setActivities] = useState<Activity[]>([]);
288
+ const [loading, setLoading] = useState(true);
289
+ const [error, setError] = useState<string | null>(null);
290
+
291
+ const bg = useColorModeValue('gray.50', 'gray.800');
292
+
293
+ // Fetch data when inside Hailer
294
+ useEffect(() => {
295
+ if (!inside) return;
296
+
297
+ async function fetchData() {
298
+ try {
299
+ setLoading(true);
300
+ const data = await hailer.activity.list(
301
+ 'workflowId', // Replace with actual workflow ID
302
+ 'phaseId', // Replace with actual phase ID
303
+ { limit: 100 }
304
+ );
305
+ setActivities(data);
306
+ } catch (err) {
307
+ setError(err instanceof Error ? err.message : 'Failed to load data');
308
+ } finally {
309
+ setLoading(false);
310
+ }
311
+ }
312
+
313
+ fetchData();
314
+ }, [inside]); // IMPORTANT: [inside] not [hailer]
315
+
316
+ // Early return AFTER hooks
317
+ if (!inside) {
318
+ return (
319
+ <Box p={8} textAlign="center">
320
+ <Text>Please open this app inside Hailer</Text>
321
+ </Box>
322
+ );
323
+ }
324
+
325
+ if (loading) {
326
+ return (
327
+ <Box p={8} textAlign="center">
328
+ <Spinner size="xl" />
329
+ </Box>
330
+ );
331
+ }
332
+
333
+ if (error) {
334
+ return (
335
+ <Box p={8} textAlign="center">
336
+ <Text color="red.500">{error}</Text>
337
+ </Box>
338
+ );
339
+ }
340
+
341
+ return (
342
+ <Box p={4} bg={bg} minH="100vh">
343
+ <VStack spacing={4} align="stretch">
344
+ <Heading size="lg">Dashboard</Heading>
345
+
346
+ <Table variant="simple" size="sm">
347
+ <Thead>
348
+ <Tr>
349
+ <Th>Name</Th>
350
+ <Th>Status</Th>
351
+ </Tr>
352
+ </Thead>
353
+ <Tbody>
354
+ {activities.map(activity => (
355
+ <Tr key={activity._id}>
356
+ <Td>{activity.name}</Td>
357
+ <Td>{String(activity.fields?.[FIELDS.STATUS_FIELD]?.value ?? '-')}</Td>
358
+ </Tr>
359
+ ))}
360
+ </Tbody>
361
+ </Table>
362
+ </VStack>
363
+ </Box>
364
+ );
365
+ }
366
+
367
+ export default App;
368
+ ```
369
+ </app-template>
370
+
371
+ <theme-patterns>
372
+ ## Hailer Theme Colors
373
+
374
+ ```typescript
375
+ // Dark mode support
376
+ const bg = useColorModeValue('white', 'gray.700');
377
+ const borderColor = useColorModeValue('gray.200', 'gray.600');
378
+ const textColor = useColorModeValue('gray.800', 'white');
379
+ const mutedColor = useColorModeValue('gray.500', 'gray.400');
380
+
381
+ // Valid color tokens (DO NOT invent tokens)
382
+ // gray.50, gray.100, ..., gray.900
383
+ // white, black
384
+ // red.500, green.500, blue.500, yellow.500, purple.500
385
+ ```
386
+
387
+ ## Common UI Patterns
388
+
389
+ ```typescript
390
+ // Page container
391
+ <Box p={4} bg={useColorModeValue('gray.50', 'gray.800')} minH="100vh">
392
+
393
+ // Card
394
+ <Box p={4} bg={useColorModeValue('white', 'gray.700')} borderRadius="md" shadow="sm">
395
+
396
+ // Section with border
397
+ <Box p={4} border="1px" borderColor={useColorModeValue('gray.200', 'gray.600')} borderRadius="md">
398
+ ```
399
+ </theme-patterns>
400
+
401
+ <build-fixes>
402
+ ## Common Build Errors
403
+
404
+ ### Cannot find module '@hailer/app-sdk'
405
+ ```typescript
406
+ // WRONG
407
+ import { useHailer } from '@hailer/app-sdk';
408
+
409
+ // CORRECT
410
+ import useHailer from './hailer/use-hailer';
411
+ ```
412
+
413
+ ### has no exported member 'useHailer'
414
+ ```typescript
415
+ // WRONG - named import
416
+ import { useHailer } from './hailer/use-hailer';
417
+
418
+ // CORRECT - default import
419
+ import useHailer from './hailer/use-hailer';
420
+ ```
421
+
422
+ ### fields possibly undefined
423
+ ```typescript
424
+ // WRONG
425
+ const value = activity.fields[fieldId].value;
426
+
427
+ // CORRECT
428
+ const value = activity.fields?.[fieldId]?.value;
429
+ ```
430
+
431
+ ### Infinite re-render loop
432
+ ```typescript
433
+ // WRONG - hailer changes every render
434
+ useEffect(() => { ... }, [hailer]);
435
+
436
+ // CORRECT - inside is stable
437
+ useEffect(() => { ... }, [inside]);
438
+ ```
439
+
440
+ ### Hooks order error
441
+ ```typescript
442
+ // WRONG - early return before hook
443
+ if (!inside) return <Text>Error</Text>;
444
+ const [data, setData] = useState([]); // Error!
445
+
446
+ // CORRECT - hooks first, then early return
447
+ const [data, setData] = useState([]);
448
+ if (!inside) return <Text>Error</Text>;
449
+ ```
450
+ </build-fixes>
451
+
452
+ <file-structure>
453
+ ## Required Files
454
+
455
+ ```
456
+ src/
457
+ App.tsx # Main component (EDIT THIS)
458
+ main.tsx # Entry point (NEVER EDIT)
459
+ hailer/
460
+ use-hailer.ts # SDK hook (generated)
461
+ types/
462
+ index.ts # Type definitions (CREATE)
463
+ utils/
464
+ fields.ts # Field helpers (CREATE)
465
+ constants/
466
+ fields.ts # Field ID constants (CREATE)
467
+ ```
468
+
469
+ ## Constants File Pattern
470
+
471
+ ```typescript
472
+ // src/constants/fields.ts
473
+ export const WORKFLOW_ID = 'workflowId123';
474
+ export const PHASE_ID = 'phaseId456';
475
+
476
+ export const FIELDS = {
477
+ NAME: 'fieldId001',
478
+ STATUS: 'fieldId002',
479
+ DATE: 'fieldId003',
480
+ } as const;
481
+ ```
482
+
483
+ ## Types File Pattern
484
+
485
+ ```typescript
486
+ // src/types/index.ts
487
+ export interface Activity {
488
+ _id: string;
489
+ name: string;
490
+ fields?: Record<string, { value: unknown }>;
491
+ created?: number;
492
+ updated?: number;
493
+ }
494
+
495
+ export interface ActivityLinkValue {
496
+ _id: string;
497
+ name: string;
498
+ }
499
+
500
+ export interface UserValue {
501
+ _id: string;
502
+ firstname: string;
503
+ lastname: string;
504
+ }
505
+ ```
506
+ </file-structure>
@@ -0,0 +1,169 @@
1
+ ---
2
+ name: publish-hailer-app
3
+ description: Guide for publishing Hailer apps to production
4
+ ---
5
+
6
+ # Publish Hailer App Skill
7
+
8
+ Guide for publishing Hailer apps to production using MCP tools.
9
+
10
+ <critical>
11
+ ## MANDATORY: Validate Before Publishing
12
+
13
+ The `publish_hailer_app` tool will SILENTLY FAIL if manifest.json is misconfigured.
14
+
15
+ **YOU MUST validate these before calling publish_hailer_app:**
16
+
17
+ 1. Read `public/manifest.json` in the project
18
+ 2. Check `appId` exists and is 24 characters
19
+ 3. Check `version` exists and is NOT empty (e.g., "1.0.0")
20
+ 4. Check `versionDescription` exists and is NOT empty
21
+
22
+ If ANY are missing/empty, FIX THEM FIRST before publishing.
23
+ </critical>
24
+
25
+ <validation-code>
26
+ ## Validation Steps (DO THIS FIRST)
27
+
28
+ ```
29
+ 1. Read({file_path: "{projectDir}/public/manifest.json"})
30
+
31
+ 2. Verify JSON contains:
32
+ - "appId": "24-char-id" ← If missing: use create_app first
33
+ - "version": "1.0.0" ← If empty: set to "1.0.0"
34
+ - "versionDescription": "..." ← If empty: set to "Initial release"
35
+
36
+ 3. If any field missing/empty → Edit manifest.json to fix
37
+
38
+ 4. ONLY THEN call publish_hailer_app
39
+ ```
40
+ </validation-code>
41
+
42
+ <workflow>
43
+ ## Full Publishing Workflow
44
+
45
+ ### Step 1: Check if Production App Exists
46
+
47
+ ```javascript
48
+ list_apps()
49
+ ```
50
+ Look for your app with a `https://apps.hailer.com/...` URL (NOT localhost).
51
+
52
+ ### Step 2: Create Production App (if needed)
53
+
54
+ If app doesn't exist or only has localhost URL:
55
+ ```javascript
56
+ create_app({
57
+ name: "My App Name",
58
+ description: "What the app does"
59
+ })
60
+ ```
61
+ Returns new appId with production URL.
62
+
63
+ ### Step 3: Validate & Fix manifest.json
64
+
65
+ Read the manifest:
66
+ ```javascript
67
+ Read({file_path: "{projectDir}/public/manifest.json"})
68
+ ```
69
+
70
+ Required structure:
71
+ ```json
72
+ {
73
+ "appId": "695816e2ba1d8bef3af7e018",
74
+ "version": "1.0.0",
75
+ "versionDescription": "Initial production release"
76
+ }
77
+ ```
78
+
79
+ **If appId missing:** Add the ID from Step 2
80
+ **If version empty:** Set to "1.0.0"
81
+ **If versionDescription empty:** Set to "Initial release"
82
+
83
+ ### Step 4: Publish
84
+
85
+ ```javascript
86
+ publish_hailer_app({
87
+ projectDirectory: "/path/to/app"
88
+ })
89
+ ```
90
+
91
+ ### Step 5: Verify
92
+
93
+ ```javascript
94
+ list_apps()
95
+ ```
96
+ Confirm URL is `https://apps.hailer.com/...` (not localhost).
97
+ </workflow>
98
+
99
+ <silent-failure>
100
+ ## Why Silent Failures Happen
101
+
102
+ The SDK publish script requires `version` and `versionDescription` in manifest.json.
103
+ If missing/empty, the tool returns "Success" but files are NOT uploaded.
104
+
105
+ **Symptoms of silent failure:**
106
+ - Tool says "App Published Successfully!"
107
+ - But app URL is still `http://localhost:3000`
108
+ - Or production URL returns 403/404
109
+
110
+ **Always validate manifest BEFORE publishing.**
111
+ </silent-failure>
112
+
113
+ <dev-vs-prod>
114
+ ## Dev vs Production Apps
115
+
116
+ | Type | URL | Created By | Can Publish To |
117
+ |------|-----|------------|----------------|
118
+ | Dev | `http://localhost:3000` | `scaffold_hailer_app` | NO - stays localhost |
119
+ | Prod | `https://apps.hailer.com/...` | `create_app` | YES |
120
+
121
+ **Key insight:** Dev apps (scaffolded) will ALWAYS use localhost.
122
+ To deploy to production, create a NEW app with `create_app`.
123
+ </dev-vs-prod>
124
+
125
+ <updating>
126
+ ## Updating Published Apps
127
+
128
+ 1. Bump version in manifest.json:
129
+ ```json
130
+ {
131
+ "version": "1.0.1",
132
+ "versionDescription": "Fixed data loading bug"
133
+ }
134
+ ```
135
+
136
+ 2. Publish:
137
+ ```javascript
138
+ publish_hailer_app({
139
+ projectDirectory: "/path/to/app"
140
+ })
141
+ ```
142
+
143
+ New version replaces old at same URL.
144
+ </updating>
145
+
146
+ <sharing>
147
+ ## Sharing Apps
148
+
149
+ ```javascript
150
+ // Entire workspace
151
+ add_app_member({ appId: "...", member: "network_{workspaceId}" })
152
+
153
+ // Team
154
+ add_app_member({ appId: "...", member: "team_{teamId}" })
155
+
156
+ // User
157
+ add_app_member({ appId: "...", member: "user_{userId}" })
158
+ ```
159
+ </sharing>
160
+
161
+ <checklist>
162
+ ## Pre-Publish Checklist
163
+
164
+ - [ ] Production app exists (`create_app`, not scaffold)
165
+ - [ ] manifest.json `appId` is 24 chars (not empty)
166
+ - [ ] manifest.json `version` is set (e.g., "1.0.0")
167
+ - [ ] manifest.json `versionDescription` is set (not empty)
168
+ - [ ] node_modules exists (dependencies installed)
169
+ </checklist>