agent-enderun 0.8.7 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/{ENDERUN.md → .enderun/ENDERUN.md} +5 -3
  2. package/.enderun/PROJECT_MEMORY.md +12 -53
  3. package/.enderun/STATUS.md +7 -8
  4. package/.enderun/agents/agent_army_schema.json +28 -6
  5. package/.enderun/agents/analyst.md +9 -18
  6. package/.enderun/agents/backend.md +22 -52
  7. package/.enderun/agents/devops.md +9 -19
  8. package/.enderun/agents/explorer.md +7 -16
  9. package/.enderun/agents/frontend.md +82 -84
  10. package/.enderun/agents/git.md +7 -16
  11. package/.enderun/agents/manager.md +34 -40
  12. package/.enderun/agents/mobile.md +7 -16
  13. package/.enderun/agents/native.md +7 -16
  14. package/.enderun/agents/orchestrator.md +16 -25
  15. package/.enderun/agents/qa.md +12 -24
  16. package/.enderun/agents/schema/agent-lifecycle-schema.json +29 -8
  17. package/.enderun/agents/security.md +9 -18
  18. package/.enderun/config.json +1 -1
  19. package/.enderun/knowledge/SHIM_TEMPLATE.md +25 -0
  20. package/.enderun/knowledge/eslint-standards.md +63 -0
  21. package/.enderun/knowledge/hermes_protocol.md +1 -1
  22. package/.enderun/knowledge/token_economy_and_memory.md +129 -0
  23. package/.enderun/mcp_config.json +9 -0
  24. package/.enderun/memory-graph/agent-contexts/analyst.json +1 -1
  25. package/.enderun/memory-graph/agent-contexts/backend.json +1 -1
  26. package/.enderun/memory-graph/agent-contexts/devops.json +1 -1
  27. package/.enderun/memory-graph/agent-contexts/explorer.json +1 -1
  28. package/.enderun/memory-graph/agent-contexts/frontend.json +1 -1
  29. package/.enderun/memory-graph/agent-contexts/git.json +1 -1
  30. package/.enderun/memory-graph/agent-contexts/manager.json +1 -1
  31. package/.enderun/memory-graph/agent-contexts/mobile.json +1 -1
  32. package/.enderun/memory-graph/agent-contexts/native.json +1 -1
  33. package/.enderun/memory-graph/agent-contexts/orchestrator.json +1 -1
  34. package/.enderun/memory-graph/agent-contexts/qa.json +1 -1
  35. package/.enderun/memory-graph/agent-contexts/security.json +1 -1
  36. package/.enderun/memory-graph/graph.json +123 -16
  37. package/.enderun/memory-graph/shared-facts.json +26 -32
  38. package/README.md +93 -295
  39. package/agent.md +25 -0
  40. package/bin/cli.js +9 -9
  41. package/bin/update-contract.js +34 -37
  42. package/bin/validate-agent-army.js +38 -13
  43. package/claude.md +14 -8
  44. package/docs/README.md +35 -16
  45. package/docs/roadmap.md +1 -1
  46. package/docs/tech-stack.md +30 -0
  47. package/eslint.config.js +68 -0
  48. package/framework-mcp/dist/index.js +184 -24
  49. package/framework-mcp/package.json +6 -33
  50. package/framework-mcp/tsconfig.json +3 -6
  51. package/gemini.md +12 -6
  52. package/grok.md +25 -0
  53. package/mcp.json +2 -2
  54. package/package.json +12 -6
  55. package/panda.config.ts +86 -86
  56. package/tsconfig.json +9 -0
  57. package/.enderun/claude.md +0 -19
  58. package/.enderun/cursor.md +0 -19
  59. package/.enderun/gemini.md +0 -19
  60. package/.enderun/logs/analyst.json +0 -1
  61. package/.enderun/logs/backend.json +0 -1
  62. package/.enderun/logs/devops.json +0 -1
  63. package/.enderun/logs/explorer.json +0 -1
  64. package/.enderun/logs/frontend.json +0 -1
  65. package/.enderun/logs/git.json +0 -1
  66. package/.enderun/logs/manager.json +0 -1
  67. package/.enderun/logs/mobile.json +0 -1
  68. package/.enderun/logs/native.json +0 -1
  69. package/.enderun/logs/orchestrator.json +0 -1
  70. package/.enderun/logs/qa.json +0 -1
  71. package/.enderun/logs/security.json +0 -1
  72. package/.env.example +0 -10
  73. package/cursor.md +0 -19
  74. package/framework-mcp/README.md +0 -55
  75. package/framework-mcp/dist/schemas.js +0 -118
  76. package/framework-mcp/dist/tools/codebase.js +0 -294
  77. package/framework-mcp/dist/tools/contract.js +0 -197
  78. package/framework-mcp/dist/tools/framework.js +0 -196
  79. package/framework-mcp/dist/tools/git.js +0 -51
  80. package/framework-mcp/dist/tools/governance.js +0 -696
  81. package/framework-mcp/dist/tools/index.js +0 -47
  82. package/framework-mcp/dist/tools/knowledge.js +0 -133
  83. package/framework-mcp/dist/tools/memory.js +0 -200
  84. package/framework-mcp/dist/tools/messages.js +0 -193
  85. package/framework-mcp/dist/tools/monitoring.js +0 -294
  86. package/framework-mcp/dist/tools/orchestration.js +0 -368
  87. package/framework-mcp/dist/tools/pipeline.js +0 -267
  88. package/framework-mcp/dist/tools/security.js +0 -137
  89. package/framework-mcp/dist/utils.js +0 -121
  90. package/framework-mcp/src/index.ts +0 -49
  91. package/framework-mcp/src/schemas.ts +0 -106
  92. package/framework-mcp/src/tools/codebase.ts +0 -284
  93. package/framework-mcp/src/tools/contract.ts +0 -221
  94. package/framework-mcp/src/tools/framework.ts +0 -192
  95. package/framework-mcp/src/tools/git.ts +0 -42
  96. package/framework-mcp/src/tools/governance.ts +0 -891
  97. package/framework-mcp/src/tools/index.ts +0 -50
  98. package/framework-mcp/src/tools/knowledge.ts +0 -141
  99. package/framework-mcp/src/tools/memory.ts +0 -207
  100. package/framework-mcp/src/tools/messages.ts +0 -213
  101. package/framework-mcp/src/tools/monitoring.ts +0 -351
  102. package/framework-mcp/src/tools/orchestration.ts +0 -440
  103. package/framework-mcp/src/tools/pipeline.ts +0 -353
  104. package/framework-mcp/src/tools/security.ts +0 -143
  105. package/framework-mcp/src/utils.ts +0 -130
  106. package/src/cli/index.ts +0 -2188
