@claudetools/tools 0.8.2 → 0.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/cli.js +41 -0
  2. package/dist/context/deduplication.d.ts +72 -0
  3. package/dist/context/deduplication.js +77 -0
  4. package/dist/context/deduplication.test.d.ts +6 -0
  5. package/dist/context/deduplication.test.js +84 -0
  6. package/dist/context/emergency-eviction.d.ts +73 -0
  7. package/dist/context/emergency-eviction.example.d.ts +13 -0
  8. package/dist/context/emergency-eviction.example.js +94 -0
  9. package/dist/context/emergency-eviction.js +226 -0
  10. package/dist/context/eviction-engine.d.ts +76 -0
  11. package/dist/context/eviction-engine.example.d.ts +7 -0
  12. package/dist/context/eviction-engine.example.js +144 -0
  13. package/dist/context/eviction-engine.js +176 -0
  14. package/dist/context/example-usage.d.ts +1 -0
  15. package/dist/context/example-usage.js +128 -0
  16. package/dist/context/exchange-summariser.d.ts +80 -0
  17. package/dist/context/exchange-summariser.js +261 -0
  18. package/dist/context/health-monitor.d.ts +97 -0
  19. package/dist/context/health-monitor.example.d.ts +1 -0
  20. package/dist/context/health-monitor.example.js +164 -0
  21. package/dist/context/health-monitor.js +210 -0
  22. package/dist/context/importance-scorer.d.ts +94 -0
  23. package/dist/context/importance-scorer.example.d.ts +1 -0
  24. package/dist/context/importance-scorer.example.js +140 -0
  25. package/dist/context/importance-scorer.js +187 -0
  26. package/dist/context/index.d.ts +9 -0
  27. package/dist/context/index.js +16 -0
  28. package/dist/context/session-helper.d.ts +10 -0
  29. package/dist/context/session-helper.js +51 -0
  30. package/dist/context/session-store.d.ts +94 -0
  31. package/dist/context/session-store.js +286 -0
  32. package/dist/context/usage-estimator.d.ts +131 -0
  33. package/dist/context/usage-estimator.js +260 -0
  34. package/dist/context/usage-estimator.test.d.ts +1 -0
  35. package/dist/context/usage-estimator.test.js +208 -0
  36. package/dist/context-cli.d.ts +16 -0
  37. package/dist/context-cli.js +309 -0
  38. package/dist/evaluation/build-dataset.d.ts +1 -0
  39. package/dist/evaluation/build-dataset.js +135 -0
  40. package/dist/evaluation/threshold-eval.d.ts +63 -0
  41. package/dist/evaluation/threshold-eval.js +250 -0
  42. package/dist/handlers/codedna-handlers.d.ts +2 -2
  43. package/dist/handlers/tool-handlers.js +126 -165
  44. package/dist/helpers/api-client.d.ts +5 -1
  45. package/dist/helpers/api-client.js +3 -1
  46. package/dist/helpers/compact-formatter.d.ts +51 -0
  47. package/dist/helpers/compact-formatter.js +130 -0
  48. package/dist/helpers/engagement-tracker.d.ts +10 -0
  49. package/dist/helpers/engagement-tracker.js +61 -0
  50. package/dist/helpers/error-tracking.js +1 -1
  51. package/dist/helpers/session-validation.d.ts +76 -0
  52. package/dist/helpers/session-validation.js +221 -0
  53. package/dist/helpers/usage-analytics.js +1 -1
  54. package/dist/hooks/index.d.ts +4 -0
  55. package/dist/hooks/index.js +6 -0
  56. package/dist/hooks/post-tool-use-hook-cli.d.ts +2 -0
  57. package/dist/hooks/post-tool-use-hook-cli.js +34 -0
  58. package/dist/hooks/post-tool-use.d.ts +67 -0
  59. package/dist/hooks/post-tool-use.js +234 -0
  60. package/dist/hooks/stop-hook-cli.d.ts +2 -0
  61. package/dist/hooks/stop-hook-cli.js +34 -0
  62. package/dist/hooks/stop.d.ts +64 -0
  63. package/dist/hooks/stop.js +192 -0
  64. package/dist/index.d.ts +3 -0
  65. package/dist/index.js +2 -0
  66. package/dist/logger.d.ts +1 -1
  67. package/dist/logger.js +4 -0
  68. package/dist/resources.js +3 -0
  69. package/dist/setup.js +206 -2
  70. package/dist/templates/claude-md.d.ts +1 -1
  71. package/dist/templates/claude-md.js +23 -35
  72. package/dist/templates/worker-prompt.js +35 -202
  73. package/dist/tools.js +26 -20
  74. package/package.json +6 -2
