@hailer/mcp 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/hooks/post-scaffold-hook.cjs +125 -0
- package/.claude/hooks/prompt-skill-loader.cjs +106 -5
- package/.claude/hooks/publish-template-guard.cjs +123 -0
- package/.claude/hooks/skill-loader.cjs +1 -1
- package/.claude/settings.json +22 -0
- package/.claude/skills/MCP-build-data-app-skill/SKILL.md +372 -0
- package/.claude/skills/MCP-publish-template-skill/SKILL.md +278 -0
- package/.claude/skills/MCP-scaffold-hailer-app-skill/SKILL.md +250 -47
- package/.claude/skills/building-hailer-apps-skill/SKILL.md +274 -9
- package/.claude/skills/hailer-app-builder/SKILL.md +340 -0
- package/.claude/skills/spawn-app-builder/SKILL.md +366 -0
- package/CHANGELOG.md +31 -2
- package/dist/app.js +8 -0
- package/dist/mcp/tools/app.d.ts +7 -0
- package/dist/mcp/tools/app.js +997 -1
- package/package.json +1 -1
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Claude Code PostToolUse Hook - Asks user about spawning app builder agent
|
|
4
|
+
*
|
|
5
|
+
* This hook triggers after scaffold_hailer_app completes successfully
|
|
6
|
+
* and ASKS the user if they want to spawn the app builder agent.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
// Read hook input from stdin
|
|
12
|
+
let input = '';
|
|
13
|
+
process.stdin.setEncoding('utf8');
|
|
14
|
+
process.stdin.on('data', chunk => input += chunk);
|
|
15
|
+
process.stdin.on('end', () => {
|
|
16
|
+
try {
|
|
17
|
+
const data = JSON.parse(input);
|
|
18
|
+
processHook(data);
|
|
19
|
+
} catch {
|
|
20
|
+
// Invalid JSON, exit silently
|
|
21
|
+
process.exit(0);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
function processHook(data) {
|
|
26
|
+
const { tool_name, tool_input, tool_output } = data;
|
|
27
|
+
|
|
28
|
+
// Only trigger for scaffold_hailer_app
|
|
29
|
+
if (tool_name !== 'mcp__hailer__scaffold_hailer_app') {
|
|
30
|
+
process.exit(0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check if scaffold was successful (look for "Setup Complete" in output)
|
|
34
|
+
if (!tool_output || !tool_output.includes('Setup Complete')) {
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Extract project info from tool_input
|
|
39
|
+
const projectName = tool_input?.projectName || 'the app';
|
|
40
|
+
const description = tool_input?.description || 'Hailer data app';
|
|
41
|
+
|
|
42
|
+
// Try to determine project path
|
|
43
|
+
const cwd = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
44
|
+
const projectPath = tool_input?.targetDirectory
|
|
45
|
+
? path.join(tool_input.targetDirectory, projectName)
|
|
46
|
+
: path.join(cwd, projectName);
|
|
47
|
+
|
|
48
|
+
// Build the AskUserQuestion instruction
|
|
49
|
+
const output = `
|
|
50
|
+
============================================================
|
|
51
|
+
✅ App Scaffolded Successfully!
|
|
52
|
+
============================================================
|
|
53
|
+
|
|
54
|
+
**Project:** ${projectName}
|
|
55
|
+
**Location:** ${projectPath}
|
|
56
|
+
**Dev Server:** http://localhost:3000 (running)
|
|
57
|
+
|
|
58
|
+
The app structure is ready. Now you need to build the actual components.
|
|
59
|
+
|
|
60
|
+
----------------------------------------
|
|
61
|
+
⚠️ MANDATORY: ASK THE USER
|
|
62
|
+
----------------------------------------
|
|
63
|
+
|
|
64
|
+
You MUST use AskUserQuestion with EXACTLY this format:
|
|
65
|
+
|
|
66
|
+
\`\`\`json
|
|
67
|
+
{
|
|
68
|
+
"questions": [
|
|
69
|
+
{
|
|
70
|
+
"question": "Would you like me to spawn a builder agent to create the app components?",
|
|
71
|
+
"header": "Build App",
|
|
72
|
+
"options": [
|
|
73
|
+
{
|
|
74
|
+
"label": "Yes, spawn builder",
|
|
75
|
+
"description": "Spawn isolated agent with TypeScript + SDK patterns"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"label": "No, I'll build manually",
|
|
79
|
+
"description": "Continue in current session"
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
"multiSelect": false
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
\`\`\`
|
|
87
|
+
|
|
88
|
+
----------------------------------------
|
|
89
|
+
IF USER SAYS YES: Follow these steps
|
|
90
|
+
----------------------------------------
|
|
91
|
+
|
|
92
|
+
1. Load the spawn-app-builder skill:
|
|
93
|
+
\`\`\`javascript
|
|
94
|
+
Skill("spawn-app-builder")
|
|
95
|
+
\`\`\`
|
|
96
|
+
|
|
97
|
+
2. Get workflow context:
|
|
98
|
+
\`\`\`javascript
|
|
99
|
+
list_workflows_minimal()
|
|
100
|
+
\`\`\`
|
|
101
|
+
|
|
102
|
+
3. Use the Task tool template from the skill with:
|
|
103
|
+
- PROJECT_PATH: ${projectPath}
|
|
104
|
+
- TASK_DESCRIPTION: ${description}
|
|
105
|
+
- WORKFLOWS_INFO: (from list_workflows_minimal)
|
|
106
|
+
|
|
107
|
+
----------------------------------------
|
|
108
|
+
IF USER SAYS NO: Load skills in current session
|
|
109
|
+
----------------------------------------
|
|
110
|
+
|
|
111
|
+
\`\`\`javascript
|
|
112
|
+
Skill("building-hailer-apps-skill")
|
|
113
|
+
Skill("hailer-app-builder")
|
|
114
|
+
\`\`\`
|
|
115
|
+
|
|
116
|
+
Then continue building in the current conversation.
|
|
117
|
+
|
|
118
|
+
============================================================
|
|
119
|
+
ASK THE USER NOW - Do not skip this question!
|
|
120
|
+
============================================================
|
|
121
|
+
`;
|
|
122
|
+
|
|
123
|
+
console.error(output);
|
|
124
|
+
process.exit(0);
|
|
125
|
+
}
|
|
@@ -23,12 +23,19 @@ process.stdin.on('end', () => {
|
|
|
23
23
|
try {
|
|
24
24
|
const data = JSON.parse(input);
|
|
25
25
|
processHook(data);
|
|
26
|
-
} catch
|
|
26
|
+
} catch {
|
|
27
27
|
// Invalid JSON, exit silently
|
|
28
28
|
process.exit(0);
|
|
29
29
|
}
|
|
30
30
|
});
|
|
31
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
|
+
|
|
32
39
|
// ALL keywords are now ambiguous - always ask for clarification
|
|
33
40
|
const AMBIGUOUS_KEYWORDS = [
|
|
34
41
|
// Pull operations
|
|
@@ -187,12 +194,63 @@ const AMBIGUOUS_KEYWORDS = [
|
|
|
187
194
|
question: 'What would you like to do with Hailer apps?',
|
|
188
195
|
options: [
|
|
189
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' },
|
|
190
198
|
{ label: 'Create app entry', description: 'Register an app in Hailer (no scaffold)', skill: 'MCP-create-app-skill' },
|
|
191
199
|
{ label: 'List apps', description: 'View existing apps in workspace', skill: 'MCP-list-apps-skill' },
|
|
192
200
|
{ label: 'Update app', description: 'Modify app properties', skill: 'MCP-update-app-skill' }
|
|
193
201
|
]
|
|
194
202
|
}
|
|
195
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
|
+
},
|
|
196
254
|
// Publish/Deploy operations
|
|
197
255
|
{
|
|
198
256
|
keyword: /\b(publish|deploy)\b/i,
|
|
@@ -205,10 +263,30 @@ const AMBIGUOUS_KEYWORDS = [
|
|
|
205
263
|
question: 'What would you like to publish or deploy?',
|
|
206
264
|
options: [
|
|
207
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' },
|
|
208
267
|
{ label: 'Share app with users', description: 'Add members to access an app', skill: 'MCP-add-app-member-skill' }
|
|
209
268
|
]
|
|
210
269
|
}
|
|
211
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
|
+
},
|
|
212
290
|
// Function field / Calculated field operations
|
|
213
291
|
{
|
|
214
292
|
keyword: /\b(function.?field|calculated.?field|formula)\b/i,
|
|
@@ -311,8 +389,8 @@ const AMBIGUOUS_KEYWORDS = [
|
|
|
311
389
|
}
|
|
312
390
|
];
|
|
313
391
|
|
|
314
|
-
//
|
|
315
|
-
|
|
392
|
+
// Note: DIRECT_KEYWORDS removed - all keywords now require disambiguation
|
|
393
|
+
// Flow instructions are now loaded from skill files instead of inline functions
|
|
316
394
|
|
|
317
395
|
function processHook(data) {
|
|
318
396
|
const { prompt, cwd } = data;
|
|
@@ -334,6 +412,7 @@ function processHook(data) {
|
|
|
334
412
|
const skillsToLoad = [];
|
|
335
413
|
const disambiguationsNeeded = [];
|
|
336
414
|
const seenDisambiguations = new Set(); // Avoid duplicate questions
|
|
415
|
+
const loadedFollowUpSkills = new Set(); // Avoid duplicate follow-up skill content
|
|
337
416
|
|
|
338
417
|
// Check all ambiguous keywords
|
|
339
418
|
for (const entry of AMBIGUOUS_KEYWORDS) {
|
|
@@ -406,6 +485,10 @@ function processHook(data) {
|
|
|
406
485
|
output += ` - If "${opt.label}": Load Skill(${opt.skill}), then run with: yes | npm run workflows-sync\n`;
|
|
407
486
|
} else if (opt.action === 'cancel') {
|
|
408
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`;
|
|
409
492
|
} else if (opt.skill) {
|
|
410
493
|
output += ` - If "${opt.label}": Load Skill(${opt.skill}), then proceed\n`;
|
|
411
494
|
} else {
|
|
@@ -413,6 +496,24 @@ function processHook(data) {
|
|
|
413
496
|
}
|
|
414
497
|
}
|
|
415
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
|
+
}
|
|
416
517
|
}
|
|
417
518
|
|
|
418
519
|
output += '='.repeat(60) + '\n';
|
|
@@ -445,8 +546,8 @@ function processHook(data) {
|
|
|
445
546
|
output += '='.repeat(60) + '\n';
|
|
446
547
|
}
|
|
447
548
|
|
|
448
|
-
// Output to
|
|
449
|
-
console.
|
|
549
|
+
// Output to stderr - displays to user
|
|
550
|
+
console.error(output);
|
|
450
551
|
|
|
451
552
|
process.exit(0);
|
|
452
553
|
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Claude Code PreToolUse Hook - Ensures all required fields before publish_template
|
|
4
|
+
*
|
|
5
|
+
* This hook intercepts publish_template calls and blocks them if required fields
|
|
6
|
+
* are missing, prompting Claude to gather all information first.
|
|
7
|
+
*
|
|
8
|
+
* Required fields from Hailer UI:
|
|
9
|
+
* - title (Name) - max 64 chars
|
|
10
|
+
* - description - max 4096 chars
|
|
11
|
+
* - version - e.g. "1.0.0"
|
|
12
|
+
* - versionDescription - release notes
|
|
13
|
+
* - publisher - publishing company name
|
|
14
|
+
* - iconFileId - uploaded icon (JPEG/PNG)
|
|
15
|
+
* - imageFileIds - preview images array (optional but recommended)
|
|
16
|
+
*/
|
|
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, allow the call
|
|
28
|
+
console.log(JSON.stringify({ decision: 'allow' }));
|
|
29
|
+
process.exit(0);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
function processHook(data) {
|
|
34
|
+
const { tool_name, tool_input } = data;
|
|
35
|
+
|
|
36
|
+
// Only intercept publish_template (MCP tool name format)
|
|
37
|
+
if (!tool_name || !tool_name.includes('publish_template')) {
|
|
38
|
+
console.log(JSON.stringify({ decision: 'allow' }));
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Parse tool input
|
|
43
|
+
let args = {};
|
|
44
|
+
try {
|
|
45
|
+
if (typeof tool_input === 'string') {
|
|
46
|
+
args = JSON.parse(tool_input);
|
|
47
|
+
} else if (typeof tool_input === 'object') {
|
|
48
|
+
args = tool_input;
|
|
49
|
+
}
|
|
50
|
+
} catch {
|
|
51
|
+
// Can't parse, let it through
|
|
52
|
+
console.log(JSON.stringify({ decision: 'allow' }));
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Check for missing required fields
|
|
57
|
+
const missing = [];
|
|
58
|
+
if (!args.title) missing.push('title (Name, max 64 chars)');
|
|
59
|
+
if (!args.description) missing.push('description (max 4096 chars)');
|
|
60
|
+
if (!args.version) missing.push('version (e.g. "1.0.0")');
|
|
61
|
+
if (!args.versionDescription) missing.push('versionDescription (release notes)');
|
|
62
|
+
if (!args.publisher) missing.push('publisher (company name)');
|
|
63
|
+
if (!args.iconFileId) missing.push('iconFileId (upload icon first)');
|
|
64
|
+
|
|
65
|
+
if (missing.length > 0) {
|
|
66
|
+
console.log(JSON.stringify({
|
|
67
|
+
decision: 'block',
|
|
68
|
+
message: `BLOCKED: publish_template is missing required fields.
|
|
69
|
+
|
|
70
|
+
Missing: ${missing.join(', ')}
|
|
71
|
+
|
|
72
|
+
Before calling publish_template, you MUST gather ALL information from the user.
|
|
73
|
+
|
|
74
|
+
Use AskUserQuestion with these questions:
|
|
75
|
+
|
|
76
|
+
\`\`\`json
|
|
77
|
+
{
|
|
78
|
+
"questions": [
|
|
79
|
+
{
|
|
80
|
+
"question": "What should be the template name? (max 64 characters)",
|
|
81
|
+
"header": "Name",
|
|
82
|
+
"options": [
|
|
83
|
+
{ "label": "Enter name", "description": "Type a descriptive template name" },
|
|
84
|
+
{ "label": "Use workspace name", "description": "Use current workspace name as template name" }
|
|
85
|
+
],
|
|
86
|
+
"multiSelect": false
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
\`\`\`
|
|
91
|
+
|
|
92
|
+
Then ask for:
|
|
93
|
+
1. **Name** (title) - Template display name (max 64 chars)
|
|
94
|
+
2. **Description** - What this template includes (max 4096 chars)
|
|
95
|
+
3. **Version** - Semantic version like "1.0.0"
|
|
96
|
+
4. **Version description** - Release notes for this version
|
|
97
|
+
5. **Publisher** - Company or person publishing this template
|
|
98
|
+
6. **Icon** - Ask how to provide icon (URL, file path, or existing fileId)
|
|
99
|
+
|
|
100
|
+
After gathering info:
|
|
101
|
+
1. Upload icon with isPublic: true (CRITICAL!):
|
|
102
|
+
upload_files({ files: [{ path: "...", isPublic: true }] })
|
|
103
|
+
2. Then call publish_template with ALL fields:
|
|
104
|
+
|
|
105
|
+
publish_template({
|
|
106
|
+
title: "Template Name",
|
|
107
|
+
description: "What it does...",
|
|
108
|
+
version: "1.0.0",
|
|
109
|
+
versionDescription: "Initial release with...",
|
|
110
|
+
publisher: "Company Name",
|
|
111
|
+
iconFileId: "<24-char-id>",
|
|
112
|
+
imageFileIds: ["<24-char-id>"] // Optional: preview images
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
DO NOT call publish_template until you have ALL required information.`
|
|
116
|
+
}));
|
|
117
|
+
process.exit(0);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// All required fields provided, allow the call
|
|
121
|
+
console.log(JSON.stringify({ decision: 'allow' }));
|
|
122
|
+
process.exit(0);
|
|
123
|
+
}
|
package/.claude/settings.json
CHANGED
|
@@ -30,6 +30,28 @@
|
|
|
30
30
|
"timeout": 5
|
|
31
31
|
}
|
|
32
32
|
]
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"matcher": "mcp__hailer__publish_template",
|
|
36
|
+
"hooks": [
|
|
37
|
+
{
|
|
38
|
+
"type": "command",
|
|
39
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/publish-template-guard.cjs\"",
|
|
40
|
+
"timeout": 5
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"PostToolUse": [
|
|
46
|
+
{
|
|
47
|
+
"matcher": "mcp__hailer__scaffold_hailer_app",
|
|
48
|
+
"hooks": [
|
|
49
|
+
{
|
|
50
|
+
"type": "command",
|
|
51
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/post-scaffold-hook.cjs\"",
|
|
52
|
+
"timeout": 5
|
|
53
|
+
}
|
|
54
|
+
]
|
|
33
55
|
}
|
|
34
56
|
]
|
|
35
57
|
}
|