@hailer/mcp 1.1.17-beta.3 → 1.2.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 (82) hide show
  1. package/.claude/CLAUDE.md +94 -135
  2. package/.claude/skills/create-and-publish-app/SKILL.md +204 -0
  3. package/.claude/skills/hailer-app-builder/SKILL.md +47 -0
  4. package/.claude/skills/publish-hailer-app/SKILL.md +35 -4
  5. package/.claude/skills/sdk-function-fields/SKILL.md +431 -287
  6. package/dist/bot/bot-manager.d.ts.map +1 -1
  7. package/dist/bot/bot-manager.js +2 -0
  8. package/dist/bot/bot-manager.js.map +1 -1
  9. package/dist/bot/bot.d.ts +2 -1
  10. package/dist/bot/bot.d.ts.map +1 -1
  11. package/dist/bot/bot.js +109 -41
  12. package/dist/bot/bot.js.map +1 -1
  13. package/dist/bot/services/message-classifier.d.ts.map +1 -1
  14. package/dist/bot/services/message-classifier.js +6 -0
  15. package/dist/bot/services/message-classifier.js.map +1 -1
  16. package/dist/bot/services/signal-router.d.ts.map +1 -1
  17. package/dist/bot/services/signal-router.js +1 -0
  18. package/dist/bot/services/signal-router.js.map +1 -1
  19. package/dist/bot/services/system-prompt.d.ts +4 -0
  20. package/dist/bot/services/system-prompt.d.ts.map +1 -1
  21. package/dist/bot/services/system-prompt.js +41 -12
  22. package/dist/bot/services/system-prompt.js.map +1 -1
  23. package/dist/bot/services/types.d.ts +7 -31
  24. package/dist/bot/services/types.d.ts.map +1 -1
  25. package/dist/bot/services/workspace-refresh.js.map +1 -1
  26. package/dist/bot/workspace-overview.d.ts.map +1 -1
  27. package/dist/bot/workspace-overview.js +4 -1
  28. package/dist/bot/workspace-overview.js.map +1 -1
  29. package/dist/bot-config/context.js.map +1 -1
  30. package/dist/bot-config/loader.d.ts.map +1 -1
  31. package/dist/bot-config/loader.js +1 -0
  32. package/dist/bot-config/loader.js.map +1 -1
  33. package/dist/bot-config/types.d.ts +2 -0
  34. package/dist/bot-config/types.d.ts.map +1 -1
  35. package/dist/mcp/UserContextCache.d.ts.map +1 -1
  36. package/dist/mcp/UserContextCache.js +8 -16
  37. package/dist/mcp/UserContextCache.js.map +1 -1
  38. package/dist/mcp/tool-registry.d.ts +3 -2
  39. package/dist/mcp/tool-registry.d.ts.map +1 -1
  40. package/dist/mcp/tool-registry.js +14 -9
  41. package/dist/mcp/tool-registry.js.map +1 -1
  42. package/dist/mcp/tools/activity.d.ts.map +1 -1
  43. package/dist/mcp/tools/activity.js +39 -94
  44. package/dist/mcp/tools/activity.js.map +1 -1
  45. package/dist/mcp/tools/app-scaffold.d.ts.map +1 -1
  46. package/dist/mcp/tools/app-scaffold.js +300 -575
  47. package/dist/mcp/tools/app-scaffold.js.map +1 -1
  48. package/dist/mcp/tools/date.d.ts +5 -0
  49. package/dist/mcp/tools/date.d.ts.map +1 -0
  50. package/dist/mcp/tools/date.js +23 -0
  51. package/dist/mcp/tools/date.js.map +1 -0
  52. package/dist/mcp/tools/discussion.d.ts.map +1 -1
  53. package/dist/mcp/tools/discussion.js +17 -9
  54. package/dist/mcp/tools/discussion.js.map +1 -1
  55. package/dist/mcp/tools/index.d.ts.map +1 -1
  56. package/dist/mcp/tools/index.js +2 -0
  57. package/dist/mcp/tools/index.js.map +1 -1
  58. package/dist/mcp/tools/insight.d.ts.map +1 -1
  59. package/dist/mcp/tools/insight.js +13 -19
  60. package/dist/mcp/tools/insight.js.map +1 -1
  61. package/dist/mcp/tools/workflow.d.ts +1 -0
  62. package/dist/mcp/tools/workflow.d.ts.map +1 -1
  63. package/dist/mcp/tools/workflow.js +293 -46
  64. package/dist/mcp/tools/workflow.js.map +1 -1
  65. package/dist/mcp/utils/data-transformers.d.ts +47 -10
  66. package/dist/mcp/utils/data-transformers.d.ts.map +1 -1
  67. package/dist/mcp/utils/data-transformers.js +12 -9
  68. package/dist/mcp/utils/data-transformers.js.map +1 -1
  69. package/dist/mcp/utils/types.d.ts +2 -0
  70. package/dist/mcp/utils/types.d.ts.map +1 -1
  71. package/dist/mcp/utils/types.js.map +1 -1
  72. package/dist/mcp/webhook-handler.d.ts.map +1 -1
  73. package/dist/mcp/webhook-handler.js +4 -1
  74. package/dist/mcp/webhook-handler.js.map +1 -1
  75. package/dist/mcp/workspace-cache.d.ts +8 -2
  76. package/dist/mcp/workspace-cache.d.ts.map +1 -1
  77. package/dist/mcp/workspace-cache.js +12 -8
  78. package/dist/mcp/workspace-cache.js.map +1 -1
  79. package/dist/plugins/vipunen/tools.d.ts +1 -0
  80. package/dist/plugins/vipunen/tools.d.ts.map +1 -1
  81. package/dist/plugins/vipunen/tools.js.map +1 -1
  82. package/package.json +1 -1