@@ -0,0 +1,234 @@
1
+ // =============================================================================
2
+ // Post-Tool-Use Hook: Automatic Context Management
3
+ // =============================================================================
4
+ // This hook runs after each tool execution (Read, Edit, Write, Bash, etc.)
5
+ // Updates session state with tool output size and triggers eviction if needed
6
+ //
7
+ // Trigger Points:
8
+ // - estimated_fill > 0.60 → Standard eviction cycle
9
+ // - estimated_fill > 0.70 → Emergency eviction
10
+ //
11
+ // =============================================================================
12
+ import { mcpLogger } from '../logger.js';
13
+ import { getSessionId } from '../helpers/engagement-tracker.js';
14
+ import { estimateTokens } from '../context/usage-estimator.js';
15
+ const sessions = new Map();
16
+ // Default context limit for Claude Code (Sonnet 4.5)
17
+ const DEFAULT_CONTEXT_LIMIT = 200000;
18
+ // Eviction thresholds
19
+ const STANDARD_EVICTION_THRESHOLD = 0.60; // 60%
20
+ const EMERGENCY_EVICTION_THRESHOLD = 0.70; // 70%
21
+ // =============================================================================
22
+ // Session Store
23
+ // =============================================================================
24
+ class SessionStore {
25
+ /**
26
+ * Get or create session data
27
+ */
28
+ getSession(sessionId) {
29
+ let session = sessions.get(sessionId);
30
+ if (!session) {
31
+ session = {
32
+ session_id: sessionId,
33
+ tool_executions: 0,
34
+ estimated_tokens: 0,
35
+ context_limit: DEFAULT_CONTEXT_LIMIT,
36
+ estimated_fill: 0,
37
+ last_updated: new Date(),
38
+ tool_output_tokens: 0,
39
+ };
40
+ sessions.set(sessionId, session);
41
+ }
42
+ return session;
43
+ }
44
+ /**
45
+ * Update session with tool output tokens
46
+ */
47
+ updateSession(sessionId, outputTokens) {
48
+ const session = this.getSession(sessionId);
49
+ // Increment tool execution count
50
+ session.tool_executions += 1;
51
+ // Add output tokens to total
52
+ session.tool_output_tokens += outputTokens;
53
+ session.estimated_tokens += outputTokens;
54
+ // Update estimated fill
55
+ session.estimated_fill = session.estimated_tokens / session.context_limit;
56
+ // Update timestamp
57
+ session.last_updated = new Date();
58
+ sessions.set(sessionId, session);
59
+ return session;
60
+ }
61
+ /**
62
+ * Clear session (for cleanup)
63
+ */
64
+ clearSession(sessionId) {
65
+ sessions.delete(sessionId);
66
+ }
67
+ /**
68
+ * Get all sessions (for debugging)
69
+ */
70
+ getAllSessions() {
71
+ return Array.from(sessions.values());
72
+ }
73
+ }
74
+ const sessionStore = new SessionStore();
75
+ // =============================================================================
76
+ // Eviction Trigger Logic
77
+ // =============================================================================
78
+ class EvictionTriggerEngine {
79
+ /**
80
+ * Check if eviction should be triggered based on fill percentage
81
+ */
82
+ checkEvictionTrigger(session) {
83
+ const fill = session.estimated_fill;
84
+ if (fill > EMERGENCY_EVICTION_THRESHOLD) {
85
+ return {
86
+ triggered: true,
87
+ level: 'emergency',
88
+ estimated_fill: fill,
89
+ message: `EMERGENCY: Context fill at ${(fill * 100).toFixed(1)}% - triggering emergency eviction`,
90
+ };
91
+ }
92
+ if (fill > STANDARD_EVICTION_THRESHOLD) {
93
+ return {
94
+ triggered: true,
95
+ level: 'standard',
96
+ estimated_fill: fill,
97
+ message: `Context fill at ${(fill * 100).toFixed(1)}% - triggering standard eviction`,
98
+ };
99
+ }
100
+ return {
101
+ triggered: false,
102
+ level: 'none',
103
+ estimated_fill: fill,
104
+ message: `Context fill at ${(fill * 100).toFixed(1)}% - no eviction needed`,
105
+ };
106
+ }
107
+ /**
108
+ * Run standard eviction cycle
109
+ * NOTE: This is a placeholder - actual implementation would call into
110
+ * the context rotation/eviction system when it's built
111
+ */
112
+ async runStandardEviction(session) {
113
+ mcpLogger.info('EVICTION', `Running standard eviction for session ${session.session_id}`);
114
+ mcpLogger.debug('EVICTION', `Fill: ${(session.estimated_fill * 100).toFixed(1)}%`);
115
+ // TODO: Integrate with actual eviction engine
116
+ // This would call into packages/tools/src/context/rotation-engine.ts (when built)
117
+ // For now, just log the trigger
118
+ console.warn(`\n⚠️ [ClaudeTools] Context Rotation Triggered`);
119
+ console.warn(` Fill: ${(session.estimated_fill * 100).toFixed(1)}%`);
120
+ console.warn(` Action: Standard eviction cycle\n`);
121
+ }
122
+ /**
123
+ * Run emergency eviction cycle
124
+ * More aggressive eviction for critical situations
125
+ */
126
+ async runEmergencyEviction(session) {
127
+ mcpLogger.info('EVICTION', `Running EMERGENCY eviction for session ${session.session_id}`);
128
+ mcpLogger.debug('EVICTION', `Fill: ${(session.estimated_fill * 100).toFixed(1)}%`);
129
+ // TODO: Integrate with actual eviction engine
130
+ // Emergency mode would evict more aggressively
131
+ console.error(`\n🚨 [ClaudeTools] EMERGENCY Context Rotation`);
132
+ console.error(` Fill: ${(session.estimated_fill * 100).toFixed(1)}%`);
133
+ console.error(` Action: Emergency eviction - aggressive cleanup\n`);
134
+ }
135
+ }
136
+ const evictionEngine = new EvictionTriggerEngine();
137
+ // =============================================================================
138
+ // Post-Tool-Use Hook Handler
139
+ // =============================================================================
140
+ /**
141
+ * Handle PostToolUse hook event
142
+ * Updates session state with tool output and triggers eviction if needed
143
+ */
144
+ export async function handlePostToolUseHook(input) {
145
+ try {
146
+ // Get session ID (from input or fallback to tracker)
147
+ const sessionId = input.session_id || getSessionId();
148
+ // CRITICAL: Prevent infinite loops
149
+ if (input.post_tool_use_active) {
150
+ mcpLogger.debug('HOOK', 'PostToolUse hook already active - skipping');
151
+ return;
152
+ }
153
+ // Only track tools that generate significant output
154
+ // Read, Edit, Write, Bash, etc. - not memory tools
155
+ if (!shouldTrackTool(input.tool_name)) {
156
+ mcpLogger.debug('HOOK', `Skipping tracking for tool: ${input.tool_name}`);
157
+ return;
158
+ }
159
+ // Estimate tokens from tool output
160
+ const outputTokens = input.tool_output ? estimateTokens(input.tool_output) : 0;
161
+ if (outputTokens === 0) {
162
+ mcpLogger.debug('HOOK', `No output tokens for ${input.tool_name} - skipping`);
163
+ return;
164
+ }
165
+ // Update session state
166
+ const session = sessionStore.updateSession(sessionId, outputTokens);
167
+ // Log tool execution
168
+ mcpLogger.info('HOOK', `PostToolUse: ${input.tool_name} (+${outputTokens} tokens)`);
169
+ mcpLogger.debug('HOOK', `Session: ${sessionId}`);
170
+ mcpLogger.debug('HOOK', `Total estimated: ${session.estimated_tokens} tokens`);
171
+ mcpLogger.debug('HOOK', `Fill: ${(session.estimated_fill * 100).toFixed(1)}%`);
172
+ // Check eviction trigger
173
+ const trigger = evictionEngine.checkEvictionTrigger(session);
174
+ if (trigger.triggered) {
175
+ mcpLogger.info('HOOK', trigger.message);
176
+ if (trigger.level === 'emergency') {
177
+ await evictionEngine.runEmergencyEviction(session);
178
+ }
179
+ else if (trigger.level === 'standard') {
180
+ await evictionEngine.runStandardEviction(session);
181
+ }
182
+ }
183
+ else {
184
+ mcpLogger.debug('HOOK', trigger.message);
185
+ }
186
+ // Output verbose status if significant
187
+ if (outputTokens > 1000 || trigger.triggered) {
188
+ outputToolOutputSummary(input.tool_name, outputTokens, session, trigger);
189
+ }
190
+ }
191
+ catch (error) {
192
+ // Don't fail the tool execution on hook errors
193
+ mcpLogger.error('HOOK', `Error in PostToolUse hook: ${error}`);
194
+ }
195
+ }
196
+ /**
197
+ * Determine if a tool should be tracked for context usage
198
+ * Only track tools that generate significant output (Read, Bash, etc.)
199
+ * Skip memory tools (they're tracked separately)
200
+ */
201
+ function shouldTrackTool(toolName) {
202
+ // Tools that generate significant output
203
+ const trackedTools = [
204
+ 'Read',
205
+ 'Bash',
206
+ 'Grep',
207
+ 'Glob',
208
+ 'WebFetch',
209
+ 'WebSearch',
210
+ // Add other tools as needed
211
+ ];
212
+ return trackedTools.includes(toolName);
213
+ }
214
+ /**
215
+ * Output tool execution summary
216
+ * Shown when tool generates significant output or triggers eviction
217
+ */
218
+ function outputToolOutputSummary(toolName, outputTokens, session, trigger) {
219
+ const fillPercentage = (session.estimated_fill * 100).toFixed(1);
220
+ const statusEmoji = trigger.triggered ? '⚠️' : '✓';
221
+ console.log(`\n${statusEmoji} Tool Output Tracking:`);
222
+ console.log(` Tool: ${toolName}`);
223
+ console.log(` Output: +${outputTokens.toLocaleString()} tokens`);
224
+ console.log(` Total: ${session.estimated_tokens.toLocaleString()}/${session.context_limit.toLocaleString()} tokens (${fillPercentage}%)`);
225
+ console.log(` Executions: ${session.tool_executions}`);
226
+ if (trigger.triggered) {
227
+ console.log(` Eviction: ${trigger.level.toUpperCase()} triggered`);
228
+ }
229
+ console.log('');
230
+ }
231
+ // =============================================================================
232
+ // Exports
233
+ // =============================================================================
234
+ export { sessionStore, evictionEngine };
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ // =============================================================================
3
+ // Stop Hook CLI Entry Point
4
+ // =============================================================================
5
+ // Called by Claude Code when Stop event occurs
6
+ // Reads hook input from stdin, processes it, and outputs to stdout/stderr
7
+ import { handleStopHook } from './stop.js';
8
+ async function main() {
9
+ try {
10
+ // Read JSON input from stdin
11
+ let inputData = '';
12
+ // Set up stdin reading
13
+ process.stdin.setEncoding('utf8');
14
+ for await (const chunk of process.stdin) {
15
+ inputData += chunk;
16
+ }
17
+ // Parse hook input
18
+ const hookInput = JSON.parse(inputData);
19
+ // Validate hook event
20
+ if (hookInput.hook_event_name !== 'Stop') {
21
+ console.error(`Error: Expected Stop event, got ${hookInput.hook_event_name}`);
22
+ process.exit(1);
23
+ }
24
+ // Process the hook
25
+ await handleStopHook(hookInput);
26
+ // Success - exit code 0
27
+ process.exit(0);
28
+ }
29
+ catch (error) {
30
+ console.error('Error processing Stop hook:', error);
31
+ process.exit(1);
32
+ }
33
+ }
34
+ main();
@@ -0,0 +1,64 @@
1
+ import { getSessionStats } from '../helpers/session-validation.js';
2
+ interface StopHookInput {
3
+ session_id?: string;
4
+ hook_event_name: 'Stop';
5
+ stop_hook_reason: 'end_turn' | 'max_turns' | 'error';
6
+ stop_hook_active?: boolean;
7
+ last_assistant_message?: string;
8
+ }
9
+ interface ContextHealth {
10
+ estimated_fill: number;
11
+ warning_level: 'healthy' | 'caution' | 'critical';
12
+ message: string;
13
+ }
14
+ interface SessionData {
15
+ session_id: string;
16
+ exchanges: number;
17
+ estimated_tokens: number;
18
+ context_limit: number;
19
+ estimated_fill: number;
20
+ last_updated: Date;
21
+ engagement_score: number;
22
+ }
23
+ declare class UsageEstimator {
24
+ /**
25
+ * Estimate tokens used in current session
26
+ * Uses session statistics to calculate rough estimate
27
+ */
28
+ getEstimatedTokens(sessionStats: ReturnType<typeof getSessionStats>): number;
29
+ /**
30
+ * Calculate estimated fill percentage
31
+ */
32
+ getEstimatedFill(contextLimit: number, estimatedTokens: number): number;
33
+ /**
34
+ * Determine health status based on fill percentage
35
+ */
36
+ getHealthStatus(fill: number): ContextHealth;
37
+ }
38
+ declare const usageEstimator: UsageEstimator;
39
+ declare class SessionStore {
40
+ /**
41
+ * Get or create session data
42
+ */
43
+ getSession(sessionId: string): SessionData;
44
+ /**
45
+ * Update session with latest stats
46
+ */
47
+ updateSession(sessionId: string, stats: ReturnType<typeof getSessionStats>): SessionData;
48
+ /**
49
+ * Get all sessions (for debugging)
50
+ */
51
+ getAllSessions(): SessionData[];
52
+ /**
53
+ * Clear session (for cleanup)
54
+ */
55
+ clearSession(sessionId: string): void;
56
+ }
57
+ declare const sessionStore: SessionStore;
58
+ /**
59
+ * Handle Stop hook event
60
+ * Updates session state and logs context health warnings
61
+ */
62
+ export declare function handleStopHook(input: StopHookInput): Promise<void>;
63
+ export { sessionStore, usageEstimator };
64
+ export type { StopHookInput, SessionData, ContextHealth };
@@ -0,0 +1,192 @@
1
+ // =============================================================================
2
+ // Stop Hook: Usage Dashboard and Context Health Monitoring
3
+ // =============================================================================
4
+ // This hook runs when the main agent finishes responding (Stop event)
5
+ // Updates session state with latest exchange data and warns if context fill > 60%
6
+ import { mcpLogger } from '../logger.js';
7
+ import { getSessionId } from '../helpers/engagement-tracker.js';
8
+ import { getSessionStats } from '../helpers/session-validation.js';
9
+ const sessions = new Map();
10
+ // Default context limit for Claude Code (Sonnet 4.5)
11
+ const DEFAULT_CONTEXT_LIMIT = 200000;
12
+ // Token estimation heuristic (rough estimate)
13
+ // Average tokens per exchange based on typical usage patterns
14
+ const AVG_TOKENS_PER_EXCHANGE = 2000; // Conservative estimate
15
+ // =============================================================================
16
+ // Usage Estimator
17
+ // =============================================================================
18
+ class UsageEstimator {
19
+ /**
20
+ * Estimate tokens used in current session
21
+ * Uses session statistics to calculate rough estimate
22
+ */
23
+ getEstimatedTokens(sessionStats) {
24
+ // Base estimation from tool calls
25
+ const toolCallTokens = sessionStats.totalCalls * 200; // ~200 tokens per tool call
26
+ // Add engagement-based estimation
27
+ const engagementTokens = sessionStats.engagement.score * 100; // More engagement = more context
28
+ // Add base conversation overhead
29
+ const baseOverhead = 5000; // Initial prompt + system context
30
+ return baseOverhead + toolCallTokens + engagementTokens;
31
+ }
32
+ /**
33
+ * Calculate estimated fill percentage
34
+ */
35
+ getEstimatedFill(contextLimit, estimatedTokens) {
36
+ return estimatedTokens / contextLimit;
37
+ }
38
+ /**
39
+ * Determine health status based on fill percentage
40
+ */
41
+ getHealthStatus(fill) {
42
+ if (fill < 0.6) {
43
+ return {
44
+ estimated_fill: fill,
45
+ warning_level: 'healthy',
46
+ message: 'Context usage is healthy',
47
+ };
48
+ }
49
+ else if (fill < 0.8) {
50
+ return {
51
+ estimated_fill: fill,
52
+ warning_level: 'caution',
53
+ message: 'Context fill approaching limits - consider eviction strategies',
54
+ };
55
+ }
56
+ else {
57
+ return {
58
+ estimated_fill: fill,
59
+ warning_level: 'critical',
60
+ message: 'CRITICAL: Context fill very high - eviction strongly recommended',
61
+ };
62
+ }
63
+ }
64
+ }
65
+ const usageEstimator = new UsageEstimator();
66
+ // =============================================================================
67
+ // Session Store
68
+ // =============================================================================
69
+ class SessionStore {
70
+ /**
71
+ * Get or create session data
72
+ */
73
+ getSession(sessionId) {
74
+ let session = sessions.get(sessionId);
75
+ if (!session) {
76
+ session = {
77
+ session_id: sessionId,
78
+ exchanges: 0,
79
+ estimated_tokens: 0,
80
+ context_limit: DEFAULT_CONTEXT_LIMIT,
81
+ estimated_fill: 0,
82
+ last_updated: new Date(),
83
+ engagement_score: 0,
84
+ };
85
+ sessions.set(sessionId, session);
86
+ }
87
+ return session;
88
+ }
89
+ /**
90
+ * Update session with latest stats
91
+ */
92
+ updateSession(sessionId, stats) {
93
+ const session = this.getSession(sessionId);
94
+ // Update exchange count (each Stop = 1 exchange)
95
+ session.exchanges += 1;
96
+ // Update estimated tokens
97
+ session.estimated_tokens = usageEstimator.getEstimatedTokens(stats);
98
+ // Update estimated fill
99
+ session.estimated_fill = usageEstimator.getEstimatedFill(session.context_limit, session.estimated_tokens);
100
+ // Update engagement score
101
+ session.engagement_score = stats.engagement.score;
102
+ // Update timestamp
103
+ session.last_updated = new Date();
104
+ sessions.set(sessionId, session);
105
+ return session;
106
+ }
107
+ /**
108
+ * Get all sessions (for debugging)
109
+ */
110
+ getAllSessions() {
111
+ return Array.from(sessions.values());
112
+ }
113
+ /**
114
+ * Clear session (for cleanup)
115
+ */
116
+ clearSession(sessionId) {
117
+ sessions.delete(sessionId);
118
+ }
119
+ }
120
+ const sessionStore = new SessionStore();
121
+ // =============================================================================
122
+ // Stop Hook Handler
123
+ // =============================================================================
124
+ /**
125
+ * Handle Stop hook event
126
+ * Updates session state and logs context health warnings
127
+ */
128
+ export async function handleStopHook(input) {
129
+ try {
130
+ // Get session ID (from input or fallback to tracker)
131
+ const sessionId = input.session_id || getSessionId();
132
+ // CRITICAL: Prevent infinite loops
133
+ if (input.stop_hook_active) {
134
+ mcpLogger.debug('HOOK', 'Stop hook already active - allowing stop');
135
+ return;
136
+ }
137
+ // Get current session statistics
138
+ const stats = getSessionStats();
139
+ // Update session state
140
+ const session = sessionStore.updateSession(sessionId, stats);
141
+ // Get health status
142
+ const health = usageEstimator.getHealthStatus(session.estimated_fill);
143
+ // Log session statistics
144
+ mcpLogger.info('HOOK', `Stop event processed - Session: ${sessionId}`);
145
+ mcpLogger.debug('HOOK', `Exchanges: ${session.exchanges}`);
146
+ mcpLogger.debug('HOOK', `Estimated tokens: ${session.estimated_tokens}`);
147
+ mcpLogger.debug('HOOK', `Engagement score: ${session.engagement_score}/100`);
148
+ // Warn if approaching limits
149
+ if (health.warning_level === 'caution') {
150
+ console.warn(`\n⚠️ [ClaudeTools] Context fill at ${(health.estimated_fill * 100).toFixed(1)}%`);
151
+ console.warn(` ${health.message}\n`);
152
+ }
153
+ else if (health.warning_level === 'critical') {
154
+ console.error(`\n🚨 [ClaudeTools] CRITICAL: Context fill at ${(health.estimated_fill * 100).toFixed(1)}%`);
155
+ console.error(` ${health.message}\n`);
156
+ }
157
+ else {
158
+ mcpLogger.debug('HOOK', `Context health: ${health.warning_level} (${(health.estimated_fill * 100).toFixed(1)}%)`);
159
+ }
160
+ // Output context health status (for verbose mode)
161
+ outputContextHealth(session, health);
162
+ }
163
+ catch (error) {
164
+ // Don't fail the stop event on errors
165
+ mcpLogger.error('HOOK', `Error in Stop hook: ${error}`);
166
+ }
167
+ }
168
+ /**
169
+ * Output context health status
170
+ * Shown in verbose mode via stdout
171
+ */
172
+ function outputContextHealth(session, health) {
173
+ const fillPercentage = (health.estimated_fill * 100).toFixed(1);
174
+ const statusEmoji = health.warning_level === 'healthy'
175
+ ? '✓'
176
+ : health.warning_level === 'caution'
177
+ ? '⚠'
178
+ : '🚨';
179
+ console.log(`\n${statusEmoji} Context Health Status:`);
180
+ console.log(` Fill: ${fillPercentage}% (${session.estimated_tokens.toLocaleString()}/${session.context_limit.toLocaleString()} tokens)`);
181
+ console.log(` Exchanges: ${session.exchanges}`);
182
+ console.log(` Engagement: ${session.engagement_score}/100`);
183
+ console.log(` Status: ${health.warning_level.toUpperCase()}`);
184
+ if (health.warning_level !== 'healthy') {
185
+ console.log(` Message: ${health.message}`);
186
+ }
187
+ console.log('');
188
+ }
189
+ // =============================================================================
190
+ // Exports
191
+ // =============================================================================
192
+ export { sessionStore, usageEstimator };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  export type { ExpertWorker } from './helpers/workers.js';
2
2
  export type { Task, TaskContext, DispatchableTask } from './helpers/tasks.js';
