@hailer/mcp 1.1.12 → 1.1.13

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 (271) hide show
  1. package/CHANGELOG.md +0 -7
  2. package/{.claude → dist}/CLAUDE.md +2 -2
  3. package/dist/app.js +18 -5
  4. package/dist/bot/bot-config.d.ts +10 -1
  5. package/dist/bot/bot-config.js +64 -3
  6. package/dist/bot/bot-manager.d.ts +2 -0
  7. package/dist/bot/bot-manager.js +9 -2
  8. package/dist/bot/bot.d.ts +33 -0
  9. package/dist/bot/bot.js +461 -160
  10. package/dist/bot/services/message-classifier.js +17 -0
  11. package/dist/bot/services/permission-guard.d.ts +52 -0
  12. package/dist/bot/services/permission-guard.js +149 -0
  13. package/dist/bot/services/types.d.ts +5 -0
  14. package/dist/bot/services/typing-indicator.d.ts +6 -1
  15. package/dist/bot/services/typing-indicator.js +19 -3
  16. package/dist/cli.js +0 -0
  17. package/dist/config.d.ts +6 -1
  18. package/dist/config.js +43 -0
  19. package/dist/core.js +3 -6
  20. package/dist/lib/discussion-lock.d.ts +42 -0
  21. package/dist/lib/discussion-lock.js +110 -0
  22. package/dist/mcp/UserContextCache.d.ts +5 -0
  23. package/dist/mcp/UserContextCache.js +51 -19
  24. package/dist/mcp/hailer-clients.d.ts +19 -1
  25. package/dist/mcp/hailer-clients.js +158 -24
  26. package/dist/mcp/session-store.d.ts +68 -0
  27. package/dist/mcp/session-store.js +169 -0
  28. package/dist/mcp/signal-handler.js +2 -0
  29. package/dist/mcp/tool-registry.d.ts +17 -4
  30. package/dist/mcp/tool-registry.js +37 -7
  31. package/dist/mcp/tools/activity.js +99 -7
  32. package/dist/mcp/tools/app-scaffold.js +304 -336
  33. package/dist/mcp/tools/bot-config/constants.d.ts +23 -0
  34. package/dist/mcp/tools/bot-config/constants.js +94 -0
  35. package/dist/mcp/tools/bot-config/core.d.ts +253 -0
  36. package/dist/mcp/tools/bot-config/core.js +2456 -0
  37. package/dist/mcp/tools/bot-config/index.d.ts +10 -0
  38. package/dist/mcp/tools/bot-config/index.js +59 -0
  39. package/dist/mcp/tools/bot-config/tools.d.ts +7 -0
  40. package/dist/mcp/tools/bot-config/tools.js +15 -0
  41. package/dist/mcp/tools/bot-config/types.d.ts +50 -0
  42. package/dist/mcp/tools/bot-config/types.js +6 -0
  43. package/dist/mcp/tools/bug-fixer-tools.d.ts +45 -0
  44. package/dist/mcp/tools/bug-fixer-tools.js +1096 -0
  45. package/dist/mcp/tools/company.d.ts +9 -0
  46. package/dist/mcp/tools/company.js +88 -0
  47. package/dist/mcp/tools/discussion.js +68 -0
  48. package/dist/mcp/tools/document.d.ts +11 -0
  49. package/dist/mcp/tools/document.js +741 -0
  50. package/dist/mcp/tools/investigate.d.ts +9 -0
  51. package/dist/mcp/tools/investigate.js +254 -0
  52. package/dist/mcp/tools/workflow-permissions.d.ts +15 -0
  53. package/dist/mcp/tools/workflow-permissions.js +204 -0
  54. package/dist/mcp/tools/workflow.js +57 -18
  55. package/dist/mcp/utils/index.d.ts +2 -0
  56. package/dist/mcp/utils/index.js +12 -1
  57. package/dist/mcp/utils/role-utils.d.ts +74 -0
  58. package/dist/mcp/utils/role-utils.js +151 -0
  59. package/dist/mcp/utils/types.d.ts +43 -1
  60. package/dist/mcp/utils/types.js +14 -0
  61. package/dist/mcp/webhook-handler.d.ts +4 -0
  62. package/dist/mcp/webhook-handler.js +8 -0
  63. package/dist/mcp-server.d.ts +23 -2
  64. package/dist/mcp-server.js +639 -127
  65. package/dist/plugins/vipunen/client.d.ts +150 -0
  66. package/dist/plugins/vipunen/client.js +535 -0
  67. package/dist/plugins/vipunen/config/schema-config.json +19 -0
  68. package/dist/plugins/vipunen/config/schema-doc.json +22 -0
  69. package/dist/plugins/vipunen/index.d.ts +41 -0
  70. package/dist/plugins/vipunen/index.js +88 -0
  71. package/dist/plugins/vipunen/tools.d.ts +26 -0
  72. package/dist/plugins/vipunen/tools.js +501 -0
  73. package/dist/stdio-server.d.ts +14 -0
  74. package/dist/stdio-server.js +101 -0
  75. package/package.json +2 -1
  76. package/.claude/agents/agent-ada-skill-builder.md +0 -94
  77. package/.claude/agents/agent-alejandro-function-fields.md +0 -342
  78. package/.claude/agents/agent-bjorn-config-audit.md +0 -103
  79. package/.claude/agents/agent-builder-agent-creator.md +0 -130
  80. package/.claude/agents/agent-code-simplifier.md +0 -53
  81. package/.claude/agents/agent-dmitri-activity-crud.md +0 -159
  82. package/.claude/agents/agent-giuseppe-app-builder.md +0 -247
  83. package/.claude/agents/agent-gunther-mcp-tools.md +0 -39
  84. package/.claude/agents/agent-helga-workflow-config.md +0 -204
  85. package/.claude/agents/agent-igor-activity-mover-automation.md +0 -125
  86. package/.claude/agents/agent-ingrid-doc-templates.md +0 -261
  87. package/.claude/agents/agent-ivan-monolith.md +0 -154
  88. package/.claude/agents/agent-kenji-data-reader.md +0 -86
  89. package/.claude/agents/agent-lars-code-inspector.md +0 -102
  90. package/.claude/agents/agent-marco-mockup-builder.md +0 -110
  91. package/.claude/agents/agent-marcus-api-documenter.md +0 -323
  92. package/.claude/agents/agent-marketplace-publisher.md +0 -280
  93. package/.claude/agents/agent-marketplace-reviewer.md +0 -309
  94. package/.claude/agents/agent-permissions-handler.md +0 -208
  95. package/.claude/agents/agent-simple-writer.md +0 -48
  96. package/.claude/agents/agent-svetlana-code-review.md +0 -171
  97. package/.claude/agents/agent-tanya-test-runner.md +0 -333
  98. package/.claude/agents/agent-ui-designer.md +0 -100
  99. package/.claude/agents/agent-viktor-sql-insights.md +0 -212
  100. package/.claude/agents/agent-web-search.md +0 -55
  101. package/.claude/agents/agent-yevgeni-discussions.md +0 -45
  102. package/.claude/agents/agent-zara-zapier.md +0 -159
  103. package/.claude/commands/app-squad.md +0 -135
  104. package/.claude/commands/audit-squad.md +0 -158
  105. package/.claude/commands/autoplan.md +0 -563
  106. package/.claude/commands/cleanup-squad.md +0 -98
  107. package/.claude/commands/config-squad.md +0 -106
  108. package/.claude/commands/crud-squad.md +0 -87
  109. package/.claude/commands/data-squad.md +0 -97
  110. package/.claude/commands/debug-squad.md +0 -303
  111. package/.claude/commands/doc-squad.md +0 -65
  112. package/.claude/commands/handoff.md +0 -137
  113. package/.claude/commands/health.md +0 -49
  114. package/.claude/commands/help.md +0 -29
  115. package/.claude/commands/help:agents.md +0 -151
  116. package/.claude/commands/help:commands.md +0 -78
  117. package/.claude/commands/help:faq.md +0 -79
  118. package/.claude/commands/help:plugins.md +0 -50
  119. package/.claude/commands/help:skills.md +0 -93
  120. package/.claude/commands/help:tools.md +0 -75
  121. package/.claude/commands/hotfix-squad.md +0 -112
  122. package/.claude/commands/integration-squad.md +0 -82
  123. package/.claude/commands/janitor-squad.md +0 -167
  124. package/.claude/commands/learn-auto.md +0 -120
  125. package/.claude/commands/learn.md +0 -120
  126. package/.claude/commands/mcp-list.md +0 -27
  127. package/.claude/commands/onboard-squad.md +0 -140
  128. package/.claude/commands/plan-workspace.md +0 -732
  129. package/.claude/commands/prd.md +0 -130
  130. package/.claude/commands/project-status.md +0 -82
  131. package/.claude/commands/publish.md +0 -138
  132. package/.claude/commands/recap.md +0 -69
  133. package/.claude/commands/restore.md +0 -64
  134. package/.claude/commands/review-squad.md +0 -152
  135. package/.claude/commands/save.md +0 -24
  136. package/.claude/commands/stats.md +0 -19
  137. package/.claude/commands/swarm.md +0 -210
  138. package/.claude/commands/tool-builder.md +0 -39
  139. package/.claude/commands/ws-pull.md +0 -44
  140. package/.claude/hooks/_shared-memory.cjs +0 -305
  141. package/.claude/hooks/_utils.cjs +0 -108
  142. package/.claude/hooks/agent-failure-detector.cjs +0 -383
  143. package/.claude/hooks/agent-usage-logger.cjs +0 -204
  144. package/.claude/hooks/app-edit-guard.cjs +0 -494
  145. package/.claude/hooks/auto-learn.cjs +0 -304
  146. package/.claude/hooks/bash-guard.cjs +0 -272
  147. package/.claude/hooks/builder-mode-manager.cjs +0 -354
  148. package/.claude/hooks/bulk-activity-guard.cjs +0 -271
  149. package/.claude/hooks/context-watchdog.cjs +0 -230
  150. package/.claude/hooks/delegation-reminder.cjs +0 -465
  151. package/.claude/hooks/design-system-lint.cjs +0 -271
  152. package/.claude/hooks/post-scaffold-hook.cjs +0 -181
  153. package/.claude/hooks/prompt-guard.cjs +0 -354
  154. package/.claude/hooks/publish-template-guard.cjs +0 -147
  155. package/.claude/hooks/session-start.cjs +0 -35
  156. package/.claude/hooks/shared-memory-writer.cjs +0 -147
  157. package/.claude/hooks/skill-injector.cjs +0 -140
  158. package/.claude/hooks/skill-usage-logger.cjs +0 -258
  159. package/.claude/hooks/src-edit-guard.cjs +0 -240
  160. package/.claude/hooks/sync-marketplace-agents.cjs +0 -346
  161. package/.claude/settings.json +0 -257
  162. package/.claude/skills/SDK-activity-patterns/SKILL.md +0 -428
  163. package/.claude/skills/SDK-document-templates/SKILL.md +0 -1033
  164. package/.claude/skills/SDK-function-fields/SKILL.md +0 -542
  165. package/.claude/skills/SDK-generate-skill/SKILL.md +0 -92
  166. package/.claude/skills/SDK-init-skill/SKILL.md +0 -127
  167. package/.claude/skills/SDK-insight-queries/SKILL.md +0 -787
  168. package/.claude/skills/SDK-ws-config-skill/SKILL.md +0 -1139
  169. package/.claude/skills/agent-structure/SKILL.md +0 -98
  170. package/.claude/skills/api-documentation-patterns/SKILL.md +0 -474
  171. package/.claude/skills/chrome-mcp-reference/SKILL.md +0 -370
  172. package/.claude/skills/delegation-routing/SKILL.md +0 -202
  173. package/.claude/skills/frontend-design/SKILL.md +0 -254
  174. package/.claude/skills/hailer-activity-mover/SKILL.md +0 -213
  175. package/.claude/skills/hailer-api-client/SKILL.md +0 -518
  176. package/.claude/skills/hailer-app-builder/SKILL.md +0 -1434
  177. package/.claude/skills/hailer-apps-pictures/SKILL.md +0 -269
  178. package/.claude/skills/hailer-design-system/SKILL.md +0 -235
  179. package/.claude/skills/hailer-monolith-automations/SKILL.md +0 -686
  180. package/.claude/skills/hailer-permissions-system/SKILL.md +0 -121
  181. package/.claude/skills/hailer-project-protocol/SKILL.md +0 -488
  182. package/.claude/skills/hailer-rest-api/SKILL.md +0 -61
  183. package/.claude/skills/hailer-rest-api/hailer-activities.md +0 -184
  184. package/.claude/skills/hailer-rest-api/hailer-admin.md +0 -473
  185. package/.claude/skills/hailer-rest-api/hailer-calendar.md +0 -256
  186. package/.claude/skills/hailer-rest-api/hailer-feed.md +0 -249
  187. package/.claude/skills/hailer-rest-api/hailer-insights.md +0 -195
  188. package/.claude/skills/hailer-rest-api/hailer-messaging.md +0 -276
  189. package/.claude/skills/hailer-rest-api/hailer-workflows.md +0 -283
  190. package/.claude/skills/insight-join-patterns/SKILL.md +0 -174
  191. package/.claude/skills/integration-patterns/SKILL.md +0 -421
  192. package/.claude/skills/json-only-output/SKILL.md +0 -72
  193. package/.claude/skills/lsp-setup/SKILL.md +0 -160
  194. package/.claude/skills/mcp-direct-tools/SKILL.md +0 -153
  195. package/.claude/skills/optional-parameters/SKILL.md +0 -72
  196. package/.claude/skills/publish-hailer-app/SKILL.md +0 -244
  197. package/.claude/skills/testing-patterns/SKILL.md +0 -630
  198. package/.claude/skills/tool-builder/SKILL.md +0 -250
  199. package/.claude/skills/tool-parameter-usage/SKILL.md +0 -126
  200. package/.claude/skills/tool-response-verification/SKILL.md +0 -92
  201. package/.claude/skills/zapier-hailer-patterns/SKILL.md +0 -581
  202. package/.mcp.json +0 -13
  203. package/.opencode/agent/agent-ada-skill-builder.md +0 -35
  204. package/.opencode/agent/agent-alejandro-function-fields.md +0 -39
  205. package/.opencode/agent/agent-bjorn-config-audit.md +0 -36
  206. package/.opencode/agent/agent-builder-agent-creator.md +0 -39
  207. package/.opencode/agent/agent-code-simplifier.md +0 -31
  208. package/.opencode/agent/agent-dmitri-activity-crud.md +0 -40
  209. package/.opencode/agent/agent-giuseppe-app-builder.md +0 -37
  210. package/.opencode/agent/agent-gunther-mcp-tools.md +0 -39
  211. package/.opencode/agent/agent-helga-workflow-config.md +0 -203
  212. package/.opencode/agent/agent-igor-activity-mover-automation.md +0 -46
  213. package/.opencode/agent/agent-ingrid-doc-templates.md +0 -39
  214. package/.opencode/agent/agent-ivan-monolith.md +0 -46
  215. package/.opencode/agent/agent-kenji-data-reader.md +0 -53
  216. package/.opencode/agent/agent-lars-code-inspector.md +0 -28
  217. package/.opencode/agent/agent-marco-mockup-builder.md +0 -42
  218. package/.opencode/agent/agent-marcus-api-documenter.md +0 -53
  219. package/.opencode/agent/agent-marketplace-publisher.md +0 -44
  220. package/.opencode/agent/agent-marketplace-reviewer.md +0 -42
  221. package/.opencode/agent/agent-permissions-handler.md +0 -50
  222. package/.opencode/agent/agent-simple-writer.md +0 -45
  223. package/.opencode/agent/agent-svetlana-code-review.md +0 -39
  224. package/.opencode/agent/agent-tanya-test-runner.md +0 -57
  225. package/.opencode/agent/agent-ui-designer.md +0 -56
  226. package/.opencode/agent/agent-viktor-sql-insights.md +0 -34
  227. package/.opencode/agent/agent-web-search.md +0 -42
  228. package/.opencode/agent/agent-yevgeni-discussions.md +0 -37
  229. package/.opencode/agent/agent-zara-zapier.md +0 -53
  230. package/.opencode/commands/app-squad.md +0 -135
  231. package/.opencode/commands/audit-squad.md +0 -158
  232. package/.opencode/commands/autoplan.md +0 -563
  233. package/.opencode/commands/cleanup-squad.md +0 -98
  234. package/.opencode/commands/config-squad.md +0 -106
  235. package/.opencode/commands/crud-squad.md +0 -87
  236. package/.opencode/commands/data-squad.md +0 -97
  237. package/.opencode/commands/debug-squad.md +0 -303
  238. package/.opencode/commands/doc-squad.md +0 -65
  239. package/.opencode/commands/handoff.md +0 -137
  240. package/.opencode/commands/health.md +0 -49
  241. package/.opencode/commands/help-agents.md +0 -151
  242. package/.opencode/commands/help-commands.md +0 -32
  243. package/.opencode/commands/help-faq.md +0 -29
  244. package/.opencode/commands/help-plugins.md +0 -28
  245. package/.opencode/commands/help-skills.md +0 -7
  246. package/.opencode/commands/help-tools.md +0 -40
  247. package/.opencode/commands/help.md +0 -28
  248. package/.opencode/commands/hotfix-squad.md +0 -112
  249. package/.opencode/commands/integration-squad.md +0 -82
  250. package/.opencode/commands/janitor-squad.md +0 -167
  251. package/.opencode/commands/learn-auto.md +0 -120
  252. package/.opencode/commands/learn.md +0 -120
  253. package/.opencode/commands/mcp-list.md +0 -27
  254. package/.opencode/commands/onboard-squad.md +0 -140
  255. package/.opencode/commands/plan-workspace.md +0 -732
  256. package/.opencode/commands/prd.md +0 -131
  257. package/.opencode/commands/project-status.md +0 -82
  258. package/.opencode/commands/publish.md +0 -138
  259. package/.opencode/commands/recap.md +0 -69
  260. package/.opencode/commands/restore.md +0 -64
  261. package/.opencode/commands/review-squad.md +0 -152
  262. package/.opencode/commands/save.md +0 -24
  263. package/.opencode/commands/stats.md +0 -19
  264. package/.opencode/commands/swarm.md +0 -210
  265. package/.opencode/commands/tool-builder.md +0 -39
  266. package/.opencode/commands/ws-pull.md +0 -44
  267. package/.opencode/opencode.json +0 -28
  268. package/SESSION-HANDOFF.md +0 -68
  269. package/inbox/2026-03-04-bot-config-patterns.md +0 -24
  270. package/scripts/postinstall.cjs +0 -64
  271. package/scripts/test-hal-tools.ts +0 -154
