agent-enderun 0.1.10 → 0.2.0
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/.enderun/BRAIN_DASHBOARD.md +43 -0
- package/.enderun/ENDERUN.md +203 -0
- package/.enderun/PROJECT_MEMORY.md +137 -36
- package/.enderun/agents/analyst.md +21 -10
- package/.enderun/agents/backend.md +12 -11
- package/.enderun/agents/explorer.md +10 -7
- package/.enderun/agents/frontend.md +9 -20
- package/.enderun/agents/git.md +16 -12
- package/.enderun/agents/manager.md +14 -15
- package/.enderun/agents/mobile.md +5 -5
- package/.enderun/agents/native.md +5 -5
- package/.enderun/benchmarks/.gitkeep +0 -0
- package/.enderun/cli-commands.json +13 -1
- package/.enderun/config.json +1 -1
- package/.enderun/docs/api/README.md +10 -9
- package/.enderun/docs/api/auth.md +11 -0
- package/.enderun/docs/api/errors.md +7 -0
- package/.enderun/docs/error-handling.md +12 -0
- package/.enderun/docs/privacy.md +3 -0
- package/.enderun/docs/security.md +12 -0
- package/.enderun/docs/tech-stack.md +1 -0
- package/.enderun/docs/troubleshooting.md +7 -0
- package/.enderun/knowledge/api_design_rules.md +6 -0
- package/.enderun/knowledge/async_error_handling.md +18 -0
- package/.enderun/knowledge/branded_types_pattern.md +1 -0
- package/.enderun/knowledge/code_review_checklist.md +7 -0
- package/.enderun/knowledge/contract_versioning.md +7 -0
- package/.enderun/knowledge/database_migration.md +6 -0
- package/.enderun/knowledge/deployment_checklist.md +7 -0
- package/.enderun/knowledge/git_commit_strategy.md +10 -0
- package/.enderun/knowledge/legacy_onboarding.md +7 -0
- package/.enderun/knowledge/monitoring_setup.md +5 -0
- package/.enderun/knowledge/performance_guidelines.md +11 -0
- package/.enderun/knowledge/repository_patterns.md +9 -0
- package/.enderun/knowledge/security_scanning.md +6 -0
- package/.enderun/knowledge/testing_standards.md +7 -0
- package/.enderun/knowledge/troubleshooting_guide.md +5 -0
- package/.enderun/knowledge/zero_ui_library_policy.md +1 -0
- package/.enderun/logs/analyst.json +1 -0
- package/.enderun/logs/backend.json +1 -0
- package/.enderun/logs/explorer.json +1 -0
- package/.enderun/logs/frontend.json +1 -0
- package/.enderun/logs/git.json +1 -0
- package/.enderun/logs/manager.json +363 -0
- package/.enderun/logs/mobile.json +1 -0
- package/.enderun/logs/native.json +1 -0
- package/.enderun/monitoring/.gitkeep +0 -0
- package/ENDERUN.md +8 -8
- package/LICENSE +21 -0
- package/README.md +595 -195
- package/bin/cli.js +306 -79
- package/package.json +35 -2
- package/packages/framework-mcp/README.md +47 -81
- package/packages/framework-mcp/dist/index.js +13 -971
- package/packages/framework-mcp/dist/schemas.js +84 -0
- package/packages/framework-mcp/dist/tools/academy.js +184 -0
- package/packages/framework-mcp/dist/tools/codebase.js +294 -0
- package/packages/framework-mcp/dist/tools/contract.js +95 -0
- package/packages/framework-mcp/dist/tools/database.js +52 -0
- package/packages/framework-mcp/dist/tools/framework.js +161 -0
- package/packages/framework-mcp/dist/tools/git.js +53 -0
- package/packages/framework-mcp/dist/tools/index.js +42 -0
- package/packages/framework-mcp/dist/tools/knowledge.js +69 -0
- package/packages/framework-mcp/dist/tools/memory.js +94 -0
- package/packages/framework-mcp/dist/tools/messages.js +71 -0
- package/packages/framework-mcp/dist/tools/repository.js +76 -0
- package/packages/framework-mcp/dist/tools/security.js +122 -0
- package/packages/framework-mcp/dist/utils.js +82 -0
- package/packages/framework-mcp/package-lock.json +1836 -0
- package/packages/framework-mcp/package.json +1 -1
- package/packages/framework-mcp/src/index.ts +20 -970
- package/packages/framework-mcp/src/schemas.ts +106 -0
- package/packages/framework-mcp/src/tools/academy.ts +178 -0
- package/packages/framework-mcp/src/tools/codebase.ts +284 -0
- package/packages/framework-mcp/src/tools/contract.ts +91 -0
- package/packages/framework-mcp/src/tools/database.ts +49 -0
- package/packages/framework-mcp/src/tools/framework.ts +157 -0
- package/packages/framework-mcp/src/tools/git.ts +43 -0
- package/packages/framework-mcp/src/tools/index.ts +45 -0
- package/packages/framework-mcp/src/tools/knowledge.ts +68 -0
- package/packages/framework-mcp/src/tools/memory.ts +88 -0
- package/packages/framework-mcp/src/tools/messages.ts +70 -0
- package/packages/framework-mcp/src/tools/repository.ts +76 -0
- package/packages/framework-mcp/src/tools/security.ts +122 -0
- package/packages/framework-mcp/src/utils.ts +90 -0
- package/packages/framework-mcp/tests/mcp-server.test.ts +6 -0
- package/packages/shared-types/README.md +28 -51
- package/packages/shared-types/contract.version.json +4 -4
- package/packages/shared-types/dist/index.d.ts +80 -48
- package/packages/shared-types/dist/index.d.ts.map +1 -1
- package/packages/shared-types/dist/index.js +5 -8
- package/packages/shared-types/dist/index.js.map +1 -1
- package/packages/shared-types/package.json +1 -1
- package/packages/shared-types/src/index.ts +79 -51
- package/CHANGELOG.md +0 -97
- package/CLAUDE.md +0 -7
- package/CODEX.md +0 -7
- package/CURSOR.md +0 -7
- package/GEMINI.md +0 -7
- package/docs/tech-stack.md +0 -10
- package/gemini-extension.json +0 -5
- package/panda.config.ts +0 -20
- /package/{docs → .enderun/docs}/project-docs.md +0 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const SECURITY_AUDIT_ARGS_SCHEMA = z.object({
|
|
3
|
+
path: z.string().default("."),
|
|
4
|
+
});
|
|
5
|
+
export const SEARCH_CODEBASE_ARGS_SCHEMA = z.object({
|
|
6
|
+
query: z.string().min(1).max(300),
|
|
7
|
+
extension: z
|
|
8
|
+
.string()
|
|
9
|
+
.regex(/^[a-z0-9]+$/i)
|
|
10
|
+
.default("ts"),
|
|
11
|
+
});
|
|
12
|
+
export const UPDATE_MEMORY_ARGS_SCHEMA = z.object({
|
|
13
|
+
section: z.enum(["CURRENT STATUS", "HISTORY", "ACTIVE TASKS"]),
|
|
14
|
+
content: z.string().min(1),
|
|
15
|
+
});
|
|
16
|
+
export const ANALYZE_DEPENDENCIES_ARGS_SCHEMA = z.object({
|
|
17
|
+
path: z.string().min(1),
|
|
18
|
+
});
|
|
19
|
+
export const LOG_AGENT_ACTION_ARGS_SCHEMA = z.object({
|
|
20
|
+
agent: z.string().min(1),
|
|
21
|
+
action: z.string().min(1),
|
|
22
|
+
requestId: z.string().min(1),
|
|
23
|
+
files: z.array(z.string()).default([]),
|
|
24
|
+
status: z.enum(["SUCCESS", "FAILURE"]),
|
|
25
|
+
summary: z.string().min(1),
|
|
26
|
+
details: z.record(z.any()).default({}),
|
|
27
|
+
});
|
|
28
|
+
export const SEND_AGENT_MESSAGE_ARGS_SCHEMA = z.object({
|
|
29
|
+
to: z.string().min(1),
|
|
30
|
+
message: z.string().min(1),
|
|
31
|
+
traceId: z.string().min(1),
|
|
32
|
+
});
|
|
33
|
+
export const SEARCH_KNOWLEDGE_BASE_ARGS_SCHEMA = z.object({
|
|
34
|
+
query: z.string().min(1),
|
|
35
|
+
});
|
|
36
|
+
export const UPDATE_KNOWLEDGE_BASE_ARGS_SCHEMA = z.object({
|
|
37
|
+
topic: z.string().min(1),
|
|
38
|
+
content: z.string().min(1),
|
|
39
|
+
});
|
|
40
|
+
export const ANALYZE_DATABASE_SCHEMA_ARGS_SCHEMA = z.object({
|
|
41
|
+
path: z.string().default("apps/backend"),
|
|
42
|
+
});
|
|
43
|
+
export const GENERATE_STRATEGIC_BRIEFING_ARGS_SCHEMA = z.object({
|
|
44
|
+
focusArea: z.string().optional(),
|
|
45
|
+
});
|
|
46
|
+
export const ANALYZE_CONSTITUTION_COMPLIANCE_ARGS_SCHEMA = z.object({
|
|
47
|
+
path: z.string().min(1),
|
|
48
|
+
});
|
|
49
|
+
export const GENERATE_DEPENDENCY_GRAPH_ARGS_SCHEMA = z.object({
|
|
50
|
+
path: z.string().default("src"),
|
|
51
|
+
format: z.enum(["mermaid", "json"]).default("mermaid"),
|
|
52
|
+
});
|
|
53
|
+
export const GENERATE_ACADEMY_PROGRESS_REPORT_ARGS_SCHEMA = z.object({
|
|
54
|
+
days: z.number().default(7),
|
|
55
|
+
});
|
|
56
|
+
export const ANALYZE_DOCUMENTATION_DEBT_ARGS_SCHEMA = z.object({
|
|
57
|
+
path: z.string().default("."),
|
|
58
|
+
});
|
|
59
|
+
export const VALIDATE_REPOSITORY_HEALTH_ARGS_SCHEMA = z.object({
|
|
60
|
+
scope: z.enum(["full", "lint", "test", "build"]).default("full"),
|
|
61
|
+
});
|
|
62
|
+
export const READ_AGENT_MESSAGES_ARGS_SCHEMA = z.object({
|
|
63
|
+
agent: z.string().min(1),
|
|
64
|
+
});
|
|
65
|
+
export const VERIFY_CONTRACT_INTEGRITY_ARGS_SCHEMA = z.object({
|
|
66
|
+
domain: z.string().min(1),
|
|
67
|
+
});
|
|
68
|
+
export const GENERATE_SEMANTIC_COMMIT_MESSAGE_ARGS_SCHEMA = z.object({
|
|
69
|
+
traceId: z.string().min(1),
|
|
70
|
+
});
|
|
71
|
+
export const ANALYZE_CODEBASE_INTELLIGENCE_ARGS_SCHEMA = z.object({
|
|
72
|
+
path: z.string().default("."),
|
|
73
|
+
});
|
|
74
|
+
export const GET_ACADEMY_PERFORMANCE_ARGS_SCHEMA = z.object({
|
|
75
|
+
periodDays: z.number().default(30),
|
|
76
|
+
});
|
|
77
|
+
export const ANALYZE_PROCEDURAL_CONTINUITY_ARGS_SCHEMA = z.object({
|
|
78
|
+
targetPath: z.string().min(1),
|
|
79
|
+
referencePath: z.string().min(1),
|
|
80
|
+
});
|
|
81
|
+
export const GET_AGENT_AUDIT_REPORT_ARGS_SCHEMA = z.object({
|
|
82
|
+
agent: z.string().min(1),
|
|
83
|
+
days: z.number().default(7),
|
|
84
|
+
});
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { getFrameworkDir } from "../utils.js";
|
|
4
|
+
import { GET_ACADEMY_PERFORMANCE_ARGS_SCHEMA, GENERATE_ACADEMY_PROGRESS_REPORT_ARGS_SCHEMA, GET_AGENT_AUDIT_REPORT_ARGS_SCHEMA, LOG_AGENT_ACTION_ARGS_SCHEMA } from "../schemas.js";
|
|
5
|
+
export const academyTools = [
|
|
6
|
+
{
|
|
7
|
+
name: "get_academy_performance",
|
|
8
|
+
description: "Aggregates performance data from all agents to provide a global overview of the Academy's health and intelligence growth.",
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: "object",
|
|
11
|
+
properties: {
|
|
12
|
+
periodDays: {
|
|
13
|
+
type: "number",
|
|
14
|
+
description: "Number of days to include in the report",
|
|
15
|
+
default: 30,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: "generate_strategic_briefing",
|
|
22
|
+
description: "Generates a high-level strategic briefing for the user, summarizing Academy health, progress, and future strategy.",
|
|
23
|
+
inputSchema: {
|
|
24
|
+
type: "object",
|
|
25
|
+
properties: {
|
|
26
|
+
focusArea: {
|
|
27
|
+
type: "string",
|
|
28
|
+
description: "Optional area to focus on (e.g. 'quality', 'velocity', 'architecture')",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "generate_academy_progress_report",
|
|
35
|
+
description: "Generates a comprehensive narrative report on the Academy's progress, milestones, and challenges.",
|
|
36
|
+
inputSchema: {
|
|
37
|
+
type: "object",
|
|
38
|
+
properties: {
|
|
39
|
+
days: {
|
|
40
|
+
type: "number",
|
|
41
|
+
description: "Number of days to include in the report",
|
|
42
|
+
default: 7,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "get_agent_audit_report",
|
|
49
|
+
description: "Generates a summary report of an agent's activities based on their logs. Useful for self-improvement and progress tracking.",
|
|
50
|
+
inputSchema: {
|
|
51
|
+
type: "object",
|
|
52
|
+
properties: {
|
|
53
|
+
agent: {
|
|
54
|
+
type: "string",
|
|
55
|
+
description: "Agent name (e.g. analyst, backend)",
|
|
56
|
+
},
|
|
57
|
+
days: {
|
|
58
|
+
type: "number",
|
|
59
|
+
description: "Number of recent days to analyze",
|
|
60
|
+
default: 7,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
required: ["agent"],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: "log_agent_action",
|
|
68
|
+
description: "Safely append a structured log entry to the agent's log file.",
|
|
69
|
+
inputSchema: {
|
|
70
|
+
type: "object",
|
|
71
|
+
properties: {
|
|
72
|
+
agent: { type: "string", description: "Agent name" },
|
|
73
|
+
action: { type: "string", description: "Action performed" },
|
|
74
|
+
requestId: { type: "string", description: "Trace ID or Request ID" },
|
|
75
|
+
files: { type: "array", items: { type: "string" }, description: "Files affected" },
|
|
76
|
+
status: { type: "string", enum: ["SUCCESS", "FAILURE"], description: "Action status" },
|
|
77
|
+
summary: { type: "string", description: "Short English summary" },
|
|
78
|
+
details: { type: "object", description: "Additional details" },
|
|
79
|
+
},
|
|
80
|
+
required: ["agent", "action", "requestId", "status", "summary"],
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
export const academyHandlers = {
|
|
85
|
+
get_academy_performance: async (args, projectRoot) => {
|
|
86
|
+
const parsed = GET_ACADEMY_PERFORMANCE_ARGS_SCHEMA.safeParse(args ?? {});
|
|
87
|
+
const days = parsed.success ? parsed.data.periodDays : 30;
|
|
88
|
+
try {
|
|
89
|
+
const frameworkDir = getFrameworkDir(projectRoot);
|
|
90
|
+
const logsDir = path.join(projectRoot, frameworkDir, "logs");
|
|
91
|
+
if (!fs.existsSync(logsDir))
|
|
92
|
+
return { content: [{ type: "text", text: "No logs found." }] };
|
|
93
|
+
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
|
|
94
|
+
let totalActions = 0, totalSuccess = 0;
|
|
95
|
+
const agentStats = fs.readdirSync(logsDir).filter(f => f.endsWith(".json")).map(file => {
|
|
96
|
+
const logs = JSON.parse(fs.readFileSync(path.join(logsDir, file), "utf-8")).filter((l) => new Date(l.timestamp) >= cutoff);
|
|
97
|
+
const success = logs.filter((l) => l.status === "SUCCESS").length;
|
|
98
|
+
totalActions += logs.length;
|
|
99
|
+
totalSuccess += success;
|
|
100
|
+
return { agent: file.replace(".json", ""), actions: logs.length, successRate: (success / (logs.length || 1)) * 100 };
|
|
101
|
+
}).filter(s => s.actions > 0);
|
|
102
|
+
return { content: [{ type: "text", text: `### GLOBAL ACADEMY PERFORMANCE REPORT\n\n- **Analysis Period:** Last ${days} days\n- **Global Success Rate:** ${((totalSuccess / (totalActions || 1)) * 100).toFixed(1)}%\n\n**Agent Breakdown:**\n` + agentStats.map(s => `- **@${s.agent}:** ${s.actions} actions (${s.successRate.toFixed(1)}% success)`).join("\n") }] };
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
return { content: [{ type: "text", text: "Failed to generate global performance report." }] };
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
generate_strategic_briefing: async (args, projectRoot) => {
|
|
109
|
+
try {
|
|
110
|
+
const frameworkDir = getFrameworkDir(projectRoot);
|
|
111
|
+
const logsDir = path.join(projectRoot, frameworkDir, "logs");
|
|
112
|
+
let totalActions = 0, successCount = 0;
|
|
113
|
+
if (fs.existsSync(logsDir)) {
|
|
114
|
+
fs.readdirSync(logsDir).filter(f => f.endsWith(".json")).forEach(file => {
|
|
115
|
+
const logs = JSON.parse(fs.readFileSync(path.join(logsDir, file), "utf-8"));
|
|
116
|
+
totalActions += logs.length;
|
|
117
|
+
successCount += logs.filter((l) => l.status === "SUCCESS").length;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
const successRate = (successCount / (totalActions || 1)) * 100;
|
|
121
|
+
return { content: [{ type: "text", text: `# 🏛️ ACADEMY STRATEGIC BRIEFING\n\n- **Academy Health:** ${successRate.toFixed(1)}% Success Rate over ${totalActions} actions.\n**Status:** READY FOR PHASE_1 COMPLETION.` }] };
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
return { content: [{ type: "text", text: "Strategic briefing generation failed." }] };
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
generate_academy_progress_report: async (args, projectRoot) => {
|
|
128
|
+
const parsed = GENERATE_ACADEMY_PROGRESS_REPORT_ARGS_SCHEMA.safeParse(args ?? {});
|
|
129
|
+
const days = parsed.success ? parsed.data.days : 7;
|
|
130
|
+
try {
|
|
131
|
+
const frameworkDir = getFrameworkDir(projectRoot);
|
|
132
|
+
const memoryPath = path.join(projectRoot, frameworkDir, "PROJECT_MEMORY.md");
|
|
133
|
+
const memoryContent = fs.existsSync(memoryPath) ? fs.readFileSync(memoryPath, "utf-8") : "";
|
|
134
|
+
const historySection = memoryContent.match(/## HISTORY[\s\S]*$/m)?.[0] ?? "";
|
|
135
|
+
const milestones = Array.from(historySection.matchAll(/### (.*)\n\n- \*\*Agent:\*\* (.*)\n- \*\*Trace ID:\*\* (.*)\n- \*\*Action:\*\* (.*)/g)).slice(0, 5).map(m => `- [${m[1]}] **${m[2]}:** ${m[4]}`).join("\n");
|
|
136
|
+
return { content: [{ type: "text", text: `# 🎓 AGENT ACADEMY PROGRESS REPORT\n\n## 🏆 Key Milestones Achieved\n${milestones}` }] };
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
return { content: [{ type: "text", text: "Failed to generate progress report." }] };
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
get_agent_audit_report: async (args, projectRoot) => {
|
|
143
|
+
const parsed = GET_AGENT_AUDIT_REPORT_ARGS_SCHEMA.safeParse(args ?? {});
|
|
144
|
+
if (!parsed.success)
|
|
145
|
+
return { content: [{ type: "text", text: "Invalid agent or days argument." }] };
|
|
146
|
+
try {
|
|
147
|
+
const frameworkDir = getFrameworkDir(projectRoot);
|
|
148
|
+
const logPath = path.join(projectRoot, frameworkDir, "logs", `${parsed.data.agent}.json`);
|
|
149
|
+
if (!fs.existsSync(logPath))
|
|
150
|
+
return { content: [{ type: "text", text: `No logs found for agent: ${parsed.data.agent}` }] };
|
|
151
|
+
const cutoff = new Date(Date.now() - parsed.data.days * 24 * 60 * 60 * 1000);
|
|
152
|
+
const logs = JSON.parse(fs.readFileSync(logPath, "utf-8")).filter((l) => new Date(l.timestamp) >= cutoff);
|
|
153
|
+
const successCount = logs.filter((l) => l.status === "SUCCESS").length;
|
|
154
|
+
return { content: [{ type: "text", text: `### AGENT AUDIT REPORT: ${parsed.data.agent.toUpperCase()}\n\n- **Total Actions:** ${logs.length}\n- **Success Rate:** ${((successCount / (logs.length || 1)) * 100).toFixed(1)}%` }] };
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
return { content: [{ type: "text", text: "Failed to generate audit report." }] };
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
log_agent_action: async (args, projectRoot) => {
|
|
161
|
+
const parsed = LOG_AGENT_ACTION_ARGS_SCHEMA.safeParse(args ?? {});
|
|
162
|
+
if (!parsed.success)
|
|
163
|
+
return { content: [{ type: "text", text: "Invalid arguments for log_agent_action." }] };
|
|
164
|
+
try {
|
|
165
|
+
const frameworkDir = getFrameworkDir(projectRoot);
|
|
166
|
+
const logsDir = path.join(projectRoot, frameworkDir, "logs");
|
|
167
|
+
if (!fs.existsSync(logsDir))
|
|
168
|
+
fs.mkdirSync(logsDir, { recursive: true });
|
|
169
|
+
const logPath = path.join(logsDir, `${parsed.data.agent}.json`);
|
|
170
|
+
let logs = fs.existsSync(logPath) ? JSON.parse(fs.readFileSync(logPath, "utf-8")) : [];
|
|
171
|
+
if (!Array.isArray(logs))
|
|
172
|
+
logs = [];
|
|
173
|
+
const requestId = parsed.data.requestId;
|
|
174
|
+
if (requestId !== "—" && !/^[0-9A-Z]{26}$/.test(requestId))
|
|
175
|
+
return { content: [{ type: "text", text: "ERROR: requestId MUST be a 26-character ULID." }] };
|
|
176
|
+
logs.push({ timestamp: new Date().toISOString(), ...parsed.data });
|
|
177
|
+
fs.writeFileSync(logPath, JSON.stringify(logs, null, 2));
|
|
178
|
+
return { content: [{ type: "text", text: `SUCCESS: Logged action to ${parsed.data.agent}.json` }] };
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
return { content: [{ type: "text", text: "Failed to log action." }] };
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
};
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { Project } from "ts-morph";
|
|
4
|
+
import { collectFilesRecursively, resolveSafePath } from "../utils.js";
|
|
5
|
+
import { SEARCH_CODEBASE_ARGS_SCHEMA, ANALYZE_DEPENDENCIES_ARGS_SCHEMA, ANALYZE_CODEBASE_INTELLIGENCE_ARGS_SCHEMA, ANALYZE_PROCEDURAL_CONTINUITY_ARGS_SCHEMA, GENERATE_DEPENDENCY_GRAPH_ARGS_SCHEMA } from "../schemas.js";
|
|
6
|
+
export const codebaseTools = [
|
|
7
|
+
{
|
|
8
|
+
name: "search_codebase",
|
|
9
|
+
description: "Semantic search across the codebase using grep for exact matches and context. Ideal for finding logic and patterns.",
|
|
10
|
+
inputSchema: {
|
|
11
|
+
type: "object",
|
|
12
|
+
properties: {
|
|
13
|
+
query: {
|
|
14
|
+
type: "string",
|
|
15
|
+
description: "Search query or regex pattern",
|
|
16
|
+
},
|
|
17
|
+
extension: {
|
|
18
|
+
type: "string",
|
|
19
|
+
description: "File extension filter (e.g., ts, md)",
|
|
20
|
+
default: "ts",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
required: ["query"],
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: "codebase_search",
|
|
28
|
+
description: "Compatibility alias for search_codebase. Use when older agent prompts still reference codebase_search.",
|
|
29
|
+
inputSchema: {
|
|
30
|
+
type: "object",
|
|
31
|
+
properties: {
|
|
32
|
+
query: {
|
|
33
|
+
type: "string",
|
|
34
|
+
description: "Search query or regex pattern",
|
|
35
|
+
},
|
|
36
|
+
extension: {
|
|
37
|
+
type: "string",
|
|
38
|
+
description: "File extension filter (e.g., ts, md)",
|
|
39
|
+
default: "ts",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
required: ["query"],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "analyze_dependencies",
|
|
47
|
+
description: "Analyze code dependencies for a specific file or folder using import tracking.",
|
|
48
|
+
inputSchema: {
|
|
49
|
+
type: "object",
|
|
50
|
+
properties: {
|
|
51
|
+
path: {
|
|
52
|
+
type: "string",
|
|
53
|
+
description: "Path to analyze (relative to project root)",
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
required: ["path"],
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "codebase_graph_query",
|
|
61
|
+
description: "Compatibility alias for analyze_dependencies. Returns import-level dependency information for a file or folder.",
|
|
62
|
+
inputSchema: {
|
|
63
|
+
type: "object",
|
|
64
|
+
properties: {
|
|
65
|
+
path: {
|
|
66
|
+
type: "string",
|
|
67
|
+
description: "Path to analyze (relative to project root)",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
required: ["path"],
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: "analyze_codebase_intelligence",
|
|
75
|
+
description: "Scans the codebase for complexity spikes and potential dead code (exported but unused symbols).",
|
|
76
|
+
inputSchema: {
|
|
77
|
+
type: "object",
|
|
78
|
+
properties: {
|
|
79
|
+
path: {
|
|
80
|
+
type: "string",
|
|
81
|
+
description: "Path to scan (relative to project root)",
|
|
82
|
+
default: ".",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: "analyze_procedural_continuity",
|
|
89
|
+
description: "Compares a target file with a reference file to identify stylistic or structural deviations. Ensures agents follow existing patterns.",
|
|
90
|
+
inputSchema: {
|
|
91
|
+
type: "object",
|
|
92
|
+
properties: {
|
|
93
|
+
targetPath: {
|
|
94
|
+
type: "string",
|
|
95
|
+
description: "Path to the newly created/modified file",
|
|
96
|
+
},
|
|
97
|
+
referencePath: {
|
|
98
|
+
type: "string",
|
|
99
|
+
description: "Path to an existing 'gold standard' file for comparison",
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
required: ["targetPath", "referencePath"],
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: "generate_dependency_graph",
|
|
107
|
+
description: "Generates a Mermaid-compatible dependency graph of the codebase or a specific directory.",
|
|
108
|
+
inputSchema: {
|
|
109
|
+
type: "object",
|
|
110
|
+
properties: {
|
|
111
|
+
path: {
|
|
112
|
+
type: "string",
|
|
113
|
+
description: "Path to scan (relative to project root)",
|
|
114
|
+
default: "src",
|
|
115
|
+
},
|
|
116
|
+
format: {
|
|
117
|
+
type: "string",
|
|
118
|
+
enum: ["mermaid", "json"],
|
|
119
|
+
default: "mermaid",
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
];
|
|
125
|
+
export const codebaseHandlers = {
|
|
126
|
+
search_codebase: async (args, projectRoot) => {
|
|
127
|
+
const parsed = SEARCH_CODEBASE_ARGS_SCHEMA.safeParse(args);
|
|
128
|
+
if (!parsed.success) {
|
|
129
|
+
return { content: [{ type: "text", text: "Invalid query/extension argument." }] };
|
|
130
|
+
}
|
|
131
|
+
const { query, extension } = parsed.data;
|
|
132
|
+
try {
|
|
133
|
+
const files = collectFilesRecursively(projectRoot, new Set([extension]));
|
|
134
|
+
let queryRegex;
|
|
135
|
+
try {
|
|
136
|
+
queryRegex = new RegExp(query, "i");
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
return { content: [{ type: "text", text: "Invalid regex pattern in query." }] };
|
|
140
|
+
}
|
|
141
|
+
const matches = [];
|
|
142
|
+
const MAX_RESULTS = 30;
|
|
143
|
+
for (const filePath of files) {
|
|
144
|
+
if (matches.length >= MAX_RESULTS)
|
|
145
|
+
break;
|
|
146
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
147
|
+
const lines = content.split("\n");
|
|
148
|
+
for (let i = 0; i < lines.length; i++) {
|
|
149
|
+
if (matches.length >= MAX_RESULTS)
|
|
150
|
+
break;
|
|
151
|
+
const line = lines[i];
|
|
152
|
+
if (queryRegex.test(line)) {
|
|
153
|
+
const relativePath = path.relative(projectRoot, filePath);
|
|
154
|
+
matches.push(`- ${relativePath}:${i + 1}: ${line.trim()}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return { content: [{ type: "text", text: matches.join("\n") || "No matches found." }] };
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
return { content: [{ type: "text", text: "Search failed." }] };
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
analyze_dependencies: async (args, projectRoot) => {
|
|
165
|
+
const parsed = ANALYZE_DEPENDENCIES_ARGS_SCHEMA.safeParse(args);
|
|
166
|
+
if (!parsed.success) {
|
|
167
|
+
return { content: [{ type: "text", text: "Invalid path argument." }] };
|
|
168
|
+
}
|
|
169
|
+
const targetPath = parsed.data.path;
|
|
170
|
+
try {
|
|
171
|
+
const fullPath = resolveSafePath(projectRoot, targetPath);
|
|
172
|
+
if (!fs.existsSync(fullPath))
|
|
173
|
+
return { content: [{ type: "text", text: `Path not found: ${targetPath}` }] };
|
|
174
|
+
const stats = fs.statSync(fullPath);
|
|
175
|
+
const tsProject = new Project({ compilerOptions: { allowJs: true } });
|
|
176
|
+
if (stats.isDirectory()) {
|
|
177
|
+
tsProject.addSourceFilesAtPaths(path.join(fullPath, "**/*.{ts,tsx,js,jsx}"));
|
|
178
|
+
const sourceFiles = tsProject.getSourceFiles();
|
|
179
|
+
return { content: [{ type: "text", text: `Directory contains ${sourceFiles.length} source files. Use a specific file path for deep dependency analysis.` }] };
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
const sourceFile = tsProject.addSourceFileAtPath(fullPath);
|
|
183
|
+
const imports = sourceFile.getImportDeclarations();
|
|
184
|
+
const importDetails = imports.map((imp) => {
|
|
185
|
+
const moduleSpecifier = imp.getModuleSpecifierValue();
|
|
186
|
+
const source = imp.getModuleSpecifierSourceFile();
|
|
187
|
+
const resolvedPath = source ? path.relative(projectRoot, source.getFilePath()) : "unresolved/external";
|
|
188
|
+
return `- ${moduleSpecifier} (${resolvedPath})`;
|
|
189
|
+
});
|
|
190
|
+
return { content: [{ type: "text", text: `Dependencies for ${targetPath}:\n${importDetails.length > 0 ? importDetails.join("\n") : "No imports found."}` }] };
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
return { content: [{ type: "text", text: "Analysis failed: " + (error instanceof Error ? error.message : String(error)) }] };
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
analyze_codebase_intelligence: async (args, projectRoot) => {
|
|
198
|
+
const parsed = ANALYZE_CODEBASE_INTELLIGENCE_ARGS_SCHEMA.safeParse(args);
|
|
199
|
+
const targetPath = parsed.success ? parsed.data.path : ".";
|
|
200
|
+
try {
|
|
201
|
+
const safeTargetPath = resolveSafePath(projectRoot, targetPath);
|
|
202
|
+
if (!fs.existsSync(safeTargetPath))
|
|
203
|
+
return { content: [{ type: "text", text: "Target path not found." }] };
|
|
204
|
+
const tsProject = new Project({ compilerOptions: { allowJs: true } });
|
|
205
|
+
tsProject.addSourceFilesAtPaths(path.join(safeTargetPath, "**/*.{ts,tsx}"));
|
|
206
|
+
const complexitySpikes = [];
|
|
207
|
+
const potentialDeadCode = [];
|
|
208
|
+
for (const sourceFile of tsProject.getSourceFiles()) {
|
|
209
|
+
const relativePath = path.relative(projectRoot, sourceFile.getFilePath());
|
|
210
|
+
const lineCount = sourceFile.getEndLineNumber();
|
|
211
|
+
if (lineCount > 300)
|
|
212
|
+
complexitySpikes.push(`${relativePath} (${lineCount} lines)`);
|
|
213
|
+
sourceFile.getExportedDeclarations().forEach((declarations, name) => {
|
|
214
|
+
const isUsed = tsProject.getSourceFiles().some(sf => {
|
|
215
|
+
if (sf === sourceFile)
|
|
216
|
+
return false;
|
|
217
|
+
return sf.getImportDeclarations().some(i => i.getModuleSpecifierValue().includes(sourceFile.getBaseNameWithoutExtension()) && i.getNamedImports().some(ni => ni.getName() === name));
|
|
218
|
+
});
|
|
219
|
+
if (!isUsed && !sourceFile.getFilePath().includes("index.ts") && !sourceFile.getFilePath().includes("shared-types")) {
|
|
220
|
+
potentialDeadCode.push(`${relativePath} -> ${name}`);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
return { content: [{ type: "text", text: `### CODEBASE INTELLIGENCE REPORT\n\n**Complexity Spikes (>300 lines):**\n${complexitySpikes.length > 0 ? complexitySpikes.map(s => `- ${s}`).join("\n") : "- None detected"}\n\n**Potential Dead Code (Unused Exports):**\n${potentialDeadCode.length > 0 ? potentialDeadCode.map(d => `- ${d}`).join("\n") : "- None detected (within scanned scope)"}` }] };
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
return { content: [{ type: "text", text: "Intelligence scan failed." }] };
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
analyze_procedural_continuity: async (args, projectRoot) => {
|
|
231
|
+
const parsed = ANALYZE_PROCEDURAL_CONTINUITY_ARGS_SCHEMA.safeParse(args);
|
|
232
|
+
if (!parsed.success)
|
|
233
|
+
return { content: [{ type: "text", text: "Invalid targetPath or referencePath." }] };
|
|
234
|
+
try {
|
|
235
|
+
const targetFull = resolveSafePath(projectRoot, parsed.data.targetPath);
|
|
236
|
+
const referenceFull = resolveSafePath(projectRoot, parsed.data.referencePath);
|
|
237
|
+
if (!fs.existsSync(targetFull) || !fs.existsSync(referenceFull))
|
|
238
|
+
return { content: [{ type: "text", text: "One or both files not found." }] };
|
|
239
|
+
const tsProject = new Project({ compilerOptions: { allowJs: true } });
|
|
240
|
+
const targetFile = tsProject.addSourceFileAtPath(targetFull);
|
|
241
|
+
const referenceFile = tsProject.addSourceFileAtPath(referenceFull);
|
|
242
|
+
const deviations = [];
|
|
243
|
+
const targetImports = targetFile.getImportDeclarations().map(i => i.getModuleSpecifierValue());
|
|
244
|
+
const referenceImports = referenceFile.getImportDeclarations().map(i => i.getModuleSpecifierValue());
|
|
245
|
+
const missingImports = referenceImports.filter(i => !targetImports.includes(i) && !i.startsWith("."));
|
|
246
|
+
if (missingImports.length > 0)
|
|
247
|
+
deviations.push(`Missing standard imports found in reference: ${missingImports.join(", ")}`);
|
|
248
|
+
if (referenceFile.getClasses().length > 0 && targetFile.getClasses().length === 0)
|
|
249
|
+
deviations.push("Reference uses classes, but target does not.");
|
|
250
|
+
const targetFunctions = targetFile.getFunctions().map(f => f.getName());
|
|
251
|
+
const referenceFunctions = referenceFile.getFunctions().map(f => f.getName());
|
|
252
|
+
if (referenceFunctions.some(f => f?.startsWith("get")) && !targetFunctions.some(f => f?.startsWith("get")))
|
|
253
|
+
deviations.push("Reference uses 'get' prefix for functions, target does not.");
|
|
254
|
+
return { content: [{ type: "text", text: deviations.length > 0 ? `### PROCEDURAL CONTINUITY DEVIATIONS\n\n${deviations.map(d => `- ${d}`).join("\n")}` : "No major structural or stylistic deviations detected based on reference." }] };
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
return { content: [{ type: "text", text: "Continuity analysis failed." }] };
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
generate_dependency_graph: async (args, projectRoot) => {
|
|
261
|
+
const parsed = GENERATE_DEPENDENCY_GRAPH_ARGS_SCHEMA.safeParse(args);
|
|
262
|
+
const targetPath = parsed.success ? parsed.data.path : "src";
|
|
263
|
+
const format = parsed.success ? parsed.data.format : "mermaid";
|
|
264
|
+
try {
|
|
265
|
+
const safeTargetPath = resolveSafePath(projectRoot, targetPath);
|
|
266
|
+
if (!fs.existsSync(safeTargetPath))
|
|
267
|
+
return { content: [{ type: "text", text: "Target path not found." }] };
|
|
268
|
+
const tsProject = new Project({ compilerOptions: { allowJs: true } });
|
|
269
|
+
tsProject.addSourceFilesAtPaths(path.join(safeTargetPath, "**/*.{ts,tsx}"));
|
|
270
|
+
const edges = [];
|
|
271
|
+
for (const sourceFile of tsProject.getSourceFiles()) {
|
|
272
|
+
const from = sourceFile.getBaseNameWithoutExtension();
|
|
273
|
+
sourceFile.getImportDeclarations().forEach(imp => {
|
|
274
|
+
const moduleSpecifier = imp.getModuleSpecifierValue();
|
|
275
|
+
if (moduleSpecifier.startsWith(".")) {
|
|
276
|
+
const to = path.basename(moduleSpecifier, path.extname(moduleSpecifier));
|
|
277
|
+
edges.push({ from, to });
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
if (format === "json")
|
|
282
|
+
return { content: [{ type: "text", text: JSON.stringify(edges, null, 2) }] };
|
|
283
|
+
let mermaid = "graph TD\n";
|
|
284
|
+
const uniqueEdges = Array.from(new Set(edges.map(e => `${e.from} --> ${e.to}`)));
|
|
285
|
+
if (uniqueEdges.length === 0)
|
|
286
|
+
return { content: [{ type: "text", text: "No internal dependencies found in the scanned path." }] };
|
|
287
|
+
mermaid += uniqueEdges.join("\n");
|
|
288
|
+
return { content: [{ type: "text", text: `### VISUAL ARCHITECTURE MAP: ${targetPath}\n\n\`\`\`mermaid\n${mermaid}\n\`\`\`\n\n**Total Nodes:** ${new Set(edges.flatMap(e => [e.from, e.to])).size} | **Total Edges:** ${uniqueEdges.length}` }] };
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
return { content: [{ type: "text", text: "Dependency graph generation failed." }] };
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import crypto from "crypto";
|
|
4
|
+
import { Project } from "ts-morph";
|
|
5
|
+
import { getFrameworkDir, collectFilesRecursively } from "../utils.js";
|
|
6
|
+
import { VERIFY_CONTRACT_INTEGRITY_ARGS_SCHEMA } from "../schemas.js";
|
|
7
|
+
export const contractTools = [
|
|
8
|
+
{
|
|
9
|
+
name: "verify_api_contract",
|
|
10
|
+
description: "Verify if the shared-types match the stored contract hash in contract.version.json.",
|
|
11
|
+
inputSchema: { type: "object", properties: {} },
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: "update_contract_hash",
|
|
15
|
+
description: "Generate a new hash for shared-types and update contract.version.json.",
|
|
16
|
+
inputSchema: { type: "object", properties: {} },
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: "verify_contract_integrity",
|
|
20
|
+
description: "Deeply analyzes API documentation and shared types to ensure they are perfectly synchronized.",
|
|
21
|
+
inputSchema: {
|
|
22
|
+
type: "object",
|
|
23
|
+
properties: {
|
|
24
|
+
domain: {
|
|
25
|
+
type: "string",
|
|
26
|
+
description: "The API domain to verify (e.g. 'auth', 'user')",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
required: ["domain"],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
];
|
|
33
|
+
export const contractHandlers = {
|
|
34
|
+
verify_api_contract: async (args, projectRoot) => {
|
|
35
|
+
try {
|
|
36
|
+
const sharedTypesDir = path.join(projectRoot, "packages/shared-types/src");
|
|
37
|
+
const contractJsonPath = path.join(projectRoot, "packages/shared-types/contract.version.json");
|
|
38
|
+
if (!fs.existsSync(sharedTypesDir) || !fs.existsSync(contractJsonPath))
|
|
39
|
+
return { content: [{ type: "text", text: "Missing shared-types directory or contract.version.json" }] };
|
|
40
|
+
const files = collectFilesRecursively(sharedTypesDir, new Set(["ts"])).sort();
|
|
41
|
+
const hash = crypto.createHash("sha256");
|
|
42
|
+
files.forEach(f => hash.update(fs.readFileSync(f)));
|
|
43
|
+
const currentHash = hash.digest("hex");
|
|
44
|
+
const storedHash = JSON.parse(fs.readFileSync(contractJsonPath, "utf-8")).contract_hash;
|
|
45
|
+
return { content: [{ type: "text", text: currentHash === storedHash ? "✅ MATCH: Contract is valid and synchronized." : `❌ MISMATCH: Current hash (${currentHash.slice(0, 8)}...) does not match stored hash (${storedHash.slice(0, 8)}...).` }] };
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
return { content: [{ type: "text", text: "Failed to verify contract." }] };
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
update_contract_hash: async (args, projectRoot) => {
|
|
52
|
+
try {
|
|
53
|
+
const sharedTypesDir = path.join(projectRoot, "packages/shared-types/src");
|
|
54
|
+
const contractJsonPath = path.join(projectRoot, "packages/shared-types/contract.version.json");
|
|
55
|
+
if (!fs.existsSync(sharedTypesDir))
|
|
56
|
+
return { content: [{ type: "text", text: "Missing shared-types directory" }] };
|
|
57
|
+
const files = collectFilesRecursively(sharedTypesDir, new Set(["ts"])).sort();
|
|
58
|
+
if (files.length === 0)
|
|
59
|
+
return { content: [{ type: "text", text: "⚠️ WARNING: No TypeScript files found in shared-types/src. Hash not updated." }] };
|
|
60
|
+
const hash = crypto.createHash("sha256");
|
|
61
|
+
files.forEach(f => hash.update(fs.readFileSync(f)));
|
|
62
|
+
const currentHash = hash.digest("hex");
|
|
63
|
+
const contractJson = fs.existsSync(contractJsonPath) ? JSON.parse(fs.readFileSync(contractJsonPath, "utf-8")) : {};
|
|
64
|
+
contractJson.contract_hash = currentHash;
|
|
65
|
+
contractJson.last_updated = new Date().toISOString();
|
|
66
|
+
fs.writeFileSync(contractJsonPath, JSON.stringify(contractJson, null, 2));
|
|
67
|
+
return { content: [{ type: "text", text: `SUCCESS: Contract hash updated to ${currentHash}` }] };
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
return { content: [{ type: "text", text: "Failed to update contract hash." }] };
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
verify_contract_integrity: async (args, projectRoot) => {
|
|
74
|
+
const parsed = VERIFY_CONTRACT_INTEGRITY_ARGS_SCHEMA.safeParse(args ?? {});
|
|
75
|
+
if (!parsed.success)
|
|
76
|
+
return { content: [{ type: "text", text: "Invalid domain argument." }] };
|
|
77
|
+
try {
|
|
78
|
+
const frameworkDir = getFrameworkDir(projectRoot);
|
|
79
|
+
const apiDocPath = path.join(projectRoot, frameworkDir, "docs/api", `${parsed.data.domain}.md`);
|
|
80
|
+
const sharedTypesPath = path.join(projectRoot, "packages/shared-types/src/index.ts");
|
|
81
|
+
if (!fs.existsSync(apiDocPath))
|
|
82
|
+
return { content: [{ type: "text", text: `API documentation not found for domain: ${parsed.data.domain}` }] };
|
|
83
|
+
if (!fs.existsSync(sharedTypesPath))
|
|
84
|
+
return { content: [{ type: "text", text: "Shared types index.ts not found." }] };
|
|
85
|
+
const sourceFile = new Project().addSourceFileAtPath(sharedTypesPath);
|
|
86
|
+
const mentionedTypes = Array.from(fs.readFileSync(apiDocPath, "utf-8").matchAll(/`([^`]+)`/g)).map(m => m[1]).filter(t => /^[A-Z][a-zA-Z0-9]+(DTO|Response|Request|Status|Type)?$/.test(t));
|
|
87
|
+
const uniqueTypes = Array.from(new Set(mentionedTypes));
|
|
88
|
+
const missingTypes = uniqueTypes.filter(t => !sourceFile.getInterface(t) && !sourceFile.getTypeAlias(t) && !sourceFile.getEnum(t) && !sourceFile.getClass(t));
|
|
89
|
+
return { content: [{ type: "text", text: `### CONTRACT INTEGRITY SHIELD: ${parsed.data.domain.toUpperCase()}\n\n` + `- **Missing/Undefined Types:** ${missingTypes.length > 0 ? `⚠️ ${missingTypes.join(", ")}` : "✅ All types synchronized"}\n\n` + `**Result:** ${missingTypes.length === 0 ? "PASSED" : "FAILED"}` }] };
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
return { content: [{ type: "text", text: "Contract verification failed." }] };
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
};
|