package/.claude/CLAUDE.md CHANGED
@@ -1,167 +1,126 @@
1
- # Hailer MCP Server
1
+ # Hailer SDK + MCP Project
2
2
 
3
- ## Rules (MUST follow)
3
+ ## The One Rule
4
4
 
5
- 1. **Workspace reads spawn haiku sub-agent.** Never read `workspace/` files in main context. They fill context and can't be freed.
6
- 2. **Skill loads → sub-agent only.** Never load skills (Skill tool) in main context. Tell the sub-agent which skills to load.
7
- 3. **Workspace edits → spawn sonnet sub-agent.** Fields, functions, phases — always in a sub-agent with skill names + IDs front-loaded.
8
- 4. **MCP tools are fine in main context.** Tool results are small. Use them for discovery (IDs, schemas, data).
9
- 5. **Load skills before editing.** `sdk-function-fields` + `sdk-ws-config-skill` for function fields. Skills have critical steps (omit `_id` for new fields, push → pull → phases).
10
- 6. **New fields: omit `_id`.** The server assigns it. Push → pull → add to phases → push phases.
11
- 7. **Function fields: `fields-push:force`** not `fields-push`. Non-force skips `functionVariables` diffs.
12
- 8. **After pull: verify enum imports.** Identical hex suffixes across workflows can resolve to wrong enum. Fix manually.
13
- 9. **Pattern:** MCP discovery (main) → spawn sub-agent with IDs + skill names + task → sub-agent does all file work → returns summary.
5
+ **If it can be done locally, do it locally. MCP tools are ONLY for things that require a server call.**
14
6
 
15
- ---
7
+ This project has `workspace/` files managed by `@hailer/sdk`. These files ARE the source of truth for all workflow configuration. The MCP server provides tools for runtime operations that require talking to Hailer's API.
16
8
 
17
- MCP tools for Hailer workspaces: workflows, activities, insights, and apps.
9
+ ## What Goes Where
18
10
 
19
- ## Quick Start
11
+ | Need | Do this | NEVER this |
12
+ |------|---------|------------|
13
+ | **Find workflow/field/phase IDs** | Read `workspace/enums.ts` | ~~`list_workflows_minimal`~~, ~~`get_workflow_schema`~~, ~~`list_workflow_phases`~~ |
14
+ | **See field types, labels, options** | Read `workspace/<Name>_<id>/fields.ts` | ~~`get_workflow_schema`~~ |
15
+ | **See phases** | Read `workspace/<Name>_<id>/phases.ts` | ~~`list_workflow_phases`~~ |
16
+ | **See all workflows** | Read `workspace/workflows.ts` | ~~`list_workflows`~~ |
17
+ | **Create/modify fields** | Edit `fields.ts` → `npm run fields-push:force` | ~~`update_workflow_field`~~ |
18
+ | **Create/modify phases** | Edit `phases.ts` → `npm run phases-push:force` | ~~`update_workflow_phase`~~ |
19
+ | **Create/modify workflows** | Edit `workflows.ts` → `npm run workflows-sync:force` | ~~`install_workflow`~~ |
20
+ | **Create/modify teams** | Edit `teams.ts` → `npm run teams-push:force` | — |
21
+ | **Create/modify groups** | Edit `groups.ts` → `npm run groups-push:force` | — |
22
+ | **Create activities** | MCP `create_activity` | — |
23
+ | **List/read activities** | MCP `list_activities`, `show_activity_by_id` | — |
24
+ | **Update activities** | MCP `update_activity` | — |
25
+ | **Query data** | MCP `preview_insight`, `create_insight` | — |
26
+ | **Discussions** | MCP discussion tools | — |
27
+ | **Activity counts** | MCP `core_init` (once per session) | — |
28
+ | **Scaffold app** | MCP `scaffold_hailer_app` | — |
29
+ | **Publish app** | MCP `publish_hailer_app` | — |
20
30
 
