@hailer/mcp 0.0.6 → 0.1.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 (122) hide show
  1. package/.claude/agents/ada.md +127 -0
  2. package/.claude/agents/agent-builder.md +151 -0
  3. package/.claude/agents/alejandro.md +66 -0
  4. package/.claude/agents/bjorn.md +305 -0
  5. package/.claude/agents/dmitri.md +61 -0
  6. package/.claude/agents/giuseppe.md +66 -0
  7. package/.claude/agents/gunther.md +355 -0
  8. package/.claude/agents/helga.md +68 -0
  9. package/.claude/agents/ingrid.md +108 -0
  10. package/.claude/agents/kenji.md +58 -0
  11. package/.claude/agents/svetlana.md +394 -0
  12. package/.claude/agents/viktor.md +63 -0
  13. package/.claude/agents/yevgeni.md +60 -0
  14. package/.claude/hooks/agent-failure-detector.cjs +286 -0
  15. package/.claude/hooks/app-edit-guard.cjs +462 -0
  16. package/.claude/hooks/interactive-mode.cjs +59 -0
  17. package/.claude/hooks/mcp-server-guard.cjs +92 -0
  18. package/.claude/hooks/post-scaffold-hook.cjs +31 -0
  19. package/.claude/hooks/sdk-delete-guard.cjs +2 -0
  20. package/.claude/hooks/src-edit-guard.cjs +208 -0
  21. package/.claude/settings.json +47 -2
  22. package/.claude/skills/insight-join-patterns/SKILL.md +209 -0
  23. package/.env.example +13 -1
  24. package/CLAUDE.md +135 -0
  25. package/dist/app.js +4 -3
  26. package/dist/cli.js +0 -0
  27. package/dist/client/adaptive-documentation-bot.d.ts +0 -2
  28. package/dist/client/adaptive-documentation-bot.js +5 -16
  29. package/dist/client/message-processor.js +5 -0
  30. package/dist/client/providers/anthropic-provider.js +21 -7
  31. package/dist/mcp/UserContextCache.d.ts +14 -0
  32. package/dist/mcp/UserContextCache.js +49 -24
  33. package/dist/mcp/auth.d.ts +7 -0
  34. package/dist/mcp/auth.js +13 -5
  35. package/dist/mcp/hailer-clients.d.ts +5 -2
  36. package/dist/mcp/signal-handler.d.ts +28 -2
  37. package/dist/mcp/signal-handler.js +4 -2
  38. package/dist/mcp/tool-registry.d.ts +55 -2
  39. package/dist/mcp/tool-registry.js +197 -2
  40. package/dist/mcp/tools/app-core.d.ts +15 -0
  41. package/dist/mcp/tools/app-core.js +609 -0
  42. package/dist/mcp/tools/app-marketplace.d.ts +21 -0
  43. package/dist/mcp/tools/app-marketplace.js +1284 -0
  44. package/dist/mcp/tools/app-member.d.ts +11 -0
  45. package/dist/mcp/tools/app-member.js +258 -0
  46. package/dist/mcp/tools/app-scaffold.d.ts +11 -0
  47. package/dist/mcp/tools/app-scaffold.js +743 -0
  48. package/dist/mcp/tools/app.d.ts +13 -22
  49. package/dist/mcp/tools/app.js +17 -2466
  50. package/dist/mcp/tools/file.js +6 -6
  51. package/dist/mcp/tools/insight.d.ts +1 -0
  52. package/dist/mcp/tools/insight.js +203 -64
  53. package/dist/mcp/tools/user.js +3 -9
  54. package/dist/mcp/tools/workflow.js +49 -38
  55. package/dist/mcp/utils/hailer-api-client.js +4 -13
  56. package/dist/mcp/utils/tool-helpers.d.ts +102 -0
  57. package/dist/mcp/utils/tool-helpers.js +179 -0
  58. package/dist/mcp/utils/types.d.ts +6 -0
  59. package/dist/mcp/workspace-cache.d.ts +5 -5
  60. package/dist/mcp/workspace-cache.js +4 -3
  61. package/package.json +1 -1
  62. package/.claude/hooks/PreToolUse.sh +0 -52
  63. package/.claude/hooks/prompt-skill-loader.cjs +0 -553
  64. package/.claude/hooks/skill-loader.cjs +0 -142
  65. package/.claude/settings.local.json +0 -49
  66. package/.claude/skills/MCP-add-app-member-skill/SKILL.md +0 -977
  67. package/.claude/skills/MCP-build-data-app-skill/SKILL.md +0 -372
  68. package/.claude/skills/MCP-create-app-skill/SKILL.md +0 -1101
  69. package/.claude/skills/MCP-create-insight-skill/SKILL.md +0 -1317
  70. package/.claude/skills/MCP-get-insight-data-skill/SKILL.md +0 -1053
  71. package/.claude/skills/MCP-insight-api/SKILL.md +0 -185
  72. package/.claude/skills/MCP-insight-api/references/insight-endpoints.md +0 -514
  73. package/.claude/skills/MCP-install-workflow-skill/SKILL.md +0 -1056
  74. package/.claude/skills/MCP-list-apps-skill/SKILL.md +0 -1010
  75. package/.claude/skills/MCP-list-workflows-minimal-skill/SKILL.md +0 -992
  76. package/.claude/skills/MCP-local-first-skill/SKILL.md +0 -570
  77. package/.claude/skills/MCP-populate-workflow-data-skill/SKILL.md +0 -395
  78. package/.claude/skills/MCP-preview-insight-skill/SKILL.md +0 -1290
  79. package/.claude/skills/MCP-publish-hailer-app-skill/SKILL.md +0 -453
  80. package/.claude/skills/MCP-publish-template-skill/SKILL.md +0 -278
  81. package/.claude/skills/MCP-remove-app-member-skill/SKILL.md +0 -671
  82. package/.claude/skills/MCP-remove-app-skill/SKILL.md +0 -985
  83. package/.claude/skills/MCP-remove-insight-skill/SKILL.md +0 -1011
  84. package/.claude/skills/MCP-remove-workflow-skill/SKILL.md +0 -920
  85. package/.claude/skills/MCP-scaffold-hailer-app-skill/SKILL.md +0 -1314
  86. package/.claude/skills/MCP-update-app-skill/SKILL.md +0 -970
  87. package/.claude/skills/MCP-update-workflow-field-skill/SKILL.md +0 -1098
  88. package/.claude/skills/SDK-create-function-field-skill/SKILL.md +0 -313
  89. package/.claude/skills/SDK-generate-skill/SKILL.md +0 -223
  90. package/.claude/skills/SDK-init-skill/SKILL.md +0 -177
  91. package/.claude/skills/SDK-workspace-setup-skill/SKILL.md +0 -605
  92. package/.claude/skills/SDK-ws-config-skill/SKILL.md +0 -435
  93. package/.claude/skills/activity-api/SKILL.md +0 -96
  94. package/.claude/skills/activity-api/references/activity-endpoints.md +0 -845
  95. package/.claude/skills/agent-building/SKILL.md +0 -243
  96. package/.claude/skills/agent-building/references/architecture-patterns.md +0 -446
  97. package/.claude/skills/agent-building/references/code-examples.md +0 -587
  98. package/.claude/skills/agent-building/references/implementation-guide.md +0 -619
  99. package/.claude/skills/app-api/SKILL.md +0 -219
  100. package/.claude/skills/app-api/references/app-endpoints.md +0 -759
  101. package/.claude/skills/building-hailer-apps-skill/SKILL.md +0 -813
  102. package/.claude/skills/hailer-api/SKILL.md +0 -283
  103. package/.claude/skills/hailer-api/references/activities.md +0 -620
  104. package/.claude/skills/hailer-api/references/authentication.md +0 -216
  105. package/.claude/skills/hailer-api/references/datasets.md +0 -437
  106. package/.claude/skills/hailer-api/references/files.md +0 -301
  107. package/.claude/skills/hailer-api/references/insights.md +0 -469
  108. package/.claude/skills/hailer-api/references/workflows.md +0 -720
  109. package/.claude/skills/hailer-api/references/workspaces-users.md +0 -445
  110. package/.claude/skills/hailer-app-builder/SKILL.md +0 -340
  111. package/.claude/skills/mcp-tools/SKILL.md +0 -419
  112. package/.claude/skills/mcp-tools/references/api-endpoints.md +0 -499
  113. package/.claude/skills/mcp-tools/references/data-structures.md +0 -554
  114. package/.claude/skills/mcp-tools/references/implementation-patterns.md +0 -717
  115. package/.claude/skills/skill-testing/README.md +0 -137
  116. package/.claude/skills/skill-testing/SKILL.md +0 -348
  117. package/.claude/skills/skill-testing/references/test-patterns.md +0 -705
  118. package/.claude/skills/skill-testing/references/testing-guide.md +0 -603
  119. package/.claude/skills/skill-testing/references/validation-checklist.md +0 -537
  120. package/.claude/skills/spawn-app-builder/SKILL.md +0 -366
  121. package/.claude/skills/tool-builder/SKILL.md +0 -328
  122. package/tsconfig.json +0 -23
