agent-enderun 0.8.7 → 0.9.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.
Files changed (106) hide show
  1. package/{ENDERUN.md → .enderun/ENDERUN.md} +5 -3
  2. package/.enderun/PROJECT_MEMORY.md +12 -53
  3. package/.enderun/STATUS.md +7 -8
  4. package/.enderun/agents/agent_army_schema.json +28 -6
  5. package/.enderun/agents/analyst.md +9 -18
  6. package/.enderun/agents/backend.md +22 -52
  7. package/.enderun/agents/devops.md +9 -19
  8. package/.enderun/agents/explorer.md +7 -16
  9. package/.enderun/agents/frontend.md +82 -84
  10. package/.enderun/agents/git.md +7 -16
  11. package/.enderun/agents/manager.md +34 -40
  12. package/.enderun/agents/mobile.md +7 -16
  13. package/.enderun/agents/native.md +7 -16
  14. package/.enderun/agents/orchestrator.md +16 -25
  15. package/.enderun/agents/qa.md +12 -24
  16. package/.enderun/agents/schema/agent-lifecycle-schema.json +29 -8
  17. package/.enderun/agents/security.md +9 -18
  18. package/.enderun/config.json +1 -1
  19. package/.enderun/knowledge/SHIM_TEMPLATE.md +25 -0
  20. package/.enderun/knowledge/eslint-standards.md +63 -0
  21. package/.enderun/knowledge/hermes_protocol.md +1 -1
  22. package/.enderun/knowledge/token_economy_and_memory.md +129 -0
  23. package/.enderun/mcp_config.json +9 -0
  24. package/.enderun/memory-graph/agent-contexts/analyst.json +1 -1
  25. package/.enderun/memory-graph/agent-contexts/backend.json +1 -1
  26. package/.enderun/memory-graph/agent-contexts/devops.json +1 -1
  27. package/.enderun/memory-graph/agent-contexts/explorer.json +1 -1
  28. package/.enderun/memory-graph/agent-contexts/frontend.json +1 -1
  29. package/.enderun/memory-graph/agent-contexts/git.json +1 -1
  30. package/.enderun/memory-graph/agent-contexts/manager.json +1 -1
  31. package/.enderun/memory-graph/agent-contexts/mobile.json +1 -1
  32. package/.enderun/memory-graph/agent-contexts/native.json +1 -1
  33. package/.enderun/memory-graph/agent-contexts/orchestrator.json +1 -1
  34. package/.enderun/memory-graph/agent-contexts/qa.json +1 -1
  35. package/.enderun/memory-graph/agent-contexts/security.json +1 -1
  36. package/.enderun/memory-graph/graph.json +123 -16
  37. package/.enderun/memory-graph/shared-facts.json +26 -32
  38. package/README.md +93 -295
  39. package/agent.md +25 -0
  40. package/bin/cli.js +9 -9
  41. package/bin/update-contract.js +34 -37
  42. package/bin/validate-agent-army.js +38 -13
  43. package/claude.md +14 -8
  44. package/docs/README.md +35 -16
  45. package/docs/roadmap.md +1 -1
  46. package/docs/tech-stack.md +30 -0
  47. package/eslint.config.js +68 -0
  48. package/framework-mcp/dist/index.js +184 -24
  49. package/framework-mcp/package.json +6 -33
  50. package/framework-mcp/tsconfig.json +3 -6
  51. package/gemini.md +12 -6
  52. package/grok.md +25 -0
  53. package/mcp.json +2 -2
  54. package/package.json +12 -6
  55. package/panda.config.ts +86 -86
  56. package/tsconfig.json +9 -0
  57. package/.enderun/claude.md +0 -19
  58. package/.enderun/cursor.md +0 -19
  59. package/.enderun/gemini.md +0 -19
  60. package/.enderun/logs/analyst.json +0 -1
  61. package/.enderun/logs/backend.json +0 -1
  62. package/.enderun/logs/devops.json +0 -1
  63. package/.enderun/logs/explorer.json +0 -1
  64. package/.enderun/logs/frontend.json +0 -1
  65. package/.enderun/logs/git.json +0 -1
  66. package/.enderun/logs/manager.json +0 -1
  67. package/.enderun/logs/mobile.json +0 -1
  68. package/.enderun/logs/native.json +0 -1
  69. package/.enderun/logs/orchestrator.json +0 -1
  70. package/.enderun/logs/qa.json +0 -1
  71. package/.enderun/logs/security.json +0 -1
  72. package/.env.example +0 -10
  73. package/cursor.md +0 -19
  74. package/framework-mcp/README.md +0 -55
  75. package/framework-mcp/dist/schemas.js +0 -118
  76. package/framework-mcp/dist/tools/codebase.js +0 -294
  77. package/framework-mcp/dist/tools/contract.js +0 -197
  78. package/framework-mcp/dist/tools/framework.js +0 -196
  79. package/framework-mcp/dist/tools/git.js +0 -51
  80. package/framework-mcp/dist/tools/governance.js +0 -696
  81. package/framework-mcp/dist/tools/index.js +0 -47
  82. package/framework-mcp/dist/tools/knowledge.js +0 -133
  83. package/framework-mcp/dist/tools/memory.js +0 -200
  84. package/framework-mcp/dist/tools/messages.js +0 -193
  85. package/framework-mcp/dist/tools/monitoring.js +0 -294
  86. package/framework-mcp/dist/tools/orchestration.js +0 -368
  87. package/framework-mcp/dist/tools/pipeline.js +0 -267
  88. package/framework-mcp/dist/tools/security.js +0 -137
  89. package/framework-mcp/dist/utils.js +0 -121
  90. package/framework-mcp/src/index.ts +0 -49
  91. package/framework-mcp/src/schemas.ts +0 -106
  92. package/framework-mcp/src/tools/codebase.ts +0 -284
  93. package/framework-mcp/src/tools/contract.ts +0 -221
  94. package/framework-mcp/src/tools/framework.ts +0 -192
  95. package/framework-mcp/src/tools/git.ts +0 -42
  96. package/framework-mcp/src/tools/governance.ts +0 -891
  97. package/framework-mcp/src/tools/index.ts +0 -50
  98. package/framework-mcp/src/tools/knowledge.ts +0 -141
  99. package/framework-mcp/src/tools/memory.ts +0 -207
  100. package/framework-mcp/src/tools/messages.ts +0 -213
  101. package/framework-mcp/src/tools/monitoring.ts +0 -351
  102. package/framework-mcp/src/tools/orchestration.ts +0 -440
  103. package/framework-mcp/src/tools/pipeline.ts +0 -353
  104. package/framework-mcp/src/tools/security.ts +0 -143
  105. package/framework-mcp/src/utils.ts +0 -130
  106. package/src/cli/index.ts +0 -2188