21
- ```bash
22
- npm init @hailer/sdk # Scaffold project with workspace/ config
23
- npm run pull # Pull latest workflow schemas from Hailer
24
- npm run generate # Generate TypeScript types
25
- ```
26
-
27
- ## How to Work
31
+ **If you're about to call an MCP tool, ask yourself: "Is this data in workspace/ files?" If yes, read the file instead.**
28
32
 
29
- Do simple things directly. For complex tasks, spawn parallel sub-agents.
33
+ ## How to Read workspace/ Files
30
34
 
31
- **Model usageconserve Opus for orchestration only:**
32
- - **You (Opus):** Orchestrate, synthesize results, make decisions. Do NOT write large code blocks yourself.
33
- - **Sub-agents (Sonnet):** Write code, build features, complex edits. Use `model: "sonnet"`.
34
- - **Sub-agents (Haiku):** Read files, search code, data lookups, reviews. Use `model: "haiku"`.
35
- - **Rule:** If a task takes more than ~20 lines of code, delegate to a sonnet sub-agent.
35
+ **Never read workspace/ files in main context** they're large and fill context permanently.
36
36
 
37
- **Speed rules:**
38
- - Do data discovery yourself (MCP calls are instant)
39
- - Spawn all agents in one message
40
- - Front-load context agents should write code, not discover things
41
- - Background long tasks with `run_in_background: true`
37
+ 1. Spawn a **haiku sub-agent** to read the files
38
+ 2. Have it extract what you need (IDs, field types, phase names)
39
+ 3. It returns a short summary
40
+ 4. Use that summary for MCP runtime calls
42
41
 
43
- **Feature requests:** Ask "Want me to create a PRD first?" before implementing.
42
+ ```
43
+ Pattern: haiku reads workspace/ → you get IDs → MCP create_activity/list_activities
44
+ ```
44
45
 
45
- ## Skills
46
+ ## Sub-Agent Rules
46
47
 
47
- Skills are in `.claude/skills/<name>/SKILL.md`. Load via the Skill tool.
48
+ - **Haiku:** Read files, search code, data lookups, reviews
49
+ - **Sonnet:** Write code, build features, complex edits
50
+ - **You (Opus):** Orchestrate, synthesize results, make decisions
51
+ - **Skills:** Load in sub-agents only, never in main context
52
+ - If a task takes more than ~20 lines of code, delegate to sonnet
48
53
 
49
- ### Workspace & Config
50
- | Skill | Use when |
51
- |-------|----------|
52
- | `sdk-ws-config-skill` | Workflows, fields, phases, teams, groups, new project setup |
53
- | `sdk-function-fields` | Calculated fields, nameFunction |
54
- | `sdk-activity-patterns` | Creating/updating activities |
55
- | `sdk-insight-queries` | SQL-like insight queries, cross-workflow JOINs |
56
- | `sdk-document-templates` | PDF/CSV document templates |
57
- | `hailer-permissions-system` | Workflow permissions |
58
-
59
- ### App Development
60
- | Skill | Use when |
61
- |-------|----------|
62
- | `hailer-app-builder` | Building Hailer apps (React/Chakra) |
63
- | `hailer-design-system` | Theme, colors, icons, layout, spacing, responsive design |
64
- | `hailer-apps-pictures` | Images/photos in apps |
65
- | `publish-hailer-app` | Publishing to production |
66
-
67
- ### Integrations
68
- | Skill | Use when |
69
- |-------|----------|
70
- | `hailer-monolith-automations` | Webhook handlers, scheduled jobs, phase cascade bots |
71
- | `zapier-hailer-patterns` | Zapier integrations |
72
- | `integration-patterns` | General integration patterns |
73
- | `hailer-api-client` | HailerApiClient for backend |
74
-
75
- ### Code Quality
76
- | Skill | Use when |
77
- |-------|----------|
78
- | `testing-patterns` | Vitest/playwright tests |
79
- | `api-documentation-patterns` | API endpoint docs |
80
- | `lsp-setup` | LSP code inspection |
81
- | `tool-builder` | Building new MCP tools |
82
-
83
- ## Local-First Data
84
-
85
- Check workspace/ BEFORE making API calls.
54
+ ## workspace/ Structure
86
55
 
87
56
  ```
88
57
  workspace/
89
- ├── workflows.ts, enums.ts, teams.ts, groups.ts
90
- └── [Workflow]_[id]/
91
- ├── fields.ts
92
- └── phases.ts
58
+ ├── enums.ts # ALL IDs — WorkflowIds, FieldIds, PhaseIds, etc.
59
+ ├── workflows.ts # Workflow registry
60
+ ├── teams.ts # Team definitions
61
+ ├── groups.ts # Group definitions
62
+ ├── insights.ts # Insight definitions
63
+ └── <WorkflowName>_<id>/
64
+ ├── fields.ts # Field definitions (type, label, key, options, required)
65
+ ├── phases.ts # Phase definitions (name, key, isInitial, isEndpoint)
66
+ ├── main.ts # Workflow config
67
+ └── functions/ # Function field code (if any)
93
68
  ```
