@claudetools/tools 0.9.0 → 0.9.2

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 (85) hide show
  1. package/dist/cli.js +9 -1
  2. package/dist/codedna/__tests__/examples/mongoose-example.d.ts +6 -0
  3. package/dist/codedna/__tests__/examples/mongoose-example.js +163 -0
  4. package/dist/codedna/__tests__/fixtures/typeorm-production-test.d.ts +1 -0
  5. package/dist/codedna/__tests__/fixtures/typeorm-production-test.js +231 -0
  6. package/dist/codedna/__tests__/fixtures/typeorm-test.d.ts +1 -0
  7. package/dist/codedna/__tests__/fixtures/typeorm-test.js +124 -0
  8. package/dist/codedna/__tests__/laravel-output-review.d.ts +1 -0
  9. package/dist/codedna/__tests__/laravel-output-review.js +249 -0
  10. package/dist/codedna/__tests__/mongoose-output-test.d.ts +1 -0
  11. package/dist/codedna/__tests__/mongoose-output-test.js +178 -0
  12. package/dist/codedna/examples/radix-example.d.ts +2 -0
  13. package/dist/codedna/examples/radix-example.js +259 -0
  14. package/dist/codedna/index.d.ts +5 -3
  15. package/dist/codedna/index.js +6 -3
  16. package/dist/codedna/kappa-ast.d.ts +143 -5
  17. package/dist/codedna/kappa-drizzle-generator.js +8 -5
  18. package/dist/codedna/kappa-gofiber-generator.d.ts +65 -0
  19. package/dist/codedna/kappa-gofiber-generator.js +587 -0
  20. package/dist/codedna/kappa-laravel-generator.d.ts +68 -0
  21. package/dist/codedna/kappa-laravel-generator.js +741 -0
  22. package/dist/codedna/kappa-lexer.d.ts +44 -0
  23. package/dist/codedna/kappa-lexer.js +124 -0
  24. package/dist/codedna/kappa-mantine-generator.d.ts +65 -0
  25. package/dist/codedna/kappa-mantine-generator.js +518 -0
  26. package/dist/codedna/kappa-mongoose-generator.d.ts +44 -0
  27. package/dist/codedna/kappa-mongoose-generator.js +442 -0
  28. package/dist/codedna/kappa-parser.d.ts +43 -1
  29. package/dist/codedna/kappa-parser.js +601 -0
  30. package/dist/codedna/kappa-radix-generator.d.ts +61 -0
  31. package/dist/codedna/kappa-radix-generator.js +566 -0
  32. package/dist/codedna/kappa-typeorm-generator.d.ts +59 -0
  33. package/dist/codedna/kappa-typeorm-generator.js +723 -0
  34. package/dist/codedna/kappa-vitest-generator.d.ts +85 -0
  35. package/dist/codedna/kappa-vitest-generator.js +739 -0
  36. package/dist/codedna/parser.js +26 -1
  37. package/dist/codegen/cloud-client.d.ts +160 -0
  38. package/dist/codegen/cloud-client.js +195 -0
  39. package/dist/codegen/codegen-tool.d.ts +35 -0
  40. package/dist/codegen/codegen-tool.js +312 -0
  41. package/dist/codegen/field-inference.d.ts +24 -0
  42. package/dist/codegen/field-inference.js +101 -0
  43. package/dist/codegen/form-parser.d.ts +13 -0
  44. package/dist/codegen/form-parser.js +186 -0
  45. package/dist/codegen/index.d.ts +2 -0
  46. package/dist/codegen/index.js +4 -0
  47. package/dist/codegen/natural-parser.d.ts +50 -0
  48. package/dist/codegen/natural-parser.js +769 -0
  49. package/dist/handlers/codedna-handlers.d.ts +1 -1
  50. package/dist/handlers/codegen-handlers.d.ts +20 -0
  51. package/dist/handlers/codegen-handlers.js +60 -0
  52. package/dist/handlers/kappa-handlers.d.ts +97 -0
  53. package/dist/handlers/kappa-handlers.js +408 -0
  54. package/dist/handlers/tool-handlers.js +124 -221
  55. package/dist/helpers/api-client.js +48 -3
  56. package/dist/helpers/compact-formatter.d.ts +9 -2
  57. package/dist/helpers/compact-formatter.js +26 -2
  58. package/dist/helpers/config.d.ts +7 -2
  59. package/dist/helpers/config.js +25 -10
  60. package/dist/helpers/session-validation.d.ts +1 -1
  61. package/dist/helpers/session-validation.js +2 -4
  62. package/dist/helpers/tasks.d.ts +21 -0
  63. package/dist/helpers/tasks.js +52 -0
  64. package/dist/helpers/workers.d.ts +1 -1
  65. package/dist/helpers/workers.js +19 -19
  66. package/dist/setup.d.ts +1 -0
  67. package/dist/setup.js +228 -3
  68. package/dist/templates/claude-md.d.ts +1 -1
  69. package/dist/templates/claude-md.js +37 -152
  70. package/dist/templates/orchestrator-prompt.d.ts +2 -2
  71. package/dist/templates/orchestrator-prompt.js +31 -38
  72. package/dist/templates/self-critique.d.ts +50 -0
  73. package/dist/templates/self-critique.js +209 -0
  74. package/dist/templates/worker-prompt.d.ts +3 -3
  75. package/dist/templates/worker-prompt.js +18 -18
  76. package/dist/tools.js +77 -413
  77. package/docs/codedna/generator-testing-summary.md +205 -0
  78. package/docs/codedna/radix-ui-generator.md +478 -0
  79. package/docs/kappa-gofiber-generator.md +274 -0
  80. package/docs/kappa-laravel-fixes.md +172 -0
  81. package/docs/kappa-mongoose-generator.md +322 -0
  82. package/docs/kappa-vitest-generator.md +337 -0
  83. package/package.json +1 -1
  84. package/dist/context/deduplication.test.d.ts +0 -6
  85. package/dist/context/deduplication.test.js +0 -84