3
+ export type { DeduplicationTracker } from './context/index.js';
3
4
  export { EXPERT_WORKERS, matchTaskToWorker } from './helpers/workers.js';
4
5
  export { parseJsonArray, getDispatchableTasks, getExecutionContext, resolveTaskDependencies, createTask, listTasks, getTask, claimTask, releaseTask, updateTaskStatus, addTaskContext, getTaskSummary, heartbeatTask } from './helpers/tasks.js';
5
6
  export { injectContext } from './helpers/api-client.js';
7
+ export { recordToolCall, getToolCallWarnings, getSessionStats, clearSessionState, recordContextReference, calculateEngagementScore, getEngagementStats } from './helpers/session-validation.js';
8
+ export { createDeduplicationTracker, InMemoryDeduplicationTracker } from './context/index.js';
6
9
  export declare function startServer(): Promise<void>;
package/dist/index.js CHANGED
@@ -15,6 +15,8 @@ import { registerPromptHandlers } from './prompts.js';
15
15
  export { EXPERT_WORKERS, matchTaskToWorker } from './helpers/workers.js';
16
16
  export { parseJsonArray, getDispatchableTasks, getExecutionContext, resolveTaskDependencies, createTask, listTasks, getTask, claimTask, releaseTask, updateTaskStatus, addTaskContext, getTaskSummary, heartbeatTask } from './helpers/tasks.js';
