@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.
- package/.claude/agents/agent-dmitri-activity-crud.md +3 -1
- package/.claude/agents/agent-giuseppe-app-builder.md +11 -12
- package/.claude/agents/agent-kenji-data-reader.md +5 -3
- package/.claude/hooks/sync-marketplace-agents.cjs +117 -56
- package/.claude/skills/hailer-app-builder/SKILL.md +506 -0
- package/.claude/skills/publish-hailer-app/SKILL.md +169 -0
- package/.claude/skills/tool-parameter-usage/SKILL.md +112 -0
- package/CHANGELOG.md +20 -0
- package/CLAUDE.md +37 -16
- package/REFACTOR_STATUS.md +127 -0
- package/dist/cli.js +0 -0
- package/dist/client/agents/base.d.ts +202 -0
- package/dist/client/agents/base.js +737 -0
- package/dist/client/agents/definitions.d.ts +53 -0
- package/dist/client/agents/definitions.js +178 -0
- package/dist/client/agents/orchestrator.d.ts +119 -0
- package/dist/client/agents/orchestrator.js +760 -0
- package/dist/client/agents/specialist.d.ts +86 -0
- package/dist/client/agents/specialist.js +340 -0
- package/dist/client/bot-manager.d.ts +44 -0
- package/dist/client/bot-manager.js +173 -0
- package/dist/client/chat-agent-daemon.d.ts +464 -0
- package/dist/client/chat-agent-daemon.js +1774 -0
- package/dist/client/daemon-factory.d.ts +106 -0
- package/dist/client/daemon-factory.js +301 -0
- package/dist/client/factory.d.ts +107 -0
- package/dist/client/factory.js +304 -0
- package/dist/client/index.d.ts +17 -0
- package/dist/client/index.js +38 -0
- package/dist/client/multi-bot-manager.d.ts +18 -0
- package/dist/client/multi-bot-manager.js +88 -1
- package/dist/client/orchestrator-daemon.d.ts +87 -0
- package/dist/client/orchestrator-daemon.js +444 -0
- package/dist/client/services/agent-registry.d.ts +108 -0
- package/dist/client/services/agent-registry.js +630 -0
- package/dist/client/services/conversation-manager.d.ts +50 -0
- package/dist/client/services/conversation-manager.js +136 -0
- package/dist/client/services/mcp-client.d.ts +48 -0
- package/dist/client/services/mcp-client.js +105 -0
- package/dist/client/services/message-classifier.d.ts +37 -0
- package/dist/client/services/message-classifier.js +187 -0
- package/dist/client/services/message-formatter.d.ts +84 -0
- package/dist/client/services/message-formatter.js +353 -0
- package/dist/client/services/session-logger.d.ts +106 -0
- package/dist/client/services/session-logger.js +446 -0
- package/dist/client/services/tool-executor.d.ts +41 -0
- package/dist/client/services/tool-executor.js +169 -0
- package/dist/client/services/workspace-schema-cache.d.ts +149 -0
- package/dist/client/services/workspace-schema-cache.js +732 -0
- package/dist/client/specialist-daemon.d.ts +77 -0
- package/dist/client/specialist-daemon.js +197 -0
- package/dist/client/specialists.d.ts +53 -0
- package/dist/client/specialists.js +178 -0
- package/dist/client/tool-schema-loader.d.ts +4 -3
- package/dist/client/tool-schema-loader.js +54 -8
- package/dist/client/types.d.ts +283 -55
- package/dist/client/types.js +113 -2
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/core.d.ts +10 -2
- package/dist/core.js +43 -27
- package/dist/lib/logger.js +15 -3
- package/dist/mcp/UserContextCache.js +2 -2
- package/dist/mcp/hailer-clients.js +5 -5
- package/dist/mcp/signal-handler.js +27 -5
- package/dist/mcp/tools/activity.js +137 -65
- package/dist/mcp/tools/app-core.js +4 -140
- package/dist/mcp/tools/app-marketplace.js +15 -260
- package/dist/mcp/tools/app-member.js +2 -73
- package/dist/mcp/tools/app-scaffold.js +146 -87
- package/dist/mcp/tools/discussion.js +348 -73
- package/dist/mcp/tools/insight.js +74 -190
- package/dist/mcp/tools/workflow.js +20 -94
- package/dist/mcp/utils/hailer-api-client.d.ts +4 -2
- package/dist/mcp/utils/hailer-api-client.js +24 -10
- package/dist/mcp-server.d.ts +4 -0
- package/dist/mcp-server.js +24 -4
- package/dist/routes/agents.d.ts +44 -0
- package/dist/routes/agents.js +311 -0
- package/dist/services/agent-credential-store.d.ts +73 -0
- package/dist/services/agent-credential-store.js +212 -0
- package/lineup-manager/dist/assets/index-8ce6041d.css +1 -0
- package/lineup-manager/dist/assets/index-e168f265.js +600 -0
- package/lineup-manager/dist/index.html +15 -0
- package/lineup-manager/dist/manifest.json +17 -0
- package/lineup-manager/dist/vite.svg +1 -0
- package/package.json +1 -1
- package/dist/client/adaptive-documentation-bot.d.ts +0 -106
- package/dist/client/adaptive-documentation-bot.js +0 -464
- package/dist/client/adaptive-documentation-types.d.ts +0 -66
- package/dist/client/adaptive-documentation-types.js +0 -9
- package/dist/client/agent-activity-bot.d.ts +0 -51
- package/dist/client/agent-activity-bot.js +0 -166
- package/dist/client/agent-tracker.d.ts +0 -499
- package/dist/client/agent-tracker.js +0 -659
- package/dist/client/description-updater.d.ts +0 -56
- package/dist/client/description-updater.js +0 -259
- package/dist/client/log-parser.d.ts +0 -72
- package/dist/client/log-parser.js +0 -387
- package/dist/client/mcp-assistant.d.ts +0 -21
- package/dist/client/mcp-assistant.js +0 -58
- package/dist/client/mcp-client.d.ts +0 -50
- package/dist/client/mcp-client.js +0 -538
- package/dist/client/message-processor.d.ts +0 -35
- package/dist/client/message-processor.js +0 -357
- package/dist/client/providers/anthropic-provider.d.ts +0 -19
- package/dist/client/providers/anthropic-provider.js +0 -645
- package/dist/client/providers/assistant-provider.d.ts +0 -17
- package/dist/client/providers/assistant-provider.js +0 -51
- package/dist/client/providers/llm-provider.d.ts +0 -47
- package/dist/client/providers/llm-provider.js +0 -367
- package/dist/client/providers/openai-provider.d.ts +0 -23
- package/dist/client/providers/openai-provider.js +0 -630
- package/dist/client/simple-llm-caller.d.ts +0 -19
- package/dist/client/simple-llm-caller.js +0 -100
- package/dist/client/skill-generator.d.ts +0 -81
- package/dist/client/skill-generator.js +0 -386
- package/dist/client/test-adaptive-bot.d.ts +0 -9
- package/dist/client/test-adaptive-bot.js +0 -82
- package/dist/client/token-pricing.d.ts +0 -38
- package/dist/client/token-pricing.js +0 -127
- package/dist/client/token-tracker.d.ts +0 -232
- package/dist/client/token-tracker.js +0 -457
- package/dist/client/token-usage-bot.d.ts +0 -53
- package/dist/client/token-usage-bot.js +0 -153
- package/dist/client/tool-executor.d.ts +0 -69
- package/dist/client/tool-executor.js +0 -159
- package/dist/lib/materialize.d.ts +0 -3
- package/dist/lib/materialize.js +0 -101
- package/dist/lib/normalizedName.d.ts +0 -7
- package/dist/lib/normalizedName.js +0 -48
- package/dist/lib/terminal-prompt.d.ts +0 -9
- package/dist/lib/terminal-prompt.js +0 -108
- package/dist/mcp/tools/skill.d.ts +0 -10
- package/dist/mcp/tools/skill.js +0 -279
- package/dist/mcp/tools/workflow-template.d.ts +0 -19
- 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>
|