@@ -12,11 +12,10 @@ import { queryDependencies, analyzeImpact } from '../helpers/dependencies.js';
12
12
  import { checkPatterns } from '../helpers/patterns.js';
13
13
  import { formatContextForClaude } from '../helpers/formatter.js';
14
14
  import { shortId, compactTaskList, compactTaskCreated, compactTaskStart, compactTaskComplete, compactTaskClaim, compactTaskRelease, compactStatusUpdate, compactContextAdded, compactHeartbeat, compactTaskHandoff, compactSummary, } from '../helpers/compact-formatter.js';
15
- import { createTask, listTasks, getTask, claimTask, releaseTask, updateTaskStatus, addTaskContext, getTaskSummary, heartbeatTask, handoffTask, parseJsonArray, getDispatchableTasks, getExecutionContext, resolveTaskDependencies, getEpicStatus, getEpicAggregate, getActiveTaskCount, reviseEpic, } from '../helpers/tasks.js';
15
+ import { createTask, listTasks, getTask, claimTask, releaseTask, updateTaskStatus, addTaskContext, getTaskSummary, heartbeatTask, handoffTask, parseJsonArray, getDispatchableTasks, getExecutionContext, resolveTaskDependencies, getEpicStatus, getEpicAggregate, getActiveTaskCount, reviseEpic, checkWorkflowCompliance, } from '../helpers/tasks.js';
16
16
  import { detectTimedOutTasks, retryTask, failTask, autoRetryTimedOutTasks, } from '../helpers/tasks-retry.js';
17
17
  import { detectLibrariesFromPlan } from '../helpers/library-detection.js';
18
- import { handleGenerateApi, handleGenerateFrontend, handleGenerateComponent, handleListGenerators, handleValidateSpec, handleListPatterns, handleGetPattern, handleDetectPatterns, handleInitProject, } from './codedna-handlers.js';
19
- import { handleKappaParse, handleKappaGenerateSchema, handleKappaGenerateApi, handleKappaGeneratePages, handleKappaGenerateForms, handleKappaGenerateComponents, handleKappaGenerateDesign, handleKappaGenerateAll, } from './kappa-handlers.js';
18
+ import { cloudCodegen, CloudCodegenClient } from '../codegen/cloud-client.js';
20
19
  export function registerToolHandlers(server) {
21
20
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
22
21
  const { name, arguments: args } = request.params;
@@ -769,7 +768,7 @@ export function registerToolHandlers(server) {
769
768
  }
770
769
  }
771
770
  output += `\n**Tip:** For entities, use DSL format: "User(email:string:unique, password:string:hashed)"\n`;
772
- output += `Workers will use CodeDNA to generate code automatically.\n`;
771
+ output += `Workers will use codegen to generate code automatically.\n`;
773
772
  }
774
773
  // Auto-dispatch section with exact Task tool invocations
775
774
  output += `\n---\n\n`;