@@ -1,351 +0,0 @@
1
- import { Tool } from "@modelcontextprotocol/sdk/types.js";
2
- import { promises as fs } from "fs";
3
- import path from "path";
4
- import { getFrameworkDir } from "../utils.js";
5
-
6
- // ─── Tool Definitions ─────────────────────────────────────────────────────────
7
-
8
- export const monitoringTools: Tool[] = [
9
- {
10
- name: "get_army_health_dashboard",
11
- description:
12
- "Returns a comprehensive health dashboard for the entire Agent Enderun Army. Includes agent states, active tasks, health scores, queue depth, and recent blockages. Single call for full army situational awareness.",
13
- inputSchema: {
14
- type: "object",
15
- properties: {
16
- projectRoot: { type: "string", description: "Root directory of the project" },
17
- },
18
- required: [],
19
- },
20
- },
21
- {
22
- name: "calculate_agent_performance_score",
23
- description:
24
- "Calculates a dynamic performance score for a specific agent based on their last N actions from the audit log. Returns success rate, average execution time, and blockage frequency.",
25
- inputSchema: {
26
- type: "object",
27
- properties: {
28
- agentName: { type: "string", description: "Agent identifier (e.g. @backend)" },
29
- lastN: { type: "number", description: "Number of recent actions to analyze", default: 20 },
30
- },
31
- required: ["agentName"],
32
- },
33
- },
34
- {
35
- name: "detect_bottleneck_agents",
36
- description:
37
- "Scans all agent logs and STATUS.md to identify agents that are causing delays — either by being BLOCKED, exceeding execution timeout, or having a high blockage frequency. Returns a prioritized list of bottlenecks.",
38
- inputSchema: {
39
- type: "object",
40
- properties: {
41
- thresholdMinutes: {
42
- type: "number",
43
- description: "Minutes after which an EXECUTING agent is considered a bottleneck",
44
- default: 30,
45
- },
46
- },
47
- required: [],
48
- },
49
- },
50
- {
51
- name: "generate_war_room_report",
52
- description:
53
- "Generates a crisis-mode summary report for @manager. Includes: all BLOCKED/ERROR agents, unresolved dependencies, critical path analysis, and recommended immediate actions. Designed for high-urgency situations.",
54
- inputSchema: {
55
- type: "object",
56
- properties: {
57
- traceId: { type: "string", description: "Active Trace ID for the crisis" },
58
- crisisDescription: { type: "string", description: "Brief description of the crisis or incident" },
59
- },
60
- required: ["traceId", "crisisDescription"],
61
- },
62
- },
63
- ];
64
-
65
- // ─── Handlers ─────────────────────────────────────────────────────────────────
66
-
67
- export const monitoringHandlers: Record<
68
- string,
69
- (args: unknown, projectRoot: string) => Promise<{ content: Array<{ type: string; text: string }> }>
70
- > = {
71
- async get_army_health_dashboard(_args, projectRoot) {
72
- const results: {
73
- generatedAt: string;
74
- agents: unknown[];
75
- queueDepth: { pending: number; processing: number; completed: number; failed: number };
76
- recentBlockages: unknown[];
77
- recentBroadcasts: unknown[];
78
- overallHealth: string;
79
- } = {
80
- generatedAt: new Date().toISOString(),
81
- agents: [],
82
- queueDepth: { pending: 0, processing: 0, completed: 0, failed: 0 },
83
- recentBlockages: [],
84
- recentBroadcasts: [],
85
- overallHealth: "GREEN",
86
- };
87
-
88
- // Read STATUS.md
89
- const frameworkDir = getFrameworkDir(projectRoot);
90
- const statusPath = path.join(projectRoot, frameworkDir, "STATUS.md");
91
- try {
92
- const statusContent = await fs.readFile(statusPath, "utf-8");
93
- const lines = statusContent
94
- .split("\n")
95
- .filter((l) => l.includes("|") && !l.includes("---") && !l.includes("Agent"));
96
- results.agents = lines
97
- .map((line) => {
98
- const cells = line.split("|").map((c) => c.trim()).filter(Boolean);
99
- return cells.length >= 4
100
- ? {
101
- agent: cells[0],
102
- state: cells[1],
103
- currentTask: cells[2],
104
- blockedBy: cells[3],
105
- healthScore: cells[4] ?? "—",
106
- lastAction: cells[5] ?? "—",
107
- }
108
- : null;
109
- })
110
- .filter(Boolean);
111
- } catch {
112
- results.agents = [{ error: "STATUS.md not found or unreadable" }];
113
- }
114
-
115
- // Read queue depths
116
- const queueBase = path.join(projectRoot, getFrameworkDir(projectRoot), "queue");
117
- for (const subdir of ["pending", "processing", "completed", "failed"] as const) {
118
- try {
119
- const files = await fs.readdir(path.join(queueBase, subdir));
120
- results.queueDepth[subdir] = files.filter((f) => f.endsWith(".json")).length;
121
- } catch {
122
- results.queueDepth[subdir] = 0;
123
- }
124
- }
125
-
126
- // Read orchestrator log for recent blockages and broadcasts
127
- const logPath = path.join(projectRoot, getFrameworkDir(projectRoot), "logs", "orchestrator.json");
128
- try {
129
- const logContent = await fs.readFile(logPath, "utf-8");
130
- const log = JSON.parse(logContent) as {
131
- blockages?: unknown[];
132
- broadcasts?: unknown[];
133
- };
134
- results.recentBlockages = (log.blockages ?? []).slice(-5);
135
- results.recentBroadcasts = (log.broadcasts ?? []).slice(-3);
136
- } catch {
137
- // No log yet
138
- }
139
-
140
- // Calculate overall health
141
- const blockedCount = (results.agents as Array<{ state?: string }>).filter(
142
- (a) => a?.state?.includes("BLOCKED") || a?.state?.includes("ERROR")
143
- ).length;
144
- if (blockedCount >= 3) results.overallHealth = "RED";
145
- else if (blockedCount >= 1) results.overallHealth = "YELLOW";
146
- else results.overallHealth = "GREEN";
147
-
148
- return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
149
- },
150
-
151
- async calculate_agent_performance_score(args, projectRoot) {
152
- const { agentName, lastN } = args as { agentName: string; lastN?: number };
153
- const n = lastN ?? 20;
154
- const logName = agentName.replace("@", "") + ".json";
155
- const logPath = path.join(projectRoot, getFrameworkDir(projectRoot), "logs", logName);
156
-
157
- try {
158
- const content = await fs.readFile(logPath, "utf-8");
159
- const log = JSON.parse(content) as { actions?: Array<{ status?: string; durationMs?: number }> };
160
- const actions = (log.actions ?? []).slice(-n);
161
- const total = actions.length;
162
- const successful = actions.filter((a) => a.status === "SUCCESS" || a.status === "DONE").length;
163
- const successRate = total > 0 ? ((successful / total) * 100).toFixed(1) + "%" : "N/A";
164
- const avgDuration =
165
- total > 0
166
- ? (
167
- actions.reduce((sum, a) => sum + (a.durationMs ?? 0), 0) / total
168
- ).toFixed(0) + "ms"
169
- : "N/A";
170
-
171
- return {
172
- content: [
173
- {
174
- type: "text",
175
- text: JSON.stringify(
176
- {
177
- agent: agentName,
178
- analyzedActions: total,
179
- successRate,
180
- averageExecutionTime: avgDuration,
181
- generatedAt: new Date().toISOString(),
182
- },
183
- null,
184
- 2
185
- ),
186
- },
187
- ],
188
- };
189
- } catch {
190
- return {
191
- content: [
192
- {
193
- type: "text",
194
- text: JSON.stringify({
195
- agent: agentName,
196
- error: "No log found or log is empty",
197
- analyzedActions: 0,
198
- }),
199
- },
200
- ],
201
- };
202
- }
203
- },
204
-
205
- async detect_bottleneck_agents(args, projectRoot) {
206
- const { thresholdMinutes } = args as { thresholdMinutes?: number };
207
- const threshold = thresholdMinutes ?? 30;
208
-
209
- const statusPath = path.join(projectRoot, getFrameworkDir(projectRoot), "STATUS.md");
210
- const bottlenecks: Array<{ agent: string; reason: string; state: string; severity: string }> = [];
211
-
212
- try {
213
- const content = await fs.readFile(statusPath, "utf-8");
214
- const lines = content
215
- .split("\n")
216
- .filter((l) => l.includes("|") && !l.includes("---") && !l.includes("Agent"));
217
-
218
- for (const line of lines) {
219
- const cells = line.split("|").map((c) => c.trim()).filter(Boolean);
220
- if (cells.length < 2) continue;
221
- const [agent, state] = cells;
222
- if (state.includes("BLOCKED")) {
223
- bottlenecks.push({ agent, state, reason: "Agent is in BLOCKED state", severity: "CRITICAL" });
224
- } else if (state.includes("ERROR")) {
225
- bottlenecks.push({ agent, state, reason: "Agent is in ERROR state", severity: "CRITICAL" });
226
- } else if (state.includes("EXECUTING")) {
227
- bottlenecks.push({
228
- agent,
229
- state,
230
- reason: `Agent has been EXECUTING for more than ${threshold} minutes (verify manually)`,
231
- severity: "HIGH",
232
- });
233
- }
234
- }
235
- } catch {
236
- return {
237
- content: [
238
- {
239
- type: "text",
240
- text: JSON.stringify({ error: "Could not read STATUS.md" }),
241
- },
242
- ],
243
- };
244
- }
245
-
246
- return {
247
- content: [
248
- {
249
- type: "text",
250
- text: JSON.stringify(
251
- {
252
- bottlenecks,
253
- total: bottlenecks.length,
254
- analysisThresholdMinutes: threshold,
255
- generatedAt: new Date().toISOString(),
256
- },
257
- null,
258
- 2
259
- ),
260
- },
261
- ],
262
- };
263
- },
264
-
265
- async generate_war_room_report(args, projectRoot) {
266
- const { traceId, crisisDescription } = args as {
267
- traceId: string;
268
- crisisDescription: string;
269
- };
270
-
271
- // Gather all data for the war room report
272
- const report: {
273
- warRoomReport: boolean;
274
- traceId: string;
275
- crisisDescription: string;
276
- generatedAt: string;
277
- agentStatuses: unknown[];
278
- queueSnapshot: unknown;
279
- recentBlockages: unknown[];
280
- immediateActions: string[];
281
- } = {
282
- warRoomReport: true,
283
- traceId,
284
- crisisDescription,
285
- generatedAt: new Date().toISOString(),
286
- agentStatuses: [],
287
- queueSnapshot: {},
288
- recentBlockages: [],
289
- immediateActions: [],
290
- };
291
-
292
- // Agent statuses
293
- const frameworkDir = getFrameworkDir(projectRoot);
294
- const statusPath = path.join(projectRoot, frameworkDir, "STATUS.md");
295
- try {
296
- const content = await fs.readFile(statusPath, "utf-8");
297
- const lines = content
298
- .split("\n")
299
- .filter((l) => l.includes("|") && !l.includes("---") && !l.includes("Agent"));
300
- report.agentStatuses = lines
301
- .map((l) => {
302
- const cells = l.split("|").map((c) => c.trim()).filter(Boolean);
303
- return cells.length >= 2 ? { agent: cells[0], state: cells[1] } : null;
304
- })
305
- .filter(Boolean);
306
- } catch {
307
- report.agentStatuses = [];
308
- }
309
-
310
- // Queue snapshot
311
- const queueBase = path.join(projectRoot, getFrameworkDir(projectRoot), "queue");
312
- const snapshot: Record<string, number> = {};
313
- for (const dir of ["pending", "processing", "completed", "failed"]) {
314
- try {
315
- const files = await fs.readdir(path.join(queueBase, dir));
316
- snapshot[dir] = files.length;
317
- } catch {
318
- snapshot[dir] = 0;
319
- }
320
- }
321
- report.queueSnapshot = snapshot;
322
-
323
- // Recent blockages
324
- const logPath = path.join(projectRoot, getFrameworkDir(projectRoot), "logs", "orchestrator.json");
325
- try {
326
- const logContent = await fs.readFile(logPath, "utf-8");
327
- const log = JSON.parse(logContent) as { blockages?: unknown[] };
328
- report.recentBlockages = (log.blockages ?? []).slice(-10);
329
- } catch {
330
- report.recentBlockages = [];
331
- }
332
-
333
- // Generate recommended immediate actions
334
- const blockedAgents = (report.agentStatuses as Array<{ agent: string; state: string }>).filter(
335
- (a) => a.state.includes("BLOCKED") || a.state.includes("ERROR")
336
- );
337
- if (blockedAgents.length > 0) {
338
- report.immediateActions.push(
339
- `IMMEDIATE: Resolve ${blockedAgents.map((a) => a.agent).join(", ")} blockages — these are on the critical path`
340
- );
341
- }
342
- if ((snapshot["pending"] ?? 0) > 5) {
343
- report.immediateActions.push("HIGH: Queue has excessive pending tasks — consider deploying additional agents");
344
- }
345
- if (report.immediateActions.length === 0) {
346
- report.immediateActions.push("No critical immediate actions required — monitor agent states");
347
- }
348
-
349
- return { content: [{ type: "text", text: JSON.stringify(report, null, 2) }] };
350
- },
351
- };