94
69
 
95
- LOCAL: Workflow/field/phase IDs, field types, labels, options
96
- API: Activity data, counts, discussion messages
97
- REFRESH: `npm run pull`
70
+ **Refresh:** `npm run pull` syncs server → local files
98
71
 
99
- ## Hooks
72
+ ## SDK Push Commands (all use `:force` to avoid interactive prompts)
100
73
 
101
- 4 safety hooks in `.claude/hooks/`:
74
+ | Command | What it does |
75
+ |---------|-------------|
76
+ | `npm run push:force` | Push everything |
77
+ | `npm run fields-push:force` | Push field changes only |
78
+ | `npm run phases-push:force` | Push phase changes only |
79
+ | `npm run workflows-sync:force` | Sync workflow registry (create/delete) |
80
+ | `npm run teams-push:force` | Push team changes |
81
+ | `npm run groups-push:force` | Push group changes |
82
+ | `npm run pull` | Pull latest from Hailer → local files |
102
83
 
103
- - `session-start.cjs` — loads SESSION-HANDOFF.md on start
104
- - `bash-guard.cjs` — confirms destructive shell commands
105
- - `bulk-activity-guard.cjs` confirms bulk activity writes
106
- - `publish-template-guard.cjs` — confirms template publishes
107
- - `context-watchdog.cjs` — monitors context usage
84
+ ## SDK Gotchas
85
+
86
+ | Gotcha | Correct | Wrong |
87
+ |--------|---------|-------|
88
+ | New fields | Omit `_id` — server assigns it | Adding `_id` manually |
89
+ | After push | Always `npm run pull` to get server-assigned IDs | Assuming local IDs are final |
90
+ | Function fields | `npm run fields-push:force` (non-force skips functionVariables) | `npm run fields-push` |
91
+ | Enum imports | Verify after pull — identical hex suffixes can collide | Trusting auto-generated imports |
92
+ | `linkedfrom` fields | Don't work in isolated-vm — use `<` backlink dependency | Using in function fields |
93
+ | Function field code | Plain JavaScript only | TypeScript syntax |
94
+ | Phase transitions | Exact string match on phase name | Guessed names |
95
+ | Field values (MCP) | Date: Unix ms (`1730937600000`), Dropdown: exact string, ActivityLink: string ID | ISO dates, arrays, objects |
108
96
 
109
97
  ## App Development
110
98
 
111
- **Scaffold:** Always use `scaffold_hailer_app` MCP tool. Never copy an existing app.
99
+ | Step | How |
100
+ |------|-----|
101
+ | Scaffold | MCP `scaffold_hailer_app` — never copy apps |
102
+ | Publish | MCP `publish_hailer_app` — auto icon, name, description, sharing |
103
+ | Local dev | `npm run dev` from app directory |
112
104
 
113
- **Local dev:** Scaffold creates app at `http://localhost:3000`. Run `npm run dev`.
105
+ ## Skills
114
106
 
115
- **Publishing:** Only when user asks. Load `publish-hailer-app` skill.
107
+ Load in sub-agents via Skill tool. Never in main context.
108
+
109
+ | Skill | When |
110
+ |-------|------|
111
+ | `sdk-ws-config-skill` | Workflows, fields, phases, teams, groups |
112
+ | `sdk-function-fields` | Calculated fields, nameFunction |
113
+ | `sdk-activity-patterns` | Creating/updating activities via MCP |
114
+ | `sdk-insight-queries` | SQL insight queries |
115
+ | `hailer-app-builder` | Building Hailer apps (React/Chakra) |
116
+ | `hailer-design-system` | Theme, colors, icons, layout |
117
+ | `publish-hailer-app` | Publishing apps to production |
118
+ | `testing-patterns` | Vitest/playwright tests |
116
119
 
117
120
  ## Commands
118
121
 
119
- `/command <param>` (angle brackets = required). `/help:topic` (colon = subtopic).
122
+ `/command <param>` angle brackets = required
120
123
 
121
124
  **Essential:** `/save`, `/handoff`, `/prd`, `/autoplan`, `/ws-pull`
122
125
 
