@hailer/mcp 1.1.12 → 1.1.14

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 (269) 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
@@ -1,140 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * <hook-name>skill-injector</hook-name>
4
- * <purpose>Auto-injects skill content into subagent context via SubagentStart</purpose>
5
- * <triggers>SubagentStart</triggers>
6
- *
7
- * Problem: Agents have skills listed in frontmatter but never actually call
8
- * the Skill tool. This hook reads the agent's frontmatter `skills:` list
9
- * and injects skill content via additionalContext when the subagent starts.
10
- *
11
- * Uses SubagentStart hook event with additionalContext (not PreToolUse updatedInput).
12
- *
13
- * Hook type: SubagentStart
14
- */
15
-
16
- const fs = require('fs');
17
- const path = require('path');
18
-
19
- const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
20
- const AGENTS_DIR = path.join(PROJECT_DIR, '.claude', 'agents');
21
- const SKILLS_DIR = path.join(PROJECT_DIR, '.claude', 'skills');
22
-
23
- // Skip if stdin is TTY
24
- if (process.stdin.isTTY) {
25
- console.log(JSON.stringify({}));
26
- process.exit(0);
27
- }
28
-
29
- let input = '';
30
- process.stdin.setEncoding('utf8');
31
- process.stdin.on('data', chunk => input += chunk);
32
- process.stdin.on('end', () => {
33
- try {
34
- const data = JSON.parse(input);
35
- processHook(data);
36
- } catch (e) {
37
- console.error(`[skill-injector] Parse error: ${e.message}`);
38
- console.log(JSON.stringify({}));
39
- process.exit(0);
40
- }
41
- });
42
-
43
- /**
44
- * Parse skills: list from agent frontmatter
45
- */
46
- function getAgentSkills(agentType) {
47
- const agentFile = path.join(AGENTS_DIR, `${agentType}.md`);
48
- if (!fs.existsSync(agentFile)) {
49
- return [];
50
- }
51
-
52
- try {
53
- const content = fs.readFileSync(agentFile, 'utf8');
54
- const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
55
- if (!fmMatch) return [];
56
-
57
- const frontmatter = fmMatch[1];
58
- const skills = [];
59
- let inSkills = false;
60
-
61
- for (const line of frontmatter.split('\n')) {
62
- if (line.match(/^skills:\s*$/)) {
63
- inSkills = true;
64
- continue;
65
- }
66
- if (inSkills) {
67
- const skillMatch = line.match(/^\s+-\s+(.+)$/);
68
- if (skillMatch) {
69
- skills.push(skillMatch[1].trim());
70
- } else {
71
- break;
72
- }
73
- }
74
- }
75
-
76
- return skills;
77
- } catch (e) {
78
- console.error(`[skill-injector] Error reading ${agentFile}: ${e.message}`);
79
- return [];
80
- }
81
- }
82
-
83
- /**
84
- * Read skill content from SKILL.md
85
- */
86
- function readSkillContent(skillName) {
87
- const skillFile = path.join(SKILLS_DIR, skillName, 'SKILL.md');
88
- if (!fs.existsSync(skillFile)) {
89
- return null;
90
- }
91
-
92
- try {
93
- const content = fs.readFileSync(skillFile, 'utf8');
94
- // Limit to 20KB per skill to avoid bloating context
95
- if (content.length > 20000) {
96
- return content.substring(0, 20000) + '\n\n[... truncated at 20KB ...]';
97
- }
98
- return content;
99
- } catch (e) {
100
- console.error(`[skill-injector] Error reading skill ${skillName}: ${e.message}`);
101
- return null;
102
- }
103
- }
104
-
105
- function processHook(data) {
106
- const agentType = data.agent_type || '';
107
-
108
- // Get skills for this agent type
109
- const skills = getAgentSkills(agentType);
110
- if (skills.length === 0) {
111
- console.log(JSON.stringify({}));
112
- process.exit(0);
113
- }
114
-
115
- // Load skill contents
116
- const skillBlocks = [];
117
- for (const skillName of skills) {
118
- const content = readSkillContent(skillName);
119
- if (content) {
120
- skillBlocks.push(`<skill name="${skillName}">\n${content}\n</skill>`);
121
- }
122
- }
123
-
124
- if (skillBlocks.length === 0) {
125
- console.log(JSON.stringify({}));
126
- process.exit(0);
127
- }
128
-
129
- const skillSection = `<injected-skills>\nThe following skills have been auto-loaded based on your agent configuration. Use this knowledge for your task.\n\n${skillBlocks.join('\n\n')}\n</injected-skills>`;
130
-
131
- console.error(`[skill-injector] Injected ${skillBlocks.length} skill(s) into ${agentType}: ${skills.join(', ')}`);
132
-
133
- console.log(JSON.stringify({
134
- hookSpecificOutput: {
135
- hookEventName: 'SubagentStart',
136
- additionalContext: skillSection
137
- }
138
- }));
139
- process.exit(0);
140
- }
@@ -1,258 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * <hook-name>skill-usage-logger</hook-name>
4
- *
5
- * <purpose>
6
- * Logs every skill load for analytics - which skills are used, by which agents, in which projects.
7
- * </purpose>
8
- *
9
- * <triggers>
10
- * - PostToolUse on Skill
11
- * </triggers>
12
- *
13
- * <log-file>$CLAUDE_PROJECT_DIR/inbox/skill-usage.jsonl</log-file>
14
- */
15
-
16
- const fs = require('fs');
17
- const path = require('path');
18
- const os = require('os');
19
-
20
- const LOG_FILE = path.join(
21
- process.env.HAILER_INBOX_DIR || path.join(process.env.CLAUDE_PROJECT_DIR || process.cwd(), 'inbox'),
22
- 'skill-usage.jsonl'
23
- );
24
-
25
- // Read hook input from stdin
26
- let input = '';
27
- process.stdin.setEncoding('utf8');
28
- process.stdin.on('data', chunk => input += chunk);
29
- process.stdin.on('end', () => {
30
- try {
31
- const data = JSON.parse(input);
32
- processHook(data);
33
- } catch (e) {
34
- console.error(`[skill-usage-logger] Failed to parse input: ${e.message}`);
35
- process.exit(0);
36
- }
37
- });
38
-
39
- /**
40
- * Detect if running in subagent context from transcript_path
41
- * Subagent paths contain /subagents/
42
- * Returns: { isSubagent: boolean, agentName: string | null }
43
- */
44
- function detectSubagentContext(transcriptPath) {
45
- if (!transcriptPath) {
46
- return { isSubagent: false, agentName: null };
47
- }
48
-
49
- // Subagent paths look like: .../{session_id}/subagents/agent-{agent_id}.jsonl
50
- if (transcriptPath.includes('/subagents/')) {
51
- // Extract agent name from path
52
- const match = transcriptPath.match(/subagents\/([^/]+)\.jsonl$/);
53
- if (match) {
54
- // Clean up agent ID - might be like "agent-a7004fa" or full name
55
- return { isSubagent: true, agentName: match[1] };
56
- }
57
- return { isSubagent: true, agentName: 'unknown' };
58
- }
59
-
60
- return { isSubagent: false, agentName: null };
61
- }
62
-
63
- function processHook(data) {
64
- const { tool_name, tool_input, tool_response, transcript_path } = data;
65
-
66
- // Only process Skill tool
67
- if (tool_name !== 'Skill') {
68
- process.exit(0);
69
- }
70
-
71
- // Extract skill name from tool_input
72
- const skillName = tool_input?.skill || tool_input?.command || 'unknown';
73
- const skillArgs = tool_input?.args || null;
74
-
75
- // Detect subagent context
76
- const { isSubagent, agentName } = detectSubagentContext(transcript_path);
77
-
78
- // Get project from env or cwd
79
- const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
80
- const project = path.basename(projectDir);
81
-
82
- const entry = {
83
- ts: new Date().toISOString(),
84
- skill: skillName,
85
- agent: isSubagent ? agentName : 'orchestrator',
86
- isSubagent,
87
- project,
88
- args: skillArgs
89
- };
90
-
91
- try {
92
- // Ensure directory exists
93
- const logDir = path.dirname(LOG_FILE);
94
- if (!fs.existsSync(logDir)) {
95
- fs.mkdirSync(logDir, { recursive: true });
96
- }
97
-
98
- fs.appendFileSync(LOG_FILE, JSON.stringify(entry) + '\n');
99
- } catch (err) {
100
- console.error(`[skill-usage-logger] Could not write: ${err.message}`);
101
- }
102
-
103
- process.exit(0);
104
- }
105
-
106
- // CLI: Show recent skill loads
107
- if (process.argv[2] === '--recent' || process.argv[2] === '-r') {
108
- const limit = parseInt(process.argv[3]) || 20;
109
-
110
- if (!fs.existsSync(LOG_FILE)) {
111
- console.log('No skill usage data yet');
112
- process.exit(0);
113
- }
114
-
115
- const lines = fs.readFileSync(LOG_FILE, 'utf8').trim().split('\n');
116
- const recent = lines.slice(-limit);
117
-
118
- console.log(`Last ${recent.length} skill loads:\n`);
119
- console.log('Time'.padEnd(10) + 'Skill'.padEnd(30) + 'Agent'.padEnd(20) + 'Project');
120
- console.log('-'.repeat(75));
121
-
122
- for (const line of recent) {
123
- try {
124
- const entry = JSON.parse(line);
125
- const time = entry.ts.split('T')[1].split('.')[0];
126
- const agent = entry.isSubagent ? entry.agent : 'orchestrator';
127
- console.log(`${time.padEnd(10)}${entry.skill.padEnd(30)}${agent.padEnd(20)}${entry.project}`);
128
- } catch {}
129
- }
130
- process.exit(0);
131
- }
132
-
133
- // CLI: Show stats by skill
134
- if (process.argv[2] === '--stats' || process.argv[2] === '-s') {
135
- if (!fs.existsSync(LOG_FILE)) {
136
- console.log('No skill usage data yet');
137
- process.exit(0);
138
- }
139
-
140
- const lines = fs.readFileSync(LOG_FILE, 'utf8').trim().split('\n');
141
- const skillStats = {};
142
- const agentSkills = {};
143
-
144
- for (const line of lines) {
145
- try {
146
- const entry = JSON.parse(line);
147
-
148
- // Count by skill
149
- if (!skillStats[entry.skill]) {
150
- skillStats[entry.skill] = { total: 0, byOrchestrator: 0, bySubagent: 0 };
151
- }
152
- skillStats[entry.skill].total++;
153
- if (entry.isSubagent) {
154
- skillStats[entry.skill].bySubagent++;
155
- } else {
156
- skillStats[entry.skill].byOrchestrator++;
157
- }
158
-
159
- // Track which agents use which skills
160
- if (entry.isSubagent && entry.agent) {
161
- if (!agentSkills[entry.agent]) {
162
- agentSkills[entry.agent] = new Set();
163
- }
164
- agentSkills[entry.agent].add(entry.skill);
165
- }
166
- } catch {}
167
- }
168
-
169
- console.log('Skill Usage Stats:\n');
170
- console.log('Skill'.padEnd(35) + 'Total'.padEnd(8) + 'Orch'.padEnd(8) + 'Subagent');
171
- console.log('-'.repeat(60));
172
-
173
- const sorted = Object.entries(skillStats).sort((a, b) => b[1].total - a[1].total);
174
- for (const [skill, s] of sorted) {
175
- console.log(`${skill.padEnd(35)}${String(s.total).padEnd(8)}${String(s.byOrchestrator).padEnd(8)}${s.bySubagent}`);
176
- }
177
-
178
- console.log('-'.repeat(60));
179
- console.log(`Total: ${lines.length} skill loads\n`);
180
-
181
- // Show agent -> skill mapping
182
- if (Object.keys(agentSkills).length > 0) {
183
- console.log('Skills by Agent:\n');
184
- for (const [agent, skills] of Object.entries(agentSkills)) {
185
- console.log(` ${agent}: ${[...skills].join(', ')}`);
186
- }
187
- }
188
-
189
- process.exit(0);
190
- }
191
-
192
- // CLI: Show unused skills (skills in .claude/skills/ not in log)
193
- if (process.argv[2] === '--unused' || process.argv[2] === '-u') {
194
- const skillsDir = path.join(process.cwd(), '.claude', 'skills');
195
-
196
- if (!fs.existsSync(skillsDir)) {
197
- console.log('No .claude/skills/ directory found');
198
- process.exit(0);
199
- }
200
-
201
- // Get all skill directories
202
- const allSkills = fs.readdirSync(skillsDir, { withFileTypes: true })
203
- .filter(d => d.isDirectory())
204
- .map(d => d.name);
205
-
206
- // Get used skills from log
207
- const usedSkills = new Set();
208
- if (fs.existsSync(LOG_FILE)) {
209
- const lines = fs.readFileSync(LOG_FILE, 'utf8').trim().split('\n');
210
- for (const line of lines) {
211
- try {
212
- const entry = JSON.parse(line);
213
- usedSkills.add(entry.skill);
214
- } catch {}
215
- }
216
- }
217
-
218
- const unused = allSkills.filter(s => !usedSkills.has(s));
219
-
220
- if (unused.length === 0) {
221
- console.log('All skills have been used at least once!');
222
- } else {
223
- console.log(`Unused skills (${unused.length}):\n`);
224
- for (const skill of unused.sort()) {
225
- console.log(` - ${skill}`);
226
- }
227
- }
228
-
229
- process.exit(0);
230
- }
231
-
232
- // CLI: Clear log
233
- if (process.argv[2] === '--clear') {
234
- if (fs.existsSync(LOG_FILE)) {
235
- fs.unlinkSync(LOG_FILE);
236
- console.log('✓ Skill usage log cleared');
237
- } else {
238
- console.log('No log file to clear');
239
- }
240
- process.exit(0);
241
- }
242
-
243
- // CLI: Help
244
- if (process.argv[2] === '--help' || process.argv[2] === '-h') {
245
- console.log(`
246
- Skill Usage Logger - Track skill loads by agents
247
-
248
- Usage:
249
- node skill-usage-logger.cjs --recent [N] Show last N skill loads (default 20)
250
- node skill-usage-logger.cjs --stats Show usage statistics
251
- node skill-usage-logger.cjs --unused Show skills never loaded
252
- node skill-usage-logger.cjs --clear Clear the log
253
- node skill-usage-logger.cjs --help Show this help
254
-
255
- Log file: ${LOG_FILE}
256
- `);
257
- process.exit(0);
258
- }
@@ -1,240 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * <hook-name>src-edit-guard</hook-name>
4
- *
5
- * <purpose>
6
- * Blocks direct edits to src/ directory.
7
- * Forces main agent to spawn subagents for MCP server code changes.
8
- * Preserves orchestrator context by delegating implementation work.
9
- * </purpose>
10
- *
11
- * <triggers>
12
- * - PreToolUse on Write and Edit tools
13
- * - Only when file path starts with src/
14
- * </triggers>
15
- *
16
- * <allowed-when>
17
- * - Builder mode is active (/tmp/.claude-builder-agent-active exists)
18
- * - File is outside src/ directory
19
- * </allowed-when>
20
- *
21
- * <behavior>
22
- * 1. Checks if file is in src/ directory
23
- * 2. Checks builder mode status
24
- * 3. Blocks with instructions to spawn general-purpose agent
25
- * </behavior>
26
- *
27
- * <shared-files>
28
- * /tmp/.claude-builder-agent-active (shared with app-edit-guard, builder-mode-manager)
29
- * </shared-files>
30
- *
31
- * <cli-commands>
32
- * --on: Enable builder mode (allow src/ edits)
33
- * --off: Disable builder mode (block src/ edits)
34
- * --status: Check current mode
35
- * </cli-commands>
36
- */
37
-
38
- const fs = require('fs');
39
- const path = require('path');
40
- const os = require('os');
41
-
42
- // Skip in subagent context - subagents can't use AskUserQuestion or Bash to recover
43
- if (process.env.CLAUDE_AGENT_ID || process.env.CLAUDE_SUBAGENT) {
44
- console.log(JSON.stringify({ decision: 'allow' }));
45
- process.exit(0);
46
- }
47
-
48
- // UNIFIED: Use same builder mode file as app-edit-guard.cjs
49
- const TEMP_DIR = os.tmpdir();
50
- const BUILDER_MODE_FILE = path.join(TEMP_DIR, '.claude-builder-agent-active');
51
-
52
- // Read hook input from stdin
53
- let input = '';
54
- process.stdin.setEncoding('utf8');
55
- process.stdin.on('data', chunk => input += chunk);
56
- process.stdin.on('end', () => {
57
- try {
58
- const data = JSON.parse(input);
59
- processHook(data);
60
- } catch (e) {
61
- // Invalid JSON - allow to avoid blocking legitimate operations
62
- outputAllow();
63
- }
64
- });
65
-
66
- function outputAllow() {
67
- console.log(JSON.stringify({ decision: 'allow' }));
68
- process.exit(0);
69
- }
70
-
71
- function outputBlock(message) {
72
- console.log(JSON.stringify({
73
- decision: 'block',
74
- reason: message
75
- }));
76
- process.exit(0);
77
- }
78
-
79
- /**
80
- * Check if builder mode is active (subagent editing)
81
- */
82
- function isBuilderModeActive() {
83
- try {
84
- return fs.existsSync(BUILDER_MODE_FILE);
85
- } catch {
86
- return false;
87
- }
88
- }
89
-
90
- /**
91
- * Enable builder mode
92
- */
93
- function enableBuilderMode() {
94
- fs.writeFileSync(BUILDER_MODE_FILE, JSON.stringify({
95
- enabledAt: new Date().toISOString(),
96
- pid: process.pid
97
- }));
98
- }
99
-
100
- /**
101
- * Disable builder mode
102
- */
103
- function disableBuilderMode() {
104
- if (fs.existsSync(BUILDER_MODE_FILE)) {
105
- fs.unlinkSync(BUILDER_MODE_FILE);
106
- return true;
107
- }
108
- return false;
109
- }
110
-
111
- function processHook(data) {
112
- const { tool_name, tool_input } = data;
113
-
114
- // Only guard Write and Edit tools
115
- if (tool_name !== 'Write' && tool_name !== 'Edit') {
116
- outputAllow();
117
- return;
118
- }
119
-
120
- const filePath = tool_input?.file_path;
121
- if (!filePath) {
122
- outputAllow();
123
- return;
124
- }
125
-
126
- // Normalize and get relative path
127
- const normalizedPath = path.resolve(filePath);
128
- const projectRoot = process.env.CLAUDE_PROJECT_DIR || process.cwd();
129
- const relativePath = path.relative(projectRoot, normalizedPath);
130
-
131
- // Check if file is in src/ directory
132
- const isInSrc = relativePath.startsWith('src/') || relativePath.startsWith('src\\');
133
-
134
- if (!isInSrc) {
135
- // Not in src/ directory - allow
136
- outputAllow();
137
- return;
138
- }
139
-
140
- // Check if builder mode is active - ALLOW
141
- if (isBuilderModeActive()) {
142
- outputAllow();
143
- return;
144
- }
145
-
146
- // BLOCK with helpful message
147
- outputBlock(`
148
- đŸšĢ BLOCKED: Direct edit to source file
149
-
150
- File: ${relativePath}
151
-
152
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
153
- WHY: Use a subagent to save your context for orchestration
154
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
155
-
156
- TO PROCEED: Spawn a general-purpose agent with detailed instructions:
157
-
158
- Task({
159
- subagent_type: "general-purpose",
160
- description: "Fix issue #XX",
161
- prompt: \`
162
- [Detailed instructions for the change]
163
-
164
- File to modify: ${relativePath}
165
-
166
- [Explain what to change and why]
167
- \`
168
- })
169
-
170
- The agent will:
171
- 1. Make the code changes
172
- 2. Verify the build passes
173
- 3. Report back results
174
-
175
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
176
- OR: Enable builder mode temporarily (if you need to edit directly)
177
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
178
-
179
- Bash: node .claude/hooks/src-edit-guard.cjs --on
180
- [Make your edits]
181
- Bash: node .claude/hooks/src-edit-guard.cjs --off
182
- `);
183
- }
184
-
185
- // CLI: Enable builder mode
186
- if (process.argv[2] === '--on') {
187
- enableBuilderMode();
188
- console.log('🔧 Builder mode ENABLED - src/ edits now allowed');
189
- process.exit(0);
190
- }
191
-
192
- // CLI: Disable builder mode
193
- if (process.argv[2] === '--off') {
194
- if (disableBuilderMode()) {
195
- console.log('🔒 Builder mode DISABLED - src/ edits blocked again');
196
- } else {
197
- console.log('â„šī¸ Builder mode was not active');
198
- }
199
- process.exit(0);
200
- }
201
-
202
- // CLI: Check status
203
- if (process.argv[2] === '--status') {
204
- if (isBuilderModeActive()) {
205
- const data = JSON.parse(fs.readFileSync(BUILDER_MODE_FILE, 'utf8'));
206
- console.log(`🔧 Builder mode is ACTIVE (enabled at ${data.enabledAt})`);
207
- } else {
208
- console.log('🔒 Builder mode is OFF - src/ edits are blocked');
209
- }
210
- process.exit(0);
211
- }
212
-
213
- // CLI: Help
214
- if (process.argv[2] === '--help' || process.argv[2] === '-h') {
215
- console.log(`
216
- Source Code Edit Guard - Blocks direct edits to src/ directory
217
-
218
- Usage:
219
- node src-edit-guard.cjs --on Enable builder mode (allow edits)
220
- node src-edit-guard.cjs --off Disable builder mode (block edits)
221
- node src-edit-guard.cjs --status Check current mode
222
- node src-edit-guard.cjs --help Show this help
223
-
224
- UNIFIED SYSTEM: This hook uses the same builder mode file as app-edit-guard.cjs.
225
- Enabling builder mode here also enables it for Hailer app edits, and vice versa.
226
-
227
- Preferred commands (via app-edit-guard.cjs):
228
- node app-edit-guard.cjs --agent-on Enable global builder mode
229
- node app-edit-guard.cjs --agent-off Disable global builder mode
230
-
231
- As a hook:
232
- Reads JSON from stdin with tool_name and tool_input
233
- Outputs JSON with decision: "allow" or "block"
234
-
235
- Purpose:
236
- Forces the main Claude agent to spawn subagents for code changes.
237
- This saves context in the main agent for orchestration tasks.
238
- `);
239
- process.exit(0);
240
- }