agent-enderun 0.9.0 ā 0.9.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/BRAIN_DASHBOARD.md +2 -2
- package/.enderun/PROJECT_MEMORY.md +26 -4
- package/.enderun/STATUS.md +24 -28
- package/.enderun/agents/backend.md +37 -8
- package/.enderun/agents/database.md +55 -0
- package/.enderun/agents/devops.md +28 -14
- package/.enderun/agents/explorer.md +1 -1
- package/.enderun/agents/frontend.md +21 -7
- package/.enderun/agents/git.md +1 -1
- package/.enderun/agents/manager.md +41 -20
- package/.enderun/agents/mobile.md +1 -1
- package/.enderun/agents/native.md +1 -1
- package/.enderun/agents/quality.md +122 -0
- package/.enderun/cli-commands.json +3 -3
- package/.enderun/config.json +7 -1
- package/.enderun/knowledge/context_boundary_rules.md +57 -0
- package/.enderun/knowledge/corporate-governance/high-risk-action-approval-flow.md +29 -0
- package/.enderun/knowledge/documentation_ownership.md +53 -0
- package/.enderun/knowledge/frontend_professionalization_guidelines.md +7 -7
- package/.enderun/knowledge/hermes_live_test_guidelines.md +5 -5
- package/.enderun/knowledge/lessons-learned.md +14 -0
- package/.enderun/knowledge/manager_authority_audit_enforcement.md +8 -8
- package/.enderun/knowledge/project_scaffold_guidelines.md +4 -4
- package/.enderun/knowledge/reference_application_guidelines.md +6 -6
- package/.enderun/memory-graph/agent-contexts/quality.json +1 -0
- package/.enderun/queue/README.md +13 -13
- package/README.md +204 -102
- package/bin/cli.js +1 -1
- package/bin/init-check.js +69 -0
- package/bin/update-contract.js +24 -2
- package/docs/README.md +21 -32
- package/docs/architecture/README.md +9 -0
- package/docs/architecture/standards/data-fetching-patterns.md +13 -0
- package/docs/architecture/standards/design-system.md +31 -0
- package/docs/architecture/standards/logging.md +7 -0
- package/docs/architecture/standards/tech-stack.md +9 -0
- package/docs/getting-started.md +9 -259
- package/docs/user/README.md +35 -0
- package/docs/user/action-plan-2026.md +9 -0
- package/docs/user/getting-started.md +13 -0
- package/docs/user/roadmap.md +13 -0
- package/eslint.config.js +1 -1
- package/framework-mcp/dist/index.js +7 -13
- package/package.json +3 -1
- package/src/cli/adapters.ts +208 -0
- package/src/cli/commands/app.ts +38 -0
- package/src/cli/commands/check.ts +87 -0
- package/src/cli/commands/compliance.ts +55 -0
- package/src/cli/commands/contract.ts +45 -0
- package/src/cli/commands/explorer.ts +45 -0
- package/src/cli/commands/git.ts +39 -0
- package/src/cli/commands/init.ts +272 -0
- package/src/cli/commands/knowledge.ts +44 -0
- package/src/cli/commands/lint.ts +25 -0
- package/src/cli/commands/log.ts +37 -0
- package/src/cli/commands/memory.ts +78 -0
- package/src/cli/commands/orchestrate.ts +111 -0
- package/src/cli/commands/script.ts +20 -0
- package/src/cli/commands/security.ts +38 -0
- package/src/cli/commands/status.ts +59 -0
- package/src/cli/commands/trace.ts +46 -0
- package/src/cli/index.ts +107 -0
- package/src/cli/utils/app.ts +764 -0
- package/src/cli/utils/claude.ts +56 -0
- package/src/cli/utils/fs.ts +139 -0
- package/src/cli/utils/memory.ts +141 -0
- package/src/cli/utils/pkg.ts +215 -0
- package/src/cli/utils/string.ts +48 -0
- package/src/cli/utils/time.ts +27 -0
- package/.enderun/agents/analyst.md +0 -236
- package/.enderun/agents/orchestrator.md +0 -172
- package/.enderun/agents/qa.md +0 -121
- package/.enderun/agents/security.md +0 -202
- package/.enderun/knowledge/api_design_rules.md +0 -6
- package/.enderun/knowledge/branded_types_pattern.md +0 -8
- package/.enderun/knowledge/code_review_checklist.md +0 -7
- package/.enderun/knowledge/contract_versioning.md +0 -7
- package/.enderun/knowledge/database_migration.md +0 -6
- package/.enderun/knowledge/deployment_checklist.md +0 -7
- package/.enderun/knowledge/git_commit_strategy.md +0 -10
- package/.enderun/knowledge/monitoring_setup.md +0 -5
- package/.enderun/knowledge/performance_guidelines.md +0 -11
- package/.enderun/knowledge/repository_patterns.md +0 -9
- package/.enderun/knowledge/security_scanning.md +0 -6
- package/.enderun/knowledge/testing_standards.md +0 -7
- package/.enderun/knowledge/troubleshooting_guide.md +0 -5
- package/.enderun/memory-graph/agent-contexts/analyst.json +0 -1
- package/.enderun/memory-graph/agent-contexts/orchestrator.json +0 -1
- package/.enderun/memory-graph/agent-contexts/qa.json +0 -1
- package/.enderun/memory-graph/agent-contexts/security.json +0 -1
- package/docs/action-plan-2026.md +0 -119
- package/docs/roadmap.md +0 -142
- package/docs/tech-stack.md +0 -30
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { execSync } from "child_process";
|
|
4
|
+
|
|
5
|
+
import { getFrameworkDir, getConfiguredPaths } from "../utils/memory.js";
|
|
6
|
+
import { getPackageVersion } from "../utils/pkg.js"; // To get FRAMEWORK_VERSION
|
|
7
|
+
|
|
8
|
+
const FRAMEWORK_VERSION = getPackageVersion();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Check framework health and MCP status.
|
|
12
|
+
*/
|
|
13
|
+
export function checkCommand() {
|
|
14
|
+
console.warn(`š Checking Agent Enderun Health (v${FRAMEWORK_VERSION})...`);
|
|
15
|
+
let issues = 0;
|
|
16
|
+
|
|
17
|
+
const frameworkDir = getFrameworkDir();
|
|
18
|
+
const pathsMap = getConfiguredPaths();
|
|
19
|
+
|
|
20
|
+
const constitutionPath = fs.existsSync(path.join(process.cwd(), "ENDERUN.md"))
|
|
21
|
+
? "ENDERUN.md"
|
|
22
|
+
: path.join(frameworkDir, "ENDERUN.md");
|
|
23
|
+
|
|
24
|
+
const checks = [
|
|
25
|
+
{ name: "Constitution (ENDERUN.md)", path: constitutionPath },
|
|
26
|
+
{ name: "Memory (PROJECT_MEMORY.md)", path: path.join(frameworkDir, "PROJECT_MEMORY.md") },
|
|
27
|
+
{ name: "Command Map (cli-commands.json)", path: path.join(frameworkDir, "cli-commands.json") },
|
|
28
|
+
{ name: "Framework Config (config.json)", path: path.join(frameworkDir, "config.json") },
|
|
29
|
+
{ name: "Agent Status (STATUS.md)", path: path.join(frameworkDir, "STATUS.md") },
|
|
30
|
+
{ name: "MCP Config (mcp.json)", path: "mcp.json" },
|
|
31
|
+
{ name: "ESLint Config (eslint.config.js)", path: "eslint.config.js" },
|
|
32
|
+
{ name: "ESLint Standards", path: path.join(frameworkDir, "knowledge/eslint-standards.md") },
|
|
33
|
+
{ name: "Backend Contract", path: path.join(pathsMap.backend, "contract.version.json") },
|
|
34
|
+
{ name: "MCP Server", path: "framework-mcp/package.json" },
|
|
35
|
+
{ name: "Panda CSS Config", path: "panda.config.ts" },
|
|
36
|
+
{ name: "Brain Dashboard", path: path.join(frameworkDir, "BRAIN_DASHBOARD.md") },
|
|
37
|
+
{ name: "Project Architectural Portal (docs/README.md)", path: path.join(pathsMap.docs, "README.md") },
|
|
38
|
+
{ name: "System Getting Started Guide (docs/getting-started.md)", path: path.join(pathsMap.docs, "getting-started.md") },
|
|
39
|
+
{ name: "Enterprise Approval Flows Protocol", path: path.join(pathsMap.docs, "architecture/approval-flows.md") },
|
|
40
|
+
{ name: "Professional Error Handling Pattern", path: path.join(pathsMap.docs, "backend/error-handling.md") },
|
|
41
|
+
{ name: "Atomic Component Standards", path: path.join(pathsMap.docs, "frontend/component-patterns.md") },
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
for (const check of checks) {
|
|
45
|
+
if (fs.existsSync(path.join(process.cwd(), check.path))) {
|
|
46
|
+
console.warn(`ā
${check.name} found.`);
|
|
47
|
+
} else {
|
|
48
|
+
console.warn(`ā ${check.name} MISSING! (${check.path})`);
|
|
49
|
+
issues++;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Dependency Check
|
|
54
|
+
const mcpNodeModules = path.join(process.cwd(), "framework-mcp/node_modules");
|
|
55
|
+
const rootNodeModules = path.join(process.cwd(), "node_modules");
|
|
56
|
+
if (!fs.existsSync(mcpNodeModules) && !fs.existsSync(rootNodeModules)) {
|
|
57
|
+
console.warn("ā Dependencies MISSING! (Run 'npm install')");
|
|
58
|
+
issues++;
|
|
59
|
+
} else {
|
|
60
|
+
console.warn("ā
Dependencies found.");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// MCP Build Check
|
|
64
|
+
const mcpPath = path.join(process.cwd(), "framework-mcp/dist/index.js");
|
|
65
|
+
if (!fs.existsSync(mcpPath)) {
|
|
66
|
+
console.warn("ā MCP Build MISSING! (Run 'npm run enderun:build')");
|
|
67
|
+
issues++;
|
|
68
|
+
} else {
|
|
69
|
+
console.warn("ā
MCP Build found.");
|
|
70
|
+
console.warn("ā³ Testing MCP Server syntax...");
|
|
71
|
+
try {
|
|
72
|
+
execSync(`node --check ${mcpPath}`, { stdio: "pipe" });
|
|
73
|
+
console.warn("ā
MCP Server syntax valid.");
|
|
74
|
+
} catch {
|
|
75
|
+
// If --check fails on ESM, we might skip it or use a better check
|
|
76
|
+
console.warn("ā ļø MCP Syntax check skipped (ESM/Environment).");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (issues === 0) {
|
|
81
|
+
console.warn("\nš All systems green! Agent Enderun is ready for orchestration.");
|
|
82
|
+
} else {
|
|
83
|
+
console.warn(`
|
|
84
|
+
ā ļø Found ${issues} issues. Please fix them before starting.`);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
import { collectFiles } from "../utils/fs.js";
|
|
5
|
+
|
|
6
|
+
const targetDir = process.cwd(); // Assuming targetDir is process.cwd() in the CLI context
|
|
7
|
+
|
|
8
|
+
export function complianceCheckCommand(targetPath: string) {
|
|
9
|
+
console.warn(`š Checking Constitution Compliance: ${targetPath}...`);
|
|
10
|
+
const violations: string[] = [];
|
|
11
|
+
const forbidden = ["@shadcn", "mui", "@chakra-ui", "tailwindcss"];
|
|
12
|
+
|
|
13
|
+
// 1. package.json scan (Blind Spot 5)
|
|
14
|
+
const pkgJsonPath = path.join(targetDir, "package.json");
|
|
15
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
16
|
+
try {
|
|
17
|
+
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8"));
|
|
18
|
+
const allDeps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
19
|
+
forbidden.forEach(lib => {
|
|
20
|
+
if (allDeps[lib] || Object.keys(allDeps).some(d => d.toLowerCase().includes(lib.toLowerCase()))) {
|
|
21
|
+
violations.push(`package.json: Forbidden library dependency '${lib}' found in package.json.`);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
} catch {
|
|
25
|
+
// ignore
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 2. Source files scan
|
|
30
|
+
const files = collectFiles(path.join(targetDir, targetPath), [".ts", ".tsx", ".js", ".jsx", ".md"]);
|
|
31
|
+
files.forEach(f => {
|
|
32
|
+
const isTypesFile = f.includes("brands.ts") || f.includes("brands.js") || f.includes("brands.tsx") || f.includes("src/types/brands");
|
|
33
|
+
|
|
34
|
+
const content = fs.readFileSync(f, "utf8");
|
|
35
|
+
forbidden.forEach(lib => {
|
|
36
|
+
if (content.includes(lib)) violations.push(`${path.relative(targetDir, f)}: Forbidden library '${lib}' found.`);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Robust Branded Types validation (Blind Spot 4)
|
|
40
|
+
if ((f.endsWith(".ts") || f.endsWith(".tsx")) && !isTypesFile && content.includes("interface")) {
|
|
41
|
+
const plainIdMatches = content.match(/\b\w*(?:id|Id|ID)\??\s*:\s*(?:string|number)\b/g);
|
|
42
|
+
if (plainIdMatches) {
|
|
43
|
+
plainIdMatches.forEach(match => {
|
|
44
|
+
violations.push(`${path.relative(targetDir, f)}: Potential Branded Types violation (plain identifier '${match.trim()}' found inside interface).`);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (violations.length === 0) {
|
|
51
|
+
console.warn("ā
All systems compliant with ENDERUN.md.");
|
|
52
|
+
} else {
|
|
53
|
+
violations.forEach(v => console.warn(`ā ${v}`));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import crypto from "crypto";
|
|
4
|
+
import { getConfiguredPaths } from "../utils/memory.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Verify type safety between backend and frontend contracts.
|
|
8
|
+
*/
|
|
9
|
+
export function verifyApiContractCommand() {
|
|
10
|
+
const projectRoot = process.cwd();
|
|
11
|
+
const pathsMap = getConfiguredPaths();
|
|
12
|
+
const sharedDir = path.join(projectRoot, pathsMap.backend, "src/types");
|
|
13
|
+
const contractPath = path.join(projectRoot, pathsMap.backend, "contract.version.json");
|
|
14
|
+
|
|
15
|
+
if (!fs.existsSync(sharedDir) || !fs.existsSync(contractPath)) {
|
|
16
|
+
console.error("ā Error: API types or contract version file missing.");
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const walk = (d: string): string[] => fs.readdirSync(d, { withFileTypes: true }).flatMap((e) => {
|
|
21
|
+
const fullPath = path.join(d, e.name);
|
|
22
|
+
return e.isDirectory() ? walk(fullPath) : (e.name.endsWith(".ts") ? [fullPath] : []);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const hash = crypto.createHash("sha256");
|
|
26
|
+
for (const filePath of walk(sharedDir).sort()) {
|
|
27
|
+
hash.update(path.relative(projectRoot, filePath));
|
|
28
|
+
hash.update("\0");
|
|
29
|
+
hash.update(fs.readFileSync(filePath));
|
|
30
|
+
hash.update("\0");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const currentHash = hash.digest("hex");
|
|
34
|
+
const contract = JSON.parse(fs.readFileSync(contractPath, "utf8"));
|
|
35
|
+
|
|
36
|
+
if (contract.contract_hash === currentHash) {
|
|
37
|
+
console.warn("ā
Contract is valid and synchronized.");
|
|
38
|
+
} else {
|
|
39
|
+
console.error("ā Error: Contract drift detected!");
|
|
40
|
+
console.error(` Stored: ${contract.contract_hash}`);
|
|
41
|
+
console.error(` Actual: ${currentHash}`);
|
|
42
|
+
console.warn("\nš Run 'npm run update-contract' to re-sync.");
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
import { collectFiles } from "../utils/fs.js";
|
|
5
|
+
|
|
6
|
+
const targetDir = process.cwd();
|
|
7
|
+
|
|
8
|
+
export function explorerGraphCommand(targetPath: string) {
|
|
9
|
+
console.warn(`šŗļø Generating Dependency Graph for: ${targetPath}...`);
|
|
10
|
+
const files = collectFiles(path.join(targetDir, targetPath), [".ts", ".tsx"]);
|
|
11
|
+
const edges: string[] = [];
|
|
12
|
+
files.forEach((f) => {
|
|
13
|
+
const content = fs.readFileSync(f, "utf8");
|
|
14
|
+
const name = path.basename(f, path.extname(f));
|
|
15
|
+
const imports = content.match(/from\s+['"]\.\.?\/[^'"]+['"]/g) || [];
|
|
16
|
+
imports.forEach((imp) => {
|
|
17
|
+
const target = path.basename(imp.split(/['"]/)[1]);
|
|
18
|
+
edges.push(`${name} --> ${target}`);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
if (edges.length === 0) {
|
|
22
|
+
console.warn("ā¹ļø No internal dependencies found.");
|
|
23
|
+
} else {
|
|
24
|
+
console.warn(
|
|
25
|
+
"```mermaid\ngraph TD\n" + Array.from(new Set(edges)).join("\n") + "\n```",
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function explorerAuditCommand(targetPath: string) {
|
|
31
|
+
console.warn(`š§ Codebase Intelligence Scan: ${targetPath}...`);
|
|
32
|
+
const files = collectFiles(path.join(targetDir, targetPath), [".ts", ".tsx"]);
|
|
33
|
+
const complexity: string[] = [];
|
|
34
|
+
files.forEach((f) => {
|
|
35
|
+
const content = fs.readFileSync(f, "utf8");
|
|
36
|
+
const lines = content.split("\n").length;
|
|
37
|
+
if (lines > 300) complexity.push(`${path.relative(targetDir, f)} (${lines} lines)`);
|
|
38
|
+
});
|
|
39
|
+
if (complexity.length > 0) {
|
|
40
|
+
console.warn("\nā ļø Complexity Spikes:");
|
|
41
|
+
complexity.forEach((c) => console.warn(`- ${c}`));
|
|
42
|
+
} else {
|
|
43
|
+
console.warn("ā
Codebase structure looks clean.");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import cp from "child_process";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { getConfiguredPaths } from "../utils/memory.js";
|
|
4
|
+
|
|
5
|
+
export function gitCommitCommand(traceId: string) {
|
|
6
|
+
try {
|
|
7
|
+
const diff = cp.execSync("git diff --staged", { encoding: "utf8" });
|
|
8
|
+
if (!diff) {
|
|
9
|
+
console.warn("ā¹ļø No staged changes found. Use 'git add' first.");
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const files = cp.execSync("git diff --staged --name-only", { encoding: "utf8" })
|
|
13
|
+
.split("\n")
|
|
14
|
+
.filter(Boolean);
|
|
15
|
+
const pathsMap = getConfiguredPaths();
|
|
16
|
+
const backendTypesPath = path.join(pathsMap.backend, "src/types");
|
|
17
|
+
let type = "feat";
|
|
18
|
+
const scope = "code";
|
|
19
|
+
if (files.some((f) => f.includes(".md"))) type = "docs";
|
|
20
|
+
else if (files.some((f) => f.includes(backendTypesPath))) type = "arch";
|
|
21
|
+
else if (files.some((f) => f.includes("bin/cli.js"))) type = "fix";
|
|
22
|
+
|
|
23
|
+
const summary =
|
|
24
|
+
files.length === 1 ? `update ${path.basename(files[0])}` : `update ${files.length} files`;
|
|
25
|
+
console.warn(`\n### SUGGESTED COMMIT MESSAGE ###\n\n[${traceId}] ${type}(${scope}): ${summary}\n`);
|
|
26
|
+
} catch {
|
|
27
|
+
console.warn("ā Git command failed.");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function gitSyncCommand() {
|
|
32
|
+
console.warn("š Syncing with remote repository...");
|
|
33
|
+
try {
|
|
34
|
+
cp.execSync("git pull origin main --rebase", { stdio: "inherit" });
|
|
35
|
+
console.warn("ā
Successfully synced and rebased.");
|
|
36
|
+
} catch {
|
|
37
|
+
console.warn("ā Sync failed. Please resolve conflicts manually.");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
resolveAdapter,
|
|
8
|
+
isAdapterShimFile,
|
|
9
|
+
remapFrameworkContent,
|
|
10
|
+
runAdapterPostInit,
|
|
11
|
+
type AdapterConfig,
|
|
12
|
+
} from "../adapters.js";
|
|
13
|
+
import { getPackageManager, mergePackageJson, deepCleanProtocols, sanitizeJson, getPackageVersion } from "../utils/pkg.js";
|
|
14
|
+
import { updateGitIgnore, ensureDir, writeJsonFile, copyDir } from "../utils/fs.js";
|
|
15
|
+
import { initializeMemory } from "../utils/memory.js";
|
|
16
|
+
import { checkCommand } from "./check.js";
|
|
17
|
+
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const __dirname = path.dirname(__filename);
|
|
20
|
+
const sourceDir = path.join(__dirname, "../../..");
|
|
21
|
+
const targetDir = process.cwd();
|
|
22
|
+
|
|
23
|
+
const FRAMEWORK_VERSION = getPackageVersion();
|
|
24
|
+
|
|
25
|
+
const CORE_FILES = [
|
|
26
|
+
".enderun",
|
|
27
|
+
"mcp.json",
|
|
28
|
+
".env.example",
|
|
29
|
+
"ENDERUN.md",
|
|
30
|
+
"package.json",
|
|
31
|
+
"tsconfig.json",
|
|
32
|
+
"panda.config.ts",
|
|
33
|
+
"vitest.config.ts",
|
|
34
|
+
"framework-mcp",
|
|
35
|
+
"docs",
|
|
36
|
+
"eslint.config.js",
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const FRAMEWORK_SUBDIRS = [
|
|
40
|
+
"agents",
|
|
41
|
+
"agents/schema",
|
|
42
|
+
"skills",
|
|
43
|
+
"knowledge",
|
|
44
|
+
"benchmarks",
|
|
45
|
+
"monitoring",
|
|
46
|
+
"logs",
|
|
47
|
+
"messages",
|
|
48
|
+
"memory-graph",
|
|
49
|
+
"memory-graph/agent-contexts",
|
|
50
|
+
"queue",
|
|
51
|
+
"queue/pending",
|
|
52
|
+
"queue/processing",
|
|
53
|
+
"queue/completed",
|
|
54
|
+
"queue/failed",
|
|
55
|
+
"queue/pipelines",
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
function buildDirsToCreate(adapter: AdapterConfig): string[] {
|
|
59
|
+
const dirs = [
|
|
60
|
+
adapter.frameworkDir,
|
|
61
|
+
...FRAMEWORK_SUBDIRS.map((d) => `${adapter.frameworkDir}/${d}`),
|
|
62
|
+
"apps/web",
|
|
63
|
+
"apps/backend",
|
|
64
|
+
"docs",
|
|
65
|
+
"framework-mcp",
|
|
66
|
+
"tests",
|
|
67
|
+
];
|
|
68
|
+
if (adapter.nestedDirs) {
|
|
69
|
+
for (const nested of adapter.nestedDirs) {
|
|
70
|
+
dirs.push(`${adapter.frameworkDir}/${nested}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return [...new Set(dirs)];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Scaffold the framework for a specific IDE adapter (separate runtime folder per IDE).
|
|
78
|
+
*/
|
|
79
|
+
export async function initCommand(adapterInput?: string) {
|
|
80
|
+
const adapter = resolveAdapter(adapterInput);
|
|
81
|
+
const { frameworkDir, shimFile, templateDir } = adapter;
|
|
82
|
+
const targetFrameworkDir = path.join(targetDir, frameworkDir);
|
|
83
|
+
|
|
84
|
+
console.warn(`š Installing Agent Enderun (Adapter: ${adapter.id} ā ${frameworkDir}/)...`);
|
|
85
|
+
|
|
86
|
+
if (!fs.existsSync(targetFrameworkDir)) {
|
|
87
|
+
fs.mkdirSync(targetFrameworkDir, { recursive: true });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
for (const dir of buildDirsToCreate(adapter)) {
|
|
91
|
+
const fullPath = path.join(targetDir, dir);
|
|
92
|
+
if (!fs.existsSync(fullPath)) {
|
|
93
|
+
fs.mkdirSync(fullPath, { recursive: true });
|
|
94
|
+
console.warn(`š Created directory: ${dir}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const filesToProcess = [
|
|
99
|
+
...CORE_FILES,
|
|
100
|
+
shimFile,
|
|
101
|
+
].filter(
|
|
102
|
+
(f) =>
|
|
103
|
+
CORE_FILES.includes(f) ||
|
|
104
|
+
f === shimFile ||
|
|
105
|
+
fs.existsSync(path.join(sourceDir, f)),
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
ensureDir(path.join(targetDir, "apps/backend"));
|
|
109
|
+
const initContractPath = path.join(targetDir, "apps/backend/contract.version.json");
|
|
110
|
+
if (!fs.existsSync(initContractPath)) {
|
|
111
|
+
writeJsonFile(initContractPath, {
|
|
112
|
+
contract_hash: "initial_hash_placeholder",
|
|
113
|
+
last_updated: new Date().toISOString(),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let targetPkg: Record<string, unknown> = {};
|
|
118
|
+
try {
|
|
119
|
+
const targetPkgPath = path.join(targetDir, "package.json");
|
|
120
|
+
if (fs.existsSync(targetPkgPath)) {
|
|
121
|
+
targetPkg = JSON.parse(fs.readFileSync(targetPkgPath, "utf8"));
|
|
122
|
+
}
|
|
123
|
+
} catch {
|
|
124
|
+
// ignore
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const pkgName = typeof targetPkg.name === "string" ? targetPkg.name : "";
|
|
128
|
+
const targetScope = pkgName
|
|
129
|
+
? pkgName.startsWith("@")
|
|
130
|
+
? pkgName.split("/")[0]
|
|
131
|
+
: `@${pkgName}`
|
|
132
|
+
: `@${path.basename(targetDir)}`;
|
|
133
|
+
|
|
134
|
+
for (const item of filesToProcess) {
|
|
135
|
+
const src = path.join(sourceDir, item);
|
|
136
|
+
let dest = path.join(targetDir, item);
|
|
137
|
+
|
|
138
|
+
if (item === "framework-mcp" && fs.existsSync(dest)) {
|
|
139
|
+
try {
|
|
140
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
141
|
+
} catch {
|
|
142
|
+
// ignore
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (item === templateDir || item.startsWith(`${templateDir}/`)) {
|
|
147
|
+
dest = path.join(targetDir, item.replace(templateDir, frameworkDir));
|
|
148
|
+
}
|
|
149
|
+
if (item === "ENDERUN.md") {
|
|
150
|
+
dest = path.join(targetFrameworkDir, "ENDERUN.md");
|
|
151
|
+
}
|
|
152
|
+
if (isAdapterShimFile(item)) {
|
|
153
|
+
dest = path.join(targetDir, item);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!fs.existsSync(src)) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (fs.lstatSync(src).isDirectory()) {
|
|
161
|
+
const skipFiles =
|
|
162
|
+
item === templateDir ? ["logs", "PROJECT_MEMORY.md", "PROJECT_MEMORY.lock"] : [];
|
|
163
|
+
const nonDestructive = ["docs", templateDir].includes(item);
|
|
164
|
+
copyDir(
|
|
165
|
+
src,
|
|
166
|
+
dest,
|
|
167
|
+
new Set(skipFiles),
|
|
168
|
+
nonDestructive,
|
|
169
|
+
frameworkDir,
|
|
170
|
+
targetScope,
|
|
171
|
+
sanitizeJson,
|
|
172
|
+
adapter.id,
|
|
173
|
+
);
|
|
174
|
+
} else {
|
|
175
|
+
if (item === "package.json") continue;
|
|
176
|
+
|
|
177
|
+
if (fs.existsSync(dest) && !isAdapterShimFile(item)) {
|
|
178
|
+
console.warn(`ā¹ļø Skipping existing file: ${item}`);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const ext = path.extname(item);
|
|
183
|
+
const textExtensions = [".md", ".json", ".js", ".ts", ".txt", ""];
|
|
184
|
+
if (textExtensions.includes(ext)) {
|
|
185
|
+
let content = fs.readFileSync(src, "utf8");
|
|
186
|
+
content = remapFrameworkContent(content, frameworkDir, adapter.id);
|
|
187
|
+
|
|
188
|
+
if (ext === ".json") {
|
|
189
|
+
try {
|
|
190
|
+
const json = JSON.parse(content);
|
|
191
|
+
content = JSON.stringify(sanitizeJson(json, targetScope), null, 2);
|
|
192
|
+
content = remapFrameworkContent(content, frameworkDir, adapter.id);
|
|
193
|
+
} catch {
|
|
194
|
+
content = content.replace(/workspace:[^"'\s]*/g, "*");
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
fs.writeFileSync(dest, content);
|
|
199
|
+
} else {
|
|
200
|
+
fs.copyFileSync(src, dest);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
console.warn(`ā
${item} processed -> ${path.relative(targetDir, dest)}`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
mergePackageJson(path.join(targetDir, "package.json"), path.join(sourceDir, "package.json"));
|
|
207
|
+
updateGitIgnore(path.join(targetDir, ".gitignore"), frameworkDir);
|
|
208
|
+
|
|
209
|
+
const finalMemoryPath = path.join(targetFrameworkDir, "PROJECT_MEMORY.md");
|
|
210
|
+
initializeMemory(finalMemoryPath, frameworkDir);
|
|
211
|
+
|
|
212
|
+
deepCleanProtocols(targetDir, targetScope);
|
|
213
|
+
|
|
214
|
+
const sampleTestPath = path.join(targetDir, "tests/initial.test.ts");
|
|
215
|
+
if (!fs.existsSync(sampleTestPath)) {
|
|
216
|
+
fs.writeFileSync(
|
|
217
|
+
sampleTestPath,
|
|
218
|
+
`import { describe, it, expect } from "vitest";
|
|
219
|
+
|
|
220
|
+
describe("Initial Setup", () => {
|
|
221
|
+
it("should verify the testing environment is active", () => {
|
|
222
|
+
expect(true).toBe(true);
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
`,
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (!fs.existsSync(path.join(targetDir, ".git"))) {
|
|
230
|
+
try {
|
|
231
|
+
execSync("git init", { cwd: targetDir, stdio: "ignore" });
|
|
232
|
+
console.warn("ā
Git repository initialized.");
|
|
233
|
+
} catch {
|
|
234
|
+
// ignore
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
console.warn(`\nš ļø Running ${adapter.id} adapter configuration...`);
|
|
239
|
+
runAdapterPostInit(adapter, targetDir);
|
|
240
|
+
|
|
241
|
+
console.warn(`\nā ${adapter.id.toUpperCase()}: Setup complete.`);
|
|
242
|
+
console.warn(` ⢠Framework runtime: ${frameworkDir}/`);
|
|
243
|
+
console.warn(` ⢠Entrypoint shim: ${shimFile}`);
|
|
244
|
+
if (adapter.nestedDirs?.length) {
|
|
245
|
+
console.warn(` ⢠Nested: ${adapter.nestedDirs.map((d) => `${frameworkDir}/${d}/`).join(", ")}`);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const pkgMgr = getPackageManager();
|
|
249
|
+
const installCmd = pkgMgr === "npm" ? "npm install" : `${pkgMgr} install`;
|
|
250
|
+
const buildCmd = pkgMgr === "npm" ? "npm run enderun:build" : `${pkgMgr} run enderun:build`;
|
|
251
|
+
|
|
252
|
+
console.warn(`\n⨠Framework scaffolded! (v${FRAMEWORK_VERSION})`);
|
|
253
|
+
|
|
254
|
+
if (process.env.ENDERUN_SKIP_INSTALL === "1") {
|
|
255
|
+
console.warn("\nāļø Skipping install steps (ENDERUN_SKIP_INSTALL=1).");
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
try {
|
|
260
|
+
execSync(installCmd, { stdio: "inherit" });
|
|
261
|
+
console.warn(`\nšļø Step 2/3: Running '${buildCmd}'...`);
|
|
262
|
+
execSync(buildCmd, { stdio: "inherit" });
|
|
263
|
+
console.warn("\nš Step 3/3: Running 'agent-enderun check'...");
|
|
264
|
+
checkCommand();
|
|
265
|
+
console.warn("\nš Agent Enderun is fully installed and verified!");
|
|
266
|
+
} catch (e: unknown) {
|
|
267
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
268
|
+
console.error("\nā Automatic installation failed. Run manually:");
|
|
269
|
+
console.warn(`š ${installCmd} && ${buildCmd}`);
|
|
270
|
+
console.error(message);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
import { getFrameworkDir } from "../utils/memory.js";
|
|
5
|
+
|
|
6
|
+
const targetDir = process.cwd(); // Assuming targetDir is process.cwd() in the CLI context
|
|
7
|
+
|
|
8
|
+
export function updateKnowledgeBaseCommand(topic: string, content: string) {
|
|
9
|
+
if (!topic || !content) {
|
|
10
|
+
console.error("ā Usage: agent-enderun update_knowledge_base <topic> <content>");
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const frameworkDir = getFrameworkDir();
|
|
14
|
+
const kbDir = path.join(targetDir, frameworkDir, "knowledge");
|
|
15
|
+
if (!fs.existsSync(kbDir)) fs.mkdirSync(kbDir, { recursive: true });
|
|
16
|
+
const fileName = topic.replace(/[^a-z0-9]/gi, "_").toLowerCase() + ".md";
|
|
17
|
+
fs.writeFileSync(path.join(kbDir, fileName), content);
|
|
18
|
+
console.warn(`ā
Knowledge base updated: ${topic}`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function searchKnowledgeBaseCommand(query: string) {
|
|
22
|
+
if (!query) {
|
|
23
|
+
console.error("ā Usage: agent-enderun search_knowledge_base <query>");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const frameworkDir = getFrameworkDir();
|
|
27
|
+
const kbDir = path.join(targetDir, frameworkDir, "knowledge");
|
|
28
|
+
if (!fs.existsSync(kbDir)) {
|
|
29
|
+
console.warn("ā¹ļø Knowledge base is empty.");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const files = fs.readdirSync(kbDir).filter(f => f.endsWith(".md"));
|
|
33
|
+
let found = false;
|
|
34
|
+
for (const file of files) {
|
|
35
|
+
const content = fs.readFileSync(path.join(kbDir, file), "utf-8");
|
|
36
|
+
if (content.toLowerCase().includes(query.toLowerCase()) || file.toLowerCase().includes(query.toLowerCase())) {
|
|
37
|
+
console.warn(`
|
|
38
|
+
### ${file.replace(".md", "")}
|
|
39
|
+
${content.slice(0, 300)}...`);
|
|
40
|
+
found = true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (!found) console.warn("ā¹ļø No matching entries found.");
|
|
44
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
|
+
|
|
3
|
+
import { getPackageVersion } from "../utils/pkg.js";
|
|
4
|
+
|
|
5
|
+
const FRAMEWORK_VERSION = getPackageVersion();
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Run ESLint for the project (same as npm run lint).
|
|
9
|
+
*/
|
|
10
|
+
export function lintCommand(): void {
|
|
11
|
+
console.warn(`š Running ESLint (v${FRAMEWORK_VERSION})...`);
|
|
12
|
+
const projectRoot = process.cwd();
|
|
13
|
+
try {
|
|
14
|
+
execSync("npm run lint", {
|
|
15
|
+
cwd: projectRoot,
|
|
16
|
+
stdio: "inherit",
|
|
17
|
+
env: process.env,
|
|
18
|
+
});
|
|
19
|
+
console.warn("\nā
ESLint passed with no errors.");
|
|
20
|
+
} catch {
|
|
21
|
+
console.warn("\nā ESLint reported errors. Fix violations before committing.");
|
|
22
|
+
console.warn(" Tip: npm run lint -- --fix");
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
import { getFrameworkDir } from "../utils/memory.js";
|
|
5
|
+
import { normalizeAgentName } from "../utils/string.js";
|
|
6
|
+
|
|
7
|
+
const targetDir = process.cwd(); // Assuming targetDir is process.cwd() in the CLI context
|
|
8
|
+
|
|
9
|
+
export function logAgentActionCommand(data: { agent?: unknown; action?: string; requestId?: string; status?: string; summary?: string; files?: string[]; details?: Record<string, unknown> }) {
|
|
10
|
+
const frameworkDir = getFrameworkDir();
|
|
11
|
+
const logsDir = path.join(targetDir, frameworkDir, "logs");
|
|
12
|
+
if (!fs.existsSync(logsDir)) {
|
|
13
|
+
fs.mkdirSync(logsDir, { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const agent = normalizeAgentName(data.agent);
|
|
17
|
+
const logPath = path.join(logsDir, `${agent}.json`);
|
|
18
|
+
let logs: Record<string, unknown>[] = [];
|
|
19
|
+
|
|
20
|
+
if (fs.existsSync(logPath)) {
|
|
21
|
+
try {
|
|
22
|
+
logs = JSON.parse(fs.readFileSync(logPath, "utf8"));
|
|
23
|
+
if (!Array.isArray(logs)) logs = [];
|
|
24
|
+
} catch {
|
|
25
|
+
logs = [];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const newEntry = {
|
|
30
|
+
timestamp: new Date().toISOString(),
|
|
31
|
+
...data,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
logs.push(newEntry);
|
|
35
|
+
fs.writeFileSync(logPath, JSON.stringify(logs, null, 2));
|
|
36
|
+
console.warn(`ā
Logged action to ${frameworkDir}/logs/${agent}.json`);
|
|
37
|
+
}
|