agent-enderun 0.8.6 → 0.8.8
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.
- package/.enderun/BRAIN_DASHBOARD.md +4 -4
- package/.enderun/PROJECT_MEMORY.md +7 -7
- package/.enderun/STATUS.md +3 -5
- package/.enderun/agents/agent_army_schema.json +1 -1
- package/.enderun/agents/analyst.md +11 -18
- package/.enderun/agents/backend.md +42 -31
- package/.enderun/agents/devops.md +9 -18
- package/.enderun/agents/explorer.md +7 -16
- package/.enderun/agents/frontend.md +75 -91
- package/.enderun/agents/git.md +7 -16
- package/.enderun/agents/manager.md +38 -25
- package/.enderun/agents/mobile.md +7 -16
- package/.enderun/agents/orchestrator.md +16 -25
- package/.enderun/agents/qa.md +16 -22
- package/.enderun/agents/security.md +9 -18
- package/.enderun/cli-commands.json +0 -4
- package/.enderun/config.json +2 -2
- package/.enderun/knowledge/manager_authority_audit_enforcement.md +2 -2
- package/.enderun/knowledge/token_economy_and_memory.md +129 -0
- package/.enderun/memory-graph/agent-contexts/manager.json +1 -1
- package/.enderun/memory-graph/graph.json +1 -2
- package/.enderun/memory-graph/shared-facts.json +1 -2
- package/ENDERUN.md +1 -1
- package/README.md +379 -244
- package/docs/roadmap.md +1 -1
- package/framework-mcp/dist/schemas.js +0 -34
- package/framework-mcp/dist/tools/framework.js +40 -11
- package/framework-mcp/dist/tools/governance.js +1 -1
- package/framework-mcp/dist/tools/memory.js +24 -7
- package/framework-mcp/dist/tools/monitoring.js +6 -6
- package/framework-mcp/dist/tools/orchestration.js +9 -8
- package/framework-mcp/dist/utils.js +2 -2
- package/framework-mcp/package.json +1 -1
- package/framework-mcp/src/tools/framework.ts +39 -8
- package/framework-mcp/src/tools/memory.ts +20 -5
- package/framework-mcp/src/tools/monitoring.ts +6 -6
- package/framework-mcp/src/tools/orchestration.ts +9 -8
- package/framework-mcp/src/utils.ts +2 -2
- package/mcp.json +2 -2
- package/package.json +1 -1
- package/src/cli/index.ts +30 -2
- package/.enderun/agents/native.md +0 -115
- package/.enderun/claude.md +0 -19
- package/.enderun/cursor.md +0 -19
- package/.enderun/gemini.md +0 -19
- package/.enderun/memory-graph/agent-contexts/native.json +0 -1
package/docs/roadmap.md
CHANGED
|
@@ -42,7 +42,7 @@ This program systematically audits current agent maturity levels and runs struct
|
|
|
42
42
|
| **@explorer** | 6.5 | 8.5 | High / Stable. Highly capable in dependency analysis and codebase intelligence. |
|
|
43
43
|
| **@analyst** | 6.5 | 8.5 | Medium / Stable. Handles system health metrics and compliance audits. |
|
|
44
44
|
| **@git** | 6.0 | 8.0 | Medium / Stable. Coordinates PRs, semantic commits, and version compliance. |
|
|
45
|
-
| **@mobile
|
|
45
|
+
| **@mobile** | 2.0 | 7.5 | Low / Dormant. Foundational blueprints and Hermes safety rules added. |
|
|
46
46
|
|
|
47
47
|
> **Note:** `@frontend` is currently the highest risk to enterprise onboarding and is the top priority.
|
|
48
48
|
|
|
@@ -46,12 +46,6 @@ export const UPDATE_KNOWLEDGE_BASE_ARGS_SCHEMA = z.object({
|
|
|
46
46
|
content: z.string().min(1),
|
|
47
47
|
tags: z.array(z.string().min(1)).default([]),
|
|
48
48
|
});
|
|
49
|
-
export const ANALYZE_DATABASE_SCHEMA_ARGS_SCHEMA = z.object({
|
|
50
|
-
path: z.string().default("apps/backend"),
|
|
51
|
-
});
|
|
52
|
-
export const GENERATE_STRATEGIC_BRIEFING_ARGS_SCHEMA = z.object({
|
|
53
|
-
focusArea: z.string().optional(),
|
|
54
|
-
});
|
|
55
49
|
export const ANALYZE_CONSTITUTION_COMPLIANCE_ARGS_SCHEMA = z.object({
|
|
56
50
|
path: z.string().min(1),
|
|
57
51
|
});
|
|
@@ -59,15 +53,6 @@ export const GENERATE_DEPENDENCY_GRAPH_ARGS_SCHEMA = z.object({
|
|
|
59
53
|
path: z.string().default("src"),
|
|
60
54
|
format: z.enum(["mermaid", "json"]).default("mermaid"),
|
|
61
55
|
});
|
|
62
|
-
export const GENERATE_ACADEMY_PROGRESS_REPORT_ARGS_SCHEMA = z.object({
|
|
63
|
-
days: z.number().default(7),
|
|
64
|
-
});
|
|
65
|
-
export const ANALYZE_DOCUMENTATION_DEBT_ARGS_SCHEMA = z.object({
|
|
66
|
-
path: z.string().default("."),
|
|
67
|
-
});
|
|
68
|
-
export const VALIDATE_REPOSITORY_HEALTH_ARGS_SCHEMA = z.object({
|
|
69
|
-
scope: z.enum(["full", "lint", "test", "build"]).default("full"),
|
|
70
|
-
});
|
|
71
56
|
export const READ_AGENT_MESSAGES_ARGS_SCHEMA = z.object({
|
|
72
57
|
agent: z.string().min(1),
|
|
73
58
|
traceId: z.string().min(1).optional(),
|
|
@@ -81,17 +66,10 @@ export const GENERATE_SEMANTIC_COMMIT_MESSAGE_ARGS_SCHEMA = z.object({
|
|
|
81
66
|
export const ANALYZE_CODEBASE_INTELLIGENCE_ARGS_SCHEMA = z.object({
|
|
82
67
|
path: z.string().default("."),
|
|
83
68
|
});
|
|
84
|
-
export const GET_ACADEMY_PERFORMANCE_ARGS_SCHEMA = z.object({
|
|
85
|
-
periodDays: z.number().default(30),
|
|
86
|
-
});
|
|
87
69
|
export const ANALYZE_PROCEDURAL_CONTINUITY_ARGS_SCHEMA = z.object({
|
|
88
70
|
targetPath: z.string().min(1),
|
|
89
71
|
referencePath: z.string().min(1),
|
|
90
72
|
});
|
|
91
|
-
export const GET_AGENT_AUDIT_REPORT_ARGS_SCHEMA = z.object({
|
|
92
|
-
agent: z.string().min(1),
|
|
93
|
-
days: z.number().default(7),
|
|
94
|
-
});
|
|
95
73
|
export const GET_AGENT_INBOX_STATS_ARGS_SCHEMA = z.object({
|
|
96
74
|
agent: z.string().min(1),
|
|
97
75
|
});
|
|
@@ -104,15 +82,3 @@ export const SYNC_CONTRACT_HASH_ARGS_SCHEMA = z.object({
|
|
|
104
82
|
export const VERIFY_FRAMEWORK_HEALTH_ARGS_SCHEMA = z.object({
|
|
105
83
|
detailed: z.boolean().default(false),
|
|
106
84
|
});
|
|
107
|
-
export const INSTANTIATE_BLUEPRINT_ARGS_SCHEMA = z.object({
|
|
108
|
-
blueprintPath: z.string().min(1),
|
|
109
|
-
targetPath: z.string().min(1),
|
|
110
|
-
});
|
|
111
|
-
export const LIST_BLUEPRINTS_ARGS_SCHEMA = z.object({
|
|
112
|
-
category: z.enum(["backend", "frontend"]).optional(),
|
|
113
|
-
});
|
|
114
|
-
export const SAVE_AS_BLUEPRINT_ARGS_SCHEMA = z.object({
|
|
115
|
-
blueprintPath: z.string().min(1),
|
|
116
|
-
content: z.string().min(1),
|
|
117
|
-
description: z.string().optional(),
|
|
118
|
-
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import { execSync } from "child_process";
|
|
4
3
|
import { getFrameworkDir, collectMarkdownArtifacts, FRAMEWORK_VERSION } from "../utils.js";
|
|
5
4
|
export const frameworkTools = [
|
|
6
5
|
{
|
|
@@ -119,10 +118,9 @@ export const frameworkHandlers = {
|
|
|
119
118
|
missing.push(`[MISSING FOLDER] ${frameworkDir}/agents`);
|
|
120
119
|
}
|
|
121
120
|
else {
|
|
122
|
-
const agents = fs.readdirSync(agentsDir)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
});
|
|
121
|
+
const agents = fs.readdirSync(agentsDir)
|
|
122
|
+
.filter((f) => f.endsWith(".md") && f !== "README.md")
|
|
123
|
+
.map((f) => f.replace(".md", ""));
|
|
126
124
|
const logsDir = path.join(projectRoot, frameworkDir, "logs");
|
|
127
125
|
for (const agentName of agents) {
|
|
128
126
|
const logFile = path.join(logsDir, `${agentName}.json`);
|
|
@@ -182,15 +180,46 @@ export const frameworkHandlers = {
|
|
|
182
180
|
},
|
|
183
181
|
verify_framework_health: async (args, projectRoot) => {
|
|
184
182
|
try {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
183
|
+
const frameworkDir = getFrameworkDir(projectRoot);
|
|
184
|
+
const requiredFiles = [
|
|
185
|
+
["Constitution", fs.existsSync(path.join(projectRoot, "ENDERUN.md")) ? "ENDERUN.md" : path.join(frameworkDir, "ENDERUN.md")],
|
|
186
|
+
["Memory", path.join(frameworkDir, "PROJECT_MEMORY.md")],
|
|
187
|
+
["Command Map", path.join(frameworkDir, "cli-commands.json")],
|
|
188
|
+
["Framework Config", path.join(frameworkDir, "config.json")],
|
|
189
|
+
["Agent Status", path.join(frameworkDir, "STATUS.md")],
|
|
190
|
+
["MCP Config", "mcp.json"],
|
|
191
|
+
["Backend Contract", "apps/backend/contract.version.json"],
|
|
192
|
+
["MCP Server", "framework-mcp/package.json"],
|
|
193
|
+
["Panda CSS Config", "panda.config.ts"],
|
|
194
|
+
["Brain Dashboard", path.join(frameworkDir, "BRAIN_DASHBOARD.md")],
|
|
195
|
+
["Docs Portal", "docs/README.md"],
|
|
196
|
+
["Getting Started", "docs/getting-started.md"],
|
|
197
|
+
];
|
|
198
|
+
const lines = [];
|
|
199
|
+
let issues = 0;
|
|
200
|
+
for (const [name, relativePath] of requiredFiles) {
|
|
201
|
+
if (fs.existsSync(path.join(projectRoot, relativePath))) {
|
|
202
|
+
lines.push(`OK ${name}: ${relativePath}`);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
issues += 1;
|
|
206
|
+
lines.push(`MISSING ${name}: ${relativePath}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
const buildPath = path.join(projectRoot, "framework-mcp/dist/index.js");
|
|
210
|
+
if (fs.existsSync(buildPath)) {
|
|
211
|
+
lines.push("OK MCP Build: framework-mcp/dist/index.js");
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
issues += 1;
|
|
215
|
+
lines.push("MISSING MCP Build: framework-mcp/dist/index.js");
|
|
216
|
+
}
|
|
217
|
+
const status = issues === 0 ? "PASSED" : "FAILED";
|
|
218
|
+
return { content: [{ type: "text", text: `### FRAMEWORK HEALTH CHECK (${status})\n\n${lines.join("\n")}` }] };
|
|
190
219
|
}
|
|
191
220
|
catch (error) {
|
|
192
221
|
const err = error;
|
|
193
|
-
return { content: [{ type: "text", text: `### FRAMEWORK HEALTH CHECK (FAILED)\n\n${err.
|
|
222
|
+
return { content: [{ type: "text", text: `### FRAMEWORK HEALTH CHECK (FAILED)\n\n${err.message ?? "Unknown error"}` }] };
|
|
194
223
|
}
|
|
195
224
|
}
|
|
196
225
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Governance Tools - MCP Tools for enforcing corporate governance standards
|
|
3
3
|
*
|
|
4
|
-
* These tools bridge the .
|
|
4
|
+
* These tools bridge the .enderun/knowledge/corporate-governance/ knowledge base
|
|
5
5
|
* with actual code-level compliance verification.
|
|
6
6
|
*
|
|
7
7
|
* Philosophy: We do not just document standards — we actively verify them.
|
|
@@ -78,14 +78,31 @@ export const memoryHandlers = {
|
|
|
78
78
|
const lockPath = memoryPath + ".lock";
|
|
79
79
|
const lockOwner = `lock-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
80
80
|
try {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
81
|
+
try {
|
|
82
|
+
// Atomic write using 'wx' flag to prevent TOCTOU race conditions
|
|
83
|
+
fs.writeFileSync(lockPath, JSON.stringify({ owner: lockOwner, createdAt: new Date().toISOString() }), { flag: "wx" });
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
if (error.code === "EEXIST") {
|
|
87
|
+
const stats = fs.statSync(lockPath);
|
|
88
|
+
if (Date.now() - stats.mtimeMs > 120000) {
|
|
89
|
+
try {
|
|
90
|
+
fs.unlinkSync(lockPath);
|
|
91
|
+
// Retry atomic write once after unlinking stale lock
|
|
92
|
+
fs.writeFileSync(lockPath, JSON.stringify({ owner: lockOwner, createdAt: new Date().toISOString() }), { flag: "wx" });
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return { content: [{ type: "text", text: "Memory is locked. Try again later." }] };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
return { content: [{ type: "text", text: "Memory is locked. Try again later." }] };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
87
105
|
}
|
|
88
|
-
fs.writeFileSync(lockPath, JSON.stringify({ owner: lockOwner, createdAt: new Date().toISOString() }));
|
|
89
106
|
let memoryContent = fs.readFileSync(memoryPath, "utf-8");
|
|
90
107
|
if (section === "HISTORY") {
|
|
91
108
|
let updated = false;
|
|
@@ -93,7 +93,7 @@ export const monitoringHandlers = {
|
|
|
93
93
|
results.agents = [{ error: "STATUS.md not found or unreadable" }];
|
|
94
94
|
}
|
|
95
95
|
// Read queue depths
|
|
96
|
-
const queueBase = path.join(projectRoot,
|
|
96
|
+
const queueBase = path.join(projectRoot, getFrameworkDir(projectRoot), "queue");
|
|
97
97
|
for (const subdir of ["pending", "processing", "completed", "failed"]) {
|
|
98
98
|
try {
|
|
99
99
|
const files = await fs.readdir(path.join(queueBase, subdir));
|
|
@@ -104,7 +104,7 @@ export const monitoringHandlers = {
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
// Read orchestrator log for recent blockages and broadcasts
|
|
107
|
-
const logPath = path.join(projectRoot,
|
|
107
|
+
const logPath = path.join(projectRoot, getFrameworkDir(projectRoot), "logs", "orchestrator.json");
|
|
108
108
|
try {
|
|
109
109
|
const logContent = await fs.readFile(logPath, "utf-8");
|
|
110
110
|
const log = JSON.parse(logContent);
|
|
@@ -128,7 +128,7 @@ export const monitoringHandlers = {
|
|
|
128
128
|
const { agentName, lastN } = args;
|
|
129
129
|
const n = lastN ?? 20;
|
|
130
130
|
const logName = agentName.replace("@", "") + ".json";
|
|
131
|
-
const logPath = path.join(projectRoot,
|
|
131
|
+
const logPath = path.join(projectRoot, getFrameworkDir(projectRoot), "logs", logName);
|
|
132
132
|
try {
|
|
133
133
|
const content = await fs.readFile(logPath, "utf-8");
|
|
134
134
|
const log = JSON.parse(content);
|
|
@@ -172,7 +172,7 @@ export const monitoringHandlers = {
|
|
|
172
172
|
async detect_bottleneck_agents(args, projectRoot) {
|
|
173
173
|
const { thresholdMinutes } = args;
|
|
174
174
|
const threshold = thresholdMinutes ?? 30;
|
|
175
|
-
const statusPath = path.join(projectRoot,
|
|
175
|
+
const statusPath = path.join(projectRoot, getFrameworkDir(projectRoot), "STATUS.md");
|
|
176
176
|
const bottlenecks = [];
|
|
177
177
|
try {
|
|
178
178
|
const content = await fs.readFile(statusPath, "utf-8");
|
|
@@ -256,7 +256,7 @@ export const monitoringHandlers = {
|
|
|
256
256
|
report.agentStatuses = [];
|
|
257
257
|
}
|
|
258
258
|
// Queue snapshot
|
|
259
|
-
const queueBase = path.join(projectRoot,
|
|
259
|
+
const queueBase = path.join(projectRoot, getFrameworkDir(projectRoot), "queue");
|
|
260
260
|
const snapshot = {};
|
|
261
261
|
for (const dir of ["pending", "processing", "completed", "failed"]) {
|
|
262
262
|
try {
|
|
@@ -269,7 +269,7 @@ export const monitoringHandlers = {
|
|
|
269
269
|
}
|
|
270
270
|
report.queueSnapshot = snapshot;
|
|
271
271
|
// Recent blockages
|
|
272
|
-
const logPath = path.join(projectRoot,
|
|
272
|
+
const logPath = path.join(projectRoot, getFrameworkDir(projectRoot), "logs", "orchestrator.json");
|
|
273
273
|
try {
|
|
274
274
|
const logContent = await fs.readFile(logPath, "utf-8");
|
|
275
275
|
const log = JSON.parse(logContent);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { promises as fs } from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
+
import { getFrameworkDir } from "../utils.js";
|
|
3
4
|
// ─── Tool Definitions ─────────────────────────────────────────────────────────
|
|
4
5
|
export const orchestrationTools = [
|
|
5
6
|
{
|
|
@@ -178,7 +179,7 @@ export const orchestrationHandlers = {
|
|
|
178
179
|
parallelOpportunities: executionWaves.filter((w) => w.length > 1).length,
|
|
179
180
|
};
|
|
180
181
|
// Persist to queue
|
|
181
|
-
const queueDir = path.join(projectRoot,
|
|
182
|
+
const queueDir = path.join(projectRoot, getFrameworkDir(projectRoot), "queue", "pending");
|
|
182
183
|
await fs.mkdir(queueDir, { recursive: true });
|
|
183
184
|
await fs.writeFile(path.join(queueDir, `${traceId}-dag.json`), JSON.stringify(dag, null, 2), "utf-8");
|
|
184
185
|
return { content: [{ type: "text", text: JSON.stringify(dag, null, 2) }] };
|
|
@@ -188,7 +189,7 @@ export const orchestrationHandlers = {
|
|
|
188
189
|
// Try to load capability roster from shared-facts.json (dynamic — fixes KRITIK-3)
|
|
189
190
|
let agentCapabilityRoster = [];
|
|
190
191
|
try {
|
|
191
|
-
const sharedFactsPath = path.join(projectRoot,
|
|
192
|
+
const sharedFactsPath = path.join(projectRoot, getFrameworkDir(projectRoot), "memory-graph", "shared-facts.json");
|
|
192
193
|
const content = await fs.readFile(sharedFactsPath, "utf-8");
|
|
193
194
|
const facts = JSON.parse(content);
|
|
194
195
|
agentCapabilityRoster = facts.agentCapabilityRoster ?? [];
|
|
@@ -249,7 +250,7 @@ export const orchestrationHandlers = {
|
|
|
249
250
|
sourceMap: Object.keys(domainMap).length > 0 ? "shared-facts" : "fallback",
|
|
250
251
|
};
|
|
251
252
|
// Write assignment log
|
|
252
|
-
const logPath = path.join(projectRoot,
|
|
253
|
+
const logPath = path.join(projectRoot, getFrameworkDir(projectRoot), "logs", "orchestrator.json");
|
|
253
254
|
let log = { assignments: [] };
|
|
254
255
|
try {
|
|
255
256
|
const existing = await fs.readFile(logPath, "utf-8");
|
|
@@ -264,7 +265,7 @@ export const orchestrationHandlers = {
|
|
|
264
265
|
return { content: [{ type: "text", text: JSON.stringify(assignment, null, 2) }] };
|
|
265
266
|
},
|
|
266
267
|
async get_agent_workload(_args, projectRoot) {
|
|
267
|
-
const statusPath = path.join(projectRoot,
|
|
268
|
+
const statusPath = path.join(projectRoot, getFrameworkDir(projectRoot), "STATUS.md");
|
|
268
269
|
let statusContent;
|
|
269
270
|
try {
|
|
270
271
|
statusContent = await fs.readFile(statusPath, "utf-8");
|
|
@@ -301,7 +302,7 @@ export const orchestrationHandlers = {
|
|
|
301
302
|
status: "ESCALATED_TO_MANAGER",
|
|
302
303
|
};
|
|
303
304
|
// Log blockage
|
|
304
|
-
const logPath = path.join(projectRoot,
|
|
305
|
+
const logPath = path.join(projectRoot, getFrameworkDir(projectRoot), "logs", "orchestrator.json");
|
|
305
306
|
let log = { assignments: [], blockages: [] };
|
|
306
307
|
try {
|
|
307
308
|
const existing = await fs.readFile(logPath, "utf-8");
|
|
@@ -316,7 +317,7 @@ export const orchestrationHandlers = {
|
|
|
316
317
|
await fs.mkdir(path.dirname(logPath), { recursive: true });
|
|
317
318
|
await fs.writeFile(logPath, JSON.stringify(log, null, 2), "utf-8");
|
|
318
319
|
// Write a message to @manager's inbox via messages folder
|
|
319
|
-
const messagesDir = path.join(projectRoot,
|
|
320
|
+
const messagesDir = path.join(projectRoot, getFrameworkDir(projectRoot), "messages");
|
|
320
321
|
await fs.mkdir(messagesDir, { recursive: true });
|
|
321
322
|
const messageFile = path.join(messagesDir, `BLOCKAGE-${traceId}-${Date.now()}.json`);
|
|
322
323
|
await fs.writeFile(messageFile, JSON.stringify({
|
|
@@ -342,11 +343,11 @@ export const orchestrationHandlers = {
|
|
|
342
343
|
timestamp: new Date().toISOString(),
|
|
343
344
|
};
|
|
344
345
|
// Write to messages folder for all agents
|
|
345
|
-
const messagesDir = path.join(projectRoot,
|
|
346
|
+
const messagesDir = path.join(projectRoot, getFrameworkDir(projectRoot), "messages");
|
|
346
347
|
await fs.mkdir(messagesDir, { recursive: true });
|
|
347
348
|
await fs.writeFile(path.join(messagesDir, `${broadcast.id}.json`), JSON.stringify(broadcast, null, 2), "utf-8");
|
|
348
349
|
// Log broadcast
|
|
349
|
-
const logPath = path.join(projectRoot,
|
|
350
|
+
const logPath = path.join(projectRoot, getFrameworkDir(projectRoot), "logs", "orchestrator.json");
|
|
350
351
|
let log = {
|
|
351
352
|
assignments: [],
|
|
352
353
|
blockages: [],
|
|
@@ -8,13 +8,13 @@ const getPackageVersion = () => {
|
|
|
8
8
|
const pkgPath = path.join(__dirname, "../package.json");
|
|
9
9
|
if (fs.existsSync(pkgPath)) {
|
|
10
10
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
11
|
-
return pkg.version || "0.
|
|
11
|
+
return pkg.version || "0.8.8";
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
catch {
|
|
15
15
|
// fallback
|
|
16
16
|
}
|
|
17
|
-
return "0.
|
|
17
|
+
return "0.8.8";
|
|
18
18
|
};
|
|
19
19
|
export const FRAMEWORK_VERSION = getPackageVersion();
|
|
20
20
|
/**
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import { execSync } from "child_process";
|
|
4
3
|
import {
|
|
5
4
|
getFrameworkDir,
|
|
6
5
|
collectMarkdownArtifacts,
|
|
@@ -179,14 +178,46 @@ export const frameworkHandlers = {
|
|
|
179
178
|
},
|
|
180
179
|
verify_framework_health: async (args: unknown, projectRoot: string) => {
|
|
181
180
|
try {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
181
|
+
const frameworkDir = getFrameworkDir(projectRoot);
|
|
182
|
+
const requiredFiles = [
|
|
183
|
+
["Constitution", fs.existsSync(path.join(projectRoot, "ENDERUN.md")) ? "ENDERUN.md" : path.join(frameworkDir, "ENDERUN.md")],
|
|
184
|
+
["Memory", path.join(frameworkDir, "PROJECT_MEMORY.md")],
|
|
185
|
+
["Command Map", path.join(frameworkDir, "cli-commands.json")],
|
|
186
|
+
["Framework Config", path.join(frameworkDir, "config.json")],
|
|
187
|
+
["Agent Status", path.join(frameworkDir, "STATUS.md")],
|
|
188
|
+
["MCP Config", "mcp.json"],
|
|
189
|
+
["Backend Contract", "apps/backend/contract.version.json"],
|
|
190
|
+
["MCP Server", "framework-mcp/package.json"],
|
|
191
|
+
["Panda CSS Config", "panda.config.ts"],
|
|
192
|
+
["Brain Dashboard", path.join(frameworkDir, "BRAIN_DASHBOARD.md")],
|
|
193
|
+
["Docs Portal", "docs/README.md"],
|
|
194
|
+
["Getting Started", "docs/getting-started.md"],
|
|
195
|
+
] as const;
|
|
196
|
+
|
|
197
|
+
const lines: string[] = [];
|
|
198
|
+
let issues = 0;
|
|
199
|
+
for (const [name, relativePath] of requiredFiles) {
|
|
200
|
+
if (fs.existsSync(path.join(projectRoot, relativePath))) {
|
|
201
|
+
lines.push(`OK ${name}: ${relativePath}`);
|
|
202
|
+
} else {
|
|
203
|
+
issues += 1;
|
|
204
|
+
lines.push(`MISSING ${name}: ${relativePath}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const buildPath = path.join(projectRoot, "framework-mcp/dist/index.js");
|
|
209
|
+
if (fs.existsSync(buildPath)) {
|
|
210
|
+
lines.push("OK MCP Build: framework-mcp/dist/index.js");
|
|
211
|
+
} else {
|
|
212
|
+
issues += 1;
|
|
213
|
+
lines.push("MISSING MCP Build: framework-mcp/dist/index.js");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const status = issues === 0 ? "PASSED" : "FAILED";
|
|
217
|
+
return { content: [{ type: "text", text: `### FRAMEWORK HEALTH CHECK (${status})\n\n${lines.join("\n")}` }] };
|
|
187
218
|
} catch (error: unknown) {
|
|
188
|
-
const err = error as {
|
|
189
|
-
return { content: [{ type: "text", text: `### FRAMEWORK HEALTH CHECK (FAILED)\n\n${err.
|
|
219
|
+
const err = error as { message?: string };
|
|
220
|
+
return { content: [{ type: "text", text: `### FRAMEWORK HEALTH CHECK (FAILED)\n\n${err.message ?? "Unknown error"}` }] };
|
|
190
221
|
}
|
|
191
222
|
}
|
|
192
223
|
};
|
|
@@ -83,12 +83,27 @@ export const memoryHandlers = {
|
|
|
83
83
|
const lockPath = memoryPath + ".lock";
|
|
84
84
|
const lockOwner = `lock-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
85
85
|
try {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
try {
|
|
87
|
+
// Atomic write using 'wx' flag to prevent TOCTOU race conditions
|
|
88
|
+
fs.writeFileSync(lockPath, JSON.stringify({ owner: lockOwner, createdAt: new Date().toISOString() }), { flag: "wx" });
|
|
89
|
+
} catch (error: any) {
|
|
90
|
+
if (error.code === "EEXIST") {
|
|
91
|
+
const stats = fs.statSync(lockPath);
|
|
92
|
+
if (Date.now() - stats.mtimeMs > 120000) {
|
|
93
|
+
try {
|
|
94
|
+
fs.unlinkSync(lockPath);
|
|
95
|
+
// Retry atomic write once after unlinking stale lock
|
|
96
|
+
fs.writeFileSync(lockPath, JSON.stringify({ owner: lockOwner, createdAt: new Date().toISOString() }), { flag: "wx" });
|
|
97
|
+
} catch {
|
|
98
|
+
return { content: [{ type: "text", text: "Memory is locked. Try again later." }] };
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
return { content: [{ type: "text", text: "Memory is locked. Try again later." }] };
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
90
106
|
}
|
|
91
|
-
fs.writeFileSync(lockPath, JSON.stringify({ owner: lockOwner, createdAt: new Date().toISOString() }));
|
|
92
107
|
let memoryContent = fs.readFileSync(memoryPath, "utf-8");
|
|
93
108
|
if (section === "HISTORY") {
|
|
94
109
|
let updated = false;
|
|
@@ -113,7 +113,7 @@ export const monitoringHandlers: Record<
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
// Read queue depths
|
|
116
|
-
const queueBase = path.join(projectRoot,
|
|
116
|
+
const queueBase = path.join(projectRoot, getFrameworkDir(projectRoot), "queue");
|
|
117
117
|
for (const subdir of ["pending", "processing", "completed", "failed"] as const) {
|
|
118
118
|
try {
|
|
119
119
|
const files = await fs.readdir(path.join(queueBase, subdir));
|
|
@@ -124,7 +124,7 @@ export const monitoringHandlers: Record<
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
// Read orchestrator log for recent blockages and broadcasts
|
|
127
|
-
const logPath = path.join(projectRoot,
|
|
127
|
+
const logPath = path.join(projectRoot, getFrameworkDir(projectRoot), "logs", "orchestrator.json");
|
|
128
128
|
try {
|
|
129
129
|
const logContent = await fs.readFile(logPath, "utf-8");
|
|
130
130
|
const log = JSON.parse(logContent) as {
|
|
@@ -152,7 +152,7 @@ export const monitoringHandlers: Record<
|
|
|
152
152
|
const { agentName, lastN } = args as { agentName: string; lastN?: number };
|
|
153
153
|
const n = lastN ?? 20;
|
|
154
154
|
const logName = agentName.replace("@", "") + ".json";
|
|
155
|
-
const logPath = path.join(projectRoot,
|
|
155
|
+
const logPath = path.join(projectRoot, getFrameworkDir(projectRoot), "logs", logName);
|
|
156
156
|
|
|
157
157
|
try {
|
|
158
158
|
const content = await fs.readFile(logPath, "utf-8");
|
|
@@ -206,7 +206,7 @@ export const monitoringHandlers: Record<
|
|
|
206
206
|
const { thresholdMinutes } = args as { thresholdMinutes?: number };
|
|
207
207
|
const threshold = thresholdMinutes ?? 30;
|
|
208
208
|
|
|
209
|
-
const statusPath = path.join(projectRoot,
|
|
209
|
+
const statusPath = path.join(projectRoot, getFrameworkDir(projectRoot), "STATUS.md");
|
|
210
210
|
const bottlenecks: Array<{ agent: string; reason: string; state: string; severity: string }> = [];
|
|
211
211
|
|
|
212
212
|
try {
|
|
@@ -308,7 +308,7 @@ export const monitoringHandlers: Record<
|
|
|
308
308
|
}
|
|
309
309
|
|
|
310
310
|
// Queue snapshot
|
|
311
|
-
const queueBase = path.join(projectRoot,
|
|
311
|
+
const queueBase = path.join(projectRoot, getFrameworkDir(projectRoot), "queue");
|
|
312
312
|
const snapshot: Record<string, number> = {};
|
|
313
313
|
for (const dir of ["pending", "processing", "completed", "failed"]) {
|
|
314
314
|
try {
|
|
@@ -321,7 +321,7 @@ export const monitoringHandlers: Record<
|
|
|
321
321
|
report.queueSnapshot = snapshot;
|
|
322
322
|
|
|
323
323
|
// Recent blockages
|
|
324
|
-
const logPath = path.join(projectRoot,
|
|
324
|
+
const logPath = path.join(projectRoot, getFrameworkDir(projectRoot), "logs", "orchestrator.json");
|
|
325
325
|
try {
|
|
326
326
|
const logContent = await fs.readFile(logPath, "utf-8");
|
|
327
327
|
const log = JSON.parse(logContent) as { blockages?: unknown[] };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
2
2
|
import { promises as fs } from "fs";
|
|
3
3
|
import path from "path";
|
|
4
|
+
import { getFrameworkDir } from "../utils.js";
|
|
4
5
|
|
|
5
6
|
// ─── Tool Definitions ─────────────────────────────────────────────────────────
|
|
6
7
|
|
|
@@ -208,7 +209,7 @@ export const orchestrationHandlers: Record<
|
|
|
208
209
|
};
|
|
209
210
|
|
|
210
211
|
// Persist to queue
|
|
211
|
-
const queueDir = path.join(projectRoot,
|
|
212
|
+
const queueDir = path.join(projectRoot, getFrameworkDir(projectRoot), "queue", "pending");
|
|
212
213
|
await fs.mkdir(queueDir, { recursive: true });
|
|
213
214
|
await fs.writeFile(
|
|
214
215
|
path.join(queueDir, `${traceId}-dag.json`),
|
|
@@ -230,7 +231,7 @@ export const orchestrationHandlers: Record<
|
|
|
230
231
|
// Try to load capability roster from shared-facts.json (dynamic — fixes KRITIK-3)
|
|
231
232
|
let agentCapabilityRoster: Array<{ agent: string; primaryDomain: string; capability: number }> = [];
|
|
232
233
|
try {
|
|
233
|
-
const sharedFactsPath = path.join(projectRoot,
|
|
234
|
+
const sharedFactsPath = path.join(projectRoot, getFrameworkDir(projectRoot), "memory-graph", "shared-facts.json");
|
|
234
235
|
const content = await fs.readFile(sharedFactsPath, "utf-8");
|
|
235
236
|
const facts = JSON.parse(content) as { agentCapabilityRoster?: typeof agentCapabilityRoster };
|
|
236
237
|
agentCapabilityRoster = facts.agentCapabilityRoster ?? [];
|
|
@@ -294,7 +295,7 @@ export const orchestrationHandlers: Record<
|
|
|
294
295
|
};
|
|
295
296
|
|
|
296
297
|
// Write assignment log
|
|
297
|
-
const logPath = path.join(projectRoot,
|
|
298
|
+
const logPath = path.join(projectRoot, getFrameworkDir(projectRoot), "logs", "orchestrator.json");
|
|
298
299
|
let log: { assignments: unknown[] } = { assignments: [] };
|
|
299
300
|
try {
|
|
300
301
|
const existing = await fs.readFile(logPath, "utf-8");
|
|
@@ -310,7 +311,7 @@ export const orchestrationHandlers: Record<
|
|
|
310
311
|
},
|
|
311
312
|
|
|
312
313
|
async get_agent_workload(_args, projectRoot) {
|
|
313
|
-
const statusPath = path.join(projectRoot,
|
|
314
|
+
const statusPath = path.join(projectRoot, getFrameworkDir(projectRoot), "STATUS.md");
|
|
314
315
|
let statusContent: string;
|
|
315
316
|
try {
|
|
316
317
|
statusContent = await fs.readFile(statusPath, "utf-8");
|
|
@@ -356,7 +357,7 @@ export const orchestrationHandlers: Record<
|
|
|
356
357
|
};
|
|
357
358
|
|
|
358
359
|
// Log blockage
|
|
359
|
-
const logPath = path.join(projectRoot,
|
|
360
|
+
const logPath = path.join(projectRoot, getFrameworkDir(projectRoot), "logs", "orchestrator.json");
|
|
360
361
|
let log: { assignments: unknown[]; blockages: unknown[] } = { assignments: [], blockages: [] };
|
|
361
362
|
try {
|
|
362
363
|
const existing = await fs.readFile(logPath, "utf-8");
|
|
@@ -370,7 +371,7 @@ export const orchestrationHandlers: Record<
|
|
|
370
371
|
await fs.writeFile(logPath, JSON.stringify(log, null, 2), "utf-8");
|
|
371
372
|
|
|
372
373
|
// Write a message to @manager's inbox via messages folder
|
|
373
|
-
const messagesDir = path.join(projectRoot,
|
|
374
|
+
const messagesDir = path.join(projectRoot, getFrameworkDir(projectRoot), "messages");
|
|
374
375
|
await fs.mkdir(messagesDir, { recursive: true });
|
|
375
376
|
const messageFile = path.join(messagesDir, `BLOCKAGE-${traceId}-${Date.now()}.json`);
|
|
376
377
|
await fs.writeFile(
|
|
@@ -409,7 +410,7 @@ export const orchestrationHandlers: Record<
|
|
|
409
410
|
};
|
|
410
411
|
|
|
411
412
|
// Write to messages folder for all agents
|
|
412
|
-
const messagesDir = path.join(projectRoot,
|
|
413
|
+
const messagesDir = path.join(projectRoot, getFrameworkDir(projectRoot), "messages");
|
|
413
414
|
await fs.mkdir(messagesDir, { recursive: true });
|
|
414
415
|
await fs.writeFile(
|
|
415
416
|
path.join(messagesDir, `${broadcast.id}.json`),
|
|
@@ -418,7 +419,7 @@ export const orchestrationHandlers: Record<
|
|
|
418
419
|
);
|
|
419
420
|
|
|
420
421
|
// Log broadcast
|
|
421
|
-
const logPath = path.join(projectRoot,
|
|
422
|
+
const logPath = path.join(projectRoot, getFrameworkDir(projectRoot), "logs", "orchestrator.json");
|
|
422
423
|
let log: { assignments: unknown[]; blockages: unknown[]; broadcasts: unknown[] } = {
|
|
423
424
|
assignments: [],
|
|
424
425
|
blockages: [],
|
|
@@ -10,12 +10,12 @@ const getPackageVersion = (): string => {
|
|
|
10
10
|
const pkgPath = path.join(__dirname, "../package.json");
|
|
11
11
|
if (fs.existsSync(pkgPath)) {
|
|
12
12
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
13
|
-
return pkg.version || "0.8.
|
|
13
|
+
return pkg.version || "0.8.8";
|
|
14
14
|
}
|
|
15
15
|
} catch {
|
|
16
16
|
// fallback
|
|
17
17
|
}
|
|
18
|
-
return "0.8.
|
|
18
|
+
return "0.8.8";
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
export const FRAMEWORK_VERSION = getPackageVersion();
|
package/mcp.json
CHANGED