agent-enderun 0.5.8 → 0.6.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 (75) hide show
  1. package/.enderun/PROJECT_MEMORY.md +16 -151
  2. package/.enderun/STATUS.md +19 -10
  3. package/.enderun/agents/analyst.md +19 -6
  4. package/.enderun/agents/backend.md +66 -48
  5. package/.enderun/agents/explorer.md +14 -2
  6. package/.enderun/agents/frontend.md +59 -14
  7. package/.enderun/agents/git.md +1 -1
  8. package/.enderun/agents/manager.md +10 -2
  9. package/.enderun/agents/mobile.md +1 -1
  10. package/.enderun/agents/native.md +1 -1
  11. package/.enderun/blueprints/backend/errors/domain-error.ts +84 -0
  12. package/.enderun/blueprints/backend/middleware/error-handler.ts +24 -0
  13. package/.enderun/blueprints/backend/types/api.ts +20 -0
  14. package/.enderun/blueprints/backend/types/brands.ts +12 -0
  15. package/.enderun/blueprints/backend/types/constants.ts +34 -0
  16. package/.enderun/blueprints/backend/types/index.ts +49 -0
  17. package/.enderun/blueprints/backend/types/logs.ts +16 -0
  18. package/.enderun/blueprints/backend/types/models.ts +65 -0
  19. package/.enderun/blueprints/frontend/ui/Button.tsx +60 -0
  20. package/.enderun/blueprints/frontend/ui/Input.tsx +43 -0
  21. package/.enderun/docs/api/README.md +7 -12
  22. package/.enderun/docs/project-docs.md +17 -7
  23. package/.enderun/knowledge/blueprint_driven_construction.md +22 -0
  24. package/.enderun/knowledge/code_review_checklist.md +1 -1
  25. package/.enderun/knowledge/contract_versioning.md +5 -5
  26. package/ENDERUN.md +29 -6
  27. package/README.md +45 -23
  28. package/bin/cli.js +205 -27
  29. package/bin/update-contract.js +3 -0
  30. package/{packages/framework-mcp → framework-mcp}/dist/index.js +0 -0
  31. package/{packages/framework-mcp → framework-mcp}/dist/schemas.js +12 -0
  32. package/{packages/framework-mcp → framework-mcp}/dist/tools/contract.js +16 -10
  33. package/{packages/framework-mcp → framework-mcp}/dist/tools/framework.js +5 -3
  34. package/{packages/framework-mcp → framework-mcp}/dist/tools/git.js +2 -4
  35. package/{packages/framework-mcp → framework-mcp}/dist/tools/index.js +3 -0
  36. package/{packages/framework-mcp → framework-mcp}/dist/tools/knowledge.js +1 -1
  37. package/{packages/framework-mcp → framework-mcp}/dist/tools/repository.js +1 -1
  38. package/framework-mcp/dist/tools/scaffold.js +129 -0
  39. package/{packages/framework-mcp → framework-mcp}/dist/utils.js +1 -1
  40. package/{packages/framework-mcp → framework-mcp}/package.json +4 -1
  41. package/{packages/framework-mcp → framework-mcp}/src/schemas.ts +15 -0
  42. package/{packages/framework-mcp → framework-mcp}/src/tools/contract.ts +22 -9
  43. package/{packages/framework-mcp → framework-mcp}/src/tools/framework.ts +6 -4
  44. package/{packages/framework-mcp → framework-mcp}/src/tools/git.ts +2 -3
  45. package/{packages/framework-mcp → framework-mcp}/src/tools/index.ts +3 -0
  46. package/{packages/framework-mcp → framework-mcp}/src/tools/knowledge.ts +6 -6
  47. package/{packages/framework-mcp → framework-mcp}/src/tools/repository.ts +1 -1
  48. package/framework-mcp/src/tools/scaffold.ts +147 -0
  49. package/{packages/framework-mcp → framework-mcp}/src/utils.ts +1 -1
  50. package/package.json +8 -12
  51. package/panda.config.ts +79 -5
  52. package/.enderun/logs/manager.json +0 -18
  53. package/packages/shared-types/README.md +0 -61
  54. package/packages/shared-types/contract.version.json +0 -12
  55. package/packages/shared-types/dist/index.d.ts +0 -92
  56. package/packages/shared-types/dist/index.js +0 -8
  57. package/packages/shared-types/dist/index.js.map +0 -1
  58. package/packages/shared-types/package.json +0 -33
  59. package/packages/shared-types/src/index.ts +0 -102
  60. package/packages/shared-types/tsconfig.json +0 -9
  61. /package/{packages/framework-mcp → framework-mcp}/README.md +0 -0
  62. /package/{packages/framework-mcp → framework-mcp}/dist/tools/academy.js +0 -0
  63. /package/{packages/framework-mcp → framework-mcp}/dist/tools/codebase.js +0 -0
  64. /package/{packages/framework-mcp → framework-mcp}/dist/tools/database.js +0 -0
  65. /package/{packages/framework-mcp → framework-mcp}/dist/tools/memory.js +0 -0
  66. /package/{packages/framework-mcp → framework-mcp}/dist/tools/messages.js +0 -0
  67. /package/{packages/framework-mcp → framework-mcp}/dist/tools/security.js +0 -0
  68. /package/{packages/framework-mcp → framework-mcp}/src/index.ts +0 -0
  69. /package/{packages/framework-mcp → framework-mcp}/src/tools/academy.ts +0 -0
  70. /package/{packages/framework-mcp → framework-mcp}/src/tools/codebase.ts +0 -0
  71. /package/{packages/framework-mcp → framework-mcp}/src/tools/database.ts +0 -0
  72. /package/{packages/framework-mcp → framework-mcp}/src/tools/memory.ts +0 -0
  73. /package/{packages/framework-mcp → framework-mcp}/src/tools/messages.ts +0 -0
  74. /package/{packages/framework-mcp → framework-mcp}/src/tools/security.ts +0 -0
  75. /package/{packages/framework-mcp → framework-mcp}/tsconfig.json +0 -0
