@hailer/mcp 1.0.28 → 1.1.2

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 (233) hide show
  1. package/.claude/.session-checked +1 -0
  2. package/.claude/agents/agent-ada-skill-builder.md +10 -2
  3. package/.claude/agents/agent-alejandro-function-fields.md +104 -37
  4. package/.claude/agents/agent-bjorn-config-audit.md +41 -21
  5. package/.claude/agents/agent-builder-agent-creator.md +13 -3
  6. package/.claude/agents/agent-code-simplifier.md +53 -0
  7. package/.claude/agents/agent-dmitri-activity-crud.md +126 -11
  8. package/.claude/agents/agent-giuseppe-app-builder.md +212 -22
  9. package/.claude/agents/agent-gunther-mcp-tools.md +7 -36
  10. package/.claude/agents/agent-helga-workflow-config.md +75 -10
  11. package/.claude/agents/agent-igor-activity-mover-automation.md +125 -0
  12. package/.claude/agents/agent-ingrid-doc-templates.md +164 -36
  13. package/.claude/agents/agent-ivan-monolith.md +154 -0
  14. package/.claude/agents/agent-kenji-data-reader.md +15 -8
  15. package/.claude/agents/agent-lars-code-inspector.md +56 -8
  16. package/.claude/agents/agent-marco-mockup-builder.md +110 -0
  17. package/.claude/agents/agent-marcus-api-documenter.md +323 -0
  18. package/.claude/agents/agent-marketplace-publisher.md +232 -72
  19. package/.claude/agents/agent-marketplace-reviewer.md +255 -79
  20. package/.claude/agents/agent-permissions-handler.md +208 -0
  21. package/.claude/agents/agent-simple-writer.md +48 -0
  22. package/.claude/agents/agent-svetlana-code-review.md +127 -14
  23. package/.claude/agents/agent-tanya-test-runner.md +333 -0
  24. package/.claude/agents/agent-ui-designer.md +100 -0
  25. package/.claude/agents/agent-viktor-sql-insights.md +19 -6
  26. package/.claude/agents/agent-web-search.md +55 -0
  27. package/.claude/agents/agent-yevgeni-discussions.md +7 -1
  28. package/.claude/agents/agent-zara-zapier.md +159 -0
  29. package/.claude/commands/app-squad.md +135 -0
  30. package/.claude/commands/audit-squad.md +158 -0
  31. package/.claude/commands/autoplan.md +563 -0
  32. package/.claude/commands/cleanup-squad.md +98 -0
  33. package/.claude/commands/config-squad.md +106 -0
  34. package/.claude/commands/crud-squad.md +87 -0
  35. package/.claude/commands/data-squad.md +97 -0
  36. package/.claude/commands/debug-squad.md +303 -0
  37. package/.claude/commands/doc-squad.md +65 -0
  38. package/.claude/commands/handoff.md +137 -0
  39. package/.claude/commands/health.md +49 -0
  40. package/.claude/commands/help.md +2 -1
  41. package/.claude/commands/help:agents.md +96 -16
  42. package/.claude/commands/help:commands.md +55 -11
  43. package/.claude/commands/help:faq.md +16 -1
  44. package/.claude/commands/help:skills.md +93 -0
  45. package/.claude/commands/hotfix-squad.md +112 -0
  46. package/.claude/commands/integration-squad.md +82 -0
  47. package/.claude/commands/janitor-squad.md +167 -0
  48. package/.claude/commands/learn-auto.md +120 -0
  49. package/.claude/commands/learn.md +120 -0
  50. package/.claude/commands/mcp-list.md +27 -0
  51. package/.claude/commands/onboard-squad.md +140 -0
  52. package/.claude/commands/plan-workspace.md +732 -0
  53. package/.claude/commands/prd.md +131 -0
  54. package/.claude/commands/project-status.md +82 -0
  55. package/.claude/commands/publish.md +138 -0
  56. package/.claude/commands/recap.md +69 -0
  57. package/.claude/commands/restore.md +64 -0
  58. package/.claude/commands/review-squad.md +152 -0
  59. package/.claude/commands/save.md +24 -0
  60. package/.claude/commands/stats.md +19 -0
  61. package/.claude/commands/swarm.md +210 -0
  62. package/.claude/commands/tool-builder.md +3 -1
  63. package/.claude/commands/ws-pull.md +1 -1
  64. package/.claude/commands/yolo-off.md +17 -0
  65. package/.claude/commands/yolo.md +82 -0
  66. package/.claude/hooks/_shared-memory.cjs +305 -0
  67. package/.claude/hooks/_utils.cjs +134 -0
  68. package/.claude/hooks/agent-failure-detector.cjs +164 -79
  69. package/.claude/hooks/agent-usage-logger.cjs +204 -0
  70. package/.claude/hooks/app-edit-guard.cjs +20 -4
  71. package/.claude/hooks/auto-learn.cjs +316 -0
  72. package/.claude/hooks/bash-guard.cjs +282 -0
  73. package/.claude/hooks/builder-mode-manager.cjs +183 -54
  74. package/.claude/hooks/bulk-activity-guard.cjs +283 -0
  75. package/.claude/hooks/context-watchdog.cjs +292 -0
  76. package/.claude/hooks/delegation-reminder.cjs +478 -0
  77. package/.claude/hooks/design-system-lint.cjs +283 -0
  78. package/.claude/hooks/post-scaffold-hook.cjs +16 -3
  79. package/.claude/hooks/prompt-guard.cjs +366 -0
  80. package/.claude/hooks/publish-template-guard.cjs +16 -0
  81. package/.claude/hooks/session-start.cjs +35 -0
  82. package/.claude/hooks/shared-memory-writer.cjs +147 -0
  83. package/.claude/hooks/skill-injector.cjs +140 -0
  84. package/.claude/hooks/skill-usage-logger.cjs +258 -0
  85. package/.claude/hooks/src-edit-guard.cjs +16 -1
  86. package/.claude/hooks/sync-marketplace-agents.cjs +53 -8
  87. package/.claude/scripts/yolo-toggle.cjs +142 -0
  88. package/.claude/settings.json +141 -14
  89. package/.claude/skills/SDK-activity-patterns/SKILL.md +428 -0
  90. package/.claude/skills/SDK-document-templates/SKILL.md +1033 -0
  91. package/.claude/skills/SDK-function-fields/SKILL.md +542 -0
  92. package/.claude/skills/SDK-generate-skill/SKILL.md +92 -0
  93. package/.claude/skills/SDK-init-skill/SKILL.md +127 -0
  94. package/.claude/skills/SDK-insight-queries/SKILL.md +787 -0
  95. package/.claude/skills/SDK-ws-config-skill/SKILL.md +1139 -0
  96. package/.claude/skills/agent-structure/SKILL.md +98 -0
  97. package/.claude/skills/api-documentation-patterns/SKILL.md +474 -0
  98. package/.claude/skills/chrome-mcp-reference/SKILL.md +370 -0
  99. package/.claude/skills/delegation-routing/SKILL.md +202 -0
  100. package/.claude/skills/frontend-design/SKILL.md +254 -0
  101. package/.claude/skills/hailer-activity-mover/SKILL.md +213 -0
  102. package/.claude/skills/hailer-api-client/SKILL.md +518 -0
  103. package/.claude/skills/hailer-app-builder/SKILL.md +939 -11
  104. package/.claude/skills/hailer-apps-pictures/SKILL.md +269 -0
  105. package/.claude/skills/hailer-design-system/SKILL.md +235 -0
  106. package/.claude/skills/hailer-monolith-automations/SKILL.md +686 -0
  107. package/.claude/skills/hailer-permissions-system/SKILL.md +121 -0
  108. package/.claude/skills/hailer-project-protocol/SKILL.md +488 -0
  109. package/.claude/skills/hailer-rest-api/SKILL.md +61 -0
  110. package/.claude/skills/hailer-rest-api/hailer-activities.md +184 -0
  111. package/.claude/skills/hailer-rest-api/hailer-admin.md +473 -0
  112. package/.claude/skills/hailer-rest-api/hailer-calendar.md +256 -0
  113. package/.claude/skills/hailer-rest-api/hailer-feed.md +249 -0
  114. package/.claude/skills/hailer-rest-api/hailer-insights.md +195 -0
  115. package/.claude/skills/hailer-rest-api/hailer-messaging.md +276 -0
  116. package/.claude/skills/hailer-rest-api/hailer-workflows.md +283 -0
  117. package/.claude/skills/insight-join-patterns/SKILL.md +3 -0
  118. package/.claude/skills/integration-patterns/SKILL.md +421 -0
  119. package/.claude/skills/json-only-output/SKILL.md +52 -12
  120. package/.claude/skills/lsp-setup/SKILL.md +160 -0
  121. package/.claude/skills/mcp-direct-tools/SKILL.md +153 -0
  122. package/.claude/skills/optional-parameters/SKILL.md +32 -23
  123. package/.claude/skills/publish-hailer-app/SKILL.md +76 -12
  124. package/.claude/skills/testing-patterns/SKILL.md +630 -0
  125. package/.claude/skills/tool-builder/SKILL.md +250 -0
  126. package/.claude/skills/tool-parameter-usage/SKILL.md +59 -45
  127. package/.claude/skills/tool-response-verification/SKILL.md +82 -48
  128. package/.claude/skills/zapier-hailer-patterns/SKILL.md +581 -0
  129. package/.env.example +26 -7
  130. package/CLAUDE.md +290 -224
  131. package/dist/CLAUDE.md +370 -0
  132. package/dist/app.d.ts +1 -1
  133. package/dist/app.js +101 -101
  134. package/dist/bot/bot-config.d.ts +26 -0
  135. package/dist/bot/bot-config.js +135 -0
  136. package/dist/bot/bot-manager.d.ts +40 -0
  137. package/dist/bot/bot-manager.js +137 -0
  138. package/dist/bot/bot.d.ts +127 -0
  139. package/dist/bot/bot.js +1328 -0
  140. package/dist/bot/operation-logger.d.ts +28 -0
  141. package/dist/bot/operation-logger.js +132 -0
  142. package/dist/bot/services/conversation-manager.d.ts +60 -0
  143. package/dist/bot/services/conversation-manager.js +246 -0
  144. package/dist/bot/services/index.d.ts +9 -0
  145. package/dist/bot/services/index.js +18 -0
  146. package/dist/bot/services/message-classifier.d.ts +42 -0
  147. package/dist/bot/services/message-classifier.js +228 -0
  148. package/dist/bot/services/message-formatter.d.ts +88 -0
  149. package/dist/bot/services/message-formatter.js +411 -0
  150. package/dist/bot/services/session-logger.d.ts +162 -0
  151. package/dist/bot/services/session-logger.js +724 -0
  152. package/dist/bot/services/token-billing.d.ts +78 -0
  153. package/dist/bot/services/token-billing.js +233 -0
  154. package/dist/bot/services/types.d.ts +169 -0
  155. package/dist/bot/services/types.js +12 -0
  156. package/dist/bot/services/typing-indicator.d.ts +23 -0
  157. package/dist/bot/services/typing-indicator.js +60 -0
  158. package/dist/bot/services/workspace-schema-cache.d.ts +122 -0
  159. package/dist/bot/services/workspace-schema-cache.js +506 -0
  160. package/dist/bot/tool-executor.d.ts +28 -0
  161. package/dist/bot/tool-executor.js +48 -0
  162. package/dist/bot/workspace-overview.d.ts +12 -0
  163. package/dist/bot/workspace-overview.js +94 -0
  164. package/dist/cli.d.ts +1 -8
  165. package/dist/cli.js +1 -249
  166. package/dist/config.d.ts +96 -3
  167. package/dist/config.js +148 -37
  168. package/dist/core.d.ts +5 -0
  169. package/dist/core.js +61 -8
  170. package/dist/lib/discussion-lock.d.ts +42 -0
  171. package/dist/lib/discussion-lock.js +110 -0
  172. package/dist/lib/logger.d.ts +0 -1
  173. package/dist/lib/logger.js +39 -23
  174. package/dist/lib/request-logger.d.ts +77 -0
  175. package/dist/lib/request-logger.js +147 -0
  176. package/dist/mcp/UserContextCache.js +16 -13
  177. package/dist/mcp/hailer-clients.js +18 -17
  178. package/dist/mcp/signal-handler.js +29 -13
  179. package/dist/mcp/tool-registry.d.ts +4 -15
  180. package/dist/mcp/tool-registry.js +94 -32
  181. package/dist/mcp/tools/activity.js +28 -69
  182. package/dist/mcp/tools/app-core.js +9 -4
  183. package/dist/mcp/tools/app-marketplace.js +22 -12
  184. package/dist/mcp/tools/app-member.js +5 -2
  185. package/dist/mcp/tools/app-scaffold.js +32 -18
  186. package/dist/mcp/tools/bot-config/constants.d.ts +23 -0
  187. package/dist/mcp/tools/bot-config/constants.js +94 -0
  188. package/dist/mcp/tools/bot-config/core.d.ts +253 -0
  189. package/dist/mcp/tools/bot-config/core.js +2456 -0
  190. package/dist/mcp/tools/bot-config/index.d.ts +10 -0
  191. package/dist/mcp/tools/bot-config/index.js +59 -0
  192. package/dist/mcp/tools/bot-config/tools.d.ts +7 -0
  193. package/dist/mcp/tools/bot-config/tools.js +15 -0
  194. package/dist/mcp/tools/bot-config/types.d.ts +50 -0
  195. package/dist/mcp/tools/bot-config/types.js +6 -0
  196. package/dist/mcp/tools/discussion.js +107 -77
  197. package/dist/mcp/tools/document.d.ts +11 -0
  198. package/dist/mcp/tools/document.js +741 -0
  199. package/dist/mcp/tools/file.js +5 -2
  200. package/dist/mcp/tools/insight.js +36 -12
  201. package/dist/mcp/tools/investigate.d.ts +9 -0
  202. package/dist/mcp/tools/investigate.js +254 -0
  203. package/dist/mcp/tools/user.d.ts +2 -4
  204. package/dist/mcp/tools/user.js +9 -50
  205. package/dist/mcp/tools/workflow.d.ts +1 -0
  206. package/dist/mcp/tools/workflow.js +164 -52
  207. package/dist/mcp/utils/hailer-api-client.js +26 -17
  208. package/dist/mcp/webhook-handler.d.ts +64 -3
  209. package/dist/mcp/webhook-handler.js +219 -9
  210. package/dist/mcp-server.d.ts +4 -0
  211. package/dist/mcp-server.js +237 -25
  212. package/dist/plugins/bug-fixer/index.d.ts +2 -0
  213. package/dist/plugins/bug-fixer/index.js +18 -0
  214. package/dist/plugins/bug-fixer/tools.d.ts +45 -0
  215. package/dist/plugins/bug-fixer/tools.js +1096 -0
  216. package/package.json +10 -10
  217. package/scripts/test-hal-tools.ts +154 -0
  218. package/.claude/agents/agent-nora-name-functions.md +0 -123
  219. package/.claude/assistant-knowledge.md +0 -23
  220. package/.claude/commands/install-plugin.md +0 -261
  221. package/.claude/commands/list-plugins.md +0 -42
  222. package/.claude/commands/marketplace-setup.md +0 -33
  223. package/.claude/commands/publish-plugin.md +0 -55
  224. package/.claude/commands/uninstall-plugin.md +0 -87
  225. package/.claude/hooks/interactive-mode.cjs +0 -87
  226. package/.claude/hooks/mcp-server-guard.cjs +0 -108
  227. package/.claude/skills/marketplace-publishing.md +0 -155
  228. package/dist/bot/chat-bot.d.ts +0 -31
  229. package/dist/bot/chat-bot.js +0 -357
  230. package/dist/mcp/tools/metrics.d.ts +0 -13
  231. package/dist/mcp/tools/metrics.js +0 -546
  232. package/dist/stdio-server.d.ts +0 -14
  233. package/dist/stdio-server.js +0 -114
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Shared utilities for Claude Code hooks
4
+ *
5
+ * Usage:
6
+ * const { isYoloMode, readStdin, safeJsonParse, outputAllow } = require('./_utils.cjs');
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+
12
+ // Cache yolo mode per-process since it doesn't change within a single hook execution
13
+ let _yoloCache;
14
+
15
+ /**
16
+ * Check if yolo mode is active
17
+ * @returns {boolean}
18
+ */
19
+ function isYoloMode() {
20
+ if (_yoloCache !== undefined) return _yoloCache;
21
+ try {
22
+ const statePath = path.join(
23
+ process.env.CLAUDE_PROJECT_DIR || process.cwd(),
24
+ '.claude',
25
+ 'yolo-state.json'
26
+ );
27
+ const state = JSON.parse(fs.readFileSync(statePath, 'utf8'));
28
+ _yoloCache = state.mode === 'yolo';
29
+ return _yoloCache;
30
+ } catch {
31
+ _yoloCache = false;
32
+ return false;
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Read stdin safely with TTY check (async)
38
+ * @returns {Promise<string>} The stdin content or empty string if TTY
39
+ */
40
+ function readStdin() {
41
+ return new Promise((resolve, reject) => {
42
+ if (process.stdin.isTTY) {
43
+ resolve('');
44
+ return;
45
+ }
46
+ let data = '';
47
+ process.stdin.setEncoding('utf8');
48
+ process.stdin.on('data', chunk => data += chunk);
49
+ process.stdin.on('end', () => resolve(data));
50
+ process.stdin.on('error', reject);
51
+ });
52
+ }
53
+
54
+ /**
55
+ * Read stdin synchronously with TTY check
56
+ * @returns {string} The stdin content or empty string if TTY
57
+ */
58
+ function readStdinSync() {
59
+ if (process.stdin.isTTY) {
60
+ return '';
61
+ }
62
+ return fs.readFileSync(0, 'utf8');
63
+ }
64
+
65
+ /**
66
+ * Parse JSON safely, return null on failure
67
+ * @param {string} str - JSON string to parse
68
+ * @param {string} hookName - Hook name for error logging
69
+ * @returns {object|null}
70
+ */
71
+ function safeJsonParse(str, hookName = 'hook') {
72
+ try {
73
+ return JSON.parse(str);
74
+ } catch (e) {
75
+ console.error(`[${hookName}] Failed to parse JSON: ${e.message}`);
76
+ return null;
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Output allow decision and exit
82
+ * @param {string} [message] - Optional message to log to stderr
83
+ */
84
+ function outputAllow(message) {
85
+ if (message) {
86
+ console.error(message);
87
+ }
88
+ console.log(JSON.stringify({ decision: 'allow' }));
89
+ process.exit(0);
90
+ }
91
+
92
+ /**
93
+ * Output deny decision and exit
94
+ * @param {string} reason - Reason for denial
95
+ */
96
+ function outputDeny(reason) {
97
+ console.log(JSON.stringify({
98
+ permissionDecision: 'deny',
99
+ permissionDecisionReason: reason
100
+ }));
101
+ process.exit(0);
102
+ }
103
+
104
+ /**
105
+ * Get project directory from environment
106
+ * @returns {string}
107
+ */
108
+ function getProjectDir() {
109
+ return process.env.CLAUDE_PROJECT_DIR || process.cwd();
110
+ }
111
+
112
+ /**
113
+ * Check if a path exists
114
+ * @param {string} filePath
115
+ * @returns {boolean}
116
+ */
117
+ function fileExists(filePath) {
118
+ try {
119
+ return fs.existsSync(filePath);
120
+ } catch {
121
+ return false;
122
+ }
123
+ }
124
+
125
+ module.exports = {
126
+ isYoloMode,
127
+ readStdin,
128
+ readStdinSync,
129
+ safeJsonParse,
130
+ outputAllow,
131
+ outputDeny,
132
+ getProjectDir,
133
+ fileExists
134
+ };
@@ -22,20 +22,42 @@
22
22
  * <behavior>
23
23
  * 1. Detects failure patterns in agent response
24
24
  * 2. Categorizes error type
25
- * 3. Tracks per-agent failure count
26
- * 4. After 2+ failures, suggests fix via AskUserQuestion
27
- * 5. Recommends spawning ada to create skill or update agent
25
+ * 3. Logs to global failures.log
26
+ * 4. Auto-spawns Ada on first actionable failure to create skill or update agent
28
27
  * </behavior>
29
28
  *
30
29
  * <tracker-file>/tmp/.claude-agent-failures.json</tracker-file>
30
+ * <log-file>$CLAUDE_PROJECT_DIR/inbox/failures.log</log-file>
31
31
  */
32
32
 
33
33
  const fs = require('fs');
34
34
  const path = require('path');
35
35
  const os = require('os');
36
36
 
37
- const TRACKER_FILE = path.join(os.tmpdir(), '.claude-agent-failures.json');
38
- const FAILURE_THRESHOLD = 2;
37
+ // Skip in yolo mode
38
+ try {
39
+ const statePath = path.join(process.env.CLAUDE_PROJECT_DIR || process.cwd(), '.claude', 'yolo-state.json');
40
+ const state = JSON.parse(fs.readFileSync(statePath, 'utf8'));
41
+ if (state.mode === 'yolo') process.exit(0);
42
+ } catch (e) {
43
+ // ENOENT is expected when not in yolo mode - only warn on unexpected errors
44
+ if (e.code !== 'ENOENT' && !e.message.includes('Unexpected')) {
45
+ console.error(`[agent-failure-detector] Warning: ${e.message}`);
46
+ }
47
+ }
48
+
49
+ const TEMP_DIR = os.tmpdir();
50
+ const TRACKER_FILE = path.join(TEMP_DIR, '.claude-agent-failures.json');
51
+ const GLOBAL_LOG_FILE = path.join(
52
+ process.env.HAILER_INBOX_DIR || path.join(process.env.CLAUDE_PROJECT_DIR || process.cwd(), 'inbox'),
53
+ 'failures.log'
54
+ );
55
+ const FAILURE_THRESHOLD = 1; // Auto-spawn Ada on first failure
56
+
57
+ // Skip if stdin is TTY (no piped input)
58
+ if (process.stdin.isTTY) {
59
+ process.exit(0);
60
+ }
39
61
 
40
62
  let input = '';
41
63
  process.stdin.setEncoding('utf8');
@@ -44,7 +66,8 @@ process.stdin.on('end', () => {
44
66
  try {
45
67
  const data = JSON.parse(input);
46
68
  processHook(data);
47
- } catch {
69
+ } catch (e) {
70
+ console.error(`[agent-failure-detector] Failed to parse input: ${e.message}`);
48
71
  process.exit(0);
49
72
  }
50
73
  });
@@ -54,7 +77,9 @@ function loadTracker() {
54
77
  if (fs.existsSync(TRACKER_FILE)) {
55
78
  return JSON.parse(fs.readFileSync(TRACKER_FILE, 'utf8'));
56
79
  }
57
- } catch {}
80
+ } catch (e) {
81
+ console.error(`[agent-failure-detector] Warning: ${e.message}`);
82
+ }
58
83
  return { failures: {}, prompted: {}, errorTypes: {} };
59
84
  }
60
85
 
@@ -62,6 +87,28 @@ function saveTracker(tracker) {
62
87
  fs.writeFileSync(TRACKER_FILE, JSON.stringify(tracker, null, 2));
63
88
  }
64
89
 
90
+ /**
91
+ * Log failure to global file for persistent tracking
92
+ */
93
+ function logFailureToGlobal(agentName, errorInfo, errorMessage, projectDir) {
94
+ try {
95
+ const timestamp = new Date().toISOString();
96
+ const project = projectDir ? path.basename(projectDir) : 'unknown';
97
+ const logEntry = `[${timestamp}] ${agentName} | ${errorInfo.category} | ${project} | ${errorMessage}\n`;
98
+
99
+ // Ensure ~/.claude directory exists
100
+ const claudeDir = path.dirname(GLOBAL_LOG_FILE);
101
+ if (!fs.existsSync(claudeDir)) {
102
+ fs.mkdirSync(claudeDir, { recursive: true });
103
+ }
104
+
105
+ fs.appendFileSync(GLOBAL_LOG_FILE, logEntry);
106
+ } catch (err) {
107
+ // Don't fail the hook if logging fails
108
+ console.error(`[agent-failure-detector] Could not write to log: ${err.message}`);
109
+ }
110
+ }
111
+
65
112
  // Error categorization - determines fix type
66
113
  const ERROR_CATEGORIES = {
67
114
  // API/Schema errors -> Create skill (reusable knowledge)
@@ -79,7 +126,23 @@ const ERROR_CATEGORIES = {
79
126
  'activitylink',
80
127
  'workflow.*not found',
81
128
  'phase.*not found',
82
- 'field.*not found'
129
+ 'field.*not found',
130
+ 'functionvariables',
131
+ 'functionfield',
132
+ 'namefunction',
133
+ 'wrong.*type',
134
+ 'expected.*got',
135
+ 'cannot read prop',
136
+ 'undefined.*null',
137
+ 'processid',
138
+ 'phaseid',
139
+ 'fieldid',
140
+ 'workflowid',
141
+ 'insight.*error',
142
+ 'template.*error',
143
+ 'pdfmake',
144
+ 'mcp.*error',
145
+ 'tool.*failed'
83
146
  ],
84
147
  suggestion: 'Create a skill documenting the correct pattern',
85
148
  action: 'create_skill'
@@ -95,7 +158,18 @@ const ERROR_CATEGORIES = {
95
158
  'didn\'t check',
96
159
  'missing context',
97
160
  'needs to first',
98
- 'order of operations'
161
+ 'order of operations',
162
+ 'wrong agent',
163
+ 'not my responsibility',
164
+ 'outside.*scope',
165
+ 'cannot handle',
166
+ 'don\'t have.*tool',
167
+ 'need.*first',
168
+ 'pull.*before',
169
+ 'push.*after',
170
+ 'didn\'t read',
171
+ 'assumed',
172
+ 'skipped'
99
173
  ],
100
174
  suggestion: 'Update agent definition with better instructions',
101
175
  action: 'update_agent'
@@ -107,7 +181,16 @@ const ERROR_CATEGORIES = {
107
181
  'unauthorized',
108
182
  'access denied',
109
183
  'not allowed',
110
- 'admin required'
184
+ 'admin required',
185
+ '401',
186
+ '403',
187
+ 'oauth',
188
+ 'token.*expired',
189
+ 'token.*invalid',
190
+ 'authentication_error',
191
+ 'credentials',
192
+ 'login failed',
193
+ 'session expired'
111
194
  ],
112
195
  suggestion: 'User permission issue - not an agent problem',
113
196
  action: 'ignore'
@@ -120,7 +203,18 @@ const ERROR_CATEGORIES = {
120
203
  'network',
121
204
  'unavailable',
122
205
  'try again',
123
- 'rate limit'
206
+ 'rate limit',
207
+ 'econnrefused',
208
+ 'enotfound',
209
+ 'socket',
210
+ '500',
211
+ '502',
212
+ '503',
213
+ '504',
214
+ 'server error',
215
+ 'temporarily',
216
+ 'overloaded',
217
+ 'busy'
124
218
  ],
125
219
  suggestion: 'Transient error - retry may work',
126
220
  action: 'ignore'
@@ -174,22 +268,36 @@ function detectFailure(responseText) {
174
268
  }
175
269
 
176
270
  function extractErrorMessage(responseText) {
177
- // Try to extract meaningful error message
271
+ // Try to extract meaningful error message - more patterns
178
272
  const patterns = [
179
273
  /"error":\s*"([^"]+)"/i,
180
274
  /"message":\s*"([^"]+)"/i,
275
+ /"summary":\s*"([^"]+)"/i,
276
+ /"reason":\s*"([^"]+)"/i,
181
277
  /Error:\s*([^\n]+)/i,