@@ -1,553 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Claude Code UserPromptSubmit Hook - Auto-loads skills based on prompt keywords
4
- *
5
- * This hook detects keywords in user prompts and triggers disambiguation
6
- * questions to clarify user intent before loading skills.
7
- *
8
- * Features:
9
- * - Keyword-based skill loading
10
- * - Disambiguation prompts for ALL keywords (no assumptions)
11
- * - Marker files to avoid loading same skill twice per session
12
- */
13
-
14
- const fs = require('fs');
15
- const path = require('path');
16
- const os = require('os');
17
-
18
- // Read hook input from stdin
19
- let input = '';
20
- process.stdin.setEncoding('utf8');
21
- process.stdin.on('data', chunk => input += chunk);
22
- process.stdin.on('end', () => {
23
- try {
24
- const data = JSON.parse(input);
25
- processHook(data);
26
- } catch {
27
- // Invalid JSON, exit silently
28
- process.exit(0);
29
- }
30
- });
31
-
32
- // Mapping from followUp types to skill file names
33
- const FOLLOW_UP_SKILLS = {
34
- 'build-app-flow': 'MCP-build-data-app-skill',
35
- 'spawn-builder-flow': 'spawn-app-builder',
36
- 'publish-template-flow': 'MCP-publish-template-skill'
37
- };
38
-
39
- // ALL keywords are now ambiguous - always ask for clarification
40
- const AMBIGUOUS_KEYWORDS = [
41
- // Pull operations
42
- {
43
- keyword: /\bpull\b/i,
44
- contextPatterns: [
45
- /\b(config|workspace|fields?|phases?|groups?|teams?|ws-config)\b/i,
46
- /\bnpm run pull\b/i
47
- ],
48
- skill: 'SDK-ws-config-skill',
49
- disambiguation: {
50
- keyword: 'pull',
51
- question: 'What would you like to pull?',
52
- options: [
53
- { label: 'Workspace configuration', description: 'Pull workflow configs, fields, phases (npm run pull)', skill: 'SDK-ws-config-skill' },
54
- { label: 'Activity data', description: 'Fetch activities from a workflow (MCP tools)', skill: null }
55
- ]
56
- }
57
- },
58
- // Push operations
59
- {
60
- keyword: /\bpush\b/i,
61
- contextPatterns: [
62
- /\b(config|workspace|fields?|phases?|groups?|teams?|ws-config)\b/i,
63
- /\bnpm run push\b/i
64
- ],
65
- skill: 'SDK-ws-config-skill',
66
- disambiguation: {
67
- keyword: 'push',
68
- question: 'What would you like to push?',
69
- options: [
70
- { label: 'Workspace configuration', description: 'Push workflow configs, fields, phases (npm run push)', skill: 'SDK-ws-config-skill' },
71
- { label: 'Activity updates', description: 'Update activities in a workflow (MCP tools)', skill: null }
72
- ]
73
- }
74
- },
75
- // Generate operations
76
- {
77
- keyword: /\bgenerate\b/i,
78
- contextPatterns: [
79
- /\b(types?|enums?|typescript|ts)\b/i,
80
- /\bnpm run generate\b/i
81
- ],
82
- skill: 'SDK-generate-skill',
83
- disambiguation: {
84
- keyword: 'generate',
85
- question: 'What would you like to generate?',
86
- options: [
87
- { label: 'TypeScript types/enums', description: 'Generate types from Hailer workspace (npm run generate)', skill: 'SDK-generate-skill' },
88
- { label: 'Sample data', description: 'Create test activities in workflows', skill: 'MCP-populate-workflow-data-skill' },
89
- { label: 'Reports/insights', description: 'Create SQL-like insights', skill: 'MCP-create-insight-skill' }
90
- ]
91
- }
92
- },
93
- // Setup operations
94
- {
95
- keyword: /\bsetup\b/i,
96
- contextPatterns: [
97
- /\b(project|init|mcp|bot.?account)\b/i,
98
- /\bhailer-sdk init\b/i
99
- ],
100
- skill: 'SDK-init-skill',
101
- disambiguation: {
102
- keyword: 'setup',
103
- question: 'What would you like to set up?',
104
- options: [
105
- { label: 'New Hailer project', description: 'Initialize project with hailer-sdk init', skill: 'SDK-init-skill' },
106
- { label: 'Workspace structure', description: 'Create workflows, fields, phases', skill: 'SDK-workspace-setup-skill' },
107
- { label: 'Hailer app', description: 'Scaffold a new Hailer application', skill: 'MCP-scaffold-hailer-app-skill' }
108
- ]
109
- }
110
- },
111
- // Initialize operations
112
- {
113
- keyword: /\b(init|initialize)\b/i,
114
- contextPatterns: [
115
- /\bhailer-sdk init\b/i,
116
- /\bnew project\b/i
117
- ],
118
- skill: 'SDK-init-skill',
119
- disambiguation: {
120
- keyword: 'init',
121
- question: 'What would you like to initialize?',
122
- options: [
123
- { label: 'New Hailer project', description: 'Initialize project with hailer-sdk init', skill: 'SDK-init-skill' },
124
- { label: 'Bot account', description: 'Set up a bot account for automation', skill: 'SDK-init-skill' },
125
- { label: 'MCP server', description: 'Configure MCP server integration', skill: 'SDK-init-skill' }
126
- ]
127
- }
128
- },
129
- // Workflow operations
130
- {
131
- keyword: /\bworkflow\b/i,
132
- contextPatterns: [
133
- /\b(install|create|new).?workflow\b/i,
134
- /\bworkflow.?(install|create|new)\b/i
135
- ],
136
- skill: 'MCP-install-workflow-skill',
137
- disambiguation: {
138
- keyword: 'workflow',
139
- question: 'What would you like to do with workflows?',
140
- options: [
141
- { label: 'Create new workflow', description: 'Install a new workflow structure', skill: 'MCP-install-workflow-skill' },
142
- { label: 'List workflows', description: 'View existing workflows in workspace', skill: null },
143
- { label: 'Update workflow structure', description: 'Modify fields, phases via SDK', skill: 'SDK-ws-config-skill' },
144
- { label: 'Remove workflow', description: 'Delete a workflow from workspace', skill: 'MCP-remove-workflow-skill' }
145
- ]
146
- }
147
- },
148
- // Insight/Report operations
149
- {
150
- keyword: /\b(insight|report|analytics)\b/i,
151
- contextPatterns: [
152
- /\b(create|new|build).?(insight|report)\b/i,
153
- /\b(insight|report).?(create|new|build)\b/i
154
- ],
155
- skill: 'MCP-create-insight-skill',
156
- disambiguation: {
157
- keyword: 'insight',
158
- question: 'What would you like to do with insights?',
159
- options: [
160
- { label: 'Create new insight', description: 'Build a new SQL-like report', skill: 'MCP-create-insight-skill' },
161
- { label: 'List insights', description: 'View existing insights in workspace', skill: null },
162
- { label: 'Get insight data', description: 'Execute an insight and retrieve results', skill: 'MCP-get-insight-data-skill' },
163
- { label: 'Preview/test SQL', description: 'Test a query before saving', skill: 'MCP-preview-insight-skill' }
164
- ]
165
- }
166
- },
167
- // SQL/Query operations (alias for insight)
168
- {
169
- keyword: /\b(sql|query)\b/i,
170
- contextPatterns: [
171
- /\b(run|execute|test|preview).?(sql|query)\b/i
172
- ],
173
- skill: 'MCP-preview-insight-skill',
174
- disambiguation: {
175
- keyword: 'sql',
176
- question: 'What would you like to do with SQL queries?',
177
- options: [
178
- { label: 'Create insight with SQL', description: 'Build a saved SQL report', skill: 'MCP-create-insight-skill' },
179
- { label: 'Preview/test SQL', description: 'Test a query before saving', skill: 'MCP-preview-insight-skill' },
180
- { label: 'Run existing insight', description: 'Execute a saved insight', skill: 'MCP-get-insight-data-skill' }
181
- ]
182
- }
183
- },
184
- // App operations
185
- {
186
- keyword: /\b(app|application)\b/i,
187
- contextPatterns: [
188
- /\b(scaffold|create|new|build).?app\b/i,
189
- /\bhailer.?app\b/i
190
- ],
191
- skill: 'MCP-scaffold-hailer-app-skill',
192
- disambiguation: {
193
- keyword: 'app',
194
- question: 'What would you like to do with Hailer apps?',
195
- options: [
196
- { label: 'Scaffold new app', description: 'Create a new app project from template', skill: 'MCP-scaffold-hailer-app-skill' },
197
- { label: 'Build data app', description: 'Build app to visualize/manage workspace data', skill: 'MCP-scaffold-hailer-app-skill', followUp: 'build-app-flow' },
198
- { label: 'Create app entry', description: 'Register an app in Hailer (no scaffold)', skill: 'MCP-create-app-skill' },
199
- { label: 'List apps', description: 'View existing apps in workspace', skill: 'MCP-list-apps-skill' },
200
- { label: 'Update app', description: 'Modify app properties', skill: 'MCP-update-app-skill' }
201
- ]
202
- }
203
- },
204
- // Build data app / visualize data operations
205
- {
206
- keyword: /\b(visualize|dashboard|data.?manager|manage.?data|data.?app)\b/i,
207
- contextPatterns: [
208
- /\b(build|create|make).*(visualize|dashboard|data)\b/i
209
- ],
210
- skill: 'MCP-scaffold-hailer-app-skill',
211
- disambiguation: {
212
- keyword: 'data app',
213
- question: 'What kind of data app would you like to build?',
214
- options: [
215
- { label: 'Full data manager', description: 'Browse, edit, create across all workflows', skill: 'MCP-scaffold-hailer-app-skill', followUp: 'build-app-flow' },
216
- { label: 'Dashboard overview', description: 'Cards/stats for workflows at a glance', skill: 'MCP-scaffold-hailer-app-skill', followUp: 'build-app-flow' },
217
- { label: 'Specific workflow app', description: 'Focus on one or few workflows', skill: 'MCP-scaffold-hailer-app-skill', followUp: 'build-app-flow' }
218
- ]
219
- }
220
- },
221
- // Write app code / Code the app operations
222
- {
223
- keyword: /\b(code|write|implement|develop).{0,20}(app|component|feature)\b/i,
224
- contextPatterns: [
225
- /\b(start|begin).{0,10}(code|coding|writing|implementing)\b/i,
226
- /\bwrite.{0,10}(code|component|feature)\b/i,
227
- /\bcode.{0,10}(app|component)\b/i
228
- ],
229
- skill: 'spawn-app-builder',
230
- disambiguation: {
231
- keyword: 'code app',
232
- question: 'What would you like to do?',
233
- options: [
234
- { label: 'Spawn app builder agent', description: 'Launch isolated agent with TypeScript + SDK skills embedded', skill: 'spawn-app-builder', followUp: 'spawn-builder-flow' },
235
- { label: 'Load skills in current session', description: 'Load skills here instead of spawning new agent', skill: 'hailer-app-builder', loadMultiple: ['building-hailer-apps-skill', 'hailer-app-builder'] },
236
- { label: 'Scaffold new app first', description: 'Create project structure then write code', skill: 'MCP-scaffold-hailer-app-skill' }
237
- ]
238
- }
239
- },
240
- // Continue app development / dev on app / work on app / build more / add features
241
- {
242
- keyword: /\b(dev|work|continue|build|add|improve|enhance|extend|modify|change|update|fix).{0,15}(on|the|to|for)?.{0,10}(app|component|dashboard|feature|ui|code)\b/i,
243
- contextPatterns: [], // Always require disambiguation for safety
244
- skill: 'spawn-app-builder',
245
- disambiguation: {
246
- keyword: 'dev app',
247
- question: 'How should I proceed with app development?',
248
- options: [
249
- { label: 'Spawn builder agent', description: 'RECOMMENDED: Isolated agent with TypeScript + SDK skills', skill: 'spawn-app-builder', followUp: 'spawn-builder-flow' },
250
- { label: 'Load skills here', description: 'Continue in this conversation with skills loaded', skill: 'hailer-app-builder', loadMultiple: ['building-hailer-apps-skill', 'hailer-app-builder'] }
251
- ]
252
- }
253
- },
254
- // Publish/Deploy operations
255
- {
256
- keyword: /\b(publish|deploy)\b/i,
257
- contextPatterns: [
258
- /\b(publish|deploy).?app\b/i
259
- ],
260
- skill: 'MCP-publish-hailer-app-skill',
261
- disambiguation: {
262
- keyword: 'publish',
263
- question: 'What would you like to publish or deploy?',
264
- options: [
265
- { label: 'Publish Hailer app', description: 'Build and upload app to production', skill: 'MCP-publish-hailer-app-skill' },
266
- { label: 'Publish workspace template', description: 'Share workspace config in marketplace', skill: 'MCP-publish-template-skill' },
267
- { label: 'Share app with users', description: 'Add members to access an app', skill: 'MCP-add-app-member-skill' }
268
- ]
269
- }
270
- },
271
- // Template/Marketplace operations
272
- {
273
- keyword: /\b(template|marketplace)\b/i,
274
- contextPatterns: [
275
- /\b(publish|create|install|list).?template\b/i,
276
- /\btemplate.?(publish|create|install|list)\b/i
277
- ],
278
- skill: 'MCP-publish-template-skill',
279
- disambiguation: {
280
- keyword: 'template',
281
- question: 'What would you like to do with templates?',
282
- options: [
283
- { label: 'Publish template', description: 'Share workspace config in marketplace', skill: 'MCP-publish-template-skill', followUp: 'publish-template-flow' },
284
- { label: 'Install template', description: 'Add template to workspace', skill: null },
285
- { label: 'List templates', description: 'View available templates', skill: null },
286
- { label: 'Get template details', description: 'View template info', skill: null }
287
- ]
288
- }
289
- },
290
- // Function field / Calculated field operations
291
- {
292
- keyword: /\b(function.?field|calculated.?field|formula)\b/i,
293
- contextPatterns: [
294
- /\b(create|add|new).?(function|calculated).?field\b/i
295
- ],
296
- skill: 'SDK-create-function-field-skill',
297
- disambiguation: {
298
- keyword: 'function field',
299
- question: 'What would you like to do with function fields?',
300
- options: [
301
- { label: 'Create function field', description: 'Add a new calculated field to workflow', skill: 'SDK-create-function-field-skill' },
302
- { label: 'Update function field', description: 'Modify existing function field formula', skill: 'MCP-update-workflow-field-skill' },
303
- { label: 'Test function', description: 'Test function code against real data', skill: null }
304
- ]
305
- }
306
- },
307
- // Sample data / Populate operations
308
- {
309
- keyword: /\b(sample.?data|test.?data|populate|bulk.?create)\b/i,
310
- contextPatterns: [
311
- /\b(create|add|generate).?(sample|test).?data\b/i
312
- ],
313
- skill: 'MCP-populate-workflow-data-skill',
314
- disambiguation: {
315
- keyword: 'data',
316
- question: 'What would you like to do with data?',
317
- options: [
318
- { label: 'Populate with sample data', description: 'Generate realistic test activities', skill: 'MCP-populate-workflow-data-skill' },
319
- { label: 'Bulk create activities', description: 'Create multiple activities at once', skill: null },
320
- { label: 'Import data', description: 'Import from external source', skill: null }
321
- ]
322
- }
323
- },
324
- // Field operations
325
- {
326
- keyword: /\b(add|modify|update|delete|remove|create).{0,20}field\b/i,
327
- contextPatterns: [
328
- /\bnpm run (fields|push)\b/i,
329
- /\bworkspace.?config\b/i
330
- ],
331
- skill: 'SDK-ws-config-skill',
332
- disambiguation: {
333
- keyword: 'field',
334
- question: 'How would you like to modify fields?',
335
- options: [
336
- { label: 'Via SDK (local files)', description: 'Edit workspace/ files and push (version controlled)', skill: 'SDK-ws-config-skill' },
337
- { label: 'Via MCP (direct API)', description: 'Update field directly via API', skill: 'MCP-update-workflow-field-skill' }
338
- ]
339
- }
340
- },
341
- // Phase operations
342
- {
343
- keyword: /\b(add|modify|update|delete|remove|create).{0,20}phase\b/i,
344
- contextPatterns: [
345
- /\bnpm run (phases|push)\b/i,
346
- /\bworkspace.?config\b/i
347
- ],
348
- skill: 'SDK-ws-config-skill',
349
- disambiguation: {
350
- keyword: 'phase',
351
- question: 'How would you like to modify phases?',
352
- options: [
353
- { label: 'Via SDK (local files)', description: 'Edit workspace/ files and push (version controlled)', skill: 'SDK-ws-config-skill' },
354
- { label: 'Via MCP (direct API)', description: 'Update phase directly via API', skill: null }
355
- ]
356
- }
357
- },
358
- // Activity operations
359
- {
360
- keyword: /\b(activities|activity)\b/i,
361
- contextPatterns: [
362
- /\b(list|show|get|fetch|create|update|delete).?(activities|activity)\b/i
363
- ],
364
- skill: null,
365
- disambiguation: {
366
- keyword: 'activity',
367
- question: 'What would you like to do with activities?',
368
- options: [
369
- { label: 'List activities', description: 'View activities in a workflow', skill: null },
370
- { label: 'Create activity', description: 'Add a new activity to workflow', skill: null },
371
- { label: 'Update activity', description: 'Modify an existing activity', skill: null },
372
- { label: 'Bulk create activities', description: 'Create multiple activities at once', skill: 'MCP-populate-workflow-data-skill' }
373
- ]
374
- }
375
- },
376
- // Sync operations (DESTRUCTIVE - require confirmation)
377
- {
378
- keyword: /\b(sync|workflows?.?sync)\b/i,
379
- contextPatterns: [], // Always require confirmation, never auto-proceed
380
- skill: 'SDK-ws-config-skill',
381
- disambiguation: {
382
- keyword: 'sync',
383
- question: '⚠️ Sync operations can DELETE resources from Hailer. Items removed from local config will be PERMANENTLY deleted remotely. Proceed?',
384
- options: [
385
- { label: 'Yes, run sync', description: 'I understand - proceed with destructive sync operation', skill: 'SDK-ws-config-skill', action: 'confirm-sync' },
386
- { label: 'No, cancel', description: 'Abort - don\'t delete anything', skill: null, action: 'cancel' }
387
- ]
388
- }
389
- }
390
- ];
391
-
392
- // Note: DIRECT_KEYWORDS removed - all keywords now require disambiguation
393
- // Flow instructions are now loaded from skill files instead of inline functions
394
-
395
- function processHook(data) {
396
- const { prompt, cwd } = data;
397
-
398
- if (!prompt) {
399
- process.exit(0);
400
- }
401
-
402
- // Marker directory for tracking loaded skills this session
403
- const markerDir = path.join(os.tmpdir(), '.claude-skills-loaded');
404
- if (!fs.existsSync(markerDir)) {
405
- fs.mkdirSync(markerDir, { recursive: true });
406
- }
407
-
408
- // Find project directory
409
- const projectDir = process.env.CLAUDE_PROJECT_DIR || cwd || process.cwd();
410
-
411
- // Track what we'll output
412
- const skillsToLoad = [];
413
- const disambiguationsNeeded = [];
414
- const seenDisambiguations = new Set(); // Avoid duplicate questions
415
- const loadedFollowUpSkills = new Set(); // Avoid duplicate follow-up skill content
416
-
417
- // Check all ambiguous keywords
418
- for (const entry of AMBIGUOUS_KEYWORDS) {
419
- if (entry.keyword.test(prompt)) {
420
- // Check if context makes it clear
421
- const hasContext = entry.contextPatterns && entry.contextPatterns.some(p => p.test(prompt));
422
-
423
- if (hasContext && entry.skill) {
424
- // Context is clear - load the skill
425
- const markerFile = path.join(markerDir, entry.skill);
426
- if (!fs.existsSync(markerFile)) {
427
- const skillPath = path.join(projectDir, '.claude', 'skills', entry.skill, 'SKILL.md');
428
- if (fs.existsSync(skillPath)) {
429
- skillsToLoad.push({
430
- skill: entry.skill,
431
- description: entry.disambiguation.question,
432
- path: skillPath,
433
- markerFile
434
- });
435
- }
436
- }
437
- } else {
438
- // Context unclear - need disambiguation (avoid duplicates)
439
- const disambKey = entry.disambiguation.keyword;
440
- if (!seenDisambiguations.has(disambKey)) {
441
- seenDisambiguations.add(disambKey);
442
- disambiguationsNeeded.push(entry.disambiguation);
443
- }
444
- }
445
- }
446
- }
447
-
448
- // Nothing to output
449
- if (skillsToLoad.length === 0 && disambiguationsNeeded.length === 0) {
450
- process.exit(0);
451
- }
452
-
453
- // Build output
454
- let output = '\n';
455
-
456
- // Output disambiguation prompts first
457
- if (disambiguationsNeeded.length > 0) {
458
- output += '='.repeat(60) + '\n';
459
- output += '⚠️ MANDATORY ACTION REQUIRED - DO NOT SKIP\n';
460
- output += '='.repeat(60) + '\n\n';
461
- output += 'STOP! You MUST use the AskUserQuestion tool BEFORE doing anything else.\n';
462
- output += 'The user\'s intent is ambiguous. Do NOT guess or assume.\n\n';
463
-
464
- for (const disamb of disambiguationsNeeded) {
465
- output += '-'.repeat(40) + '\n';
466
- output += `Ambiguous keyword detected: "${disamb.keyword}"\n\n`;
467
- output += 'USE THIS EXACT AskUserQuestion CALL:\n\n';
468
- output += '```json\n';
469
- output += JSON.stringify({
470
- questions: [{
471
- question: disamb.question,
472
- header: disamb.keyword.charAt(0).toUpperCase() + disamb.keyword.slice(1),
473
- options: disamb.options.map(opt => ({
474
- label: opt.label,
475
- description: opt.description
476
- })),
477
- multiSelect: false
478
- }]
479
- }, null, 2);
480
- output += '\n```\n\n';
481
-
482
- output += 'After user responds:\n';
483
- for (const opt of disamb.options) {
484
- if (opt.action === 'confirm-sync') {
485
- output += ` - If "${opt.label}": Load Skill(${opt.skill}), then run with: yes | npm run workflows-sync\n`;
486
- } else if (opt.action === 'cancel') {
487
- output += ` - If "${opt.label}": Acknowledge cancellation, do NOT run any sync command\n`;
488
- } else if (opt.followUp === 'publish-template-flow') {
489
- output += ` - If "${opt.label}": Load Skill(${opt.skill}), then use EXACTLY this AskUserQuestion (see below)\n`;
490
- } else if (opt.loadMultiple && Array.isArray(opt.loadMultiple)) {
491
- output += ` - If "${opt.label}": Load BOTH skills in order: Skill(${opt.loadMultiple.join('), Skill(')}), then proceed\n`;
492
- } else if (opt.skill) {
493
- output += ` - If "${opt.label}": Load Skill(${opt.skill}), then proceed\n`;
494
- } else {
495
- output += ` - If "${opt.label}": Use MCP tools directly (no skill needed)\n`;
496
- }
497
- }
498
- output += '\n';
499
-
500
- // Add follow-up skill content (deduplicated - each skill only included once)
501
- for (const opt of disamb.options) {
502
- if (opt.followUp && !loadedFollowUpSkills.has(opt.followUp)) {
503
- const skillName = FOLLOW_UP_SKILLS[opt.followUp];
504
- if (skillName) {
505
- const skillPath = path.join(projectDir, '.claude', 'skills', skillName, 'SKILL.md');
506
- if (fs.existsSync(skillPath)) {
507
- loadedFollowUpSkills.add(opt.followUp);
508
- output += '\n' + '-'.repeat(40) + '\n';
509
- output += `📚 FOLLOW-UP SKILL: ${skillName}\n`;
510
- output += '-'.repeat(40) + '\n';
511
- output += fs.readFileSync(skillPath, 'utf8');
512
- output += '\n';
513
- }
514
- }
515
- }
516
- }
517
- }
518
-
519
- output += '='.repeat(60) + '\n';
520
- output += 'REMEMBER: Ask FIRST, then act. Never assume user intent.\n';
521
- output += '='.repeat(60) + '\n\n';
522
- }
523
-
524
- // Output skills that can be loaded directly (only when context is clear)
525
- if (skillsToLoad.length > 0) {
526
- output += '='.repeat(60) + '\n';
527
- output += 'AUTO-LOADED SKILLS (context was clear from prompt)\n';
528
- output += '='.repeat(60) + '\n\n';
529
-
530
- for (const skill of skillsToLoad) {
531
- const skillContent = fs.readFileSync(skill.path, 'utf8');
532
-
533
- output += '-'.repeat(60) + '\n';
534
- output += `SKILL: ${skill.skill}\n`;
535
- output += `Purpose: ${skill.description}\n`;
536
- output += '-'.repeat(60) + '\n\n';
537
- output += skillContent;
538
- output += '\n\n';
539
-
540
- // Mark skill as loaded
541
- fs.writeFileSync(skill.markerFile, new Date().toISOString());
542
- }
543
-
544
- output += '='.repeat(60) + '\n';
545
- output += 'END OF AUTO-LOADED SKILLS\n';
546
- output += '='.repeat(60) + '\n';
547
- }
548
-
549
- // Output to stderr - displays to user
550
- console.error(output);
551
-
552
- process.exit(0);
553
- }
@@ -1,142 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Claude Code PreToolUse Hook - Auto-loads skills before tools
4
- *
5
- * This hook intercepts tool calls and outputs relevant skill content
6
- * to stderr (which Claude sees) when matching tools are detected.
7
- *
8
- * Supports:
9
- * - SDK commands (Bash tool with npm run pull/push/etc.)
10
- * - MCP tools (mcp__hailer__install_workflow, etc.)
11
- *
12
- * Uses marker files to avoid loading the same skill twice in a session.
13
- */
14
-
15
- const fs = require('fs');
16
- const path = require('path');
17
- const os = require('os');
18
-
19
- // Read hook input from stdin
20
- let input = '';
21
- process.stdin.setEncoding('utf8');
22
- process.stdin.on('data', chunk => input += chunk);
23
- process.stdin.on('end', () => {
24
- try {
25
- const data = JSON.parse(input);
26
- processHook(data);
27
- } catch {
28
- // Invalid JSON, exit silently
29
- process.exit(0);
30
- }
31
- });
32
-
33
- // MCP tool to skill mapping
34
- const MCP_TOOL_SKILLS = {
35
- 'mcp__hailer__install_workflow': 'MCP-install-workflow-skill',
36
- 'mcp__hailer__create_insight': 'MCP-create-insight-skill',
37
- 'mcp__hailer__preview_insight': 'MCP-preview-insight-skill',
38
- 'mcp__hailer__get_insight_data': 'MCP-get-insight-data-skill',
39
- 'mcp__hailer__create_app': 'MCP-create-app-skill',
40
- 'mcp__hailer__update_app': 'MCP-update-app-skill',
41
- 'mcp__hailer__add_app_member': 'MCP-add-app-member-skill',
42
- 'mcp__hailer__remove_app_member': 'MCP-remove-app-member-skill',
43
- 'mcp__hailer__scaffold_hailer_app': 'MCP-scaffold-hailer-app-skill',
44
- 'mcp__hailer__publish_hailer_app': 'MCP-publish-hailer-app-skill',
45
- 'mcp__hailer__update_workflow_field': 'MCP-update-workflow-field-skill',
46
- 'mcp__hailer__remove_workflow': 'MCP-remove-workflow-skill',
47
- 'mcp__hailer__remove_insight': 'MCP-remove-insight-skill',
48
- 'mcp__hailer__remove_app': 'MCP-remove-app-skill',
49
- };
50
-
51
- // SDK command patterns to skill mapping
52
- const SDK_COMMAND_SKILLS = [
53
- {
54
- patterns: [
55
- /npm run pull/,
56
- /npm run push/,
57
- /npm run workflows/,
58
- /npm run phases/,
59
- /npm run fields/,
60
- /npm run groups/,
61
- /npm run teams/,
62
- /npm run insights/,
63
- /hailer-sdk ws-config/,
64
- /hailer-sdk workspace-config/
65
- ],
66
- skill: 'SDK-ws-config-skill'
67
- },
68
- {
69
- patterns: [/npm run generate/, /hailer-sdk generate/],
70
- skill: 'SDK-generate-skill'
71
- },
72
- {
73
- patterns: [/hailer-sdk init/],
74
- skill: 'SDK-init-skill'
75
- }
76
- ];
77
-
78
- function processHook(data) {
79
- const { tool_name, tool_input, cwd } = data;
80
-
81
- let matchedSkill = null;
82
-
83
- // Check for MCP tools first
84
- if (tool_name && tool_name.startsWith('mcp__hailer__')) {
85
- matchedSkill = MCP_TOOL_SKILLS[tool_name];
86
- }
87
- // Check for Bash commands (SDK)
88
- else if (tool_name === 'Bash') {
89
- const command = tool_input?.command || '';
90
- for (const mapping of SDK_COMMAND_SKILLS) {
91
- for (const pattern of mapping.patterns) {
92
- if (pattern.test(command)) {
93
- matchedSkill = mapping.skill;
94
- break;
95
- }
96
- }
97
- if (matchedSkill) break;
98
- }
99
- }
100
-
101
- if (!matchedSkill) {
102
- process.exit(0);
103
- }
104
-
105
- // Marker directory for tracking loaded skills this session
106
- const markerDir = path.join(os.tmpdir(), '.claude-skills-loaded');
107
- if (!fs.existsSync(markerDir)) {
108
- fs.mkdirSync(markerDir, { recursive: true });
109
- }
110
-
111
- // Check if skill was already loaded this session
112
- const markerFile = path.join(markerDir, matchedSkill);
113
- if (fs.existsSync(markerFile)) {
114
- process.exit(0);
115
- }
116
-
117
- // Find skill file - try project dir first, then cwd
118
- const projectDir = process.env.CLAUDE_PROJECT_DIR || cwd || process.cwd();
119
- const skillPath = path.join(projectDir, '.claude', 'skills', matchedSkill, 'SKILL.md');
120
-
121
- if (!fs.existsSync(skillPath)) {
122
- process.exit(0);
123
- }
124
-
125
- // Read and output skill content to stderr (Claude sees stderr)
126
- const skillContent = fs.readFileSync(skillPath, 'utf8');
127
-
128
- console.error('');
129
- console.error('━'.repeat(60));
130
- console.error(`📚 AUTO-LOADING SKILL: ${matchedSkill}`);
131
- console.error('━'.repeat(60));
132
- console.error('');
133
- console.error(skillContent);
134
- console.error('');
135
- console.error('━'.repeat(60));
136
- console.error('');
137
-
138
- // Mark skill as loaded
139
- fs.writeFileSync(markerFile, new Date().toISOString());
140
-
141
- process.exit(0);
142
- }