@@ -0,0 +1,129 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { resolveSafePath, getFrameworkDir } from "../utils.js";
4
+ import { INSTANTIATE_BLUEPRINT_ARGS_SCHEMA, LIST_BLUEPRINTS_ARGS_SCHEMA, SAVE_AS_BLUEPRINT_ARGS_SCHEMA } from "../schemas.js";
5
+ export const scaffoldTools = [
6
+ {
7
+ name: "instantiate_blueprint",
8
+ description: "Clones a Gold Standard capability from .enderun/blueprints into the application source.",
9
+ inputSchema: {
10
+ type: "object",
11
+ properties: {
12
+ blueprintPath: { type: "string", description: "Relative path within blueprints/ (e.g., backend/errors)" },
13
+ targetPath: { type: "string", description: "Target directory in the app (e.g., apps/backend/src/errors)" },
14
+ },
15
+ required: ["blueprintPath", "targetPath"],
16
+ },
17
+ },
18
+ {
19
+ name: "save_as_blueprint",
20
+ description: "Saves or updates a high-quality code pattern as a Gold Standard blueprint for reuse.",
21
+ inputSchema: {
22
+ type: "object",
23
+ properties: {
24
+ blueprintPath: { type: "string", description: "Target relative path in blueprints/ (e.g., frontend/ui/Button.tsx)" },
25
+ content: { type: "string", description: "The full source code of the blueprint" },
26
+ description: { type: "string", description: "Brief description of what this blueprint provides" },
27
+ },
28
+ required: ["blueprintPath", "content"],
29
+ },
30
+ },
31
+ {
32
+ name: "list_blueprints",
33
+ description: "Lists all available Gold Standard blueprints in the framework.",
34
+ inputSchema: {
35
+ type: "object",
36
+ properties: {
37
+ category: { type: "string", enum: ["backend", "frontend"], description: "Optional category to filter blueprints" },
38
+ },
39
+ },
40
+ },
41
+ ];
42
+ export const scaffoldHandlers = {
43
+ instantiate_blueprint: async (args, projectRoot) => {
44
+ const parsed = INSTANTIATE_BLUEPRINT_ARGS_SCHEMA.safeParse(args);
45
+ if (!parsed.success)
46
+ return { content: [{ type: "text", text: `Invalid arguments: ${parsed.error.message}` }] };
47
+ const { blueprintPath, targetPath } = parsed.data;
48
+ const frameworkDir = getFrameworkDir(projectRoot);
49
+ const sourceDir = resolveSafePath(projectRoot, path.join(frameworkDir, "blueprints", blueprintPath));
50
+ const destDir = resolveSafePath(projectRoot, targetPath);
51
+ try {
52
+ if (!fs.existsSync(sourceDir)) {
53
+ return { content: [{ type: "text", text: `Blueprint not found: ${blueprintPath}` }] };
54
+ }
55
+ // Create target directory if it doesn't exist
56
+ fs.mkdirSync(path.dirname(destDir), { recursive: true });
57
+ // Copy recursively
58
+ // Using cpSync which is available in Node.js 16.7.0+
59
+ fs.cpSync(sourceDir, destDir, { recursive: true });
60
+ return { content: [{ type: "text", text: `Successfully instantiated blueprint '${blueprintPath}' into '${targetPath}'` }] };
61
+ }
62
+ catch (error) {
63
+ return { content: [{ type: "text", text: `Scaffold failed: ${error.message}` }] };
64
+ }
65
+ },
66
+ save_as_blueprint: async (args, projectRoot) => {
67
+ const parsed = SAVE_AS_BLUEPRINT_ARGS_SCHEMA.safeParse(args);
68
+ if (!parsed.success)
69
+ return { content: [{ type: "text", text: `Invalid arguments: ${parsed.error.message}` }] };
70
+ const { blueprintPath, content, description } = parsed.data;
71
+ const frameworkDir = getFrameworkDir(projectRoot);
72
+ const targetFile = resolveSafePath(projectRoot, path.join(frameworkDir, "blueprints", blueprintPath));
73
+ try {
74
+ // Ensure the blueprints subdirectory exists
75
+ fs.mkdirSync(path.dirname(targetFile), { recursive: true });
76
+ // Write the blueprint content
77
+ fs.writeFileSync(targetFile, content, "utf-8");
78
+ // Optionally write a .metadata file if description provided
79
+ if (description) {
80
+ const metaFile = `${targetFile}.metadata.json`;
81
+ fs.writeFileSync(metaFile, JSON.stringify({ description, savedAt: new Date().toISOString() }, null, 2), "utf-8");
82
+ }
83
+ return { content: [{ type: "text", text: `Successfully saved blueprint to '${blueprintPath}'` }] };
84
+ }
85
+ catch (error) {
86
+ return { content: [{ type: "text", text: `Failed to save blueprint: ${error.message}` }] };
87
+ }
88
+ },
89
+ list_blueprints: async (args, projectRoot) => {
90
+ const parsed = LIST_BLUEPRINTS_ARGS_SCHEMA.safeParse(args ?? {});
91
+ const category = parsed.success ? parsed.data.category : undefined;
92
+ const frameworkDir = getFrameworkDir(projectRoot);
93
+ const blueprintsRoot = path.join(projectRoot, frameworkDir, "blueprints");
94
+ try {
95
+ if (!fs.existsSync(blueprintsRoot)) {
96
+ return { content: [{ type: "text", text: "Blueprints directory not found." }] };
97
+ }
98
+ const walk = (dir) => {
99
+ const results = [];
100
+ if (!fs.existsSync(dir))
101
+ return results;
102
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
103
+ for (const entry of entries) {
104
+ const res = path.join(dir, entry.name);
105
+ if (entry.isDirectory()) {
106
+ // If it's a leaf directory containing files, or we want to show all dirs
107
+ const subEntries = fs.readdirSync(res);
108
+ const hasFiles = subEntries.some(e => !fs.lstatSync(path.join(res, e)).isDirectory());
109
+ if (hasFiles) {
110
+ results.push(path.relative(blueprintsRoot, res));
111
+ }
112
+ results.push(...walk(res));
113
+ }
114
+ }
115
+ return results;
116
+ };
117
+ let available = walk(blueprintsRoot);
118
+ if (category) {
119
+ available = available.filter(b => b.startsWith(category));
120
+ }
121
+ // Remove duplicates
122
+ available = Array.from(new Set(available));
123
+ return { content: [{ type: "text", text: `### AVAILABLE BLUEPRINTS\n\n` + available.map(b => `- ${b}`).join("\n") }] };
124
+ }
125
+ catch (error) {
126
+ return { content: [{ type: "text", text: `Listing blueprints failed: ${error.message}` }] };
127
+ }
128
+ },
129
+ };
@@ -1,6 +1,6 @@
1
1
  import path from "path";
