@hailer/mcp 1.1.12 → 1.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +0 -7
- package/{.claude → dist}/CLAUDE.md +2 -2
- package/dist/app.js +18 -5
- package/dist/bot/bot-config.d.ts +10 -1
- package/dist/bot/bot-config.js +64 -3
- package/dist/bot/bot-manager.d.ts +2 -0
- package/dist/bot/bot-manager.js +9 -2
- package/dist/bot/bot.d.ts +33 -0
- package/dist/bot/bot.js +461 -160
- 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/cli.js +0 -0
- package/dist/config.d.ts +6 -1
- package/dist/config.js +43 -0
- package/dist/core.js +3 -6
- package/dist/lib/discussion-lock.d.ts +42 -0
- package/dist/lib/discussion-lock.js +110 -0
- 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 +158 -24
- package/dist/mcp/session-store.d.ts +68 -0
- package/dist/mcp/session-store.js +169 -0
- package/dist/mcp/signal-handler.js +2 -0
- 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/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/bug-fixer-tools.d.ts +45 -0
- package/dist/mcp/tools/bug-fixer-tools.js +1096 -0
- 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/document.d.ts +11 -0
- package/dist/mcp/tools/document.js +741 -0
- package/dist/mcp/tools/investigate.d.ts +9 -0
- package/dist/mcp/tools/investigate.js +254 -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 +4 -0
- package/dist/mcp/webhook-handler.js +8 -0
- package/dist/mcp-server.d.ts +23 -2
- package/dist/mcp-server.js +639 -127
- 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/dist/stdio-server.d.ts +14 -0
- package/dist/stdio-server.js +101 -0
- package/package.json +2 -1
- 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/.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 -203
- 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 -28
- package/SESSION-HANDOFF.md +0 -68
- package/inbox/2026-03-04-bot-config-patterns.md +0 -24
- package/scripts/postinstall.cjs +0 -64
- package/scripts/test-hal-tools.ts +0 -154
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* <hook-name>context-watchdog</hook-name>
|
|
4
|
-
*
|
|
5
|
-
* <purpose>
|
|
6
|
-
* Auto-detect context exhaustion and trigger handoff before context is lost.
|
|
7
|
-
* Three layers:
|
|
8
|
-
* 1. PreCompact (auto) - escalating response by compaction count:
|
|
9
|
-
* 1st: inform (context still good after one compaction)
|
|
10
|
-
* 2nd: strongly urge handoff (summary-of-summary, quality degrades)
|
|
11
|
-
* 3rd+: force handoff
|
|
12
|
-
* 2. PostToolUse (Task) - count agent calls, warn at thresholds
|
|
13
|
-
* 3. Stop - if heavily compacted, block stop until handoff written
|
|
14
|
-
* </purpose>
|
|
15
|
-
*
|
|
16
|
-
* <triggers>
|
|
17
|
-
* - PreCompact (matcher: auto)
|
|
18
|
-
* - PostToolUse (matcher: Task)
|
|
19
|
-
* - Stop (matcher: "")
|
|
20
|
-
* </triggers>
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
const fs = require('fs');
|
|
24
|
-
const path = require('path');
|
|
25
|
-
|
|
26
|
-
// --- Config ---
|
|
27
|
-
const EARLY_WARNING_CALLS = 20; // Soft heads-up
|
|
28
|
-
const URGENT_WARNING_CALLS = 35; // Strong warning
|
|
29
|
-
const MAX_STOP_BLOCKS = 2; // Prevent infinite stop-block loops
|
|
30
|
-
|
|
31
|
-
// --- Helpers ---
|
|
32
|
-
|
|
33
|
-
function ordinal(n) {
|
|
34
|
-
const s = ['th', 'st', 'nd', 'rd'];
|
|
35
|
-
const v = n % 100;
|
|
36
|
-
return n + (s[(v - 20) % 10] || s[v] || s[0]);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function getProjectRoot() {
|
|
40
|
-
let dir = process.cwd();
|
|
41
|
-
while (dir !== '/' && !fs.existsSync(path.join(dir, '.claude'))) {
|
|
42
|
-
dir = path.dirname(dir);
|
|
43
|
-
}
|
|
44
|
-
return dir;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function statePath(projectRoot) {
|
|
48
|
-
return path.join(projectRoot, '.claude', '.context-watchdog.json');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function loadState(projectRoot, sessionId) {
|
|
52
|
-
const fp = statePath(projectRoot);
|
|
53
|
-
try {
|
|
54
|
-
if (fs.existsSync(fp)) {
|
|
55
|
-
const state = JSON.parse(fs.readFileSync(fp, 'utf8'));
|
|
56
|
-
if (state.sessionId === sessionId) return state;
|
|
57
|
-
}
|
|
58
|
-
} catch {}
|
|
59
|
-
// New session or corrupted state - reset
|
|
60
|
-
return {
|
|
61
|
-
sessionId,
|
|
62
|
-
toolCalls: 0,
|
|
63
|
-
compactCount: 0,
|
|
64
|
-
lastWarning: 'none', // none | early | urgent | compact
|
|
65
|
-
stopBlocks: 0,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function saveState(projectRoot, state) {
|
|
70
|
-
try {
|
|
71
|
-
fs.writeFileSync(statePath(projectRoot), JSON.stringify(state));
|
|
72
|
-
} catch {}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function handoffRecentlyWritten(projectRoot) {
|
|
76
|
-
const fp = path.join(projectRoot, 'SESSION-HANDOFF.md');
|
|
77
|
-
try {
|
|
78
|
-
if (fs.existsSync(fp)) {
|
|
79
|
-
const age = Date.now() - fs.statSync(fp).mtimeMs;
|
|
80
|
-
return age < 120_000; // Written within last 2 minutes
|
|
81
|
-
}
|
|
82
|
-
} catch {}
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Output helpers - schema differs by hook event type
|
|
87
|
-
let _hookEvent = null; // Set during processHook
|
|
88
|
-
|
|
89
|
-
function allow(message) {
|
|
90
|
-
if (_hookEvent === 'Stop') {
|
|
91
|
-
// Stop schema: { continue?, stopReason? }
|
|
92
|
-
console.log(JSON.stringify({}));
|
|
93
|
-
} else if (_hookEvent === 'PreCompact') {
|
|
94
|
-
// PreCompact: just output empty or with message
|
|
95
|
-
if (message) {
|
|
96
|
-
console.log(JSON.stringify({ additionalContext: message }));
|
|
97
|
-
} else {
|
|
98
|
-
console.log(JSON.stringify({}));
|
|
99
|
-
}
|
|
100
|
-
} else {
|
|
101
|
-
// PreToolUse/PostToolUse schema
|
|
102
|
-
const out = { decision: 'allow' };
|
|
103
|
-
if (message) out.message = message;
|
|
104
|
-
console.log(JSON.stringify(out));
|
|
105
|
-
}
|
|
106
|
-
process.exit(0);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function block(reason) {
|
|
110
|
-
if (_hookEvent === 'Stop') {
|
|
111
|
-
// Stop schema: continue=true means "don't stop, keep going"
|
|
112
|
-
console.log(JSON.stringify({ continue: true, stopReason: reason }));
|
|
113
|
-
} else {
|
|
114
|
-
console.log(JSON.stringify({ decision: 'block', reason }));
|
|
115
|
-
}
|
|
116
|
-
process.exit(0);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// --- Main ---
|
|
120
|
-
|
|
121
|
-
let input = '';
|
|
122
|
-
process.stdin.setEncoding('utf8');
|
|
123
|
-
process.stdin.on('data', chunk => input += chunk);
|
|
124
|
-
process.stdin.on('end', () => {
|
|
125
|
-
try {
|
|
126
|
-
processHook(JSON.parse(input));
|
|
127
|
-
} catch {
|
|
128
|
-
allow();
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
function processHook(data) {
|
|
133
|
-
const projectRoot = getProjectRoot();
|
|
134
|
-
const sessionId = data.session_id || '';
|
|
135
|
-
const state = loadState(projectRoot, sessionId);
|
|
136
|
-
const event = data.hook_event_name;
|
|
137
|
-
_hookEvent = event; // Set for allow/block output formatting
|
|
138
|
-
|
|
139
|
-
// ─── Layer 1: PreCompact (auto) ────────────────────────
|
|
140
|
-
// Context IS full. Escalating response by compaction count.
|
|
141
|
-
if (event === 'PreCompact') {
|
|
142
|
-
state.compactCount++;
|
|
143
|
-
state.lastWarning = 'compact';
|
|
144
|
-
saveState(projectRoot, state);
|
|
145
|
-
|
|
146
|
-
// 1st compaction: inform — context is still usable
|
|
147
|
-
if (state.compactCount === 1) {
|
|
148
|
-
allow(`📋 Context compacting (1st time). Context is still good — consider running /handoff soon to preserve state.`);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// 2nd compaction: strongly urge handoff — quality starts degrading
|
|
152
|
-
if (state.compactCount === 2) {
|
|
153
|
-
allow(
|
|
154
|
-
`⚠️ Context compacting for the 2nd time. Earlier details are now a summary-of-a-summary.\n\n` +
|
|
155
|
-
`Strongly recommended: run /handoff now and start a fresh session with /recap.\n` +
|
|
156
|
-
`Continuing risks: forgetting decisions, re-reading files, repeating mistakes.`
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// 3rd+ compaction: force handoff
|
|
161
|
-
if (state.compactCount >= 3) {
|
|
162
|
-
allow(
|
|
163
|
-
`🛑 Context compacting for the ${ordinal(state.compactCount)} time — session is significantly degraded.\n\n` +
|
|
164
|
-
`You should run /handoff and start fresh. Continuing will lead to:\n` +
|
|
165
|
-
`- Forgotten decisions and context\n` +
|
|
166
|
-
`- Re-reading files already read\n` +
|
|
167
|
-
`- Potential mistakes from lost context\n\n` +
|
|
168
|
-
`Please run /handoff now.`
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// ─── Layer 2: PostToolUse (Task) - agent call counter ──
|
|
174
|
-
if (event === 'PostToolUse' && data.tool_name === 'Task') {
|
|
175
|
-
state.toolCalls++;
|
|
176
|
-
saveState(projectRoot, state);
|
|
177
|
-
|
|
178
|
-
// Skip warnings if compact already fired (more urgent message already sent)
|
|
179
|
-
if (state.lastWarning === 'compact') {
|
|
180
|
-
allow();
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Urgent warning
|
|
184
|
-
if (state.toolCalls === URGENT_WARNING_CALLS && state.lastWarning !== 'urgent') {
|
|
185
|
-
state.lastWarning = 'urgent';
|
|
186
|
-
saveState(projectRoot, state);
|
|
187
|
-
allow(`📊 ${state.toolCalls} agent calls this session. Context may be getting full - consider /handoff.`);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Early warning
|
|
191
|
-
if (state.toolCalls === EARLY_WARNING_CALLS && state.lastWarning === 'none') {
|
|
192
|
-
state.lastWarning = 'early';
|
|
193
|
-
saveState(projectRoot, state);
|
|
194
|
-
allow(`📊 Context check: ${state.toolCalls} agent calls. Consider /handoff soon.`);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
allow();
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// ─── Layer 3: Stop - safety net ────────────────────────
|
|
201
|
-
// Block stop if context was heavily compacted and no handoff written.
|
|
202
|
-
if (event === 'Stop') {
|
|
203
|
-
// Prevent infinite loop: if stop hook already active, let it through
|
|
204
|
-
if (data.stop_hook_active) {
|
|
205
|
-
allow();
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// After 2+ compactions (any mode), block stop until handoff is written
|
|
209
|
-
const shouldBlock = state.compactCount >= 2 && state.stopBlocks < MAX_STOP_BLOCKS;
|
|
210
|
-
|
|
211
|
-
if (shouldBlock && !handoffRecentlyWritten(projectRoot)) {
|
|
212
|
-
state.stopBlocks++;
|
|
213
|
-
saveState(projectRoot, state);
|
|
214
|
-
block(
|
|
215
|
-
`Context was compacted ${state.compactCount} time(s) but no handoff found.\n\n` +
|
|
216
|
-
`Before stopping, you MUST:\n` +
|
|
217
|
-
`1. Write SESSION-HANDOFF.md with current state and next steps\n` +
|
|
218
|
-
`2. Update DEVELOPMENT.md with progress\n` +
|
|
219
|
-
`3. Run /save "Handoff: ${state.compactCount}x compacted"\n` +
|
|
220
|
-
`4. Tell user: "Session degraded after ${state.compactCount} compactions. Start fresh with /recap."`
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
allow();
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Default - unknown event, output safe empty JSON
|
|
228
|
-
console.log(JSON.stringify({}));
|
|
229
|
-
process.exit(0);
|
|
230
|
-
}
|
|
@@ -1,465 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* <hook-name>delegation-reminder</hook-name>
|
|
4
|
-
* <purpose>Enforces delegation pattern - orchestrator should delegate, not do directly</purpose>
|
|
5
|
-
* <triggers>PreToolUse</triggers>
|
|
6
|
-
*
|
|
7
|
-
* delegation-reminder.cjs
|
|
8
|
-
*
|
|
9
|
-
* HARD BLOCKS (data safety - writes only):
|
|
10
|
-
* - MCP write tools: create_activity, update_activity → Dmitri
|
|
11
|
-
* - install_workflow → Helga (use SDK)
|
|
12
|
-
*
|
|
13
|
-
* SOFT SUGGESTIONS (allows but reminds - specialists may need direct access):
|
|
14
|
-
* - MCP read tools: list_activities, get_workflow_schema, etc. → appropriate specialist
|
|
15
|
-
* - workspace/ reads → Kenji (but Helga/Viktor/Alejandro/Ingrid need access too)
|
|
16
|
-
* - Glob/Grep code searches → Explore agent
|
|
17
|
-
* - Task(giuseppe) for new app: Suggest ui-designer first
|
|
18
|
-
* - Task(giuseppe) for demo/mockup: Suggest marco-mockup instead
|
|
19
|
-
* - workspace/ writes: Suggest Helga
|
|
20
|
-
* - apps/ files: Suggest Giuseppe
|
|
21
|
-
* - .claude/agents/: Suggest Builder
|
|
22
|
-
* - .claude/skills/: Suggest Ada
|
|
23
|
-
* - git commit: Suggest Svetlana for review
|
|
24
|
-
* - After .ts/.tsx edits: Suggest Tanya for tests
|
|
25
|
-
*
|
|
26
|
-
* SILENTLY ALLOWS:
|
|
27
|
-
* - Schema/list tools used by multiple specialists (get_workflow_schema, list_workflows, etc.)
|
|
28
|
-
*
|
|
29
|
-
* SKIPS:
|
|
30
|
-
* - When called from within a subagent (checks agent stack + env vars)
|
|
31
|
-
*
|
|
32
|
-
* NOTE: File read/write suggestions are ALWAYS soft (allow + message).
|
|
33
|
-
* Only MCP write tools and built-in agent types are hard-blocked.
|
|
34
|
-
*
|
|
35
|
-
* Hook type: PreToolUse
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
const fs = require('fs');
|
|
39
|
-
const path = require('path');
|
|
40
|
-
const os = require('os');
|
|
41
|
-
|
|
42
|
-
// Cross-platform temp directory
|
|
43
|
-
const TEMP_DIR = os.tmpdir();
|
|
44
|
-
|
|
45
|
-
// Agent stack file maintained by builder-mode-manager.cjs
|
|
46
|
-
const AGENT_STACK_FILE = path.join(TEMP_DIR, '.claude-agent-stack.json');
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Check if we're running inside a subagent.
|
|
50
|
-
* Uses multiple detection methods:
|
|
51
|
-
* 1. Agent stack file (maintained by builder-mode-manager.cjs)
|
|
52
|
-
* 2. CLAUDE_SUBAGENT environment variable (set by Claude Code Task tool)
|
|
53
|
-
* 3. CLAUDE_AGENT_* environment variables (agent identity markers)
|
|
54
|
-
*/
|
|
55
|
-
function isInsideSubagent() {
|
|
56
|
-
// Method 1: Check agent stack file
|
|
57
|
-
try {
|
|
58
|
-
const stack = JSON.parse(fs.readFileSync(AGENT_STACK_FILE, 'utf8'));
|
|
59
|
-
if (stack.agents && stack.agents.length > 0) {
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
} catch (e) {
|
|
63
|
-
// File doesn't exist or invalid JSON - continue to other methods
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Method 2: Check CLAUDE_SUBAGENT env var (may be set by Task tool)
|
|
67
|
-
if (process.env.CLAUDE_SUBAGENT === 'true' || process.env.CLAUDE_SUBAGENT === '1') {
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Method 3: Check for agent identity markers in environment
|
|
72
|
-
// When Task tool spawns a subagent, certain env vars may be present
|
|
73
|
-
if (process.env.CLAUDE_AGENT_TYPE || process.env.CLAUDE_AGENT_ID) {
|
|
74
|
-
return true;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Tool -> Agent mappings
|
|
81
|
-
const MCP_TOOL_AGENTS = {
|
|
82
|
-
// Kenji - data reader
|
|
83
|
-
'mcp__hailer__list_activities': 'agent-kenji-data-reader',
|
|
84
|
-
'mcp__hailer__count_activities': 'agent-kenji-data-reader',
|
|
85
|
-
'mcp__hailer__get_activity': 'agent-kenji-data-reader',
|
|
86
|
-
'mcp__hailer__list_workflows': 'agent-kenji-data-reader',
|
|
87
|
-
'mcp__hailer__list_workflows_minimal': 'agent-kenji-data-reader',
|
|
88
|
-
'mcp__hailer__get_workflow_schema': 'agent-kenji-data-reader',
|
|
89
|
-
'mcp__hailer__list_workflow_phases': 'agent-kenji-data-reader',
|
|
90
|
-
|
|
91
|
-
// Dmitri - activity CRUD
|
|
92
|
-
'mcp__hailer__create_activity': 'agent-dmitri-activity-crud',
|
|
93
|
-
'mcp__hailer__update_activity': 'agent-dmitri-activity-crud',
|
|
94
|
-
|
|
95
|
-
// Viktor - insights
|
|
96
|
-
'mcp__hailer__preview_insight': 'agent-viktor-sql-insights',
|
|
97
|
-
'mcp__hailer__get_insight_data': 'agent-viktor-sql-insights',
|
|
98
|
-
'mcp__hailer__list_insights': 'agent-viktor-sql-insights',
|
|
99
|
-
|
|
100
|
-
// Yevgeni - discussions
|
|
101
|
-
'mcp__hailer__list_my_discussions': 'agent-yevgeni-discussions',
|
|
102
|
-
'mcp__hailer__fetch_discussion_messages': 'agent-yevgeni-discussions',
|
|
103
|
-
'mcp__hailer__add_discussion_message': 'agent-yevgeni-discussions',
|
|
104
|
-
'mcp__hailer__join_discussion': 'agent-yevgeni-discussions',
|
|
105
|
-
'mcp__hailer__leave_discussion': 'agent-yevgeni-discussions',
|
|
106
|
-
'mcp__hailer__invite_discussion_members': 'agent-yevgeni-discussions',
|
|
107
|
-
|
|
108
|
-
// Permissions
|
|
109
|
-
'mcp__hailer__list_apps': 'agent-permissions-handler',
|
|
110
|
-
'mcp__hailer__add_app_member': 'agent-permissions-handler',
|
|
111
|
-
'mcp__hailer__remove_app_member': 'agent-permissions-handler',
|
|
112
|
-
'mcp__hailer__search_workspace_users': 'agent-permissions-handler',
|
|
113
|
-
|
|
114
|
-
// Giuseppe - app scaffold and local dev
|
|
115
|
-
'mcp__hailer__scaffold_hailer_app': 'agent-giuseppe-app-builder',
|
|
116
|
-
'mcp__hailer__create_app': 'agent-giuseppe-app-builder',
|
|
117
|
-
|
|
118
|
-
// Helga - workflow config (use SDK, not MCP)
|
|
119
|
-
'mcp__hailer__install_workflow': 'agent-helga-workflow-config',
|
|
120
|
-
'mcp__hailer__create_workflow': 'agent-helga-workflow-config',
|
|
121
|
-
'mcp__hailer__update_workflow': 'agent-helga-workflow-config',
|
|
122
|
-
'mcp__hailer__delete_workflow': 'agent-helga-workflow-config',
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
// File patterns that suggest delegation
|
|
126
|
-
const FILE_PATTERN_AGENTS = {
|
|
127
|
-
'workspace/': 'agent-kenji-data-reader',
|
|
128
|
-
'/workspace/': 'agent-kenji-data-reader',
|
|
129
|
-
'apps/': 'agent-giuseppe-app-builder',
|
|
130
|
-
'/apps/': 'agent-giuseppe-app-builder',
|
|
131
|
-
'.claude/agents/': 'agent-builder-agent-creator',
|
|
132
|
-
'.claude/skills/': 'agent-ada-skill-builder',
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
// All file read/write suggestions always allow with a soft message.
|
|
136
|
-
// Only MCP write tools (create_activity, update_activity, install_workflow)
|
|
137
|
-
// and built-in agent types are hard-blocked.
|
|
138
|
-
|
|
139
|
-
// Read stdin asynchronously for better cross-platform support
|
|
140
|
-
let stdinData = '';
|
|
141
|
-
process.stdin.setEncoding('utf8');
|
|
142
|
-
process.stdin.on('data', chunk => stdinData += chunk);
|
|
143
|
-
process.stdin.on('end', () => {
|
|
144
|
-
try {
|
|
145
|
-
const input = JSON.parse(stdinData);
|
|
146
|
-
processInput(input);
|
|
147
|
-
} catch (e) {
|
|
148
|
-
console.error(`[delegation-reminder] Failed to parse input: ${e.message}`);
|
|
149
|
-
console.log(JSON.stringify({ decision: 'allow' }));
|
|
150
|
-
process.exit(0);
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// Check if stdin is TTY (no piped input)
|
|
155
|
-
if (process.stdin.isTTY) {
|
|
156
|
-
console.log(JSON.stringify({ decision: 'allow' }));
|
|
157
|
-
process.exit(0);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function processInput(input) {
|
|
161
|
-
const { tool_name, tool_input } = input;
|
|
162
|
-
|
|
163
|
-
// Skip if running inside a subagent (check agent stack, not env var)
|
|
164
|
-
if (isInsideSubagent()) {
|
|
165
|
-
console.log(JSON.stringify({ decision: "allow" }));
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// WORKFLOW SUGGESTIONS: Task tool spawning
|
|
170
|
-
if (tool_name === 'Task') {
|
|
171
|
-
const agentType = tool_input?.subagent_type || '';
|
|
172
|
-
// ReDoS protection: limit prompt size before regex matching
|
|
173
|
-
let prompt = (tool_input?.prompt || '').toLowerCase();
|
|
174
|
-
if (prompt.length > 10000) prompt = prompt.substring(0, 10000);
|
|
175
|
-
|
|
176
|
-
// HARD BLOCK: Built-in agent types - use specialized agents instead
|
|
177
|
-
// Built-in agents lack Skill tool and specialized domain knowledge
|
|
178
|
-
const BUILTIN_AGENTS = {
|
|
179
|
-
'Plan': 'Agents should plan their own work. Give the executing agent a prompt that includes research + planning + implementation. E.g., Ingrid can load SDK-document-templates skill and plan the template herself.',
|
|
180
|
-
'general-purpose': 'Use the appropriate specialized agent. Check /help:agents for the right one.',
|
|
181
|
-
'Bash': 'Run bash commands directly with the Bash tool instead of spawning a Bash agent.',
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
if (BUILTIN_AGENTS[agentType]) {
|
|
185
|
-
console.log(JSON.stringify({
|
|
186
|
-
decision: "block",
|
|
187
|
-
reason: `🚫 BUILT-IN AGENT BLOCKED: "${agentType}" is a Claude Code built-in agent. Use specialized agents instead.\n\n${BUILTIN_AGENTS[agentType]}\n\nAgents should do their own planning+research+execution. Include research steps in the agent's prompt instead of splitting into separate Plan/Explore agents.`,
|
|
188
|
-
}));
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Giuseppe for new app - suggest ui-designer first
|
|
193
|
-
if (agentType.includes('giuseppe')) {
|
|
194
|
-
// Check if it's a demo/mockup request - use word boundaries to avoid false positives
|
|
195
|
-
if (/\b(demo|mockup|prototype)\b/i.test(prompt) ||
|
|
196
|
-
((/\b(show|present)\b/i.test(prompt)) &&
|
|
197
|
-
(/\b(customer|client|stakeholder)\b/i.test(prompt)))) {
|
|
198
|
-
console.log(JSON.stringify({
|
|
199
|
-
decision: "allow",
|
|
200
|
-
message: `💡 FOR DEMOS: Consider using agent-marco-mockup-builder instead.\n\nMarco creates interactive demos with mock data - perfect for showing customers concepts.\nGiuseppe builds production apps.\n\nUse marco for: demos, prototypes, "what if" explorations\nUse giuseppe for: real apps with SDK integration`,
|
|
201
|
-
}));
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// Check if design was mentioned
|
|
206
|
-
if (!prompt.includes('design') && !prompt.includes('spec') && !prompt.includes('following the design')) {
|
|
207
|
-
console.log(JSON.stringify({
|
|
208
|
-
decision: "allow",
|
|
209
|
-
message: `💡 APP WORKFLOW: Consider the full flow:\n\n1. Plan Mode → understand requirements\n2. ui-designer → create design spec\n3. Giuseppe → build the app\n\nIf you haven't created a design spec yet, consider:\nTask(subagent_type="ui-designer", prompt='{"task":"design_app","requirements":"..."}')`,
|
|
210
|
-
}));
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// SUGGEST: Git commit - run tests and code review first
|
|
217
|
-
if (tool_name === 'Bash') {
|
|
218
|
-
const command = tool_input?.command || '';
|
|
219
|
-
|
|
220
|
-
if (command.includes('git commit')) {
|
|
221
|
-
console.log(JSON.stringify({
|
|
222
|
-
decision: "allow",
|
|
223
|
-
message: `💡 BEFORE COMMIT: Consider running tests and code review.\n\n1. Task(subagent_type="agent-tanya-test-runner", prompt='{"task":"run_tests"}')\n2. Task(subagent_type="agent-svetlana-code-review", prompt='{"task":"review_changes"}')\n\nTanya runs tests, Svetlana checks for bugs and security issues.`,
|
|
224
|
-
}));
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Suggest tests after build passes
|
|
229
|
-
if (command.includes('npm run build') && !command.includes('test')) {
|
|
230
|
-
console.log(JSON.stringify({
|
|
231
|
-
decision: "allow",
|
|
232
|
-
message: `💡 BUILD PASSED? Consider running tests.\n\nTask(subagent_type="agent-tanya-test-runner", prompt='{"task":"run_tests"}')\n\nTanya runs vitest/playwright and reports failures.`,
|
|
233
|
-
}));
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// SOFT SUGGEST: Glob/Grep code searches - always soft (orchestrator needs these for delegation)
|
|
239
|
-
// These are orchestration tools, not domain work - never block always
|
|
240
|
-
if (tool_name === 'Glob' || tool_name === 'Grep') {
|
|
241
|
-
console.log(JSON.stringify({
|
|
242
|
-
decision: "allow",
|
|
243
|
-
message: `💡 CODE SEARCH: For workspace/schema searches, delegate to Kenji.\nFor broader research, include search instructions in the appropriate agent's prompt.\n\n(Specialist agents may search within their domains - this is just a reminder.)`,
|
|
244
|
-
}));
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// SUGGEST: Web search - delegate to save context (always soft - orchestrator may need quick lookups)
|
|
249
|
-
if (tool_name === 'WebSearch' || tool_name === 'WebFetch') {
|
|
250
|
-
console.log(JSON.stringify({
|
|
251
|
-
decision: "allow",
|
|
252
|
-
message: `💡 SAVE CONTEXT: Consider delegating web research to agent-web-search.\n\nThe agent searches, fetches pages, and returns concise summaries instead of dumping raw content.\n\nTask(subagent_type="agent-web-search", prompt='{"query":"your question","context":"optional background"}')`,
|
|
253
|
-
}));
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// MCP tools - block writes, suggest for reads
|
|
258
|
-
if (tool_name?.startsWith('mcp__hailer__')) {
|
|
259
|
-
// HARD BLOCK: install_workflow should use SDK, not MCP
|
|
260
|
-
// Enforced always mode - delegation is about correctness, not permissions
|
|
261
|
-
if (tool_name === 'mcp__hailer__install_workflow') {
|
|
262
|
-
console.log(JSON.stringify({
|
|
263
|
-
decision: "block",
|
|
264
|
-
reason: `🚫 USE SDK, NOT MCP: For workflow creation, use agent-helga-workflow-config with the SDK approach.
|
|
265
|
-
|
|
266
|
-
Helga manages workflows via workspace/ files + npm commands, not MCP tools.
|
|
267
|
-
|
|
268
|
-
Task(subagent_type="agent-helga-workflow-config", prompt='{"task":"create_workflow","name":"MyWorkflow","description":"..."}')
|
|
269
|
-
|
|
270
|
-
The install_workflow MCP tool is for admin/dev use only.`,
|
|
271
|
-
}));
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// HARD BLOCK: Activity writes must go through Dmitri
|
|
276
|
-
// Enforced always mode - Dmitri validates field formats
|
|
277
|
-
if (tool_name === 'mcp__hailer__create_activity' || tool_name === 'mcp__hailer__update_activity') {
|
|
278
|
-
console.log(JSON.stringify({
|
|
279
|
-
decision: "block",
|
|
280
|
-
reason: `🚫 DELEGATE TO DMITRI: Activity writes must go through agent-dmitri-activity-crud.
|
|
281
|
-
|
|
282
|
-
Dmitri validates:
|
|
283
|
-
- Required fields are provided
|
|
284
|
-
- Field value formats (activitylink=string, date=timestamp)
|
|
285
|
-
- Real MongoDB ObjectIds (not SDK enums)
|
|
286
|
-
|
|
287
|
-
Task(subagent_type="agent-dmitri-activity-crud", prompt='{"task":"create","workflow_id":"...","phase_id":"...","activities":[...]}')`,
|
|
288
|
-
}));
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// MCP READ TOOLS: Always allow with soft message (never block, always)
|
|
293
|
-
// Subagent detection is unreliable — we can't tell if Viktor or the orchestrator
|
|
294
|
-
// is calling preview_insight. Blocking would break agents using their own tools.
|
|
295
|
-
const suggestedAgent = MCP_TOOL_AGENTS[tool_name];
|
|
296
|
-
if (suggestedAgent) {
|
|
297
|
-
// Skip suggestion for tools that multiple agents legitimately use
|
|
298
|
-
const multiAgentTools = [
|
|
299
|
-
'mcp__hailer__get_workflow_schema',
|
|
300
|
-
'mcp__hailer__list_workflows',
|
|
301
|
-
'mcp__hailer__list_workflows_minimal',
|
|
302
|
-
'mcp__hailer__list_workflow_phases',
|
|
303
|
-
];
|
|
304
|
-
|
|
305
|
-
if (multiAgentTools.includes(tool_name)) {
|
|
306
|
-
console.log(JSON.stringify({ decision: "allow" }));
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// Always allow — agents need their own tools
|
|
311
|
-
// (Viktor→preview_insight, Kenji→list_activities, etc.)
|
|
312
|
-
console.log(JSON.stringify({
|
|
313
|
-
decision: "allow",
|
|
314
|
-
message: `💡 MCP TOOL: If you're the orchestrator, consider delegating to ${suggestedAgent}.`,
|
|
315
|
-
}));
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// Unknown MCP tool - allow with reminder
|
|
320
|
-
console.log(JSON.stringify({
|
|
321
|
-
decision: "allow",
|
|
322
|
-
message: `💡 DELEGATE: Consider /help:agents for the right agent. Tool: ${tool_name}`,
|
|
323
|
-
}));
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// BLOCK: Reading workspace/ - must delegate to Kenji (with bypass option)
|
|
328
|
-
if (tool_name === 'Read') {
|
|
329
|
-
const filePath = tool_input?.file_path || '';
|
|
330
|
-
|
|
331
|
-
// workspace/ reads - ALWAYS ALLOW (never block, always)
|
|
332
|
-
// Multiple specialist agents (Helga, Viktor, Alejandro, Ingrid) need workspace access
|
|
333
|
-
// Subagent detection isn't reliable, so we can't distinguish orchestrator from agents
|
|
334
|
-
if (filePath.includes('workspace/') || filePath.includes('/workspace/')) {
|
|
335
|
-
console.log(JSON.stringify({
|
|
336
|
-
decision: "allow",
|
|
337
|
-
message: `💡 WORKSPACE READ: If you're the orchestrator, consider delegating to Kenji.\n\nTask(subagent_type="agent-kenji-data-reader", prompt='{"task":"read_workspace_file","path":"${filePath}"}')\n\n(Specialist agents like Helga/Viktor need direct access - this is just a reminder.)`,
|
|
338
|
-
}));
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// Other file patterns - ALWAYS ALLOW reads (never block, always)
|
|
343
|
-
// Agents need to read files in their domain (Giuseppe reads apps/, etc.)
|
|
344
|
-
for (const [pattern, agent] of Object.entries(FILE_PATTERN_AGENTS)) {
|
|
345
|
-
if (filePath.includes(pattern) && !pattern.includes('workspace')) {
|
|
346
|
-
const suggestions = {
|
|
347
|
-
'apps/': 'Giuseppe builds and maintains apps. Consider delegating app work.',
|
|
348
|
-
'.claude/agents/': 'Use agent-builder-agent-creator for creating/modifying agents.',
|
|
349
|
-
'.claude/skills/': 'Use agent-ada-skill-builder for creating/modifying skills.',
|
|
350
|
-
};
|
|
351
|
-
const suggestion = suggestions[pattern];
|
|
352
|
-
if (suggestion) {
|
|
353
|
-
console.log(JSON.stringify({
|
|
354
|
-
decision: "allow",
|
|
355
|
-
message: `💡 CONSIDER DELEGATING: ${suggestion}\n\nAgent: ${agent}`,
|
|
356
|
-
}));
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// SOFT REMIND: Write/Edit on various files
|
|
364
|
-
// ALWAYS ALLOW writes — agents need to write in their domains
|
|
365
|
-
// Subagent detection is unreliable, so blocking writes breaks agents
|
|
366
|
-
if (tool_name === 'Write' || tool_name === 'Edit') {
|
|
367
|
-
const filePath = tool_input?.file_path || '';
|
|
368
|
-
|
|
369
|
-
// WORKSPACE WRITES: Suggest appropriate specialist (always allow)
|
|
370
|
-
if (filePath.includes('workspace/')) {
|
|
371
|
-
let message;
|
|
372
|
-
if (filePath.includes('/functions/')) {
|
|
373
|
-
message = `💡 FUNCTION FIELD CODE: If you're the orchestrator, use agent-alejandro-function-fields.`;
|
|
374
|
-
} else if (filePath.includes('fields.ts')) {
|
|
375
|
-
message = `💡 FIELD DEFINITIONS: If you're the orchestrator, use agent-helga-workflow-config for field config, agent-alejandro-function-fields for function code.`;
|
|
376
|
-
} else if (filePath.includes('/templates/')) {
|
|
377
|
-
message = `💡 DOCUMENT TEMPLATES: If you're the orchestrator, use agent-ingrid-doc-templates.`;
|
|
378
|
-
} else if (filePath.includes('insights.ts')) {
|
|
379
|
-
message = `💡 INSIGHTS: If you're the orchestrator, use agent-viktor-sql-insights.`;
|
|
380
|
-
} else {
|
|
381
|
-
message = `💡 WORKSPACE CONFIG: If you're the orchestrator, use agent-helga-workflow-config.`;
|
|
382
|
-
}
|
|
383
|
-
console.log(JSON.stringify({ decision: "allow", message }));
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// Suggest delegation for agent/skill modifications (always allow)
|
|
388
|
-
if (filePath.includes('.claude/agents/')) {
|
|
389
|
-
console.log(JSON.stringify({
|
|
390
|
-
decision: "allow",
|
|
391
|
-
message: `💡 CONSIDER DELEGATING: Use agent-builder-agent-creator for agent modifications.`,
|
|
392
|
-
}));
|
|
393
|
-
return;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
if (filePath.includes('.claude/skills/')) {
|
|
397
|
-
console.log(JSON.stringify({
|
|
398
|
-
decision: "allow",
|
|
399
|
-
message: `💡 CONSIDER DELEGATING: Use agent-ada-skill-builder for skill modifications.`,
|
|
400
|
-
}));
|
|
401
|
-
return;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// .claude/ config files - always allow
|
|
405
|
-
if (filePath.includes('.claude/') && (filePath.endsWith('.json') || filePath.endsWith('.cjs'))) {
|
|
406
|
-
console.log(JSON.stringify({
|
|
407
|
-
decision: "allow",
|
|
408
|
-
message: `💡 CONFIG CHANGE: After editing .claude/ config, consider running audit.\n\nTask(subagent_type="agent-bjorn-config-audit", prompt='{"task":"audit_config"}')`,
|
|
409
|
-
}));
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
if (filePath.includes('apps/') && (filePath.endsWith('.tsx') || filePath.endsWith('.ts'))) {
|
|
414
|
-
console.log(JSON.stringify({
|
|
415
|
-
decision: "allow",
|
|
416
|
-
message: `💡 CONSIDER DELEGATING: If you're the orchestrator, use agent-giuseppe-app-builder for app code.`,
|
|
417
|
-
}));
|
|
418
|
-
return;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// Integration files
|
|
422
|
-
if (filePath.includes('integrations/') || filePath.includes('activity-mover')) {
|
|
423
|
-
console.log(JSON.stringify({
|
|
424
|
-
decision: "allow",
|
|
425
|
-
message: `💡 INTEGRATIONS: If you're the orchestrator, consider: agent-igor (movers), agent-ivan (monolith), agent-zara (Zapier).`,
|
|
426
|
-
}));
|
|
427
|
-
return;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// General .ts/.tsx code edits - allow with test reminder
|
|
431
|
-
if ((filePath.endsWith('.ts') || filePath.endsWith('.tsx')) &&
|
|
432
|
-
!filePath.includes('workspace/') &&
|
|
433
|
-
!filePath.includes('apps/') &&
|
|
434
|
-
!filePath.includes('.claude/')) {
|
|
435
|
-
console.log(JSON.stringify({
|
|
436
|
-
decision: "allow",
|
|
437
|
-
message: `💡 CODE CHANGE: Consider running tests after editing.\n\nTask(subagent_type="agent-tanya-test-runner", prompt='{"task":"run_tests"}')`,
|
|
438
|
-
}));
|
|
439
|
-
return;
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// SUGGEST: Lars for debugging when errors mentioned in prompt
|
|
444
|
-
if (tool_name === 'Task') {
|
|
445
|
-
// ReDoS protection: limit prompt size before regex matching
|
|
446
|
-
let prompt = (tool_input?.prompt || '').toLowerCase();
|
|
447
|
-
if (prompt.length > 10000) prompt = prompt.substring(0, 10000);
|
|
448
|
-
const agentType = tool_input?.subagent_type || '';
|
|
449
|
-
|
|
450
|
-
// If spawning general-purpose or explore for debugging, suggest lars
|
|
451
|
-
if ((agentType.includes('general') || agentType.includes('explore')) &&
|
|
452
|
-
(prompt.includes('bug') || prompt.includes('debug') || prompt.includes('error') ||
|
|
453
|
-
prompt.includes('not working') || prompt.includes('broken') || prompt.includes('why'))) {
|
|
454
|
-
console.log(JSON.stringify({
|
|
455
|
-
decision: "allow",
|
|
456
|
-
message: `💡 FOR CODE ISSUES: Consider agent-lars-code-inspector for dead code, unused imports, and type errors.`,
|
|
457
|
-
}));
|
|
458
|
-
return;
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
// Allow the tool call
|
|
463
|
-
console.log(JSON.stringify({ decision: "allow" }));
|
|
464
|
-
process.exit(0);
|
|
465
|
-
}
|