@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.
- package/dist/cli.js +9 -1
- package/dist/codedna/__tests__/examples/mongoose-example.d.ts +6 -0
- package/dist/codedna/__tests__/examples/mongoose-example.js +163 -0
- package/dist/codedna/__tests__/fixtures/typeorm-production-test.d.ts +1 -0
- package/dist/codedna/__tests__/fixtures/typeorm-production-test.js +231 -0
- package/dist/codedna/__tests__/fixtures/typeorm-test.d.ts +1 -0
- package/dist/codedna/__tests__/fixtures/typeorm-test.js +124 -0
- package/dist/codedna/__tests__/laravel-output-review.d.ts +1 -0
- package/dist/codedna/__tests__/laravel-output-review.js +249 -0
- package/dist/codedna/__tests__/mongoose-output-test.d.ts +1 -0
- package/dist/codedna/__tests__/mongoose-output-test.js +178 -0
- package/dist/codedna/examples/radix-example.d.ts +2 -0
- package/dist/codedna/examples/radix-example.js +259 -0
- package/dist/codedna/index.d.ts +5 -3
- package/dist/codedna/index.js +6 -3
- package/dist/codedna/kappa-ast.d.ts +143 -5
- package/dist/codedna/kappa-drizzle-generator.js +8 -5
- package/dist/codedna/kappa-gofiber-generator.d.ts +65 -0
- package/dist/codedna/kappa-gofiber-generator.js +587 -0
- package/dist/codedna/kappa-laravel-generator.d.ts +68 -0
- package/dist/codedna/kappa-laravel-generator.js +741 -0
- package/dist/codedna/kappa-lexer.d.ts +44 -0
- package/dist/codedna/kappa-lexer.js +124 -0
- package/dist/codedna/kappa-mantine-generator.d.ts +65 -0
- package/dist/codedna/kappa-mantine-generator.js +518 -0
- package/dist/codedna/kappa-mongoose-generator.d.ts +44 -0
- package/dist/codedna/kappa-mongoose-generator.js +442 -0
- package/dist/codedna/kappa-parser.d.ts +43 -1
- package/dist/codedna/kappa-parser.js +601 -0
- package/dist/codedna/kappa-radix-generator.d.ts +61 -0
- package/dist/codedna/kappa-radix-generator.js +566 -0
- package/dist/codedna/kappa-typeorm-generator.d.ts +59 -0
- package/dist/codedna/kappa-typeorm-generator.js +723 -0
- package/dist/codedna/kappa-vitest-generator.d.ts +85 -0
- package/dist/codedna/kappa-vitest-generator.js +739 -0
- package/dist/codedna/parser.js +26 -1
- package/dist/codegen/cloud-client.d.ts +160 -0
- package/dist/codegen/cloud-client.js +195 -0
- package/dist/codegen/codegen-tool.d.ts +35 -0
- package/dist/codegen/codegen-tool.js +312 -0
- package/dist/codegen/field-inference.d.ts +24 -0
- package/dist/codegen/field-inference.js +101 -0
- package/dist/codegen/form-parser.d.ts +13 -0
- package/dist/codegen/form-parser.js +186 -0
- package/dist/codegen/index.d.ts +2 -0
- package/dist/codegen/index.js +4 -0
- package/dist/codegen/natural-parser.d.ts +50 -0
- package/dist/codegen/natural-parser.js +769 -0
- package/dist/handlers/codedna-handlers.d.ts +1 -1
- package/dist/handlers/codegen-handlers.d.ts +20 -0
- package/dist/handlers/codegen-handlers.js +60 -0
- package/dist/handlers/kappa-handlers.d.ts +97 -0
- package/dist/handlers/kappa-handlers.js +408 -0
- package/dist/handlers/tool-handlers.js +124 -221
- package/dist/helpers/api-client.js +48 -3
- package/dist/helpers/compact-formatter.d.ts +9 -2
- package/dist/helpers/compact-formatter.js +26 -2
- package/dist/helpers/config.d.ts +7 -2
- package/dist/helpers/config.js +25 -10
- package/dist/helpers/session-validation.d.ts +1 -1
- package/dist/helpers/session-validation.js +2 -4
- package/dist/helpers/tasks.d.ts +21 -0
- package/dist/helpers/tasks.js +52 -0
- package/dist/helpers/workers.d.ts +1 -1
- package/dist/helpers/workers.js +19 -19
- package/dist/setup.d.ts +1 -0
- package/dist/setup.js +228 -3
- package/dist/templates/claude-md.d.ts +1 -1
- package/dist/templates/claude-md.js +37 -152
- package/dist/templates/orchestrator-prompt.d.ts +2 -2
- package/dist/templates/orchestrator-prompt.js +31 -38
- package/dist/templates/self-critique.d.ts +50 -0
- package/dist/templates/self-critique.js +209 -0
- package/dist/templates/worker-prompt.d.ts +3 -3
- package/dist/templates/worker-prompt.js +18 -18
- package/dist/tools.js +77 -413
- package/docs/codedna/generator-testing-summary.md +205 -0
- package/docs/codedna/radix-ui-generator.md +478 -0
- package/docs/kappa-gofiber-generator.md +274 -0
- package/docs/kappa-laravel-fixes.md +172 -0
- package/docs/kappa-mongoose-generator.md +322 -0
- package/docs/kappa-vitest-generator.md +337 -0
- package/package.json +1 -1
- package/dist/context/deduplication.test.d.ts +0 -6
- 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 {
|
|
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
|
|
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
|
|
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
|
-
`##
|
|
804
|
-
`If this task involves CRUD/API operations, use
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
1898
|
+
// CODEGEN TOOL (Cloud-Only)
|
|
1887
1899
|
// =========================================================================
|
|
1888
|
-
case '
|
|
1889
|
-
const
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
if (
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
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
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
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
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
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 '
|
|
2077
|
-
const
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
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
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|
package/dist/helpers/config.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
38
|
-
*
|
|
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
|
/**
|
package/dist/helpers/config.js
CHANGED
|
@@ -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
|
-
*
|
|
171
|
-
*
|
|
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
|
-
//
|
|
175
|
-
// This
|
|
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
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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
|
-
'
|
|
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);
|