@@ -1,354 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * <hook-name>prompt-guard</hook-name>
4
- *
5
- * <purpose>
6
- * Consolidated UserPromptSubmit hook. Replaces 4 separate hooks:
7
- * - session-structure-check (project structure + auto-update hooks)
8
- * - interactive-mode (suggest clarifying questions)
9
- * - sync-marketplace-agents (update CLAUDE.md with marketplace agents)
10
- * - git-hooks-check (notify about missing pre-commit hook)
11
- *
12
- * Single Node process instead of 4 = faster, no timeouts.
13
- * </purpose>
14
- *
15
- * <triggers>UserPromptSubmit</triggers>
16
- * @version 1.0.0
17
- */
18
-
19
- const fs = require('fs');
20
- const path = require('path');
21
- const os = require('os');
22
-
23
- const ALLOW = JSON.stringify({ decision: 'allow' });
24
- const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
25
-
26
- // Skip if stdin is TTY (no piped input)
27
- if (process.stdin.isTTY) {
28
- console.log(ALLOW);
29
- process.exit(0);
30
- }
31
-
32
- // --- Read stdin ---
33
- let input = '';
34
- process.stdin.setEncoding('utf8');
35
- process.stdin.on('data', chunk => input += chunk);
36
- process.stdin.on('end', () => {
37
- try {
38
- const data = JSON.parse(input || '{}');
39
- processHook(data);
40
- } catch (e) {
41
- console.error('[prompt-guard] CRASH:', e.message, e.stack);
42
- console.log(ALLOW);
43
- process.exit(0);
44
- }
45
- });
46
-
47
- function processHook(data) {
48
- const messages = [];
49
-
50
- // 1. Session structure check (once per session)
51
- try {
52
- const structureMsg = checkSessionStructure();
53
- if (structureMsg) messages.push(structureMsg);
54
- } catch (e) {
55
- console.error('[prompt-guard] checkSessionStructure crashed:', e.message);
56
- }
57
-
58
- // 2. Interactive mode (every message)
59
- try {
60
- const interactiveMsg = checkInteractiveMode(data.prompt);
61
- if (interactiveMsg) messages.push(interactiveMsg);
62
- } catch (e) {
63
- console.error('[prompt-guard] checkInteractiveMode crashed:', e.message);
64
- }
65
-
66
- // 3. Sync marketplace agents (only when plugins changed)
67
- try { syncMarketplaceAgents(); } catch (e) {
68
- console.error('[prompt-guard] Marketplace sync crashed:', e.message);
69
- }
70
-
71
- // 4. Git hooks check (once per session)
72
- try {
73
- const gitMsg = checkGitHooks();
74
- if (gitMsg) console.error(gitMsg);
75
- } catch (e) {
76
- console.error('[prompt-guard] checkGitHooks crashed:', e.message);
77
- }
78
-
79
- // Output
80
- if (messages.length > 0) {
81
- console.log(JSON.stringify({
82
- decision: 'allow',
83
- message: messages.join('\n')
84
- }));
85
- } else {
86
- console.log(ALLOW);
87
- }
88
- process.exit(0);
89
- }
90
-
91
- // ============================================================
92
- // 1. Session Structure Check (from session-structure-check.cjs)
93
- // ============================================================
94
- function checkSessionStructure() {
95
- // Find project root
96
- let projectRoot = projectDir;
97
- while (projectRoot !== '/' && !fs.existsSync(path.join(projectRoot, '.claude'))) {
98
- projectRoot = path.dirname(projectRoot);
99
- }
100
-
101
- // Session marker - only run once
102
- const markerPath = path.join(projectRoot, '.claude', '.session-checked');
103
- if (fs.existsSync(markerPath)) return null;
104
-
105
- try { fs.writeFileSync(markerPath, new Date().toISOString()); } catch {}
106
-
107
- const reminders = [];
108
-
109
- const handoffPath = path.join(projectRoot, 'SESSION-HANDOFF.md');
110
- const developmentMdPath = path.join(projectRoot, 'DEVELOPMENT.md');
111
- const workspacePath = path.join(projectRoot, 'workspace');
112
-
113
- if (fs.existsSync(handoffPath)) {
114
- reminders.push('SESSION-HANDOFF.md found - read it to resume previous work, then update it.');
115
- }
116
- if (!fs.existsSync(developmentMdPath) && fs.existsSync(workspacePath)) {
117
- reminders.push('No DEVELOPMENT.md found. Consider creating one to track project status.');
118
- }
119
-
120
- // Hooks are now project-local via $CLAUDE_PROJECT_DIR in settings.json
121
- // No auto-update needed
122
-
123
- if (reminders.length > 0) {
124
- return '<session-start>\n' + reminders.join('\n') + '\n</session-start>';
125
- }
126
- return null;
127
- }
128
-
129
- // ============================================================
130
- // 2. Interactive Mode (from interactive-mode.cjs)
131
- // ============================================================
132
- function checkInteractiveMode(prompt) {
133
- if (!prompt) return null;
134
-
135
- const taskPatterns = [
136
- { pattern: /(add|create).*(field|workflow|phase)/i, type: 'schema', questions: ['Field type?', 'Required or optional?', 'Default values?'] },
137
- { pattern: /(build|create|make).*(app|dashboard|portal|ui)/i, type: 'app', questions: ['What data to display?', 'What layout/components?', 'What user actions needed?'] },
138
- { pattern: /(create|add).*(insight|report)/i, type: 'insight', questions: ['What metrics/aggregations?', 'Which workflows to query?', 'Any filters needed?'] },
139
- { pattern: /(import|create).*(activit|record)|bulk/i, type: 'data', questions: ['Which workflow?', 'What field values?', 'How many records?'] },
140
- { pattern: /(update|change|modify).*(activit|record|field|data)/i, type: 'update', questions: ['Which records affected?', 'What new values?', 'Confirm before applying?'] },
141
- ];
142
-
143
- // Feature implementation detection
144
- const isFeatureRequest = /implement|build.*feature|add.*feature|new feature/i.test(prompt);
145
- const planMarkers = (prompt.match(/step\s*\d|phase\s*\d|##|requirement|trigger|action/gi) || []).length;
146
- const hasDetailedPlan = prompt.length > 500 || planMarkers >= 2;
147
-
148
- if (isFeatureRequest && hasDetailedPlan) {
149
- return `<interactive-mode>
150
- FEATURE IMPLEMENTATION DETECTED with detailed plan.
151
-
152
- Before implementing, ask: "Want me to create a PRD first?"
153
- - PRDs track requirements in docs/prd-*.md
154
- - Links to DEVELOPMENT.md roadmap
155
- - Skip only if user explicitly declines
156
-
157
- Use AskUserQuestion to offer PRD creation.
158
- </interactive-mode>`;
159
- }
160
-
161
- const matched = taskPatterns.find(p => p.pattern.test(prompt));
162
- if (matched) {
163
- return `<interactive-mode>
164
- BEFORE STARTING: Consider asking clarifying questions.
165
-
166
- Task type detected: ${matched.type}
167
- Suggested questions to ask user:
168
- ${matched.questions.map(q => `- ${q}`).join('\n')}
169
-
170
- Use AskUserQuestion tool if requirements are unclear.
171
- Gather specifics before spawning agents or making changes.
172
- </interactive-mode>`;
173
- }
174
-
175
- return null;
176
- }
177
-
178
- // ============================================================
179
- // 3. Sync Marketplace Agents (from sync-marketplace-agents.cjs)
180
- // ============================================================
181
- function syncMarketplaceAgents() {
182
- const crypto = require('crypto');
183
-
184
- const PLUGINS_DIR = path.join(os.homedir(), '.claude', 'plugins', 'marketplaces');
185
- const INSTALLED_PLUGINS = path.join(os.homedir(), '.claude', 'plugins', 'installed_plugins.json');
186
- const CLAUDE_MD = path.join(projectDir, 'CLAUDE.md');
187
- const SYNC_STATE_DIR = path.join(os.homedir(), '.claude', 'sync-state');
188
- const PROJECT_HASH = crypto.createHash('md5').update(projectDir).digest('hex').slice(0, 12);
189
- const SYNC_STATE = path.join(SYNC_STATE_DIR, `${PROJECT_HASH}.state`);
190
-
191
- function getInstalledPluginsHash() {
192
- if (!fs.existsSync(INSTALLED_PLUGINS)) return 'empty';
193
- return crypto.createHash('md5').update(fs.readFileSync(INSTALLED_PLUGINS, 'utf-8')).digest('hex');
194
- }
195
-
196
- // Check if plugins changed
197
- const currentHash = getInstalledPluginsHash();
198
- try {
199
- if (fs.existsSync(SYNC_STATE) && fs.readFileSync(SYNC_STATE, 'utf-8').trim() === currentHash) {
200
- return; // No change
201
- }
202
- } catch {}
203
-
204
- try {
205
- const installedPlugins = getInstalledPlugins(INSTALLED_PLUGINS);
206
- const agents = scanMarketplaceAgents(PLUGINS_DIR, installedPlugins);
207
- updateClaudeMd(CLAUDE_MD, agents);
208
-
209
- // Save sync state
210
- fs.mkdirSync(SYNC_STATE_DIR, { recursive: true });
211
- fs.writeFileSync(SYNC_STATE, currentHash);
212
- console.error(`[prompt-guard] Detected plugin changes, found ${agents.length} marketplace agents`);
213
- } catch (e) {
214
- console.error(`[prompt-guard] Sync agents warning: ${e.message}`);
215
- }
216
- }
217
-
218
- function getInstalledPlugins(installedPluginsPath) {
219
- const plugins = new Set();
220
- if (!fs.existsSync(installedPluginsPath)) return plugins;
221
- try {
222
- const data = JSON.parse(fs.readFileSync(installedPluginsPath, 'utf-8'));
223
- if (data.plugins) for (const key of Object.keys(data.plugins)) plugins.add(key);
224
- } catch {}
225
- return plugins;
226
- }
227
-
228
- function scanMarketplaceAgents(pluginsDir, enabledPlugins) {
229
- const agents = [];
230
- if (!fs.existsSync(pluginsDir)) return agents;
231
-
232
- for (const marketplace of fs.readdirSync(pluginsDir)) {
233
- const marketplacePath = path.join(pluginsDir, marketplace);
234
- if (!fs.statSync(marketplacePath).isDirectory()) continue;
235
-
236
- // Flat structure: {marketplace}/agents/
237
- const flatAgentsPath = path.join(marketplacePath, 'agents');
238
- if (fs.existsSync(flatAgentsPath)) {
239
- const pluginKey = `${marketplace}@${marketplace}`;
240
- if (enabledPlugins.has(pluginKey)) {
241
- for (const f of fs.readdirSync(flatAgentsPath).filter(f => f.endsWith('.md'))) {
242
- const content = fs.readFileSync(path.join(flatAgentsPath, f), 'utf-8');
243
- const fm = parseFrontmatter(content);
244
- agents.push({ marketplace, plugin: marketplace, name: path.basename(f, '.md'), fullName: `${marketplace}:${path.basename(f, '.md')}`, description: fm.description || '', model: fm.model || 'sonnet' });
245
- }
246
- }
247
- }
248
-
249
- // Root-level plugins: {marketplace}/{plugin}/agents/
250
- for (const item of fs.readdirSync(marketplacePath)) {
251
- if (item === '.claude-plugin' || item === '.git' || item === 'plugins') continue;
252
- const itemPath = path.join(marketplacePath, item);
253
- if (!fs.statSync(itemPath).isDirectory()) continue;
254
- if (!fs.existsSync(path.join(itemPath, '.claude-plugin', 'plugin.json'))) continue;
255
- if (!enabledPlugins.has(`${item}@${marketplace}`)) continue;
256
- const agentsPath = path.join(itemPath, 'agents');
257
- if (!fs.existsSync(agentsPath)) continue;
258
- for (const f of fs.readdirSync(agentsPath).filter(f => f.endsWith('.md'))) {
259
- const content = fs.readFileSync(path.join(agentsPath, f), 'utf-8');
260
- const fm = parseFrontmatter(content);
261
- agents.push({ marketplace, plugin: item, name: path.basename(f, '.md'), fullName: `${item}:${path.basename(f, '.md')}`, description: fm.description || '', model: fm.model || 'sonnet' });
262
- }
263
- }
264
-
265
- // Nested: {marketplace}/plugins/{plugin}/agents/
266
- const nestedPath = path.join(marketplacePath, 'plugins');
267
- if (fs.existsSync(nestedPath)) {
268
- for (const plugin of fs.readdirSync(nestedPath)) {
269
- if (!enabledPlugins.has(`${plugin}@${marketplace}`)) continue;
270
- const pluginPath = path.join(nestedPath, plugin);
271
- if (!fs.statSync(pluginPath).isDirectory()) continue;
272
- const agentsPath = path.join(pluginPath, 'agents');
273
- if (!fs.existsSync(agentsPath)) continue;
274
- for (const f of fs.readdirSync(agentsPath).filter(f => f.endsWith('.md'))) {
275
- const content = fs.readFileSync(path.join(agentsPath, f), 'utf-8');
276
- const fm = parseFrontmatter(content);
277
- agents.push({ marketplace, plugin, name: path.basename(f, '.md'), fullName: `${plugin}:${path.basename(f, '.md')}`, description: fm.description || '', model: fm.model || 'sonnet' });
278
- }
279
- }
280
- }
281
- }
282
- return agents;
283
- }
284
-
285
- function parseFrontmatter(content) {
286
- const match = content.match(/^---\n([\s\S]*?)\n---/);
287
- if (!match) return {};
288
- const fm = {};
289
- for (const line of match[1].split('\n')) {
290
- const i = line.indexOf(':');
291
- if (i <= 0 || line.startsWith(' ') || line.startsWith('\t')) continue;
292
- const key = line.slice(0, i).trim();
293
- let value = line.slice(i + 1).trim();
294
- if (key === 'description') value = value.replace(/\\n.*/g, '').slice(0, 100);
295
- fm[key] = value;
296
- }
297
- return fm;
298
- }
299
-
300
- function updateClaudeMd(claudeMdPath, agents) {
301
- if (!fs.existsSync(claudeMdPath)) return;
302
- let content = fs.readFileSync(claudeMdPath, 'utf-8');
303
-
304
- let table = agents.length === 0 ? 'No marketplace agents installed.' :
305
- '| Agent | Plugin | Model | Description |\n|-------|--------|-------|-------------|\n' +
306
- agents.map(a => `| \`${a.fullName}\` | ${a.plugin} | ${a.model} | ${a.description.slice(0, 50)}${a.description.length > 50 ? '...' : ''} |`).join('\n');
307
-
308
- const sectionStart = content.indexOf('<config-source>');
309
- const sectionEnd = content.indexOf('</config-source>');
310
- if (sectionStart === -1 || sectionEnd === -1) return;
311
-
312
- const tableStart = content.indexOf('| Agent |', sectionStart);
313
- const noAgents = content.indexOf('No marketplace agents installed.', sectionStart);
314
-
315
- let replaceStart, replaceEnd;
316
- if (tableStart !== -1 && tableStart < sectionEnd) { replaceStart = tableStart; replaceEnd = sectionEnd; }
317
- else if (noAgents !== -1 && noAgents < sectionEnd) { replaceStart = noAgents; replaceEnd = noAgents + 'No marketplace agents installed.'.length; }
318
- else { replaceStart = content.indexOf('\n', sectionStart) + 1; replaceEnd = replaceStart; }
319
-
320
- content = content.substring(0, replaceStart) + table + '\n\n' + content.substring(replaceEnd);
321
- fs.writeFileSync(claudeMdPath, content);
322
- }
323
-
324
- // ============================================================
325
- // 4. Git Hooks Check (from git-hooks-check.cjs)
326
- // ============================================================
327
- function checkGitHooks() {
328
- const SESSION_FILE = path.join(os.tmpdir(), '.claude-git-hooks-checked');
329
- const SESSION_TTL = 3600000;
330
-
331
- try {
332
- if (fs.existsSync(SESSION_FILE)) {
333
- const data = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf8'));
334
- if (data.projectDir === projectDir && (Date.now() - data.checkedAt) < SESSION_TTL) return null;
335
- }
336
- } catch {}
337
-
338
- fs.writeFileSync(SESSION_FILE, JSON.stringify({ projectDir, checkedAt: Date.now() }));
339
-
340
- const gitDir = path.join(projectDir, '.git');
341
- if (!fs.existsSync(gitDir)) return null;
342
-
343
- const hookSource = path.join(projectDir, '.claude', 'hooks', 'pre-commit-test.sh');
344
- if (!fs.existsSync(hookSource)) return null;
345
-
346
- const preCommitHook = path.join(gitDir, 'hooks', 'pre-commit');
347
- if (fs.existsSync(preCommitHook)) return null;
348
-
349
- return `
350
- Git pre-commit hook not installed.
351
- Install: ln -sf ../../.claude/hooks/pre-commit-test.sh .git/hooks/pre-commit
352
- `;
353
- }
354
-
@@ -1,147 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * <hook-name>publish-template-guard</hook-name>
4
- *
5
- * <purpose>
6
- * Ensures all required fields are provided before publish_template is called.
7
- * Blocks incomplete calls and instructs Claude to gather information first.
8
- * </purpose>
9
- *
10
- * <triggers>
11
- * - mcp__hailer__publish_template tool calls
12
- * </triggers>
13
- *
14
- * <required-fields>
15
- * - title: Template name (max 64 chars)
16
- * - description: What it includes (max 4096 chars)
17
- * - version: Semantic version (e.g. "1.0.0")
18
- * - versionDescription: Release notes
19
- * - publisher: Company or person name
20
- * - iconFileId: Uploaded icon ID (24 chars)
21
- * </required-fields>
22
- *
23
- * <behavior>
24
- * 1. Checks tool_input for missing required fields
25
- * 2. Blocks with decision: "block" if incomplete
26
- * 3. Provides AskUserQuestion template for Claude to gather info
27
- * </behavior>
28
- *
29
- * <workflow>
30
- * 1. Claude asks user for template details
31
- * 2. Claude uploads icon with upload_files
32
- * 3. Claude calls publish_template with ALL fields
33
- * </workflow>
34
- */
35
-
36
- // Skip in subagent context - subagents can't use AskUserQuestion to recover
37
- if (process.env.CLAUDE_AGENT_ID || process.env.CLAUDE_SUBAGENT) {
38
- console.log(JSON.stringify({ decision: 'allow' }));
39
- process.exit(0);
40
- }
41
-
42
- // Read hook input from stdin
43
- let input = '';
44
- process.stdin.setEncoding('utf8');
45
- process.stdin.on('data', chunk => input += chunk);
46
- process.stdin.on('end', () => {
47
- try {
48
- const data = JSON.parse(input);
49
- processHook(data);
50
- } catch {
51
- // Invalid JSON, allow the call
52
- console.log(JSON.stringify({ decision: 'allow' }));
53
- process.exit(0);
54
- }
55
- });
56
-
57
- function processHook(data) {
58
- const { tool_name, tool_input } = data;
59
-
60
- // Only intercept publish_template (MCP tool name format)
61
- if (!tool_name || !tool_name.includes('publish_template')) {
62
- console.log(JSON.stringify({ decision: 'allow' }));
63
- process.exit(0);
64
- }
65
-
66
- // Parse tool input
67
- let args = {};
68
- try {
69
- if (typeof tool_input === 'string') {
70
- args = JSON.parse(tool_input);
71
- } else if (typeof tool_input === 'object') {
72
- args = tool_input;
73
- }
74
- } catch {
75
- // Can't parse, let it through
76
- console.log(JSON.stringify({ decision: 'allow' }));
77
- process.exit(0);
78
- }
79
-
80
- // Check for missing required fields
81
- const missing = [];
82
- if (!args.title) missing.push('title (Name, max 64 chars)');
83
- if (!args.description) missing.push('description (max 4096 chars)');
84
- if (!args.version) missing.push('version (e.g. "1.0.0")');
85
- if (!args.versionDescription) missing.push('versionDescription (release notes)');
86
- if (!args.publisher) missing.push('publisher (company name)');
87
- if (!args.iconFileId) missing.push('iconFileId (upload icon first)');
88
-
89
- if (missing.length > 0) {
90
- console.log(JSON.stringify({
91
- decision: 'block',
92
- message: `BLOCKED: publish_template is missing required fields.
93
-
94
- Missing: ${missing.join(', ')}
95
-
96
- Before calling publish_template, you MUST gather ALL information from the user.
97
-
98
- Use AskUserQuestion with these questions:
99
-
100
- \`\`\`json
101
- {
102
- "questions": [
103
- {
104
- "question": "What should be the template name? (max 64 characters)",
105
- "header": "Name",
106
- "options": [
107
- { "label": "Enter name", "description": "Type a descriptive template name" },
108
- { "label": "Use workspace name", "description": "Use current workspace name as template name" }
109
- ],
110
- "multiSelect": false
111
- }
112
- ]
113
- }
114
- \`\`\`
115
-
116
- Then ask for:
117
- 1. **Name** (title) - Template display name (max 64 chars)
118
- 2. **Description** - What this template includes (max 4096 chars)
119
- 3. **Version** - Semantic version like "1.0.0"
120
- 4. **Version description** - Release notes for this version
121
- 5. **Publisher** - Company or person publishing this template
122
- 6. **Icon** - Ask how to provide icon (URL, file path, or existing fileId)
123
-
124
- After gathering info:
125
- 1. Upload icon with isPublic: true (CRITICAL!):
126
- upload_files({ files: [{ path: "...", isPublic: true }] })
127
- 2. Then call publish_template with ALL fields:
128
-
129
- publish_template({
130
- title: "Template Name",
131
- description: "What it does...",
132
- version: "1.0.0",
133
- versionDescription: "Initial release with...",
134
- publisher: "Company Name",
135
- iconFileId: "<24-char-id>",
136
- imageFileIds: ["<24-char-id>"] // Optional: preview images
137
- })
138
-
139
- DO NOT call publish_template until you have ALL required information.`
140
- }));
141
- process.exit(0);
142
- }
143
-
144
- // All required fields provided, allow the call
145
- console.log(JSON.stringify({ decision: 'allow' }));
146
- process.exit(0);
147
- }
@@ -1,35 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * SessionStart hook - auto-loads SESSION-HANDOFF.md into context.
4
- * Eliminates the need to manually run /recap at session start.
5
- */
6
- const fs = require('fs');
7
- const path = require('path');
8
-
9
- // Consume stdin (required by hook protocol)
10
- try { fs.readFileSync('/dev/stdin', 'utf8'); } catch (e) { /* stdin may be empty or closed */ }
11
- const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
12
-
13
- const files = [
14
- { name: 'SESSION-HANDOFF.md', label: 'Session Handoff' },
15
- { name: 'DEVELOPMENT.md', label: 'Project Status' }
16
- ];
17
-
18
- const sections = [];
19
-
20
- for (const file of files) {
21
- const filePath = path.join(projectDir, file.name);
22
- if (fs.existsSync(filePath)) {
23
- const content = fs.readFileSync(filePath, 'utf8').trim();
24
- if (content) {
25
- sections.push(`## ${file.label}\n\n${content}`);
26
- }
27
- }
28
- }
29
-
30
- if (sections.length > 0) {
31
- const context = `# Auto-loaded Project Context\n\n${sections.join('\n\n---\n\n')}`;
32
- console.log(JSON.stringify({ additionalContext: context }));
33
- } else {
34
- console.log(JSON.stringify({}));
35
- }
@@ -1,147 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * <hook-name>shared-memory-writer</hook-name>
4
- *
5
- * <purpose>
6
- * Auto-caches Kenji agent results into shared memory for squad reuse.
7
- * Watches PostToolUse:Task for Kenji completions, parses schema data,
8
- * and writes to .shared-memory.json so subsequent squads skip redundant lookups.
9
- * </purpose>
10
- *
11
- * <triggers>
12
- * - PostToolUse on Task
13
- * - Only when subagent_type is "agent-kenji-data-reader"
14
- * - Only when response contains workflow schema data
15
- * </triggers>
16
- *
17
- * <behavior>
18
- * 1. Detects Kenji agent completions
19
- * 2. Parses response for workflow schema data (workflow IDs, field lists, phases)
20
- * 3. Writes to shared memory under "workflow-schemas" key
21
- * 4. Non-blocking: outputs to stderr, never blocks the tool call
22
- * </behavior>
23
- */
24
-
25
- // Skip if stdin is TTY (no piped input)
26
- if (process.stdin.isTTY) {
27
- process.exit(0);
28
- }
29
-
30
- let input = '';
31
- process.stdin.setEncoding('utf8');
32
- process.stdin.on('data', chunk => input += chunk);
33
- process.stdin.on('end', () => {
34
- try {
35
- const data = JSON.parse(input);
36
- processHook(data);
37
- } catch (e) {
38
- console.error(`[shared-memory-writer] Failed to parse input: ${e.message}`);
39
- process.exit(0);
40
- }
41
- });
42
-
43
- function processHook(data) {
44
- const { tool_name, tool_input, tool_response } = data;
45
-
46
- // Only process Task tool calls
47
- if (tool_name !== 'Task') {
48
- process.exit(0);
49
- }
50
-
51
- // Only process Kenji agent completions
52
- const agentName = tool_input?.subagent_type;
53
- if (agentName !== 'agent-kenji-data-reader') {
54
- process.exit(0);
55
- }
56
-
57
- const responseText = typeof tool_response === 'string'
58
- ? tool_response
59
- : JSON.stringify(tool_response || '');
60
-
61
- // Check if response contains schema-like data
62
- if (!containsSchemaData(responseText)) {
63
- console.error('[shared-memory-writer] Kenji response has no schema data, skipping cache.');
64
- process.exit(0);
65
- }
66
-
67
- // Write to shared memory
68
- try {
69
- const mem = require('./_shared-memory.cjs');
70
- const existing = mem.get('workflow-schemas');
71
-
72
- // Merge with existing data if present, or write fresh
73
- const schemaData = extractSchemaData(responseText);
74
-
75
- if (existing && existing.data) {
76
- // Merge: keep existing entries, add/overwrite with new ones
77
- const merged = { ...existing.data, ...schemaData, lastUpdated: new Date().toISOString() };
78
- mem.set('workflow-schemas', merged, 'agent-kenji-data-reader');
79
- console.error(`[shared-memory-writer] Merged Kenji schema data into shared memory (${Object.keys(schemaData).length} keys).`);
80
- } else {
81
- schemaData.lastUpdated = new Date().toISOString();
82
- mem.set('workflow-schemas', schemaData, 'agent-kenji-data-reader');
83
- console.error(`[shared-memory-writer] Cached Kenji schema data to shared memory (${Object.keys(schemaData).length} keys).`);
84
- }
85
- } catch (e) {
86
- console.error(`[shared-memory-writer] Failed to cache: ${e.message}`);
87
- }
88
-
89
- process.exit(0);
90
- }
91
-
92
- /**
93
- * Check if response contains workflow schema data indicators.
94
- */
95
- function containsSchemaData(text) {
96
- const lower = text.toLowerCase();
97
- const indicators = [
98
- 'workflow',
99
- 'field',
100
- 'phase',
101
- 'processid',
102
- 'fieldid',
103
- 'phaseid',
104
- 'fields.ts',
105
- 'phases.ts',
106
- 'workflows.ts'
107
- ];
108
-
109
- let matches = 0;
110
- for (const indicator of indicators) {
111
- if (lower.includes(indicator)) matches++;
112
- }
113
-
114
- // Need at least 2 schema indicators to be considered schema data
115
- return matches >= 2;
116
- }
117
-
118
- /**
119
- * Extract structured schema data from Kenji's response.
120
- * Stores the raw response keyed by identifiable workflow references.
121
- */
122
- function extractSchemaData(text) {
123
- const data = { _rawResponse: text };
124
-
125
- // Try to parse as JSON first (Kenji often returns JSON)
126
- try {
127
- const parsed = JSON.parse(text);
128
- if (parsed.result) {
129
- data._parsed = parsed.result;
130
- } else if (parsed.data) {
131
- data._parsed = parsed.data;
132
- } else {
133
- data._parsed = parsed;
134
- }
135
- } catch {
136
- // Not JSON - that's fine, the raw text is still useful context
137
- }
138
-
139
- // Extract workflow IDs mentioned (patterns like 24-char hex strings)
140
- const workflowIdPattern = /[0-9a-f]{24}/g;
141
- const ids = text.match(workflowIdPattern);
142
- if (ids) {
143
- data._workflowIds = [...new Set(ids)];
144
- }
145
-
146
- return data;
147
- }