@@ -777,7 +776,7 @@ export function registerToolHandlers(server) {
777
776
  output += `**IMPORTANT:** Execute these Task tool calls in a SINGLE message to spawn parallel workers:\n\n`;
778
777
  output += `\`\`\`\n`;
779
778
  for (const dt of dispatchable) {
780
- // Build a comprehensive prompt using 10/10 template with CodeDNA instructions
779
+ // Build a comprehensive prompt using 10/10 template with codegen instructions
781
780
  const workerPrompt = buildWorkerPrompt({
782
781
  task: {
783
782
  id: dt.task.id,
@@ -800,8 +799,8 @@ export function registerToolHandlers(server) {
800
799
  ``,
801
800
  dt.task.description || '',
802
801
  ``,
803
- `## CodeDNA Priority`,
804
- `If this task involves CRUD/API operations, use codedna_generate_api FIRST.`,
802
+ `## Codegen Priority`,
803
+ `If this task involves CRUD/API operations, use codegen FIRST.`,
805
804
  `Look for Entity DSL in description (e.g., "User(email:string:unique)").`,
806
805
  ``,
807
806
  `## Protocol`,
@@ -913,6 +912,9 @@ export function registerToolHandlers(server) {
913
912
  const taskId = args?.task_id;
914
913
  const summary = args?.summary;
915
914
  const agentId = args?.agent_id || 'claude-code';
915
+ const verification = args?.verification;
916
+ // Check workflow compliance BEFORE claiming (to detect if task_start was skipped)
917
+ const workflowViolations = await checkWorkflowCompliance(DEFAULT_USER_ID, projectId, agentId, { isCompletingTask: true, taskId });
916
918
  // Try to claim first in case task wasn't started via task_start
917
919
  // This makes task_complete work standalone without requiring task_start
918
920
  try {
@@ -922,16 +924,26 @@ export function registerToolHandlers(server) {
922
924
  // Ignore claim errors - task might already be claimed by this agent or another
923
925
  // releaseTask will handle the actual authorization check
924
926
  }
925
- // Add completion context
926
- await addTaskContext(DEFAULT_USER_ID, projectId, taskId, 'work_log', summary, agentId);
927
+ // Add completion context (include verification if provided)
928
+ const contextSummary = verification
929
+ ? `${summary}\n\nVerification: ${JSON.stringify(verification)}`
930
+ : summary;
931
+ await addTaskContext(DEFAULT_USER_ID, projectId, taskId, 'work_log', contextSummary, agentId);
927
932
  // Release and mark as done
928
933
  const releaseResult = await releaseTask(DEFAULT_USER_ID, projectId, taskId, agentId, 'done', summary);
929
934
  const task = releaseResult.data.task;
930
935
  // Check for newly unblocked tasks (orchestration awareness)
931
936
  const newlyUnblocked = await resolveTaskDependencies(DEFAULT_USER_ID, projectId, taskId, task.parent_id || undefined);
932
937
  mcpLogger.toolResult(name, true, timer());
933
- // Compact output
934
- const output = compactTaskComplete(task, summary, newlyUnblocked);
938
+ // Compact output with verification status
939
+ let output = compactTaskComplete(task, summary, newlyUnblocked, verification);
940
+ // Append workflow warnings if any violations detected
941
+ if (workflowViolations.length > 0) {
942
+ output += '\n\n📋 **Workflow Notes:**';
943
+ for (const v of workflowViolations) {
944
+ output += `\n${v.message}`;
945
+ }
946
+ }
935
947
  return {
936
948
  content: [{ type: 'text', text: output }],
937
949
  };
@@ -1883,223 +1895,114 @@ export function registerToolHandlers(server) {
1883
1895
  };
1884
1896
  }
1885
1897
  // =========================================================================
1886
- // CODEDNA CODE GENERATION HANDLERS
1898
+ // CODEGEN TOOL (Cloud-Only)
1887
1899
  // =========================================================================
1888
- case 'codedna_generate_api': {
1889
- const result = await handleGenerateApi(args);
1890
- mcpLogger.toolResult(name, true, timer());
1891
- let output = JSON.stringify(result, null, 2);
1892
- if (workflowWarnings) {
1893
- output += '\n\n' + workflowWarnings;
1894
- }
1895
- return {
1896
- content: [{
1897
- type: 'text',
1898
- text: output,
1899
- }],
1900
- };
1901
- }
1902
- case 'codedna_generate_frontend': {
1903
- const result = await handleGenerateFrontend(args);
1904
- mcpLogger.toolResult(name, true, timer());
1905
- let output = JSON.stringify(result, null, 2);
1906
- if (workflowWarnings) {
1907
- output += '\n\n' + workflowWarnings;
1908
- }
1909
- return {
1910
- content: [{
1911
- type: 'text',
1912
- text: output,
1913
- }],
1914
- };
1915
- }
1916
- case 'codedna_generate_component': {
1917
- const result = await handleGenerateComponent(args);
1918
- mcpLogger.toolResult(name, true, timer());
1919
- let output = JSON.stringify(result, null, 2);
1920
- if (workflowWarnings) {
1921
- output += '\n\n' + workflowWarnings;
1922
- }
1923
- return {
1924
- content: [{
1925
- type: 'text',
1926
- text: output,
1927
- }],
1928
- };
1929
- }
1930
- case 'codedna_list_generators': {
1931
- const result = await handleListGenerators(args);
1932
- mcpLogger.toolResult(name, true, timer());
1933
- return {
1934
- content: [{
1935
- type: 'text',
1936
- text: JSON.stringify(result, null, 2),
1937
- }],
1938
- };
1939
- }
1940
- case 'codedna_validate_spec': {
1941
- const result = await handleValidateSpec(args);
1942
- mcpLogger.toolResult(name, true, timer());
1943
- return {
1944
- content: [{
1945
- type: 'text',
1946
- text: JSON.stringify(result, null, 2),
1947
- }],
1948
- };
1949
- }
1950
- // =====================================================================
1951
- // CodeDNA Pattern Library Tools
1952
- // =====================================================================
1953
- case 'codedna_list_patterns': {
1954
- const result = await handleListPatterns(args);
1955
- mcpLogger.toolResult(name, true, timer());
1956
- return {
1957
- content: [{
1958
- type: 'text',
1959
- text: JSON.stringify(result, null, 2),
1960
- }],
1961
- };
1962
- }
1963
- case 'codedna_get_pattern': {
1964
- const result = await handleGetPattern(args);
1965
- mcpLogger.toolResult(name, true, timer());
1966
- return {
1967
- content: [{
1968
- type: 'text',
1969
- text: JSON.stringify(result, null, 2),
1970
- }],
1971
- };
1972
- }
1973
- case 'codedna_detect_patterns': {
1974
- const result = await handleDetectPatterns(args);
1975
- mcpLogger.toolResult(name, true, timer());
1976
- return {
1977
- content: [{
1978
- type: 'text',
1979
- text: JSON.stringify(result, null, 2),
1980
- }],
1981
- };
1982
- }
1983
- case 'codedna_init_project': {
1984
- const result = await handleInitProject(args);
1985
- mcpLogger.toolResult(name, true, timer());
1986
- return {
1987
- content: [{
1988
- type: 'text',
1989
- text: JSON.stringify(result, null, 2),
1990
- }],
1991
- };
1992
- }
1993
- // =====================================================================
1994
- // Kappa v2.5 DSL Tools
1995
- // =====================================================================
1996
- case 'kappa_parse': {
1997
- const result = await handleKappaParse(args);
1998
- mcpLogger.toolResult(name, true, timer());
1999
- return {
2000
- content: [{
2001
- type: 'text',
2002
- text: JSON.stringify(result, null, 2),
2003
- }],
2004
- };
2005
- }
2006
- case 'kappa_generate_schema': {
2007
- const result = await handleKappaGenerateSchema(args);
2008
- mcpLogger.toolResult(name, true, timer());
2009
- let output = JSON.stringify(result, null, 2);
2010
- if (workflowWarnings) {
2011
- output += '\n\n' + workflowWarnings;
2012
- }
2013
- return {
2014
- content: [{
2015
- type: 'text',
2016
- text: output,
2017
- }],
2018
- };
2019
- }
2020
- case 'kappa_generate_api': {
2021
- const result = await handleKappaGenerateApi(args);
2022
- mcpLogger.toolResult(name, true, timer());
2023
- let output = JSON.stringify(result, null, 2);
2024
- if (workflowWarnings) {
2025
- output += '\n\n' + workflowWarnings;
2026
- }
2027
- return {
2028
- content: [{
2029
- type: 'text',
2030
- text: output,
2031
- }],
2032
- };
2033
- }
2034
- case 'kappa_generate_pages': {
2035
- const result = await handleKappaGeneratePages(args);
2036
- mcpLogger.toolResult(name, true, timer());
2037
- let output = JSON.stringify(result, null, 2);
2038
- if (workflowWarnings) {
2039
- output += '\n\n' + workflowWarnings;
1900
+ case 'codegen': {
1901
+ const describe = args?.describe;
1902
+ const stack = args?.stack;
1903
+ const generate = args?.generate;
1904
+ if (!describe) {
1905
+ return {
1906
+ content: [{ type: 'text', text: '❌ Missing required parameter: describe' }],
1907
+ isError: true,
1908
+ };
2040
1909
  }
2041
- return {
2042
- content: [{
2043
- type: 'text',
2044
- text: output,
2045
- }],
2046
- };
2047
- }
2048
- case 'kappa_generate_forms': {
2049
- const result = await handleKappaGenerateForms(args);
2050
- mcpLogger.toolResult(name, true, timer());
2051
- let output = JSON.stringify(result, null, 2);
2052
- if (workflowWarnings) {
2053
- output += '\n\n' + workflowWarnings;
1910
+ try {
1911
+ const result = await cloudCodegen({
1912
+ describe,
1913
+ stack,
1914
+ generate,
1915
+ });
1916
+ mcpLogger.toolResult(name, result.success, timer(), result.summary);
1917
+ if (!result.success) {
1918
+ let errorOutput = `❌ Code generation failed\n\n`;
1919
+ errorOutput += `**Error:** ${result.summary}\n`;
1920
+ if (result.errors && result.errors.length > 0) {
1921
+ errorOutput += `\n**Details:**\n`;
1922
+ result.errors.forEach((e) => {
1923
+ errorOutput += `- ${e}\n`;
1924
+ });
1925
+ }
1926
+ errorOutput += `\n**Tip:** Describe entities naturally, e.g., "User with email, password, role (admin/user)"`;
1927
+ return {
1928
+ content: [{ type: 'text', text: errorOutput }],
1929
+ isError: true,
1930
+ };
1931
+ }
1932
+ // Format successful output
1933
+ let output = `# ✓ Generated ${result.files?.length || 0} files\n\n`;
1934
+ output += `**Entities:** ${result.entities?.join(', ')}\n`;
1935
+ if (result.generators && result.generators.length > 0) {
1936
+ output += `**Generators:** ${result.generators.map(g => g.name).join(', ')}\n`;
1937
+ }
1938
+ if (result.generationTimeMs) {
1939
+ output += `**Generation Time:** ${result.generationTimeMs}ms\n`;
1940
+ }
1941
+ output += `\n## Files\n\n`;
1942
+ for (const file of result.files || []) {
1943
+ output += `### ${file.path}\n`;
1944
+ output += `${file.description}\n\n`;
1945
+ const ext = file.path.split('.').pop() || 'typescript';
1946
+ output += `\`\`\`${ext}\n${file.content}\n\`\`\`\n\n`;
1947
+ }
1948
+ if (workflowWarnings) {
1949
+ output += '\n\n' + workflowWarnings;
1950
+ }
1951
+ return {
1952
+ content: [{ type: 'text', text: output }],
1953
+ };
2054
1954
  }
2055
- return {
2056
- content: [{
2057
- type: 'text',
2058
- text: output,
2059
- }],
2060
- };
2061
- }
2062
- case 'kappa_generate_components': {
2063
- const result = await handleKappaGenerateComponents(args);
2064
- mcpLogger.toolResult(name, true, timer());
2065
- let output = JSON.stringify(result, null, 2);
2066
- if (workflowWarnings) {
2067
- output += '\n\n' + workflowWarnings;
1955
+ catch (error) {
1956
+ mcpLogger.toolResult(name, false, timer(), String(error));
1957
+ return {
1958
+ content: [{ type: 'text', text: `❌ API error: ${error instanceof Error ? error.message : String(error)}` }],
1959
+ isError: true,
1960
+ };
2068
1961
  }
2069
- return {
2070
- content: [{
2071
- type: 'text',
2072
- text: output,
2073
- }],
2074
- };
2075
1962
  }
2076
- case 'kappa_generate_design': {
2077
- const result = await handleKappaGenerateDesign(args);
2078
- mcpLogger.toolResult(name, true, timer());
2079
- let output = JSON.stringify(result, null, 2);
2080
- if (workflowWarnings) {
2081
- output += '\n\n' + workflowWarnings;
1963
+ case 'codegen_list': {
1964
+ const category = args?.category;
1965
+ const framework = args?.framework;
1966
+ try {
1967
+ const client = new CloudCodegenClient();
1968
+ const generators = await client.listGenerators({ category, framework });
1969
+ mcpLogger.toolResult(name, true, timer(), `${generators.length} generators`);
1970
+ let output = `# Available Generators`;
1971
+ if (category)
1972
+ output += ` (${category})`;
1973
+ if (framework)
1974
+ output += ` [${framework}]`;
1975
+ output += `\n\n`;
1976
+ output += `Found **${generators.length}** published generators:\n\n`;
1977
+ // Group by category
1978
+ const byCategory = new Map();
1979
+ for (const gen of generators) {
1980
+ const cat = gen.categoryName || gen.category;
1981
+ if (!byCategory.has(cat)) {
1982
+ byCategory.set(cat, []);
1983
+ }
1984
+ byCategory.get(cat).push(gen);
1985
+ }
1986
+ for (const [cat, gens] of byCategory) {
1987
+ output += `## ${cat}\n\n`;
1988
+ output += `| Generator | Quality | Uses | Description |\n`;
1989
+ output += `|-----------|---------|------|-------------|\n`;
1990
+ for (const gen of gens) {
1991
+ output += `| ${gen.name} | ${gen.qualityScore}% | ${gen.usageCount} | ${gen.description.slice(0, 50)}${gen.description.length > 50 ? '...' : ''} |\n`;
1992
+ }
1993
+ output += `\n`;
1994
+ }
1995
+ return {
1996
+ content: [{ type: 'text', text: output }],
1997
+ };
2082
1998
  }
2083
- return {
2084
- content: [{
2085
- type: 'text',
2086
- text: output,
2087
- }],
2088
- };
2089
- }
2090
- case 'kappa_generate_all': {
2091
- const result = await handleKappaGenerateAll(args);
2092
- mcpLogger.toolResult(name, true, timer());
2093
- let output = JSON.stringify(result, null, 2);
2094
- if (workflowWarnings) {
2095
- output += '\n\n' + workflowWarnings;
1999
+ catch (error) {
2000
+ mcpLogger.toolResult(name, false, timer(), String(error));
2001
+ return {
2002
+ content: [{ type: 'text', text: `❌ Failed to list generators: ${error instanceof Error ? error.message : String(error)}` }],
2003
+ isError: true,
2004
+ };
2096
2005
  }
2097
- return {
2098
- content: [{
2099
- type: 'text',
2100
- text: output,
2101
- }],
2102
- };
2103
2006
  }
2104
2007
  default:
2105
2008
  throw new Error(`Unknown tool: ${name}`);
@@ -22,7 +22,21 @@ export async function apiRequest(endpoint, method = 'GET', body) {
22
22
  }
23
23
  const response = await fetch(url, options);
24
24
  if (!response.ok) {
25
- throw new Error(`API error: ${response.status} ${response.statusText}`);
25
+ // Try to extract error message from response body
26
+ let errorMessage = `${response.status} ${response.statusText}`;
27
+ try {
28
+ const errorBody = await response.json();
29
+ if (errorBody.error) {
30
+ errorMessage = errorBody.error;
31
+ }
32
+ else if (errorBody.message) {
33
+ errorMessage = errorBody.message;
34
+ }
35
+ }
36
+ catch {
37
+ // Response body wasn't JSON, use status text
38
+ }
39
+ throw new Error(`API error: ${errorMessage}`);
26
40
  }
27
41
  return response.json();
28
42
  }
@@ -54,11 +68,42 @@ export async function getContext(projectId, query, userId = DEFAULT_USER_ID) {
54
68
  return apiRequest(`/api/v1/memory/${userId}/${projectId}/context${params}`);
55
69
  }
56
70
  export async function getSummary(projectId, userId = DEFAULT_USER_ID) {
57
- const response = await fetch(`${API_BASE_URL}/api/v1/memory/${userId}/${projectId}/summary`);
71
+ // Use same auth pattern as apiRequest for consistency
72
+ const config = getConfig();
73
+ const apiKey = config.apiKey || process.env.CLAUDETOOLS_API_KEY || process.env.MEMORY_API_KEY;
74
+ if (!apiKey) {
75
+ throw new Error('No API key found. Set CLAUDETOOLS_API_KEY or MEMORY_API_KEY in environment or ~/.claudetools/config.json');
76
+ }
77
+ const url = `${API_BASE_URL}/api/v1/memory/${userId}/${projectId}/summary`;
78
+ const response = await fetch(url, {
79
+ method: 'GET',
80
+ headers: {
81
+ 'Content-Type': 'application/json',
82
+ 'Authorization': `Bearer ${apiKey}`,
83
+ },
84
+ });
85
+ if (!response.ok) {
86
+ let errorMessage = `${response.status} ${response.statusText}`;
87
+ try {
88
+ const errorBody = await response.json();
89
+ if (errorBody.error) {
90
+ errorMessage = errorBody.error;
91
+ }
92
+ else if (errorBody.message) {
93
+ errorMessage = errorBody.message;
94
+ }
95
+ }
96
+ catch {
97
+ // Response body wasn't JSON, use status text
98
+ }
99
+ throw new Error(`API error: ${errorMessage}`);
100
+ }
101
+ // Server returns plain text summary
58
102
  return response.text();
59
103
  }
60
104
  export async function getEntities(projectId, userId = DEFAULT_USER_ID) {
61
- return apiRequest(`/api/v1/memory/${userId}/${projectId}/entities`);
105
+ const response = await apiRequest(`/api/v1/memory/${userId}/${projectId}/entities`);
106
+ return response.data || [];
62
107
  }
63
108
  export async function injectContext(projectId, query, userId = DEFAULT_USER_ID) {
64
109
  const response = await apiRequest(`/api/v1/memory/${userId}/${projectId}/inject`, 'POST', {
@@ -1,4 +1,11 @@
1
1
  import type { Task, TaskContext } from './tasks.js';
2
+ /** Verification evidence for completed tasks */
3
+ export interface TaskVerification {
4
+ tests_passed?: boolean;
5
+ type_check_passed?: boolean;
6
+ manually_verified?: boolean;
7
+ evidence?: string;
8
+ }
2
9
  /** Truncate task ID to first 8 chars */
3
10
  export declare function shortId(id: string): string;
4
11
  /** Format task status with icon */
@@ -11,8 +18,8 @@ export declare function compactTaskList(tasks: Task[]): string;
11
18
  export declare function compactTaskCreated(task: Task): string;
12
19
  /** Compact task start result */
13
20
  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;
21
+ /** Compact task complete result with verification status */
22
+ export declare function compactTaskComplete(task: Task, summary: string, unblocked?: Task[], verification?: TaskVerification): string;
16
23
  /** Compact task claim result */
17
24
  export declare function compactTaskClaim(task: Task, claimed: boolean, expires: string): string;
18
25
  /** Compact task release result */
@@ -38,9 +38,33 @@ export function compactTaskCreated(task) {
38
38
  export function compactTaskStart(task, lockExpires) {
39
39
  return `Started: ${task.title} (${shortId(task.id)}) lock until ${new Date(lockExpires).toLocaleTimeString()}`;
40
40
  }
41
- /** Compact task complete result */
42
- export function compactTaskComplete(task, summary, unblocked) {
41
+ /** Compact task complete result with verification status */
42
+ export function compactTaskComplete(task, summary, unblocked, verification) {
43
43
  let result = `✅ ${task.title} → done`;
44
+ // Add verification status if provided
45
+ if (verification) {
46
+ const checks = [];
47
+ if (verification.tests_passed === true)
48
+ checks.push('✓tests');
49
+ else if (verification.tests_passed === false)
50
+ checks.push('✗tests');
51
+ if (verification.type_check_passed === true)
52
+ checks.push('✓types');
53
+ else if (verification.type_check_passed === false)
54
+ checks.push('✗types');
55
+ if (verification.manually_verified === true)
56
+ checks.push('✓manual');
57
+ if (checks.length > 0) {
58
+ result += ` [${checks.join(' ')}]`;
59
+ }
60
+ if (verification.evidence) {
61
+ result += `\n📋 ${verification.evidence}`;
62
+ }
63
+ }
64
+ else {
65
+ // Warn if no verification provided (encourages best practice)
66
+ result += ' [⚠️ no verification]';
67
+ }
44
68
  if (unblocked && unblocked.length > 0) {
45
69
  result += `\n🔓 Unblocked: ${unblocked.map(t => t.title).join(', ')}`;
46
70
  }
@@ -34,8 +34,13 @@ export declare function resolveProjectId(): string;
34
34
  export declare function resolveProjectIdAsync(): Promise<string>;
35
35
  /**
36
36
  * Gets the default project ID (synchronous version)
37
- * ALWAYS checks session-context.md first (even if cached), then config, then falls back to cwd resolution
38
- * This handles race conditions where MCP server starts before session-start hook writes the file
37
+ *
38
+ * CRITICAL: Uses "lock on first read" strategy to prevent multi-session conflicts:
39
+ * - First call: reads session-context.md (or config/cwd) and LOCKS the value
40
+ * - Subsequent calls: returns the locked value, ignoring session-context.md changes
41
+ *
42
+ * This ensures each MCP server process uses the project ID from when it started,
43
+ * even if other Claude Code sessions overwrite the shared session-context.md file.
39
44
  */
40
45
  export declare function getDefaultProjectId(): string;
41
46
  /**
@@ -164,26 +164,38 @@ export async function resolveProjectIdAsync() {
164
164
  ` 3. Or manually register via API`);
165
165
  }
166
166
  // Lazy-loaded DEFAULT_PROJECT_ID to avoid startup errors
167
+ // IMPORTANT: Once set, this is LOCKED for the lifetime of this MCP server process
168
+ // This prevents multi-session conflicts where different Claude Code sessions
169
+ // overwrite the shared session-context.md file
167
170
  let _defaultProjectId = null;
171
+ let _projectIdLocked = false; // Tracks if we've completed initial resolution
168
172
  /**
169
173
  * Gets the default project ID (synchronous version)
170
- * ALWAYS checks session-context.md first (even if cached), then config, then falls back to cwd resolution
171
- * This handles race conditions where MCP server starts before session-start hook writes the file
174
+ *
175
+ * CRITICAL: Uses "lock on first read" strategy to prevent multi-session conflicts:
176
+ * - First call: reads session-context.md (or config/cwd) and LOCKS the value
177
+ * - Subsequent calls: returns the locked value, ignoring session-context.md changes
178
+ *
179
+ * This ensures each MCP server process uses the project ID from when it started,
180
+ * even if other Claude Code sessions overwrite the shared session-context.md file.
172
181
  */
173
182
  export function getDefaultProjectId() {
174
- // ALWAYS check session-context.md first - it may have been written after we cached a fallback value
175
- // This handles the race condition where MCP server starts before Claude Code's session-start hook runs
183
+ // If already locked, return cached value immediately
184
+ // This prevents other sessions from hijacking this MCP server's project context
185
+ if (_projectIdLocked && _defaultProjectId) {
186
+ return _defaultProjectId;
187
+ }
188
+ // First-time resolution: check session-context.md
176
189
  const sessionProjectId = getProjectIdFromSessionContext();
177
190
  if (sessionProjectId) {
178
- // Update cache if different (session-context.md is authoritative)
179
- if (_defaultProjectId !== sessionProjectId) {
180
- mcpLogger.debug('MEMORY', `Updating project ID from session-context: ${sessionProjectId} (was: ${_defaultProjectId})`);
181
- _defaultProjectId = sessionProjectId;
182
- }
191
+ _defaultProjectId = sessionProjectId;
192
+ _projectIdLocked = true;
193
+ mcpLogger.info('MEMORY', `Project ID locked from session-context: ${sessionProjectId}`);
183
194
  return _defaultProjectId;
184
195
  }
185
- // If we have a cached value and no session-context.md, use cache
196
+ // If we have a cached value but not locked, lock it now
186
197
  if (_defaultProjectId) {
198
+ _projectIdLocked = true;
187
199
  return _defaultProjectId;
188
200
  }
189
201
  // Try config second
@@ -192,15 +204,18 @@ export function getDefaultProjectId() {
192
204
  // Legacy format - convert to local_ format instead of throwing
193
205
  const sanitized = config.defaultProjectId.toLowerCase().replace(/[^a-z0-9]/g, '_');
194
206
  _defaultProjectId = `local_${sanitized}`;
207
+ _projectIdLocked = true;
195
208
  console.error(`[claudetools] Warning: Legacy project ID "${config.defaultProjectId}" ` +
196
209
  `converted to "${_defaultProjectId}". Run "claudetools cleanup" to fix config.`);
197
210
  return _defaultProjectId;
198
211
  }
199
212
  _defaultProjectId = config.defaultProjectId;
213
+ _projectIdLocked = true;
200
214
  return _defaultProjectId;
201
215
  }
202
216
  // Fall back to resolveProjectId (throws if not resolved)
203
217
  _defaultProjectId = resolveProjectId();
218
+ _projectIdLocked = true;
204
219
  return _defaultProjectId;
205
220
  }
206
221
  /**
@@ -12,7 +12,7 @@ export declare function validateTaskStartSequence(taskId: string): {
12
12
  };
13
13
  /**
14
14
  * Validate that codebase_map was called before code modification tools
15
- * Code modification tools: codedna_generate_*, or any task involving file changes
15
+ * Code modification tools: codegen, or any task involving file changes
16
16
  */
17
17
  export declare function validateCodebaseMapSequence(): {
18
18
  valid: boolean;
@@ -91,7 +91,7 @@ export function validateTaskStartSequence(taskId) {
91
91
  }
92
92
  /**
93
93
  * Validate that codebase_map was called before code modification tools
94
- * Code modification tools: codedna_generate_*, or any task involving file changes
94
+ * Code modification tools: codegen, or any task involving file changes
95
95
  */
96
96
  export function validateCodebaseMapSequence() {
97
97
  const warnings = [];
@@ -108,9 +108,7 @@ export function validateCodebaseMapSequence() {
108
108
  */
109
109
  export function isCodeModificationTool(toolName) {
110
110
  const codeTools = [
111
- 'codedna_generate_api',
112
- 'codedna_generate_frontend',
113
- 'codedna_generate_component',
111
+ 'codegen',
114
112
  'task_start', // Tasks often involve code changes
115
113
  ];
116
114
  return codeTools.includes(toolName);