agent-enderun 0.1.10 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/.enderun/BRAIN_DASHBOARD.md +43 -0
  2. package/.enderun/ENDERUN.md +203 -0
  3. package/.enderun/PROJECT_MEMORY.md +137 -36
  4. package/.enderun/agents/analyst.md +21 -10
  5. package/.enderun/agents/backend.md +12 -11
  6. package/.enderun/agents/explorer.md +10 -7
  7. package/.enderun/agents/frontend.md +9 -20
  8. package/.enderun/agents/git.md +16 -12
  9. package/.enderun/agents/manager.md +14 -15
  10. package/.enderun/agents/mobile.md +5 -5
  11. package/.enderun/agents/native.md +5 -5
  12. package/.enderun/benchmarks/.gitkeep +0 -0
  13. package/.enderun/cli-commands.json +13 -1
  14. package/.enderun/config.json +1 -1
  15. package/.enderun/docs/api/README.md +10 -9
  16. package/.enderun/docs/api/auth.md +11 -0
  17. package/.enderun/docs/api/errors.md +7 -0
  18. package/.enderun/docs/error-handling.md +12 -0
  19. package/.enderun/docs/privacy.md +3 -0
  20. package/.enderun/docs/security.md +12 -0
  21. package/.enderun/docs/tech-stack.md +1 -0
  22. package/.enderun/docs/troubleshooting.md +7 -0
  23. package/.enderun/knowledge/api_design_rules.md +6 -0
  24. package/.enderun/knowledge/async_error_handling.md +18 -0
  25. package/.enderun/knowledge/branded_types_pattern.md +1 -0
  26. package/.enderun/knowledge/code_review_checklist.md +7 -0
  27. package/.enderun/knowledge/contract_versioning.md +7 -0
  28. package/.enderun/knowledge/database_migration.md +6 -0
  29. package/.enderun/knowledge/deployment_checklist.md +7 -0
  30. package/.enderun/knowledge/git_commit_strategy.md +10 -0
  31. package/.enderun/knowledge/legacy_onboarding.md +7 -0
  32. package/.enderun/knowledge/monitoring_setup.md +5 -0
  33. package/.enderun/knowledge/performance_guidelines.md +11 -0
  34. package/.enderun/knowledge/repository_patterns.md +9 -0
  35. package/.enderun/knowledge/security_scanning.md +6 -0
  36. package/.enderun/knowledge/testing_standards.md +7 -0
  37. package/.enderun/knowledge/troubleshooting_guide.md +5 -0
  38. package/.enderun/knowledge/zero_ui_library_policy.md +1 -0
  39. package/.enderun/logs/analyst.json +1 -0
  40. package/.enderun/logs/backend.json +1 -0
  41. package/.enderun/logs/explorer.json +1 -0
  42. package/.enderun/logs/frontend.json +1 -0
  43. package/.enderun/logs/git.json +1 -0
  44. package/.enderun/logs/manager.json +363 -0
  45. package/.enderun/logs/mobile.json +1 -0
  46. package/.enderun/logs/native.json +1 -0
  47. package/.enderun/monitoring/.gitkeep +0 -0
  48. package/ENDERUN.md +8 -8
  49. package/LICENSE +21 -0
  50. package/README.md +595 -195
  51. package/bin/cli.js +306 -79
  52. package/package.json +35 -2
  53. package/packages/framework-mcp/README.md +47 -81
  54. package/packages/framework-mcp/dist/index.js +13 -971
  55. package/packages/framework-mcp/dist/schemas.js +84 -0
  56. package/packages/framework-mcp/dist/tools/academy.js +184 -0
  57. package/packages/framework-mcp/dist/tools/codebase.js +294 -0
  58. package/packages/framework-mcp/dist/tools/contract.js +95 -0
  59. package/packages/framework-mcp/dist/tools/database.js +52 -0
  60. package/packages/framework-mcp/dist/tools/framework.js +161 -0
  61. package/packages/framework-mcp/dist/tools/git.js +53 -0
  62. package/packages/framework-mcp/dist/tools/index.js +42 -0
  63. package/packages/framework-mcp/dist/tools/knowledge.js +69 -0
  64. package/packages/framework-mcp/dist/tools/memory.js +94 -0
  65. package/packages/framework-mcp/dist/tools/messages.js +71 -0
  66. package/packages/framework-mcp/dist/tools/repository.js +76 -0
  67. package/packages/framework-mcp/dist/tools/security.js +122 -0
  68. package/packages/framework-mcp/dist/utils.js +82 -0
  69. package/packages/framework-mcp/package-lock.json +1836 -0
  70. package/packages/framework-mcp/package.json +1 -1
  71. package/packages/framework-mcp/src/index.ts +20 -970
  72. package/packages/framework-mcp/src/schemas.ts +106 -0
  73. package/packages/framework-mcp/src/tools/academy.ts +178 -0
  74. package/packages/framework-mcp/src/tools/codebase.ts +284 -0
  75. package/packages/framework-mcp/src/tools/contract.ts +91 -0
  76. package/packages/framework-mcp/src/tools/database.ts +49 -0
  77. package/packages/framework-mcp/src/tools/framework.ts +157 -0
  78. package/packages/framework-mcp/src/tools/git.ts +43 -0
  79. package/packages/framework-mcp/src/tools/index.ts +45 -0
  80. package/packages/framework-mcp/src/tools/knowledge.ts +68 -0
  81. package/packages/framework-mcp/src/tools/memory.ts +88 -0
  82. package/packages/framework-mcp/src/tools/messages.ts +70 -0
  83. package/packages/framework-mcp/src/tools/repository.ts +76 -0
  84. package/packages/framework-mcp/src/tools/security.ts +122 -0
  85. package/packages/framework-mcp/src/utils.ts +90 -0
  86. package/packages/framework-mcp/tests/mcp-server.test.ts +6 -0
  87. package/packages/shared-types/README.md +28 -51
  88. package/packages/shared-types/contract.version.json +4 -4
  89. package/packages/shared-types/dist/index.d.ts +80 -48
  90. package/packages/shared-types/dist/index.d.ts.map +1 -1
  91. package/packages/shared-types/dist/index.js +5 -8
  92. package/packages/shared-types/dist/index.js.map +1 -1
  93. package/packages/shared-types/package.json +1 -1
  94. package/packages/shared-types/src/index.ts +79 -51
  95. package/CHANGELOG.md +0 -97
  96. package/CLAUDE.md +0 -7
  97. package/CODEX.md +0 -7
  98. package/CURSOR.md +0 -7
  99. package/GEMINI.md +0 -7
  100. package/docs/tech-stack.md +0 -10
  101. package/gemini-extension.json +0 -5
  102. package/panda.config.ts +0 -20
  103. /package/{docs → .enderun/docs}/project-docs.md +0 -0
