@hailer/mcp 1.1.11 → 1.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.js +18 -5
- package/dist/bot/bot-config.d.ts +12 -1
- package/dist/bot/bot-config.js +98 -14
- package/dist/bot/bot-manager.d.ts +13 -3
- package/dist/bot/bot-manager.js +80 -25
- package/dist/bot/bot.d.ts +46 -0
- package/dist/bot/bot.js +542 -166
- package/dist/bot/services/message-classifier.js +17 -0
- package/dist/bot/services/permission-guard.d.ts +52 -0
- package/dist/bot/services/permission-guard.js +149 -0
- package/dist/bot/services/types.d.ts +5 -0
- package/dist/bot/services/typing-indicator.d.ts +6 -1
- package/dist/bot/services/typing-indicator.js +19 -3
- package/dist/config.d.ts +6 -1
- package/dist/config.js +43 -0
- package/dist/core.js +3 -6
- package/dist/mcp/UserContextCache.d.ts +5 -0
- package/dist/mcp/UserContextCache.js +51 -19
- package/dist/mcp/hailer-clients.d.ts +19 -1
- package/dist/mcp/hailer-clients.js +157 -20
- package/dist/mcp/session-store.d.ts +68 -0
- package/dist/mcp/session-store.js +169 -0
- package/dist/mcp/signal-handler.js +12 -12
- package/dist/mcp/tool-registry.d.ts +17 -4
- package/dist/mcp/tool-registry.js +37 -7
- package/dist/mcp/tools/activity.js +99 -7
- package/dist/mcp/tools/app-scaffold.js +304 -336
- package/dist/mcp/tools/company.d.ts +9 -0
- package/dist/mcp/tools/company.js +88 -0
- package/dist/mcp/tools/discussion.js +68 -0
- package/dist/mcp/tools/workflow-permissions.d.ts +15 -0
- package/dist/mcp/tools/workflow-permissions.js +204 -0
- package/dist/mcp/tools/workflow.js +57 -18
- package/dist/mcp/utils/index.d.ts +2 -0
- package/dist/mcp/utils/index.js +12 -1
- package/dist/mcp/utils/role-utils.d.ts +74 -0
- package/dist/mcp/utils/role-utils.js +151 -0
- package/dist/mcp/utils/types.d.ts +43 -1
- package/dist/mcp/utils/types.js +14 -0
- package/dist/mcp/webhook-handler.d.ts +6 -0
- package/dist/mcp/webhook-handler.js +11 -0
- package/dist/mcp-server.d.ts +23 -2
- package/dist/mcp-server.js +639 -111
- package/dist/plugins/vipunen/client.d.ts +150 -0
- package/dist/plugins/vipunen/client.js +535 -0
- package/dist/plugins/vipunen/config/schema-config.json +19 -0
- package/dist/plugins/vipunen/config/schema-doc.json +22 -0
- package/dist/plugins/vipunen/index.d.ts +41 -0
- package/dist/plugins/vipunen/index.js +88 -0
- package/dist/plugins/vipunen/tools.d.ts +26 -0
- package/dist/plugins/vipunen/tools.js +501 -0
- package/package.json +2 -1
- package/.claude/.context-watchdog.json +0 -1
- package/.claude/.session-checked +0 -1
- package/.claude/CLAUDE.md +0 -370
- package/.claude/agents/agent-ada-skill-builder.md +0 -94
- package/.claude/agents/agent-alejandro-function-fields.md +0 -342
- package/.claude/agents/agent-bjorn-config-audit.md +0 -103
- package/.claude/agents/agent-builder-agent-creator.md +0 -130
- package/.claude/agents/agent-code-simplifier.md +0 -53
- package/.claude/agents/agent-dmitri-activity-crud.md +0 -159
- package/.claude/agents/agent-giuseppe-app-builder.md +0 -247
- package/.claude/agents/agent-gunther-mcp-tools.md +0 -39
- package/.claude/agents/agent-helga-workflow-config.md +0 -204
- package/.claude/agents/agent-igor-activity-mover-automation.md +0 -125
- package/.claude/agents/agent-ingrid-doc-templates.md +0 -261
- package/.claude/agents/agent-ivan-monolith.md +0 -154
- package/.claude/agents/agent-kenji-data-reader.md +0 -86
- package/.claude/agents/agent-lars-code-inspector.md +0 -102
- package/.claude/agents/agent-marco-mockup-builder.md +0 -110
- package/.claude/agents/agent-marcus-api-documenter.md +0 -323
- package/.claude/agents/agent-marketplace-publisher.md +0 -280
- package/.claude/agents/agent-marketplace-reviewer.md +0 -309
- package/.claude/agents/agent-permissions-handler.md +0 -208
- package/.claude/agents/agent-simple-writer.md +0 -48
- package/.claude/agents/agent-svetlana-code-review.md +0 -171
- package/.claude/agents/agent-tanya-test-runner.md +0 -333
- package/.claude/agents/agent-ui-designer.md +0 -100
- package/.claude/agents/agent-viktor-sql-insights.md +0 -212
- package/.claude/agents/agent-web-search.md +0 -55
- package/.claude/agents/agent-yevgeni-discussions.md +0 -45
- package/.claude/agents/agent-zara-zapier.md +0 -159
- package/.claude/commands/app-squad.md +0 -135
- package/.claude/commands/audit-squad.md +0 -158
- package/.claude/commands/autoplan.md +0 -563
- package/.claude/commands/cleanup-squad.md +0 -98
- package/.claude/commands/config-squad.md +0 -106
- package/.claude/commands/crud-squad.md +0 -87
- package/.claude/commands/data-squad.md +0 -97
- package/.claude/commands/debug-squad.md +0 -303
- package/.claude/commands/doc-squad.md +0 -65
- package/.claude/commands/handoff.md +0 -137
- package/.claude/commands/health.md +0 -49
- package/.claude/commands/help.md +0 -29
- package/.claude/commands/help:agents.md +0 -151
- package/.claude/commands/help:commands.md +0 -78
- package/.claude/commands/help:faq.md +0 -79
- package/.claude/commands/help:plugins.md +0 -50
- package/.claude/commands/help:skills.md +0 -93
- package/.claude/commands/help:tools.md +0 -75
- package/.claude/commands/hotfix-squad.md +0 -112
- package/.claude/commands/integration-squad.md +0 -82
- package/.claude/commands/janitor-squad.md +0 -167
- package/.claude/commands/learn-auto.md +0 -120
- package/.claude/commands/learn.md +0 -120
- package/.claude/commands/mcp-list.md +0 -27
- package/.claude/commands/onboard-squad.md +0 -140
- package/.claude/commands/plan-workspace.md +0 -732
- package/.claude/commands/prd.md +0 -130
- package/.claude/commands/project-status.md +0 -82
- package/.claude/commands/publish.md +0 -138
- package/.claude/commands/recap.md +0 -69
- package/.claude/commands/restore.md +0 -64
- package/.claude/commands/review-squad.md +0 -152
- package/.claude/commands/save.md +0 -24
- package/.claude/commands/stats.md +0 -19
- package/.claude/commands/swarm.md +0 -210
- package/.claude/commands/tool-builder.md +0 -39
- package/.claude/commands/ws-pull.md +0 -44
- package/.claude/hooks/_shared-memory.cjs +0 -305
- package/.claude/hooks/_utils.cjs +0 -108
- package/.claude/hooks/agent-failure-detector.cjs +0 -383
- package/.claude/hooks/agent-usage-logger.cjs +0 -204
- package/.claude/hooks/app-edit-guard.cjs +0 -494
- package/.claude/hooks/auto-learn.cjs +0 -304
- package/.claude/hooks/bash-guard.cjs +0 -272
- package/.claude/hooks/builder-mode-manager.cjs +0 -354
- package/.claude/hooks/bulk-activity-guard.cjs +0 -271
- package/.claude/hooks/context-watchdog.cjs +0 -230
- package/.claude/hooks/delegation-reminder.cjs +0 -465
- package/.claude/hooks/design-system-lint.cjs +0 -271
- package/.claude/hooks/post-scaffold-hook.cjs +0 -181
- package/.claude/hooks/prompt-guard.cjs +0 -354
- package/.claude/hooks/publish-template-guard.cjs +0 -147
- package/.claude/hooks/session-start.cjs +0 -35
- package/.claude/hooks/shared-memory-writer.cjs +0 -147
- package/.claude/hooks/skill-injector.cjs +0 -140
- package/.claude/hooks/skill-usage-logger.cjs +0 -258
- package/.claude/hooks/src-edit-guard.cjs +0 -240
- package/.claude/hooks/sync-marketplace-agents.cjs +0 -346
- package/.claude/settings.json +0 -257
- package/.claude/skills/SDK-activity-patterns/SKILL.md +0 -428
- package/.claude/skills/SDK-document-templates/SKILL.md +0 -1033
- package/.claude/skills/SDK-function-fields/SKILL.md +0 -542
- package/.claude/skills/SDK-generate-skill/SKILL.md +0 -92
- package/.claude/skills/SDK-init-skill/SKILL.md +0 -127
- package/.claude/skills/SDK-insight-queries/SKILL.md +0 -787
- package/.claude/skills/SDK-ws-config-skill/SKILL.md +0 -1139
- package/.claude/skills/agent-structure/SKILL.md +0 -98
- package/.claude/skills/api-documentation-patterns/SKILL.md +0 -474
- package/.claude/skills/chrome-mcp-reference/SKILL.md +0 -370
- package/.claude/skills/delegation-routing/SKILL.md +0 -202
- package/.claude/skills/frontend-design/SKILL.md +0 -254
- package/.claude/skills/hailer-activity-mover/SKILL.md +0 -213
- package/.claude/skills/hailer-api-client/SKILL.md +0 -518
- package/.claude/skills/hailer-app-builder/SKILL.md +0 -1434
- package/.claude/skills/hailer-apps-pictures/SKILL.md +0 -269
- package/.claude/skills/hailer-design-system/SKILL.md +0 -235
- package/.claude/skills/hailer-monolith-automations/SKILL.md +0 -686
- package/.claude/skills/hailer-permissions-system/SKILL.md +0 -121
- package/.claude/skills/hailer-project-protocol/SKILL.md +0 -488
- package/.claude/skills/hailer-rest-api/SKILL.md +0 -61
- package/.claude/skills/hailer-rest-api/hailer-activities.md +0 -184
- package/.claude/skills/hailer-rest-api/hailer-admin.md +0 -473
- package/.claude/skills/hailer-rest-api/hailer-calendar.md +0 -256
- package/.claude/skills/hailer-rest-api/hailer-feed.md +0 -249
- package/.claude/skills/hailer-rest-api/hailer-insights.md +0 -195
- package/.claude/skills/hailer-rest-api/hailer-messaging.md +0 -276
- package/.claude/skills/hailer-rest-api/hailer-workflows.md +0 -283
- package/.claude/skills/insight-join-patterns/SKILL.md +0 -174
- package/.claude/skills/integration-patterns/SKILL.md +0 -421
- package/.claude/skills/json-only-output/SKILL.md +0 -72
- package/.claude/skills/lsp-setup/SKILL.md +0 -160
- package/.claude/skills/mcp-direct-tools/SKILL.md +0 -153
- package/.claude/skills/optional-parameters/SKILL.md +0 -72
- package/.claude/skills/publish-hailer-app/SKILL.md +0 -244
- package/.claude/skills/testing-patterns/SKILL.md +0 -630
- package/.claude/skills/tool-builder/SKILL.md +0 -250
- package/.claude/skills/tool-parameter-usage/SKILL.md +0 -126
- package/.claude/skills/tool-response-verification/SKILL.md +0 -92
- package/.claude/skills/zapier-hailer-patterns/SKILL.md +0 -581
- package/.hailer-mcp-port +0 -1
- package/.mcp.json +0 -13
- package/.opencode/agent/agent-ada-skill-builder.md +0 -35
- package/.opencode/agent/agent-alejandro-function-fields.md +0 -39
- package/.opencode/agent/agent-bjorn-config-audit.md +0 -36
- package/.opencode/agent/agent-builder-agent-creator.md +0 -39
- package/.opencode/agent/agent-code-simplifier.md +0 -31
- package/.opencode/agent/agent-dmitri-activity-crud.md +0 -40
- package/.opencode/agent/agent-giuseppe-app-builder.md +0 -37
- package/.opencode/agent/agent-gunther-mcp-tools.md +0 -39
- package/.opencode/agent/agent-helga-workflow-config.md +0 -204
- package/.opencode/agent/agent-igor-activity-mover-automation.md +0 -46
- package/.opencode/agent/agent-ingrid-doc-templates.md +0 -39
- package/.opencode/agent/agent-ivan-monolith.md +0 -46
- package/.opencode/agent/agent-kenji-data-reader.md +0 -53
- package/.opencode/agent/agent-lars-code-inspector.md +0 -28
- package/.opencode/agent/agent-marco-mockup-builder.md +0 -42
- package/.opencode/agent/agent-marcus-api-documenter.md +0 -53
- package/.opencode/agent/agent-marketplace-publisher.md +0 -44
- package/.opencode/agent/agent-marketplace-reviewer.md +0 -42
- package/.opencode/agent/agent-permissions-handler.md +0 -50
- package/.opencode/agent/agent-simple-writer.md +0 -45
- package/.opencode/agent/agent-svetlana-code-review.md +0 -39
- package/.opencode/agent/agent-tanya-test-runner.md +0 -57
- package/.opencode/agent/agent-ui-designer.md +0 -56
- package/.opencode/agent/agent-viktor-sql-insights.md +0 -34
- package/.opencode/agent/agent-web-search.md +0 -42
- package/.opencode/agent/agent-yevgeni-discussions.md +0 -37
- package/.opencode/agent/agent-zara-zapier.md +0 -53
- package/.opencode/commands/app-squad.md +0 -135
- package/.opencode/commands/audit-squad.md +0 -158
- package/.opencode/commands/autoplan.md +0 -563
- package/.opencode/commands/cleanup-squad.md +0 -98
- package/.opencode/commands/config-squad.md +0 -106
- package/.opencode/commands/crud-squad.md +0 -87
- package/.opencode/commands/data-squad.md +0 -97
- package/.opencode/commands/debug-squad.md +0 -303
- package/.opencode/commands/doc-squad.md +0 -65
- package/.opencode/commands/handoff.md +0 -137
- package/.opencode/commands/health.md +0 -49
- package/.opencode/commands/help-agents.md +0 -151
- package/.opencode/commands/help-commands.md +0 -32
- package/.opencode/commands/help-faq.md +0 -29
- package/.opencode/commands/help-plugins.md +0 -28
- package/.opencode/commands/help-skills.md +0 -7
- package/.opencode/commands/help-tools.md +0 -40
- package/.opencode/commands/help.md +0 -28
- package/.opencode/commands/hotfix-squad.md +0 -112
- package/.opencode/commands/integration-squad.md +0 -82
- package/.opencode/commands/janitor-squad.md +0 -167
- package/.opencode/commands/learn-auto.md +0 -120
- package/.opencode/commands/learn.md +0 -120
- package/.opencode/commands/mcp-list.md +0 -27
- package/.opencode/commands/onboard-squad.md +0 -140
- package/.opencode/commands/plan-workspace.md +0 -732
- package/.opencode/commands/prd.md +0 -131
- package/.opencode/commands/project-status.md +0 -82
- package/.opencode/commands/publish.md +0 -138
- package/.opencode/commands/recap.md +0 -69
- package/.opencode/commands/restore.md +0 -64
- package/.opencode/commands/review-squad.md +0 -152
- package/.opencode/commands/save.md +0 -24
- package/.opencode/commands/stats.md +0 -19
- package/.opencode/commands/swarm.md +0 -210
- package/.opencode/commands/tool-builder.md +0 -39
- package/.opencode/commands/ws-pull.md +0 -44
- package/.opencode/opencode.json +0 -21
- package/inbox/failures.log +0 -1
- package/inbox/usage.jsonl +0 -4
- package/scripts/postinstall.cjs +0 -64
- package/scripts/test-hal-tools.ts +0 -154
|
@@ -1,354 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* <hook-name>builder-mode-manager</hook-name>
|
|
4
|
-
*
|
|
5
|
-
* <purpose>
|
|
6
|
-
* Automatically manages builder mode for code-editing agents.
|
|
7
|
-
* Enables before spawn, disables after completion.
|
|
8
|
-
* Handles nested agent stacks correctly.
|
|
9
|
-
* </purpose>
|
|
10
|
-
*
|
|
11
|
-
* <triggers>
|
|
12
|
-
* - PreToolUse on Task tool (before agent spawns)
|
|
13
|
-
* - PostToolUse on Task tool (after agent completes)
|
|
14
|
-
* </triggers>
|
|
15
|
-
*
|
|
16
|
-
* <code-editing-agents>
|
|
17
|
-
* giuseppe, gunther, general-purpose, agent-builder, helga, ingrid, ada
|
|
18
|
-
* </code-editing-agents>
|
|
19
|
-
*
|
|
20
|
-
* <behavior>
|
|
21
|
-
* PreToolUse:
|
|
22
|
-
* 1. Push agent onto stack
|
|
23
|
-
* 2. If code-editing agent and builder mode off → enable
|
|
24
|
-
*
|
|
25
|
-
* PostToolUse:
|
|
26
|
-
* 1. Pop agent from stack
|
|
27
|
-
* 2. If no remaining code-editing agents → disable builder mode
|
|
28
|
-
* </behavior>
|
|
29
|
-
*
|
|
30
|
-
* <shared-files>
|
|
31
|
-
* /tmp/.claude-builder-agent-active (builder mode flag)
|
|
32
|
-
* /tmp/.claude-agent-stack.json (nested agent tracking)
|
|
33
|
-
* </shared-files>
|
|
34
|
-
*
|
|
35
|
-
* <cli-commands>
|
|
36
|
-
* --status: Show builder mode and active agents
|
|
37
|
-
* --reset: Clear stack and disable builder mode
|
|
38
|
-
* </cli-commands>
|
|
39
|
-
*/
|
|
40
|
-
|
|
41
|
-
const fs = require('fs');
|
|
42
|
-
const path = require('path');
|
|
43
|
-
const os = require('os');
|
|
44
|
-
|
|
45
|
-
const TEMP_DIR = os.tmpdir();
|
|
46
|
-
const BUILDER_MODE_FILE = path.join(TEMP_DIR, '.claude-builder-agent-active');
|
|
47
|
-
const AGENT_STACK_FILE = path.join(TEMP_DIR, '.claude-agent-stack.json');
|
|
48
|
-
const LOCK_FILE = AGENT_STACK_FILE + '.lock';
|
|
49
|
-
const LOCK_TIMEOUT = 5000; // 5 seconds max wait
|
|
50
|
-
|
|
51
|
-
// Agents that need builder mode (can edit code)
|
|
52
|
-
// Uses partial matching - any subagent_type containing these strings triggers builder mode
|
|
53
|
-
const CODE_EDITING_AGENTS = [
|
|
54
|
-
'giuseppe', // agent-giuseppe-app-builder
|
|
55
|
-
'gunther', // agent-gunther-mcp-tools
|
|
56
|
-
'general-purpose', // general-purpose agent
|
|
57
|
-
'agent-builder', // agent-builder-agent-creator
|
|
58
|
-
'helga', // agent-helga-workflow-config
|
|
59
|
-
'ingrid', // agent-ingrid-doc-templates
|
|
60
|
-
'ada', // agent-ada-skill-builder
|
|
61
|
-
'simple-writer', // agent-simple-writer
|
|
62
|
-
'code-simplifier', // agent-code-simplifier
|
|
63
|
-
'marco', // agent-marco-mockup-builder
|
|
64
|
-
];
|
|
65
|
-
|
|
66
|
-
// NOTE: No in-memory cache - multiple hooks may run concurrently
|
|
67
|
-
// Each hook reads fresh from file to avoid stale reads
|
|
68
|
-
|
|
69
|
-
// Staleness threshold - remove agents older than this (4 hours - long-running agents need time)
|
|
70
|
-
const AGENT_STALENESS_MS = 4 * 60 * 60 * 1000;
|
|
71
|
-
|
|
72
|
-
// Read hook input from stdin
|
|
73
|
-
let input = '';
|
|
74
|
-
process.stdin.setEncoding('utf8');
|
|
75
|
-
process.stdin.on('data', chunk => input += chunk);
|
|
76
|
-
process.stdin.on('end', () => {
|
|
77
|
-
try {
|
|
78
|
-
const data = JSON.parse(input);
|
|
79
|
-
processHook(data);
|
|
80
|
-
} catch (e) {
|
|
81
|
-
// Invalid JSON - allow to avoid blocking
|
|
82
|
-
console.error(`[builder-mode-manager] Warning: ${e.message}`);
|
|
83
|
-
console.log(JSON.stringify({ decision: 'allow' }));
|
|
84
|
-
process.exit(0);
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
function acquireLock() {
|
|
89
|
-
const start = Date.now();
|
|
90
|
-
while (Date.now() - start < LOCK_TIMEOUT) {
|
|
91
|
-
try {
|
|
92
|
-
fs.mkdirSync(LOCK_FILE);
|
|
93
|
-
return true;
|
|
94
|
-
} catch (e) {
|
|
95
|
-
try {
|
|
96
|
-
const stat = fs.statSync(LOCK_FILE);
|
|
97
|
-
if (Date.now() - stat.mtimeMs > 10000) {
|
|
98
|
-
fs.rmdirSync(LOCK_FILE);
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
} catch (e2) { /* lock was released between check */ }
|
|
102
|
-
const waitUntil = Date.now() + 50;
|
|
103
|
-
while (Date.now() < waitUntil) { /* busy wait */ }
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function releaseLock() {
|
|
110
|
-
try { fs.rmdirSync(LOCK_FILE); } catch (e) { /* already released */ }
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Remove stale agents from the stack (older than AGENT_STALENESS_MS)
|
|
115
|
-
* Also clears builderModeEnabledBy if that agent was removed
|
|
116
|
-
* Returns true if any agents were removed
|
|
117
|
-
*/
|
|
118
|
-
function cleanupStaleAgents(stack) {
|
|
119
|
-
const now = Date.now();
|
|
120
|
-
const originalCount = stack.agents.length;
|
|
121
|
-
|
|
122
|
-
stack.agents = stack.agents.filter(agent => {
|
|
123
|
-
// Keep agents without timestamp (backward compatibility - assume current session)
|
|
124
|
-
if (!agent.startedAt) return true;
|
|
125
|
-
|
|
126
|
-
const agentTime = new Date(agent.startedAt).getTime();
|
|
127
|
-
|
|
128
|
-
// Remove agents with invalid timestamps (NaN from malformed dates)
|
|
129
|
-
if (isNaN(agentTime)) {
|
|
130
|
-
console.error(`[builder-mode-manager] Warning: Invalid timestamp for agent ${agent.type}, removing`);
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return (now - agentTime) < AGENT_STALENESS_MS;
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
const removed = originalCount - stack.agents.length;
|
|
138
|
-
if (removed > 0) {
|
|
139
|
-
console.error(`[builder-mode-manager] Cleaned up ${removed} stale agent(s) from stack`);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Clear builderModeEnabledBy if that agent is no longer in stack OR stack is empty
|
|
143
|
-
if (stack.builderModeEnabledBy) {
|
|
144
|
-
const enablerStillActive = stack.agents.some(a => a.type === stack.builderModeEnabledBy);
|
|
145
|
-
if (!enablerStillActive || stack.agents.length === 0) {
|
|
146
|
-
stack.builderModeEnabledBy = null;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return removed > 0;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Load the agent stack (tracks nested agent calls)
|
|
155
|
-
* Always reads fresh from file - no caching to avoid race conditions
|
|
156
|
-
* Automatically cleans up stale agents from previous sessions
|
|
157
|
-
*/
|
|
158
|
-
function loadAgentStack() {
|
|
159
|
-
try {
|
|
160
|
-
if (fs.existsSync(AGENT_STACK_FILE)) {
|
|
161
|
-
const stack = JSON.parse(fs.readFileSync(AGENT_STACK_FILE, 'utf8'));
|
|
162
|
-
|
|
163
|
-
// Clean up stale agents from previous sessions
|
|
164
|
-
const hadStaleAgents = cleanupStaleAgents(stack);
|
|
165
|
-
|
|
166
|
-
// If we cleaned up agents, save the updated stack
|
|
167
|
-
if (hadStaleAgents) {
|
|
168
|
-
// Also reset builder mode if no agents remain
|
|
169
|
-
if (stack.agents.length === 0) {
|
|
170
|
-
stack.builderModeEnabledBy = null;
|
|
171
|
-
disableBuilderMode();
|
|
172
|
-
}
|
|
173
|
-
saveAgentStack(stack);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
return stack;
|
|
177
|
-
}
|
|
178
|
-
} catch (e) {
|
|
179
|
-
console.error(`[builder-mode-manager] Warning: ${e.message}`);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return { agents: [], builderModeEnabledBy: null };
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Save the agent stack atomically (write to temp, then rename)
|
|
187
|
-
*/
|
|
188
|
-
function saveAgentStack(stack) {
|
|
189
|
-
const tmpFile = AGENT_STACK_FILE + '.tmp-' + process.pid;
|
|
190
|
-
fs.writeFileSync(tmpFile, JSON.stringify(stack, null, 2), { mode: 0o600 });
|
|
191
|
-
fs.renameSync(tmpFile, AGENT_STACK_FILE); // Atomic on POSIX
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Enable builder mode (atomic write: temp file + rename)
|
|
196
|
-
*/
|
|
197
|
-
function enableBuilderMode(agentName) {
|
|
198
|
-
const tmpFile = BUILDER_MODE_FILE + '.tmp-' + process.pid;
|
|
199
|
-
fs.writeFileSync(tmpFile, JSON.stringify({
|
|
200
|
-
enabledAt: new Date().toISOString(),
|
|
201
|
-
enabledBy: agentName
|
|
202
|
-
}), { mode: 0o600 });
|
|
203
|
-
fs.renameSync(tmpFile, BUILDER_MODE_FILE); // Atomic on POSIX
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Disable builder mode
|
|
208
|
-
*/
|
|
209
|
-
function disableBuilderMode() {
|
|
210
|
-
if (fs.existsSync(BUILDER_MODE_FILE)) {
|
|
211
|
-
fs.unlinkSync(BUILDER_MODE_FILE);
|
|
212
|
-
return true;
|
|
213
|
-
}
|
|
214
|
-
return false;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Check if builder mode is active
|
|
219
|
-
*/
|
|
220
|
-
function isBuilderModeActive() {
|
|
221
|
-
return fs.existsSync(BUILDER_MODE_FILE);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function processHook(data) {
|
|
225
|
-
const { tool_name, tool_input, tool_response } = data;
|
|
226
|
-
|
|
227
|
-
// Only handle Task tool
|
|
228
|
-
if (tool_name !== 'Task') {
|
|
229
|
-
console.log(JSON.stringify({ decision: 'allow' }));
|
|
230
|
-
process.exit(0);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const agentType = tool_input?.subagent_type;
|
|
234
|
-
|
|
235
|
-
if (!agentType) {
|
|
236
|
-
console.log(JSON.stringify({ decision: 'allow' }));
|
|
237
|
-
process.exit(0);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
const needsBuilderMode = CODE_EDITING_AGENTS.some(name => agentType.includes(name));
|
|
241
|
-
|
|
242
|
-
// Determine if this is PreToolUse or PostToolUse based on presence of tool_response
|
|
243
|
-
const isPreToolUse = tool_response === undefined;
|
|
244
|
-
|
|
245
|
-
const locked = acquireLock();
|
|
246
|
-
try {
|
|
247
|
-
const stack = loadAgentStack();
|
|
248
|
-
|
|
249
|
-
if (isPreToolUse) {
|
|
250
|
-
// === PreToolUse: Agent is about to be spawned ===
|
|
251
|
-
|
|
252
|
-
// Push agent onto stack
|
|
253
|
-
stack.agents.push({
|
|
254
|
-
type: agentType,
|
|
255
|
-
startedAt: new Date().toISOString(),
|
|
256
|
-
needsBuilderMode
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
// Enable builder mode if needed and not already active
|
|
260
|
-
if (needsBuilderMode && !isBuilderModeActive()) {
|
|
261
|
-
enableBuilderMode(agentType);
|
|
262
|
-
stack.builderModeEnabledBy = agentType;
|
|
263
|
-
console.error(`🔧 Builder mode AUTO-ENABLED for agent: ${agentType}`);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
saveAgentStack(stack);
|
|
267
|
-
|
|
268
|
-
} else {
|
|
269
|
-
// === PostToolUse: Agent has completed ===
|
|
270
|
-
|
|
271
|
-
// Remove the matching agent from stack (find last one with this type)
|
|
272
|
-
// Using findLastIndex instead of pop() to handle parallel agents completing out of order
|
|
273
|
-
const removeIdx = stack.agents.findLastIndex(a => a.type === agentType);
|
|
274
|
-
if (removeIdx >= 0) {
|
|
275
|
-
stack.agents.splice(removeIdx, 1);
|
|
276
|
-
} else {
|
|
277
|
-
// Agent not found - could be stale or already cleaned up
|
|
278
|
-
console.error(`[builder-mode-manager] Warning: agent ${agentType} not found in stack, skipping removal`);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Check if any remaining agents need builder mode
|
|
282
|
-
const remainingNeedBuilderMode = stack.agents.some(a => a.needsBuilderMode);
|
|
283
|
-
|
|
284
|
-
// Disable builder mode if no more agents need it
|
|
285
|
-
if (isBuilderModeActive() && !remainingNeedBuilderMode) {
|
|
286
|
-
disableBuilderMode();
|
|
287
|
-
stack.builderModeEnabledBy = null;
|
|
288
|
-
console.error(`🔒 Builder mode AUTO-DISABLED (${agentType} completed)`);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
saveAgentStack(stack);
|
|
292
|
-
}
|
|
293
|
-
} finally {
|
|
294
|
-
if (locked) releaseLock();
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Always allow Task tool
|
|
298
|
-
console.log(JSON.stringify({ decision: 'allow' }));
|
|
299
|
-
process.exit(0);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// CLI: Show status
|
|
303
|
-
if (process.argv[2] === '--status') {
|
|
304
|
-
const stack = loadAgentStack();
|
|
305
|
-
console.log('Builder Mode Manager Status');
|
|
306
|
-
console.log('===========================');
|
|
307
|
-
console.log(`Builder mode active: ${isBuilderModeActive()}`);
|
|
308
|
-
console.log(`Active agents: ${stack.agents.length}`);
|
|
309
|
-
if (stack.agents.length > 0) {
|
|
310
|
-
stack.agents.forEach((a, i) => {
|
|
311
|
-
console.log(` ${i + 1}. ${a.type} (needs builder: ${a.needsBuilderMode})`);
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
if (stack.builderModeEnabledBy) {
|
|
315
|
-
console.log(`Builder mode enabled by: ${stack.builderModeEnabledBy}`);
|
|
316
|
-
}
|
|
317
|
-
process.exit(0);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// CLI: Reset (clear stack and disable builder mode)
|
|
321
|
-
if (process.argv[2] === '--reset') {
|
|
322
|
-
disableBuilderMode();
|
|
323
|
-
if (fs.existsSync(AGENT_STACK_FILE)) {
|
|
324
|
-
fs.unlinkSync(AGENT_STACK_FILE);
|
|
325
|
-
}
|
|
326
|
-
console.log('✅ Builder mode manager reset');
|
|
327
|
-
console.log(' - Builder mode disabled');
|
|
328
|
-
console.log(' - Agent stack cleared');
|
|
329
|
-
process.exit(0);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// CLI: Help
|
|
333
|
-
if (process.argv[2] === '--help' || process.argv[2] === '-h') {
|
|
334
|
-
console.log(`
|
|
335
|
-
Builder Mode Manager - Auto-enables builder mode for code-editing agents
|
|
336
|
-
|
|
337
|
-
Usage:
|
|
338
|
-
node builder-mode-manager.cjs --status Show current status
|
|
339
|
-
node builder-mode-manager.cjs --reset Reset builder mode and agent stack
|
|
340
|
-
node builder-mode-manager.cjs --help Show this help
|
|
341
|
-
|
|
342
|
-
Code-editing agents (auto-enable builder mode):
|
|
343
|
-
${CODE_EDITING_AGENTS.join(', ')}
|
|
344
|
-
|
|
345
|
-
As a hook:
|
|
346
|
-
PreToolUse (Task): Enables builder mode before spawning code-editing agents
|
|
347
|
-
PostToolUse (Task): Disables builder mode when no more code-editing agents active
|
|
348
|
-
|
|
349
|
-
Files:
|
|
350
|
-
${BUILDER_MODE_FILE} - Builder mode flag (shared with app-edit-guard, src-edit-guard)
|
|
351
|
-
${AGENT_STACK_FILE} - Tracks active agent stack for cleanup
|
|
352
|
-
`);
|
|
353
|
-
process.exit(0);
|
|
354
|
-
}
|
|
@@ -1,271 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* <hook-name>bulk-activity-guard</hook-name>
|
|
4
|
-
*
|
|
5
|
-
* <purpose>
|
|
6
|
-
* Confirms before creating many activities at once.
|
|
7
|
-
* Prevents accidental bulk data creation via Dmitri agent.
|
|
8
|
-
* </purpose>
|
|
9
|
-
*
|
|
10
|
-
* <triggers>
|
|
11
|
-
* - PreToolUse on mcp__hailer__create_activity
|
|
12
|
-
* - PreToolUse on mcp__hailer__bulk_create_activities (if exists)
|
|
13
|
-
* </triggers>
|
|
14
|
-
*
|
|
15
|
-
* <thresholds>
|
|
16
|
-
* - Single call with array > 5 items: Warn
|
|
17
|
-
* - Tracked calls in short window > 10: Warn
|
|
18
|
-
* </thresholds>
|
|
19
|
-
*
|
|
20
|
-
* <behavior>
|
|
21
|
-
* 1. Check if bulk creation (array input or rapid sequential calls)
|
|
22
|
-
* 2. If threshold exceeded, block and require confirmation
|
|
23
|
-
* 3. Track recent calls in temp file for sequential detection
|
|
24
|
-
* </behavior>
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
const fs = require('fs');
|
|
28
|
-
const path = require('path');
|
|
29
|
-
const os = require('os');
|
|
30
|
-
|
|
31
|
-
// Skip in subagent context - orchestrator handles confirmation via delegation-bulk-guard
|
|
32
|
-
// Subagents can't use AskUserQuestion or Bash, so blocking them is unrecoverable
|
|
33
|
-
if (process.env.CLAUDE_AGENT_ID || process.env.CLAUDE_SUBAGENT) {
|
|
34
|
-
// Output allow and exit - orchestrator already confirmed
|
|
35
|
-
console.log(JSON.stringify({ decision: 'allow' }));
|
|
36
|
-
process.exit(0);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const TRACKER_FILE = path.join(os.tmpdir(), '.claude-activity-creation-tracker.json');
|
|
40
|
-
const LOCK_FILE = TRACKER_FILE + '.lock';
|
|
41
|
-
const LOCK_TIMEOUT = 5000; // 5 seconds max wait
|
|
42
|
-
const BULK_THRESHOLD = 5;
|
|
43
|
-
const SEQUENTIAL_THRESHOLD = 10;
|
|
44
|
-
const TIME_WINDOW_MS = 60000; // 1 minute window for sequential detection
|
|
45
|
-
|
|
46
|
-
// Read hook input from stdin
|
|
47
|
-
let input = '';
|
|
48
|
-
process.stdin.setEncoding('utf8');
|
|
49
|
-
process.stdin.on('data', chunk => input += chunk);
|
|
50
|
-
process.stdin.on('end', () => {
|
|
51
|
-
try {
|
|
52
|
-
const data = JSON.parse(input);
|
|
53
|
-
processHook(data);
|
|
54
|
-
} catch (e) {
|
|
55
|
-
console.error(`[bulk-activity-guard] Warning: ${e.message}`);
|
|
56
|
-
console.log(JSON.stringify({ decision: 'allow' }));
|
|
57
|
-
process.exit(0);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
function outputAllow() {
|
|
62
|
-
console.log(JSON.stringify({ decision: 'allow' }));
|
|
63
|
-
process.exit(0);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function outputBlock(message) {
|
|
67
|
-
console.log(JSON.stringify({
|
|
68
|
-
decision: 'block',
|
|
69
|
-
reason: message
|
|
70
|
-
}));
|
|
71
|
-
process.exit(0);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function acquireLock() {
|
|
75
|
-
const start = Date.now();
|
|
76
|
-
while (Date.now() - start < LOCK_TIMEOUT) {
|
|
77
|
-
try {
|
|
78
|
-
fs.mkdirSync(LOCK_FILE);
|
|
79
|
-
return true;
|
|
80
|
-
} catch (e) {
|
|
81
|
-
// Lock exists - check if stale (older than 10 seconds)
|
|
82
|
-
try {
|
|
83
|
-
const stat = fs.statSync(LOCK_FILE);
|
|
84
|
-
if (Date.now() - stat.mtimeMs > 10000) {
|
|
85
|
-
fs.rmdirSync(LOCK_FILE);
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
} catch (e2) { /* lock was released between check */ }
|
|
89
|
-
// Wait 50ms and retry
|
|
90
|
-
const waitUntil = Date.now() + 50;
|
|
91
|
-
while (Date.now() < waitUntil) { /* busy wait - hooks are sync */ }
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return false; // Timeout - proceed without lock (fallback)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function releaseLock() {
|
|
98
|
-
try { fs.rmdirSync(LOCK_FILE); } catch (e) { /* already released */ }
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function loadTracker() {
|
|
102
|
-
try {
|
|
103
|
-
if (fs.existsSync(TRACKER_FILE)) {
|
|
104
|
-
return JSON.parse(fs.readFileSync(TRACKER_FILE, 'utf8'));
|
|
105
|
-
}
|
|
106
|
-
} catch (e) {
|
|
107
|
-
console.error(`[bulk-activity-guard] Warning: ${e.message}`);
|
|
108
|
-
}
|
|
109
|
-
return { calls: [], confirmed: false, confirmedAt: null };
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function saveTracker(tracker) {
|
|
113
|
-
// Atomic write: write to temp file, then rename
|
|
114
|
-
const tmpFile = TRACKER_FILE + '.tmp-' + process.pid;
|
|
115
|
-
fs.writeFileSync(tmpFile, JSON.stringify(tracker, null, 2), { mode: 0o600 });
|
|
116
|
-
fs.renameSync(tmpFile, TRACKER_FILE); // Atomic on POSIX
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function processHook(data) {
|
|
120
|
-
const { tool_name, tool_input } = data;
|
|
121
|
-
|
|
122
|
-
// Only check activity creation tools - exact match to avoid false positives
|
|
123
|
-
if (!tool_name || (tool_name !== 'mcp__hailer__create_activity' && tool_name !== 'mcp__hailer__bulk_create_activities')) {
|
|
124
|
-
outputAllow();
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const locked = acquireLock();
|
|
129
|
-
try {
|
|
130
|
-
const now = Date.now();
|
|
131
|
-
const tracker = loadTracker();
|
|
132
|
-
|
|
133
|
-
// Clean old calls outside time window
|
|
134
|
-
tracker.calls = tracker.calls.filter(t => now - t < TIME_WINDOW_MS);
|
|
135
|
-
|
|
136
|
-
// Check if recently confirmed (within last 5 minutes)
|
|
137
|
-
if (tracker.confirmed && tracker.confirmedAt && (now - tracker.confirmedAt) < 300000) {
|
|
138
|
-
// User recently confirmed bulk creation, allow
|
|
139
|
-
tracker.calls.push(now);
|
|
140
|
-
saveTracker(tracker);
|
|
141
|
-
outputAllow();
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Reset confirmation if window expired
|
|
146
|
-
tracker.confirmed = false;
|
|
147
|
-
tracker.confirmedAt = null;
|
|
148
|
-
|
|
149
|
-
// Check for bulk array in input
|
|
150
|
-
let itemCount = 1;
|
|
151
|
-
try {
|
|
152
|
-
if (tool_input?.activities && Array.isArray(tool_input.activities)) {
|
|
153
|
-
itemCount = tool_input.activities.length;
|
|
154
|
-
}
|
|
155
|
-
} catch {}
|
|
156
|
-
|
|
157
|
-
// Add current call
|
|
158
|
-
tracker.calls.push(now);
|
|
159
|
-
const recentCallCount = tracker.calls.length;
|
|
160
|
-
saveTracker(tracker);
|
|
161
|
-
|
|
162
|
-
// Check thresholds
|
|
163
|
-
const isBulkArray = itemCount > BULK_THRESHOLD;
|
|
164
|
-
const isSequentialBulk = recentCallCount > SEQUENTIAL_THRESHOLD;
|
|
165
|
-
|
|
166
|
-
if (isBulkArray || isSequentialBulk) {
|
|
167
|
-
const reason = isBulkArray
|
|
168
|
-
? `Creating ${itemCount} activities in one call`
|
|
169
|
-
: `${recentCallCount} activity creations in the last minute`;
|
|
170
|
-
|
|
171
|
-
outputBlock(`
|
|
172
|
-
🚫 BLOCKED: Bulk Activity Creation Detected
|
|
173
|
-
|
|
174
|
-
**Reason:** ${reason}
|
|
175
|
-
|
|
176
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
177
|
-
CONFIRM WITH USER FIRST
|
|
178
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
179
|
-
|
|
180
|
-
Use AskUserQuestion to confirm:
|
|
181
|
-
|
|
182
|
-
\`\`\`json
|
|
183
|
-
{
|
|
184
|
-
"questions": [{
|
|
185
|
-
"question": "About to create ${isBulkArray ? itemCount : recentCallCount}+ activities. Continue?",
|
|
186
|
-
"header": "Bulk Create",
|
|
187
|
-
"options": [
|
|
188
|
-
{ "label": "Yes, create all", "description": "Proceed with bulk creation" },
|
|
189
|
-
{ "label": "No, stop", "description": "Cancel the operation" }
|
|
190
|
-
],
|
|
191
|
-
"multiSelect": false
|
|
192
|
-
}]
|
|
193
|
-
}
|
|
194
|
-
\`\`\`
|
|
195
|
-
|
|
196
|
-
If user confirms, run this to unlock for 5 minutes:
|
|
197
|
-
Bash: node "${process.argv[1].replace(/'/g, "'\\''")}" --confirm
|
|
198
|
-
|
|
199
|
-
Then retry the operation.
|
|
200
|
-
|
|
201
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
202
|
-
`);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
outputAllow();
|
|
206
|
-
} finally {
|
|
207
|
-
if (locked) releaseLock();
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// CLI: Confirm bulk creation
|
|
212
|
-
if (process.argv[2] === '--confirm') {
|
|
213
|
-
const locked = acquireLock();
|
|
214
|
-
try {
|
|
215
|
-
const tracker = loadTracker();
|
|
216
|
-
tracker.confirmed = true;
|
|
217
|
-
tracker.confirmedAt = Date.now();
|
|
218
|
-
saveTracker(tracker);
|
|
219
|
-
console.log('✅ Bulk activity creation confirmed for 5 minutes');
|
|
220
|
-
} finally {
|
|
221
|
-
if (locked) releaseLock();
|
|
222
|
-
}
|
|
223
|
-
process.exit(0);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// CLI: Reset tracker
|
|
227
|
-
if (process.argv[2] === '--reset') {
|
|
228
|
-
if (fs.existsSync(TRACKER_FILE)) {
|
|
229
|
-
fs.unlinkSync(TRACKER_FILE);
|
|
230
|
-
}
|
|
231
|
-
console.log('✅ Activity creation tracker reset');
|
|
232
|
-
process.exit(0);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// CLI: Status
|
|
236
|
-
if (process.argv[2] === '--status') {
|
|
237
|
-
const tracker = loadTracker();
|
|
238
|
-
const now = Date.now();
|
|
239
|
-
const recentCalls = tracker.calls.filter(t => now - t < TIME_WINDOW_MS).length;
|
|
240
|
-
console.log('Activity Creation Tracker');
|
|
241
|
-
console.log('=========================');
|
|
242
|
-
console.log(`Recent calls (1 min): ${recentCalls}`);
|
|
243
|
-
console.log(`Confirmed: ${tracker.confirmed}`);
|
|
244
|
-
if (tracker.confirmedAt) {
|
|
245
|
-
const remaining = Math.max(0, 300000 - (now - tracker.confirmedAt));
|
|
246
|
-
console.log(`Confirmation expires in: ${Math.round(remaining / 1000)}s`);
|
|
247
|
-
}
|
|
248
|
-
process.exit(0);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// CLI: Help
|
|
252
|
-
if (process.argv[2] === '--help' || process.argv[2] === '-h') {
|
|
253
|
-
console.log(`
|
|
254
|
-
Bulk Activity Guard - Confirms before bulk activity creation
|
|
255
|
-
|
|
256
|
-
Usage:
|
|
257
|
-
node bulk-activity-guard.cjs --confirm Confirm bulk creation for 5 minutes
|
|
258
|
-
node bulk-activity-guard.cjs --reset Reset the tracker
|
|
259
|
-
node bulk-activity-guard.cjs --status Show current status
|
|
260
|
-
node bulk-activity-guard.cjs --help Show this help
|
|
261
|
-
|
|
262
|
-
Thresholds:
|
|
263
|
-
- Single call with >${BULK_THRESHOLD} items: Blocked
|
|
264
|
-
- >${SEQUENTIAL_THRESHOLD} calls in 1 minute: Blocked
|
|
265
|
-
|
|
266
|
-
As a hook:
|
|
267
|
-
Reads JSON from stdin with tool_name and tool_input
|
|
268
|
-
Outputs JSON with decision: "allow" or "block"
|
|
269
|
-
`);
|
|
270
|
-
process.exit(0);
|
|
271
|
-
}
|