@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.
- package/.claude/.session-checked +1 -0
- package/.claude/agents/agent-ada-skill-builder.md +10 -2
- package/.claude/agents/agent-alejandro-function-fields.md +104 -37
- package/.claude/agents/agent-bjorn-config-audit.md +41 -21
- package/.claude/agents/agent-builder-agent-creator.md +13 -3
- package/.claude/agents/agent-code-simplifier.md +53 -0
- package/.claude/agents/agent-dmitri-activity-crud.md +126 -11
- package/.claude/agents/agent-giuseppe-app-builder.md +212 -22
- package/.claude/agents/agent-gunther-mcp-tools.md +7 -36
- package/.claude/agents/agent-helga-workflow-config.md +75 -10
- package/.claude/agents/agent-igor-activity-mover-automation.md +125 -0
- package/.claude/agents/agent-ingrid-doc-templates.md +164 -36
- package/.claude/agents/agent-ivan-monolith.md +154 -0
- package/.claude/agents/agent-kenji-data-reader.md +15 -8
- package/.claude/agents/agent-lars-code-inspector.md +56 -8
- package/.claude/agents/agent-marco-mockup-builder.md +110 -0
- package/.claude/agents/agent-marcus-api-documenter.md +323 -0
- package/.claude/agents/agent-marketplace-publisher.md +232 -72
- package/.claude/agents/agent-marketplace-reviewer.md +255 -79
- package/.claude/agents/agent-permissions-handler.md +208 -0
- package/.claude/agents/agent-simple-writer.md +48 -0
- package/.claude/agents/agent-svetlana-code-review.md +127 -14
- package/.claude/agents/agent-tanya-test-runner.md +333 -0
- package/.claude/agents/agent-ui-designer.md +100 -0
- package/.claude/agents/agent-viktor-sql-insights.md +19 -6
- package/.claude/agents/agent-web-search.md +55 -0
- package/.claude/agents/agent-yevgeni-discussions.md +7 -1
- package/.claude/agents/agent-zara-zapier.md +159 -0
- package/.claude/commands/app-squad.md +135 -0
- package/.claude/commands/audit-squad.md +158 -0
- package/.claude/commands/autoplan.md +563 -0
- package/.claude/commands/cleanup-squad.md +98 -0
- package/.claude/commands/config-squad.md +106 -0
- package/.claude/commands/crud-squad.md +87 -0
- package/.claude/commands/data-squad.md +97 -0
- package/.claude/commands/debug-squad.md +303 -0
- package/.claude/commands/doc-squad.md +65 -0
- package/.claude/commands/handoff.md +137 -0
- package/.claude/commands/health.md +49 -0
- package/.claude/commands/help.md +2 -1
- package/.claude/commands/help:agents.md +96 -16
- package/.claude/commands/help:commands.md +55 -11
- package/.claude/commands/help:faq.md +16 -1
- package/.claude/commands/help:skills.md +93 -0
- package/.claude/commands/hotfix-squad.md +112 -0
- package/.claude/commands/integration-squad.md +82 -0
- package/.claude/commands/janitor-squad.md +167 -0
- package/.claude/commands/learn-auto.md +120 -0
- package/.claude/commands/learn.md +120 -0
- package/.claude/commands/mcp-list.md +27 -0
- package/.claude/commands/onboard-squad.md +140 -0
- package/.claude/commands/plan-workspace.md +732 -0
- package/.claude/commands/prd.md +131 -0
- package/.claude/commands/project-status.md +82 -0
- package/.claude/commands/publish.md +138 -0
- package/.claude/commands/recap.md +69 -0
- package/.claude/commands/restore.md +64 -0
- package/.claude/commands/review-squad.md +152 -0
- package/.claude/commands/save.md +24 -0
- package/.claude/commands/stats.md +19 -0
- package/.claude/commands/swarm.md +210 -0
- package/.claude/commands/tool-builder.md +3 -1
- package/.claude/commands/ws-pull.md +1 -1
- package/.claude/commands/yolo-off.md +17 -0
- package/.claude/commands/yolo.md +82 -0
- package/.claude/hooks/_shared-memory.cjs +305 -0
- package/.claude/hooks/_utils.cjs +134 -0
- package/.claude/hooks/agent-failure-detector.cjs +164 -79
- package/.claude/hooks/agent-usage-logger.cjs +204 -0
- package/.claude/hooks/app-edit-guard.cjs +20 -4
- package/.claude/hooks/auto-learn.cjs +316 -0
- package/.claude/hooks/bash-guard.cjs +282 -0
- package/.claude/hooks/builder-mode-manager.cjs +183 -54
- package/.claude/hooks/bulk-activity-guard.cjs +283 -0
- package/.claude/hooks/context-watchdog.cjs +292 -0
- package/.claude/hooks/delegation-reminder.cjs +478 -0
- package/.claude/hooks/design-system-lint.cjs +283 -0
- package/.claude/hooks/post-scaffold-hook.cjs +16 -3
- package/.claude/hooks/prompt-guard.cjs +366 -0
- package/.claude/hooks/publish-template-guard.cjs +16 -0
- package/.claude/hooks/session-start.cjs +35 -0
- package/.claude/hooks/shared-memory-writer.cjs +147 -0
- package/.claude/hooks/skill-injector.cjs +140 -0
- package/.claude/hooks/skill-usage-logger.cjs +258 -0
- package/.claude/hooks/src-edit-guard.cjs +16 -1
- package/.claude/hooks/sync-marketplace-agents.cjs +53 -8
- package/.claude/scripts/yolo-toggle.cjs +142 -0
- package/.claude/settings.json +141 -14
- package/.claude/skills/SDK-activity-patterns/SKILL.md +428 -0
- package/.claude/skills/SDK-document-templates/SKILL.md +1033 -0
- package/.claude/skills/SDK-function-fields/SKILL.md +542 -0
- package/.claude/skills/SDK-generate-skill/SKILL.md +92 -0
- package/.claude/skills/SDK-init-skill/SKILL.md +127 -0
- package/.claude/skills/SDK-insight-queries/SKILL.md +787 -0
- package/.claude/skills/SDK-ws-config-skill/SKILL.md +1139 -0
- package/.claude/skills/agent-structure/SKILL.md +98 -0
- package/.claude/skills/api-documentation-patterns/SKILL.md +474 -0
- package/.claude/skills/chrome-mcp-reference/SKILL.md +370 -0
- package/.claude/skills/delegation-routing/SKILL.md +202 -0
- package/.claude/skills/frontend-design/SKILL.md +254 -0
- package/.claude/skills/hailer-activity-mover/SKILL.md +213 -0
- package/.claude/skills/hailer-api-client/SKILL.md +518 -0
- package/.claude/skills/hailer-app-builder/SKILL.md +939 -11
- package/.claude/skills/hailer-apps-pictures/SKILL.md +269 -0
- package/.claude/skills/hailer-design-system/SKILL.md +235 -0
- package/.claude/skills/hailer-monolith-automations/SKILL.md +686 -0
- package/.claude/skills/hailer-permissions-system/SKILL.md +121 -0
- package/.claude/skills/hailer-project-protocol/SKILL.md +488 -0
- package/.claude/skills/hailer-rest-api/SKILL.md +61 -0
- package/.claude/skills/hailer-rest-api/hailer-activities.md +184 -0
- package/.claude/skills/hailer-rest-api/hailer-admin.md +473 -0
- package/.claude/skills/hailer-rest-api/hailer-calendar.md +256 -0
- package/.claude/skills/hailer-rest-api/hailer-feed.md +249 -0
- package/.claude/skills/hailer-rest-api/hailer-insights.md +195 -0
- package/.claude/skills/hailer-rest-api/hailer-messaging.md +276 -0
- package/.claude/skills/hailer-rest-api/hailer-workflows.md +283 -0
- package/.claude/skills/insight-join-patterns/SKILL.md +3 -0
- package/.claude/skills/integration-patterns/SKILL.md +421 -0
- package/.claude/skills/json-only-output/SKILL.md +52 -12
- package/.claude/skills/lsp-setup/SKILL.md +160 -0
- package/.claude/skills/mcp-direct-tools/SKILL.md +153 -0
- package/.claude/skills/optional-parameters/SKILL.md +32 -23
- package/.claude/skills/publish-hailer-app/SKILL.md +76 -12
- package/.claude/skills/testing-patterns/SKILL.md +630 -0
- package/.claude/skills/tool-builder/SKILL.md +250 -0
- package/.claude/skills/tool-parameter-usage/SKILL.md +59 -45
- package/.claude/skills/tool-response-verification/SKILL.md +82 -48
- package/.claude/skills/zapier-hailer-patterns/SKILL.md +581 -0
- package/.env.example +26 -7
- package/CLAUDE.md +290 -224
- package/dist/CLAUDE.md +370 -0
- package/dist/app.d.ts +1 -1
- package/dist/app.js +101 -101
- package/dist/bot/bot-config.d.ts +26 -0
- package/dist/bot/bot-config.js +135 -0
- package/dist/bot/bot-manager.d.ts +40 -0
- package/dist/bot/bot-manager.js +137 -0
- package/dist/bot/bot.d.ts +127 -0
- package/dist/bot/bot.js +1328 -0
- package/dist/bot/operation-logger.d.ts +28 -0
- package/dist/bot/operation-logger.js +132 -0
- package/dist/bot/services/conversation-manager.d.ts +60 -0
- package/dist/bot/services/conversation-manager.js +246 -0
- package/dist/bot/services/index.d.ts +9 -0
- package/dist/bot/services/index.js +18 -0
- package/dist/bot/services/message-classifier.d.ts +42 -0
- package/dist/bot/services/message-classifier.js +228 -0
- package/dist/bot/services/message-formatter.d.ts +88 -0
- package/dist/bot/services/message-formatter.js +411 -0
- package/dist/bot/services/session-logger.d.ts +162 -0
- package/dist/bot/services/session-logger.js +724 -0
- package/dist/bot/services/token-billing.d.ts +78 -0
- package/dist/bot/services/token-billing.js +233 -0
- package/dist/bot/services/types.d.ts +169 -0
- package/dist/bot/services/types.js +12 -0
- package/dist/bot/services/typing-indicator.d.ts +23 -0
- package/dist/bot/services/typing-indicator.js +60 -0
- package/dist/bot/services/workspace-schema-cache.d.ts +122 -0
- package/dist/bot/services/workspace-schema-cache.js +506 -0
- package/dist/bot/tool-executor.d.ts +28 -0
- package/dist/bot/tool-executor.js +48 -0
- package/dist/bot/workspace-overview.d.ts +12 -0
- package/dist/bot/workspace-overview.js +94 -0
- package/dist/cli.d.ts +1 -8
- package/dist/cli.js +1 -249
- package/dist/config.d.ts +96 -3
- package/dist/config.js +148 -37
- package/dist/core.d.ts +5 -0
- package/dist/core.js +61 -8
- package/dist/lib/discussion-lock.d.ts +42 -0
- package/dist/lib/discussion-lock.js +110 -0
- package/dist/lib/logger.d.ts +0 -1
- package/dist/lib/logger.js +39 -23
- package/dist/lib/request-logger.d.ts +77 -0
- package/dist/lib/request-logger.js +147 -0
- package/dist/mcp/UserContextCache.js +16 -13
- package/dist/mcp/hailer-clients.js +18 -17
- package/dist/mcp/signal-handler.js +29 -13
- package/dist/mcp/tool-registry.d.ts +4 -15
- package/dist/mcp/tool-registry.js +94 -32
- package/dist/mcp/tools/activity.js +28 -69
- package/dist/mcp/tools/app-core.js +9 -4
- package/dist/mcp/tools/app-marketplace.js +22 -12
- package/dist/mcp/tools/app-member.js +5 -2
- package/dist/mcp/tools/app-scaffold.js +32 -18
- package/dist/mcp/tools/bot-config/constants.d.ts +23 -0
- package/dist/mcp/tools/bot-config/constants.js +94 -0
- package/dist/mcp/tools/bot-config/core.d.ts +253 -0
- package/dist/mcp/tools/bot-config/core.js +2456 -0
- package/dist/mcp/tools/bot-config/index.d.ts +10 -0
- package/dist/mcp/tools/bot-config/index.js +59 -0
- package/dist/mcp/tools/bot-config/tools.d.ts +7 -0
- package/dist/mcp/tools/bot-config/tools.js +15 -0
- package/dist/mcp/tools/bot-config/types.d.ts +50 -0
- package/dist/mcp/tools/bot-config/types.js +6 -0
- package/dist/mcp/tools/discussion.js +107 -77
- package/dist/mcp/tools/document.d.ts +11 -0
- package/dist/mcp/tools/document.js +741 -0
- package/dist/mcp/tools/file.js +5 -2
- package/dist/mcp/tools/insight.js +36 -12
- package/dist/mcp/tools/investigate.d.ts +9 -0
- package/dist/mcp/tools/investigate.js +254 -0
- package/dist/mcp/tools/user.d.ts +2 -4
- package/dist/mcp/tools/user.js +9 -50
- package/dist/mcp/tools/workflow.d.ts +1 -0
- package/dist/mcp/tools/workflow.js +164 -52
- package/dist/mcp/utils/hailer-api-client.js +26 -17
- package/dist/mcp/webhook-handler.d.ts +64 -3
- package/dist/mcp/webhook-handler.js +219 -9
- package/dist/mcp-server.d.ts +4 -0
- package/dist/mcp-server.js +237 -25
- package/dist/plugins/bug-fixer/index.d.ts +2 -0
- package/dist/plugins/bug-fixer/index.js +18 -0
- package/dist/plugins/bug-fixer/tools.d.ts +45 -0
- package/dist/plugins/bug-fixer/tools.js +1096 -0
- package/package.json +10 -10
- package/scripts/test-hal-tools.ts +154 -0
- package/.claude/agents/agent-nora-name-functions.md +0 -123
- package/.claude/assistant-knowledge.md +0 -23
- package/.claude/commands/install-plugin.md +0 -261
- package/.claude/commands/list-plugins.md +0 -42
- package/.claude/commands/marketplace-setup.md +0 -33
- package/.claude/commands/publish-plugin.md +0 -55
- package/.claude/commands/uninstall-plugin.md +0 -87
- package/.claude/hooks/interactive-mode.cjs +0 -87
- package/.claude/hooks/mcp-server-guard.cjs +0 -108
- package/.claude/skills/marketplace-publishing.md +0 -155
- package/dist/bot/chat-bot.d.ts +0 -31
- package/dist/bot/chat-bot.js +0 -357
- package/dist/mcp/tools/metrics.d.ts +0 -13
- package/dist/mcp/tools/metrics.js +0 -546
- package/dist/stdio-server.d.ts +0 -14
- 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.
|
|
26
|
-
* 4.
|
|
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
|
-
|
|
38
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
238
|
-
let
|
|
239
|
-
|
|
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
|
-
|
|
265
|
-
|
|
266
|
-
${'='.repeat(60)}
|
|
378
|
+
<user-prompt-submit-hook>
|
|
379
|
+
🤖 AUTO-SPAWNING ADA - ${errorInfo.category} failure detected
|
|
267
380
|
|
|
268
|
-
Agent
|
|
381
|
+
Agent: ${agentName}
|
|
382
|
+
Action: ${actionType === 'create_skill' ? 'Creating skill' : 'Updating agent'}
|
|
269
383
|
|
|
270
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
+
}
|