182
- /failed to ([^\n.]+)/i
278
+ /failed to ([^\n.]+)/i,
279
+ /could not ([^\n.]+)/i,
280
+ /unable to ([^\n.]+)/i,
281
+ /invalid ([^\n.]+)/i,
282
+ /missing ([^\n.]+)/i,
283
+ /\d{3}\s*[{"]([^"}\n]+)/i, // HTTP status followed by message
284
+ /"status":\s*"error"[^}]*"([^"]+)"/i
183
285
  ];
184
286
 
185
287
  for (const pattern of patterns) {
186
288
  const match = responseText.match(pattern);
187
- if (match) {
188
- return match[1].substring(0, 100); // Limit length
289
+ if (match && match[1] && match[1].trim().length > 3) {
290
+ return match[1].trim().substring(0, 150); // Limit length
189
291
  }
190
292
  }
191
293
 
192
- return 'Unknown error';
294
+ // Last resort: grab first 100 chars of anything that looks like an error
295
+ const errorSnippet = responseText.match(/error[^a-z]*(.{10,100})/i);
296
+ if (errorSnippet) {
297
+ return errorSnippet[1].trim().substring(0, 100);
298
+ }
299
+
300
+ return 'Unknown error - check agent output';
193
301
  }
194
302
 
195
303
  function processHook(data) {
@@ -221,7 +329,11 @@ function processHook(data) {
221
329
  process.exit(0);
222
330
  }
223
331
 
224
- // Track the failure with category
332
+ // Log to global file for persistent tracking
333
+ const projectDir = process.env.CLAUDE_PROJECT_DIR;
334
+ logFailureToGlobal(agentName, errorInfo, errorMessage, projectDir);
335
+
336
+ // Track the failure with category (session-level tracking for prompts)
225
337
  const tracker = loadTracker();
226
338
  const key = `${agentName}:${errorInfo.category}`;
227
339
  tracker.failures[key] = (tracker.failures[key] || 0) + 1;
@@ -234,76 +346,49 @@ function processHook(data) {
234
346
  tracker.prompted[key] = true;
235
347
  saveTracker(tracker);
236
348
 
237
- // Build suggestion based on category
238
- let fixOptions;
239
- let recommendation;
240
-
241
- if (errorInfo.action === 'create_skill') {
242
- recommendation = `📚 RECOMMENDED: Create a skill to document this pattern`;
243
- fixOptions = [
244
- { label: 'Create skill', description: `Ada creates docs/skills/ entry for ${errorInfo.matchedPattern || 'this pattern'}` },
245
- { label: 'Update agent instead', description: `Add knowledge directly to ${agentName}'s definition` },
246
- { label: 'Ignore', description: 'Continue without changes' }
247
- ];
248
- } else if (errorInfo.action === 'update_agent') {
249
- recommendation = `🤖 RECOMMENDED: Update ${agentName}'s agent definition`;
250
- fixOptions = [
251
- { label: 'Update agent', description: `Ada adds better instructions to ${agentName}.md` },
252
- { label: 'Create skill instead', description: 'Document as reusable skill for all agents' },
253
- { label: 'Ignore', description: 'Continue without changes' }
254
- ];
255
- } else {
256
- recommendation = `🔍 ANALYZE: Let Ada determine the best fix`;
257
- fixOptions = [
258
- { label: 'Let Ada analyze', description: 'Ada will determine if skill or agent update is better' },
259
- { label: 'Ignore', description: 'Continue without changes' }
260
- ];
349
+ // Determine action type
350
+ let actionType = errorInfo.action;
351
+ if (actionType === 'analyze') {
352
+ actionType = 'create_skill'; // Default to skill creation
261
353
  }
262
354
 
355
+ // Sanitize error message to prevent injection via temp file
356
+ const safeErrorMessage = errorMessage
357
+ .replace(/[`$\\]/g, '')
358
+ .substring(0, 500);
359
+
360
+ // Build the Ada task prompt
361
+ const adaPromptObj = {
362
+ task: actionType,
363
+ agent: agentName,
364
+ error: safeErrorMessage,
365
+ category: errorInfo.category,
366
+ pattern: errorInfo.matchedPattern || 'unknown'
367
+ };
368
+
369
+ // SECURITY: Write prompt to temp file to avoid shell injection
370
+ // Never embed user-controlled strings directly in shell commands
371
+ const tmpDir = os.tmpdir();
372
+ const tmpFile = path.join(tmpDir, `ada-prompt-${Date.now()}-${process.pid}.json`);
373
+ fs.writeFileSync(tmpFile, JSON.stringify(adaPromptObj, null, 2));
374
+
375
+ // AUTO-SPAWN Ada on first actionable failure
376
+ // The orchestrator reads the temp file path and handles the Task call safely
263
377
  const output = `
264
- ${'='.repeat(60)}
265
- 🔧 AGENT IMPROVEMENT SUGGESTION
266
- ${'='.repeat(60)}
378
+ <user-prompt-submit-hook>
379
+ 🤖 AUTO-SPAWNING ADA - ${errorInfo.category} failure detected
267
380
 
268
- Agent "${agentName}" has failed ${failureCount} times with similar errors.
381
+ Agent: ${agentName}
382
+ Action: ${actionType === 'create_skill' ? 'Creating skill' : 'Updating agent'}
269
383
 
270
- **Error type:** ${errorInfo.category}
271
- **Error:** ${errorMessage}
272
- **Pattern:** ${errorInfo.matchedPattern || 'general failure'}
273
-
274
- ${recommendation}
275
-
276
- USE THIS AskUserQuestion CALL:
277
-
278
- \`\`\`json
279
- {
280
- "questions": [{
281
- "question": "${agentName} failed ${failureCount}x (${errorInfo.category}). How should Ada fix this?",
282
- "header": "Fix type",
283
- "options": ${JSON.stringify(fixOptions)},
284
- "multiSelect": false
285
- }]
286
- }
287
- \`\`\`
288
-
289
- After user responds, invoke Ada with context:
290
- \`\`\`
291
- Task(subagent_type="ada", prompt=\`{
292
- "task": "${errorInfo.action === 'create_skill' ? 'create_skill' : 'update_agent'}",
293
- "context": {
294
- "agent": "${agentName}",
295
- "error_type": "${errorInfo.category}",
296
- "error_message": "${errorMessage.replace(/"/g, '\\"')}",
297
- "failure_count": ${failureCount}
298
- },
299
- "output": ["action_taken", "file_path"]
300
- }\`)
301
- \`\`\`
384
+ Ada prompt saved to: ${tmpFile}
302
385
 
303
- ${'='.repeat(60)}
386
+ ORCHESTRATOR: Read the JSON file above and call:
387
+ Task(subagent_type="agent-ada-skill-builder", prompt=<contents of ${tmpFile}>)
388
+ </user-prompt-submit-hook>
304
389
  `;
305
390
 
306
- console.error(output);
391
+ console.log(output);
307
392
  }
308
393
 
309
394
  process.exit(0);
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * <hook-name>agent-usage-logger</hook-name>
4
+ *
5
+ * <purpose>
6
+ * Logs every agent call for analytics - which agents are used, success/fail rates, duration.
7
+ * </purpose>
8
+ *
9
+ * <triggers>
10
+ * - PostToolUse on Task
11
+ * </triggers>
12
+ *
13
+ * <log-file>$CLAUDE_PROJECT_DIR/inbox/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
+ '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(`[agent-usage-logger] Failed to parse input: ${e.message}`);
35
+ process.exit(0);
36
+ }
37
+ });
38
+
39
+ // Cache directory creation state to avoid repeated mkdirSync calls
40
+ let _dirCreated = false;
41
+
42
+ function detectStatus(responseText) {
43
+ // Try to parse as JSON first for reliable status detection
44
+ try {
45
+ const parsed = JSON.parse(responseText);
46
+ if (parsed.status === 'success' || parsed.status === 'ready_to_push') {
47
+ return 'success';
48
+ }
49
+ if (parsed.status === 'error') {
50
+ return 'error';
51
+ }
52
+ } catch (e) {
53
+ // Not valid JSON, fall back to string matching
54
+ }
55
+
56
+ // Fallback: string matching for non-JSON responses
57
+ const lower = responseText.toLowerCase();
58
+
59
+ if (lower.includes('"status":"success"') || lower.includes('"status": "success"')) {
60
+ return 'success';
61
+ }
62
+ if (lower.includes('"status":"ready_to_push"') || lower.includes('"status": "ready_to_push"')) {
63
+ return 'success';
64
+ }
65
+ if (lower.includes('"status":"error"') || lower.includes('"status": "error"')) {
66
+ return 'error';
67
+ }
68
+ if (lower.includes('failed') || lower.includes('error:') || lower.includes('could not')) {
69
+ return 'error';
70
+ }
71
+
72
+ return 'unknown';
73
+ }
74
+
75
+ function processHook(data) {
76
+ const { tool_name, tool_input, tool_response } = data;
77
+
78
+ if (tool_name !== 'Task') {
79
+ process.exit(0);
80
+ }
81
+
82
+ const agentName = tool_input?.subagent_type || 'unknown';
83
+ const description = tool_input?.description || '';
84
+ const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
85
+ const project = path.basename(projectDir);
86
+
87
+ const responseText = typeof tool_response === 'string'
88
+ ? tool_response
89
+ : JSON.stringify(tool_response || '');
90
+
91
+ const status = detectStatus(responseText);
92
+
93
+ const entry = {
94
+ ts: new Date().toISOString(),
95
+ agent: agentName,
96
+ status,
97
+ project,
98
+ description: (description || '').substring(0, 50)
99
+ };
100
+
101
+ try {
102
+ // Ensure directory exists (recursive: true is idempotent, no TOCTOU race)
103
+ if (!_dirCreated) {
104
+ const claudeDir = path.dirname(LOG_FILE);
105
+ fs.mkdirSync(claudeDir, { recursive: true });
106
+ _dirCreated = true;
107
+ }
108
+
109
+ fs.appendFileSync(LOG_FILE, JSON.stringify(entry) + '\n');
110
+ } catch (err) {
111
+ // Don't fail the hook if logging fails
112
+ console.error(`[agent-usage-logger] Could not write: ${err.message}`);
113
+ }
114
+
115
+ process.exit(0);
116
+ }
117
+
118
+ // CLI: Show recent usage
119
+ if (process.argv[2] === '--recent' || process.argv[2] === '-r') {
120
+ const limit = parseInt(process.argv[3]) || 20;
121
+
122
+ if (!fs.existsSync(LOG_FILE)) {
123
+ console.log('No usage data yet');
124
+ process.exit(0);
125
+ }
126
+
127
+ const lines = fs.readFileSync(LOG_FILE, 'utf8').trim().split('\n').filter(Boolean);
128
+ const recent = lines.slice(-limit);
129
+
130
+ console.log(`Last ${recent.length} agent calls:\n`);
131
+ for (const line of recent) {
132
+ try {
133
+ const entry = JSON.parse(line);
134
+ const status = entry.status === 'success' ? '✓' : entry.status === 'error' ? '✗' : '?';
135
+ const time = entry.ts.split('T')[1].split('.')[0];
136
+ console.log(`${time} ${status} ${entry.agent.padEnd(20)} ${entry.project}`);
137
+ } catch {}
138
+ }
139
+ process.exit(0);
140
+ }
141
+
142
+ // CLI: Show stats
143
+ if (process.argv[2] === '--stats' || process.argv[2] === '-s') {
144
+ if (!fs.existsSync(LOG_FILE)) {
145
+ console.log('No usage data yet');
146
+ process.exit(0);
147
+ }
148
+
149
+ const lines = fs.readFileSync(LOG_FILE, 'utf8').trim().split('\n').filter(Boolean);
150
+ const stats = {};
151
+
152
+ for (const line of lines) {
153
+ try {
154
+ const entry = JSON.parse(line);
155
+ if (!stats[entry.agent]) {
156
+ stats[entry.agent] = { total: 0, success: 0, error: 0 };
157
+ }
158
+ stats[entry.agent].total++;
159
+ if (entry.status === 'success') stats[entry.agent].success++;
160
+ if (entry.status === 'error') stats[entry.agent].error++;
161
+ } catch {}
162
+ }
163
+
164
+ console.log('Agent Usage Stats:\n');
165
+ console.log('Agent'.padEnd(25) + 'Total'.padEnd(8) + 'Success'.padEnd(10) + 'Errors');
166
+ console.log('-'.repeat(50));
167
+
168
+ const sorted = Object.entries(stats).sort((a, b) => b[1].total - a[1].total);
169
+ for (const [agent, s] of sorted) {
170
+ const rate = s.total > 0 ? Math.round((s.success / s.total) * 100) : 0;
171
+ console.log(`${agent.padEnd(25)}${String(s.total).padEnd(8)}${(s.success + ' (' + rate + '%)').padEnd(10)}${s.error}`);
172
+ }
173
+
174
+ console.log('-'.repeat(50));
175
+ console.log(`Total: ${lines.length} calls`);
176
+ process.exit(0);
177
+ }
178
+
179
+ // CLI: Clear log
180
+ if (process.argv[2] === '--clear') {
181
+ if (fs.existsSync(LOG_FILE)) {
182
+ fs.unlinkSync(LOG_FILE);
183
+ console.log('✓ Usage log cleared');
184
+ } else {
185
+ console.log('No log file to clear');
186
+ }
187
+ process.exit(0);
188
+ }
189
+
190
+ // CLI: Help
191
+ if (process.argv[2] === '--help' || process.argv[2] === '-h') {
192
+ console.log(`
193
+ Agent Usage Logger - Track agent calls
194
+
195
+ Usage:
196
+ node agent-usage-logger.cjs --recent [N] Show last N calls (default 20)
197
+ node agent-usage-logger.cjs --stats Show usage statistics
198
+ node agent-usage-logger.cjs --clear Clear the log
199
+ node agent-usage-logger.cjs --help Show this help
200
+
201
+ Log file: ${LOG_FILE}
202
+ `);
203
+ process.exit(0);
204
+ }