123
- **Squads:**
124
- | Squad | Use for |
125
- |-------|---------|
126
- | `/app-squad` | Build apps end-to-end |
127
- | `/review-squad` | Code review + tests |
128
- | `/config-squad` | Workflow + fields + insights |
129
- | `/hotfix-squad` | Quick bug fixes |
130
- | `/debug-squad` | Investigation |
131
- | `/swarm <desc>` | Large-scale parallel work |
132
-
133
- ## Project Structure
134
-
135
- ```
136
- src/ # MCP server source
137
- workspace/ # Hailer config - check FIRST for IDs
138
- apps/ # Frontend apps
139
- integrations/ # Backend services
140
- .claude/ # Skills, hooks, commands
141
- ```
142
-
143
- ## When to Use SDK Files vs MCP Tools
144
-
145
- | Task | Use | NOT |
146
- |------|-----|-----|
147
- | Create/modify fields, phases, workflows | Edit `workspace/` files → `npm run push` | MCP `update_workflow_field` |
148
- | Create function fields | Edit `fields.ts` + `functions/` → `vitest` → `npm run fields-push` | MCP tools |
149
- | Read/write activity data | MCP tools (`list_activities`, `create_activity`, etc.) | Editing files |
150
- | Query/report on data | MCP `preview_insight`, `create_insight` | — |
151
- | Test existing function field code | MCP `test_function_field` (with `functionVariables`) | — |
152
-
153
- **Function field workflow:** See Rules #5-8 at top of file.
154
-
155
- ## Hailer SDK Gotchas
156
-
157
- | Gotcha | Correct | Wrong |
158
- |--------|---------|-------|
159
- | Activity field updates | `{type: "string", value: "x"}` | Raw value `"x"` |
160
- | `linkedfrom` field type | Does NOT work in isolated-vm | Use `<` backlink dependency in functionVariables instead |
161
- | Code in isolated-vm | Plain JavaScript only | TypeScript syntax |
162
- | Phase transitions | Exact string match | Guessed names |
163
- | Field IDs | Read from workspace/ | Guessing from labels |
164
- | Dropdown values | `{data: [{value, label}]}` | `{options: [...]}` |
165
- | ActivityLink format | Plain string array | Nested objects |
166
-
167
- **Rule:** Always read workspace/ first. Never guess IDs or formats.
126
+ **Squads:** `/app-squad`, `/review-squad`, `/config-squad`, `/hotfix-squad`, `/debug-squad`, `/swarm <desc>`
@@ -0,0 +1,204 @@
1
+ ---
2
+ name: create-and-publish-app
3
+ description: How to scaffold, create and publish Hailer apps using @hailer/create-app npm package (CLI-based alternative to scaffold_hailer_app MCP tool)
4
+ version: 1.1.0
5
+ triggers:
6
+ - create-app
7
+ - npx create-app
8
+ - hailer create-app
9
+ - scaffold app cli
10
+ ---
11
+
12
+ # Create and Publish Hailer App
13
+
14
+ Scaffold, create and publish Hailer apps using `@hailer/create-app` npm package.
15
+
16
+ **npm package docs:** https://www.npmjs.com/package/@hailer/create-app
17
+ **Registry README (machine-readable):** https://registry.npmjs.org/@hailer/create-app (check `readme` field)
18
+
19
+ <when-to-use>
20
+ ## When to Use This vs scaffold_hailer_app MCP Tool
21
+
22
+ | Approach | When |
23
+ |----------|------|
24
+ | `npx @hailer/create-app` (this skill) | CLI-based scaffolding, `npm run publish-production` for publishing |
25
+ | `scaffold_hailer_app` MCP tool | MCP-based scaffolding, `publish_hailer_app` MCP tool for publishing |
26
+
27
+ Both create valid Hailer apps. This skill covers the npm CLI path.
28
+
29
+ **IMPORTANT: The `scaffold_hailer_app` MCP tool uses `/bin/sh` internally. In Hailer Studio (cluster environment), `/bin/sh` is not at the standard path and the MCP tool will fail with `spawnSync /bin/sh ENOENT`. Always use `npx @hailer/create-app` in Hailer Studio.**
30
+ </when-to-use>
31
+
32
+ <step-1>
33
+ ## Step 1 — Scaffold the Project
34
+
35
+ ```bash
36
+ npx @hailer/create-app <project-name> --template react-ts
37
+ ```
38
+
39
+ - Run from a parent directory (e.g. project root or an `apps/` subfolder — create it first if needed: `mkdir -p apps`)
40
+ - `react-ts` is the recommended template
41
+ - **CRITICAL:** The CLI is interactive-only when called via `npm create` — always use `npx @hailer/create-app` with the name and template as arguments to avoid prompts
42
+ - **Hailer Studio only:** Do NOT use `scaffold_hailer_app` MCP tool — it fails with `spawnSync /bin/sh ENOENT` in the cluster environment
43
+ </step-1>
44
+
45
+ <step-2>
46
+ ## Step 2 — Install Dependencies
47
+
48
+ ```bash
49
+ cd apps/<project-name>
50
+ npm install
51
+ ```
52
+ </step-2>
53
+
54
+ <step-3>
55
+ ## Step 3 — Build the App UI
56
+
57
+ Replace `src/App.tsx` and add components. Key patterns:
58
+
59
+ - Use `useApp` from `src/hailer/use-app.ts` (the new template uses zustand-based state)
60
+ - Call `api.init()` in a `useEffect` in `App.tsx`
61
+ - Use `hailer.activity.list(workflowId, phaseId, options)` to fetch data — call once per phase in parallel with `Promise.all`
62
+ - Use `hailer.ui.activity.open(id)` to open an activity sidebar
63
+ - Use `hailer.ui.activity.create(workflowId)` to open the create form
64
+ </step-3>
65
+
66
+ <step-4>
67
+ ## Step 4 — Publish Dev Version
68
+
69
+ **Do NOT publish as production yet WHEN IN HAILER STUDIO.** Publish the app under the name `<App Name> - Dev` so it can be tested inside Hailer. This is the dev iteration loop — build, publish as Dev, test, fix, repeat.
70
+
71
+ ### Hailer Studio (cluster)
72
+
73
+ ```bash
74
+ npm run publish-production -- --host http://hailer-api:1337 --create --app-name "<App Name> - Dev" --workspace <workspaceId> --force
75
+ ```
76
+
77
+ After the first publish, `public/manifest.json` is updated with the dev app's `appId`. Subsequent dev publishes just need `--force` — no `--create` or `--app-name` needed.
78
+
79
+ Then share it with the workspace:
80
+
81
+ ```javascript
82
+ add_app_member({ appId: "<devAppId>", member: "network_<workspaceId>" })
83
+ ```
84
+
85
+ **Why not localhost:3000?** In Hailer Studio the sandbox is not reachable from outside — `localhost:3000` is inaccessible to the user. Publishing is the only way to test the app inside Hailer.
86
+
87
+ **Only proceed to Step 5 when the user explicitly says they are happy and want to publish to production.**
88
+ </step-4>
89
+
90
+ <step-5>
91
+ ## Step 5 — Publish to Production (Only When User Explicitly Asks)
92
+
93
+ When the user confirms they are happy with the dev version and explicitly asks to publish:
94
+
95
+ Update `public/manifest.json` — clear the `appId` (or use a different one) so `--create` makes a new separate production app entry. Then:
96
+
97
+ ### On a developer's local machine (can reach api.hailer.com)
98
+
99
+ ```bash
100
+ npm run publish-production -- --create --app-name "<App Name>" --workspace <workspaceId> --force
101
+ ```
102
+
103
+ ### In Hailer Studio (cluster environment — cannot reach api.hailer.com)
104
+
105
+ ```bash
106
+ npm run publish-production -- --host http://hailer-api:1337 --create --app-name "<App Name>" --workspace <workspaceId> --force
107
+ ```
108
+
109
+ The `--host` flag overrides the hardcoded `https://api.hailer.com` default. Credentials are picked up automatically from `~/.env`.
110
+
111
+ | Flag | Purpose |
112
+ |------|---------|
113
+ | `--create` | Creates a brand new app entry in Hailer |
114
+ | `--app-name` | Name shown in Hailer |
115
+ | `--workspace` | Workspace ID from `config.json` at project root |
116
+ | `--force` | Skips confirmation prompt |
117
+ | `--host` | API URL — required in cluster environments |
118
+ | `--user-api-key` | API key — read from `~/.env` if not provided as flag |
119
+
120
+ **Credential resolution order:**
121
+ 1. `--user-api-key` or `--email` flags
122
+ 2. `USER_API_KEY` or `HAILER_USER_API_KEY` environment variables
123
+ 3. `HAILER_USER_API_KEY` from `~/.env` ← auto-injected by Hailer Studio
124
+
125
+ After running, `public/manifest.json` is auto-updated with the new `appId`.
126
+
127
+ ### After publishing — share with workspace
128
+
129
+ The CLI publish does NOT auto-share. Share manually via MCP:
130
+
131
+ ```javascript
132
+ add_app_member({ appId: "<appId>", member: "network_<workspaceId>" })
133
+ ```
134
+ </step-5>
135
+
136
+ <step-6>
137
+ ## Step 6 — Subsequent Publishes
138
+
139
+ ### Local machine
140
+
141
+ ```bash
142
+ npm run publish-production -- --force
143
+ ```
144
+
145
+ ### Hailer Studio (cluster)
146
+
147
+ ```bash
148
+ npm run publish-production -- --host http://hailer-api:1337 --force
149
+ ```
150
+
151
+ The `appId` in `manifest.json` determines which app gets updated.
152
+ </step-6>
153
+
154
+ <dev-vs-prod>
155
+ ## Dev vs Production Strategy
156
+
157
+ Always maintain two separate published app entries in Hailer:
158
+
159
+ | App | Name convention | Purpose |
160
+ |-----|----------------|---------|
161
+ | Dev | `My App - Dev` | Publish here repeatedly during development and testing |
162
+ | Production | `My App` | Publish here only when user confirms they are happy |
163
+
164
+ **In Hailer Studio, `localhost:3000` is not accessible to the user — publishing is the only way to test inside Hailer. This means the dev app gets published and re-published on every iteration.**
165
+
166
+ To switch between dev and production app entries, keep track of both `appId` values. Before publishing production, set `appId` in `public/manifest.json` to the production app's ID (or use `--create` to make a new one).
167
+ </dev-vs-prod>
168
+
169
+ <environments>
170
+ ## Environments
171
+
172
+ | Command | Default target |
173
+ |---------|---------------|
174
+ | `npm run publish-production` | https://api.hailer.com |
175
+ | `npm run publish-development` | https://testapi.hailer.biz |
176
+ | `npm run publish-staging` | https://api.hailer.biz |
177
+
178
+ **In Hailer Studio**, append `-- --host http://hailer-api:1337` to any of the above to override the default host:
179
+
180
+ ```bash
181
+ npm run publish-production -- --host http://hailer-api:1337 --force
182
+ ```
183
+ </environments>
184
+
185
+ <checklist>
186
+ ## Dev App Checklist (Step 4 — do this first)
187
+
188
+ - [ ] Published as `<App Name> - Dev` using `--create --app-name "<App Name> - Dev"`
189
+ - [ ] Shared dev app with workspace via MCP `add_app_member`
190
+ - [ ] App opens and works correctly inside Hailer
191
+ - [ ] Iterate: fix → `npm run publish-production -- --host ... --force` → test in Hailer → repeat
192
+
193
+ ## Production Publish Checklist (Step 5 — only when user asks to publish)
194
+
195
+ - [ ] User has explicitly requested publishing/deploying
196
+ - [ ] Read `<project-name>/README.md` — it always has the latest commands for that version
197
+ - [ ] Verify `public/manifest.json` has the correct `appId` for the target app
198
+ - [ ] `version` and `versionDescription` in manifest are only required for marketplace publishes
199
+ - [ ] Build runs automatically as part of the publish script — no separate build step needed
200
+ - [ ] Workspace ID is in `config.json` at the project root
201
+ - [ ] Fixed `StoreSet` type error in `src/hailer/use-app.ts` (`replace?: false` not `boolean`)
202
+ - [ ] **Hailer Studio:** Append `-- --host http://hailer-api:1337` to `npm run publish-*`
203
+ - [ ] **After first publish:** Manually share via MCP `add_app_member` — CLI does not auto-share
204
+ </checklist>
@@ -483,6 +483,53 @@ const workspaces = await hailer.workspace.list();
483
483
  ```
484
484
  </sdk-api>
485
485
 
486
+ <field-resolver>
487
+ ## Field Resolver Pattern
488
+
489
+ Every scaffolded app includes `src/hailer/field-resolver.ts`. It resolves human-readable field keys to hex IDs at runtime.
490
+
491
+ ### Functions
492
+
493
+ **`createFieldResolver(workflowFields)`** — low-level. Takes a workflow's `fields` map, returns a resolver function.
494
+
495
+ **`useFieldResolver(workflows, workflowId)`** — hook-friendly wrapper. Takes `useApp().app.workflows` and a workflow ID.
496
+
497
+ ### How It Works
498
+
499
+ - Pass a field **key** (e.g., `'matchDate'`) → resolves to hex ID at runtime
500
+ - Pass a field **hex ID** (e.g., `'691ffdf84217e9e8434e5697'`) → returns as-is
501
+ - Workflows without keys → passthrough, hex IDs still work
502
+
503
+ ### Usage
504
+
505
+ ```typescript
506
+ import { useFieldResolver } from './hailer/field-resolver';
507
+ import { useApp } from './hailer/use-app';
508
+
509
+ const WORKFLOW_ID = '691ffdf84217e9e8434e56a5';
510
+ const FIELD_KEYS = {
511
+ MATCH_DATE: 'matchDate',
512
+ HOME_TEAM: 'homeTeam',
513
+ };
514
+
515
+ function MyComponent() {
516
+ const { app } = useApp();
517
+ const f = useFieldResolver(app.workflows, WORKFLOW_ID);
518
+
519
+ // f() resolves keys to IDs — works with either
520
+ const date = activity.fields?.[f(FIELD_KEYS.MATCH_DATE)];
521
+ const team = activity.fields?.[f(FIELD_KEYS.HOME_TEAM)];
522
+ }
523
+ ```
524
+
525
+ ### Key Points
526
+
527
+ - Define field keys as constants (human-readable, portable across workspaces)
528
+ - The resolver handles both keys and hex IDs — safe to pass either
529
+ - Wait for workflow data before fetching activities — add the workflow to `useEffect` deps
530
+ - `useApp().app.workflows` is loaded on init by the scaffold's `onReady` handler
531
+ </field-resolver>
532
+
486
533
  <field-patterns>
487
534
  ## Extracting Field Values
488
535
 
@@ -86,11 +86,17 @@ publish_hailer_app({
86
86
  ```
