agent-enderun 1.11.2 → 1.11.4

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 (126) hide show
  1. package/README.md +5 -5
  2. package/bin/validate-agent-army.js +0 -1
  3. package/dist/framework-mcp/src/index.js +11 -1
  4. package/dist/framework-mcp/src/index.js.map +1 -1
  5. package/dist/framework-mcp/src/tools/definitions.js +0 -10
  6. package/dist/framework-mcp/src/tools/definitions.js.map +1 -1
  7. package/dist/framework-mcp/src/tools/index.js +0 -2
  8. package/dist/framework-mcp/src/tools/index.js.map +1 -1
  9. package/dist/framework-mcp/src/utils/compliance.d.ts +1 -1
  10. package/dist/framework-mcp/src/utils/compliance.js +68 -19
  11. package/dist/framework-mcp/src/utils/compliance.js.map +1 -1
  12. package/dist/src/cli/adapters/scaffold.d.ts +1 -1
  13. package/dist/src/cli/adapters/scaffold.js +8 -8
  14. package/dist/src/cli/adapters/scaffold.js.map +1 -1
  15. package/dist/src/cli/commands/check.js +25 -40
  16. package/dist/src/cli/commands/check.js.map +1 -1
  17. package/dist/src/cli/commands/contract.d.ts +3 -1
  18. package/dist/src/cli/commands/contract.js +8 -2
  19. package/dist/src/cli/commands/contract.js.map +1 -1
  20. package/dist/src/cli/commands/init/scaffold-ops.js +0 -2
  21. package/dist/src/cli/commands/init/scaffold-ops.js.map +1 -1
  22. package/dist/src/cli/commands/init.js +4 -3
  23. package/dist/src/cli/commands/init.js.map +1 -1
  24. package/dist/src/cli/commands/orchestrate.js +10 -3
  25. package/dist/src/cli/commands/orchestrate.js.map +1 -1
  26. package/dist/src/cli/commands/status.js.map +1 -1
  27. package/dist/src/cli/index.js +14 -19
  28. package/dist/src/cli/index.js.map +1 -1
  29. package/dist/src/cli/utils/compliance.d.ts +5 -1
  30. package/dist/src/cli/utils/compliance.js +90 -42
  31. package/dist/src/cli/utils/compliance.js.map +1 -1
  32. package/dist/src/cli/utils/ui.js +11 -11
  33. package/dist/src/cli/utils/ui.js.map +1 -1
  34. package/dist/src/modules/adapters/definitions.js +114 -113
  35. package/dist/src/modules/adapters/definitions.js.map +1 -1
  36. package/dist/src/modules/adapters/registry.d.ts +12 -0
  37. package/dist/src/modules/adapters/registry.js +19 -0
  38. package/dist/src/modules/adapters/registry.js.map +1 -0
  39. package/dist/src/modules/adapters/shared.js +5 -4
  40. package/dist/src/modules/adapters/shared.js.map +1 -1
  41. package/dist/src/modules/agents/definitions.d.ts +6 -6
  42. package/dist/src/modules/agents/definitions.js +49 -25
  43. package/dist/src/modules/agents/definitions.js.map +1 -1
  44. package/dist/src/modules/agents/registry/analyst.js +1 -1
  45. package/dist/src/modules/agents/registry/analyst.js.map +1 -1
  46. package/dist/src/modules/agents/registry/architect.js +1 -1
  47. package/dist/src/modules/agents/registry/architect.js.map +1 -1
  48. package/dist/src/modules/agents/registry/backend.js +1 -1
  49. package/dist/src/modules/agents/registry/backend.js.map +1 -1
  50. package/dist/src/modules/agents/registry/database.js +1 -1
  51. package/dist/src/modules/agents/registry/database.js.map +1 -1
  52. package/dist/src/modules/agents/registry/devops.js +1 -1
  53. package/dist/src/modules/agents/registry/devops.js.map +1 -1
  54. package/dist/src/modules/agents/registry/explorer.js +1 -1
  55. package/dist/src/modules/agents/registry/explorer.js.map +1 -1
  56. package/dist/src/modules/agents/registry/frontend.js +1 -1
  57. package/dist/src/modules/agents/registry/frontend.js.map +1 -1
  58. package/dist/src/modules/agents/registry/git.js +1 -1
  59. package/dist/src/modules/agents/registry/git.js.map +1 -1
  60. package/dist/src/modules/agents/registry/manager.js +1 -2
  61. package/dist/src/modules/agents/registry/manager.js.map +1 -1
  62. package/dist/src/modules/agents/registry/mobile.js +1 -1
  63. package/dist/src/modules/agents/registry/mobile.js.map +1 -1
  64. package/dist/src/modules/agents/registry/quality.js +1 -1
  65. package/dist/src/modules/agents/registry/quality.js.map +1 -1
  66. package/dist/src/modules/agents/registry/security.js +1 -1
  67. package/dist/src/modules/agents/registry/security.js.map +1 -1
  68. package/dist/src/shared/fs.js +4 -3
  69. package/dist/src/shared/fs.js.map +1 -1
  70. package/dist/tests/agents-definitions.test.js +2 -1
  71. package/dist/tests/agents-definitions.test.js.map +1 -1
  72. package/dist/tests/integration/hermes_locking.test.d.ts +1 -0
  73. package/dist/tests/integration/hermes_locking.test.js +62 -0
  74. package/dist/tests/integration/hermes_locking.test.js.map +1 -0
  75. package/dist/tests/orchestrate.test.js +9 -6
  76. package/dist/tests/orchestrate.test.js.map +1 -1
  77. package/framework-mcp/dist/index.js +11 -1
  78. package/framework-mcp/dist/tools/definitions.js +0 -10
  79. package/framework-mcp/dist/tools/index.js +0 -2
  80. package/framework-mcp/dist/utils/compliance.js +68 -19
  81. package/framework-mcp/node_modules/.package-lock.json +1179 -0
  82. package/framework-mcp/package-lock.json +1191 -0
  83. package/framework-mcp/package.json +2 -2
  84. package/framework-mcp/src/index.ts +13 -1
  85. package/framework-mcp/src/tools/definitions.ts +0 -10
  86. package/framework-mcp/src/tools/index.ts +0 -2
  87. package/framework-mcp/src/utils/compliance.ts +82 -24
  88. package/package.json +3 -4
  89. package/src/cli/adapters/scaffold.ts +9 -8
  90. package/src/cli/commands/check.ts +26 -48
  91. package/src/cli/commands/contract.ts +5 -2
  92. package/src/cli/commands/init/scaffold-ops.ts +0 -3
  93. package/src/cli/commands/init.ts +5 -3
  94. package/src/cli/commands/orchestrate.ts +12 -3
  95. package/src/cli/commands/status.ts +3 -2
  96. package/src/cli/index.ts +14 -20
  97. package/src/cli/utils/compliance.ts +101 -47
  98. package/src/cli/utils/ui.ts +11 -11
  99. package/src/modules/adapters/definitions.ts +82 -57
  100. package/src/modules/adapters/registry.ts +27 -0
  101. package/src/modules/adapters/shared.ts +5 -4
  102. package/src/modules/agents/definitions.ts +53 -24
  103. package/src/modules/agents/registry/analyst.ts +1 -1
  104. package/src/modules/agents/registry/architect.ts +1 -1
  105. package/src/modules/agents/registry/backend.ts +1 -1
  106. package/src/modules/agents/registry/database.ts +1 -1
  107. package/src/modules/agents/registry/devops.ts +1 -1
  108. package/src/modules/agents/registry/explorer.ts +1 -1
  109. package/src/modules/agents/registry/frontend.ts +1 -1
  110. package/src/modules/agents/registry/git.ts +1 -1
  111. package/src/modules/agents/registry/manager.ts +1 -2
  112. package/src/modules/agents/registry/mobile.ts +1 -1
  113. package/src/modules/agents/registry/quality.ts +1 -1
  114. package/src/modules/agents/registry/security.ts +1 -1
  115. package/src/shared/fs.ts +4 -3
  116. package/templates/standards/frontend-standards.md +7 -2
  117. package/templates/standards/governance-standards.md +27 -1
  118. package/templates/standards/logging-and-secrets.md +1 -1
  119. package/templates/standards/observability-standards.md +1 -1
  120. package/templates/standards/quality-standards.md +2 -1
  121. package/bin/enderun-dashboard.js +0 -25
  122. package/framework-mcp/src/tools/dashboard/start_dashboard.ts +0 -33
  123. package/src/cli/commands/dashboard/data-service.ts +0 -167
  124. package/src/cli/commands/dashboard/html-template.ts +0 -696
  125. package/src/cli/commands/dashboard/server.ts +0 -43
  126. package/src/cli/commands/dashboard.ts +0 -11
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-enderun/mcp",
3
- "version": "1.10.5",
3
+ "version": "1.11.4",
4
4
  "description": "Agent Enderun Model Context Protocol (MCP) Server",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -14,6 +14,6 @@