2
2
  import fs from "fs";
3
- export const FRAMEWORK_VERSION = "0.5.8";
3
+ export const FRAMEWORK_VERSION = "0.6.0";
4
4
  export function getFrameworkDir(projectRoot) {
5
5
  const adapters = [".gemini", ".claude", ".cursor", ".enderun", ".codex"];
6
6
  for (const adp of adapters) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-enderun-mcp",
3
- "version": "0.5.8",
3
+ "version": "0.6.0",
4
4
  "description": "Enterprise-grade MCP Server for AI Agent Framework",
5
5
  "author": "Yusuf BEKAR",
6
6
  "license": "MIT",
@@ -25,6 +25,9 @@
25
25
  "dist",
26
26
  "README.md"
27
27
  ],
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
28
31
  "scripts": {
29
32
  "build": "tsc -p tsconfig.json",
30
33
  "start": "node dist/index.js",
@@ -124,3 +124,18 @@ export const SYNC_CONTRACT_HASH_ARGS_SCHEMA = z.object({
124
124
  export const VERIFY_FRAMEWORK_HEALTH_ARGS_SCHEMA = z.object({
125
125
  detailed: z.boolean().default(false),
126
126
  });
127
+
128
+ export const INSTANTIATE_BLUEPRINT_ARGS_SCHEMA = z.object({
129
+ blueprintPath: z.string().min(1),
130
+ targetPath: z.string().min(1),
131
+ });
132
+
133
+ export const LIST_BLUEPRINTS_ARGS_SCHEMA = z.object({
134
+ category: z.enum(["backend", "frontend"]).optional(),
135
+ });
136
+
137
+ export const SAVE_AS_BLUEPRINT_ARGS_SCHEMA = z.object({
138
+ blueprintPath: z.string().min(1),
139
+ content: z.string().min(1),
140
+ description: z.string().optional(),
141
+ });
@@ -50,8 +50,8 @@ export const contractTools = [
50
50
  export const contractHandlers = {
51
51
  verify_api_contract: async (args: unknown, projectRoot: string) => {
52
52
  try {
53
- const sharedTypesDir = path.join(projectRoot, "packages/shared-types/src");
54
- const contractJsonPath = path.join(projectRoot, "packages/shared-types/contract.version.json");
53
+ const sharedTypesDir = path.join(projectRoot, "apps/backend/src/types");
54
+ const contractJsonPath = path.join(projectRoot, "apps/backend/contract.version.json");
55
55
  if (!fs.existsSync(sharedTypesDir) || !fs.existsSync(contractJsonPath)) return { content: [{ type: "text", text: "Missing shared-types directory or contract.version.json" }] };
56
56
  const currentHash = calculateContractHash(sharedTypesDir, projectRoot);
57
57
  const storedHash = JSON.parse(fs.readFileSync(contractJsonPath, "utf-8")).contract_hash;
@@ -62,8 +62,8 @@ export const contractHandlers = {
62
62
  },
63
63
  update_contract_hash: async (args: unknown, projectRoot: string) => {
64
64
  try {
65
- const sharedTypesDir = path.join(projectRoot, "packages/shared-types/src");
66
- const contractJsonPath = path.join(projectRoot, "packages/shared-types/contract.version.json");
65
+ const sharedTypesDir = path.join(projectRoot, "apps/backend/src/types");
66
+ const contractJsonPath = path.join(projectRoot, "apps/backend/contract.version.json");
67
67
  if (!fs.existsSync(sharedTypesDir)) return { content: [{ type: "text", text: "Missing shared-types directory" }] };
68
68
  const files = collectFilesRecursively(sharedTypesDir, new Set(["ts"])).sort();
69
69
  if (files.length === 0) return { content: [{ type: "text", text: "⚠️ WARNING: No TypeScript files found in shared-types/src. Hash not updated." }] };
@@ -83,13 +83,26 @@ export const contractHandlers = {
83
83
  try {
84
84
  const frameworkDir = getFrameworkDir(projectRoot);
85
85
  const apiDocPath = path.join(projectRoot, frameworkDir, "docs/api", `${parsed.data.domain}.md`);
86
- const sharedTypesPath = path.join(projectRoot, "packages/shared-types/src/index.ts");
86
+ const sharedTypesDir = path.join(projectRoot, "apps/backend/src/types");
87
+
87
88
  if (!fs.existsSync(apiDocPath)) return { content: [{ type: "text", text: `API documentation not found for domain: ${parsed.data.domain}` }] };
88
- if (!fs.existsSync(sharedTypesPath)) return { content: [{ type: "text", text: "Shared types index.ts not found." }] };
89
- const sourceFile = new Project().addSourceFileAtPath(sharedTypesPath);
90
- 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));
89
+ if (!fs.existsSync(sharedTypesDir)) return { content: [{ type: "text", text: "Shared types directory not found." }] };
90
+
91
+ const project = new Project();
92
+ project.addSourceFilesAtPaths(path.join(sharedTypesDir, "**/*.ts"));
93
+
94
+ const mentionedTypes = Array.from(fs.readFileSync(apiDocPath, "utf-8").matchAll(/`([^`]+)`/g))
95
+ .map(m => m[1])
96
+ .filter(t => /^[A-Z][a-zA-Z0-9]+(DTO|Response|Request|Status|Type)?$/.test(t));
97
+
91
98
  const uniqueTypes = Array.from(new Set(mentionedTypes));
92
- const missingTypes = uniqueTypes.filter(t => !sourceFile.getInterface(t) && !sourceFile.getTypeAlias(t) && !sourceFile.getEnum(t) && !sourceFile.getClass(t));
99
+ const missingTypes = uniqueTypes.filter(t => {
100
+ const found = project.getSourceFiles().some(sf =>
101
+ sf.getInterface(t) || sf.getTypeAlias(t) || sf.getEnum(t) || sf.getClass(t)
102
+ );
103
+ return !found;
104
+ });
105
+
93
106
  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"}` }] };
94
107
  } catch (error) {
95
108
  return { content: [{ type: "text", text: "Contract verification failed." }] };
@@ -102,8 +102,9 @@ export const frameworkHandlers = {
102
102
  const missing: string[] = [];
103
103
  const frameworkDir = getFrameworkDir(projectRoot);
104
104
  const checkPaths = [
105
- { path: "apps", type: "folder", optional: true },
106
- { path: "packages/shared-types/src", type: "folder" },
105
+ { path: "apps/backend/src/types", type: "folder" },
106
+ { path: "apps/web/src/types", type: "folder" },
107
+ { path: "framework-mcp/src", type: "folder" },
107
108
  { path: path.join(frameworkDir, "docs/api"), type: "folder", optional: true },
108
109
  { path: ".env", type: "file", optional: true },
109
110
  { path: ".env.example", type: "file" },
@@ -174,8 +175,9 @@ export const frameworkHandlers = {
174
175
  const cliPath = path.join(projectRoot, "bin/cli.js");
175
176
  const output = execSync(`node ${cliPath} check`, { cwd: projectRoot, encoding: "utf-8", stdio: "pipe" });
176
177
  return { content: [{ type: "text", text: `### FRAMEWORK HEALTH CHECK\n\n${output}` }] };
177
- } catch (error: any) {
178
- return { content: [{ type: "text", text: `### FRAMEWORK HEALTH CHECK (FAILED)\n\n${error.stdout || error.message}` }] };
178
+ } catch (error: unknown) {
179
+ const err = error as { stdout?: string; message?: string };
180
+ return { content: [{ type: "text", text: `### FRAMEWORK HEALTH CHECK (FAILED)\n\n${err.stdout || err.message}` }] };
179
181
  }
180
182
  }
181
183
  };
@@ -26,14 +26,13 @@ export const gitHandlers = {
26
26
  const files = execSync("git diff --staged --name-only", { encoding: "utf-8", cwd: projectRoot }).split("\n").filter(Boolean);
27
27
  let type = "feat", scope = "code";
28
28
  if (files.some(f => f.includes(".md"))) type = "docs";
29
- else if (files.some(f => f.includes("shared-types"))) type = "arch";
29
+ else if (files.some(f => f.includes("apps/backend/src/types"))) type = "arch";
30
30
  else if (files.some(f => f.includes("framework-mcp"))) type = "chore";
31
31
  else if (files.some(f => f.includes("bin/cli.js"))) type = "fix";
32
32
  else if (files.some(f => f.includes("test"))) type = "test";
33
33
  if (files.some(f => f.includes("apps/web"))) scope = "web";
34
34
  else if (files.some(f => f.includes("apps/backend"))) scope = "backend";
35
- else if (files.some(f => f.includes("packages/shared-types"))) scope = "contract";
36
- else if (files.some(f => f.includes("packages/framework-mcp"))) scope = "mcp";
35
+ else if (files.some(f => f.includes("framework-mcp"))) scope = "mcp";
37
36
  const summary = files.length === 1 ? `update ${path.basename(files[0])}` : `update ${files.length} files`;
38
37
  return { content: [{ type: "text", text: `### SUGGESTED SEMANTIC COMMIT MESSAGE\n\n\`[${parsed.data.traceId}] ${type}(${scope}): ${summary}\`\n\n**Files Analyzed:**\n${files.map(f => `- ${f}`).join("\n")}` }] };
39
38
  } catch (error) {
@@ -9,6 +9,7 @@ import { gitTools, gitHandlers } from "./git.js";
9
9
  import { databaseTools, databaseHandlers } from "./database.js";
10
10
  import { knowledgeTools, knowledgeHandlers } from "./knowledge.js";
11
11
  import { repositoryTools, repositoryHandlers } from "./repository.js";
12
+ import { scaffoldTools, scaffoldHandlers } from "./scaffold.js";
12
13
 
13
14
  export const allTools = [
14
15
  ...codebaseTools,
@@ -22,6 +23,7 @@ export const allTools = [
22
23
  ...databaseTools,
23
24
  ...knowledgeTools,
24
25
  ...repositoryTools,
26
+ ...scaffoldTools,
25
27
  ];
26
28
 
27
29
  export const allHandlers: Record<string, (args: unknown, projectRoot: string) => Promise<unknown>> = {
@@ -36,6 +38,7 @@ export const allHandlers: Record<string, (args: unknown, projectRoot: string) =>
36
38
  ...databaseHandlers,
37
39
  ...knowledgeHandlers,
38
40
  ...repositoryHandlers,
41
+ ...scaffoldHandlers,
39
42
  };
40
43
 
41
44
  // Add compatibility aliases
@@ -48,15 +48,15 @@ function parseFrontmatter(content: string) {
48
48
  const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
49
49
  if (!match) return { metadata: {}, body: content };
50
50
  const yaml = match[1];
51
- const metadata: Record<string, any> = {};
51
+ const metadata: Record<string, string | undefined> = {};
52
52
  yaml.split("\n").forEach(line => {
53
53
  const [key, ...val] = line.split(":");
54
54
  if (key && val.length > 0) metadata[key.trim()] = val.join(":").trim();
55
55
  });
56
56
  return { metadata, body: content.replace(match[0], "").trim() };
57
- }
57
+ }
58
58
 
59
- export const knowledgeHandlers = {
59
+ export const knowledgeHandlers = {
60
60
  search_knowledge_base: async (args: unknown, projectRoot: string) => {
61
61
  const parsed = SEARCH_KNOWLEDGE_BASE_ARGS_SCHEMA.safeParse(args ?? {});
62
62
  if (!parsed.success) return { content: [{ type: "text", text: "Invalid search query." }] };
@@ -64,12 +64,12 @@ export const knowledgeHandlers = {
64
64
  const frameworkDir = getFrameworkDir(projectRoot);
65
65
  const kbDir = path.join(projectRoot, frameworkDir, "knowledge");
66
66
  if (!fs.existsSync(kbDir)) return { content: [{ type: "text", text: "Knowledge base is empty." }] };
67
-
67
+
68
68
  const query = parsed.data.query.toLowerCase();
69
69
  const results = fs.readdirSync(kbDir).filter(f => f.endsWith(".md")).map(file => {
70
70
  const content = fs.readFileSync(path.join(kbDir, file), "utf-8");
71
71
  const { metadata, body } = parseFrontmatter(content);
72
-
72
+
73
73
  const matchesQuery = body.toLowerCase().includes(query) ||
74
74
  file.toLowerCase().includes(query) ||
75
75
  (metadata.tags && metadata.tags.toLowerCase().includes(query)) ||
@@ -127,7 +127,7 @@ export const knowledgeHandlers = {
127
127
  mermaid += ` ${id}["${label}"]\n`;
128
128
 
129
129
  if (metadata.related) {
130
- const related = metadata.related.replace(/[\[\]]/g, "").split(",");
130
+ const related = metadata.related.replace(/[[\]]/g, "").split(",");
131
131
  related.forEach((r: string) => {
132
132
  mermaid += ` ${id} --> ${r.trim().replace(".md", "")}\n`;
133
133
  });
@@ -93,7 +93,7 @@ export const repositoryHandlers = {
93
93
  });
94
94
  });
95
95
  }
96
- const majorDirs = ["apps/backend", "apps/web", "packages/shared-types", "packages/framework-mcp"], missingREADME = majorDirs.filter(dir => fs.existsSync(path.join(projectRoot, dir)) && !fs.existsSync(path.join(projectRoot, dir, "README.md")));
96
+ const majorDirs = ["apps/backend", "apps/web", "framework-mcp"], missingREADME = majorDirs.filter(dir => fs.existsSync(path.join(projectRoot, dir)) && !fs.existsSync(path.join(projectRoot, dir, "README.md")));
97
97
  return { content: [{ type: "text", text: `### DOCUMENTATION DEBT REPORT\n\n**Missing JSDoc:**\n${missingJSDoc.slice(0, 10).join("\n")}\n\n**Missing README.md:**\n${missingREADME.join("\n")}` }] };
98
98
  } catch (error) {
99
99
  return { content: [{ type: "text", text: "Documentation debt scan failed." }] };
@@ -0,0 +1,147 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import {
4
+ resolveSafePath,
5
+ getFrameworkDir
6
+ } from "../utils.js";
7
+ import {
8
+ INSTANTIATE_BLUEPRINT_ARGS_SCHEMA,
9
+ LIST_BLUEPRINTS_ARGS_SCHEMA,
10
+ SAVE_AS_BLUEPRINT_ARGS_SCHEMA
11
+ } from "../schemas.js";
12
+
13
+ export const scaffoldTools = [
14
+ {
15
+ name: "instantiate_blueprint",
16
+ description: "Clones a Gold Standard capability from .enderun/blueprints into the application source.",
17
+ inputSchema: {
18
+ type: "object",
19
+ properties: {
20
+ blueprintPath: { type: "string", description: "Relative path within blueprints/ (e.g., backend/errors)" },
21
+ targetPath: { type: "string", description: "Target directory in the app (e.g., apps/backend/src/errors)" },
22
+ },
23
+ required: ["blueprintPath", "targetPath"],
24
+ },
25
+ },
26
+ {
27
+ name: "save_as_blueprint",
28
+ description: "Saves or updates a high-quality code pattern as a Gold Standard blueprint for reuse.",
29
+ inputSchema: {
30
+ type: "object",
31
+ properties: {
32
+ blueprintPath: { type: "string", description: "Target relative path in blueprints/ (e.g., frontend/ui/Button.tsx)" },
33
+ content: { type: "string", description: "The full source code of the blueprint" },
34
+ description: { type: "string", description: "Brief description of what this blueprint provides" },
35
+ },
36
+ required: ["blueprintPath", "content"],
37
+ },
38
+ },
39
+ {
40
+ name: "list_blueprints",
41
+ description: "Lists all available Gold Standard blueprints in the framework.",
42
+ inputSchema: {
43
+ type: "object",
44
+ properties: {
45
+ category: { type: "string", enum: ["backend", "frontend"], description: "Optional category to filter blueprints" },
46
+ },
47
+ },
48
+ },
49
+ ];
50
+
51
+ export const scaffoldHandlers = {
52
+ instantiate_blueprint: async (args: unknown, projectRoot: string) => {
53
+ const parsed = INSTANTIATE_BLUEPRINT_ARGS_SCHEMA.safeParse(args);
54
+ if (!parsed.success) return { content: [{ type: "text", text: `Invalid arguments: ${parsed.error.message}` }] };
55
+
56
+ const { blueprintPath, targetPath } = parsed.data;
57
+ const frameworkDir = getFrameworkDir(projectRoot);
58
+ const sourceDir = resolveSafePath(projectRoot, path.join(frameworkDir, "blueprints", blueprintPath));
59
+ const destDir = resolveSafePath(projectRoot, targetPath);
60
+
61
+ try {
62
+ if (!fs.existsSync(sourceDir)) {
63
+ return { content: [{ type: "text", text: `Blueprint not found: ${blueprintPath}` }] };
64
+ }
65
+
66
+ // Create target directory if it doesn't exist
67
+ fs.mkdirSync(path.dirname(destDir), { recursive: true });
68
+
69
+ // Copy recursively
70
+ // Using cpSync which is available in Node.js 16.7.0+
71
+ fs.cpSync(sourceDir, destDir, { recursive: true });
72
+
73
+ return { content: [{ type: "text", text: `Successfully instantiated blueprint '${blueprintPath}' into '${targetPath}'` }] };
74
+ } catch (error: any) {
75
+ return { content: [{ type: "text", text: `Scaffold failed: ${error.message}` }] };
76
+ }
77
+ },
78
+ save_as_blueprint: async (args: unknown, projectRoot: string) => {
79
+ const parsed = SAVE_AS_BLUEPRINT_ARGS_SCHEMA.safeParse(args);
80
+ if (!parsed.success) return { content: [{ type: "text", text: `Invalid arguments: ${parsed.error.message}` }] };
81
+
82
+ const { blueprintPath, content, description } = parsed.data;
83
+ const frameworkDir = getFrameworkDir(projectRoot);
84
+ const targetFile = resolveSafePath(projectRoot, path.join(frameworkDir, "blueprints", blueprintPath));
85
+
86
+ try {
87
+ // Ensure the blueprints subdirectory exists
88
+ fs.mkdirSync(path.dirname(targetFile), { recursive: true });
89
+
90
+ // Write the blueprint content
91
+ fs.writeFileSync(targetFile, content, "utf-8");
92
+
93
+ // Optionally write a .metadata file if description provided
94
+ if (description) {
95
+ const metaFile = `${targetFile}.metadata.json`;
96
+ fs.writeFileSync(metaFile, JSON.stringify({ description, savedAt: new Date().toISOString() }, null, 2), "utf-8");
97
+ }
98
+
99
+ return { content: [{ type: "text", text: `Successfully saved blueprint to '${blueprintPath}'` }] };
100
+ } catch (error: any) {
101
+ return { content: [{ type: "text", text: `Failed to save blueprint: ${error.message}` }] };
102
+ }
103
+ },
104
+ list_blueprints: async (args: unknown, projectRoot: string) => {
105
+ const parsed = LIST_BLUEPRINTS_ARGS_SCHEMA.safeParse(args ?? {});
106
+ const category = parsed.success ? parsed.data.category : undefined;
107
+ const frameworkDir = getFrameworkDir(projectRoot);
108
+ const blueprintsRoot = path.join(projectRoot, frameworkDir, "blueprints");
109
+
110
+ try {
111
+ if (!fs.existsSync(blueprintsRoot)) {
112
+ return { content: [{ type: "text", text: "Blueprints directory not found." }] };
113
+ }
114
+
115
+ const walk = (dir: string): string[] => {
116
+ const results: string[] = [];
117
+ if (!fs.existsSync(dir)) return results;
118
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
119
+ for (const entry of entries) {
120
+ const res = path.join(dir, entry.name);
121
+ if (entry.isDirectory()) {
122
+ // If it's a leaf directory containing files, or we want to show all dirs
123
+ const subEntries = fs.readdirSync(res);
124
+ const hasFiles = subEntries.some(e => !fs.lstatSync(path.join(res, e)).isDirectory());
125
+ if (hasFiles) {
126
+ results.push(path.relative(blueprintsRoot, res));
127
+ }
128
+ results.push(...walk(res));
129
+ }
130
+ }
131
+ return results;
132
+ };
133
+
134
+ let available = walk(blueprintsRoot);
135
+ if (category) {
136
+ available = available.filter(b => b.startsWith(category));
137
+ }
138
+
139
+ // Remove duplicates
140
+ available = Array.from(new Set(available));
141
+
142
+ return { content: [{ type: "text", text: `### AVAILABLE BLUEPRINTS\n\n` + available.map(b => `- ${b}`).join("\n") }] };
143
+ } catch (error: any) {
144
+ return { content: [{ type: "text", text: `Listing blueprints failed: ${error.message}` }] };
145
+ }
146
+ },
147
+ };
@@ -1,7 +1,7 @@
1
1
  import path from "path";
2
2
  import fs from "fs";
3
3
 
4
- export const FRAMEWORK_VERSION = "0.5.8";
4
+ export const FRAMEWORK_VERSION = "0.6.0";
5
5
 
6
6
  export function getFrameworkDir(projectRoot: string): string {
7
7
  const adapters = [".gemini", ".claude", ".cursor", ".enderun", ".codex"];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-enderun",
3
- "version": "0.5.8",
3
+ "version": "0.6.0",
4
4
  "description": "The Supreme AI Governance & Orchestration Framework for Enterprise Development",
5
5
  "author": "Yusuf BEKAR",
6
6
  "license": "MIT",
@@ -24,21 +24,17 @@
24
24
  "framework"
25
25
  ],
26
26
  "workspaces": [
27
- "packages/*",
28
- "apps/*"
27
+ "apps/*",
28
+ "framework-mcp"
29
29
  ],
30
30
  "bin": {
31
31
  "agent-enderun": "bin/cli.js"
32
32
  },
33
33
  "files": [
34
34
  "bin",
35
- "packages/shared-types/dist",
36
- "packages/shared-types/src",
37
- "packages/shared-types/package.json",
38
- "packages/shared-types/contract.version.json",
39
- "packages/framework-mcp/dist",
40
- "packages/framework-mcp/src",
41
- "packages/framework-mcp/package.json",
35
+ "framework-mcp/dist",
36
+ "framework-mcp/src",
37
+ "framework-mcp/package.json",
42
38
  ".enderun",
43
39
  "ENDERUN.md",
44
40
  "README.md",
@@ -55,7 +51,7 @@
55
51
  "docs"
56
52
  ],
57
53
  "scripts": {
58
- "enderun:build": "npm run build --prefix packages/shared-types && npm run build --prefix packages/framework-mcp",
54
+ "enderun:build": "npm run build --prefix framework-mcp",
59
55
  "enderun:test": "vitest run",
60
56
  "enderun:test:watch": "vitest",
61
57
  "enderun:status": "agent-enderun status",
@@ -80,7 +76,7 @@
80
76
  "@pandacss/dev": "^0.53.0"
81
77
  },
82
78
  "enderun": {
83
- "version": "0.5.8",
79
+ "version": "0.5.9",
84
80
  "initializedAt": "2026-05-19T14:23:38.760Z"
85
81
  },
86
82
  "dependencies": {}