@hailer/mcp 1.1.17-beta.2 → 1.2.0

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 (83) hide show
  1. package/.claude/CLAUDE.md +94 -135
  2. package/.claude/skills/create-and-publish-app/SKILL.md +127 -0
  3. package/.claude/skills/hailer-app-builder/SKILL.md +47 -0
  4. package/.claude/skills/publish-hailer-app/SKILL.md +63 -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
  83. package/scripts/postinstall.cjs +0 -9
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,127 @@
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.0.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
+ </when-to-use>
29
+
30
+ <step-1>
31
+ ## Step 1 — Scaffold the Project
32
+
33
+ ```bash
34
+ npx @hailer/create-app <project-name> --template react-ts
35
+ ```
36
+
37
+ - Run from the `apps/` directory (create it first if it doesn't exist: `mkdir -p apps`)
38
+ - `react-ts` is the recommended template
39
+ - **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
40
+ </step-1>
41
+
42
+ <step-2>
43
+ ## Step 2 — Install Dependencies
44
+
45
+ ```bash
46
+ cd apps/<project-name>
47
+ npm install
48
+ ```
49
+ </step-2>
50
+
51
+ <step-3>
52
+ ## Step 3 — Build the App UI
53
+
54
+ Replace `src/App.tsx` and add components. Key patterns:
55
+
56
+ - Use `useApp` from `src/hailer/use-app.ts` (the new template uses zustand-based state)
57
+ - Call `api.init()` in a `useEffect` in `App.tsx`
58
+ - Use `hailer.activity.list(workflowId, phaseId, options)` to fetch data — call once per phase in parallel with `Promise.all`
59
+ - Use `hailer.ui.activity.open(id)` to open an activity sidebar
60
+ - Use `hailer.ui.activity.create(workflowId)` to open the create form
61
+ </step-3>
62
+
63
+ <step-4>
64
+ ## Step 4 — Create and Publish (First Time)
65
+
66
+ ```bash
67
+ npm run publish-production -- --create --app-name "<App Name>" --workspace <workspaceId> --force
68
+ ```
69
+
70
+ | Flag | Purpose |
71
+ |------|---------|
72
+ | `--create` | Creates a brand new app entry in Hailer |
73
+ | `--app-name` | Name shown in Hailer |
74
+ | `--workspace` | Workspace ID from `config.json` at project root |
75
+ | `--force` | Skips confirmation prompt |
76
+
77
+ **No `--user-api-key` needed** — the publish script automatically picks up credentials from the `~/.env` file injected by Hailer Studio. Credential resolution order:
78
+ 1. `--user-api-key` or `--email` flags
79
+ 2. `USER_API_KEY` or `HAILER_USER_API_KEY` environment variables
80
+ 3. `HAILER_USER_API_KEY` from `~/.env` ← this is what Hailer Studio provides automatically
81
+
82
+ After running, `public/manifest.json` is auto-updated with the new `appId`.
83
+ </step-4>
84
+
85
+ <step-5>
86
+ ## Step 5 — Subsequent Publishes
87
+
88
+ ```bash
89
+ npm run publish-production -- --force
90
+ ```
91
+
92
+ The `appId` in `manifest.json` determines which app gets updated. No API key needed — `~/.env` is picked up automatically.
93
+ </step-5>
94
+
95
+ <dev-vs-prod>
96
+ ## Dev vs Production Strategy
97
+
98
+ Maintain two separate published apps:
99
+
100
+ | App | Purpose |
101
+ |-----|---------|
102
+ | My App Dev | Publish here during iteration |
103
+ | My App | Publish here only when ready for users |
104
+
105
+ Switch between them by changing `appId` in `public/manifest.json` before publishing.
106
+ </dev-vs-prod>
107
+
108
+ <environments>
109
+ ## Environments
110
+
111
+ | Command | Target |
112
+ |---------|--------|
113
+ | `npm run publish-production` | Production (https://api.hailer.com) |
114
+ | `npm run publish-development` | Development (https://testapi.hailer.biz) |
115
+ | `npm run publish-staging` | Staging (https://api.hailer.biz) |
116
+ </environments>
117
+
118
+ <checklist>
119
+ ## Checklist Before Publishing
120
+
121
+ - [ ] Read `apps/<project-name>/README.md` — it always has the latest commands for that version
122
+ - [ ] Verify `public/manifest.json` has the correct `appId` for the target app
123
+ - [ ] `version` and `versionDescription` in manifest are only required for marketplace publishes
124
+ - [ ] Build runs automatically as part of the publish script — no separate build step needed
125
+ - [ ] Workspace ID is in `config.json` at the project root
126
+ - [ ] No API key needed — `~/.env` is picked up automatically by the publish script
127
+ </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>
@@ -148,11 +159,41 @@ publish_hailer_app({
148
159
  ```
149
160
 
150
161
  New version replaces old at same URL.
162
+
163
+ **Marketplace apps:** If `manifest.json` contains a `targetId`, the publish automatically updates the marketplace listing version too. No separate `publish_app` call needed for version updates.
151
164
  </updating>
152
165
 
166
+ <marketplace>
167
+ ## Marketplace Publishing
168
+
169
+ **Two different things:**
170
+ 1. **App publish** (`publish_hailer_app`) — uploads files to CDN, updates app URL. If manifest has `targetId`, also updates the marketplace version.
171
+ 2. **Marketplace metadata** (`publish_app`) — updates title, description, icon, images on the marketplace listing. Does NOT upload app files.
172
+
173
+ **First-time marketplace publish:**
174
+ 1. Publish app with `publish_hailer_app` — server returns a `targetId`, auto-saved to manifest
175
+ 2. Create marketplace listing with `publish_app` using `versionId` (= targetId from manifest)
176
+ 3. Note the `productId` returned — that's the marketplace listing ID (different from targetId)
177
+
178
+ **Updating marketplace version:**
179
+ Just run `publish_hailer_app` — if manifest has `targetId`, the marketplace version updates automatically.
180
+
181
+ **Updating marketplace metadata (description, icon, images):**
182
+ Use `publish_app` with `productId` (the marketplace listing ID, NOT the targetId).
183
+
184
+ **Key IDs:**
185
+ | ID | What | Where |
186
+ |----|------|-------|
187
+ | `appId` | The app itself | manifest.json `appId` |
188
+ | `targetId` | Version entry for marketplace | manifest.json `targetId` |
189
+ | `productId` | Marketplace listing | Returned by first `publish_app` call, find via `list_templates` |
190
+ </marketplace>
191
+
153
192
  <sharing>
154
193
  ## Sharing Apps
155
194
 
195
+ Workspace sharing is automatic on first publish. For manual sharing:
196
+
156
197
  ```javascript
157
198
  // Entire workspace
158
199
  add_app_member({ appId: "...", member: "network_{workspaceId}" })
@@ -165,6 +206,22 @@ add_app_member({ appId: "...", member: "user_{userId}" })
165
206
  ```
166
207
  </sharing>
167
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
+
168
225
  <vite-config>
169
226
  ## CRITICAL: Vite Base Path
170
227
 
@@ -213,9 +270,11 @@ Hailer apps are served from `apps.hailer.com` static hosting - we can't control
213
270
 
214
271
  - [ ] manifest.json `appId` is 24 chars (dev app ID is fine — publish auto-creates prod app)
215
272
  - [ ] manifest.json `version` is set (e.g., "1.0.0")
216
- - [ ] manifest.json `versionDescription` is set (not empty)
273
+ - [ ] manifest.json `versionDescription` is set (not empty — also used as app description)
217
274
  - [ ] vite.config.ts has `base: './'`
218
275
  - [ ] index.html has no-cache meta tags (see cache-busting section)
219
276
  - [ ] node_modules exists (dependencies installed)
220
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.
221
280
  </checklist>