@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
@@ -7,9 +7,11 @@ import { getDefaultProjectId, DEFAULT_USER_ID, lastContextUsed, setLastContextUs
7
7
  import { EXPERT_WORKERS, matchTaskToWorker, buildWorkerPrompt } from '../helpers/workers.js';
8
8
  import { validateTaskDescription } from '../templates/orchestrator-prompt.js';
9
9
  import { searchMemory, addMemory, storeFact, getContext, getSummary, getEntities, injectContext, apiRequest, listCachedDocs, getCachedDocs, cacheDocs, getMemoryIndex, getMemoryDetail, getDisclosureAnalytics } from '../helpers/api-client.js';
10
+ import { recordToolCall, getToolCallWarnings } from '../helpers/session-validation.js';
10
11
  import { queryDependencies, analyzeImpact } from '../helpers/dependencies.js';
11
12
  import { checkPatterns } from '../helpers/patterns.js';
12
13
  import { formatContextForClaude } from '../helpers/formatter.js';
14
+ import { compactTaskList, compactTaskCreated, compactTaskStart, compactTaskComplete, compactTaskClaim, compactTaskRelease, compactStatusUpdate, compactContextAdded, compactHeartbeat, compactSummary, } from '../helpers/compact-formatter.js';
13
15
  import { createTask, listTasks, getTask, claimTask, releaseTask, updateTaskStatus, addTaskContext, getTaskSummary, heartbeatTask, parseJsonArray, getDispatchableTasks, getExecutionContext, resolveTaskDependencies, getEpicStatus, getActiveTaskCount, } from '../helpers/tasks.js';
14
16
  import { detectTimedOutTasks, retryTask, failTask, autoRetryTimedOutTasks, } from '../helpers/tasks-retry.js';
15
17
  import { detectLibrariesFromPlan } from '../helpers/library-detection.js';
@@ -38,6 +40,10 @@ export function registerToolHandlers(server) {
38
40
  const timer = mcpLogger.startTimer();
39
41
  // Log tool call
40
42
  mcpLogger.toolCall(name, args || {});
43
+ // Record tool call in session for sequence validation
44
+ recordToolCall(name, args);
45
+ // Get workflow warnings (if any) for this tool call
46
+ const workflowWarnings = getToolCallWarnings(name, args);
41
47
  try {
42
48
  switch (name) {
43
49
  case 'memory_explain': {
@@ -126,17 +132,89 @@ export function registerToolHandlers(server) {
126
132
  const relationship = args?.relationship;
127
133
  const entity2 = args?.entity2;
128
134
  const context = args?.context;
129
- const result = await storeFact(projectId, entity1, relationship, entity2, context);
135
+ const is_critical = args?.is_critical;
136
+ // Commercial-grade storage with blocking verification
137
+ const MAX_RETRIES = 3;
138
+ const VERIFY_DELAY_MS = 200;
139
+ let lastError = null;
140
+ let storedFactId = null;
141
+ let storedIsCritical = false;
142
+ let verified = false;
143
+ let attempts = 0;
144
+ for (let attempt = 1; attempt <= MAX_RETRIES && !verified; attempt++) {
145
+ attempts = attempt;
146
+ try {
147
+ // Step 1: Store the fact
148
+ mcpLogger.info('STORE', `Attempt ${attempt}/${MAX_RETRIES}: Storing "${entity1} ${relationship} ${entity2}"${is_critical ? ' [CRITICAL]' : ''}`);
149
+ const result = await storeFact(projectId, entity1, relationship, entity2, context, { is_critical });
150
+ storedFactId = result.fact_id;
151
+ storedIsCritical = result.is_critical;
152
+ mcpLogger.info('STORE', `Storage response: ${JSON.stringify(result)}`);
153
+ if (!result.success || !result.fact_id) {
154
+ lastError = new Error(`Storage returned unsuccessful: ${JSON.stringify(result)}`);
155
+ mcpLogger.warn('STORE', `Attempt ${attempt} failed: ${lastError.message}`);
156
+ continue;
157
+ }
158
+ // Step 2: Wait briefly for eventual consistency
159
+ await new Promise(resolve => setTimeout(resolve, VERIFY_DELAY_MS));
160
+ // Step 3: Verify the fact is retrievable by searching for it
161
+ mcpLogger.info('STORE', `Verifying fact ${storedFactId} is retrievable...`);
162
+ const searchQuery = `${entity1} ${relationship} ${entity2}`;
163
+ const searchResult = await searchMemory(projectId, searchQuery, 5);
164
+ // Check if our fact appears in results
165
+ const factFound = searchResult.relevant_facts?.some(f => f.fact?.includes(entity1) && f.fact?.includes(entity2)) || false;
166
+ if (factFound) {
167
+ verified = true;
168
+ mcpLogger.info('STORE', `āœ“ Fact verified as retrievable`);
169
+ }
170
+ else {
171
+ lastError = new Error(`Fact stored but not found in search results`);
172
+ mcpLogger.warn('STORE', `Attempt ${attempt}: Stored but not retrievable. Search returned ${searchResult.relevant_facts?.length || 0} facts.`);
173
+ }
174
+ }
175
+ catch (err) {
176
+ lastError = err instanceof Error ? err : new Error(String(err));
177
+ mcpLogger.error('STORE', `Attempt ${attempt} error: ${lastError.message}`, err);
178
+ }
179
+ }
130
180
  mcpLogger.memoryStore(entity1, relationship, entity2);
131
- mcpLogger.toolResult(name, true, timer(), `ID: ${result.fact_id}`);
132
- return {
133
- content: [
134
- {
135
- type: 'text',
136
- text: `Stored fact: "${entity1} ${relationship} ${entity2}" (ID: ${result.fact_id})`,
137
- },
138
- ],
139
- };
181
+ const criticalTag = storedIsCritical ? ' šŸ”“ CRITICAL' : '';
182
+ if (verified && storedFactId) {
183
+ mcpLogger.toolResult(name, true, timer(), `ID: ${storedFactId} (verified)${criticalTag}`);
184
+ return {
185
+ content: [
186
+ {
187
+ type: 'text',
188
+ text: `āœ“ Stored and verified: "${entity1} ${relationship} ${entity2}" (ID: ${storedFactId})${criticalTag}`,
189
+ },
190
+ ],
191
+ };
192
+ }
193
+ else if (storedFactId) {
194
+ // Stored but couldn't verify - warn but don't fail
195
+ mcpLogger.toolResult(name, true, timer(), `ID: ${storedFactId} (unverified)${criticalTag}`);
196
+ return {
197
+ content: [
198
+ {
199
+ type: 'text',
200
+ text: `āš ļø Stored but verification pending: "${entity1} ${relationship} ${entity2}" (ID: ${storedFactId})${criticalTag}\nNote: Fact may take a moment to become searchable.`,
201
+ },
202
+ ],
203
+ };
204
+ }
205
+ else {
206
+ // Complete failure after all retries
207
+ mcpLogger.toolResult(name, false, timer(), `Failed after ${attempts} attempts`);
208
+ return {
209
+ content: [
210
+ {
211
+ type: 'text',
212
+ text: `āŒ Failed to store fact after ${attempts} attempts: "${entity1} ${relationship} ${entity2}"\nError: ${lastError?.message || 'Unknown error'}`,
213
+ },
214
+ ],
215
+ isError: true,
216
+ };
217
+ }
140
218
  }
141
219
  case 'memory_get_context': {
142
220
  const query = args?.query;
@@ -758,30 +836,16 @@ export function registerToolHandlers(server) {
758
836
  await updateTaskStatus(DEFAULT_USER_ID, projectId, taskId, 'in_progress', agentId);
759
837
  mcpLogger.toolResult(name, true, timer());
760
838
  const data = claimResult.data;
761
- let output = `# Starting: ${data.task.title}\n\n`;
762
- output += `**Task ID:** ${taskId}\n`;
763
- output += `**Status:** in_progress\n`;
764
- output += `**Lock expires:** ${data.lock_expires_at}\n\n`;
839
+ // Compact output: just essentials
840
+ let output = compactTaskStart(data.task, data.lock_expires_at);
765
841
  if (data.task.description) {
766
- output += `## Description\n${data.task.description}\n\n`;
842
+ output += `\n${data.task.description}`;
767
843
  }
844
+ // Only show criteria if present (compact)
768
845
  const criteria = parseJsonArray(data.task.acceptance_criteria);
769
846
  if (criteria.length) {
770
- output += `## Acceptance Criteria\n`;
771
- criteria.forEach((c, i) => {
772
- output += `${i + 1}. ${c}\n`;
773
- });
774
- output += '\n';
775
- }
776
- if (data.context?.length) {
777
- output += `## Previous Context\n`;
778
- data.context.forEach((c) => {
779
- output += `### ${c.context_type} (${c.added_by})\n`;
780
- output += `${c.content}\n\n`;
781
- });
847
+ output += `\nCriteria: ${criteria.join('; ')}`;
782
848
  }
783
- output += `---\n`;
784
- output += `Use \`task_complete\` when done, or \`task_add_context\` to log progress.\n`;
785
849
  return {
786
850
  content: [{ type: 'text', text: output }],
787
851
  };
@@ -798,25 +862,8 @@ export function registerToolHandlers(server) {
798
862
  // Check for newly unblocked tasks (orchestration awareness)
799
863
  const newlyUnblocked = await resolveTaskDependencies(DEFAULT_USER_ID, projectId, taskId, task.parent_id || undefined);
800
864
  mcpLogger.toolResult(name, true, timer());
801
- let output = `# Completed: ${task.title}\n\n`;
802
- output += `**Task ID:** \`${taskId}\`\n`;
803
- output += `**Status:** done āœ…\n\n`;
804
- output += `## Summary\n${summary}\n\n`;
805
- // Show newly unblocked tasks for orchestration
806
- if (newlyUnblocked.length > 0) {
807
- output += `---\n\n`;
808
- output += `## šŸ”“ Newly Unblocked Tasks (${newlyUnblocked.length})\n\n`;
809
- for (const unblocked of newlyUnblocked) {
810
- const worker = matchTaskToWorker({
811
- title: unblocked.title,
812
- description: unblocked.description || undefined,
813
- domain: unblocked.domain || undefined,
814
- });
815
- output += `- **${unblocked.title}** (\`${unblocked.id}\`)\n`;
816
- output += ` → Worker: ${worker.name}\n`;
817
- }
818
- output += `\n**Next:** Use \`task_dispatch\` to spawn parallel workers for unblocked tasks.\n`;
819
- }
865
+ // Compact output
866
+ const output = compactTaskComplete(task, summary, newlyUnblocked);
820
867
  return {
821
868
  content: [{ type: 'text', text: output }],
822
869
  };
@@ -843,24 +890,8 @@ export function registerToolHandlers(server) {
843
890
  blocked_by: blockedBy,
844
891
  });
845
892
  mcpLogger.toolResult(name, true, timer());
846
- const task = result.data;
847
- let output = `# Task Created\n\n`;
848
- output += `**ID:** ${task.id}\n`;
849
- output += `**Type:** ${task.type}\n`;
850
- output += `**Title:** ${task.title}\n`;
851
- output += `**Status:** ${task.status}\n`;
852
- output += `**Priority:** ${task.priority}\n`;
853
- if (task.parent_id)
854
- output += `**Parent:** ${task.parent_id}\n`;
855
- if (task.description)
856
- output += `\n**Description:**\n${task.description}\n`;
857
- const taskCriteria = parseJsonArray(task.acceptance_criteria);
858
- if (taskCriteria.length) {
859
- output += `\n**Acceptance Criteria:**\n`;
860
- taskCriteria.forEach((c, i) => {
861
- output += `${i + 1}. ${c}\n`;
862
- });
863
- }
893
+ // Compact output
894
+ const output = compactTaskCreated(result.data);
864
895
  return {
865
896
  content: [{ type: 'text', text: output }],
866
897
  };
@@ -875,45 +906,8 @@ export function registerToolHandlers(server) {
875
906
  };
876
907
  const result = await listTasks(DEFAULT_USER_ID, projectId, filters);
877
908
  mcpLogger.toolResult(name, true, timer());
878
- const tasks = result.data;
879
- let output = `# Tasks (${tasks.length})\n\n`;
880
- if (tasks.length === 0) {
881
- output += `No tasks found matching the filters.\n`;
882
- }
883
- else {
884
- // Group by type for better organization
885
- const epics = tasks.filter(t => t.type === 'epic');
886
- const regularTasks = tasks.filter(t => t.type === 'task');
887
- const subtasks = tasks.filter(t => t.type === 'subtask');
888
- const formatTask = (t) => {
889
- const statusEmoji = {
890
- backlog: 'šŸ“‹', ready: '🟢', in_progress: 'šŸ”„',
891
- blocked: '🚫', review: 'šŸ‘€', done: 'āœ…', cancelled: 'āŒ'
892
- };
893
- const emoji = statusEmoji[t.status] || 'šŸ“';
894
- let line = `- ${emoji} **${t.title}** (\`${t.id}\`)`;
895
- line += ` [${t.status}]`;
896
- if (t.priority !== 'medium')
897
- line += ` [${t.priority}]`;
898
- if (t.assigned_to)
899
- line += ` → ${t.assigned_to}`;
900
- return line;
901
- };
902
- if (epics.length > 0) {
903
- output += `## Epics\n`;
904
- epics.forEach(t => { output += formatTask(t) + '\n'; });
905
- output += '\n';
906
- }
907
- if (regularTasks.length > 0) {
908
- output += `## Tasks\n`;
909
- regularTasks.forEach(t => { output += formatTask(t) + '\n'; });
910
- output += '\n';
911
- }
912
- if (subtasks.length > 0) {
913
- output += `## Subtasks\n`;
914
- subtasks.forEach(t => { output += formatTask(t) + '\n'; });
915
- }
916
- }
909
+ // Compact output
910
+ const output = compactTaskList(result.data);
917
911
  return {
918
912
  content: [{ type: 'text', text: output }],
919
913
  };
@@ -987,17 +981,8 @@ export function registerToolHandlers(server) {
987
981
  const result = await claimTask(DEFAULT_USER_ID, projectId, taskId, agentId, lockDuration);
988
982
  mcpLogger.toolResult(name, true, timer());
989
983
  const data = result.data;
990
- let output = `# Task Claimed\n\n`;
991
- output += `**Task:** ${data.task.title}\n`;
992
- output += `**Claimed:** ${data.claimed ? 'āœ… Yes' : 'āŒ No'}\n`;
993
- output += `**Lock Expires:** ${data.lock_expires_at}\n`;
994
- output += `**Agent:** ${agentId}\n`;
995
- if (data.context?.length) {
996
- output += `\n## Previous Context\n`;
997
- data.context.forEach(c => {
998
- output += `### ${c.context_type}\n${c.content}\n\n`;
999
- });
1000
- }
984
+ // Compact output
985
+ const output = compactTaskClaim(data.task, data.claimed, data.lock_expires_at);
1001
986
  return {
1002
987
  content: [{ type: 'text', text: output }],
1003
988
  };
@@ -1009,13 +994,8 @@ export function registerToolHandlers(server) {
1009
994
  const workLog = args?.work_log;
1010
995
  const result = await releaseTask(DEFAULT_USER_ID, projectId, taskId, agentId, newStatus, workLog);
1011
996
  mcpLogger.toolResult(name, true, timer());
1012
- const data = result.data;
1013
- let output = `# Task Released\n\n`;
1014
- output += `**Task:** ${data.task.title}\n`;
1015
- output += `**Released:** ${data.released ? 'āœ… Yes' : 'āŒ No'}\n`;
1016
- output += `**New Status:** ${data.task.status}\n`;
1017
- if (workLog)
1018
- output += `**Work Log:** ${workLog}\n`;
997
+ // Compact output
998
+ const output = compactTaskRelease(result.data.task, result.data.released);
1019
999
  return {
1020
1000
  content: [{ type: 'text', text: output }],
1021
1001
  };
@@ -1026,11 +1006,8 @@ export function registerToolHandlers(server) {
1026
1006
  const agentId = args?.agent_id;
1027
1007
  const result = await updateTaskStatus(DEFAULT_USER_ID, projectId, taskId, status, agentId);
1028
1008
  mcpLogger.toolResult(name, true, timer());
1029
- const task = result.data;
1030
- let output = `# Status Updated\n\n`;
1031
- output += `**Task:** ${task.title}\n`;
1032
- output += `**New Status:** ${task.status}\n`;
1033
- output += `**Updated At:** ${task.updated_at}\n`;
1009
+ // Compact output
1010
+ const output = compactStatusUpdate(result.data);
1034
1011
  return {
1035
1012
  content: [{ type: 'text', text: output }],
1036
1013
  };
@@ -1043,14 +1020,8 @@ export function registerToolHandlers(server) {
1043
1020
  const source = args?.source;
1044
1021
  const result = await addTaskContext(DEFAULT_USER_ID, projectId, taskId, contextType, content, addedBy, source);
1045
1022
  mcpLogger.toolResult(name, true, timer());
1046
- const ctx = result.data;
1047
- let output = `# Context Added\n\n`;
1048
- output += `**ID:** ${ctx.id}\n`;
1049
- output += `**Type:** ${ctx.context_type}\n`;
1050
- output += `**Added By:** ${ctx.added_by}\n`;
1051
- if (ctx.source)
1052
- output += `**Source:** ${ctx.source}\n`;
1053
- output += `\n**Content:**\n${ctx.content}\n`;
1023
+ // Compact output
1024
+ const output = compactContextAdded(result.data);
1054
1025
  return {
1055
1026
  content: [{ type: 'text', text: output }],
1056
1027
  };
@@ -1058,29 +1029,9 @@ export function registerToolHandlers(server) {
1058
1029
  case 'task_summary': {
1059
1030
  const result = await getTaskSummary(DEFAULT_USER_ID, projectId);
1060
1031
  mcpLogger.toolResult(name, true, timer());
1032
+ // Compact output
1061
1033
  const data = result.data;
1062
- let output = `# Task Summary\n\n`;
1063
- output += `## By Status\n`;
1064
- const statusEmoji = {
1065
- backlog: 'šŸ“‹', ready: '🟢', in_progress: 'šŸ”„',
1066
- blocked: '🚫', review: 'šŸ‘€', done: 'āœ…', cancelled: 'āŒ'
1067
- };
1068
- Object.entries(data.by_status).forEach(([status, count]) => {
1069
- const emoji = statusEmoji[status] || 'šŸ“';
1070
- output += `- ${emoji} ${status}: **${count}**\n`;
1071
- });
1072
- output += `\n## By Type\n`;
1073
- Object.entries(data.by_type).forEach(([type, count]) => {
1074
- output += `- ${type}: **${count}**\n`;
1075
- });
1076
- output += `\n## Active Agents\n`;
1077
- output += `**${data.active_agents}** agent(s) currently working on tasks\n`;
1078
- if (data.recent_events?.length) {
1079
- output += `\n## Recent Activity\n`;
1080
- data.recent_events.slice(0, 5).forEach(e => {
1081
- output += `- ${e.event_type}: ${e.task_title} (${new Date(e.created_at).toLocaleString()})\n`;
1082
- });
1083
- }
1034
+ const output = compactSummary(data.by_status, data.by_type, data.active_agents);
1084
1035
  return {
1085
1036
  content: [{ type: 'text', text: output }],
1086
1037
  };
@@ -1091,10 +1042,8 @@ export function registerToolHandlers(server) {
1091
1042
  const extendMinutes = args?.extend_minutes || 30;
1092
1043
  const result = await heartbeatTask(DEFAULT_USER_ID, projectId, taskId, agentId, extendMinutes);
1093
1044
  mcpLogger.toolResult(name, true, timer());
1094
- const data = result.data;
1095
- let output = `# Heartbeat\n\n`;
1096
- output += `**Extended:** ${data.extended ? 'āœ… Yes' : 'āŒ No'}\n`;
1097
- output += `**New Expiry:** ${data.new_expires_at}\n`;
1045
+ // Compact output
1046
+ const output = compactHeartbeat(result.data.extended, result.data.new_expires_at);
1098
1047
  return {
1099
1048
  content: [{ type: 'text', text: output }],
1100
1049
  };
@@ -1808,30 +1757,42 @@ export function registerToolHandlers(server) {
1808
1757
  case 'codedna_generate_api': {
1809
1758
  const result = await handleGenerateApi(args);
1810
1759
  mcpLogger.toolResult(name, true, timer());
1760
+ let output = JSON.stringify(result, null, 2);
1761
+ if (workflowWarnings) {
1762
+ output += '\n\n' + workflowWarnings;
1763
+ }
1811
1764
  return {
1812
1765
  content: [{
1813
1766
  type: 'text',
1814
- text: JSON.stringify(result, null, 2),
1767
+ text: output,
1815
1768
  }],
1816
1769
  };
1817
1770
  }
1818
1771
  case 'codedna_generate_frontend': {
1819
1772
  const result = await handleGenerateFrontend(args);
1820
1773
  mcpLogger.toolResult(name, true, timer());
1774
+ let output = JSON.stringify(result, null, 2);
1775
+ if (workflowWarnings) {
1776
+ output += '\n\n' + workflowWarnings;
1777
+ }
1821
1778
  return {
1822
1779
  content: [{
1823
1780
  type: 'text',
1824
- text: JSON.stringify(result, null, 2),
1781
+ text: output,
1825
1782
  }],
1826
1783
  };
1827
1784
  }
1828
1785
  case 'codedna_generate_component': {
1829
1786
  const result = await handleGenerateComponent(args);
1830
1787
  mcpLogger.toolResult(name, true, timer());
1788
+ let output = JSON.stringify(result, null, 2);
1789
+ if (workflowWarnings) {
1790
+ output += '\n\n' + workflowWarnings;
1791
+ }
1831
1792
  return {
1832
1793
  content: [{
1833
1794
  type: 'text',
1834
- text: JSON.stringify(result, null, 2),
1795
+ text: output,
1835
1796
  }],
1836
1797
  };
1837
1798
  }
@@ -26,9 +26,13 @@ export declare function addMemory(projectId: string, sessionId: string, messages
26
26
  episode_ids: string[];
27
27
  }>;
28
28
  export declare function searchMemory(projectId: string, query: string, limit?: number, userId?: string): Promise<MemoryContext>;
29
- export declare function storeFact(projectId: string, entity1: string, relationship: string, entity2: string, context: string, userId?: string): Promise<{
29
+ export declare function storeFact(projectId: string, entity1: string, relationship: string, entity2: string, context: string, options?: {
30
+ userId?: string;
31
+ is_critical?: boolean;
32
+ }): Promise<{
30
33
  success: boolean;
31
34
  fact_id: string;
35
+ is_critical: boolean;
32
36
  }>;
33
37
  export declare function getContext(projectId: string, query?: string, userId?: string): Promise<MemoryContext>;
34
38
  export declare function getSummary(projectId: string, userId?: string): Promise<string>;
@@ -39,12 +39,14 @@ export async function searchMemory(projectId, query, limit = 10, userId = DEFAUL
39
39
  limit,
40
40
  });
41
41
  }
42
- export async function storeFact(projectId, entity1, relationship, entity2, context, userId = DEFAULT_USER_ID) {
42
+ export async function storeFact(projectId, entity1, relationship, entity2, context, options = {}) {
43
+ const userId = options.userId ?? DEFAULT_USER_ID;
43
44
  return apiRequest(`/api/v1/memory/${userId}/${projectId}/fact`, 'POST', {
44
45
  entity1,
45
46
  relationship,
46
47
  entity2,
47
48
  context,
49
+ ...(options.is_critical !== undefined && { is_critical: options.is_critical }),
48
50
  });
49
51
  }
50
52
  export async function getContext(projectId, query, userId = DEFAULT_USER_ID) {
@@ -0,0 +1,51 @@
1
+ import type { Task, TaskContext } from './tasks.js';
2
+ /** Truncate task ID to first 8 chars */
3
+ export declare function shortId(id: string): string;
4
+ /** Format task status with icon */
5
+ export declare function statusIcon(status: string): string;
6
+ /** Single-line task representation */
7
+ export declare function compactTask(t: Task): string;
8
+ /** Compact task list - one line per task */
9
+ export declare function compactTaskList(tasks: Task[]): string;
10
+ /** Compact task creation result */
11
+ export declare function compactTaskCreated(task: Task): string;
12
+ /** Compact task start result */
13
+ export declare function compactTaskStart(task: Task, lockExpires: string): string;
14
+ /** Compact task complete result */
15
+ export declare function compactTaskComplete(task: Task, summary: string, unblocked?: Task[]): string;
16
+ /** Compact task claim result */
17
+ export declare function compactTaskClaim(task: Task, claimed: boolean, expires: string): string;
18
+ /** Compact task release result */
19
+ export declare function compactTaskRelease(task: Task, released: boolean): string;
20
+ /** Compact status update */
21
+ export declare function compactStatusUpdate(task: Task): string;
22
+ /** Compact context added */
23
+ export declare function compactContextAdded(ctx: TaskContext): string;
24
+ /** Compact heartbeat */
25
+ export declare function compactHeartbeat(extended: boolean, expires: string): string;
26
+ /** Compact memory search result */
27
+ export declare function compactMemorySearch(facts: {
28
+ fact: string;
29
+ }[], entities: {
30
+ name: string;
31
+ }[]): string;
32
+ /** Compact memory store result */
33
+ export declare function compactFactStored(entity1: string, rel: string, entity2: string, factId: string): string;
34
+ /** Compact summary stats */
35
+ export declare function compactSummary(byStatus: Record<string, number>, byType: Record<string, number>, activeAgents: number): string;
36
+ /** Compact epic progress */
37
+ export declare function compactEpicStatus(title: string, progress: number, stats: Record<string, number>): string;
38
+ /** Compact dispatch list */
39
+ export declare function compactDispatch(items: {
40
+ task: Task;
41
+ worker: {
42
+ name: string;
43
+ };
44
+ }[]): string;
45
+ /** Compact memory index */
46
+ export declare function compactMemoryIndex(entries: {
47
+ id: string;
48
+ summary: string;
49
+ relevance: number;
50
+ is_critical?: boolean;
51
+ }[]): string;
@@ -0,0 +1,130 @@
1
+ // =============================================================================
2
+ // Compact Response Formatter
3
+ // =============================================================================
4
+ // Minimizes tool output to conserve context window.
5
+ // Every byte counts - prefer single-line responses where possible.
6
+ const STATUS_ICONS = {
7
+ backlog: 'šŸ“‹',
8
+ ready: '🟢',
9
+ in_progress: 'šŸ”„',
10
+ blocked: '🚫',
11
+ review: 'šŸ‘€',
12
+ done: 'āœ…',
13
+ cancelled: 'āŒ',
14
+ };
15
+ /** Truncate task ID to first 8 chars */
16
+ export function shortId(id) {
17
+ return id.slice(0, 8);
18
+ }
19
+ /** Format task status with icon */
20
+ export function statusIcon(status) {
21
+ return STATUS_ICONS[status] || 'šŸ“';
22
+ }
23
+ /** Single-line task representation */
24
+ export function compactTask(t) {
25
+ return `${statusIcon(t.status)} ${t.title} (${shortId(t.id)}) [${t.status}]`;
26
+ }
27
+ /** Compact task list - one line per task */
28
+ export function compactTaskList(tasks) {
29
+ if (tasks.length === 0)
30
+ return 'No tasks found.';
31
+ return tasks.map(t => compactTask(t)).join('\n');
32
+ }
33
+ /** Compact task creation result */
34
+ export function compactTaskCreated(task) {
35
+ return `Created: ${task.type} "${task.title}" → ${shortId(task.id)}`;
36
+ }
37
+ /** Compact task start result */
38
+ export function compactTaskStart(task, lockExpires) {
39
+ return `Started: ${task.title} (${shortId(task.id)}) lock until ${new Date(lockExpires).toLocaleTimeString()}`;
40
+ }
41
+ /** Compact task complete result */
42
+ export function compactTaskComplete(task, summary, unblocked) {
43
+ let result = `āœ… ${task.title} → done`;
44
+ if (unblocked && unblocked.length > 0) {
45
+ result += `\nšŸ”“ Unblocked: ${unblocked.map(t => t.title).join(', ')}`;
46
+ }
47
+ return result;
48
+ }
49
+ /** Compact task claim result */
50
+ export function compactTaskClaim(task, claimed, expires) {
51
+ if (!claimed)
52
+ return `āŒ Failed to claim: ${task.title}`;
53
+ return `Claimed: ${task.title} (${shortId(task.id)}) until ${new Date(expires).toLocaleTimeString()}`;
54
+ }
55
+ /** Compact task release result */
56
+ export function compactTaskRelease(task, released) {
57
+ if (!released)
58
+ return `āŒ Failed to release: ${task.title}`;
59
+ return `Released: ${task.title} → ${task.status}`;
60
+ }
61
+ /** Compact status update */
62
+ export function compactStatusUpdate(task) {
63
+ return `${statusIcon(task.status)} ${task.title} → ${task.status}`;
64
+ }
65
+ /** Compact context added */
66
+ export function compactContextAdded(ctx) {
67
+ return `+ ${ctx.context_type} context added (${shortId(ctx.id)})`;
68
+ }
69
+ /** Compact heartbeat */
70
+ export function compactHeartbeat(extended, expires) {
71
+ if (!extended)
72
+ return 'āŒ Heartbeat failed';
73
+ return `♄ Extended until ${new Date(expires).toLocaleTimeString()}`;
74
+ }
75
+ /** Compact memory search result */
76
+ export function compactMemorySearch(facts, entities) {
77
+ if (facts.length === 0 && entities.length === 0)
78
+ return 'No results.';
79
+ const parts = [];
80
+ if (entities.length > 0) {
81
+ parts.push(`Entities: ${entities.map(e => e.name).join(', ')}`);
82
+ }
83
+ if (facts.length > 0) {
84
+ parts.push('Facts:');
85
+ facts.forEach(f => parts.push(`• ${f.fact}`));
86
+ }
87
+ return parts.join('\n');
88
+ }
89
+ /** Compact memory store result */
90
+ export function compactFactStored(entity1, rel, entity2, factId) {
91
+ return `Stored: ${entity1} ${rel} ${entity2} (${shortId(factId)})`;
92
+ }
93
+ /** Compact summary stats */
94
+ export function compactSummary(byStatus, byType, activeAgents) {
95
+ const statusParts = Object.entries(byStatus)
96
+ .filter(([, count]) => count > 0)
97
+ .map(([s, c]) => `${statusIcon(s)}${c}`)
98
+ .join(' ');
99
+ const typeParts = Object.entries(byType)
100
+ .filter(([, count]) => count > 0)
101
+ .map(([t, c]) => `${t}:${c}`)
102
+ .join(' ');
103
+ return `Tasks: ${statusParts}\nTypes: ${typeParts}\nAgents: ${activeAgents}`;
104
+ }
105
+ /** Compact epic progress */
106
+ export function compactEpicStatus(title, progress, stats) {
107
+ const pct = Math.round(progress * 100);
108
+ const bar = 'ā–ˆ'.repeat(Math.round(pct / 10)) + 'ā–‘'.repeat(10 - Math.round(pct / 10));
109
+ const statusLine = Object.entries(stats)
110
+ .filter(([, c]) => c > 0)
111
+ .map(([s, c]) => `${statusIcon(s)}${c}`)
112
+ .join(' ');
113
+ return `${title}: [${bar}] ${pct}%\n${statusLine}`;
114
+ }
115
+ /** Compact dispatch list */
116
+ export function compactDispatch(items) {
117
+ if (items.length === 0)
118
+ return 'No tasks ready for dispatch.';
119
+ return `Ready (${items.length}):\n` + items.map(i => `• ${i.task.title} → ${i.worker.name}`).join('\n');
120
+ }
121
+ /** Compact memory index */
122
+ export function compactMemoryIndex(entries) {
123
+ if (entries.length === 0)
124
+ return 'No memories found.';
125
+ return entries.map((e, i) => {
126
+ const critical = e.is_critical ? 'šŸ”“' : '';
127
+ const rel = Math.round(e.relevance * 100);
128
+ return `${i + 1}. ${critical}${e.summary.slice(0, 60)} (${rel}%) [${shortId(e.id)}]`;
129
+ }).join('\n');
130
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Track an engagement event via the API
3
+ * This persists the event to D1 for analytics
4
+ */
5
+ export declare function trackEngagementEvent(sessionId: string, projectId: string, eventType: 'search' | 'detail' | 'inject' | 'store_fact' | 'context_referenced'): Promise<void>;
6
+ export declare function getSessionId(): string;
7
+ /**
8
+ * Reset session ID (for testing or explicit session boundaries)
9
+ */
10
+ export declare function resetSessionId(): void;