agent-enderun 1.11.4 β†’ 1.11.5

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 (77) hide show
  1. package/README.md +84 -277
  2. package/dist/framework-mcp/src/index.js.map +1 -1
  3. package/dist/framework-mcp/src/tools/control_plane/locking.js +17 -3
  4. package/dist/framework-mcp/src/tools/control_plane/locking.js.map +1 -1
  5. package/dist/framework-mcp/src/tools/memory/get_insights.js +4 -4
  6. package/dist/framework-mcp/src/tools/memory/get_insights.js.map +1 -1
  7. package/dist/framework-mcp/src/tools/messaging/send_message.js +10 -5
  8. package/dist/framework-mcp/src/tools/messaging/send_message.js.map +1 -1
  9. package/dist/framework-mcp/src/tools/search/grep_search.js +2 -3
  10. package/dist/framework-mcp/src/tools/search/grep_search.js.map +1 -1
  11. package/dist/framework-mcp/src/utils/compliance.js.map +1 -1
  12. package/dist/framework-mcp/tests/tools/messaging/send_message.test.js +7 -7
  13. package/dist/framework-mcp/tests/tools/messaging/send_message.test.js.map +1 -1
  14. package/dist/src/cli/commands/check.js +1 -1
  15. package/dist/src/cli/commands/check.js.map +1 -1
  16. package/dist/src/cli/commands/init/scaffold-core.d.ts +1 -0
  17. package/dist/src/cli/commands/init/scaffold-core.js +14 -1
  18. package/dist/src/cli/commands/init/scaffold-core.js.map +1 -1
  19. package/dist/src/cli/commands/init.d.ts +1 -0
  20. package/dist/src/cli/commands/init.js +12 -4
  21. package/dist/src/cli/commands/init.js.map +1 -1
  22. package/dist/src/cli/commands/orchestrate.js +21 -0
  23. package/dist/src/cli/commands/orchestrate.js.map +1 -1
  24. package/dist/src/cli/commands/plan.js +1 -2
  25. package/dist/src/cli/commands/plan.js.map +1 -1
  26. package/dist/src/cli/commands/status.js.map +1 -1
  27. package/dist/src/cli/index.js +4 -2
  28. package/dist/src/cli/index.js.map +1 -1
  29. package/dist/src/cli/shims.js +39 -26
  30. package/dist/src/cli/shims.js.map +1 -1
  31. package/dist/src/modules/adapters/definitions.js +16 -0
  32. package/dist/src/modules/adapters/definitions.js.map +1 -1
  33. package/dist/src/modules/adapters/types.d.ts +1 -1
  34. package/dist/src/modules/adapters/types.js +1 -1
  35. package/dist/src/modules/adapters/types.js.map +1 -1
  36. package/dist/src/modules/agents/definitions.js +1 -1
  37. package/dist/src/shared/constants.d.ts +5 -2
  38. package/dist/src/shared/constants.js +5 -0
  39. package/dist/src/shared/constants.js.map +1 -1
  40. package/dist/tests/approve.test.js +3 -1
  41. package/dist/tests/approve.test.js.map +1 -1
  42. package/dist/tests/integration/agent_flow.test.js +2 -0
  43. package/dist/tests/integration/agent_flow.test.js.map +1 -1
  44. package/dist/tests/integration/hermes_locking.test.js +2 -0
  45. package/dist/tests/integration/hermes_locking.test.js.map +1 -1
  46. package/dist/tests/memory-utils.test.js +2 -0
  47. package/dist/tests/memory-utils.test.js.map +1 -1
  48. package/dist/tests/orchestrate.test.js +2 -0
  49. package/dist/tests/orchestrate.test.js.map +1 -1
  50. package/dist/tests/status.test.js +2 -0
  51. package/dist/tests/status.test.js.map +1 -1
  52. package/framework-mcp/dist/tools/control_plane/locking.js +17 -3
  53. package/framework-mcp/dist/tools/memory/get_insights.js +4 -4
  54. package/framework-mcp/dist/tools/messaging/send_message.js +10 -5
  55. package/framework-mcp/dist/tools/search/grep_search.js +2 -3
  56. package/framework-mcp/package-lock.json +2 -2
  57. package/framework-mcp/package.json +1 -1
  58. package/framework-mcp/src/index.ts +2 -2
  59. package/framework-mcp/src/tools/control_plane/locking.ts +18 -4
  60. package/framework-mcp/src/tools/memory/get_insights.ts +4 -4
  61. package/framework-mcp/src/tools/messaging/send_message.ts +11 -5
  62. package/framework-mcp/src/tools/search/grep_search.ts +2 -3
  63. package/framework-mcp/src/utils/compliance.ts +16 -16
  64. package/framework-mcp/tests/tools/messaging/send_message.test.ts +7 -7
  65. package/package.json +4 -3
  66. package/src/cli/commands/check.ts +1 -1
  67. package/src/cli/commands/init/scaffold-core.ts +18 -2
  68. package/src/cli/commands/init.ts +14 -5
  69. package/src/cli/commands/orchestrate.ts +24 -0
  70. package/src/cli/commands/plan.ts +1 -2
  71. package/src/cli/commands/status.ts +1 -3
  72. package/src/cli/index.ts +4 -2
  73. package/src/cli/shims.ts +39 -26
  74. package/src/modules/adapters/definitions.ts +20 -0
  75. package/src/modules/adapters/types.ts +1 -1
  76. package/src/modules/agents/definitions.ts +1 -1
  77. package/src/shared/constants.ts +5 -0
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@agent-enderun/mcp",
3
- "version": "1.11.4",
3
+ "version": "1.11.5",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@agent-enderun/mcp",
9
- "version": "1.11.4",
9
+ "version": "1.11.5",
10
10
  "dependencies": {
11
11
  "@modelcontextprotocol/sdk": "^1.26.0",
12
12
  "zod": "^3.24.2"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-enderun/mcp",
3
- "version": "1.11.4",
3
+ "version": "1.11.5",
4
4
  "description": "Agent Enderun Model Context Protocol (MCP) Server",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -59,7 +59,7 @@ function validateArgs(toolName: string, args: Record<string, unknown>): string |
59
59
 
60
60
  server.setRequestHandler(ListToolsRequestSchema, async (request) => {
61
61
  // 2026 Stateless Spec: Log client info from metadata if available
62
- const meta = (request as any)._meta;
62
+ const meta = (request as { _meta?: { client?: { name?: string; version?: string } } })._meta;
63
63
  if (meta) {
64
64
  process.stderr.write(`[MCP] Stateless ListTools from ${meta.client?.name || "unknown"} v${meta.client?.version || "?.?"}\n`);
65
65
  }
@@ -69,7 +69,7 @@ server.setRequestHandler(ListToolsRequestSchema, async (request) => {
69
69
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
70
70
  const req = request as { params: { name: string, arguments?: Record<string, unknown> } };
71
71
  const { name, arguments: args } = req.params;
72
- const meta = (request as any)._meta;
72
+ const meta = (request as { _meta?: { client?: { name?: string; version?: string } } })._meta;
73
73
 
74
74
  // 2026 Stateless Spec: Prioritize metadata-driven context
75
75
  if (meta) {
@@ -15,6 +15,7 @@ export async function handleAcquireLock(projectRoot: string, args: AcquireLockAr
15
15
  try {
16
16
  if (!fs.existsSync(lockDir)) fs.mkdirSync(lockDir, { recursive: true });
17
17
 
18
+ // Check for stale lock first
18
19
  if (fs.existsSync(lockPath)) {
19
20
  const stat = fs.statSync(lockPath);
20
21
  const now = new Date().getTime();
@@ -26,15 +27,28 @@ export async function handleAcquireLock(projectRoot: string, args: AcquireLockAr
26
27
  content: [{ type: "text", text: `Resource '${resource}' is currently locked by another agent.` }]
27
28
  };
28
29
  }
29
- // Lock expired, safe to override
30
- fs.unlinkSync(lockPath);
30
+ // Lock expired, safe to override by deleting first
31
+ try {
32
+ fs.unlinkSync(lockPath);
33
+ } catch (unlinkErr) {
34
+ // If another agent unlinked it first, ignore
35
+ }
31
36
  }
32
37
 
33
- fs.writeFileSync(lockPath, JSON.stringify({ agent, timestamp: new Date().toISOString() }));
38
+ // Use 'wx' flag for atomic file creation
39
+ const lockData = JSON.stringify({ agent, timestamp: new Date().toISOString() });
40
+ fs.writeFileSync(lockPath, lockData, { flag: "wx" });
41
+
34
42
  return {
35
43
  content: [{ type: "text", text: `βœ… Lock acquired for resource '${resource}' by ${agent}.` }]
36
44
  };
37
- } catch (e) {
45
+ } catch (e: any) {
46
+ if (e.code === "EEXIST") {
47
+ return {
48
+ isError: true,
49
+ content: [{ type: "text", text: `Resource '${resource}' was just acquired by another agent.` }]
50
+ };
51
+ }
38
52
  return {
39
53
  isError: true,
40
54
  content: [{ type: "text", text: `Failed to acquire lock: ${String(e)}` }]
@@ -19,14 +19,14 @@ export function handleGetMemoryInsights(projectRoot: string, _args: ToolArgs): T
19
19
  const content = fs.readFileSync(memoryPath, "utf8");
20
20
  const lines = content.split("\n");
21
21
 
22
- const activePhase = lines.find(l => l.includes("Active Phase:"))?.split(":")[1]?.trim() || "Unknown";
23
- const activeTrace = lines.find(l => l.includes("Active Trace:"))?.split(":")[1]?.trim() || "None";
22
+ const activePhase = lines.find(l => l.includes("**Phase:**"))?.split("**Phase:**")[1]?.trim() || "Unknown";
23
+ const activeTrace = lines.find(l => l.includes("**Trace ID:**"))?.split("**Trace ID:**")[1]?.trim() || "None";
24
24
 
25
25
  // Find the last 5 history items (heuristic)
26
- const historyStartIndex = lines.findIndex(l => l.includes("## History"));
26
+ const historyStartIndex = lines.findIndex(l => l.toUpperCase().includes("HISTORY"));
27
27
  let recentHistory = "No history found.";
28
28
  if (historyStartIndex !== -1) {
29
- recentHistory = lines.slice(historyStartIndex + 1, historyStartIndex + 15)
29
+ recentHistory = lines.slice(historyStartIndex + 1)
30
30
  .filter(l => l.trim().startsWith("-"))
31
31
  .slice(-5)
32
32
  .join("\n");
@@ -5,7 +5,7 @@ import { SendAgentMessageArgs, ToolResult } from "../types.js";
5
5
  import { Metrics } from "../../utils/metrics.js";
6
6
 
7
7
  export async function handleSendAgentMessage(projectRoot: string, args: SendAgentMessageArgs): Promise<ToolResult> {
8
- const { to, category, content, traceId } = args;
8
+ const { to, category, content, traceId, parentId, requiresApproval } = args;
9
9
  const from = args.from || "@mcp";
10
10
 
11
11
  if (!to || !category || !content || !traceId) {
@@ -20,15 +20,15 @@ export async function handleSendAgentMessage(projectRoot: string, args: SendAgen
20
20
  const messagePath = path.join(messagesDir, `${agentName}.json`);
21
21
  const lockPath = path.join(messagesDir, `${agentName}.lock`);
22
22
 
23
- // Hermes Lock Protocol: Retry 3 times with 500ms delay
24
- let retries = 3;
23
+ // Hermes Lock Protocol: Retry 20 times with 500ms delay
24
+ let retries = 20;
25
25
  let acquired = false;
26
26
  while (retries > 0) {
27
27
  try {
28
28
  if (fs.existsSync(lockPath)) {
29
29
  try {
30
30
  const stats = fs.statSync(lockPath);
31
- if (Date.now() - stats.mtimeMs > 5000) {
31
+ if (Date.now() - stats.mtimeMs > 10000) {
32
32
  fs.unlinkSync(lockPath);
33
33
  }
34
34
  } catch {
@@ -60,15 +60,21 @@ export async function handleSendAgentMessage(projectRoot: string, args: SendAgen
60
60
 
61
61
  try {
62
62
  const defaultPriority = (category === "ALERT" || category === "ACTION") ? "HIGH" : "NORMAL";
63
+ const finalRequiresApproval = requiresApproval !== undefined
64
+ ? requiresApproval
65
+ : category === "ALERT";
66
+
63
67
  const message = {
64
68
  timestamp: new Date().toISOString(),
65
69
  from,
66
70
  to,
67
71
  category,
68
72
  traceId,
73
+ parentId,
69
74
  content,
70
75
  priority: args.priority || defaultPriority,
71
- status: "PENDING"
76
+ status: "PENDING",
77
+ requiresApproval: finalRequiresApproval
72
78
  };
73
79
 
74
80
  fs.appendFileSync(messagePath, JSON.stringify(message) + "\n");
@@ -18,9 +18,8 @@ export function handleGrepSearch(projectRoot: string, args: GrepSearchArgs): Too
18
18
  }
19
19
 
20
20
  const results: string[] = [];
21
- let regex: RegExp;
22
21
  try {
23
- regex = new RegExp(pattern, "g");
22
+ new RegExp(pattern);
24
23
  } catch (e) {
25
24
  const err = `Invalid regex pattern: ${String(e)}`;
26
25
  Metrics.logError(projectRoot, "@mcp", "grep_search", err);
@@ -53,7 +52,7 @@ export function handleGrepSearch(projectRoot: string, args: GrepSearchArgs): Too
53
52
  }
54
53
  }
55
54
  }
56
- } catch (e) {
55
+ } catch {
57
56
  // Ignore directories that cannot be read
58
57
  }
59
58
  };
@@ -37,26 +37,26 @@ export function verifyCorporateCompliance(content: string, filePath: string): vo
37
37
  }
38
38
  }
39
39
 
40
- // 2. No Explicit Any Policy
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}.`);
40
+ // 2. No Explicit Any Policy
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
+ }
45
46
  }
46
47
  }
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.`);
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
+ }
57
58
  }
58
59
  }
59
- }
60
60
 
61
61
  // Handle 'any' as a keyword type (e.g., parameter: any)
62
62
  if (node.kind === ts.SyntaxKind.AnyKeyword) {
@@ -72,16 +72,16 @@ describe("Hermes Lock Protocol & Message sending", () => {
72
72
 
73
73
  expect(result.isError).toBe(true);
74
74
  expect(result.content[0].text).toContain("❌ Could not send message to @backend: Hermes lock is busy.");
75
- // Retries takes 3 attempts with 500ms delay -> duration should be at least ~1000ms
76
- expect(duration).toBeGreaterThanOrEqual(1000);
77
- });
75
+ // Retries takes 20 attempts with 500ms delay -> duration should be at least ~9000ms
76
+ expect(duration).toBeGreaterThanOrEqual(9000);
77
+ }, 15000);
78
78
 
79
- it("should bypass and acquire lock if existing lock is stale (older than 5s)", async () => {
79
+ it("should bypass and acquire lock if existing lock is stale (older than 10s)", async () => {
80
80
  fs.mkdirSync(MESSAGES_DIR, { recursive: true });
81
- // Set lock file mtime to 10 seconds ago
81
+ // Set lock file mtime to 15 seconds ago
82
82
  fs.writeFileSync(BACKEND_LOCK_FILE, "Locked by @test at " + new Date().toISOString(), "utf8");
83
- const tenSecondsAgo = new Date(Date.now() - 10000);
84
- fs.utimesSync(BACKEND_LOCK_FILE, tenSecondsAgo, tenSecondsAgo);
83
+ const fifteenSecondsAgo = new Date(Date.now() - 15000);
84
+ fs.utimesSync(BACKEND_LOCK_FILE, fifteenSecondsAgo, fifteenSecondsAgo);
85
85
 
86
86
  const args: ToolArgs = {
87
87
  to: "@backend",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-enderun",
3
- "version": "1.11.4",
3
+ "version": "1.11.5",
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",
@@ -24,7 +24,8 @@
24
24
  "framework"
25
25
  ],
26
26
  "workspaces": [
27
- "framework-mcp"
27
+ "framework-mcp",
28
+ "apps/*"
28
29
  ],
29
30
  "bin": {
30
31
  "agent-enderun": "bin/cli.js"
@@ -70,7 +71,7 @@
70
71
  "vitest": "^3.0.0"
71
72
  },
72
73
  "enderun": {
73
- "version": "1.11.4",
74
+ "version": "1.11.5",
74
75
  "initializedAt": "2026-06-04T18:51:28.970Z",
75
76
  "frameworkDir": ".enderun"
76
77
  },
@@ -39,7 +39,7 @@ export async function checkCommand() {
39
39
  } else {
40
40
  UI.success("API Contracts verified and sealed.");
41
41
  }
42
- } catch (err) {
42
+ } catch {
43
43
  UI.error("Error during contract verification.");
44
44
  issues++;
45
45
  }
@@ -45,7 +45,7 @@ export function scaffoldFrameworkConfigs(
45
45
  adapter: AdapterConfig,
46
46
  dryRun: boolean,
47
47
  selectedPalette: string,
48
- options?: { unified?: boolean; adapters?: string[] }
48
+ options?: { unified?: boolean; adapters?: string[]; profile?: string }
49
49
  ) {
50
50
  if (dryRun) return;
51
51
  const frameworkDir = path.join(targetDir, fDir);
@@ -55,6 +55,7 @@ export function scaffoldFrameworkConfigs(
55
55
  name: FRAMEWORK_NAME,
56
56
  version: getPackageVersion(),
57
57
  unified: options?.unified || false,
58
+ profile: options?.profile || "full",
58
59
  adapters: options?.adapters || [adapter.id],
59
60
  theme: {
60
61
  palette: selectedPalette,
@@ -77,10 +78,25 @@ export function scaffoldFrameworkConfigs(
77
78
  }
78
79
 
79
80
  let statusContent = "# πŸŽ–οΈ Status\n\n| Agent | State | Active Task | Last Updated | Notes | Extra | Backup |\n|---|---|---|---|---|---|---|\n";
80
- for (const ag of ALL_AGENTS) {
81
+
82
+ const liteAgents = ["manager", "architect", "frontend"];
83
+ const activeAgents = (options?.profile === "lite")
84
+ ? ALL_AGENTS.filter(ag => liteAgents.includes(ag.name))
85
+ : ALL_AGENTS;
86
+
87
+ const initialStatusJson: Record<string, { state: string; task: string; lastUpdated: string }> = {};
88
+ const now = new Date().toISOString();
89
+
90
+ for (const ag of activeAgents) {
81
91
  statusContent += "| @" + ag.name + " | READY | Idle | - | - | - | - |\n";
92
+ initialStatusJson[ag.name] = { state: "READY", task: "Idle", lastUpdated: now };
82
93
  }
83
94
  writeTextFile(path.join(frameworkDir, "STATUS.md"), statusContent);
95
+
96
+ // Populate initial status.json
97
+ const statusJsonPath = path.join(frameworkDir, "memory", "status.json");
98
+ if (!fs.existsSync(path.join(frameworkDir, "memory"))) fs.mkdirSync(path.join(frameworkDir, "memory"), { recursive: true });
99
+ writeJsonFile(statusJsonPath, initialStatusJson, dryRun);
84
100
  }
85
101
 
86
102
  export function scaffoldShims(
@@ -67,7 +67,7 @@ async function runInteractiveInit(): Promise<{ selectedDirs: string[], selectedA
67
67
  } finally { rl.close(); }
68
68
  }
69
69
 
70
- export async function initCommand(adapterName: string, options: { unified?: boolean, dryRun?: boolean, yes?: boolean }) {
70
+ export async function initCommand(adapterName: string, options: { unified?: boolean, dryRun?: boolean, yes?: boolean, profile?: string }) {
71
71
  const adapterId = (adapterName || "gemini") as AdapterId;
72
72
  const adapter = resolveAdapter(adapterId);
73
73
 
@@ -78,6 +78,7 @@ export async function initCommand(adapterName: string, options: { unified?: bool
78
78
 
79
79
  const projectRoot = process.cwd();
80
80
  const isUnified = options.unified || false;
81
+ const executionProfile = (options.profile || "full").toLowerCase();
81
82
 
82
83
  // STRICT ISOLATION STRATEGY
83
84
  const coreDir = ".enderun"; // Pure Memory & Knowledge
@@ -86,7 +87,7 @@ export async function initCommand(adapterName: string, options: { unified?: bool
86
87
  const dryRun = options.dryRun || false;
87
88
  const forceYes = options.yes || false;
88
89
 
89
- UI.intent("Enderun Initialization", `Bootstrapping ${FRAMEWORK_NAME} (v${getPackageVersion()}) with ${adapterId} adapter...`);
90
+ UI.intent("Enderun Initialization", `Bootstrapping ${FRAMEWORK_NAME} (v${getPackageVersion()}) with ${adapterId} adapter [Profile: ${executionProfile}]...`);
90
91
 
91
92
  let selectedDirs: string[];
92
93
  let selectedAgents: string[];
@@ -94,9 +95,16 @@ export async function initCommand(adapterName: string, options: { unified?: bool
94
95
 
95
96
  if (forceYes) {
96
97
  selectedDirs = ["knowledge", "prompts", "memory", "router", "registry", "observability", "rules"];
97
- selectedAgents = ALL_AGENTS.map(a => a.name);
98
+
99
+ if (executionProfile === "lite") {
100
+ const liteAgents = ["manager", "architect", "frontend"];
101
+ selectedAgents = ALL_AGENTS.filter(a => liteAgents.includes(a.name)).map(a => a.name);
102
+ } else {
103
+ selectedAgents = ALL_AGENTS.map(a => a.name);
104
+ }
105
+
98
106
  selectedPalette = "Modern Blue";
99
- UI.success("Non-interactive mode: Using default configurations.");
107
+ UI.success(`Non-interactive mode: Using default configurations for ${executionProfile} profile.`);
100
108
  } else {
101
109
  const result = await runInteractiveInit();
102
110
  selectedDirs = result.selectedDirs.filter(d => d !== "agents" && d !== "skills");
@@ -117,7 +125,8 @@ export async function initCommand(adapterName: string, options: { unified?: bool
117
125
  scaffoldConstitution(projectRoot, coreDir, adapterId, dryRun);
118
126
  scaffoldFrameworkConfigs(projectRoot, coreDir, adapter, dryRun, selectedPalette, {
119
127
  unified: isUnified,
120
- adapters: isUnified ? [...ADAPTER_IDS] : [adapterId]
128
+ adapters: isUnified ? [...ADAPTER_IDS] : [adapterId],
129
+ profile: executionProfile
121
130
  });
122
131
 
123
132
  scaffoldStandards(path.join(projectRoot, coreDir), dryRun);
@@ -56,6 +56,13 @@ export async function orchestrateCommand(options?: { maxIterations?: number }) {
56
56
  while (isLooping) {
57
57
  try {
58
58
  // Initial load from disk to sync the cache
59
+ const frameworkConfigPath = path.join(frameworkDir, "config.json");
60
+ const frameworkConfig = fs.existsSync(frameworkConfigPath)
61
+ ? JSON.parse(fs.readFileSync(frameworkConfigPath, "utf8"))
62
+ : {};
63
+ const executionProfile = frameworkConfig.profile || "full";
64
+ const liteAgents = ["manager", "architect", "frontend"];
65
+
59
66
  if (Object.keys(agentStatusCache).length === 0) {
60
67
  const diskStatus = readStatus();
61
68
  agentStatusCache = diskStatus || {};
@@ -65,6 +72,9 @@ export async function orchestrateCommand(options?: { maxIterations?: number }) {
65
72
  const agentStatuses = readStatus();
66
73
  let updatedStatus = false;
67
74
  for (const [agentName, info] of Object.entries(agentStatuses)) {
75
+ // Skip agents not in lite profile
76
+ if (executionProfile === "lite" && !liteAgents.includes(agentName)) continue;
77
+
68
78
  const { state, lastUpdated, task } = info as { state: string; task: string; lastUpdated?: string };
69
79
  if (state === "EXECUTING" && lastUpdated) {
70
80
  const lastUpdatedTime = Date.parse(lastUpdated);
@@ -95,6 +105,13 @@ export async function orchestrateCommand(options?: { maxIterations?: number }) {
95
105
  const fileMessagesMap: Record<string, HermesMessage[]> = {};
96
106
 
97
107
  for (const file of messageFiles) {
108
+ const agentName = file.replace(".json", "");
109
+ // Skip agents not in lite profile
110
+ if (executionProfile === "lite" && !liteAgents.includes(agentName)) {
111
+ // Optional: Could move these messages to a "held" or "rejected" state
112
+ continue;
113
+ }
114
+
98
115
  const filePath = path.join(messagesDir, file);
99
116
  try {
100
117
  const content = fs.readFileSync(filePath, "utf8").trim();
@@ -107,6 +124,12 @@ export async function orchestrateCommand(options?: { maxIterations?: number }) {
107
124
  const parsed = JSON.parse(line);
108
125
  const msg = HermesMessageSchema.parse(parsed);
109
126
 
127
+ // Profile Validation: Ensure recipient is active
128
+ if (executionProfile === "lite" && !liteAgents.includes(msg.to.replace("@", ""))) {
129
+ UI.error(`⚠️ Message addressed to inactive agent @${msg.to} in LITE profile. Skipping.`);
130
+ return;
131
+ }
132
+
110
133
  // Contract Validation for structured task messages
111
134
  // ACTION & DELEGATION must be valid TaskRequest payloads
112
135
  // SUBTASK must have a parentId and valid task payload
@@ -301,6 +324,7 @@ export async function sendMessage(args: {
301
324
  const stats = fs.statSync(lockPath);
302
325
  // Increased stale lock timeout from 5s to 10s for heavy ops
303
326
  if (Date.now() - stats.mtimeMs > 10000) {
327
+ UI.warning(`♻️ Stale Hermes lock detected for @${agentName}. Clearing lock for @${args.from}.`);
304
328
  fs.unlinkSync(lockPath);
305
329
  }
306
330
  } catch (err) {
@@ -55,9 +55,8 @@ export async function planCommand() {
55
55
 
56
56
  // Extract headings as task descriptions
57
57
  const headingRegex = /^#{2,4}\s+(.+)$/gm;
58
- let headingMatch;
59
58
  let headingCount = 0;
60
- while ((headingMatch = headingRegex.exec(content)) !== null) {
59
+ while (headingRegex.exec(content) !== null) {
61
60
  headingCount++;
62
61
  totalTasks++;
63
62
  }
@@ -1,7 +1,5 @@
1
1
  import chalk from "chalk";
2
- import fs from "fs";
3
- import path from "path";
4
- import { listTasks, readState, readStatus, getFrameworkDir } from "../utils/memory.js";
2
+ import { listTasks, readState, readStatus } from "../utils/memory.js";
5
3
 
6
4
  /**
7
5
  * Print the current framework status.
package/src/cli/index.ts CHANGED
@@ -64,8 +64,10 @@ async function main() {
64
64
  const dryRun = args.includes("--dry-run");
65
65
  const isUnified = args.includes("--unified");
66
66
  const forceYes = args.includes("--yes") || args.includes("-y");
67
+ const profileArg = args.find(a => a.startsWith("--profile="));
68
+ const profile = profileArg ? profileArg.split("=")[1] : "full";
67
69
  const adapter = args.find(a => !a.startsWith("-") && a !== "init") || "gemini";
68
- await initCommand(adapter, { dryRun, unified: isUnified, yes: forceYes });
70
+ await initCommand(adapter, { dryRun, unified: isUnified, yes: forceYes, profile });
69
71
  break;
70
72
  }
71
73
 
@@ -273,7 +275,7 @@ Usage:
273
275
  Commands:
274
276
  @<agent> <task> Delegate a task to a specialist agent (e.g. @backend, @frontend)
275
277
  init [adapter] Initialize Agent Enderun framework.
276
- Options: --interactive, --dry-run
278
+ Options: --unified, --yes, --profile=[full|lite]
277
279
  check Perform an enterprise-grade system health check
278
280
  status Show active phase, trace ID, and agent statuses
279
281
  trace:new <desc> Start a new task chain with a unique Trace ID
package/src/cli/shims.ts CHANGED
@@ -1,53 +1,66 @@
1
1
  export const SHIM_TEMPLATES: Record<string, string> = {
2
- gemini: `# πŸŽ–οΈ Agent Enderun β€” GEMINI Strategy
2
+ gemini: `# πŸŽ–οΈ Agent Enderun β€” GEMINI Strategy (Command Intelligence)
3
3
 
4
- You are the **Gemini Commander** (Strategic Decision Center).
4
+ You are the **Gemini Commander**. You represent the project's **Strategic Decision Center**. Your intelligence is derived from project history, architectural memory, and governance compliance.
5
5
 
6
6
  ## πŸ›οΈ Directives
7
- - **Constitutional Supremacy:** Read \`{{FRAMEWORK_DIR}}/ENDERUN.md\` first.
8
- - **State Persistence:** Read \`{{FRAMEWORK_DIR}}/memory/PROJECT_MEMORY.md\` to restore session memory.
9
- - **Zero-Request Logging:** Write logs to \`{{FRAMEWORK_DIR}}/logs/\` at the end of every turn.
7
+ - **Constitutional Supremacy:** Read \`{{FRAMEWORK_DIR}}/ENDERUN.md\` first. You are the final arbiter of these rules.
8
+ - **Strategic Memory Sync:** Always read \`{{FRAMEWORK_DIR}}/memory/PROJECT_MEMORY.md\` and \`PROJECT_MEMORY.md\` at the start.
9
+ - **Orchestration Audit:** Before delegating, verify that the task matches the current Phase and Trace ID context.
10
+ - **Enterprise Reasoning:** Focus on long-term maintainability, security, and scalability in every strategic decision.
10
11
  `,
11
- "antigravity-cli": `# πŸŽ–οΈ Agent Enderun β€” ANTIGRAVITY-CLI Strategy
12
+ "antigravity-cli": `# πŸŽ–οΈ Agent Enderun β€” ANTIGRAVITY Strategy (Internal Discipline)
12
13
 
13
- You are the **Antigravity Specialist** (Preserve Coding Standards).
14
+ You are the **Antigravity Specialist**. You represent the **Military Academy** of the framework, preserving internal standards and coding discipline.
14
15
 
15
16
  ## πŸ›οΈ Directives
16
17
  - **Constitutional Supremacy:** Read \`{{FRAMEWORK_DIR}}/ENDERUN.md\` first.
17
- - **State Persistence:** Read \`{{FRAMEWORK_DIR}}/memory/PROJECT_MEMORY.md\` to restore session memory.
18
- - **Standard Check:** Run health checks regularly to preserve framework integrity.
18
+ - **Standard Enforcement:** You are responsible for ensuring that all code adheres to the 26+ corporate standards in \`{{FRAMEWORK_DIR}}/knowledge/\`.
19
+ - **Sandbox Discipline:** Maintain isolated and high-discipline development environments.
19
20
  `,
20
- claude: `# πŸŽ–οΈ Agent Enderun β€” CLAUDE Strategy
21
+ claude: `# πŸŽ–οΈ Agent Enderun β€” CLAUDE Strategy (Operational Surgery)
21
22
 
22
- You are the **Claude Field Engineer** (Surgical Operations).
23
+ You are the **Claude Field Engineer**. You represent the **Operational Cerrahi (Surgical)** wing of the army. Your mission is precision execution with minimal footprint.
23
24
 
24
25
  ## πŸ›οΈ Directives
25
- - **Constitutional Supremacy:** Read \`{{FRAMEWORK_DIR}}/ENDERUN.md\` first.
26
- - **State Persistence:** Read \`{{FRAMEWORK_DIR}}/memory/PROJECT_MEMORY.md\` to restore session memory.
27
- - **Surgical Precision:** Do not rewrite files. Execute surgical edits.
26
+ - **Surgical Precision (MANDATORY):** NEVER rewrite an entire file. Use \`replace_text\` or \`patch_file\` tools exclusively.
27
+ - **Token Economy:** Minimize API usage by targetting only the exact lines of code needed.
28
+ - **Traceability:** Ensure every change is linked to an active Trace ID and logged traceable under \`{{FRAMEWORK_DIR}}/logs/\`.
29
+ - **Phase Discipline:** Do not attempt Phase 2 tasks if Phase 1 contracts are not sealed.
28
30
  `,
29
- grok: `# πŸŽ–οΈ Agent Enderun β€” GROK Strategy
31
+ grok: `# πŸŽ–οΈ Agent Enderun β€” GROK Strategy (Scouting Wing)
30
32
 
31
- You are the **Grok Explorer** (Experimental Protocols).
33
+ You are the **Grok Explorer**. You represent the **Otonom Keşif Kanadı (Autonomous Scouting Wing)**. Your mission is experimental discovery and boundary testing.
32
34
 
33
35
  ## πŸ›οΈ Directives
34
- - **Constitutional Supremacy:** Read \`{{FRAMEWORK_DIR}}/ENDERUN.md\` first.
35
- - **State Persistence:** Read \`{{FRAMEWORK_DIR}}/memory/PROJECT_MEMORY.md\` to restore session memory.
36
+ - **Architecture Discovery:** Use \`get_project_map\` and \`get_project_gaps\` to map unexplored territory before any specialist acts.
37
+ - **Boundary Testing:** Identify architectural weaknesses or security gaps before they become critical.
38
+ - **Experimental Protocol:** Test futuristic agent behaviors and report findings to the **Commander**.
36
39
  `,
37
- cursor: `# πŸŽ–οΈ Agent Enderun β€” CURSOR Strategy
40
+ cursor: `# πŸŽ–οΈ Agent Enderun β€” CURSOR Strategy (Implementer)
38
41
 
39
- You are the **Cursor Implementer** (IDE Integration).
42
+ You are the **Cursor Implementer**. You are the **Kod İşçisi (Code Worker)** integrated directly into the IDE.
40
43
 
41
44
  ## πŸ›οΈ Directives
42
- - **Constitutional Supremacy:** Read \`{{FRAMEWORK_DIR}}/ENDERUN.md\` first.
43
- - **State Persistence:** Read \`{{FRAMEWORK_DIR}}/memory/PROJECT_MEMORY.md\` to restore session memory.
45
+ - **IDE Synergy:** Leverage Cursor's native context and Enderun's governance to write high-quality, compliant code.
46
+ - **Atomic Implementation:** Focus on implementing the specific task delegated by the @manager.
44
47
  `,
45
- codex: `# πŸŽ–οΈ Agent Enderun β€” COPILOT Strategy
48
+ codex: `# πŸŽ–οΈ Agent Enderun β€” COPILOT Strategy (Assistant)
46
49
 
47
- You are the **Copilot Implementer** (GitHub Copilot Integration).
50
+ You are the **Copilot Assistant**. You represent the **Yardımcı Geliştirici (Assistant Developer)**.
48
51
 
49
52
  ## πŸ›οΈ Directives
50
- - **Constitutional Supremacy:** Read \`{{FRAMEWORK_DIR}}/ENDERUN.md\` first.
51
- - **State Persistence:** Read \`{{FRAMEWORK_DIR}}/memory/PROJECT_MEMORY.md\` to restore session memory.
53
+ - **Predictive Support:** Provide code completions and suggestions that strictly adhere to the project's \`{{FRAMEWORK_DIR}}/ENDERUN.md\` rules.
54
+ - **Rapid Prototyping:** Support the army by generating boilerplate that follows established enterprise patterns.
55
+ `,
56
+ local: `# πŸŽ–οΈ Agent Enderun β€” LOCAL LLM Strategy (Private Intelligence)
57
+
58
+ You are the **Local Private Intelligence**. You represent the project's **Private & Secure Command Wing**. Your intelligence is derived entirely from local models (Ollama, vLLM, etc.) and project-specific knowledge.
59
+
60
+ ## πŸ›οΈ Directives
61
+ - **Constitutional Supremacy:** Read \`{{FRAMEWORK_DIR}}/ENDERUN.md\` first. You are the final arbiter of these rules.
62
+ - **Zero Cloud Policy:** Ensure all operations remain local and secure.
63
+ - **Trace ID Discipline:** Every local inference and code generation MUST follow the active Trace ID.
64
+ - **Technical Integrity:** Adhere strictly to the 100% type-safety and surgical edit rules of the Enderun Order.
52
65
  `
53
66
  };
@@ -119,6 +119,26 @@ registry.register(
119
119
  }
120
120
  );
121
121
 
122
+ // 🏠 Local LLM (Ollama / Private AI)
123
+ registry.register(
124
+ {
125
+ id: "local",
126
+ frameworkDir: ".enderun",
127
+ shimFile: "LOCAL_AI.md",
128
+ shimTemplate: "src/cli/shims/local.md",
129
+ role: "commander",
130
+ templateDir: ".enderun",
131
+ nestedDirs: ["agents", "rules"],
132
+ agentsDir: ".enderun/agents",
133
+ agentsExt: ".md"
134
+ },
135
+ (projectRoot, mcpBlock) => {
136
+ const frameworkDir = ".enderun";
137
+ writeJsonFile(path.join(projectRoot, frameworkDir, "mcp_config.json"), mcpBlock);
138
+ UI.success(`Local LLM MCP (Ollama/Private AI) registered β†’ ${frameworkDir}/mcp_config.json`);
139
+ }
140
+ );
141
+
122
142
  // πŸ›Έ Antigravity
123
143
  registry.register(
124
144
  {
@@ -1,4 +1,4 @@
1
- export const ADAPTER_IDS = ["gemini", "claude", "grok", "cursor", "codex", "antigravity-cli"] as const;
1
+ export const ADAPTER_IDS = ["gemini", "claude", "grok", "cursor", "codex", "local", "antigravity-cli"] as const;
2
2
  export type AdapterId = (typeof ADAPTER_IDS)[number];
3
3
 
4
4
  export type AdapterRole = "commander" | "architect" | "researcher" | "implementer" | "general";
@@ -195,7 +195,7 @@ function buildSystemPrompt(
195
195
  `**Authority Tier:** ${ag.tier} (Capability: ${ag.capability}/10)`,
196
196
  "",
197
197
  "## Project Structure",
198
- `This project uses the following directory structure:`,
198
+ "This project uses the following directory structure:",
199
199
  `- **Backend:** \`${paths.backend}\``,
200
200
  `- **Frontend:** \`${paths.frontend}\``,
201
201
  `- **Mobile:** \`${paths.mobile}\``,