@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,61 @@
1
+ // =============================================================================
2
+ // Engagement Tracking Helper
3
+ // =============================================================================
4
+ // Tracks memory engagement events to the API for persistence
5
+ // =============================================================================
6
+ import { mcpLogger } from '../logger.js';
7
+ import { API_BASE_URL } from './config.js';
8
+ /**
9
+ * Track an engagement event via the API
10
+ * This persists the event to D1 for analytics
11
+ */
12
+ export async function trackEngagementEvent(sessionId, projectId, eventType) {
13
+ try {
14
+ // Get API key from environment
15
+ const apiKey = process.env.CLAUDETOOLS_API_KEY;
16
+ if (!apiKey) {
17
+ mcpLogger.debug('API', 'No API key - skipping engagement tracking');
18
+ return;
19
+ }
20
+ const response = await fetch(`${API_BASE_URL}/api/v1/engagement/track`, {
21
+ method: 'POST',
22
+ headers: {
23
+ 'Content-Type': 'application/json',
24
+ Authorization: `Bearer ${apiKey}`,
25
+ },
26
+ body: JSON.stringify({
27
+ session_id: sessionId,
28
+ project_id: projectId,
29
+ event_type: eventType,
30
+ }),
31
+ });
32
+ if (!response.ok) {
33
+ mcpLogger.warn('API', `Failed to track engagement event: ${response.status}`);
34
+ return;
35
+ }
36
+ mcpLogger.debug('API', `Tracked ${eventType} event for session ${sessionId}`);
37
+ }
38
+ catch (error) {
39
+ // Don't fail the main operation if tracking fails
40
+ mcpLogger.debug('API', `Failed to track engagement event: ${error}`);
41
+ }
42
+ }
43
+ /**
44
+ * Get current session ID
45
+ * This should ideally come from Claude Code's session system
46
+ * For now, we'll use a simple timestamp-based session ID
47
+ */
48
+ let currentSessionId = null;
49
+ export function getSessionId() {
50
+ if (!currentSessionId) {
51
+ // Generate session ID: session_<timestamp>
52
+ currentSessionId = `session_${Date.now()}`;
53
+ }
54
+ return currentSessionId;
55
+ }
56
+ /**
57
+ * Reset session ID (for testing or explicit session boundaries)
58
+ */
59
+ export function resetSessionId() {
60
+ currentSessionId = null;
61
+ }
@@ -28,7 +28,7 @@ export class CodeDNAErrorTracker {
28
28
  // Store error in memory system as a fact
29
29
  const userId = DEFAULT_USER_ID;
30
30
  const projectId = error.projectId || resolveProjectId();
31
- await storeFact(projectId, 'CodeDNA', 'ERROR_OCCURRED', error.operation, JSON.stringify(errorRecord), userId);
31
+ await storeFact(projectId, 'CodeDNA', 'ERROR_OCCURRED', error.operation, JSON.stringify(errorRecord), { userId });
32
32
  // Log to console for immediate visibility
33
33
  console.error('[CodeDNA Error]', {
34
34
  operation: error.operation,
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Record a tool call in the session
3
+ */
4
+ export declare function recordToolCall(name: string, args?: Record<string, unknown>): void;
5
+ /**
6
+ * Validate that memory_search was called before task_start
7
+ * Returns validation result with warnings if violated
8
+ */
9
+ export declare function validateTaskStartSequence(taskId: string): {
10
+ valid: boolean;
11
+ warnings: string[];
12
+ };
13
+ /**
14
+ * Validate that codebase_map was called before code modification tools
15
+ * Code modification tools: codedna_generate_*, or any task involving file changes
16
+ */
17
+ export declare function validateCodebaseMapSequence(): {
18
+ valid: boolean;
19
+ warnings: string[];
20
+ };
21
+ /**
22
+ * Check if a tool name indicates code modification
23
+ */
24
+ export declare function isCodeModificationTool(toolName: string): boolean;
25
+ /**
26
+ * Record when auto-injected context is used
27
+ * This tracks the +20 point event when context is automatically provided
28
+ */
29
+ export declare function recordContextReference(projectId?: string): void;
30
+ /**
31
+ * Calculate engagement score (0-100)
32
+ * - memory_search: +30
33
+ * - memory_detail: +20
34
+ * - memory_inject: +20
35
+ * - memory_store_fact: +10
36
+ * - context_referenced: +20
37
+ */
38
+ export declare function calculateEngagementScore(): number;
39
+ /**
40
+ * Get engagement statistics
41
+ */
42
+ export declare function getEngagementStats(): {
43
+ score: number;
44
+ breakdown: {
45
+ searchCount: number;
46
+ searchPoints: number;
47
+ detailCount: number;
48
+ detailPoints: number;
49
+ injectCount: number;
50
+ injectPoints: number;
51
+ storeFactCount: number;
52
+ storeFactPoints: number;
53
+ contextReferencedCount: number;
54
+ contextReferencedPoints: number;
55
+ };
56
+ };
57
+ /**
58
+ * Get session statistics for debugging
59
+ */
60
+ export declare function getSessionStats(): {
61
+ totalCalls: number;
62
+ uniqueTaskStarts: number;
63
+ uniqueSearches: number;
64
+ codebaseMapCalled: boolean;
65
+ recentCalls: string[];
66
+ engagement: ReturnType<typeof getEngagementStats>;
67
+ };
68
+ /**
69
+ * Clear session state (for testing or manual reset)
70
+ */
71
+ export declare function clearSessionState(): void;
72
+ /**
73
+ * Get formatted warnings for tool execution
74
+ * Returns null if no warnings
75
+ */
76
+ export declare function getToolCallWarnings(toolName: string, args?: Record<string, unknown>): string | null;
@@ -0,0 +1,221 @@
1
+ // =============================================================================
2
+ // Session-level Tool Call Validation
3
+ // =============================================================================
4
+ // Tracks tool call sequences within a session to enforce best practices:
5
+ // - memory_search before task_start
6
+ // - codebase_map before code modifications
7
+ // - No code in task descriptions (enforced via validateTaskDescription)
8
+ import { mcpLogger } from '../logger.js';
9
+ import { trackEngagementEvent, getSessionId } from './engagement-tracker.js';
10
+ const sessionState = {
11
+ toolCalls: [],
12
+ taskStarts: new Set(),
13
+ memorySearches: new Set(),
14
+ codebaseMapCalled: false,
15
+ engagement: {
16
+ searchCount: 0,
17
+ detailCount: 0,
18
+ injectCount: 0,
19
+ storeFactCount: 0,
20
+ contextReferencedCount: 0,
21
+ },
22
+ };
23
+ /**
24
+ * Record a tool call in the session
25
+ */
26
+ export function recordToolCall(name, args) {
27
+ sessionState.toolCalls.push({
28
+ name,
29
+ timestamp: Date.now(),
30
+ args,
31
+ });
32
+ // Track specific tool types
33
+ if (name === 'task_start' && args?.task_id) {
34
+ sessionState.taskStarts.add(args.task_id);
35
+ }
36
+ if (name === 'memory_search' && args?.query) {
37
+ sessionState.memorySearches.add(args.query);
38
+ }
39
+ if (name === 'codebase_map') {
40
+ sessionState.codebaseMapCalled = true;
41
+ }
42
+ // Track engagement and persist to API
43
+ const projectId = args?.project_id;
44
+ let eventType;
45
+ switch (name) {
46
+ case 'memory_search':
47
+ sessionState.engagement.searchCount++;
48
+ eventType = 'search';
49
+ break;
50
+ case 'memory_detail':
51
+ sessionState.engagement.detailCount++;
52
+ eventType = 'detail';
53
+ break;
54
+ case 'memory_inject':
55
+ sessionState.engagement.injectCount++;
56
+ eventType = 'inject';
57
+ break;
58
+ case 'memory_store_fact':
59
+ sessionState.engagement.storeFactCount++;
60
+ eventType = 'store_fact';
61
+ break;
62
+ }
63
+ // Track to API if this is a memory tool
64
+ if (eventType && projectId) {
65
+ trackEngagementEvent(getSessionId(), projectId, eventType).catch(() => {
66
+ // Silently fail - don't block the tool call
67
+ });
68
+ }
69
+ mcpLogger.debug('TOOL', `Recorded tool call: ${name}`);
70
+ }
71
+ /**
72
+ * Validate that memory_search was called before task_start
73
+ * Returns validation result with warnings if violated
74
+ */
75
+ export function validateTaskStartSequence(taskId) {
76
+ const warnings = [];
77
+ // Check if any memory_search happened in this session
78
+ if (sessionState.memorySearches.size === 0) {
79
+ warnings.push('BEST PRACTICE: Call memory_search() before task_start() to recall relevant context and past decisions.');
80
+ }
81
+ // Check recent tool calls (last 10 calls)
82
+ const recentCalls = sessionState.toolCalls.slice(-10).map(c => c.name);
83
+ const hasRecentMemorySearch = recentCalls.includes('memory_search');
84
+ if (!hasRecentMemorySearch && sessionState.memorySearches.size > 0) {
85
+ warnings.push('RECOMMENDATION: Consider calling memory_search() to check for updated context since your last search.');
86
+ }
87
+ return {
88
+ valid: warnings.length === 0,
89
+ warnings,
90
+ };
91
+ }
92
+ /**
93
+ * Validate that codebase_map was called before code modification tools
94
+ * Code modification tools: codedna_generate_*, or any task involving file changes
95
+ */
96
+ export function validateCodebaseMapSequence() {
97
+ const warnings = [];
98
+ if (!sessionState.codebaseMapCalled) {
99
+ warnings.push('BEST PRACTICE: Call codebase_map() before making code changes to understand project structure and prevent conflicts.');
100
+ }
101
+ return {
102
+ valid: warnings.length === 0,
103
+ warnings,
104
+ };
105
+ }
106
+ /**
107
+ * Check if a tool name indicates code modification
108
+ */
109
+ export function isCodeModificationTool(toolName) {
110
+ const codeTools = [
111
+ 'codedna_generate_api',
112
+ 'codedna_generate_frontend',
113
+ 'codedna_generate_component',
114
+ 'task_start', // Tasks often involve code changes
115
+ ];
116
+ return codeTools.includes(toolName);
117
+ }
118
+ /**
119
+ * Record when auto-injected context is used
120
+ * This tracks the +20 point event when context is automatically provided
121
+ */
122
+ export function recordContextReference(projectId) {
123
+ sessionState.engagement.contextReferencedCount++;
124
+ // Track to API if project ID is available
125
+ if (projectId) {
126
+ trackEngagementEvent(getSessionId(), projectId, 'context_referenced').catch(() => {
127
+ // Silently fail - don't block the resource read
128
+ });
129
+ }
130
+ mcpLogger.debug('TOOL', 'Recorded context reference (auto-inject)');
131
+ }
132
+ /**
133
+ * Calculate engagement score (0-100)
134
+ * - memory_search: +30
135
+ * - memory_detail: +20
136
+ * - memory_inject: +20
137
+ * - memory_store_fact: +10
138
+ * - context_referenced: +20
139
+ */
140
+ export function calculateEngagementScore() {
141
+ const { searchCount, detailCount, injectCount, storeFactCount, contextReferencedCount } = sessionState.engagement;
142
+ const rawScore = searchCount * 30 +
143
+ detailCount * 20 +
144
+ injectCount * 20 +
145
+ storeFactCount * 10 +
146
+ contextReferencedCount * 20;
147
+ // Cap at 100
148
+ return Math.min(rawScore, 100);
149
+ }
150
+ /**
151
+ * Get engagement statistics
152
+ */
153
+ export function getEngagementStats() {
154
+ const { searchCount, detailCount, injectCount, storeFactCount, contextReferencedCount } = sessionState.engagement;
155
+ return {
156
+ score: calculateEngagementScore(),
157
+ breakdown: {
158
+ searchCount,
159
+ searchPoints: searchCount * 30,
160
+ detailCount,
161
+ detailPoints: detailCount * 20,
162
+ injectCount,
163
+ injectPoints: injectCount * 20,
164
+ storeFactCount,
165
+ storeFactPoints: storeFactCount * 10,
166
+ contextReferencedCount,
167
+ contextReferencedPoints: contextReferencedCount * 20,
168
+ },
169
+ };
170
+ }
171
+ /**
172
+ * Get session statistics for debugging
173
+ */
174
+ export function getSessionStats() {
175
+ return {
176
+ totalCalls: sessionState.toolCalls.length,
177
+ uniqueTaskStarts: sessionState.taskStarts.size,
178
+ uniqueSearches: sessionState.memorySearches.size,
179
+ codebaseMapCalled: sessionState.codebaseMapCalled,
180
+ recentCalls: sessionState.toolCalls.slice(-10).map(c => c.name),
181
+ engagement: getEngagementStats(),
182
+ };
183
+ }
184
+ /**
185
+ * Clear session state (for testing or manual reset)
186
+ */
187
+ export function clearSessionState() {
188
+ sessionState.toolCalls = [];
189
+ sessionState.taskStarts.clear();
190
+ sessionState.memorySearches.clear();
191
+ sessionState.codebaseMapCalled = false;
192
+ sessionState.engagement = {
193
+ searchCount: 0,
194
+ detailCount: 0,
195
+ injectCount: 0,
196
+ storeFactCount: 0,
197
+ contextReferencedCount: 0,
198
+ };
199
+ mcpLogger.debug('TOOL', 'Session state cleared');
200
+ }
201
+ /**
202
+ * Get formatted warnings for tool execution
203
+ * Returns null if no warnings
204
+ */
205
+ export function getToolCallWarnings(toolName, args) {
206
+ const warnings = [];
207
+ // Validate task_start sequence
208
+ if (toolName === 'task_start' && args?.task_id) {
209
+ const validation = validateTaskStartSequence(args.task_id);
210
+ warnings.push(...validation.warnings);
211
+ }
212
+ // Validate codebase_map before code changes
213
+ if (isCodeModificationTool(toolName)) {
214
+ const validation = validateCodebaseMapSequence();
215
+ warnings.push(...validation.warnings);
216
+ }
217
+ if (warnings.length === 0) {
218
+ return null;
219
+ }
220
+ return `\n⚠️ WORKFLOW RECOMMENDATIONS:\n${warnings.map(w => ` - ${w}`).join('\n')}\n`;
221
+ }
@@ -29,7 +29,7 @@ export class UsageAnalytics {
29
29
  const userId = DEFAULT_USER_ID;
30
30
  const projectId = event.projectId || resolveProjectId();
31
31
  // Store as fact in memory system
32
- await storeFact(projectId, 'CodeDNA', 'GENERATION_COMPLETED', event.generator || event.operation, JSON.stringify(usageEvent), userId);
32
+ await storeFact(projectId, 'CodeDNA', 'GENERATION_COMPLETED', event.generator || event.operation, JSON.stringify(usageEvent), { userId });
33
33
  console.log('[CodeDNA Analytics]', {
34
34
  operation: event.operation,
35
35
  generator: event.generator,
@@ -0,0 +1,4 @@
1
+ export { handleStopHook, sessionStore, usageEstimator } from './stop.js';
2
+ export type { StopHookInput, SessionData, ContextHealth } from './stop.js';
3
+ export { handlePostToolUseHook, sessionStore as postToolUseSessionStore, evictionEngine, } from './post-tool-use.js';
4
+ export type { PostToolUseInput, SessionData as PostToolUseSessionData, EvictionTrigger, } from './post-tool-use.js';
@@ -0,0 +1,6 @@
1
+ // =============================================================================
2
+ // Claude Code Hooks - Index
3
+ // =============================================================================
4
+ // Exports hook handlers for Claude Code integration
5
+ export { handleStopHook, sessionStore, usageEstimator } from './stop.js';
6
+ export { handlePostToolUseHook, sessionStore as postToolUseSessionStore, evictionEngine, } from './post-tool-use.js';
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ // =============================================================================
3
+ // Post-Tool-Use Hook CLI Entry Point
4
+ // =============================================================================
5
+ // Called by Claude Code when PostToolUse event occurs
6
+ // Reads hook input from stdin, processes it, and outputs to stdout/stderr
7
+ import { handlePostToolUseHook } from './post-tool-use.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 !== 'PostToolUse') {
21
+ console.error(`Error: Expected PostToolUse event, got ${hookInput.hook_event_name}`);
22
+ process.exit(1);
23
+ }
24
+ // Process the hook
25
+ await handlePostToolUseHook(hookInput);
26
+ // Success - exit code 0
27
+ process.exit(0);
28
+ }
29
+ catch (error) {
30
+ console.error('Error processing PostToolUse hook:', error);
31
+ process.exit(1);
32
+ }
33
+ }
34
+ main();
@@ -0,0 +1,67 @@
1
+ interface PostToolUseInput {
2
+ session_id?: string;
3
+ hook_event_name: 'PostToolUse';
4
+ tool_name: string;
5
+ tool_input?: Record<string, unknown>;
6
+ tool_output?: string;
7
+ post_tool_use_active?: boolean;
8
+ }
9
+ interface EvictionTrigger {
10
+ triggered: boolean;
11
+ level: 'none' | 'standard' | 'emergency';
12
+ estimated_fill: number;
13
+ message: string;
14
+ }
15
+ interface SessionData {
16
+ session_id: string;
17
+ tool_executions: number;
18
+ estimated_tokens: number;
19
+ context_limit: number;
20
+ estimated_fill: number;
21
+ last_updated: Date;
22
+ tool_output_tokens: number;
23
+ }
24
+ declare class SessionStore {
25
+ /**
26
+ * Get or create session data
27
+ */
28
+ getSession(sessionId: string): SessionData;
29
+ /**
30
+ * Update session with tool output tokens
31
+ */
32
+ updateSession(sessionId: string, outputTokens: number): SessionData;
33
+ /**
34
+ * Clear session (for cleanup)
35
+ */
36
+ clearSession(sessionId: string): void;
37
+ /**
38
+ * Get all sessions (for debugging)
39
+ */
40
+ getAllSessions(): SessionData[];
41
+ }
42
+ declare const sessionStore: SessionStore;
43
+ declare class EvictionTriggerEngine {
44
+ /**
45
+ * Check if eviction should be triggered based on fill percentage
46
+ */
47
+ checkEvictionTrigger(session: SessionData): EvictionTrigger;
48
+ /**
49
+ * Run standard eviction cycle
50
+ * NOTE: This is a placeholder - actual implementation would call into
51
+ * the context rotation/eviction system when it's built
52
+ */
53
+ runStandardEviction(session: SessionData): Promise<void>;
54
+ /**
55
+ * Run emergency eviction cycle
56
+ * More aggressive eviction for critical situations
57
+ */
58
+ runEmergencyEviction(session: SessionData): Promise<void>;
59
+ }
60
+ declare const evictionEngine: EvictionTriggerEngine;
61
+ /**
62
+ * Handle PostToolUse hook event
63
+ * Updates session state with tool output and triggers eviction if needed
64
+ */
65
+ export declare function handlePostToolUseHook(input: PostToolUseInput): Promise<void>;
66
+ export { sessionStore, evictionEngine };
67
+ export type { PostToolUseInput, SessionData, EvictionTrigger };