agent-enderun 0.5.9 → 0.6.1
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/PROJECT_MEMORY.md +23 -32
- package/.enderun/STATUS.md +19 -10
- package/.enderun/agents/analyst.md +10 -4
- package/.enderun/agents/backend.md +23 -12
- package/.enderun/agents/explorer.md +2 -2
- package/.enderun/agents/frontend.md +21 -10
- package/.enderun/agents/git.md +1 -1
- package/.enderun/agents/manager.md +10 -2
- package/.enderun/agents/mobile.md +2 -2
- package/.enderun/agents/native.md +2 -2
- package/.enderun/blueprints/backend/errors/domain-error.ts +1 -1
- package/.enderun/blueprints/backend/types/index.ts +49 -0
- package/{packages/shared-types/src → .enderun/blueprints/backend/types}/models.ts +0 -1
- package/.enderun/docs/api/README.md +7 -12
- package/.enderun/docs/project-docs.md +17 -7
- package/.enderun/docs/tech-stack.md +7 -8
- package/.enderun/knowledge/blueprint_driven_construction.md +22 -0
- package/.enderun/knowledge/code_review_checklist.md +1 -1
- package/.enderun/knowledge/contract_versioning.md +5 -5
- package/ENDERUN.md +8 -7
- package/README.md +51 -35
- package/bin/cli.js +217 -29
- package/bin/update-contract.js +2 -2
- package/{packages/framework-mcp → framework-mcp}/dist/index.js +0 -0
- package/{packages/framework-mcp → framework-mcp}/dist/schemas.js +12 -0
- package/{packages/framework-mcp → framework-mcp}/dist/tools/contract.js +5 -5
- package/{packages/framework-mcp → framework-mcp}/dist/tools/framework.js +3 -2
- package/{packages/framework-mcp → framework-mcp}/dist/tools/git.js +2 -4
- package/{packages/framework-mcp → framework-mcp}/dist/tools/index.js +3 -0
- package/{packages/framework-mcp → framework-mcp}/dist/tools/repository.js +1 -1
- package/framework-mcp/dist/tools/scaffold.js +129 -0
- package/{packages/framework-mcp → framework-mcp}/dist/utils.js +1 -1
- package/{packages/framework-mcp → framework-mcp}/package.json +1 -1
- package/{packages/framework-mcp → framework-mcp}/src/schemas.ts +15 -0
- package/{packages/framework-mcp → framework-mcp}/src/tools/contract.ts +5 -5
- package/{packages/framework-mcp → framework-mcp}/src/tools/framework.ts +3 -2
- package/{packages/framework-mcp → framework-mcp}/src/tools/git.ts +2 -3
- package/{packages/framework-mcp → framework-mcp}/src/tools/index.ts +3 -0
- package/{packages/framework-mcp → framework-mcp}/src/tools/repository.ts +1 -1
- package/framework-mcp/src/tools/scaffold.ts +147 -0
- package/{packages/framework-mcp → framework-mcp}/src/utils.ts +1 -1
- package/gemini-extension.json +1 -1
- package/package.json +7 -11
- package/.enderun/logs/manager.json +0 -1
- package/packages/shared-types/README.md +0 -61
- package/packages/shared-types/contract.version.json +0 -12
- package/packages/shared-types/dist/api.d.ts +0 -18
- package/packages/shared-types/dist/api.js +0 -2
- package/packages/shared-types/dist/api.js.map +0 -1
- package/packages/shared-types/dist/brands.d.ts +0 -13
- package/packages/shared-types/dist/brands.js +0 -2
- package/packages/shared-types/dist/brands.js.map +0 -1
- package/packages/shared-types/dist/constants.d.ts +0 -27
- package/packages/shared-types/dist/constants.js +0 -22
- package/packages/shared-types/dist/constants.js.map +0 -1
- package/packages/shared-types/dist/index.d.ts +0 -11
- package/packages/shared-types/dist/index.js +0 -12
- package/packages/shared-types/dist/index.js.map +0 -1
- package/packages/shared-types/dist/logs.d.ts +0 -15
- package/packages/shared-types/dist/logs.js +0 -2
- package/packages/shared-types/dist/logs.js.map +0 -1
- package/packages/shared-types/dist/models.d.ts +0 -60
- package/packages/shared-types/dist/models.js +0 -2
- package/packages/shared-types/dist/models.js.map +0 -1
- package/packages/shared-types/package.json +0 -36
- package/packages/shared-types/src/index.ts +0 -12
- package/packages/shared-types/tsconfig.json +0 -9
- /package/{packages/shared-types/src → .enderun/blueprints/backend/types}/api.ts +0 -0
- /package/{packages/shared-types/src → .enderun/blueprints/backend/types}/brands.ts +0 -0
- /package/{packages/shared-types/src → .enderun/blueprints/backend/types}/constants.ts +0 -0
- /package/{packages/shared-types/src → .enderun/blueprints/backend/types}/logs.ts +0 -0
- /package/{packages/framework-mcp → framework-mcp}/README.md +0 -0
- /package/{packages/framework-mcp → framework-mcp}/dist/tools/academy.js +0 -0
- /package/{packages/framework-mcp → framework-mcp}/dist/tools/codebase.js +0 -0
- /package/{packages/framework-mcp → framework-mcp}/dist/tools/database.js +0 -0
- /package/{packages/framework-mcp → framework-mcp}/dist/tools/knowledge.js +0 -0
- /package/{packages/framework-mcp → framework-mcp}/dist/tools/memory.js +0 -0
- /package/{packages/framework-mcp → framework-mcp}/dist/tools/messages.js +0 -0
- /package/{packages/framework-mcp → framework-mcp}/dist/tools/security.js +0 -0
- /package/{packages/framework-mcp → framework-mcp}/src/index.ts +0 -0
- /package/{packages/framework-mcp → framework-mcp}/src/tools/academy.ts +0 -0
- /package/{packages/framework-mcp → framework-mcp}/src/tools/codebase.ts +0 -0
- /package/{packages/framework-mcp → framework-mcp}/src/tools/database.ts +0 -0
- /package/{packages/framework-mcp → framework-mcp}/src/tools/knowledge.ts +0 -0
- /package/{packages/framework-mcp → framework-mcp}/src/tools/memory.ts +0 -0
- /package/{packages/framework-mcp → framework-mcp}/src/tools/messages.ts +0 -0
- /package/{packages/framework-mcp → framework-mcp}/src/tools/security.ts +0 -0
- /package/{packages/framework-mcp → framework-mcp}/tsconfig.json +0 -0
|
@@ -27,7 +27,7 @@ export const gitHandlers = {
|
|
|
27
27
|
let type = "feat", scope = "code";
|
|
28
28
|
if (files.some(f => f.includes(".md")))
|
|
29
29
|
type = "docs";
|
|
30
|
-
else if (files.some(f => f.includes("
|
|
30
|
+
else if (files.some(f => f.includes("apps/backend/src/types")))
|
|
31
31
|
type = "arch";
|
|
32
32
|
else if (files.some(f => f.includes("framework-mcp")))
|
|
33
33
|
type = "chore";
|
|
@@ -39,9 +39,7 @@ export const gitHandlers = {
|
|
|
39
39
|
scope = "web";
|
|
40
40
|
else if (files.some(f => f.includes("apps/backend")))
|
|
41
41
|
scope = "backend";
|
|
42
|
-
else if (files.some(f => f.includes("
|
|
43
|
-
scope = "contract";
|
|
44
|
-
else if (files.some(f => f.includes("packages/framework-mcp")))
|
|
42
|
+
else if (files.some(f => f.includes("framework-mcp")))
|
|
45
43
|
scope = "mcp";
|
|
46
44
|
const summary = files.length === 1 ? `update ${path.basename(files[0])}` : `update ${files.length} files`;
|
|
47
45
|
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")}` }] };
|
|
@@ -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
|
export const allTools = [
|
|
13
14
|
...codebaseTools,
|
|
14
15
|
...frameworkTools,
|
|
@@ -21,6 +22,7 @@ export const allTools = [
|
|
|
21
22
|
...databaseTools,
|
|
22
23
|
...knowledgeTools,
|
|
23
24
|
...repositoryTools,
|
|
25
|
+
...scaffoldTools,
|
|
24
26
|
];
|
|
25
27
|
export const allHandlers = {
|
|
26
28
|
...codebaseHandlers,
|
|
@@ -34,6 +36,7 @@ export const allHandlers = {
|
|
|
34
36
|
...databaseHandlers,
|
|
35
37
|
...knowledgeHandlers,
|
|
36
38
|
...repositoryHandlers,
|
|
39
|
+
...scaffoldHandlers,
|
|
37
40
|
};
|
|
38
41
|
// Add compatibility aliases
|
|
39
42
|
allHandlers.codebase_search = allHandlers.search_codebase;
|
|
@@ -91,7 +91,7 @@ export const repositoryHandlers = {
|
|
|
91
91
|
});
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
|
-
const majorDirs = ["apps/backend", "apps/web", "
|
|
94
|
+
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")));
|
|
95
95
|
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")}` }] };
|
|
96
96
|
}
|
|
97
97
|
catch (error) {
|
|
@@ -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.
|
|
3
|
+
export const FRAMEWORK_VERSION = "0.6.1";
|
|
4
4
|
export function getFrameworkDir(projectRoot) {
|
|
5
5
|
const adapters = [".gemini", ".claude", ".cursor", ".enderun", ".codex"];
|
|
6
6
|
for (const adp of adapters) {
|
|
@@ -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, "
|
|
54
|
-
const contractJsonPath = path.join(projectRoot, "
|
|
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, "
|
|
66
|
-
const contractJsonPath = path.join(projectRoot, "
|
|
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,7 +83,7 @@ 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 sharedTypesDir = path.join(projectRoot, "
|
|
86
|
+
const sharedTypesDir = path.join(projectRoot, "apps/backend/src/types");
|
|
87
87
|
|
|
88
88
|
if (!fs.existsSync(apiDocPath)) return { content: [{ type: "text", text: `API documentation not found for domain: ${parsed.data.domain}` }] };
|
|
89
89
|
if (!fs.existsSync(sharedTypesDir)) return { content: [{ type: "text", text: "Shared types directory not found." }] };
|
|
@@ -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"
|
|
106
|
-
{ path: "
|
|
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" },
|
|
@@ -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("
|
|
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("
|
|
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
|
|
@@ -93,7 +93,7 @@ export const repositoryHandlers = {
|
|
|
93
93
|
});
|
|
94
94
|
});
|
|
95
95
|
}
|
|
96
|
-
const majorDirs = ["apps/backend", "apps/web", "
|
|
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.
|
|
4
|
+
export const FRAMEWORK_VERSION = "0.6.1";
|
|
5
5
|
|
|
6
6
|
export function getFrameworkDir(projectRoot: string): string {
|
|
7
7
|
const adapters = [".gemini", ".claude", ".cursor", ".enderun", ".codex"];
|
package/gemini-extension.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-enderun",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
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
|
-
"
|
|
28
|
-
"
|
|
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
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
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
|
|
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",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
[]
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
# AI-Enderun Shared Types (v0.3.4)
|
|
2
|
-
|
|
3
|
-
English | [Türkçe](#türkçe)
|
|
4
|
-
|
|
5
|
-
The `@ai-enderun/shared-types` package is the "Supreme Law" of data within the Agent Enderun framework. It ensures perfect synchronization between backend and frontend through a **Contract-First** approach.
|
|
6
|
-
|
|
7
|
-
## English
|
|
8
|
-
|
|
9
|
-
### Core Principles
|
|
10
|
-
|
|
11
|
-
- **Zero Drift:** Backend and frontend implementation must strictly follow these types.
|
|
12
|
-
- **Branded Types:** We use the "Branded Types" pattern to ensure type safety for domain identifiers (e.g., `UserID` vs. `ProjectID`).
|
|
13
|
-
- **Atomic Versioning:** Every change to this package triggers a hash mismatch in the framework, forcing a contract verification step.
|
|
14
|
-
|
|
15
|
-
### Key Components
|
|
16
|
-
|
|
17
|
-
#### Branded Identifiers
|
|
18
|
-
Prevents accidental usage of a UserID where a ProjectID is expected.
|
|
19
|
-
```typescript
|
|
20
|
-
export type UserID = string & { readonly __brand: "UserID" };
|
|
21
|
-
export type ProjectID = string & { readonly __brand: "ProjectID" };
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
#### API Contracts
|
|
25
|
-
Standardized shapes for requests and responses.
|
|
26
|
-
```typescript
|
|
27
|
-
export interface ApiResponse<T> {
|
|
28
|
-
data: T;
|
|
29
|
-
traceId: string;
|
|
30
|
-
timestamp: string;
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
### Contract Workflow
|
|
35
|
-
|
|
36
|
-
1. **Modify**: Update `src/index.ts` with new DTOs or Types.
|
|
37
|
-
2. **Build**: `npm run build`
|
|
38
|
-
3. **Verify**: Run `agent-enderun verify-contract` to detect changes.
|
|
39
|
-
4. **Sync**: Update `contract.version.json` with the new hash using the MCP tool `update_contract_hash`.
|
|
40
|
-
|
|
41
|
-
## Türkçe
|
|
42
|
-
|
|
43
|
-
`@ai-enderun/shared-types`, Agent Enderun çerçevesindeki verilerin "Yüce Yasası"dır. **Kontrat Öncelikli** (Contract-First) yaklaşımıyla backend ve frontend arasında kusursuz bir uyum sağlar.
|
|
44
|
-
|
|
45
|
-
### Temel Özellikler
|
|
46
|
-
|
|
47
|
-
- **Branded Types:** Kimlik karışıklıklarını önlemek için özel tipler kullanılır.
|
|
48
|
-
- **API Sözleşmeleri:** Tüm istek ve yanıtlar için standart şablonlar.
|
|
49
|
-
- **Hash Doğrulama:** Tip değişiklikleri merkezi bir hash üzerinden takip edilir.
|
|
50
|
-
|
|
51
|
-
### Geliştirme
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
npm install
|
|
55
|
-
npm run build
|
|
56
|
-
npm run typecheck
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## License
|
|
60
|
-
|
|
61
|
-
MIT
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": "0.1.11",
|
|
3
|
-
"last_updated": "2026-05-16T16:47:16.000Z",
|
|
4
|
-
"contract_hash": "8574144a23e68351f48365f5ccb3d69dc11629af54ec9c1ed0fffd57f967b777",
|
|
5
|
-
"breaking_changes": [
|
|
6
|
-
{
|
|
7
|
-
"version": "0.1.11",
|
|
8
|
-
"description": "Extended shared types for Enderun Brain Dashboard"
|
|
9
|
-
}
|
|
10
|
-
],
|
|
11
|
-
"deprecated_versions": []
|
|
12
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { TraceID } from "./brands.js";
|
|
2
|
-
/**
|
|
3
|
-
* API Response Wrappers
|
|
4
|
-
*/
|
|
5
|
-
export interface ApiResponse<T> {
|
|
6
|
-
data: T;
|
|
7
|
-
meta?: {
|
|
8
|
-
requestId: TraceID;
|
|
9
|
-
timestamp: string;
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
export interface ApiError {
|
|
13
|
-
error: {
|
|
14
|
-
code: string;
|
|
15
|
-
message: string;
|
|
16
|
-
details?: unknown;
|
|
17
|
-
};
|
|
18
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":""}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Branded Type Utility
|
|
3
|
-
*/
|
|
4
|
-
export type Brand<K, T> = K & {
|
|
5
|
-
__brand: T;
|
|
6
|
-
};
|
|
7
|
-
/**
|
|
8
|
-
* Entity IDs (Branded)
|
|
9
|
-
*/
|
|
10
|
-
export type TraceID = Brand<string, "TraceID">;
|
|
11
|
-
export type AgentID = Brand<string, "AgentID">;
|
|
12
|
-
export type ProjectID = Brand<string, "ProjectID">;
|
|
13
|
-
export type UserID = Brand<string, "UserID">;
|