@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,304 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * <hook-name>auto-learn</hook-name>
4
- *
5
- * <purpose>
6
- * Detects potential learnings from agent responses and prompts user to confirm.
7
- * Only appends to LEARNINGS.md after user approval.
8
- * Complements manual /learn command.
9
- * </purpose>
10
- *
11
- * <triggers>
12
- * - PostToolUse on Task (agent completions)
13
- * </triggers>
14
- *
15
- * <detection-patterns>
16
- * - "learned that", "discovered that", "found that"
17
- * - "note:", "important:", "gotcha:", "tip:"
18
- * - "always", "never", "must", "should" (in context of rules)
19
- * - "the trick is", "the key is", "turns out"
20
- * </detection-patterns>
21
- *
22
- * <behavior>
23
- * 1. Scan tool response for learning patterns
24
- * 2. If found, output prompt for Claude to use AskUserQuestion
25
- * 3. User confirms which learnings to save
26
- * 4. Claude runs --save command to append to LEARNINGS.md
27
- * </behavior>
28
- */
29
-
30
- const fs = require('fs');
31
- const path = require('path');
32
- const os = require('os');
33
-
34
- const LEARNINGS_FILE = path.join(
35
- process.env.HAILER_INBOX_DIR || path.join(process.env.CLAUDE_PROJECT_DIR || process.cwd(), 'inbox'),
36
- 'learnings.md'
37
- );
38
- const PENDING_FILE = path.join(os.tmpdir(), '.claude-pending-learnings.json');
39
-
40
- // Patterns that indicate a learning/insight
41
- const LEARNING_PATTERNS = [
42
- /(?:i |we |claude )(?:learned|discovered|found|realized) that (.{20,150})/i,
43
- /(?:note|important|gotcha|tip|caveat|warning):\s*(.{20,150})/i,
44
- /(?:the (?:trick|key|solution|fix) (?:is|was)) (.{20,150})/i,
45
- /(?:turns out|it turns out) (.{20,150})/i,
46
- /(?:remember to|don't forget to|make sure to) (.{20,100})/i,
47
- ];
48
-
49
- // Category detection based on content
50
- const CATEGORY_PATTERNS = [
51
- { pattern: /agent|kenji|dmitri|giuseppe|helga|alejandro|viktor/i, category: 'agent' },
52
- { pattern: /skill|pattern|template/i, category: 'skill' },
53
- { pattern: /workflow|field|phase|activity/i, category: 'workflow' },
54
- { pattern: /bug|error|fix|broken/i, category: 'bug' },
55
- { pattern: /hook|guard|check/i, category: 'hook' },
56
- ];
57
-
58
- // Read hook input from stdin
59
- let input = '';
60
- process.stdin.setEncoding('utf8');
61
- process.stdin.on('data', chunk => input += chunk);
62
- process.stdin.on('end', () => {
63
- try {
64
- const data = JSON.parse(input);
65
- processHook(data);
66
- } catch (e) {
67
- console.error(`[auto-learn] Warning: ${e.message}`);
68
- process.exit(0);
69
- }
70
- });
71
-
72
- function detectCategory(text) {
73
- for (const { pattern, category } of CATEGORY_PATTERNS) {
74
- if (pattern.test(text)) {
75
- return category;
76
- }
77
- }
78
- return 'pattern';
79
- }
80
-
81
- function extractLearnings(text) {
82
- const learnings = [];
83
-
84
- // Limit input size to prevent ReDoS attacks (100KB max)
85
- const MAX_INPUT_SIZE = 100000;
86
- if (text.length > MAX_INPUT_SIZE) {
87
- text = text.substring(0, MAX_INPUT_SIZE);
88
- }
89
-
90
- // Normalize whitespace for multi-line matching
91
- const normalizedText = text.replace(/\s+/g, ' ');
92
-
93
- // NOTE: Uses global match then per-result extraction. Acceptable for typical agent output sizes (<100KB).
94
- // For larger inputs, consider exec() loop instead.
95
- const matchStartTime = Date.now();
96
- for (const pattern of LEARNING_PATTERNS) {
97
- if (Date.now() - matchStartTime > 1000) break; // ReDoS timeout protection
98
- // Use 'gis' flags for case-insensitive, dotall (multi-line) matching
99
- const matches = normalizedText.match(new RegExp(pattern.source, 'gis'));
100
- if (matches) {
101
- for (const match of matches) {
102
- const extracted = match.match(new RegExp(pattern.source, 'is'));
103
- if (extracted && extracted[1]) {
104
- let learning = extracted[1].trim();
105
- learning = learning.replace(/\s+/g, ' ').replace(/[.!,;]$/, '').trim();
106
- if (learning.length >= 20 && !learnings.some(l => l.text === learning)) {
107
- learnings.push({
108
- text: learning,
109
- category: detectCategory(learning)
110
- });
111
- }
112
- }
113
- }
114
- }
115
- }
116
-
117
- return learnings;
118
- }
119
-
120
- function processHook(data) {
121
- const { tool_name, tool_input, tool_response } = data;
122
-
123
- // Only process Task completions
124
- if (tool_name !== 'Task') {
125
- process.exit(0);
126
- }
127
-
128
- if (tool_response === undefined) {
129
- process.exit(0);
130
- }
131
-
132
- const agentName = tool_input?.subagent_type;
133
- const responseText = typeof tool_response === 'string'
134
- ? tool_response
135
- : JSON.stringify(tool_response || '');
136
-
137
- const learnings = extractLearnings(responseText);
138
-
139
- if (learnings.length > 0) {
140
- // Save pending learnings for later confirmation
141
- fs.writeFileSync(PENDING_FILE, JSON.stringify({
142
- learnings,
143
- agentName,
144
- timestamp: new Date().toISOString()
145
- }));
146
-
147
- // Build options for AskUserQuestion (max 4)
148
- const options = learnings.slice(0, 3).map((l, i) => ({
149
- label: `Save #${i + 1}`,
150
- description: l.text.substring(0, 50) + (l.text.length > 50 ? '...' : '')
151
- }));
152
- options.push({ label: 'Skip all', description: 'Don\'t save any of these' });
153
-
154
- const output = `
155
- <user-prompt-submit-hook>
156
- 📚 POTENTIAL LEARNING${learnings.length > 1 ? 'S' : ''} DETECTED
157
-
158
- ${learnings.map((l, i) => `${i + 1}. [${l.category}] ${l.text}`).join('\n')}
159
-
160
- 👉 Ask user: "Save to inbox/learnings.md?" Options: ${options.map(o => o.label).join(' / ')}
161
-
162
- If yes, run: node .claude/hooks/auto-learn.cjs --save <indices>
163
- </user-prompt-submit-hook>
164
- `;
165
-
166
- console.log(output);
167
- }
168
-
169
- process.exit(0);
170
- }
171
-
172
- // CLI: Save specific learnings by index
173
- if (process.argv[2] === '--save') {
174
- const indices = process.argv.slice(3)
175
- .map(n => parseInt(n, 10))
176
- .filter(n => !isNaN(n))
177
- .map(n => n - 1);
178
-
179
- if (!fs.existsSync(PENDING_FILE)) {
180
- console.error('No pending learnings found');
181
- process.exit(1);
182
- }
183
-
184
- const pending = JSON.parse(fs.readFileSync(PENDING_FILE, 'utf8'));
185
- const toSave = indices
186
- .filter(i => i >= 0 && i < pending.learnings.length)
187
- .map(i => pending.learnings[i]);
188
-
189
- if (toSave.length === 0) {
190
- console.log('No valid learnings selected');
191
- process.exit(0);
192
- }
193
-
194
- // Ensure LEARNINGS.md exists (recursive: true is idempotent, no TOCTOU race)
195
- if (!fs.existsSync(LEARNINGS_FILE)) {
196
- const dir = path.dirname(LEARNINGS_FILE);
197
- fs.mkdirSync(dir, { recursive: true });
198
- fs.writeFileSync(LEARNINGS_FILE, `# Inbox
199
-
200
- Learnings captured from all projects. Review and integrate into marketplace agents/skills.
201
-
202
- ## Pending
203
- <!-- /learn and auto-learn add items here -->
204
-
205
- ## Applied
206
- <!-- Move items here after integrating into agents/skills -->
207
- `);
208
- }
209
-
210
- let content = fs.readFileSync(LEARNINGS_FILE, 'utf8');
211
- const timestamp = new Date().toISOString().split('T')[0];
212
-
213
- const entries = toSave.map(l => {
214
- const source = pending.agentName ? `from ${pending.agentName}` : '';
215
- const autoTag = '🤖 auto-learn';
216
- return `- [${l.category}] ${l.text} _(${autoTag}${source ? `, ${source}` : ''}, ${timestamp})_`;
217
- }).join('\n');
218
-
219
- // Add under Pending section
220
- const pendingMatch = content.match(/^(## Pending\s*\n(?:<!--[^>]*-->\s*\n)?)/m);
221
- if (pendingMatch) {
222
- const insertPoint = content.indexOf(pendingMatch[0]) + pendingMatch[0].length;
223
- content = content.slice(0, insertPoint) + entries + '\n' + content.slice(insertPoint);
224
- } else {
225
- // Fallback: append to end if no Pending section found
226
- content += `\n## Pending\n${entries}\n`;
227
- }
228
-
229
- // Atomic write: temp file + rename to prevent corruption
230
- const tmpFile = LEARNINGS_FILE + '.tmp-' + process.pid;
231
- fs.writeFileSync(tmpFile, content);
232
- fs.renameSync(tmpFile, LEARNINGS_FILE);
233
-
234
- // Safe unlink: ignore ENOENT if file already deleted
235
- try {
236
- fs.unlinkSync(PENDING_FILE);
237
- } catch (err) {
238
- if (err.code !== 'ENOENT') throw err;
239
- }
240
-
241
- console.log(`✅ Saved ${toSave.length} learning(s) to inbox/learnings.md`);
242
- process.exit(0);
243
- }
244
-
245
- // CLI: Clear pending
246
- if (process.argv[2] === '--clear') {
247
- if (fs.existsSync(PENDING_FILE)) {
248
- fs.unlinkSync(PENDING_FILE);
249
- console.log('✅ Cleared pending learnings');
250
- } else {
251
- console.log('No pending learnings');
252
- }
253
- process.exit(0);
254
- }
255
-
256
- // CLI: Show pending
257
- if (process.argv[2] === '--pending') {
258
- if (!fs.existsSync(PENDING_FILE)) {
259
- console.log('No pending learnings');
260
- process.exit(0);
261
- }
262
- const pending = JSON.parse(fs.readFileSync(PENDING_FILE, 'utf8'));
263
- console.log('Pending learnings:');
264
- pending.learnings.forEach((l, i) => {
265
- console.log(` ${i + 1}. [${l.category}] ${l.text}`);
266
- });
267
- process.exit(0);
268
- }
269
-
270
- // CLI: Test
271
- if (process.argv[2] === '--test' && process.argv[3]) {
272
- const text = process.argv.slice(3).join(' ');
273
- const learnings = extractLearnings(text);
274
- if (learnings.length === 0) {
275
- console.log('No learnings detected');
276
- } else {
277
- console.log('Detected:');
278
- learnings.forEach(l => console.log(` [${l.category}] ${l.text}`));
279
- }
280
- process.exit(0);
281
- }
282
-
283
- // CLI: Help
284
- if (process.argv[2] === '--help' || process.argv[2] === '-h') {
285
- console.log(`
286
- Auto-Learn Hook - Detects learnings and prompts for confirmation
287
-
288
- Usage:
289
- node auto-learn.cjs --save 1 2 3 Save learnings by index
290
- node auto-learn.cjs --pending Show pending learnings
291
- node auto-learn.cjs --clear Clear pending learnings
292
- node auto-learn.cjs --test <text> Test detection on sample text
293
- node auto-learn.cjs --help Show this help
294
-
295
- Workflow:
296
- 1. Hook detects learning in agent response
297
- 2. Prompts Claude to ask user for confirmation
298
- 3. User selects which to save
299
- 4. Claude runs --save with indices
300
-
301
- Complements manual /learn command.
302
- `);
303
- process.exit(0);
304
- }
@@ -1,272 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * <hook-name>bash-guard</hook-name>
4
- *
5
- * <purpose>
6
- * Consolidated Bash PreToolUse guard. Replaces 4 separate hooks:
7
- * - destructive-command-guard (blocks dangerous commands)
8
- * - mcp-server-guard (blocks server start commands)
9
- * - workspace-pull-guard (warns before pull with dirty workspace)
10
- * - workspace-auto-save (auto-commits before push)
11
- *
12
- * Single Node process instead of 4 = faster, no timeouts.
13
- * </purpose>
14
- *
15
- * <triggers>PreToolUse on Bash</triggers>
16
- * @version 1.0.0
17
- */
18
-
19
- const { spawnSync } = require('child_process');
20
- const fs = require('fs');
21
- const path = require('path');
22
- const os = require('os');
23
-
24
- const ALLOW = JSON.stringify({ decision: 'allow' });
25
- const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
26
-
27
- // --- CLI modes ---
28
- if (process.argv[2] === '--bypass' && process.argv[3]) {
29
- const command = process.argv[3];
30
- const bypass = { command, createdAt: Date.now(), expiresAt: Date.now() + 120000 };
31
- const BYPASS_FILE = path.join(os.tmpdir(), '.claude-destructive-bypass.json');
32
- const tmpFile = BYPASS_FILE + '.tmp-' + process.pid;
33
- fs.writeFileSync(tmpFile, JSON.stringify(bypass, null, 2));
34
- fs.renameSync(tmpFile, BYPASS_FILE);
35
- console.log(`Bypass created for: ${command}`);
36
- console.log('Expires in 2 minutes. Retry the command now.');
37
- process.exit(0);
38
- }
39
- if (process.argv[2] === '--history') {
40
- try {
41
- const result = spawnSync('git', ['log', '--oneline', '--grep=Auto-save workspace', '-20'], {
42
- cwd: projectDir, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe']
43
- });
44
- console.log((result.stdout || '').trim() || 'No auto-saves found');
45
- } catch { console.log('Could not read git log'); }
46
- process.exit(0);
47
- }
48
- if (process.argv[2] === '--help' || process.argv[2] === '-h') {
49
- console.log(`
50
- Bash Guard - Consolidated Bash PreToolUse hook
51
-
52
- Combines: destructive-command-guard, mcp-server-guard, workspace-pull-guard, workspace-auto-save
53
-
54
- Usage:
55
- node bash-guard.cjs --bypass '<cmd>' Create one-time bypass for blocked command
56
- node bash-guard.cjs --history Show recent workspace auto-saves
57
- node bash-guard.cjs --help Show this help
58
- `);
59
- process.exit(0);
60
- }
61
-
62
- // --- Read stdin ---
63
- let input = '';
64
- process.stdin.setEncoding('utf8');
65
- process.stdin.on('data', chunk => input += chunk);
66
- process.stdin.on('end', () => {
67
- try {
68
- const data = JSON.parse(input || '{}');
69
- processHook(data);
70
- } catch {
71
- allow();
72
- }
73
- });
74
-
75
- function allow(msg) {
76
- if (msg) console.error('[bash-guard] ' + msg);
77
- console.log(ALLOW);
78
- process.exit(0);
79
- }
80
-
81
- function block(reason) {
82
- console.log(JSON.stringify({ decision: 'block', reason }));
83
- process.exit(0);
84
- }
85
-
86
- function processHook(data) {
87
- const { tool_name, tool_input } = data;
88
- if (tool_name !== 'Bash') { allow(); return; }
89
-
90
- const command = tool_input?.command || '';
91
- if (!command) { allow(); return; }
92
-
93
- // Run checks in order of specificity
94
- checkDestructive(command);
95
- checkMcpServer(command);
96
- checkWorkspacePull(command);
97
- checkWorkspaceAutoSave(command);
98
- allow();
99
- }
100
-
101
- // ============================================================
102
- // 1. Destructive Command Guard
103
- // ============================================================
104
- const DANGEROUS_PATTERNS = [
105
- { pattern: /git\s+reset\s+--hard/i, name: 'git reset --hard', risk: 'Permanently discards all uncommitted changes', alternative: 'git stash or git reset --soft' },
106
- { pattern: /git\s+clean\s+-[fd]/i, name: 'git clean -f/-fd', risk: 'Permanently deletes untracked files', alternative: 'git clean -n (dry run)' },
107
- { pattern: /\brm\s+-[a-zA-Z]{0,10}(rf|fr)[a-zA-Z]{0,10}\b/i, name: 'rm -rf', risk: 'Recursively deletes files without confirmation', alternative: 'rm -ri (interactive) or move to trash' },
108
- { pattern: /git\s+push\s+[^|]*--force(?!-with-lease)[^|]*(main|master)/i, name: 'git push --force to main/master', risk: 'Overwrites remote history', alternative: 'git push --force-with-lease' },
109
- { pattern: /git\s+push\s+[^|]*(main|master)[^|]*--force(?!-with-lease)/i, name: 'git push --force to main/master', risk: 'Overwrites remote history', alternative: 'git push --force-with-lease' },
110
- { pattern: /git\s+checkout\s+\.\s*$/i, name: 'git checkout .', risk: 'Discards all uncommitted changes', alternative: 'git stash or git diff' },
111
- { pattern: /git\s+restore\s+\.\s*$/i, name: 'git restore .', risk: 'Discards all uncommitted changes', alternative: 'git stash or git diff' },
112
- { pattern: /git\s+branch\s+-D/, name: 'git branch -D', risk: 'Force-deletes branch even if not merged', alternative: 'git branch -d (safe delete)' }
113
- ];
114
-
115
- const BYPASS_FILE = path.join(os.tmpdir(), '.claude-destructive-bypass.json');
116
-
117
- function isSubagentActive() {
118
- try {
119
- const stackFile = path.join(os.tmpdir(), '.claude-agent-stack.json');
120
- if (fs.existsSync(stackFile)) {
121
- const stack = JSON.parse(fs.readFileSync(stackFile, 'utf8'));
122
- return Array.isArray(stack.agents) && stack.agents.length > 0;
123
- }
124
- } catch {}
125
- return false;
126
- }
127
-
128
- function checkBypass(command) {
129
- try {
130
- const bypass = JSON.parse(fs.readFileSync(BYPASS_FILE, 'utf8'));
131
- if (typeof bypass.expiresAt !== 'number' || typeof bypass.command !== 'string') {
132
- try { fs.unlinkSync(BYPASS_FILE); } catch {}
133
- return false;
134
- }
135
- if (bypass.expiresAt <= Date.now()) {
136
- try { fs.unlinkSync(BYPASS_FILE); } catch {}
137
- return false;
138
- }
139
- if (bypass.command === command) {
140
- try { fs.unlinkSync(BYPASS_FILE); } catch {}
141
- return true;
142
- }
143
- } catch {}
144
- return false;
145
- }
146
-
147
- function checkDestructive(command) {
148
- if (isSubagentActive()) return;
149
- if (command.includes('bash-guard') && command.includes('--bypass')) return;
150
- if (checkBypass(command)) return;
151
-
152
- for (const { pattern, name, risk, alternative } of DANGEROUS_PATTERNS) {
153
- if (pattern.test(command)) {
154
- block(`BLOCKED: Destructive Command Detected
155
-
156
- **Command:** \`${name}\`
157
- **Risk:** ${risk}
158
-
159
- **USE THESE INSTEAD (no bypass needed):**
160
- - Delete files individually: \`rm file1 file2\` (no -rf flag)
161
- - Delete directory contents: \`find /path -mindepth 1 -delete\`
162
- - Move to trash: \`mv /path ~/.Trash/\`
163
- - Safe alternative: ${alternative}
164
-
165
- **ONLY if user explicitly requests the destructive command:**
166
- 1. Ask user with AskUserQuestion
167
- 2. Run: Bash: node "${process.argv[1].replace(/'/g, "'\\''")}" --bypass '${command.replace(/'/g, "'\\''")}'
168
- 3. Retry the original command`);
169
- }
170
- }
171
- }
172
-
173
- // ============================================================
174
- // 2. MCP Server Guard
175
- // ============================================================
176
- const SERVER_START_PATTERNS = [
177
- /npm run dev\b/, /npm run start\b/, /npm start\b/,
178
- /tsx\s+.*src\/app\.ts/, /tsx\s+watch\s+.*src\/app\.ts/,
179
- /node\s+.*src\/app\.ts/, /node\s+.*dist\/app\.js/,
180
- /npx\s+tsx\s+.*src\/app/
181
- ];
182
-
183
- function checkMcpServer(command) {
184
- if (!SERVER_START_PATTERNS.some(p => p.test(command))) return;
185
-
186
- block(`MCP server commands are blocked. Tell the user to run manually:
187
- cd ${projectDir} && npm run dev`);
188
- }
189
-
190
- // ============================================================
191
- // 3. Workspace Pull Guard
192
- // ============================================================
193
- function checkWorkspacePull(command) {
194
- if (!command.match(/npm\s+run\s+pull/)) return;
195
- if (process.env.CLAUDE_AGENT_ID || process.env.CLAUDE_SUBAGENT) return;
196
-
197
- const workspaceDir = path.join(projectDir, 'workspace');
198
- if (!fs.existsSync(workspaceDir)) { invalidateSharedMemory(); return; }
199
-
200
- try {
201
- const result = spawnSync('git', ['status', '--porcelain', 'workspace/'], {
202
- cwd: projectDir, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe']
203
- });
204
- const status = (result.stdout || '').trim();
205
-
206
- if (status) {
207
- const changedFiles = status.split('\n').map(l => l.trim()).filter(Boolean);
208
- block(`BLOCKED: Uncommitted changes in workspace/
209
-
210
- \`npm run pull\` will OVERWRITE your local TypeScript files!
211
-
212
- **Changed files:**
213
- ${changedFiles.map(f => ' ' + f).join('\n')}
214
-
215
- Options:
216
- 1. Push first: npm run push → npm run pull
217
- 2. Save first: /save "WIP before pull" → npm run pull
218
- 3. Discard: git checkout -- workspace/ → npm run pull`);
219
- }
220
- invalidateSharedMemory();
221
- } catch {
222
- invalidateSharedMemory();
223
- }
224
- }
225
-
226
- // ============================================================
227
- // 4. Workspace Auto-Save
228
- // ============================================================
229
- const PUSH_PATTERNS = [
230
- /npm\s+run\s+.*push/, /npm\s+run\s+.*sync/,
231
- /workflows-sync/, /fields-push/, /phases-push/,
232
- /insights-push/, /templates-sync/
233
- ];
234
-
235
- function checkWorkspaceAutoSave(command) {
236
- if (!PUSH_PATTERNS.some(p => p.test(command))) return;
237
-
238
- const workspaceDir = path.join(projectDir, 'workspace');
239
- if (!fs.existsSync(workspaceDir)) return;
240
-
241
- try {
242
- const result = spawnSync('git', ['status', '--porcelain', 'workspace/'], {
243
- cwd: projectDir, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe']
244
- });
245
- const status = (result.stdout || '').trim();
246
- if (!status) { console.error('[bash-guard] workspace/ clean, no auto-save needed'); return; }
247
-
248
- const changes = status.split('\n').filter(Boolean).length;
249
- const timestamp = new Date().toISOString().slice(0, 19).replace('T', ' ').replace(/[:.]/g, '-');
250
- spawnSync('git', ['add', 'workspace/'], { cwd: projectDir, encoding: 'utf8' });
251
- const commitResult = spawnSync('git', ['commit', '-m', `Auto-save workspace before push (${timestamp})`], {
252
- cwd: projectDir, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe']
253
- });
254
- if (commitResult.status !== 0 && !commitResult.stderr?.includes('nothing to commit')) {
255
- throw new Error(commitResult.stderr || 'git commit failed');
256
- }
257
- console.error('[bash-guard] Auto-saved ' + changes + ' workspace change(s) before push');
258
- } catch (err) {
259
- console.error('[bash-guard] Could not auto-save: ' + err.message);
260
- }
261
- }
262
-
263
- // ============================================================
264
- // Shared memory invalidation (after pull)
265
- // ============================================================
266
- function invalidateSharedMemory() {
267
- try {
268
- const mem = require('./_shared-memory.cjs');
269
- mem.invalidate();
270
- console.error('[bash-guard] Shared memory invalidated (workspace pull).');
271
- } catch {}
272
- }