87
87
 
88
88
  This does everything in one call:
89
+ - **Auto-derives app name** from project directory (e.g., `upcoming-matches` → "Upcoming Matches")
90
+ - **Auto-sets description** from `versionDescription` in manifest.json
91
+ - **Auto-generates and uploads a colored icon** with initials (e.g., green "UM" circle)
92
+ - **Auto-shares** with the entire workspace
89
93
  - Copies manifest.json into dist/
90
94
  - Creates .tgz with `package/` prefix (matches npm pack format)
91
95
  - Uploads to S3 via POST /app/publish
92
96
  - **Auto-updates app URL** to `https://apps.hailer.com/{workspaceId}/{appId}/`
93
97
 
98
+ No manual `update_app` calls needed after publish — name, description, icon, and sharing are all automatic.
99
+
94
100
  ### Step 3: Verify
95
101
 
96
102
  ```javascript
@@ -124,9 +130,14 @@ If missing/empty, the tool returns "Success" but files are NOT uploaded.
124
130
  **Flow:**
125
131
  1. `scaffold_hailer_app` checks for existing dev app at localhost:3000 — reuses it instead of creating duplicates
126
132
  2. Dev app stays at localhost forever — it's your development slot
127
- 3. When user says "publish": `publish_hailer_app` detects the dev app URL, auto-creates a NEW production app entry, publishes to it, and updates the URL
128
-
129
- **No manual `update_app` or `create_app` needed.** The publish tool handles everything automatically.
133
+ 3. When user says "publish": `publish_hailer_app` detects the dev app URL, auto-creates a NEW production app with:
134
+ - Name derived from project directory (e.g., `upcoming-matches` → "Upcoming Matches")
135
+ - Description from `versionDescription` in manifest.json
136
+ - Colored icon with initials (auto-generated, uploaded as public)
137
+ - Shared with entire workspace
138
+ - URL pointing to production CDN
139
+
140
+ **No manual `update_app` or `create_app` needed.** The publish tool handles name, description, icon, sharing, and URL automatically.
130
141
  </dev-vs-prod>
131
142
 
132
143
  <updating>
@@ -181,6 +192,8 @@ Use `publish_app` with `productId` (the marketplace listing ID, NOT the targetId
181
192
  <sharing>
182
193
  ## Sharing Apps
183
194
 
195
+ Workspace sharing is automatic on first publish. For manual sharing:
196
+
184
197
  ```javascript
