agent-enderun 1.10.2 → 1.10.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 (162) hide show
  1. package/README.md +2 -1
  2. package/dist/framework-mcp/src/constants.d.ts +47 -0
  3. package/dist/framework-mcp/src/constants.js +65 -0
  4. package/dist/framework-mcp/src/constants.js.map +1 -0
  5. package/dist/framework-mcp/src/index.js +26 -5
  6. package/dist/framework-mcp/src/index.js.map +1 -1
  7. package/dist/framework-mcp/src/tools/control_plane/locking.d.ts +3 -9
  8. package/dist/framework-mcp/src/tools/control_plane/locking.js.map +1 -1
  9. package/dist/framework-mcp/src/tools/control_plane/registry.d.ts +2 -8
  10. package/dist/framework-mcp/src/tools/control_plane/registry.js.map +1 -1
  11. package/dist/framework-mcp/src/tools/dashboard/start_dashboard.d.ts +2 -2
  12. package/dist/framework-mcp/src/tools/dashboard/start_dashboard.js.map +1 -1
  13. package/dist/framework-mcp/src/tools/definitions.js +30 -0
  14. package/dist/framework-mcp/src/tools/definitions.js.map +1 -1
  15. package/dist/framework-mcp/src/tools/file_system/batch_surgical_edit.d.ts +2 -2
  16. package/dist/framework-mcp/src/tools/file_system/batch_surgical_edit.js +12 -4
  17. package/dist/framework-mcp/src/tools/file_system/batch_surgical_edit.js.map +1 -1
  18. package/dist/framework-mcp/src/tools/file_system/patch_file.d.ts +2 -2
  19. package/dist/framework-mcp/src/tools/file_system/patch_file.js +6 -2
  20. package/dist/framework-mcp/src/tools/file_system/patch_file.js.map +1 -1
  21. package/dist/framework-mcp/src/tools/file_system/read_file.d.ts +2 -2
  22. package/dist/framework-mcp/src/tools/file_system/read_file.js +43 -26
  23. package/dist/framework-mcp/src/tools/file_system/read_file.js.map +1 -1
  24. package/dist/framework-mcp/src/tools/file_system/replace_text.d.ts +2 -2
  25. package/dist/framework-mcp/src/tools/file_system/replace_text.js +7 -3
  26. package/dist/framework-mcp/src/tools/file_system/replace_text.js.map +1 -1
  27. package/dist/framework-mcp/src/tools/file_system/write_file.d.ts +2 -2
  28. package/dist/framework-mcp/src/tools/file_system/write_file.js +27 -15
  29. package/dist/framework-mcp/src/tools/file_system/write_file.js.map +1 -1
  30. package/dist/framework-mcp/src/tools/framework/get_status.d.ts +2 -2
  31. package/dist/framework-mcp/src/tools/framework/get_status.js.map +1 -1
  32. package/dist/framework-mcp/src/tools/framework/orchestrate.d.ts +2 -2
  33. package/dist/framework-mcp/src/tools/framework/orchestrate.js.map +1 -1
  34. package/dist/framework-mcp/src/tools/framework/run_tests.d.ts +2 -2
  35. package/dist/framework-mcp/src/tools/framework/run_tests.js.map +1 -1
  36. package/dist/framework-mcp/src/tools/framework/update_contract_hash.d.ts +2 -2
  37. package/dist/framework-mcp/src/tools/framework/update_contract_hash.js.map +1 -1
  38. package/dist/framework-mcp/src/tools/framework/update_memory.d.ts +2 -2
  39. package/dist/framework-mcp/src/tools/framework/update_memory.js +1 -1
  40. package/dist/framework-mcp/src/tools/framework/update_memory.js.map +1 -1
  41. package/dist/framework-mcp/src/tools/index.d.ts +1 -2
  42. package/dist/framework-mcp/src/tools/index.js +33 -25
  43. package/dist/framework-mcp/src/tools/index.js.map +1 -1
  44. package/dist/framework-mcp/src/tools/messaging/log_action.d.ts +2 -2
  45. package/dist/framework-mcp/src/tools/messaging/log_action.js.map +1 -1
  46. package/dist/framework-mcp/src/tools/messaging/send_message.d.ts +2 -2
  47. package/dist/framework-mcp/src/tools/messaging/send_message.js +14 -1
  48. package/dist/framework-mcp/src/tools/messaging/send_message.js.map +1 -1
  49. package/dist/framework-mcp/src/tools/observability/check_ports.d.ts +2 -2
  50. package/dist/framework-mcp/src/tools/observability/check_ports.js.map +1 -1
  51. package/dist/framework-mcp/src/tools/quality/check_lint.d.ts +5 -0
  52. package/dist/framework-mcp/src/tools/quality/check_lint.js +29 -0
  53. package/dist/framework-mcp/src/tools/quality/check_lint.js.map +1 -0
  54. package/dist/framework-mcp/src/tools/search/get_gaps.d.ts +2 -2
  55. package/dist/framework-mcp/src/tools/search/get_gaps.js.map +1 -1
  56. package/dist/framework-mcp/src/tools/search/get_map.d.ts +2 -2
  57. package/dist/framework-mcp/src/tools/search/get_map.js +1 -1
  58. package/dist/framework-mcp/src/tools/search/get_map.js.map +1 -1
  59. package/dist/framework-mcp/src/tools/search/grep_search.d.ts +2 -2
  60. package/dist/framework-mcp/src/tools/search/grep_search.js +45 -28
  61. package/dist/framework-mcp/src/tools/search/grep_search.js.map +1 -1
  62. package/dist/framework-mcp/src/tools/search/list_dir.d.ts +2 -2
  63. package/dist/framework-mcp/src/tools/search/list_dir.js.map +1 -1
  64. package/dist/framework-mcp/src/tools/shell/run_command.d.ts +2 -0
  65. package/dist/framework-mcp/src/tools/shell/run_command.js +47 -0
  66. package/dist/framework-mcp/src/tools/shell/run_command.js.map +1 -0
  67. package/dist/framework-mcp/src/tools/types.d.ts +95 -28
  68. package/dist/framework-mcp/src/utils/metrics.d.ts +20 -0
  69. package/dist/framework-mcp/src/utils/metrics.js +24 -6
  70. package/dist/framework-mcp/src/utils/metrics.js.map +1 -1
  71. package/dist/framework-mcp/src/utils/security.d.ts +1 -3
  72. package/dist/framework-mcp/src/utils/security.js +21 -18
  73. package/dist/framework-mcp/src/utils/security.js.map +1 -1
  74. package/dist/framework-mcp/tests/tools/file_system/file_system_tools.test.js +19 -14
  75. package/dist/framework-mcp/tests/tools/file_system/file_system_tools.test.js.map +1 -1
  76. package/dist/framework-mcp/tests/tools/messaging/send_message.test.js +1 -1
  77. package/dist/framework-mcp/tests/tools/messaging/send_message.test.js.map +1 -1
  78. package/dist/framework-mcp/tests/tools/quality/check_lint.test.d.ts +1 -0
  79. package/dist/framework-mcp/tests/tools/quality/check_lint.test.js +36 -0
  80. package/dist/framework-mcp/tests/tools/quality/check_lint.test.js.map +1 -0
  81. package/dist/framework-mcp/tests/tools/shell/run_command.test.d.ts +1 -0
  82. package/dist/framework-mcp/tests/tools/shell/run_command.test.js +43 -0
  83. package/dist/framework-mcp/tests/tools/shell/run_command.test.js.map +1 -0
  84. package/dist/src/cli/adapters/core.d.ts +1 -1
  85. package/dist/src/cli/adapters/core.js +5 -0
  86. package/dist/src/cli/adapters/core.js.map +1 -1
  87. package/dist/src/cli/adapters/paths.d.ts +15 -0
  88. package/dist/src/cli/adapters/paths.js +101 -0
  89. package/dist/src/cli/adapters/paths.js.map +1 -0
  90. package/dist/src/cli/adapters/scaffold.js +2 -1
  91. package/dist/src/cli/adapters/scaffold.js.map +1 -1
  92. package/dist/src/cli/commands/check.js +15 -36
  93. package/dist/src/cli/commands/check.js.map +1 -1
  94. package/dist/src/cli/commands/dashboard/data-service.d.ts +8 -0
  95. package/dist/src/cli/commands/dashboard/data-service.js +37 -0
  96. package/dist/src/cli/commands/dashboard/data-service.js.map +1 -1
  97. package/dist/src/cli/commands/init/scaffold-standards.js +6 -1
  98. package/dist/src/cli/commands/init/scaffold-standards.js.map +1 -1
  99. package/dist/src/cli/commands/init.js +2 -5
  100. package/dist/src/cli/commands/init.js.map +1 -1
  101. package/dist/src/cli/commands/orchestrate.d.ts +5 -0
  102. package/dist/src/cli/commands/orchestrate.js +148 -128
  103. package/dist/src/cli/commands/orchestrate.js.map +1 -1
  104. package/dist/src/cli/utils/fs.js +1 -1
  105. package/dist/src/cli/utils/fs.js.map +1 -1
  106. package/dist/src/cli/utils/memory.d.ts +1 -1
  107. package/dist/src/cli/utils/memory.js +28 -8
  108. package/dist/src/cli/utils/memory.js.map +1 -1
  109. package/dist/src/modules/adapters/definitions.js +3 -3
  110. package/dist/src/modules/adapters/definitions.js.map +1 -1
  111. package/dist/src/modules/agents/definitions.js +2 -1
  112. package/dist/src/modules/agents/definitions.js.map +1 -1
  113. package/dist/src/modules/agents/registry/architect.js +1 -0
  114. package/dist/src/modules/agents/registry/architect.js.map +1 -1
  115. package/dist/src/modules/agents/registry/backend.js +3 -3
  116. package/dist/src/modules/agents/registry/backend.js.map +1 -1
  117. package/dist/src/modules/agents/registry/database.js +3 -3
  118. package/dist/src/modules/agents/registry/database.js.map +1 -1
  119. package/dist/src/modules/agents/registry/devops.js +1 -1
  120. package/dist/src/modules/agents/registry/devops.js.map +1 -1
  121. package/dist/src/modules/agents/registry/quality.js +1 -0
  122. package/dist/src/modules/agents/registry/quality.js.map +1 -1
  123. package/dist/src/shared/constants.d.ts +113 -0
  124. package/dist/src/shared/constants.js +178 -0
  125. package/dist/src/shared/constants.js.map +1 -0
  126. package/dist/tests/adapters/paths.test.d.ts +1 -0
  127. package/dist/tests/adapters/paths.test.js +63 -0
  128. package/dist/tests/adapters/paths.test.js.map +1 -0
  129. package/dist/tests/approve.test.js +2 -1
  130. package/dist/tests/approve.test.js.map +1 -1
  131. package/dist/tests/cli/init/unified_init.test.d.ts +1 -0
  132. package/dist/tests/cli/init/unified_init.test.js +47 -0
  133. package/dist/tests/cli/init/unified_init.test.js.map +1 -0
  134. package/dist/tests/fs-utils.test.js +1 -1
  135. package/dist/tests/fs-utils.test.js.map +1 -1
  136. package/dist/tests/memory-utils.test.js +1 -1
  137. package/dist/tests/memory-utils.test.js.map +1 -1
  138. package/dist/tests/shared/constants.test.d.ts +1 -0
  139. package/dist/tests/shared/constants.test.js +17 -0
  140. package/dist/tests/shared/constants.test.js.map +1 -0
  141. package/dist/tests/trace.test.js +2 -2
  142. package/dist/tests/trace.test.js.map +1 -1
  143. package/package.json +6 -6
  144. package/src/cli/adapters/core.ts +7 -0
  145. package/src/cli/adapters/paths.ts +134 -0
  146. package/src/cli/adapters/scaffold.ts +2 -1
  147. package/src/cli/commands/check.ts +16 -38
  148. package/src/cli/commands/dashboard/data-service.ts +48 -0
  149. package/src/cli/commands/init/scaffold-standards.ts +6 -1
  150. package/src/cli/commands/init.ts +1 -4
  151. package/src/cli/commands/orchestrate.ts +151 -127
  152. package/src/cli/utils/fs.ts +1 -1
  153. package/src/cli/utils/memory.ts +32 -7
  154. package/src/modules/adapters/definitions.ts +4 -3
  155. package/src/modules/agents/definitions.ts +2 -1
  156. package/src/modules/agents/registry/architect.ts +1 -0
  157. package/src/modules/agents/registry/backend.ts +3 -3
  158. package/src/modules/agents/registry/database.ts +3 -3
  159. package/src/modules/agents/registry/devops.ts +1 -1
  160. package/src/modules/agents/registry/quality.ts +1 -0
  161. package/src/shared/constants.ts +212 -0
  162. package/bin/validate-agent-army.js +0 -148