@@ -1,267 +0,0 @@
1
- import { promises as fs } from "fs";
2
- import path from "path";
3
- // ─── Tool Definitions ─────────────────────────────────────────────────────────
4
- export const pipelineTools = [
5
- {
6
- name: "create_execution_pipeline",
7
- description: "Creates a multi-stage execution pipeline for complex workflows (e.g., build → test → security audit → deploy). Each stage is assigned to a specific agent and has defined dependencies. The pipeline is persisted and can be advanced or rolled back.",
8
- inputSchema: {
9
- type: "object",
10
- properties: {
11
- traceId: { type: "string", description: "Active Trace ID from @manager briefing" },
12
- name: { type: "string", description: "Pipeline name (e.g. 'Production Deploy v1.2.0')" },
13
- description: { type: "string", description: "What this pipeline accomplishes" },
14
- stages: {
15
- type: "array",
16
- description: "Ordered pipeline stages",
17
- items: {
18
- type: "object",
19
- required: ["id", "name", "agent"],
20
- properties: {
21
- id: { type: "string" },
22
- name: { type: "string" },
23
- agent: { type: "string" },
24
- dependsOn: { type: "array", items: { type: "string" } },
25
- },
26
- },
27
- },
28
- rollbackStages: {
29
- type: "array",
30
- description: "Optional rollback stages executed in reverse order on pipeline failure",
31
- items: {
32
- type: "object",
33
- required: ["id", "name", "agent"],
34
- properties: {
35
- id: { type: "string" },
36
- name: { type: "string" },
37
- agent: { type: "string" },
38
- dependsOn: { type: "array", items: { type: "string" } },
39
- },
40
- },
41
- },
42
- },
43
- required: ["traceId", "name", "stages"],
44
- },
45
- },
46
- {
47
- name: "advance_pipeline_stage",
48
- description: "Advances a pipeline to the next eligible stage. Marks the current stage as COMPLETED and activates all dependent stages that now have their dependencies satisfied. Returns the new pipeline state.",
49
- inputSchema: {
50
- type: "object",
51
- properties: {
52
- pipelineId: { type: "string", description: "Pipeline ID" },
53
- completedStageId: { type: "string", description: "The stage that just completed successfully" },
54
- stageOutput: { type: "string", description: "Optional output/result from the completed stage" },
55
- },
56
- required: ["pipelineId", "completedStageId"],
57
- },
58
- },
59
- {
60
- name: "rollback_pipeline",
61
- description: "Rolls back a pipeline to the last stable state by executing the defined rollback stages in reverse order. Updates the pipeline status to ROLLED_BACK.",
62
- inputSchema: {
63
- type: "object",
64
- properties: {
65
- pipelineId: { type: "string", description: "Pipeline ID to roll back" },
66
- reason: { type: "string", description: "Reason for the rollback (mandatory for audit)" },
67
- },
68
- required: ["pipelineId", "reason"],
69
- },
70
- },
71
- {
72
- name: "get_pipeline_status",
73
- description: "Returns the full current status of a pipeline including all stage states, progress percentage, blocking stages, and estimated completion.",
74
- inputSchema: {
75
- type: "object",
76
- properties: {
77
- pipelineId: { type: "string", description: "Pipeline ID" },
78
- },
79
- required: ["pipelineId"],
80
- },
81
- },
82
- ];
83
- // ─── Handlers ─────────────────────────────────────────────────────────────────
84
- export const pipelineHandlers = {
85
- async create_execution_pipeline(args, projectRoot) {
86
- const { traceId, name, description, stages, rollbackStages } = args;
87
- const pipelineId = `PIPE-${traceId}-${Date.now()}`;
88
- const now = new Date().toISOString();
89
- const pipeline = {
90
- id: pipelineId,
91
- traceId,
92
- name,
93
- description: description ?? "",
94
- createdAt: now,
95
- updatedAt: now,
96
- status: "PENDING",
97
- stages: stages.map((s) => ({
98
- ...s,
99
- dependsOn: s.dependsOn ?? [],
100
- status: "PENDING",
101
- })),
102
- rollbackStages: rollbackStages?.map((s) => ({
103
- ...s,
104
- dependsOn: s.dependsOn ?? [],
105
- status: "PENDING",
106
- })),
107
- };
108
- // Activate stages with no dependencies
109
- for (const stage of pipeline.stages) {
110
- if (stage.dependsOn.length === 0) {
111
- stage.status = "RUNNING";
112
- stage.startedAt = now;
113
- }
114
- }
115
- pipeline.status = "RUNNING";
116
- // Persist pipeline
117
- const pipelineDir = path.join(projectRoot, ".enderun", "queue", "pipelines");
118
- await fs.mkdir(pipelineDir, { recursive: true });
119
- await fs.writeFile(path.join(pipelineDir, `${pipelineId}.json`), JSON.stringify(pipeline, null, 2), "utf-8");
120
- return { content: [{ type: "text", text: JSON.stringify(pipeline, null, 2) }] };
121
- },
122
- async advance_pipeline_stage(args, projectRoot) {
123
- const { pipelineId, completedStageId, stageOutput } = args;
124
- const pipelineDir = path.join(projectRoot, ".enderun", "queue", "pipelines");
125
- const pipelinePath = path.join(pipelineDir, `${pipelineId}.json`);
126
- let pipeline;
127
- try {
128
- const content = await fs.readFile(pipelinePath, "utf-8");
129
- pipeline = JSON.parse(content);
130
- }
131
- catch {
132
- return {
133
- content: [
134
- { type: "text", text: JSON.stringify({ error: "PIPELINE_NOT_FOUND", pipelineId }) },
135
- ],
136
- };
137
- }
138
- const now = new Date().toISOString();
139
- // Mark completed stage
140
- const completedStage = pipeline.stages.find((s) => s.id === completedStageId);
141
- if (!completedStage) {
142
- return {
143
- content: [
144
- { type: "text", text: JSON.stringify({ error: "STAGE_NOT_FOUND", completedStageId }) },
145
- ],
146
- };
147
- }
148
- completedStage.status = "COMPLETED";
149
- completedStage.completedAt = now;
150
- if (stageOutput)
151
- completedStage.output = stageOutput;
152
- // Activate newly unblocked stages
153
- const completedIds = new Set(pipeline.stages.filter((s) => s.status === "COMPLETED").map((s) => s.id));
154
- const newlyActivated = [];
155
- for (const stage of pipeline.stages) {
156
- if (stage.status === "PENDING" && stage.dependsOn.every((dep) => completedIds.has(dep))) {
157
- stage.status = "RUNNING";
158
- stage.startedAt = now;
159
- newlyActivated.push(stage.id);
160
- }
161
- }
162
- // Check if pipeline is complete
163
- const allDone = pipeline.stages.every((s) => s.status === "COMPLETED");
164
- if (allDone) {
165
- pipeline.status = "COMPLETED";
166
- }
167
- pipeline.updatedAt = now;
168
- await fs.writeFile(pipelinePath, JSON.stringify(pipeline, null, 2), "utf-8");
169
- return {
170
- content: [
171
- {
172
- type: "text",
173
- text: JSON.stringify({ pipeline, newlyActivatedStages: newlyActivated }, null, 2),
174
- },
175
- ],
176
- };
177
- },
178
- async rollback_pipeline(args, projectRoot) {
179
- const { pipelineId, reason } = args;
180
- const pipelineDir = path.join(projectRoot, ".enderun", "queue", "pipelines");
181
- const pipelinePath = path.join(pipelineDir, `${pipelineId}.json`);
182
- let pipeline;
183
- try {
184
- const content = await fs.readFile(pipelinePath, "utf-8");
185
- pipeline = JSON.parse(content);
186
- }
187
- catch {
188
- return {
189
- content: [
190
- { type: "text", text: JSON.stringify({ error: "PIPELINE_NOT_FOUND", pipelineId }) },
191
- ],
192
- };
193
- }
194
- const now = new Date().toISOString();
195
- pipeline.status = "ROLLED_BACK";
196
- pipeline.updatedAt = now;
197
- // Mark all non-completed stages as SKIPPED
198
- for (const stage of pipeline.stages) {
199
- if (stage.status === "RUNNING" || stage.status === "PENDING") {
200
- stage.status = "SKIPPED";
201
- }
202
- }
203
- // Activate rollback stages if defined
204
- if (pipeline.rollbackStages) {
205
- for (const stage of pipeline.rollbackStages) {
206
- stage.status = "RUNNING";
207
- stage.startedAt = now;
208
- }
209
- }
210
- await fs.writeFile(pipelinePath, JSON.stringify(pipeline, null, 2), "utf-8");
211
- // Write rollback event to devops log
212
- const devopsLogPath = path.join(projectRoot, ".enderun", "logs", "devops.json");
213
- let devopsLog = { rollbacks: [] };
214
- try {
215
- const existing = await fs.readFile(devopsLogPath, "utf-8");
216
- devopsLog = JSON.parse(existing);
217
- if (!devopsLog.rollbacks)
218
- devopsLog.rollbacks = [];
219
- }
220
- catch {
221
- // Start fresh
222
- }
223
- devopsLog.rollbacks.push({ pipelineId, reason, timestamp: now });
224
- await fs.mkdir(path.dirname(devopsLogPath), { recursive: true });
225
- await fs.writeFile(devopsLogPath, JSON.stringify(devopsLog, null, 2), "utf-8");
226
- return { content: [{ type: "text", text: JSON.stringify({ pipeline, rollbackReason: reason }, null, 2) }] };
227
- },
228
- async get_pipeline_status(args, projectRoot) {
229
- const { pipelineId } = args;
230
- const pipelinePath = path.join(projectRoot, ".enderun", "queue", "pipelines", `${pipelineId}.json`);
231
- try {
232
- const content = await fs.readFile(pipelinePath, "utf-8");
233
- const pipeline = JSON.parse(content);
234
- const total = pipeline.stages.length;
235
- const completed = pipeline.stages.filter((s) => s.status === "COMPLETED").length;
236
- const running = pipeline.stages.filter((s) => s.status === "RUNNING").map((s) => s.id);
237
- const blocked = pipeline.stages.filter((s) => s.status === "PENDING" && s.dependsOn.some((dep) => {
238
- const depStage = pipeline.stages.find((st) => st.id === dep);
239
- return depStage && depStage.status !== "COMPLETED";
240
- })).map((s) => s.id);
241
- return {
242
- content: [
243
- {
244
- type: "text",
245
- text: JSON.stringify({
246
- pipelineId,
247
- name: pipeline.name,
248
- status: pipeline.status,
249
- progress: `${completed}/${total} stages (${Math.round((completed / total) * 100)}%)`,
250
- currentlyRunning: running,
251
- waitingOn: blocked,
252
- createdAt: pipeline.createdAt,
253
- updatedAt: pipeline.updatedAt,
254
- }, null, 2),
255
- },
256
- ],
257
- };
258
- }
259
- catch {
260
- return {
261
- content: [
262
- { type: "text", text: JSON.stringify({ error: "PIPELINE_NOT_FOUND", pipelineId }) },
263
- ],
264
- };
265
- }
266
- },
267
- };
@@ -1,137 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import { Project, SyntaxKind } from "ts-morph";
4
- import { collectFilesRecursively, resolveSafePath, buildLineMatches } from "../utils.js";
5
- import { SECURITY_AUDIT_ARGS_SCHEMA, ANALYZE_CONSTITUTION_COMPLIANCE_ARGS_SCHEMA } from "../schemas.js";
6
- export const securityTools = [
7
- {
8
- name: "security_audit_scan",
9
- description: "Scans the codebase for security vulnerabilities like hardcoded secrets, raw SQL, and unsafe async patterns.",
10
- inputSchema: {
11
- type: "object",
12
- properties: {
13
- path: {
14
- type: "string",
15
- description: "Path to scan (relative to project root)",
16
- default: ".",
17
- },
18
- },
19
- },
20
- },
21
- {
22
- name: "analyze_constitution_compliance",
23
- description: "Scans a file or directory for violations of the project's ENDERUN.md rules (e.g. Zero UI Library, Branded Types).",
24
- inputSchema: {
25
- type: "object",
26
- properties: {
27
- path: {
28
- type: "string",
29
- description: "Path to scan (relative to project root)",
30
- },
31
- },
32
- required: ["path"],
33
- },
34
- },
35
- ];
36
- export const securityHandlers = {
37
- security_audit_scan: async (args, projectRoot) => {
38
- const parsed = SECURITY_AUDIT_ARGS_SCHEMA.safeParse(args ?? {});
39
- if (!parsed.success)
40
- return { content: [{ type: "text", text: "Invalid path argument." }] };
41
- const vulnerabilities = [];
42
- const scanRules = [
43
- { pattern: /sql`/, message: "Potential Raw SQL usage detected (check Kysely usage)", severity: "HIGH" },
44
- { pattern: /(?<!\/\/\s*)console\.log/, message: "console.log found in production code", severity: "LOW" },
45
- { pattern: /(password|secret|api_?key)\s*[:=]\s*['"][^'"]+['"]/i, message: "Potential hardcoded secret/password detected", severity: "CRITICAL" },
46
- { pattern: /:\s*any(?!\w)/, message: "Usage of 'any' type detected", severity: "MEDIUM" },
47
- { pattern: /(?<!\w)eval\s*\(/, message: "Dangerous 'eval()' usage detected", severity: "HIGH" },
48
- { pattern: /\.innerHTML\s*=/, message: "Unsafe innerHTML assignment detected (XSS risk)", severity: "MEDIUM" },
49
- { pattern: /dangerouslySetInnerHTML/, message: "React dangerouslySetInnerHTML detected", severity: "MEDIUM" },
50
- { pattern: /TODO:/, message: "Outstanding TODO item found", severity: "LOW" },
51
- ];
52
- try {
53
- const safeTargetPath = resolveSafePath(projectRoot, parsed.data.path);
54
- if (!fs.existsSync(safeTargetPath))
55
- return { content: [{ type: "text", text: "Target path not found." }] };
56
- const files = collectFilesRecursively(safeTargetPath, new Set(["ts", "tsx"]));
57
- for (const rule of scanRules) {
58
- const ruleMatches = buildLineMatches(files, (line) => typeof rule.pattern === "string" ? line.includes(rule.pattern) : rule.pattern.test(line), 5, projectRoot);
59
- if (ruleMatches.length > 0)
60
- vulnerabilities.push(`[${rule.severity}] ${rule.message}:\n${ruleMatches.join("\n")}`);
61
- }
62
- const tsProject = new Project({ compilerOptions: { allowJs: true } });
63
- tsProject.addSourceFilesAtPaths(path.join(safeTargetPath, "**/*.{ts,tsx}"));
64
- for (const sourceFile of tsProject.getSourceFiles()) {
65
- const relativePath = path.relative(projectRoot, sourceFile.getFilePath());
66
- sourceFile.forEachDescendant((node) => {
67
- if (node.getKindName() === "AnyKeyword") {
68
- vulnerabilities.push(`[MEDIUM] Precise 'any' type detected in AST at ${relativePath}:${node.getStartLineNumber()}`);
69
- }
70
- if (node.getKind() === SyntaxKind.CallExpression) {
71
- const callExp = node.asKind(SyntaxKind.CallExpression);
72
- if (callExp?.getExpression().getText() === "console.log") {
73
- vulnerabilities.push(`[LOW] Production 'console.log' detected in AST at ${relativePath}:${node.getStartLineNumber()}`);
74
- }
75
- }
76
- });
77
- }
78
- return { content: [{ type: "text", text: vulnerabilities.length > 0 ? `### ADVANCED SECURITY AUDIT RESULTS\n\n${Array.from(new Set(vulnerabilities)).join("\n\n")}` : "No security patterns or rule violations detected (Regex & AST verified)." }] };
79
- }
80
- catch {
81
- return { content: [{ type: "text", text: "Security audit failed." }] };
82
- }
83
- },
84
- analyze_constitution_compliance: async (args, projectRoot) => {
85
- const parsed = ANALYZE_CONSTITUTION_COMPLIANCE_ARGS_SCHEMA.safeParse(args ?? {});
86
- if (!parsed.success)
87
- return { content: [{ type: "text", text: "Invalid path argument." }] };
88
- try {
89
- const safePath = resolveSafePath(projectRoot, parsed.data.path);
90
- const violations = [];
91
- const forbiddenLibraries = ["@shadcn", "mui", "@chakra-ui", "antd", "bootstrap", "tailwindcss"];
92
- if (fs.existsSync(safePath) && fs.lstatSync(safePath).isDirectory()) {
93
- const tsProject = new Project({ compilerOptions: { allowJs: true } });
94
- tsProject.addSourceFilesAtPaths(path.join(safePath, "**/*.{ts,tsx,js,jsx}"));
95
- for (const sourceFile of tsProject.getSourceFiles()) {
96
- const relativePath = path.relative(projectRoot, sourceFile.getFilePath());
97
- if (relativePath.includes("node_modules") || relativePath.includes("dist"))
98
- continue;
99
- // AST-based Import Check for Zero UI Policy
100
- sourceFile.getImportDeclarations().forEach(imp => {
101
- const moduleValue = imp.getModuleSpecifierValue();
102
- if (forbiddenLibraries.some(lib => moduleValue.includes(lib))) {
103
- violations.push(`${relativePath}: AST Violation - Zero UI Library Policy (imported '${moduleValue}')`);
104
- }
105
- });
106
- // AST-based String ID Check for Branded Types Law
107
- sourceFile.getInterfaces().forEach(iface => {
108
- const idProp = iface.getProperty("id");
109
- if (idProp && idProp.getType().isString() && !idProp.getType().getText().includes("Brand")) {
110
- violations.push(`${relativePath}: AST Violation - Branded Types Law (interface '${iface.getName()}' uses plain string for ID)`);
111
- }
112
- });
113
- const content = sourceFile.getFullText();
114
- if (content.includes("console.log") && !relativePath.includes("cli.js")) {
115
- violations.push(`${relativePath}: Text Violation - Logging Policy (found 'console.log')`);
116
- }
117
- if (content.includes("sql`")) {
118
- violations.push(`${relativePath}: Text Violation - Potential Kysely Standards (found raw 'sql' tag)`);
119
- }
120
- }
121
- }
122
- else if (fs.existsSync(safePath)) {
123
- // Fallback for single files
124
- const content = fs.readFileSync(safePath, "utf-8");
125
- const relativePath = path.relative(projectRoot, safePath);
126
- for (const lib of forbiddenLibraries) {
127
- if (content.includes(lib))
128
- violations.push(`${relativePath}: Violation of Zero UI Library Policy (found '${lib}')`);
129
- }
130
- }
131
- return { content: [{ type: "text", text: `### CONSTITUTION COMPLIANCE REPORT\n\n` + (violations.length > 0 ? `⚠️ **VIOLATIONS FOUND:**\n${violations.map(v => `- ${v}`).join("\n")}` : "✅ **ALL SYSTEMS COMPLIANT:** AST and Regex scans passed. No rule violations detected.") }] };
132
- }
133
- catch {
134
- return { content: [{ type: "text", text: "Constitution compliance check failed." }] };
135
- }
136
- },
137
- };
@@ -1,121 +0,0 @@
1
- import path from "path";
2
- import fs from "fs";
3
- import { fileURLToPath } from "url";
4
- const __filename = fileURLToPath(import.meta.url);
5
- const __dirname = path.dirname(__filename);
6
- const getPackageVersion = () => {
7
- try {
8
- const pkgPath = path.join(__dirname, "../package.json");
9
- if (fs.existsSync(pkgPath)) {
10
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
11
- return pkg.version || "0.7.5";
12
- }
13
- }
14
- catch {
15
- // fallback
16
- }
17
- return "0.6.6";
18
- };
19
- export const FRAMEWORK_VERSION = getPackageVersion();
20
- /**
21
- * Dynamically detects the framework directory for the current project.
22
- * Checks in priority order: .enderun → .gemini → .cursor → fallback .gemini
23
- * This resolves the adapter mismatch bug (KRITIK-1 in v0.7.5 analysis).
24
- */
25
- export function getFrameworkDir(projectRoot) {
26
- const candidates = [".enderun", ".gemini", ".cursor"];
27
- for (const dir of candidates) {
28
- const dirPath = path.join(projectRoot, dir);
29
- if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) {
30
- // Verify it looks like a framework dir (has PROJECT_MEMORY.md or agents/ dir)
31
- const hasMemory = fs.existsSync(path.join(dirPath, "PROJECT_MEMORY.md"));
32
- const hasAgents = fs.existsSync(path.join(dirPath, "agents"));
33
- if (hasMemory || hasAgents) {
34
- return dir;
35
- }
36
- }
37
- }
38
- // Ultimate fallback — return first existing dir or .gemini
39
- for (const dir of candidates) {
40
- if (fs.existsSync(path.join(projectRoot, dir)))
41
- return dir;
42
- }
43
- return ".gemini";
44
- }
45
- export function resolveSafePath(projectRoot, targetPath) {
46
- const resolved = path.resolve(projectRoot, targetPath);
47
- const relative = path.relative(projectRoot, resolved);
48
- if (relative.startsWith("..") || path.isAbsolute(relative)) {
49
- throw new Error("Path escapes project root.");
50
- }
51
- return resolved;
52
- }
53
- export function collectFilesRecursively(targetPath, extensions) {
54
- const results = [];
55
- const entries = fs.readdirSync(targetPath, { withFileTypes: true });
56
- for (const entry of entries) {
57
- const fullPath = path.join(targetPath, entry.name);
58
- if (entry.isDirectory()) {
59
- if (entry.name === "node_modules" || entry.name === ".git")
60
- continue;
61
- results.push(...collectFilesRecursively(fullPath, extensions));
62
- continue;
63
- }
64
- const ext = path.extname(entry.name).slice(1).toLowerCase();
65
- if (extensions.has(ext))
66
- results.push(fullPath);
67
- }
68
- return results;
69
- }
70
- export function buildLineMatches(files, matcher, maxResults, projectRoot) {
71
- const matches = [];
72
- for (const filePath of files) {
73
- if (matches.length >= maxResults)
74
- break;
75
- const content = fs.readFileSync(filePath, "utf-8");
76
- const lines = content.split("\n");
77
- for (let i = 0; i < lines.length; i++) {
78
- if (matches.length >= maxResults)
79
- break;
80
- const line = lines[i];
81
- if (!matcher(line))
82
- continue;
83
- const relativePath = path.relative(projectRoot, filePath);
84
- matches.push(`${relativePath}:${i + 1}:${line}`);
85
- }
86
- }
87
- return matches;
88
- }
89
- export function collectMarkdownArtifacts(projectRoot) {
90
- const frameworkDir = getFrameworkDir(projectRoot);
91
- const searchPaths = [
92
- path.join(projectRoot, "docs"),
93
- path.join(projectRoot, frameworkDir, "docs")
94
- ];
95
- const results = [];
96
- for (const docsRoot of searchPaths) {
97
- if (fs.existsSync(docsRoot)) {
98
- results.push(...collectFilesRecursively(docsRoot, new Set(["md"])).map((filePath) => path.relative(projectRoot, filePath)));
99
- }
100
- }
101
- return Array.from(new Set(results));
102
- }
103
- export function replaceSectionContent(markdown, sectionTitle, newBody) {
104
- const escaped = sectionTitle.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
105
- const sectionRegex = new RegExp(`## ${escaped}[\\s\\S]*?(?=\\n## |$)`);
106
- if (!sectionRegex.test(markdown)) {
107
- throw new Error(`Section not found: ${sectionTitle}`);
108
- }
109
- return markdown.replace(sectionRegex, `## ${sectionTitle}\n\n${newBody.trim()}\n`);
110
- }
111
- export function prependToSection(markdown, sectionTitle, contentToPrepend) {
112
- const escaped = sectionTitle.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
113
- const sectionRegex = new RegExp(`(## ${escaped}\\n)([\\s\\S]*?)(?=\\n## |$)`);
114
- const match = markdown.match(sectionRegex);
115
- if (!match) {
116
- throw new Error(`Section not found: ${sectionTitle}`);
117
- }
118
- const currentBody = match[2].trimStart();
119
- const updatedBody = `${contentToPrepend.trim()}\n\n${currentBody}`.trim();
120
- return markdown.replace(sectionRegex, `$1\n${updatedBody}\n`);
121
- }
@@ -1,49 +0,0 @@
1
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
- import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
4
- import { allTools, allHandlers } from "./tools/index.js";
5
- import { FRAMEWORK_VERSION } from "./utils.js";
6
-
7
- const server = new Server({
8
- name: "ai-enderun-mcp",
9
- version: FRAMEWORK_VERSION,
10
- }, {
11
- capabilities: {
12
- tools: {},
13
- },
14
- });
15
-
16
- server.setRequestHandler(ListToolsRequestSchema, async () => {
17
- return {
18
- tools: allTools,
19
- };
20
- });
21
-
22
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
23
- const { name, arguments: args } = request.params;
24
- const projectRoot = process.cwd();
25
-
26
- // Handle compatibility for context search
27
- let effectiveArgs = args;
28
- if (name === "codebase_context_search") {
29
- effectiveArgs = { extension: "md", ...(args ?? {}) };
30
- }
31
-
32
- const handler = allHandlers[name];
33
- if (handler) {
34
- return await handler(effectiveArgs, projectRoot);
35
- }
36
-
37
- throw new Error(`Tool not found: ${name}`);
38
- });
39
-
40
- async function main() {
41
- const transport = new StdioServerTransport();
42
- await server.connect(transport);
43
- console.error("Framework MCP Server running on stdio");
44
- }
45
-
46
- main().catch((error) => {
47
- console.error("Fatal error in main():", error);
48
- process.exit(1);
49
- });
@@ -1,106 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export const SECURITY_AUDIT_ARGS_SCHEMA = z.object({
4
- path: z.string().default("."),
5
- });
6
-
7
- export const SEARCH_CODEBASE_ARGS_SCHEMA = z.object({
8
- query: z.string().min(1).max(300),
9
- extension: z
10
- .string()
11
- .default("ts")
12
- .refine((s) => /^[a-z0-9]+$/i.test(s), { message: "extension must be alphanumeric" }),
13
- });
14
-
15
- export const UPDATE_MEMORY_ARGS_SCHEMA = z.object({
16
- section: z.enum(["CURRENT STATUS", "HISTORY", "ACTIVE TASKS"]),
17
- content: z.string().min(1),
18
- });
19
-
20
- export const ANALYZE_DEPENDENCIES_ARGS_SCHEMA = z.object({
21
- path: z.string().min(1),
22
- });
23
-
24
- export const LOG_AGENT_ACTION_ARGS_SCHEMA = z.object({
25
- agent: z.string().min(1),
26
- action: z.string().min(1),
27
- requestId: z.string().min(1),
28
- files: z.array(z.string()).default([]),
29
- status: z.enum(["SUCCESS", "FAILURE"]),
30
- summary: z.string().min(1),
31
- details: z.record(z.string(), z.any()).default({}),
32
- });
33
-
34
- export const SEND_AGENT_MESSAGE_ARGS_SCHEMA = z.object({
35
- from: z.string().min(1),
36
- to: z.string().min(1),
37
- message: z.string().min(1),
38
- traceId: z.string().min(1),
39
- category: z.enum(["ACTION", "DELEGATION", "INFO", "ALERT"]).default("INFO"),
40
- priority: z.enum(["LOW", "MEDIUM", "HIGH", "URGENT"]).default("MEDIUM"),
41
- });
42
-
43
- export const UPDATE_AGENT_MESSAGE_STATUS_ARGS_SCHEMA = z.object({
44
- agent: z.string().min(1),
45
- traceId: z.string().min(1),
46
- newStatus: z.enum(["ACKNOWLEDGED", "COMPLETED", "REJECTED"]),
47
- });
48
-
49
- export const SEARCH_KNOWLEDGE_BASE_ARGS_SCHEMA = z.object({
50
- query: z.string().min(1),
51
- });
52
-
53
- export const UPDATE_KNOWLEDGE_BASE_ARGS_SCHEMA = z.object({
54
- topic: z.string().min(1),
55
- content: z.string().min(1),
56
- tags: z.array(z.string().min(1)).default([]),
57
- });
58
-
59
- export const ANALYZE_CONSTITUTION_COMPLIANCE_ARGS_SCHEMA = z.object({
60
- path: z.string().min(1),
61
- });
62
-
63
- export const GENERATE_DEPENDENCY_GRAPH_ARGS_SCHEMA = z.object({
64
- path: z.string().default("src"),
65
- format: z.enum(["mermaid", "json"]).default("mermaid"),
66
- });
67
-
68
- export const READ_AGENT_MESSAGES_ARGS_SCHEMA = z.object({
69
- agent: z.string().min(1),
70
- traceId: z.string().min(1).optional(),
71
- });
72
-
73
- export const VERIFY_CONTRACT_INTEGRITY_ARGS_SCHEMA = z.object({
74
- domain: z.string().min(1),
75
- });
76
-
77
- export const GENERATE_SEMANTIC_COMMIT_MESSAGE_ARGS_SCHEMA = z.object({
78
- traceId: z.string().min(1),
79
- });
80
-
81
- export const ANALYZE_CODEBASE_INTELLIGENCE_ARGS_SCHEMA = z.object({
82
- path: z.string().default("."),
83
- });
84
-
85
- export const ANALYZE_PROCEDURAL_CONTINUITY_ARGS_SCHEMA = z.object({
86
- targetPath: z.string().min(1),
87
- referencePath: z.string().min(1),
88
- });
89
-
90
- export const GET_AGENT_INBOX_STATS_ARGS_SCHEMA = z.object({
91
- agent: z.string().min(1),
92
- });
93
-
94
- export const GET_KNOWLEDGE_GRAPH_ARGS_SCHEMA = z.object({
95
- tag: z.string().optional(),
96
- });
97
-
98
- export const SYNC_CONTRACT_HASH_ARGS_SCHEMA = z.object({
99
- force: z.boolean().default(false),
100
- });
101
-
102
- export const VERIFY_FRAMEWORK_HEALTH_ARGS_SCHEMA = z.object({
103
- detailed: z.boolean().default(false),
104
- });
105
-
106
-