@@ -0,0 +1,122 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { Project, SyntaxKind } from "ts-morph";
4
+ import { collectFilesRecursively, resolveSafePath, buildLineMatches } from "../utils.js";
5
+ import { SECURITY_AUDIT_ARGS_SCHEMA, ANALYZE_CONSTITUTION_COMPLIANCE_ARGS_SCHEMA } from "../schemas.js";
6
+ export const securityTools = [
7
+ {
8
+ name: "security_audit_scan",
9
+ description: "Scans the codebase for security vulnerabilities like hardcoded secrets, raw SQL, and unsafe async patterns.",
10
+ inputSchema: {
11
+ type: "object",
12
+ properties: {
13
+ path: {
14
+ type: "string",
15
+ description: "Path to scan (relative to project root)",
16
+ default: ".",
17
+ },
18
+ },
19
+ },
20
+ },
21
+ {
22
+ name: "analyze_constitution_compliance",
23
+ description: "Scans a file or directory for violations of the project's ENDERUN.md rules (e.g. Zero UI Library, Branded Types).",
24
+ inputSchema: {
25
+ type: "object",
26
+ properties: {
27
+ path: {
28
+ type: "string",
29
+ description: "Path to scan (relative to project root)",
30
+ },
31
+ },
32
+ required: ["path"],
33
+ },
34
+ },
35
+ ];
36
+ export const securityHandlers = {
37
+ security_audit_scan: async (args, projectRoot) => {
38
+ const parsed = SECURITY_AUDIT_ARGS_SCHEMA.safeParse(args ?? {});
39
+ if (!parsed.success)
40
+ return { content: [{ type: "text", text: "Invalid path argument." }] };
41
+ const vulnerabilities = [];
42
+ const scanRules = [
43
+ { pattern: /sql`/, message: "Potential Raw SQL usage detected (check Kysely usage)", severity: "HIGH" },
44
+ { pattern: /(?<!\/\/\s*)console\.log/, message: "console.log found in production code", severity: "LOW" },
45
+ { pattern: /(password|secret|api_?key)\s*[:=]\s*['"][^'"]+['"]/i, message: "Potential hardcoded secret/password detected", severity: "CRITICAL" },
46
+ { pattern: /:\s*any(?!\w)/, message: "Usage of 'any' type detected", severity: "MEDIUM" },
47
+ { pattern: /(?<!\w)eval\s*\(/, message: "Dangerous 'eval()' usage detected", severity: "HIGH" },
48
+ { pattern: /\.innerHTML\s*=/, message: "Unsafe innerHTML assignment detected (XSS risk)", severity: "MEDIUM" },
49
+ { pattern: /dangerouslySetInnerHTML/, message: "React dangerouslySetInnerHTML detected", severity: "MEDIUM" },
50
+ { pattern: /TODO:/, message: "Outstanding TODO item found", severity: "LOW" },
51
+ ];
52
+ try {
53
+ const safeTargetPath = resolveSafePath(projectRoot, parsed.data.path);
54
+ if (!fs.existsSync(safeTargetPath))
55
+ return { content: [{ type: "text", text: "Target path not found." }] };
56
+ const files = collectFilesRecursively(safeTargetPath, new Set(["ts", "tsx"]));
57
+ for (const rule of scanRules) {
58
+ const ruleMatches = buildLineMatches(files, (line) => typeof rule.pattern === "string" ? line.includes(rule.pattern) : rule.pattern.test(line), 5, projectRoot);
59
+ if (ruleMatches.length > 0)
60
+ vulnerabilities.push(`[${rule.severity}] ${rule.message}:\n${ruleMatches.join("\n")}`);
61
+ }
62
+ const tsProject = new Project({ compilerOptions: { allowJs: true } });
63
+ tsProject.addSourceFilesAtPaths(path.join(safeTargetPath, "**/*.{ts,tsx}"));
64
+ for (const sourceFile of tsProject.getSourceFiles()) {
65
+ const relativePath = path.relative(projectRoot, sourceFile.getFilePath());
66
+ sourceFile.forEachDescendant((node) => {
67
+ if (node.getKindName() === "AnyKeyword") {
68
+ vulnerabilities.push(`[MEDIUM] Precise 'any' type detected in AST at ${relativePath}:${node.getStartLineNumber()}`);
69
+ }
70
+ if (node.getKind() === SyntaxKind.CallExpression) {
71
+ const callExp = node.asKind(SyntaxKind.CallExpression);
72
+ if (callExp?.getExpression().getText() === "console.log") {
73
+ vulnerabilities.push(`[LOW] Production 'console.log' detected in AST at ${relativePath}:${node.getStartLineNumber()}`);
74
+ }
75
+ }
76
+ });
77
+ }
78
+ return { content: [{ type: "text", text: vulnerabilities.length > 0 ? `### ADVANCED SECURITY AUDIT RESULTS\n\n${Array.from(new Set(vulnerabilities)).join("\n\n")}` : "No security patterns or rule violations detected (Regex & AST verified)." }] };
79
+ }
80
+ catch (error) {
81
+ return { content: [{ type: "text", text: "Security scan failed." }] };
82
+ }
83
+ },
84
+ analyze_constitution_compliance: async (args, projectRoot) => {
85
+ const parsed = ANALYZE_CONSTITUTION_COMPLIANCE_ARGS_SCHEMA.safeParse(args ?? {});
86
+ if (!parsed.success)
87
+ return { content: [{ type: "text", text: "Invalid path argument." }] };
88
+ try {
89
+ const safePath = resolveSafePath(projectRoot, parsed.data.path);
90
+ const violations = [];
91
+ const checkFile = (filePath) => {
92
+ const content = fs.readFileSync(filePath, "utf-8");
93
+ const relativePath = path.relative(projectRoot, filePath);
94
+ const forbiddenLibraries = ["@shadcn", "mui", "@chakra-ui", "antd", "bootstrap", "tailwindcss"];
95
+ for (const lib of forbiddenLibraries) {
96
+ if (content.includes(lib))
97
+ violations.push(`${relativePath}: Violation of Zero UI Library Policy (found '${lib}')`);
98
+ }
99
+ if (content.includes("console.log") && !filePath.includes("cli.js") && !filePath.includes("node_modules")) {
100
+ violations.push(`${relativePath}: Violation of Logging Policy (found 'console.log', use proper logger)`);
101
+ }
102
+ if (content.includes("sql`") && !filePath.includes("node_modules")) {
103
+ violations.push(`${relativePath}: Potential Violation of Kysely Standards (found raw 'sql' tag)`);
104
+ }
105
+ if (filePath.endsWith(".ts") && !filePath.includes("shared-types") && !filePath.includes("node_modules")) {
106
+ if (content.match(/interface.*ID\s*:\s*string/))
107
+ violations.push(`${relativePath}: Violation of Branded Types Law (found ID typed as plain string)`);
108
+ }
109
+ };
110
+ if (fs.lstatSync(safePath).isDirectory()) {
111
+ collectFilesRecursively(safePath, new Set(["ts", "tsx", "js", "jsx"])).forEach(checkFile);
112
+ }
113
+ else {
114
+ checkFile(safePath);
115
+ }
116
+ return { content: [{ type: "text", text: `### CONSTITUTION COMPLIANCE REPORT\n\n` + (violations.length > 0 ? `⚠️ **VIOLATIONS FOUND:**\n${violations.map(v => `- ${v}`).join("\n")}` : "✅ **ALL SYSTEMS COMPLIANT:** No violations of ENDERUN.md rules detected.") }] };
117
+ }
118
+ catch (error) {
119
+ return { content: [{ type: "text", text: "Compliance analysis failed." }] };
120
+ }
121
+ },
122
+ };
@@ -0,0 +1,82 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ export const FRAMEWORK_VERSION = "0.2.0";
4
+ export function getFrameworkDir(projectRoot) {
5
+ const adapters = [".gemini", ".claude", ".cursor", ".codex", ".enderun"];
6
+ for (const adp of adapters) {
7
+ const fullPath = path.join(projectRoot, adp);
8
+ if (fs.existsSync(fullPath) && fs.lstatSync(fullPath).isDirectory()) {
9
+ return adp;
10
+ }
11
+ }
12
+ return ".enderun";
13
+ }
14
+ export function resolveSafePath(projectRoot, targetPath) {
15
+ const resolved = path.resolve(projectRoot, targetPath);
16
+ const relative = path.relative(projectRoot, resolved);
17
+ if (relative.startsWith("..") || path.isAbsolute(relative)) {
18
+ throw new Error("Path escapes project root.");
19
+ }
20
+ return resolved;
21
+ }
22
+ export function collectFilesRecursively(targetPath, extensions) {
23
+ const results = [];
24
+ const entries = fs.readdirSync(targetPath, { withFileTypes: true });
25
+ for (const entry of entries) {
26
+ const fullPath = path.join(targetPath, entry.name);
27
+ if (entry.isDirectory()) {
28
+ if (entry.name === "node_modules" || entry.name === ".git")
29
+ continue;
30
+ results.push(...collectFilesRecursively(fullPath, extensions));
31
+ continue;
32
+ }
33
+ const ext = path.extname(entry.name).slice(1).toLowerCase();
34
+ if (extensions.has(ext))
35
+ results.push(fullPath);
36
+ }
37
+ return results;
38
+ }
39
+ export function buildLineMatches(files, matcher, maxResults, projectRoot) {
40
+ const matches = [];
41
+ for (const filePath of files) {
42
+ if (matches.length >= maxResults)
43
+ break;
44
+ const content = fs.readFileSync(filePath, "utf-8");
45
+ const lines = content.split("\n");
46
+ for (let i = 0; i < lines.length; i++) {
47
+ if (matches.length >= maxResults)
48
+ break;
49
+ const line = lines[i];
50
+ if (!matcher(line))
51
+ continue;
52
+ const relativePath = path.relative(projectRoot, filePath);
53
+ matches.push(`${relativePath}:${i + 1}:${line}`);
54
+ }
55
+ }
56
+ return matches;
57
+ }
58
+ export function collectMarkdownArtifacts(projectRoot) {
59
+ const docsRoot = path.join(projectRoot, "docs");
60
+ if (!fs.existsSync(docsRoot))
61
+ return [];
62
+ return collectFilesRecursively(docsRoot, new Set(["md"])).map((filePath) => path.relative(projectRoot, filePath));
63
+ }
64
+ export function replaceSectionContent(markdown, sectionTitle, newBody) {
65
+ const escaped = sectionTitle.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
66
+ const sectionRegex = new RegExp(`## ${escaped}[\\s\\S]*?(?=\\n## |$)`, "m");
67
+ if (!sectionRegex.test(markdown)) {
68
+ throw new Error(`Section not found: ${sectionTitle}`);
69
+ }
70
+ return markdown.replace(sectionRegex, `## ${sectionTitle}\n\n${newBody.trim()}\n`);
71
+ }
72
+ export function prependToSection(markdown, sectionTitle, contentToPrepend) {
73
+ const escaped = sectionTitle.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
74
+ const sectionRegex = new RegExp(`(## ${escaped}\\n)([\\s\\S]*?)(?=\\n## |$)`, "m");
75
+ const match = markdown.match(sectionRegex);
76
+ if (!match) {
77
+ throw new Error(`Section not found: ${sectionTitle}`);
78
+ }
79
+ const currentBody = match[2].trimStart();
80
+ const updatedBody = `${contentToPrepend.trim()}\n\n${currentBody}`.trim();
81
+ return markdown.replace(sectionRegex, `$1\n${updatedBody}\n`);
82
+ }