@@ -1,6 +1,5 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
- import chalk from "chalk";
4
3
  import { getFrameworkDir, updateDocumentStore, readStatus } from "../utils/memory.js";
5
4
  import { UI } from "../utils/ui.js";
6
5
  import { writeTextFile, appendFile } from "../utils/fs.js";
@@ -19,7 +18,8 @@ export const HermesMessageSchema = z.object({
19
18
  parentId: z.string().optional(),
20
19
  status: z.enum(["PENDING", "PROCESSED", "WAITING", "APPROVED"]),
21
20
  priority: z.enum(["HIGH", "NORMAL", "LOW"]).optional(),
22
- action: z.string().optional()
21
+ action: z.string().optional(),
22
+ requiresApproval: z.boolean().optional(),
23
23
  });
24
24
 
25
25
  export type HermesMessage = z.infer<typeof HermesMessageSchema>;
@@ -49,162 +49,182 @@ export async function orchestrateCommand() {
49
49
  isLooping = true;
50
50
 
51
51
  while (isLooping) {
52
- // Initial load from disk to sync the cache
53
- if (Object.keys(agentStatusCache).length === 0) {
54
- const diskStatus = readStatus();
55
- agentStatusCache = diskStatus || {};
56
- }
52
+ try {
53
+ // Initial load from disk to sync the cache
54
+ if (Object.keys(agentStatusCache).length === 0) {
55
+ const diskStatus = readStatus();
56
+ agentStatusCache = diskStatus || {};
57
+ }
57
58
 
58
- // Check for agent timeouts (e.g. 30 minutes in EXECUTING state)
59
- const agentStatuses = readStatus();
60
- let updatedStatus = false;
61
- for (const [agentName, info] of Object.entries(agentStatuses)) {
62
- const { state, lastUpdated } = info as { state: string; task: string; lastUpdated?: string };
63
- if (state === "EXECUTING" && lastUpdated) {
64
- const lastUpdatedTime = Date.parse(lastUpdated);
65
- if (!isNaN(lastUpdatedTime) && Date.now() - lastUpdatedTime > 30 * 60 * 1000) {
66
- UI.error(`⚠️ Agent ${agentName} has TIMED OUT (in EXECUTING state for >30m). Transitioning to BLOCKED.`);
67
- agentStatuses[agentName] = { state: "BLOCKED", task: "Timeout occurred", lastUpdated: new Date().toISOString() };
68
- updatedStatus = true;
59
+ // Check for agent timeouts (e.g. 30 minutes in EXECUTING state)
60
+ const agentStatuses = readStatus();
61
+ let updatedStatus = false;
62
+ for (const [agentName, info] of Object.entries(agentStatuses)) {
63
+ const { state, lastUpdated } = info as { state: string; task: string; lastUpdated?: string };
64
+ if (state === "EXECUTING" && lastUpdated) {
65
+ const lastUpdatedTime = Date.parse(lastUpdated);
66
+ if (!isNaN(lastUpdatedTime) && Date.now() - lastUpdatedTime > 30 * 60 * 1000) {
67
+ UI.error(`⚠️ Agent ${agentName} has TIMED OUT (in EXECUTING state for >30m). Transitioning to BLOCKED.`);
68
+ agentStatuses[agentName] = { state: "BLOCKED", task: "Timeout occurred", lastUpdated: new Date().toISOString() };
69
+ updatedStatus = true;
70
+ }
69
71
  }
70
72
  }
71
- }
72
- if (updatedStatus) {
73
- updateDocumentStore("status", agentStatuses);
74
- agentStatusCache = { ...agentStatuses }; // Sync cache
75
- }
73
+ if (updatedStatus) {
74
+ updateDocumentStore("status", agentStatuses);
75
+ agentStatusCache = { ...agentStatuses }; // Sync cache
76
+ }
76
77
 
77
- if (fs.existsSync(messagesDir)) {
78
- const messageFiles = fs.readdirSync(messagesDir).filter((f) => f.endsWith(".json"));
79
- const pendingMessages: HermesMessage[] = [];
80
- const fileMessagesMap: Record<string, HermesMessage[]> = {};
78
+ if (fs.existsSync(messagesDir)) {
79
+ const messageFiles = fs.readdirSync(messagesDir).filter((f) => f.endsWith(".json"));
80
+ const pendingMessages: HermesMessage[] = [];
81
+ const fileMessagesMap: Record<string, HermesMessage[]> = {};
81
82
 
82
- for (const file of messageFiles) {
83
- const filePath = path.join(messagesDir, file);
84
- try {
85
- const content = fs.readFileSync(filePath, "utf8").trim();
86
- if (!content) continue;
87
- const lines = content.split("\n");
88
- const allMsgs: HermesMessage[] = [];
89
- lines.forEach((line) => {
90
- if (!line.trim()) return;
91
- try {
92
- const parsed = JSON.parse(line);
93
- const msg = HermesMessageSchema.parse(parsed);
94
-
95
- // Contract Validation
96
- if (msg.category === "ACTION" || msg.category === "DELEGATION") {
97
- try {
98
- const payload = JSON.parse(msg.content);
99
- TaskRequestSchema.parse(payload);
100
- } catch (e) {
101
- UI.error(`Invalid task contract payload: ${(e as Error).message}`);
102
- return; // Skip invalid message
83
+ for (const file of messageFiles) {
84
+ const filePath = path.join(messagesDir, file);
85
+ try {
86
+ const content = fs.readFileSync(filePath, "utf8").trim();
87
+ if (!content) continue;
88
+ const lines = content.split("\n");
89
+ const allMsgs: HermesMessage[] = [];
90
+ lines.forEach((line) => {
91
+ if (!line.trim()) return;
92
+ try {
93
+ const parsed = JSON.parse(line);
94
+ const msg = HermesMessageSchema.parse(parsed);
95
+
96
+ // Contract Validation
97
+ if (msg.category === "ACTION" || msg.category === "DELEGATION") {
98
+ try {
99
+ const payload = JSON.parse(msg.content);
100
+ TaskRequestSchema.parse(payload);
101
+ } catch (e) {
102
+ UI.error(`Invalid task contract payload: ${(e as Error).message}`);
103
+ return; // Skip invalid message
104
+ }
103
105
  }
104
- }
105
106
 
106
- if (msg.status === "PENDING") {
107
- pendingMessages.push(msg);
107
+ // A message is actionable if it's pending, or if it's been approved.
108
+ if (msg.status === "PENDING" || msg.status === "APPROVED") {
109
+ pendingMessages.push(msg);
110
+ }
111
+ allMsgs.push(msg);
112
+ } catch (e) {
113
+ UI.error(`Skipping invalid Hermes message format: ${(e as Error).message}`);
108
114
  }
109
- allMsgs.push(msg);
110
- } catch (e) {
111
- UI.error(`Skipping invalid Hermes message format: ${(e as Error).message}`);
112
- }
113
- });
114
- fileMessagesMap[file] = allMsgs;
115
- } catch (err) {
116
- UI.error(`Error occurred while reading ${file}.`);
117
- logger.debug(`Failed to read message file ${file}`, err);
115
+ });
116
+ fileMessagesMap[file] = allMsgs;
117
+ } catch (err) {
118
+ UI.error(`Error occurred while reading ${file}.`);
119
+ logger.debug(`Failed to read message file ${file}`, err);
120
+ }
118
121
  }
119
- }
120
122
 
121
- if (pendingMessages.length > 0) {
122
- // Sort by priority: HIGH -> NORMAL -> LOW
123
- const priorityOrder = { HIGH: 1, NORMAL: 2, LOW: 3 };
124
- pendingMessages.sort((a, b) => {
125
- const pA = priorityOrder[a.priority || "NORMAL"] || 2;
126
- const pB = priorityOrder[b.priority || "NORMAL"] || 2;
127
- if (pA !== pB) return pA - pB;
128
- return Date.parse(a.timestamp) - Date.parse(b.timestamp);
129
- });
123
+ if (pendingMessages.length > 0) {
124
+ // Sort by priority: HIGH -> NORMAL -> LOW
125
+ const priorityOrder = { HIGH: 1, NORMAL: 2, LOW: 3 };
126
+ pendingMessages.sort((a, b) => {
127
+ const pA = priorityOrder[a.priority || "NORMAL"] || 2;
128
+ const pB = priorityOrder[b.priority || "NORMAL"] || 2;
129
+ if (pA !== pB) return pA - pB;
130
+ return Date.parse(a.timestamp) - Date.parse(b.timestamp);
131
+ });
132
+
133
+ UI.intent("Multi-Agent Orchestration", `${pendingMessages.length} pending tasks and collaboration messages found.`);
130
134
 
131
- UI.intent("Multi-Agent Orchestration", `${pendingMessages.length} pending tasks and collaboration messages found.`);
135
+ const statusChanges: Record<string, { state: string; task: string }> = {};
132
136
 
133
- const statusChanges: Record<string, { state: string; task: string }> = {};
137
+ for (const msg of pendingMessages) {
138
+ const { from, to, content, traceId, category, status, requiresApproval } = msg;
134
139
 
135
- for (const msg of pendingMessages) {
136
- const { from, to, content, traceId, category, parentId } = msg;
140
+ // Approval Gate for high-risk actions.
141
+ // If it requires approval but isn't approved yet, skip it for now.
142
+ if ((category === "ACTION" || category === "ALERT") && requiresApproval && status !== "APPROVED") {
143
+ logger.debug(`Action for @${to} (Trace: ${traceId}) is pending manager approval.`);
144
+ continue;
145
+ }
146
+
147
+ // Gate for regular messages.
148
+ // If it's a non-approval-required message, it should only be processed when PENDING.
149
+ // This prevents re-processing of SUBTASK/REPLY messages that might get set to APPROVED.
150
+ if (!(category === "ACTION" || category === "ALERT") && status !== "PENDING") {
151
+ continue;
152
+ }
137
153
 
138
- const isSubtask = category === "SUBTASK" || !!parentId;
139
- const agentLabel = isSubtask ? `${to} (Sub-Agent)` : to;
140
154
 
141
- UI.agentBox(agentLabel, content, `Trace: ${traceId} | From: ${from} | Category: ${category}`);
155
+ const isSubtask = category === "SUBTASK" || !!msg.parentId;
156
+ const agentLabel = isSubtask ? `${to} (Sub-Agent)` : to;
142
157
 
143
- let targetState = "EXECUTING";
144
- if (category === "DELEGATION" || category === "SUBTASK") targetState = "BRIEFED";
158
+ UI.agentBox(agentLabel, content, `Trace: ${traceId} | From: ${from} | Category: ${category}`);
145
159
 
146
- let taskDescription = content;
147
- if (category === "DELEGATION" || category === "ACTION") {
148
- try {
149
- const payload = JSON.parse(content);
150
- taskDescription = payload.task || content;
151
- } catch {
152
- // Ignore
160
+ let targetState = "EXECUTING";
161
+ if (category === "DELEGATION" || category === "SUBTASK") targetState = "BRIEFED";
162
+
163
+ let taskDescription = content;
164
+ if (category === "DELEGATION" || category === "ACTION") {
165
+ try {
166
+ const payload = JSON.parse(content);
167
+ taskDescription = payload.task || content;
168
+ } catch {
169
+ // Ignore
170
+ }
153
171
  }
154
- }
155
172
 
156
- statusChanges[to] = { state: targetState, task: taskDescription };
173
+ statusChanges[to] = { state: targetState, task: taskDescription };
157
174
 
158
- if (targetState === "BRIEFED") {
159
- UI.success(`${to} informed and took over the task.`);
160
- statusChanges[to] = { state: "EXECUTING", task: taskDescription };
161
- }
175
+ if (targetState === "BRIEFED") {
176
+ UI.success(`${to} informed and took over the task.`);
177
+ statusChanges[to] = { state: "EXECUTING", task: taskDescription };
178
+ }
162
179
 
163
- if (isSubtask) {
164
- UI.success(`Sub-task connection established (Parent: ${parentId || "Unknown"}).`);
165
- }
180
+ if (isSubtask) {
181
+ UI.success(`Sub-task connection established (Parent: ${msg.parentId || "Unknown"}).`);
182
+ }
166
183
 
167
- UI.success(`Collaboration initiated: ${from} -> ${to}`);
168
- console.warn(chalk.gray(` 👉 Instructions: ${frameworkDir}/agents/${to.replace("@", "")}.md`));
184
+ UI.success(`Collaboration initiated: ${from} -> ${to}`);
169
185
 
170
- msg.status = "PROCESSED";
171
- }
186
+ msg.status = "PROCESSED";
187
+ }
172
188
 
173
- applyBatchStatusUpdates(statusChanges);
189
+ applyBatchStatusUpdates(statusChanges);
174
190
 
175
- const archiveDir = path.join(messagesDir, "archive");
176
-
177
- for (const file of Object.keys(fileMessagesMap)) {
178
- const filePath = path.join(messagesDir, file);
179
- const msgs = fileMessagesMap[file];
180
-
181
- const activeMsgs = msgs.filter(m => m.status !== "PROCESSED");
182
- const processedMsgs = msgs.filter(m => m.status === "PROCESSED");
191
+ const archiveDir = path.join(messagesDir, "archive");
183
192
 
184
- try {
185
- if (activeMsgs.length > 0) {
186
- const updatedContent = activeMsgs.map(m => JSON.stringify(m)).join("\n") + "\n";
187
- writeTextFile(filePath, updatedContent);
188
- } else if (fs.existsSync(filePath)) {
189
- writeTextFile(filePath, "");
190
- }
193
+ for (const file of Object.keys(fileMessagesMap)) {
194
+ const filePath = path.join(messagesDir, file);
195
+ const msgs = fileMessagesMap[file];
196
+
197
+ const activeMsgs = msgs.filter(m => m.status !== "PROCESSED");
198
+ const processedMsgs = msgs.filter(m => m.status === "PROCESSED");
199
+
200
+ try {
201
+ if (activeMsgs.length > 0) {
202
+ const updatedContent = activeMsgs.map(m => JSON.stringify(m)).join("\n") + "\n";
203
+ writeTextFile(filePath, updatedContent);
204
+ } else if (fs.existsSync(filePath)) {
205
+ writeTextFile(filePath, "");
206
+ }
191
207
 
192
- if (processedMsgs.length > 0) {
193
- if (!fs.existsSync(archiveDir)) {
194
- fs.mkdirSync(archiveDir, { recursive: true });
208
+ if (processedMsgs.length > 0) {
209
+ if (!fs.existsSync(archiveDir)) {
210
+ fs.mkdirSync(archiveDir, { recursive: true });
211
+ }
212
+ const agentName = file.replace(".json", "");
213
+ const archivePath = path.join(archiveDir, `${agentName}_archive.json`);
214
+ const archiveContent = processedMsgs.map(m => JSON.stringify(m)).join("\n") + "\n";
215
+ appendFile(archivePath, archiveContent);
216
+ UI.success(`Archived ${processedMsgs.length} messages for @${agentName}`);
195
217
  }
196
- const agentName = file.replace(".json", "");
197
- const archivePath = path.join(archiveDir, `${agentName}_archive.json`);
198
- const archiveContent = processedMsgs.map(m => JSON.stringify(m)).join("\n") + "\n";
199
- appendFile(archivePath, archiveContent);
200
- UI.success(`Archived ${processedMsgs.length} messages for @${agentName}`);
218
+ } catch (err) {
219
+ UI.error(`Error occurred while updating and archiving the message file ${file}.`);
220
+ logger.debug(`Failed to update and archive message file ${file}`, err);
201
221
  }
202
- } catch (err) {
203
- UI.error(`Error occurred while updating and archiving the message file ${file}.`);
204
- logger.debug(`Failed to update and archive message file ${file}`, err);
205
222
  }
206
223
  }
207
224
  }
225
+ } catch (globalLoopErr) {
226
+ UI.error(`⚠️ Critical error in Hermes orchestration loop: ${(globalLoopErr as Error).message}`);
227
+ logger.debug("Hermes global loop failure", globalLoopErr);
208
228
  }
209
229
 
210
230
  // Prevent 100% CPU and provide breathing room for I/O
@@ -223,6 +243,7 @@ export async function sendMessage(args: {
223
243
  traceId: string;
224
244
  parentId?: string;
225
245
  priority?: "HIGH" | "NORMAL" | "LOW";
246
+ requiresApproval?: boolean;
226
247
  }) {
227
248
  const frameworkDir = getFrameworkDir();
228
249
  const messagesDir = path.join(frameworkDir, "messages");
@@ -274,11 +295,14 @@ export async function sendMessage(args: {
274
295
 
275
296
  try {
276
297
  const defaultPriority = (args.category === "ALERT" || args.category === "ACTION") ? "HIGH" : "NORMAL";
298
+ const requiresApproval = args.requiresApproval !== undefined ? args.requiresApproval : (args.category === "ALERT" || args.category === "ACTION");
299
+
277
300
  const message: HermesMessage = {
278
301
  timestamp: new Date().toISOString(),
279
302
  priority: args.priority || defaultPriority,
280
303
  ...args,
281
- status: "PENDING"
304
+ status: "PENDING",
305
+ requiresApproval: requiresApproval,
282
306
  };
283
307
 
284
308
  appendFile(messagePath, JSON.stringify(message) + "\n");
@@ -8,7 +8,7 @@ import { ensureDir, writeTextFile, writeJsonFile, appendFile } from "../../share
8
8
 
9
9
  export { ensureDir, writeTextFile, writeJsonFile, appendFile };
10
10
 
11
- export function updateGitIgnore(targetPath: string, frameworkDir = ".gemini", dryRun = false): void {
11
+ export function updateGitIgnore(targetPath: string, frameworkDir = ".enderun", dryRun = false): void {
12
12
  const IGNORE_LINES = [
13
13
  "# Agent Enderun",
14
14
  `${frameworkDir}/logs/*.json`,
@@ -5,9 +5,13 @@ import { logger } from "../../shared/logger.js";
5
5
  import { ensureDir, writeTextFile, writeJsonFile } from "../../shared/fs.js";
6
6
  import { StateSchema, AgentStatusSchema, TaskSchema } from "./schemas.js";
7
7
  import type { TraceID } from "../../shared/types.js";
8
+ import {
9
+ FRAMEWORK,
10
+ FRAMEWORK_DIR_CANDIDATES,
11
+ MCP,
12
+ } from "../../shared/constants.js";
8
13
 
9
14
  export { generateULID } from "./time.js";
10
- export { FRAMEWORK_DIR_CANDIDATES } from "../adapters/index.js";
11
15
 
12
16
  const CWD = process.cwd();
13
17
  const HOME = os.homedir();
@@ -28,9 +32,24 @@ function findFrameworkDir(basePath: string): string | null {
28
32
  return null;
29
33
  }
30
34
 
35
+ export function isFrameworkDevelopmentRepo(): boolean {
36
+ try {
37
+ const pkgPath = path.join(CWD, "package.json");
38
+ if (fs.existsSync(pkgPath)) {
39
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
40
+ if (pkg.name === "agent-enderun") {
41
+ return true;
42
+ }
43
+ }
44
+ } catch (err) {
45
+ logger.debug("Failed to read package.json in isFrameworkDevelopmentRepo", err);
46
+ }
47
+ return false;
48
+ }
49
+
31
50
  export function getLocalFrameworkDir(): string {
32
51
  const localDir = findFrameworkDir(CWD);
33
- return localDir || path.join(CWD, ".gemini");
52
+ return localDir || path.join(CWD, FRAMEWORK.CORE_DIR);
34
53
  }
35
54
 
36
55
  export function getConfigDir(): string {
@@ -38,17 +57,23 @@ export function getConfigDir(): string {
38
57
  if (localDir) return localDir;
39
58
 
40
59
  // Check for standard local directories if package.json doesn't specify
41
- const localCandidates = [".enderun", ".gemini", ".claude", ".cursor", ".antigravity", ".agent"];
60
+ const localCandidates = [...FRAMEWORK_DIR_CANDIDATES, ".agent"] as string[];
42
61
  for (const cand of localCandidates) {
43
62
  const p = path.join(CWD, cand);
44
63
  if (fs.existsSync(p)) return p;
45
64
  }
46
65
 
47
- return path.join(HOME, ".gemini");
66
+ // In dev repo, don't fall back to global dir. Point to local default.
67
+ if (isFrameworkDevelopmentRepo()) {
68
+ return path.join(CWD, FRAMEWORK.CORE_DIR);
69
+ }
70
+
71
+ return path.join(HOME, FRAMEWORK.CORE_DIR);
48
72
  }
49
73
 
50
74
  export function getFrameworkDir(): string {
51
- if (process.env.ENDERUN_TEST_DIR) return process.env.ENDERUN_TEST_DIR;
75
+ const testDir = process.env[MCP.TEST_DIR_ENV];
76
+ if (testDir) return testDir;
52
77
  return getConfigDir();
53
78
  }
54
79
 
@@ -105,7 +130,7 @@ export function readActiveTraceId(memoryContent: string): string | null {
105
130
  try {
106
131
  const state = JSON.parse(memoryContent);
107
132
  return state.traceId || null;
108
- } catch {
133
+ } catch (_e) { /* ignore */
109
134
  return null;
110
135
  }
111
136
  }
@@ -254,7 +279,7 @@ export function acquireMemoryLock(lockPath: string): boolean {
254
279
  fs.writeFileSync(lockPath, String(Date.now()), { flag: "wx" });
255
280
  return true;
256
281
  }
257
- } catch {
282
+ } catch (_e) { /* ignore */
258
283
  // Ignore
259
284
  }
260
285
  }
@@ -3,6 +3,7 @@ import type { AdapterConfig, AdapterId } from "./types.js";
3
3
  import { addMcpServerToClaude, findClaudeConfigPath } from "../../cli/utils/claude.js";
4
4
  import { writeJsonFile } from "../../shared/fs.js";
5
5
  import { registerGlobalAntigravityPlugins } from "./shared.js";
6
+ import { unifiedAdapterPath, UNIFIED_ADAPTER_SLUG } from "../../shared/constants.js";
6
7
 
7
8
  /**
8
9
  * Mapped Adapter Configurations
@@ -16,7 +17,7 @@ export const ADAPTER_CONFIGS: Record<AdapterId, AdapterConfig> = {
16
17
  role: "commander",
17
18
  templateDir: ".enderun",
18
19
  nestedDirs: ["agents", "rules"],
19
- agentsDir: ".gemini/agents",
20
+ agentsDir: ".gemini/agents", // Use unified path
20
21
  agentsExt: ".md"
21
22
  },
22
23
  claude: {
@@ -38,7 +39,7 @@ export const ADAPTER_CONFIGS: Record<AdapterId, AdapterConfig> = {
38
39
  role: "researcher",
39
40
  templateDir: ".enderun",
40
41
  nestedDirs: ["rules"],
41
- agentsDir: ".grok",
42
+ agentsDir: ".grok/agents",
42
43
  agentsExt: ".md"
43
44
  },
44
45
  cursor: {
@@ -60,7 +61,7 @@ export const ADAPTER_CONFIGS: Record<AdapterId, AdapterConfig> = {
60
61
  role: "implementer",
61
62
  templateDir: ".enderun",
62
63
  nestedDirs: ["skills", "rules", "instructions"],
63
- agentsDir: ".agents/skills",
64
+ agentsDir: ".agents/instructions",
64
65
  agentsExt: ".md"
65
66
  },
66
67
  "antigravity-cli": {
@@ -6,6 +6,7 @@
6
6
  import fs from "fs";
7
7
  import path from "path";
8
8
  import { AgentDefinition } from "./types.js";
9
+ import { getPackageRoot } from "../../cli/utils/pkg.js";
9
10
 
10
11
  // Import individual agent definitions
11
12
  import { manager } from "./registry/manager.js";
@@ -124,7 +125,7 @@ function resolveModel(
124
125
  }
125
126
 
126
127
  /** Build a flat system prompt string from structured instructions */
127
- function buildSystemPrompt(ag: AgentDefinition, baseKnowledgeDir: string = path.join(process.cwd(), "templates/standards")): string {
128
+ function buildSystemPrompt(ag: AgentDefinition, baseKnowledgeDir: string = path.join(getPackageRoot(), "templates/standards")): string {
128
129
  const lines: string[] = [
129
130
  "# Identity",
130
131
  ag.instructions.identity,
@@ -42,5 +42,6 @@ export const architect: AgentDefinition = {
42
42
  "CONTRACT STABILITY: Validate 'contract.version.json' consistency before and after every schema change.",
43
43
  "GOVERNANCE READ: Always read architecture governance docs before making design decisions.",
44
44
  ],
45
+ knowledgeFiles: ["architecture-standards.md"],
45
46
  },
46
47
  };
@@ -7,8 +7,8 @@ export const backend: AgentDefinition = {
7
7
  displayName: "Backend Specialist",
8
8
  role: "Backend Development",
9
9
  description:
10
- "Server logic, Databases, and API implementation specialist. " +
11
- "Owns the Database Management & Migrations discipline.",
10
+ "Server logic and API implementation specialist. " +
11
+ "Owns the API contracts and business logic implementation.",
12
12
  capability: 9,
13
13
  tier: "core",
14
14
  tags: ["core", "logic"],
@@ -42,6 +42,6 @@ export const backend: AgentDefinition = {
42
42
  "HIGH-RISK OPS: Refuse User/Role management, bulk deletes, schema alterations, and billing changes autonomously. " +
43
43
  "Return a standard refusal, send a managerApproval request to @manager, and shift to WAITING status.",
44
44
  ],
45
- knowledgeFiles: ["skills/database_management.md"],
45
+ knowledgeFiles: ["crud-governance.md"],
46
46
  },
47
47
  };
@@ -7,8 +7,8 @@ export const database: AgentDefinition = {
7
7
  displayName: "Database Specialist",
8
8
  role: "Data Management",
9
9
  description:
10
- "SQL Schema design and Query Optimization specialist. " +
11
- "Manages data as the empire's treasury secure and optimized.",
10
+ "SQL Schema design, migration, and query optimization specialist. " +
11
+ "Owns the database schema, migrations, and data integrity.",
12
12
  capability: 9,
13
13
  tier: "core",
14
14
  tags: ["core", "data"],
@@ -37,6 +37,6 @@ export const database: AgentDefinition = {
37
37
  "DETERMINISTIC MIGRATIONS: Every migration must be reversible and produce identical results across environments.",
38
38
  "PERFORMANCE FIRST: Design indexes proactively — never retroactively after a performance incident.",
39
39
  ],
40
- knowledgeFiles: ["skills/database_management.md"],
40
+ knowledgeFiles: ["performance-standards.md", "quality-standards.md"],
41
41
  },
42
42
  };
@@ -37,6 +37,6 @@ export const devops: AgentDefinition = {
37
37
  "ENVIRONMENT ISOLATION: All variables managed via '.env' — hardcoded secrets trigger immediate escalation.",
38
38
  "NO UNTRACKED DEPLOYMENTS: Every deployment must be declared, versioned, and traceable.",
39
39
  ],
40
- knowledgeFiles: ["skills/devops_infrastructure.md"],
40
+ knowledgeFiles: ["deployment-standards.md"],
41
41
  },
42
42
  };
@@ -21,6 +21,7 @@ export const quality: AgentDefinition = {
21
21
  "read_project_memory",
22
22
  "get_memory_insights",
23
23
  "run_tests",
24
+ "check_lint",
24
25
  "log_agent_action",
25
26
  "send_agent_message",
26
27
  ],