17
17
  export { injectContext } from './helpers/api-client.js';
18
+ export { recordToolCall, getToolCallWarnings, getSessionStats, clearSessionState, recordContextReference, calculateEngagementScore, getEngagementStats } from './helpers/session-validation.js';
19
+ export { createDeduplicationTracker, InMemoryDeduplicationTracker } from './context/index.js';
18
20
  // =============================================================================
19
21
  // Server Initialization
20
22
  // =============================================================================
package/dist/logger.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type LogCategory = 'TOOL' | 'API' | 'MEMORY' | 'SEARCH' | 'INJECT' | 'EXTRACT' | 'STORE' | 'QUERY' | 'IMPACT' | 'PATTERN' | 'CONFIG' | 'REGISTRATION' | 'ERROR';
1
+ export type LogCategory = 'TOOL' | 'API' | 'MEMORY' | 'SEARCH' | 'INJECT' | 'EXTRACT' | 'STORE' | 'QUERY' | 'IMPACT' | 'PATTERN' | 'CONFIG' | 'REGISTRATION' | 'HOOK' | 'ERROR' | 'SESSION_HELPER' | 'EVICTION' | 'CONTEXT';
2
2
  declare class MCPLogger {
3
3
  private logFile;
4
4
  private logDir;
package/dist/logger.js CHANGED
@@ -19,7 +19,11 @@ const CATEGORY_ICONS = {
19
19
  PATTERN: '🔬',
20
20
  CONFIG: '⚙️',
21
21
  REGISTRATION: '📝',
22
+ HOOK: '🪝',
22
23
  ERROR: '❌',
24
+ SESSION_HELPER: '📋',
25
+ EVICTION: '🗑️',
26
+ CONTEXT: '📦',
23
27
  };
24
28
  const LEVEL_ICONS = {
25
29
  DEBUG: '🔹',
package/dist/resources.js CHANGED
@@ -5,6 +5,7 @@ import { ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcon
5
5
  import { getSummary, getEntities, getContext } from './helpers/api-client.js';
6
6
  import { formatContextForClaude } from './helpers/formatter.js';
7
7
  import { getDefaultProjectId } from './helpers/config.js';
8
+ import { recordContextReference } from './helpers/session-validation.js';
8
9
  export function registerResourceHandlers(server) {
9
10
  server.setRequestHandler(ListResourcesRequestSchema, async () => ({
10
11
  resources: [
@@ -59,6 +60,8 @@ export function registerResourceHandlers(server) {
59
60
  }
60
61
  if (uri === 'memory://context') {
61
62
  const context = await getContext(projectId);
63
+ // Track context reference for engagement scoring (+20 points)
64
+ recordContextReference(projectId);
62
65
  return {
63
66
  contents: [
64
67
  {