@hailer/mcp 1.1.11 → 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 (252) hide show
  1. package/dist/app.js +18 -5
  2. package/dist/bot/bot-config.d.ts +12 -1
  3. package/dist/bot/bot-config.js +98 -14
  4. package/dist/bot/bot-manager.d.ts +13 -3
  5. package/dist/bot/bot-manager.js +80 -25
  6. package/dist/bot/bot.d.ts +46 -0
  7. package/dist/bot/bot.js +542 -166
  8. package/dist/bot/services/message-classifier.js +17 -0
  9. package/dist/bot/services/permission-guard.d.ts +52 -0
  10. package/dist/bot/services/permission-guard.js +149 -0
  11. package/dist/bot/services/types.d.ts +5 -0
  12. package/dist/bot/services/typing-indicator.d.ts +6 -1
  13. package/dist/bot/services/typing-indicator.js +19 -3
  14. package/dist/config.d.ts +6 -1
  15. package/dist/config.js +43 -0
  16. package/dist/core.js +3 -6
  17. package/dist/mcp/UserContextCache.d.ts +5 -0
  18. package/dist/mcp/UserContextCache.js +51 -19
  19. package/dist/mcp/hailer-clients.d.ts +19 -1
  20. package/dist/mcp/hailer-clients.js +157 -20
  21. package/dist/mcp/session-store.d.ts +68 -0
  22. package/dist/mcp/session-store.js +169 -0
  23. package/dist/mcp/signal-handler.js +12 -12
  24. package/dist/mcp/tool-registry.d.ts +17 -4
  25. package/dist/mcp/tool-registry.js +37 -7
  26. package/dist/mcp/tools/activity.js +99 -7
  27. package/dist/mcp/tools/app-scaffold.js +304 -336
  28. package/dist/mcp/tools/company.d.ts +9 -0
  29. package/dist/mcp/tools/company.js +88 -0
  30. package/dist/mcp/tools/discussion.js +68 -0
  31. package/dist/mcp/tools/workflow-permissions.d.ts +15 -0
  32. package/dist/mcp/tools/workflow-permissions.js +204 -0
  33. package/dist/mcp/tools/workflow.js +57 -18
  34. package/dist/mcp/utils/index.d.ts +2 -0
  35. package/dist/mcp/utils/index.js +12 -1
  36. package/dist/mcp/utils/role-utils.d.ts +74 -0
  37. package/dist/mcp/utils/role-utils.js +151 -0
  38. package/dist/mcp/utils/types.d.ts +43 -1
  39. package/dist/mcp/utils/types.js +14 -0
  40. package/dist/mcp/webhook-handler.d.ts +6 -0
  41. package/dist/mcp/webhook-handler.js +11 -0
  42. package/dist/mcp-server.d.ts +23 -2
  43. package/dist/mcp-server.js +639 -111
  44. package/dist/plugins/vipunen/client.d.ts +150 -0
  45. package/dist/plugins/vipunen/client.js +535 -0
  46. package/dist/plugins/vipunen/config/schema-config.json +19 -0
  47. package/dist/plugins/vipunen/config/schema-doc.json +22 -0
  48. package/dist/plugins/vipunen/index.d.ts +41 -0
  49. package/dist/plugins/vipunen/index.js +88 -0
  50. package/dist/plugins/vipunen/tools.d.ts +26 -0
  51. package/dist/plugins/vipunen/tools.js +501 -0
  52. package/package.json +2 -1
  53. package/.claude/.context-watchdog.json +0 -1
  54. package/.claude/.session-checked +0 -1
  55. package/.claude/CLAUDE.md +0 -370
  56. package/.claude/agents/agent-ada-skill-builder.md +0 -94
  57. package/.claude/agents/agent-alejandro-function-fields.md +0 -342
  58. package/.claude/agents/agent-bjorn-config-audit.md +0 -103
  59. package/.claude/agents/agent-builder-agent-creator.md +0 -130
  60. package/.claude/agents/agent-code-simplifier.md +0 -53
  61. package/.claude/agents/agent-dmitri-activity-crud.md +0 -159
  62. package/.claude/agents/agent-giuseppe-app-builder.md +0 -247
  63. package/.claude/agents/agent-gunther-mcp-tools.md +0 -39
  64. package/.claude/agents/agent-helga-workflow-config.md +0 -204
  65. package/.claude/agents/agent-igor-activity-mover-automation.md +0 -125
  66. package/.claude/agents/agent-ingrid-doc-templates.md +0 -261
  67. package/.claude/agents/agent-ivan-monolith.md +0 -154
  68. package/.claude/agents/agent-kenji-data-reader.md +0 -86
  69. package/.claude/agents/agent-lars-code-inspector.md +0 -102
  70. package/.claude/agents/agent-marco-mockup-builder.md +0 -110
  71. package/.claude/agents/agent-marcus-api-documenter.md +0 -323
  72. package/.claude/agents/agent-marketplace-publisher.md +0 -280
  73. package/.claude/agents/agent-marketplace-reviewer.md +0 -309
  74. package/.claude/agents/agent-permissions-handler.md +0 -208
  75. package/.claude/agents/agent-simple-writer.md +0 -48
  76. package/.claude/agents/agent-svetlana-code-review.md +0 -171
  77. package/.claude/agents/agent-tanya-test-runner.md +0 -333
  78. package/.claude/agents/agent-ui-designer.md +0 -100
  79. package/.claude/agents/agent-viktor-sql-insights.md +0 -212
  80. package/.claude/agents/agent-web-search.md +0 -55
  81. package/.claude/agents/agent-yevgeni-discussions.md +0 -45
  82. package/.claude/agents/agent-zara-zapier.md +0 -159
  83. package/.claude/commands/app-squad.md +0 -135
  84. package/.claude/commands/audit-squad.md +0 -158
  85. package/.claude/commands/autoplan.md +0 -563
  86. package/.claude/commands/cleanup-squad.md +0 -98
  87. package/.claude/commands/config-squad.md +0 -106
  88. package/.claude/commands/crud-squad.md +0 -87
  89. package/.claude/commands/data-squad.md +0 -97
  90. package/.claude/commands/debug-squad.md +0 -303
  91. package/.claude/commands/doc-squad.md +0 -65
  92. package/.claude/commands/handoff.md +0 -137
  93. package/.claude/commands/health.md +0 -49
  94. package/.claude/commands/help.md +0 -29
  95. package/.claude/commands/help:agents.md +0 -151
  96. package/.claude/commands/help:commands.md +0 -78
  97. package/.claude/commands/help:faq.md +0 -79
  98. package/.claude/commands/help:plugins.md +0 -50
  99. package/.claude/commands/help:skills.md +0 -93
  100. package/.claude/commands/help:tools.md +0 -75
  101. package/.claude/commands/hotfix-squad.md +0 -112
  102. package/.claude/commands/integration-squad.md +0 -82
  103. package/.claude/commands/janitor-squad.md +0 -167
  104. package/.claude/commands/learn-auto.md +0 -120
  105. package/.claude/commands/learn.md +0 -120
  106. package/.claude/commands/mcp-list.md +0 -27
  107. package/.claude/commands/onboard-squad.md +0 -140
  108. package/.claude/commands/plan-workspace.md +0 -732
  109. package/.claude/commands/prd.md +0 -130
  110. package/.claude/commands/project-status.md +0 -82
  111. package/.claude/commands/publish.md +0 -138
  112. package/.claude/commands/recap.md +0 -69
  113. package/.claude/commands/restore.md +0 -64
  114. package/.claude/commands/review-squad.md +0 -152
  115. package/.claude/commands/save.md +0 -24
  116. package/.claude/commands/stats.md +0 -19
  117. package/.claude/commands/swarm.md +0 -210
  118. package/.claude/commands/tool-builder.md +0 -39
  119. package/.claude/commands/ws-pull.md +0 -44
  120. package/.claude/hooks/_shared-memory.cjs +0 -305
  121. package/.claude/hooks/_utils.cjs +0 -108
  122. package/.claude/hooks/agent-failure-detector.cjs +0 -383
  123. package/.claude/hooks/agent-usage-logger.cjs +0 -204
  124. package/.claude/hooks/app-edit-guard.cjs +0 -494
  125. package/.claude/hooks/auto-learn.cjs +0 -304
  126. package/.claude/hooks/bash-guard.cjs +0 -272
  127. package/.claude/hooks/builder-mode-manager.cjs +0 -354
  128. package/.claude/hooks/bulk-activity-guard.cjs +0 -271
  129. package/.claude/hooks/context-watchdog.cjs +0 -230
  130. package/.claude/hooks/delegation-reminder.cjs +0 -465
  131. package/.claude/hooks/design-system-lint.cjs +0 -271
  132. package/.claude/hooks/post-scaffold-hook.cjs +0 -181
  133. package/.claude/hooks/prompt-guard.cjs +0 -354
  134. package/.claude/hooks/publish-template-guard.cjs +0 -147
  135. package/.claude/hooks/session-start.cjs +0 -35
  136. package/.claude/hooks/shared-memory-writer.cjs +0 -147
  137. package/.claude/hooks/skill-injector.cjs +0 -140
  138. package/.claude/hooks/skill-usage-logger.cjs +0 -258
  139. package/.claude/hooks/src-edit-guard.cjs +0 -240
  140. package/.claude/hooks/sync-marketplace-agents.cjs +0 -346
  141. package/.claude/settings.json +0 -257
  142. package/.claude/skills/SDK-activity-patterns/SKILL.md +0 -428
  143. package/.claude/skills/SDK-document-templates/SKILL.md +0 -1033
  144. package/.claude/skills/SDK-function-fields/SKILL.md +0 -542
  145. package/.claude/skills/SDK-generate-skill/SKILL.md +0 -92
  146. package/.claude/skills/SDK-init-skill/SKILL.md +0 -127
  147. package/.claude/skills/SDK-insight-queries/SKILL.md +0 -787
  148. package/.claude/skills/SDK-ws-config-skill/SKILL.md +0 -1139
  149. package/.claude/skills/agent-structure/SKILL.md +0 -98
  150. package/.claude/skills/api-documentation-patterns/SKILL.md +0 -474
  151. package/.claude/skills/chrome-mcp-reference/SKILL.md +0 -370
  152. package/.claude/skills/delegation-routing/SKILL.md +0 -202
  153. package/.claude/skills/frontend-design/SKILL.md +0 -254
  154. package/.claude/skills/hailer-activity-mover/SKILL.md +0 -213
  155. package/.claude/skills/hailer-api-client/SKILL.md +0 -518
  156. package/.claude/skills/hailer-app-builder/SKILL.md +0 -1434
  157. package/.claude/skills/hailer-apps-pictures/SKILL.md +0 -269
  158. package/.claude/skills/hailer-design-system/SKILL.md +0 -235
  159. package/.claude/skills/hailer-monolith-automations/SKILL.md +0 -686
  160. package/.claude/skills/hailer-permissions-system/SKILL.md +0 -121
  161. package/.claude/skills/hailer-project-protocol/SKILL.md +0 -488
  162. package/.claude/skills/hailer-rest-api/SKILL.md +0 -61
  163. package/.claude/skills/hailer-rest-api/hailer-activities.md +0 -184
  164. package/.claude/skills/hailer-rest-api/hailer-admin.md +0 -473
  165. package/.claude/skills/hailer-rest-api/hailer-calendar.md +0 -256
  166. package/.claude/skills/hailer-rest-api/hailer-feed.md +0 -249
  167. package/.claude/skills/hailer-rest-api/hailer-insights.md +0 -195
  168. package/.claude/skills/hailer-rest-api/hailer-messaging.md +0 -276
  169. package/.claude/skills/hailer-rest-api/hailer-workflows.md +0 -283
  170. package/.claude/skills/insight-join-patterns/SKILL.md +0 -174
  171. package/.claude/skills/integration-patterns/SKILL.md +0 -421
  172. package/.claude/skills/json-only-output/SKILL.md +0 -72
  173. package/.claude/skills/lsp-setup/SKILL.md +0 -160
  174. package/.claude/skills/mcp-direct-tools/SKILL.md +0 -153
  175. package/.claude/skills/optional-parameters/SKILL.md +0 -72
  176. package/.claude/skills/publish-hailer-app/SKILL.md +0 -244
  177. package/.claude/skills/testing-patterns/SKILL.md +0 -630
  178. package/.claude/skills/tool-builder/SKILL.md +0 -250
  179. package/.claude/skills/tool-parameter-usage/SKILL.md +0 -126
  180. package/.claude/skills/tool-response-verification/SKILL.md +0 -92
  181. package/.claude/skills/zapier-hailer-patterns/SKILL.md +0 -581
  182. package/.hailer-mcp-port +0 -1
  183. package/.mcp.json +0 -13
  184. package/.opencode/agent/agent-ada-skill-builder.md +0 -35
  185. package/.opencode/agent/agent-alejandro-function-fields.md +0 -39
  186. package/.opencode/agent/agent-bjorn-config-audit.md +0 -36
  187. package/.opencode/agent/agent-builder-agent-creator.md +0 -39
  188. package/.opencode/agent/agent-code-simplifier.md +0 -31
  189. package/.opencode/agent/agent-dmitri-activity-crud.md +0 -40
  190. package/.opencode/agent/agent-giuseppe-app-builder.md +0 -37
  191. package/.opencode/agent/agent-gunther-mcp-tools.md +0 -39
  192. package/.opencode/agent/agent-helga-workflow-config.md +0 -204
  193. package/.opencode/agent/agent-igor-activity-mover-automation.md +0 -46
  194. package/.opencode/agent/agent-ingrid-doc-templates.md +0 -39
  195. package/.opencode/agent/agent-ivan-monolith.md +0 -46
  196. package/.opencode/agent/agent-kenji-data-reader.md +0 -53
  197. package/.opencode/agent/agent-lars-code-inspector.md +0 -28
  198. package/.opencode/agent/agent-marco-mockup-builder.md +0 -42
  199. package/.opencode/agent/agent-marcus-api-documenter.md +0 -53
  200. package/.opencode/agent/agent-marketplace-publisher.md +0 -44
  201. package/.opencode/agent/agent-marketplace-reviewer.md +0 -42
  202. package/.opencode/agent/agent-permissions-handler.md +0 -50
  203. package/.opencode/agent/agent-simple-writer.md +0 -45
  204. package/.opencode/agent/agent-svetlana-code-review.md +0 -39
  205. package/.opencode/agent/agent-tanya-test-runner.md +0 -57
  206. package/.opencode/agent/agent-ui-designer.md +0 -56
  207. package/.opencode/agent/agent-viktor-sql-insights.md +0 -34
  208. package/.opencode/agent/agent-web-search.md +0 -42
  209. package/.opencode/agent/agent-yevgeni-discussions.md +0 -37
  210. package/.opencode/agent/agent-zara-zapier.md +0 -53
  211. package/.opencode/commands/app-squad.md +0 -135
  212. package/.opencode/commands/audit-squad.md +0 -158
  213. package/.opencode/commands/autoplan.md +0 -563
  214. package/.opencode/commands/cleanup-squad.md +0 -98
  215. package/.opencode/commands/config-squad.md +0 -106
  216. package/.opencode/commands/crud-squad.md +0 -87
  217. package/.opencode/commands/data-squad.md +0 -97
  218. package/.opencode/commands/debug-squad.md +0 -303
  219. package/.opencode/commands/doc-squad.md +0 -65
  220. package/.opencode/commands/handoff.md +0 -137
  221. package/.opencode/commands/health.md +0 -49
  222. package/.opencode/commands/help-agents.md +0 -151
  223. package/.opencode/commands/help-commands.md +0 -32
  224. package/.opencode/commands/help-faq.md +0 -29
  225. package/.opencode/commands/help-plugins.md +0 -28
  226. package/.opencode/commands/help-skills.md +0 -7
  227. package/.opencode/commands/help-tools.md +0 -40
  228. package/.opencode/commands/help.md +0 -28
  229. package/.opencode/commands/hotfix-squad.md +0 -112
  230. package/.opencode/commands/integration-squad.md +0 -82
  231. package/.opencode/commands/janitor-squad.md +0 -167
  232. package/.opencode/commands/learn-auto.md +0 -120
  233. package/.opencode/commands/learn.md +0 -120
  234. package/.opencode/commands/mcp-list.md +0 -27
  235. package/.opencode/commands/onboard-squad.md +0 -140
  236. package/.opencode/commands/plan-workspace.md +0 -732
  237. package/.opencode/commands/prd.md +0 -131
  238. package/.opencode/commands/project-status.md +0 -82
  239. package/.opencode/commands/publish.md +0 -138
  240. package/.opencode/commands/recap.md +0 -69
  241. package/.opencode/commands/restore.md +0 -64
  242. package/.opencode/commands/review-squad.md +0 -152
  243. package/.opencode/commands/save.md +0 -24
  244. package/.opencode/commands/stats.md +0 -19
  245. package/.opencode/commands/swarm.md +0 -210
  246. package/.opencode/commands/tool-builder.md +0 -39
  247. package/.opencode/commands/ws-pull.md +0 -44
  248. package/.opencode/opencode.json +0 -21
  249. package/inbox/failures.log +0 -1
  250. package/inbox/usage.jsonl +0 -4
  251. package/scripts/postinstall.cjs +0 -64
  252. package/scripts/test-hal-tools.ts +0 -154