14
14
  },
15
15
  "devDependencies": {
16
16
  "@types/node": "^25.9.1",
17
- "typescript": "^5.0.0"
17
+ "typescript": "^5.9.3"
18
18
  }
19
19
  }
@@ -57,13 +57,25 @@ function validateArgs(toolName: string, args: Record<string, unknown>): string |
57
57
  return null;
58
58
  }
59
59
 
60
- server.setRequestHandler(ListToolsRequestSchema, async () => {
60
+ server.setRequestHandler(ListToolsRequestSchema, async (request) => {
61
+ // 2026 Stateless Spec: Log client info from metadata if available
62
+ const meta = (request as any)._meta;
63
+ if (meta) {
64
+ process.stderr.write(`[MCP] Stateless ListTools from ${meta.client?.name || "unknown"} v${meta.client?.version || "?.?"}\n`);
65
+ }
61
66
  return { tools: TOOLS };
62
67
  });
63
68
 
64
69
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
65
70
  const req = request as { params: { name: string, arguments?: Record<string, unknown> } };
66
71
  const { name, arguments: args } = req.params;
72
+ const meta = (request as any)._meta;
73
+
74
+ // 2026 Stateless Spec: Prioritize metadata-driven context
75
+ if (meta) {
76
+ process.stderr.write(`[MCP] Stateless CallTool: ${name} (Client: ${meta.client?.name || "unknown"})\n`);
77
+ }
78
+
67
79
  const projectRoot = process.env.ENDERUN_PROJECT_ROOT || process.cwd();
68
80
 
69
81
  try {
@@ -151,16 +151,6 @@ export const TOOLS: ToolDefinition[] = [
151
151
  }
152
152
  }
153
153
  },
154
- {
155
- name: "start_dashboard",
156
- description: "Start the Enderun Web Dashboard (Command Center) to visualize real-time agent status, logs, and system metrics in the browser.",
157
- inputSchema: {
158
- type: "object",
159
- properties: {
160
- port: { type: "number", description: "Optional: Port to run the dashboard on (default: 5858)" }
161
- }
162
- }
163
- },
164
154
  {
165
155
  name: "get_framework_status",
166
156
  description: "Get the current project phase, active traces, and agent states.",
@@ -14,7 +14,6 @@ import { handleAuditDependencies } from "./framework/audit_deps.js";
14
14
  import { handleRunTests } from "./framework/run_tests.js";
15
15
  import { handleGetSystemHealth } from "./observability/get_health.js";
16
16
  import { handleCheckPorts } from "./observability/check_ports.js";
17
- import { handleStartDashboard } from "./dashboard/start_dashboard.js";
18
17
  import { handleOrchestrateLoop } from "./framework/orchestrate.js";
19
18
  import { handleUpdateContractHash } from "./framework/update_contract_hash.js";
20
19
  import { handleReadProjectMemory } from "./memory/read_memory.js";
@@ -51,7 +50,6 @@ export const toolHandlers: Record<string, ToolHandler> = {
51
50
  run_tests: bind(handleRunTests),
52
51
  get_system_health: bind(handleGetSystemHealth),
53
52
  check_active_ports: bind(handleCheckPorts),
54
- start_dashboard: bind(handleStartDashboard),
55
53
  orchestrate_loop: bind(handleOrchestrateLoop),
56
54
  send_agent_message: bind(handleSendAgentMessage),
57
55
  log_agent_action: bind(handleLogAgentAction),
@@ -1,37 +1,95 @@
1
+ import ts from "typescript";
2
+
1
3
  /**
2
4
  * Enterprise Compliance Guardrail
3
- * Checks content against corporate standards before allowing file mutations.
5
+ * Checks content against corporate standards using AST analysis before allowing file mutations.
4
6
  */
5
7
  export function verifyCorporateCompliance(content: string, filePath: string): void {
6
- // 1. Zero Console Policy
7
- if (content.includes("console.log(") || content.includes("console.warn(") || content.includes("console.error(")) {
8
- if (!filePath.includes("logger.ts") && !filePath.includes("check.ts") && !filePath.includes("cli.ts")) {
9
- throw new Error(
10
- "❌ Corporate Compliance Breach: 'console.log/warn/error' usage is forbidden. " +
11
- "Use the 'logger' system from '@/shared/logger' instead."
12
- );
13
- }
8
+ // Skip compliance checks for non-source files or specific ignored files
9
+ if (filePath.endsWith(".json") || filePath.endsWith(".md") || filePath.endsWith(".env.example")) {
10
+ return;
14
11
  }
15
12
 
13
+ const sourceFile = ts.createSourceFile(
14
+ filePath,
15
+ content,
16
+ ts.ScriptTarget.Latest,
17
+ true
18
+ );
19
+
20
+ const errors: string[] = [];
21
+
22
+ /**
23
+ * Recursive AST Visitor
24
+ */
25
+ function visit(node: ts.Node) {
26
+ // 1. Zero Console Policy
27
+ if (ts.isPropertyAccessExpression(node)) {
28
+ const expression = node.expression;
29
+ const name = node.name.text;
30
+ if (ts.isIdentifier(expression) && expression.text === "console") {
31
+ if (["log", "warn", "error"].includes(name)) {
32
+ // Check if file is exempt
33
+ if (!filePath.includes("logger.ts") && !filePath.includes("check.ts") && !filePath.includes("cli.ts")) {
34
+ errors.push(`❌ Corporate Compliance Breach: 'console.${name}' usage is forbidden at line ${sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1}.`);
35
+ }
36
+ }
37
+ }
38
+ }
39
+
16
40
  // 2. No Explicit Any Policy
17
- const anyRegex = /[:<]\s*any\b|\bas\s+any\b/g;
18
- if (anyRegex.test(content)) {
19
- if (!filePath.includes("definitions.ts") && !filePath.includes("types.ts")) {
20
- throw new Error(
21
- "❌ Corporate Compliance Breach: 'any' type is forbidden. " +
22
- "Use 'unknown', 'generics', or proper interfaces for type safety."
23
- );
41
+ if (ts.isTypeReferenceNode(node)) {
42
+ if (ts.isIdentifier(node.typeName) && node.typeName.text === "any") {
43
+ if (!filePath.includes("definitions.ts") && !filePath.includes("types.ts")) {
44
+ errors.push(`❌ Corporate Compliance Breach: 'any' type is forbidden at line ${sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1}.`);
45
+ }
46
+ }
47
+ }
48
+
49
+ // 3. Zero UI Library Policy (No @chakra-ui, mui, @shadcn)
50
+ if (ts.isImportDeclaration(node)) {
51
+ const moduleSpecifier = node.moduleSpecifier;
52
+ if (ts.isStringLiteral(moduleSpecifier)) {
53
+ const forbiddenLibs = ["@chakra-ui", "mui", "@shadcn", "antd", "bootstrap"];
54
+ const lib = forbiddenLibs.find(l => moduleSpecifier.text.includes(l));
55
+ if (lib) {
56
+ errors.push(`❌ Corporate Compliance Breach: External UI library '${lib}' usage is FORBIDDEN at line ${sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1}. Build atomic components manually instead.`);
57
+ }
58
+ }
59
+ }
60
+
61
+ // Handle 'any' as a keyword type (e.g., parameter: any)
62
+ if (node.kind === ts.SyntaxKind.AnyKeyword) {
63
+ if (!filePath.includes("definitions.ts") && !filePath.includes("types.ts")) {
64
+ errors.push(`❌ Corporate Compliance Breach: 'any' keyword is forbidden at line ${sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1}.`);
65
+ }
24
66
  }
67
+
68
+ ts.forEachChild(node, visit);
25
69
  }
26
70
 
27
- // 3. Hardcoded Secrets Guard (Simple heuristic)
28
- const secretKeywords = [/API_KEY\s*=\s*['"][^'"]+['"]/i, /SECRET\s*=\s*['"][^'"]+['"]/i, /PASSWORD\s*=\s*['"][^'"]+['"]/i];
29
- for (const regex of secretKeywords) {
30
- if (regex.test(content) && !filePath.endsWith(".env.example")) {
31
- throw new Error(
32
- "❌ Corporate Compliance Breach: Hardcoded secrets detected. " +
33
- "All sensitive data must be managed via '.env' files."
34
- );
71
+ visit(sourceFile);
72
+
73
+ // 3. Hardcoded Secrets & PII Guard
74
+ const piiKeywords = [
75
+ { regex: /API_KEY\s*=\s*['"][^'"]+['"]/i, msg: "Hardcoded API Key" },
76
+ { regex: /SECRET\s*=\s*['"][^'"]+['"]/i, msg: "Hardcoded Secret" },
77
+ { regex: /PASSWORD\s*=\s*['"][^'"]+['"]/i, msg: "Hardcoded Password" },
78
+ { regex: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/, msg: "PII Detected: Email Address" },
79
+ { regex: /\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/, msg: "PII Detected: Credit Card Pattern" }
80
+ ];
81
+
82
+ for (const { regex, msg } of piiKeywords) {
83
+ if (regex.test(content)) {
84
+ // Allow emails in specific files like README or package.json
85
+ if (msg.includes("Email") && (filePath.endsWith("README.md") || filePath.endsWith("package.json") || filePath.includes("CONTRIBUTING"))) {
86
+ continue;
87
+ }
88
+ errors.push(`❌ Corporate Compliance Breach: ${msg} detected.`);
35
89
  }
36
90
  }
91
+
92
+ if (errors.length > 0) {
93
+ throw new Error(errors.join("\n"));
94
+ }
37
95
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-enderun",
3
- "version": "1.11.2",
3
+ "version": "1.11.4",
4
4
  "description": "The Supreme AI Governance & Autonomous Orchestration Framework for Enterprise Development. Acts as a Corporate AI Management Assistant.",
5
5
  "author": "Yusuf BEKAR",
6
6
  "license": "MIT",
@@ -48,7 +48,6 @@
48
48
  "enderun:verify": "agent-enderun verify-contract",
49
49
  "enderun:update-contract": "agent-enderun update-contract",
50
50
  "enderun:check": "agent-enderun check",
51
- "enderun:dashboard": "node bin/enderun-dashboard.js",
52
51
  "test:init": "node dist/src/cli/index.js init",
53
52
  "enderun:lint": "eslint .",
54
53
  "lint": "eslint .",
@@ -66,12 +65,12 @@
66
65
  "eslint": "^10.4.0",
67
66
  "globals": "^17.6.0",
68
67
  "tsx": "^4.22.3",
69
- "typescript": "^5.0.0",
68
+ "typescript": "^5.9.3",
70
69
  "typescript-eslint": "^8.0.0",
71
70
  "vitest": "^3.0.0"
72
71
  },
73
72
  "enderun": {
74
- "version": "1.11.2",
73
+ "version": "1.11.4",
75
74
  "initializedAt": "2026-06-04T18:51:28.970Z",
76
75
  "frameworkDir": ".enderun"
77
76
  },
@@ -20,7 +20,8 @@ export function scaffoldAgents(
20
20
  dryRun: boolean,
21
21
  agentsToScaffold?: string[],
22
22
  explicitDestDir?: string,
23
- explicitExt?: string
23
+ explicitExt?: string,
24
+ paths?: Record<string, string>
24
25
  ): void {
25
26
  const adapter = ADAPTERS[adapterId];
26
27
  if (!adapter) return;
@@ -46,27 +47,27 @@ export function scaffoldAgents(
46
47
 
47
48
  switch (adapterId) {
48
49
  case "gemini":
49
- content = toGeminiCliMd(agent, baseKnowledgeDir);
50
+ content = toGeminiCliMd(agent, baseKnowledgeDir, paths);
50
51
  break;
51
52
  case "grok":
52
53
  // Grok uses same Gemini-compatible YAML format
53
- content = toGeminiCliMd(agent, baseKnowledgeDir);
54
+ content = toGeminiCliMd(agent, baseKnowledgeDir, paths);
54
55
  break;
55
56
  case "claude":
56
- content = toClaudeCodeMd(agent, baseKnowledgeDir);
57
+ content = toClaudeCodeMd(agent, baseKnowledgeDir, paths);
57
58
  break;
58
59
  case "cursor":
59
- content = toCursorMdc(agent, baseKnowledgeDir);
60
+ content = toCursorMdc(agent, baseKnowledgeDir, paths);
60
61
  break;
61
62
  case "codex":
62
- content = toCodexMd(agent, baseKnowledgeDir);
63
+ content = toCodexMd(agent, baseKnowledgeDir, paths);
63
64
  break;
64
65
  case "antigravity-cli": {
65
66
  // Antigravity uses nested folders: agents/{name}/agent.json and agents/{name}/agent.md
66
67
  const agentDir = path.join(destAgentsDir, agent.name);
67
68
  if (!dryRun) fs.mkdirSync(agentDir, { recursive: true });
68
69
 
69
- content = toAntigravityJson(agent, baseKnowledgeDir);
70
+ content = toAntigravityJson(agent, baseKnowledgeDir, paths);
70
71
  fileName = path.join(agent.name, "agent.json");
71
72
 
72
73
  secondaryContent = `# 🎖️ Agent Enderun — @${agent.name}\n\n${agent.instructions.identity}\n\n${agent.instructions.mission}`;
@@ -75,7 +76,7 @@ export function scaffoldAgents(
75
76
  }
76
77
  default:
77
78
  // Fallback to Gemini format
78
- content = toGeminiCliMd(agent, baseKnowledgeDir);
79
+ content = toGeminiCliMd(agent, baseKnowledgeDir, paths);
79
80
  break;
80
81
  }
81
82
 
@@ -2,7 +2,7 @@ import fs from "fs";
2
2
  import path from "path";
3
3
  import { execSync } from "child_process";
4
4
 
5
- import { getFrameworkDir, getMemoryPath, getConfiguredPaths, isFrameworkDevelopmentRepo } from "../utils/memory.js";
5
+ import { getFrameworkDir, getMemoryPath, getConfiguredPaths, isFrameworkDevelopmentRepo, readState } from "../utils/memory.js";
6
6
  import { getPackageVersion, getValidatorPath } from "../utils/pkg.js";
7
7
  import { UI } from "../utils/ui.js";
8
8
  import { logger } from "../../shared/logger.js";
@@ -10,16 +10,41 @@ import { scanProjectCompliance } from "../utils/compliance.js";
10
10
  import { ALL_AGENTS } from "../../modules/agents/definitions.js";
11
11
  import { detectActiveAgentLayouts, findAgentInstruction } from "../adapters/paths.js";
12
12
 
13
+ import { verifyApiContractCommand } from "./contract.js";
14
+
13
15
  export async function checkCommand() {
14
16
  UI.intent("Agent Enderun Health Check", `Checking system health and discipline rules (v${getPackageVersion()})...`);
15
17
  let issues = 0;
16
18
 
19
+ const state = readState();
20
+ if (!state) {
21
+ UI.error("Memory state not found. Run 'init' first.");
22
+ return;
23
+ }
24
+
17
25
  const frameworkDir = getFrameworkDir();
18
26
  const memoryPath = getMemoryPath();
19
27
  const pathsMap = getConfiguredPaths();
20
28
 
21
29
  UI.success(`Using framework dir: ${frameworkDir}`);
22
30
 
31
+ // --- Contract Gate (Phase 2+ Discipline) ---
32
+ if (["PHASE_2", "PHASE_3", "PHASE_4"].includes(state.phase)) {
33
+ console.warn("\n📝 Validating API Contracts (Phase 2+ Required)...");
34
+ try {
35
+ const contractOk = await verifyApiContractCommand({ silent: true });
36
+ if (!contractOk) {
37
+ UI.error("Contract verification FAILED! You cannot be in Phase 2+ with unverified contracts.");
38
+ issues++;
39
+ } else {
40
+ UI.success("API Contracts verified and sealed.");
41
+ }
42
+ } catch (err) {
43
+ UI.error("Error during contract verification.");
44
+ issues++;
45
+ }
46
+ }
47
+
23
48
  let knowledgeDir = "knowledge";
24
49
  if (frameworkDir === ".github") knowledgeDir = "instructions";
25
50
 
@@ -57,7 +82,6 @@ export async function checkCommand() {
57
82
  { name: ".env.example", path: ".env.example" },
58
83
  ...(isFrameworkDevelopment ? [{ name: "MCP Server", path: "framework-mcp/package.json" }] : []),
59
84
  { name: "Panda CSS Config", path: appPandaPath, optional: true },
60
- { name: "Brain Dashboard", path: path.join(frameworkDir, "BRAIN_DASHBOARD.md") },
61
85
  ];
62
86
 
63
87
  for (const check of checks) {
@@ -135,52 +159,6 @@ export async function checkCommand() {
135
159
  logger.debug("npx tsc --noEmit check failed", err);
136
160
  }
137
161
 
138
- interface MetricEntry {
139
- timestamp: string;
140
- agent: string;
141
- action: string;
142
- estimatedTokens: number;
143
- }
144
-
145
- const metricsPath = path.join(frameworkDir, "observability/metrics.json");
146
- if (fs.existsSync(metricsPath)) {
147
- try {
148
- const raw = fs.readFileSync(metricsPath, "utf8");
149
- const metrics: MetricEntry[] = JSON.parse(raw);
150
- if (Array.isArray(metrics) && metrics.length > 0) {
151
- console.warn("\n📊 Token Consumption Report:");
152
- let totalTokens = 0;
153
- const breakdown: Record<string, { count: number; tokens: number }> = {};
154
-
155
- for (const entry of metrics) {
156
- const tokens = typeof entry.estimatedTokens === "number" ? entry.estimatedTokens : 0;
157
- totalTokens += tokens;
158
-
159
- const actionName = (entry.action || "unknown").split(":")[0].trim();
160
- if (!breakdown[actionName]) {
161
- breakdown[actionName] = { count: 0, tokens: 0 };
162
- }
163
- breakdown[actionName].count += 1;
164
- breakdown[actionName].tokens += tokens;
165
- }
166
-
167
- console.warn(` - Total Operations: ${metrics.length}`);
168
- console.warn(` - Total Estimated Tokens: ${totalTokens}`);
169
- console.warn("\n Breakdown by Action:");
170
- for (const [action, data] of Object.entries(breakdown)) {
171
- console.warn(` - ${action}: ${data.count} ops (${data.tokens} tokens)`);
172
- }
173
- } else {
174
- console.warn("\n📊 Token Consumption Report: No token usage logged yet.");
175
- }
176
- } catch (err) {
177
- console.warn("\n📊 Token Consumption Report: Could not read or parse metrics file.");
178
- logger.debug("Failed to read or parse metrics.json in checkCommand", err);
179
- }
180
- } else {
181
- console.warn("\n📊 Token Consumption Report: No token usage logged yet.");
182
- }
183
-
184
162
  if (issues === 0) {
185
163
  UI.success("\n🚀 All systems green! Agent Enderun is ready.");
186
164
  } else {
@@ -7,13 +7,14 @@ import { ValidationError } from "../../shared/errors.js";
7
7
  /**
8
8
  * Verify type safety between backend and frontend contracts.
9
9
  */
10
- export async function verifyApiContractCommand() {
10
+ export async function verifyApiContractCommand(options: { silent?: boolean } = {}): Promise<boolean> {
11
11
  const projectRoot = process.cwd();
12
12
  const pathsMap = getConfiguredPaths();
13
13
  const sharedDir = path.join(projectRoot, pathsMap.backend, "src/types");
14
14
  const contractPath = path.join(projectRoot, pathsMap.backend, "contract.version.json");
15
15
 
16
16
  if (!fs.existsSync(sharedDir) || !fs.existsSync(contractPath)) {
17
+ if (options.silent) return false;
17
18
  throw new ValidationError(
18
19
  "API types or contract version file missing.",
19
20
  null,
@@ -25,8 +26,10 @@ export async function verifyApiContractCommand() {
25
26
  const contract = JSON.parse(fs.readFileSync(contractPath, "utf8"));
26
27
 
27
28
  if (contract.contract_hash === currentHash) {
28
- console.warn("✅ Contract is valid and synchronized.");
29
+ if (!options.silent) console.warn("✅ Contract is valid and synchronized.");
30
+ return true;
29
31
  } else {
32
+ if (options.silent) return false;
30
33
  throw new ValidationError(
31
34
  "Contract drift detected!",
32
35
  { stored: contract.contract_hash, actual: currentHash },
@@ -48,9 +48,6 @@ export function scaffoldOps(frameworkDir: string, dryRun: boolean) {
48
48
  };
49
49
  writeJsonFile(path.join(frameworkDir, "cli-commands.json"), cliCommands, dryRun);
50
50
 
51
- // Scaffold BRAIN_DASHBOARD.md
52
- writeTextFile(path.join(frameworkDir, "BRAIN_DASHBOARD.md"), "# 🧠 Brain Dashboard\n\nActive Trace: T-000\nActive Phase: PHASE_0 (Genesis)\n\n## Agent Statuses\n- @manager: READY\n- @architect: READY\n- @backend: READY\n- @frontend: READY\n- @quality: READY\n");
53
-
54
51
  // Scaffold router/routing_rules.md
55
52
  const routerDir = path.join(frameworkDir, "router");
56
53
  if (!fs.existsSync(routerDir)) fs.mkdirSync(routerDir, { recursive: true });
@@ -11,7 +11,7 @@ import {
11
11
  type AdapterId,
12
12
  } from "../adapters/index.js";
13
13
  import { ensureDir } from "../utils/fs.js";
14
- import { initializeMemory } from "../utils/memory.js";
14
+ import { initializeMemory, getConfiguredPaths } from "../utils/memory.js";
15
15
  import { getPackageVersion } from "../utils/pkg.js";
16
16
  import { UI } from "../utils/ui.js";
17
17
 
@@ -122,18 +122,20 @@ export async function initCommand(adapterName: string, options: { unified?: bool
122
122
 
123
123
  scaffoldStandards(path.join(projectRoot, coreDir), dryRun);
124
124
 
125
+ const pathsMap = getConfiguredPaths();
126
+
125
127
  if (isUnified) {
126
128
  // Scaffold ALL agents for ALL adapters under unified hub
127
129
  for (const id of ADAPTER_IDS) {
128
130
  const dest = resolveAgentsDir(id, true, aiToolDir);
129
- scaffoldAgents(projectRoot, id, dryRun, selectedAgents, dest.agentsDir, dest.agentsExt);
131
+ scaffoldAgents(projectRoot, id, dryRun, selectedAgents, dest.agentsDir, dest.agentsExt, pathsMap);
130
132
  if (!dryRun) mirrorUnifiedAgentsToNative(projectRoot, id);
131
133
  }
132
134
  UI.success(`✅ Scaffolding complete for all adapters under ${aiToolDir}/ with native mirrors.`);
133
135
  } else {
134
136
  // Standard single-adapter scaffold
135
137
  const dest = resolveAgentsDir(adapterId, false);
136
- scaffoldAgents(projectRoot, adapterId, dryRun, selectedAgents, dest.agentsDir, dest.agentsExt);
138
+ scaffoldAgents(projectRoot, adapterId, dryRun, selectedAgents, dest.agentsDir, dest.agentsExt, pathsMap);
137
139
  UI.success(`✅ Generated agent definitions under ${dest.agentsDir}/`);
138
140
  }
139
141
 
@@ -65,12 +65,21 @@ export async function orchestrateCommand(options?: { maxIterations?: number }) {
65
65
  const agentStatuses = readStatus();
66
66
  let updatedStatus = false;
67
67
  for (const [agentName, info] of Object.entries(agentStatuses)) {
68
- const { state, lastUpdated } = info as { state: string; task: string; lastUpdated?: string };
68
+ const { state, lastUpdated, task } = info as { state: string; task: string; lastUpdated?: string };
69
69
  if (state === "EXECUTING" && lastUpdated) {
70
70
  const lastUpdatedTime = Date.parse(lastUpdated);
71
71
  if (!isNaN(lastUpdatedTime) && Date.now() - lastUpdatedTime > 30 * 60 * 1000) {
72
- UI.error(`⚠️ Agent ${agentName} has TIMED OUT (in EXECUTING state for >30m). Transitioning to BLOCKED.`);
73
- agentStatuses[agentName] = { state: "BLOCKED", task: "Timeout occurred", lastUpdated: new Date().toISOString() };
72
+ const displayAgent = agentName.startsWith("@") ? agentName : `@${agentName}`;
73
+ UI.error(`⚠️ Agent ${displayAgent} has TIMED OUT (in EXECUTING state for >30m). Triggering Self-Healing...`);
74
+
75
+ // SELF-HEALING: Log the failure and reset to READY
76
+ agentStatuses[agentName] = {
77
+ state: "READY",
78
+ task: "Idle (Recovered from Timeout)",
79
+ lastUpdated: new Date().toISOString()
80
+ };
81
+
82
+ UI.success(`✅ Hermes Self-Healing: ${displayAgent} reset to READY state. Task "${task}" aborted for recovery.`);
74
83
  updatedStatus = true;
75
84
  }
76
85
  }
@@ -1,6 +1,7 @@
1
1
  import chalk from "chalk";
2
-
3
- import { listTasks, readState, readStatus } from "../utils/memory.js";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { listTasks, readState, readStatus, getFrameworkDir } from "../utils/memory.js";
4
5
 
5
6
  /**
6
7
  * Print the current framework status.
package/src/cli/index.ts CHANGED
@@ -32,8 +32,8 @@ async function main() {
32
32
  const to = command;
33
33
  const content = args.slice(1).join(" ");
34
34
  if (!content) {
35
- console.error(`❌ Error: Missing task content for ${to}.`);
36
- console.warn(`Usage: agent-enderun ${to} "Your task description"`);
35
+ process.stderr.write(`❌ Error: Missing task content for ${to}.\n`);
36
+ process.stderr.write(`Usage: agent-enderun ${to} "Your task description"\n`);
37
37
  process.exit(64);
38
38
  }
39
39
 
@@ -54,8 +54,8 @@ async function main() {
54
54
  traceId
55
55
  });
56
56
 
57
- console.warn(`✅ Task delegated to ${to} (Trace: ${traceId})`);
58
- console.warn("👉 Run \"agent-enderun orchestrate\" to process.");
57
+ process.stdout.write(`✅ Task delegated to ${to} (Trace: ${traceId})\n`);
58
+ process.stdout.write("👉 Run \"agent-enderun orchestrate\" to process.\n");
59
59
  return;
60
60
  }
61
61
 
@@ -93,19 +93,13 @@ async function main() {
93
93
  const section = args[1];
94
94
  const content = args[2];
95
95
  if (!section || !content) {
96
- console.error("❌ Error: section and content are required.");
96
+ process.stderr.write("❌ Error: section and content are required.\n");
97
97
  process.exit(64);
98
98
  }
99
99
  await updateProjectMemoryCommand(section, content);
100
100
  break;
101
101
  }
102
102
 
103
- case "dashboard": {
104
- const { dashboardCommand } = await import("./commands/dashboard.js");
105
- await dashboardCommand(args.slice(1));
106
- break;
107
- }
108
-
109
103
  case "orchestrate":
110
104
  case "loop":
111
105
  await orchestrateCommand();
@@ -114,7 +108,7 @@ async function main() {
114
108
  case "approve": {
115
109
  const traceId = args[1];
116
110
  if (!traceId) {
117
- console.error("❌ Error: traceId is required.");
111
+ process.stderr.write("❌ Error: traceId is required.\n");
118
112
  process.exit(64);
119
113
  }
120
114
  await approveCommand(traceId);
@@ -228,7 +222,7 @@ async function main() {
228
222
  case "version":
229
223
  case "-v":
230
224
  case "--version":
231
- console.warn(`v${getPackageVersion()}`);
225
+ process.stdout.write(`v${getPackageVersion()}\n`);
232
226
  break;
233
227
 
234
228
  case "help":
@@ -258,8 +252,8 @@ async function main() {
258
252
  traceId
259
253
  });
260
254
 
261
- console.warn(`📡 Request sent to @manager: "${content}" (Trace: ${traceId})`);
262
- console.warn("👉 Run 'agent-enderun orchestrate' to process.");
255
+ process.stdout.write(`📡 Request sent to @manager: "${content}" (Trace: ${traceId})\n`);
256
+ process.stdout.write("👉 Run 'agent-enderun orchestrate' to process.\n");
263
257
  } else {
264
258
  showHelp();
265
259
  }
@@ -268,7 +262,7 @@ async function main() {
268
262
  }
269
263
 
270
264
  function showHelp() {
271
- console.warn(`
265
+ process.stdout.write(`
272
266
  🎖️ Agent Enderun CLI (v${getPackageVersion()}) — The Supreme AI Orchestrator
273
267
 
274
268
  Usage:
@@ -307,7 +301,7 @@ Natural Language:
307
301
  Example:
308
302
  agent-enderun "Audit my project"
309
303
  agent-enderun @backend "Create the login page"
310
- `);
304
+ \n`);
311
305
  }
312
306
 
313
307
  import { EnderunBaseError } from "../shared/errors.js";
@@ -315,12 +309,12 @@ import { logger } from "../shared/logger.js";
315
309
 
316
310
  main().catch((err) => {
317
311
  if (err instanceof EnderunBaseError) {
318
- console.error(`\n❌ [${err.code}] Error: ${err.message}`);
312
+ process.stderr.write(`\n❌ [${err.code}] Error: ${err.message}\n`);
319
313
  if (err.solution) {
320
- console.warn(`💡 Solution Tip: ${err.solution}\n`);
314
+ process.stderr.write(`💡 Solution Tip: ${err.solution}\n\n`);
321
315
  }
322
316
  } else {
323
- console.error(`\n❌ Fatal Error: ${err.message || String(err)}`);
317
+ process.stderr.write(`\n❌ Fatal Error: ${err.message || String(err)}\n`);
324
318
  }
325
319
 
326
320
  logger.fatal("Fatal exception during CLI execution", err);