185
198
  // Entire workspace
186
199
  add_app_member({ appId: "...", member: "network_{workspaceId}" })
@@ -193,6 +206,22 @@ add_app_member({ appId: "...", member: "user_{userId}" })
193
206
  ```
194
207
  </sharing>
195
208
 
209
+ <gotchas>
210
+ ## Gotchas
211
+
212
+ **App icons MUST be uploaded with `isPublic: true`.**
213
+ Files uploaded without this flag are private — the Hailer frontend can't load them and shows a broken/transparent image. The publish tool handles this automatically, but if you manually upload an icon via `upload_files`, always include `isPublic: true`:
214
+ ```javascript
215
+ upload_files({ files: [{ path: "/tmp/icon.png", isPublic: true }] })
216
+ ```
217
+
218
+ **SVG images don't work as app icons.**
219
+ Hailer's backend image pipeline uses sharp for resizing and doesn't serve SVGs correctly. Always use PNG or JPEG.
220
+
221
+ **Don't use SVG gradients in icon generation.**
222
+ Sharp's `flatten()` with SVG `linearGradient` produces transparency. Use `sharp.create()` with solid RGB background + composite for text.
223
+ </gotchas>
224
+
196
225
  <vite-config>
197
226
  ## CRITICAL: Vite Base Path
198
227
 
@@ -241,9 +270,11 @@ Hailer apps are served from `apps.hailer.com` static hosting - we can't control
241
270
 
242
271
  - [ ] manifest.json `appId` is 24 chars (dev app ID is fine — publish auto-creates prod app)
243
272
  - [ ] manifest.json `version` is set (e.g., "1.0.0")
244
- - [ ] manifest.json `versionDescription` is set (not empty)
273
+ - [ ] manifest.json `versionDescription` is set (not empty — also used as app description)
245
274
  - [ ] vite.config.ts has `base: './'`
246
275
  - [ ] index.html has no-cache meta tags (see cache-busting section)
247
276
  - [ ] node_modules exists (dependencies installed)
248
277
  - [ ] After publish: verify URL auto-updated to production format (check with `list_apps`)
278
+
279
+ **Automatic on first publish** (no action needed): app name, description, colored icon, workspace sharing.
249
280
  </checklist>