@@ -1,494 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * <hook-name>app-edit-guard</hook-name>
4
- *
5
- * <purpose>
6
- * Blocks direct edits to Hailer app directories.
7
- * Forces use of builder agents for app development.
8
- * </purpose>
9
- *
10
- * <triggers>
11
- * - PreToolUse on Write and Edit tools
12
- * - Only when file is inside a detected Hailer app directory
13
- * </triggers>
14
- *
15
- * <detection>
16
- * A directory is a Hailer app if it contains:
17
- * - public/manifest.json with "appId" field
18
- * - package.json with @hailer/app-sdk dependency
19
- * - vite.config.ts referencing hailer
20
- * </detection>
21
- *
22
- * <allowed-when>
23
- * - Builder agent is active (/tmp/.claude-builder-agent-active exists)
24
- * - Builder mode enabled for specific app (--builder-on)
25
- * - App released for manual editing (--release)
26
- * </allowed-when>
27
- *
28
- * <cli-commands>
29
- * --agent-on/off: Global builder mode (affects all apps + src/)
30
- * --builder-on/off: Per-app builder mode
31
- * --release/revoke: Manual editing permission
32
- * --check: Verify if path is Hailer app
33
- * --list: Show released apps
34
- * </cli-commands>
35
- *
36
- * <shared-files>
37
- * /tmp/.claude-builder-agent-active (shared with builder-mode-manager, src-edit-guard)
38
- * /tmp/.claude-released-apps.json
39
- * /tmp/.claude-builder-mode/
40
- * </shared-files>
41
- */
42
-
43
- const fs = require('fs');
44
- const path = require('path');
45
- const os = require('os');
46
-
47
- // Skip in subagent context - subagents can't use AskUserQuestion or Bash to recover
48
- // Orchestrator should use --agent-on before spawning, but this is a safety fallback
49
- if (process.env.CLAUDE_AGENT_ID || process.env.CLAUDE_SUBAGENT) {
50
- console.log(JSON.stringify({ decision: 'allow' }));
51
- process.exit(0);
52
- }
53
-
54
- const TEMP_DIR = os.tmpdir();
55
- const RELEASE_TRACKER = path.join(TEMP_DIR, '.claude-released-apps.json');
56
- const BUILDER_MODE_DIR = path.join(TEMP_DIR, '.claude-builder-mode');
57
- const BUILDER_AGENT_ACTIVE = path.join(TEMP_DIR, '.claude-builder-agent-active');
58
-
59
- // Read hook input from stdin
60
- let input = '';
61
- process.stdin.setEncoding('utf8');
62
- process.stdin.on('data', chunk => input += chunk);
63
- process.stdin.on('end', () => {
64
- try {
65
- const data = JSON.parse(input);
66
- processHook(data);
67
- } catch (e) {
68
- // Invalid JSON - fail safe by BLOCKING
69
- outputBlock('Hook received invalid input - blocking for safety');
70
- }
71
- });
72
-
73
- function outputAllow() {
74
- console.log(JSON.stringify({ decision: 'allow' }));
75
- process.exit(0);
76
- }
77
-
78
- function outputBlock(message) {
79
- console.log(JSON.stringify({
80
- decision: 'block',
81
- reason: message
82
- }));
83
- process.exit(0);
84
- }
85
-
86
- /**
87
- * Check if a directory is a Hailer app by examining its contents
88
- */
89
- function isHailerAppDirectory(dirPath) {
90
- try {
91
- // Check 1: public/manifest.json with appId
92
- const manifestPath = path.join(dirPath, 'public', 'manifest.json');
93
- if (fs.existsSync(manifestPath)) {
94
- try {
95
- const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
96
- if (manifest.appId || manifest.name) {
97
- return { isApp: true, name: manifest.name || path.basename(dirPath), reason: 'manifest.json' };
98
- }
99
- } catch {
100
- // Invalid JSON in manifest, still might be a Hailer app structure
101
- }
102
- }
103
-
104
- // Check 2: package.json with @hailer/app-sdk
105
- const packagePath = path.join(dirPath, 'package.json');
106
- if (fs.existsSync(packagePath)) {
107
- try {
108
- const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
109
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
110
- if (deps['@hailer/app-sdk']) {
111
- return { isApp: true, name: pkg.name || path.basename(dirPath), reason: '@hailer/app-sdk dependency' };
112
- }
113
- } catch {
114
- // Invalid package.json
115
- }
116
- }
117
-
118
- // Check 3: vite.config with hailer in comments or specific CORS config
119
- const viteConfigPath = path.join(dirPath, 'vite.config.ts');
120
- if (fs.existsSync(viteConfigPath)) {
121
- try {
122
- const viteConfig = fs.readFileSync(viteConfigPath, 'utf8');
123
- if (viteConfig.includes('hailer') || viteConfig.includes('app.hailer.com')) {
124
- return { isApp: true, name: path.basename(dirPath), reason: 'vite.config.ts hailer reference' };
125
- }
126
- } catch {
127
- // Can't read vite config
128
- }
129
- }
130
-
131
- return { isApp: false };
132
- } catch {
133
- return { isApp: false };
134
- }
135
- }
136
-
137
- /**
138
- * Walk up the directory tree to find if file is inside a Hailer app
139
- */
140
- function findHailerAppRoot(filePath) {
141
- let currentDir = path.dirname(filePath);
142
- const root = path.parse(currentDir).root;
143
-
144
- // Walk up max 10 levels
145
- for (let i = 0; i < 10 && currentDir !== root; i++) {
146
- const result = isHailerAppDirectory(currentDir);
147
- if (result.isApp) {
148
- return { ...result, path: currentDir };
149
- }
150
- currentDir = path.dirname(currentDir);
151
- }
152
-
153
- return null;
154
- }
155
-
156
- /**
157
- * Check if an app directory has been released for manual editing
158
- */
159
- function isAppReleased(appPath) {
160
- try {
161
- if (!fs.existsSync(RELEASE_TRACKER)) {
162
- return false;
163
- }
164
- const released = JSON.parse(fs.readFileSync(RELEASE_TRACKER, 'utf8'));
165
- return released.includes(path.resolve(appPath));
166
- } catch {
167
- return false;
168
- }
169
- }
170
-
171
- /**
172
- * Check if a builder agent is currently active (global flag)
173
- * Main Claude enables this before spawning builder agents
174
- */
175
- function isBuilderAgentActive() {
176
- return fs.existsSync(BUILDER_AGENT_ACTIVE);
177
- }
178
-
179
- /**
180
- * Check if builder mode is active for an app directory
181
- * Builder mode is enabled by creating a marker file in BUILDER_MODE_DIR
182
- */
183
- function isBuilderModeActive(appPath) {
184
- try {
185
- if (!fs.existsSync(BUILDER_MODE_DIR)) {
186
- return false;
187
- }
188
- // Hash the app path to create a unique marker filename
189
- const normalizedPath = path.resolve(appPath);
190
- const markerFile = path.join(BUILDER_MODE_DIR, Buffer.from(normalizedPath).toString('base64').replace(/[/+=]/g, '_'));
191
- return fs.existsSync(markerFile);
192
- } catch {
193
- return false;
194
- }
195
- }
196
-
197
- /**
198
- * Enable builder mode for an app directory
199
- */
200
- function enableBuilderMode(appPath) {
201
- const normalizedPath = path.resolve(appPath);
202
- if (!fs.existsSync(BUILDER_MODE_DIR)) {
203
- fs.mkdirSync(BUILDER_MODE_DIR, { recursive: true });
204
- }
205
- const markerFile = path.join(BUILDER_MODE_DIR, Buffer.from(normalizedPath).toString('base64').replace(/[/+=]/g, '_'));
206
- fs.writeFileSync(markerFile, JSON.stringify({ appPath: normalizedPath, enabledAt: new Date().toISOString() }));
207
- return markerFile;
208
- }
209
-
210
- /**
211
- * Disable builder mode for an app directory
212
- */
213
- function disableBuilderMode(appPath) {
214
- const normalizedPath = path.resolve(appPath);
215
- const markerFile = path.join(BUILDER_MODE_DIR, Buffer.from(normalizedPath).toString('base64').replace(/[/+=]/g, '_'));
216
- if (fs.existsSync(markerFile)) {
217
- fs.unlinkSync(markerFile);
218
- return true;
219
- }
220
- return false;
221
- }
222
-
223
- function processHook(data) {
224
- const { tool_name, tool_input } = data;
225
-
226
- // Only guard Write and Edit tools
227
- if (tool_name !== 'Write' && tool_name !== 'Edit') {
228
- outputAllow();
229
- return;
230
- }
231
-
232
- const filePath = tool_input?.file_path;
233
- if (!filePath) {
234
- outputAllow();
235
- return;
236
- }
237
-
238
- // Normalize path
239
- const normalizedPath = path.resolve(filePath);
240
-
241
- // Find if this file is inside a Hailer app
242
- const appInfo = findHailerAppRoot(normalizedPath);
243
-
244
- if (!appInfo) {
245
- // Not in a Hailer app directory
246
- outputAllow();
247
- return;
248
- }
249
-
250
- // Check if a builder agent is globally active - ALLOW
251
- // Main Claude enables this before spawning builder agents
252
- if (isBuilderAgentActive()) {
253
- outputAllow();
254
- return;
255
- }
256
-
257
- // Check if builder mode is active for this app - ALLOW
258
- // This is the primary mechanism for spawned builder agents
259
- if (isBuilderModeActive(appInfo.path)) {
260
- outputAllow();
261
- return;
262
- }
263
-
264
- // Check if this app has been released for manual editing - ALLOW
265
- if (isAppReleased(appInfo.path)) {
266
- outputAllow();
267
- return;
268
- }
269
-
270
- // BLOCK with helpful message
271
- outputBlock(`
272
- đŸšĢ BLOCKED: Direct edit to Hailer app "${appInfo.name}"
273
-
274
- Detected as Hailer app via: ${appInfo.reason}
275
- App directory: ${appInfo.path}
276
-
277
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
278
- WHY: Hailer apps must be built by a specialized builder agent
279
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
280
-
281
- The builder agent has:
282
- â€ĸ Access to @hailer/app-sdk documentation
283
- â€ĸ Live dev server feedback for iteration
284
- â€ĸ Proper TypeScript patterns for Hailer apps
285
-
286
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
287
- TO PROCEED: Spawn a builder agent
288
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
289
-
290
- 1. Load the spawn skill:
291
- Skill("spawn-app-builder")
292
-
293
- 2. Follow the skill instructions to spawn the agent
294
-
295
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
296
- OR: Release for manual editing (if user explicitly requests)
297
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
298
-
299
- Ask the user first! If they confirm manual editing, run:
300
- Bash: node "${process.argv[1].replace(/'/g, "'\\''")}" --release "${appInfo.path}"
301
-
302
- Then retry your edit.
303
- `);
304
- }
305
-
306
- // CLI: Release an app for manual editing
307
- if (process.argv[2] === '--release' && process.argv[3]) {
308
- const appPath = path.resolve(process.argv[3]);
309
-
310
- // Verify it's actually a Hailer app
311
- const appInfo = isHailerAppDirectory(appPath);
312
- if (!appInfo.isApp) {
313
- console.error(`Error: ${appPath} is not a Hailer app directory`);
314
- process.exit(1);
315
- }
316
-
317
- // Load existing releases
318
- let released = [];
319
- try {
320
- if (fs.existsSync(RELEASE_TRACKER)) {
321
- released = JSON.parse(fs.readFileSync(RELEASE_TRACKER, 'utf8'));
322
- }
323
- } catch {
324
- released = [];
325
- }
326
-
327
- // Add this app if not already released
328
- if (!released.includes(appPath)) {
329
- released.push(appPath);
330
- fs.writeFileSync(RELEASE_TRACKER, JSON.stringify(released, null, 2));
331
- console.log(`✅ Released "${appInfo.name}" for manual editing`);
332
- console.log(` Path: ${appPath}`);
333
- } else {
334
- console.log(`â„šī¸ "${appInfo.name}" was already released`);
335
- }
336
- process.exit(0);
337
- }
338
-
339
- // CLI: List released apps
340
- if (process.argv[2] === '--list') {
341
- try {
342
- if (fs.existsSync(RELEASE_TRACKER)) {
343
- const released = JSON.parse(fs.readFileSync(RELEASE_TRACKER, 'utf8'));
344
- if (released.length === 0) {
345
- console.log('No apps released for manual editing');
346
- } else {
347
- console.log('Released apps:');
348
- released.forEach(p => console.log(` - ${p}`));
349
- }
350
- } else {
351
- console.log('No apps released for manual editing');
352
- }
353
- } catch {
354
- console.log('No apps released for manual editing');
355
- }
356
- process.exit(0);
357
- }
358
-
359
- // CLI: Enable builder mode for an app
360
- if (process.argv[2] === '--builder-on' && process.argv[3]) {
361
- const appPath = path.resolve(process.argv[3]);
362
-
363
- // Verify it's actually a Hailer app
364
- const appInfo = isHailerAppDirectory(appPath);
365
- if (!appInfo.isApp) {
366
- console.error(`Error: ${appPath} is not a Hailer app directory`);
367
- process.exit(1);
368
- }
369
-
370
- enableBuilderMode(appPath);
371
- console.log(`🔧 Builder mode ENABLED for "${appInfo.name}"`);
372
- console.log(` Path: ${appPath}`);
373
- console.log(` Spawned agents can now edit this app`);
374
- process.exit(0);
375
- }
376
-
377
- // CLI: Disable builder mode for an app
378
- if (process.argv[2] === '--builder-off' && process.argv[3]) {
379
- const appPath = path.resolve(process.argv[3]);
380
-
381
- if (disableBuilderMode(appPath)) {
382
- console.log(`🔒 Builder mode DISABLED for: ${appPath}`);
383
- } else {
384
- console.log(`â„šī¸ Builder mode was not active for: ${appPath}`);
385
- }
386
- process.exit(0);
387
- }
388
-
389
- // CLI: Revoke release
390
- if (process.argv[2] === '--revoke' && process.argv[3]) {
391
- const appPath = path.resolve(process.argv[3]);
392
-
393
- try {
394
- if (fs.existsSync(RELEASE_TRACKER)) {
395
- let released = JSON.parse(fs.readFileSync(RELEASE_TRACKER, 'utf8'));
396
- const before = released.length;
397
- released = released.filter(p => p !== appPath);
398
- if (released.length < before) {
399
- fs.writeFileSync(RELEASE_TRACKER, JSON.stringify(released, null, 2));
400
- console.log(`✅ Revoked manual editing for: ${appPath}`);
401
- } else {
402
- console.log(`â„šī¸ App was not in released list: ${appPath}`);
403
- }
404
- }
405
- } catch {
406
- console.error('Error reading release tracker');
407
- }
408
- process.exit(0);
409
- }
410
-
411
- // CLI: Check if a path is a Hailer app
412
- if (process.argv[2] === '--check' && process.argv[3]) {
413
- const checkPath = path.resolve(process.argv[3]);
414
- const result = isHailerAppDirectory(checkPath);
415
- if (result.isApp) {
416
- console.log(`✅ Hailer app detected: ${result.name}`);
417
- console.log(` Detected via: ${result.reason}`);
418
- console.log(` Builder mode: ${isBuilderModeActive(checkPath) ? 'ACTIVE' : 'Off'}`);
419
- console.log(` Manual release: ${isAppReleased(checkPath) ? 'Yes' : 'No'}`);
420
- } else {
421
- console.log(`❌ Not a Hailer app: ${checkPath}`);
422
- }
423
- process.exit(0);
424
- }
425
-
426
- // CLI: Enable global builder agent mode
427
- if (process.argv[2] === '--agent-on') {
428
- fs.writeFileSync(BUILDER_AGENT_ACTIVE, JSON.stringify({ enabledAt: new Date().toISOString() }));
429
- console.log('🔧 Builder agent mode ENABLED globally');
430
- console.log(' All Hailer app edits are now allowed');
431
- console.log(' Run --agent-off when done');
432
- process.exit(0);
433
- }
434
-
435
- // CLI: Disable global builder agent mode
436
- if (process.argv[2] === '--agent-off') {
437
- if (fs.existsSync(BUILDER_AGENT_ACTIVE)) {
438
- fs.unlinkSync(BUILDER_AGENT_ACTIVE);
439
- console.log('🔒 Builder agent mode DISABLED');
440
- } else {
441
- console.log('â„šī¸ Builder agent mode was not active');
442
- }
443
- process.exit(0);
444
- }
445
-
446
- // CLI: Check global builder agent status
447
- if (process.argv[2] === '--agent-status') {
448
- if (fs.existsSync(BUILDER_AGENT_ACTIVE)) {
449
- console.log('🔧 Builder agent mode is ACTIVE');
450
- } else {
451
- console.log('🔒 Builder agent mode is OFF');
452
- }
453
- process.exit(0);
454
- }
455
-
456
- // CLI: Help
457
- if (process.argv[2] === '--help' || process.argv[2] === '-h') {
458
- console.log(`
459
- Hailer App Edit Guard - Bulletproof protection for Hailer apps
460
-
461
- UNIFIED SYSTEM: --agent-on/--agent-off controls BOTH this hook AND src-edit-guard.cjs.
462
- One command enables builder mode for Hailer apps AND src/ directory edits.
463
-
464
- Usage:
465
- Global Builder Agent Mode (RECOMMENDED for spawning agents):
466
- node app-edit-guard.cjs --agent-on Enable global builder mode
467
- node app-edit-guard.cjs --agent-off Disable global builder mode
468
- node app-edit-guard.cjs --agent-status Check if builder mode is active
469
-
470
- Per-App Builder Mode:
471
- node app-edit-guard.cjs --builder-on <path> Enable builder mode for an app
472
- node app-edit-guard.cjs --builder-off <path> Disable builder mode for an app
473
-
474
- Manual Editing (for direct edits by main agent):
475
- node app-edit-guard.cjs --release <path> Release an app for manual editing
476
- node app-edit-guard.cjs --revoke <path> Revoke manual editing permission
477
-
478
- Utilities:
479
- node app-edit-guard.cjs --check <path> Check if path is a Hailer app
480
- node app-edit-guard.cjs --list List all released apps
481
- node app-edit-guard.cjs --help Show this help
482
-
483
- Workflow for spawning builder agents (RECOMMENDED):
484
- 1. Main agent: node app-edit-guard.cjs --agent-on
485
- 2. Main agent spawns builder agent via Task tool
486
- 3. Builder agent can freely edit ANY Hailer app AND src/ files
487
- 4. Main agent: node app-edit-guard.cjs --agent-off
488
-
489
- As a hook:
490
- Reads JSON from stdin with tool_name and tool_input
491
- Outputs JSON with decision: "allow" or "block"
492
- `);
493
- process.exit(0);
494
- }