agent-enderun 0.6.1 → 0.6.6
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 +12 -12
- package/.enderun/PROJECT_MEMORY.md +17 -17
- package/.enderun/STATUS.md +11 -11
- package/.enderun/agents/README.md +40 -0
- package/.enderun/agents/analyst.md +238 -31
- package/.enderun/agents/backend.md +428 -18
- package/.enderun/agents/explorer.md +169 -31
- package/.enderun/agents/frontend.md +604 -76
- package/.enderun/agents/git.md +153 -62
- package/.enderun/agents/manager.md +433 -46
- package/.enderun/agents/mobile.md +143 -29
- package/.enderun/agents/native.md +191 -28
- package/.enderun/blueprints/README.md +82 -0
- package/.enderun/blueprints/backend/errors/config/meta.json +11 -0
- package/.enderun/blueprints/backend/errors/domain-error.ts +34 -55
- package/.enderun/docs/README.md +50 -0
- package/.enderun/docs/phase1-reference-app-execution-plan.md +177 -0
- package/.enderun/docs/structure-audit-2026-05-24.md +72 -0
- package/.enderun/knowledge/README.md +22 -0
- package/.enderun/knowledge/database_governance_guidelines.md +118 -0
- package/.enderun/knowledge/deployment_checklist.md +132 -6
- package/.enderun/knowledge/documentation_ownership.md +122 -0
- package/.enderun/knowledge/documentation_ownership_status.md +122 -0
- package/.enderun/knowledge/enterprise_capabilities_reference.md +149 -0
- package/.enderun/knowledge/enterprise_frontend_adaptation.md +232 -0
- package/.enderun/knowledge/enterprise_project_adaptation.md +168 -0
- package/.enderun/knowledge/framework_vs_user_project_boundary.md +52 -0
- package/.enderun/knowledge/frontend_professionalization_guidelines.md +111 -0
- package/.enderun/knowledge/frontend_real_battle_test_protocol.md +162 -0
- package/.enderun/knowledge/hermes_live_test_guidelines.md +90 -0
- package/.enderun/knowledge/manager_authority_audit_enforcement.md +104 -0
- package/.enderun/knowledge/project_scaffold_guidelines.md +99 -0
- package/.enderun/knowledge/reference_application_guidelines.md +90 -0
- package/ENDERUN.md +19 -5
- package/README.md +132 -657
- package/antigravity.md +15 -0
- package/bin/cli.js +92 -27
- package/bin/hermes-sandbox.js +136 -0
- package/docs/README.md +33 -3
- package/docs/action-plan-2026.md +119 -0
- package/docs/getting-started.md +271 -0
- package/docs/roadmap.md +142 -0
- package/framework-mcp/README.md +1 -1
- package/framework-mcp/dist/index.js +0 -0
- package/framework-mcp/dist/schemas.js +6 -0
- package/framework-mcp/dist/tools/academy.js +6 -8
- package/framework-mcp/dist/tools/codebase.js +6 -6
- package/framework-mcp/dist/tools/contract.js +94 -4
- package/framework-mcp/dist/tools/database.js +8 -3
- package/framework-mcp/dist/tools/framework.js +7 -11
- package/framework-mcp/dist/tools/git.js +2 -2
- package/framework-mcp/dist/tools/knowledge.js +5 -5
- package/framework-mcp/dist/tools/memory.js +5 -7
- package/framework-mcp/dist/tools/messages.js +76 -16
- package/framework-mcp/dist/tools/repository.js +4 -4
- package/framework-mcp/dist/tools/scaffold.js +6 -3
- package/framework-mcp/dist/tools/security.js +4 -4
- package/framework-mcp/dist/utils.js +2 -2
- package/framework-mcp/package.json +1 -1
- package/framework-mcp/src/schemas.ts +7 -0
- package/framework-mcp/src/tools/academy.ts +5 -9
- package/framework-mcp/src/tools/codebase.ts +6 -6
- package/framework-mcp/src/tools/contract.ts +114 -4
- package/framework-mcp/src/tools/database.ts +8 -3
- package/framework-mcp/src/tools/framework.ts +8 -11
- package/framework-mcp/src/tools/git.ts +2 -2
- package/framework-mcp/src/tools/knowledge.ts +6 -7
- package/framework-mcp/src/tools/memory.ts +5 -6
- package/framework-mcp/src/tools/messages.ts +94 -28
- package/framework-mcp/src/tools/repository.ts +5 -6
- package/framework-mcp/src/tools/scaffold.ts +9 -6
- package/framework-mcp/src/tools/security.ts +4 -4
- package/framework-mcp/src/utils.ts +19 -2
- package/framework-mcp/tsconfig.json +1 -1
- package/gemini.md +4 -4
- package/package.json +11 -7
- package/panda.config.ts +3 -1
- package/.enderun/benchmarks/.gitkeep +0 -0
- package/.enderun/blueprints/backend/middleware/error-handler.ts +0 -24
- package/.enderun/blueprints/backend/types/api.ts +0 -20
- package/.enderun/blueprints/backend/types/brands.ts +0 -12
- package/.enderun/blueprints/backend/types/constants.ts +0 -34
- package/.enderun/blueprints/backend/types/index.ts +0 -49
- package/.enderun/blueprints/backend/types/logs.ts +0 -16
- package/.enderun/blueprints/backend/types/models.ts +0 -65
- package/.enderun/blueprints/frontend/ui/Button.tsx +0 -60
- package/.enderun/blueprints/frontend/ui/Input.tsx +0 -43
- package/.enderun/monitoring/.gitkeep +0 -0
- package/gemini-extension.json +0 -13
|
@@ -29,13 +29,18 @@ export const databaseHandlers = {
|
|
|
29
29
|
for (const sourceFile of tsProject.getSourceFiles()) {
|
|
30
30
|
const text = sourceFile.getFullText();
|
|
31
31
|
const tableMatches = Array.from(text.matchAll(/\.createTable\(['"]([^'"]+)['"]\)/g));
|
|
32
|
-
for (
|
|
32
|
+
for (let i = 0; i < tableMatches.length; i++) {
|
|
33
|
+
const match = tableMatches[i];
|
|
33
34
|
const tableName = match[1];
|
|
34
35
|
tablesFound++;
|
|
35
36
|
mermaid += ` ${tableName} {\n`;
|
|
37
|
+
const startIndex = match.index ?? 0;
|
|
38
|
+
const nextMatch = tableMatches[i + 1];
|
|
39
|
+
const endIndex = nextMatch ? (nextMatch.index ?? text.length) : text.length;
|
|
40
|
+
const tableBlockText = text.substring(startIndex, endIndex);
|
|
36
41
|
const colRegex = /\.addColumn\(['"]([^'"]+)['"]\s*,\s*['"]([^'"]+)['"]/g;
|
|
37
42
|
let colMatch;
|
|
38
|
-
while ((colMatch = colRegex.exec(
|
|
43
|
+
while ((colMatch = colRegex.exec(tableBlockText)) !== null) {
|
|
39
44
|
mermaid += ` ${colMatch[2].replace(/[^a-zA-Z0-9]/g, '_')} ${colMatch[1]}\n`;
|
|
40
45
|
}
|
|
41
46
|
mermaid += ` }\n`;
|
|
@@ -45,7 +50,7 @@ export const databaseHandlers = {
|
|
|
45
50
|
return { content: [{ type: "text", text: "No database tables detected." }] };
|
|
46
51
|
return { content: [{ type: "text", text: `### DATABASE SCHEMA MAP\n\n\`\`\`mermaid\n${mermaid}\n\`\`\`\n\n**Total Tables Detected:** ${tablesFound}` }] };
|
|
47
52
|
}
|
|
48
|
-
catch
|
|
53
|
+
catch {
|
|
49
54
|
return { content: [{ type: "text", text: "Database schema analysis failed." }] };
|
|
50
55
|
}
|
|
51
56
|
},
|
|
@@ -2,7 +2,6 @@ import fs from "fs";
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { execSync } from "child_process";
|
|
4
4
|
import { getFrameworkDir, collectMarkdownArtifacts, FRAMEWORK_VERSION } from "../utils.js";
|
|
5
|
-
import { VERIFY_FRAMEWORK_HEALTH_ARGS_SCHEMA } from "../schemas.js";
|
|
6
5
|
export const frameworkTools = [
|
|
7
6
|
{
|
|
8
7
|
name: "get_framework_status",
|
|
@@ -70,8 +69,6 @@ export const frameworkHandlers = {
|
|
|
70
69
|
if (detected.length > 0)
|
|
71
70
|
techStack = detected.join(", ");
|
|
72
71
|
}
|
|
73
|
-
const rootDirs = fs.readdirSync(projectRoot).filter(f => fs.lstatSync(path.join(projectRoot, f)).isDirectory() && !f.startsWith("."));
|
|
74
|
-
const isMonorepo = rootDirs.includes("apps") || rootDirs.includes("packages");
|
|
75
72
|
const date = new Date().toISOString().split("T")[0];
|
|
76
73
|
const bootstrapContent = `# PROJECT MEMORY — Legacy Onboarding\n\n## CURRENT STATUS\n\n| Active Phase | Profile | Last Update | Active Trace ID | Blockers |\n| PHASE_1 | Full | ${date} | — | Legacy Onboarding |`;
|
|
77
74
|
if (fs.existsSync(memoryPath))
|
|
@@ -79,7 +76,7 @@ export const frameworkHandlers = {
|
|
|
79
76
|
fs.writeFileSync(memoryPath, bootstrapContent);
|
|
80
77
|
return { content: [{ type: "text", text: `Legacy Codebase Bootstrap complete. Detected Stack: ${techStack}.` }] };
|
|
81
78
|
}
|
|
82
|
-
catch
|
|
79
|
+
catch {
|
|
83
80
|
return { content: [{ type: "text", text: "Failed to bootstrap legacy memory." }] };
|
|
84
81
|
}
|
|
85
82
|
},
|
|
@@ -95,8 +92,8 @@ export const frameworkHandlers = {
|
|
|
95
92
|
content: [{ type: "text", text: `Framework active (v${FRAMEWORK_VERSION}). Phase: ${phase}. Profile: ${profile}.` }],
|
|
96
93
|
};
|
|
97
94
|
}
|
|
98
|
-
catch
|
|
99
|
-
return { content: [{ type: "text", text: "
|
|
95
|
+
catch {
|
|
96
|
+
return { content: [{ type: "text", text: "Failed to get framework status." }] };
|
|
100
97
|
}
|
|
101
98
|
},
|
|
102
99
|
get_project_gaps: async (args, projectRoot) => {
|
|
@@ -150,8 +147,8 @@ export const frameworkHandlers = {
|
|
|
150
147
|
content: [{ type: "text", text: `### LIVE MEMORY INSIGHTS\n\n**Active Tasks:**\n${activeTasks.trim()}\n\n**Recent History:**\n${history.trim().substring(0, 1000)}...\n\n**Brain Snapshot:**\n${dashboardAgents.trim().substring(0, 500)}...` }],
|
|
151
148
|
};
|
|
152
149
|
}
|
|
153
|
-
catch
|
|
154
|
-
return { content: [{ type: "text", text: "Failed to
|
|
150
|
+
catch {
|
|
151
|
+
return { content: [{ type: "text", text: "Failed to get memory insights." }] };
|
|
155
152
|
}
|
|
156
153
|
},
|
|
157
154
|
codebase_context: async (args, projectRoot) => {
|
|
@@ -164,15 +161,14 @@ export const frameworkHandlers = {
|
|
|
164
161
|
content: [{ type: "text", text: "### CONTEXT ARTIFACTS\n\n" + `PROJECT_MEMORY: ${fs.existsSync(memoryPath) ? "present" : "missing"}\n` + `BRAIN_DASHBOARD: ${fs.existsSync(dashboardPath) ? "present" : "missing"}\n` + `Docs:\n${artifacts.length > 0 ? artifacts.join("\n") : "No markdown artifacts found."}` }],
|
|
165
162
|
};
|
|
166
163
|
}
|
|
167
|
-
catch
|
|
168
|
-
return { content: [{ type: "text", text: "
|
|
164
|
+
catch {
|
|
165
|
+
return { content: [{ type: "text", text: "Failed to gather codebase context." }] };
|
|
169
166
|
}
|
|
170
167
|
},
|
|
171
168
|
get_system_time: async () => {
|
|
172
169
|
return { content: [{ type: "text", text: new Date().toISOString() }] };
|
|
173
170
|
},
|
|
174
171
|
verify_framework_health: async (args, projectRoot) => {
|
|
175
|
-
const parsed = VERIFY_FRAMEWORK_HEALTH_ARGS_SCHEMA.safeParse(args ?? {});
|
|
176
172
|
try {
|
|
177
173
|
// Run the internal CLI check command via tsx to ensure we test current state
|
|
178
174
|
// If the CLI is not yet linked, we might need a relative path
|
|
@@ -44,8 +44,8 @@ export const gitHandlers = {
|
|
|
44
44
|
const summary = files.length === 1 ? `update ${path.basename(files[0])}` : `update ${files.length} files`;
|
|
45
45
|
return { content: [{ type: "text", text: `### SUGGESTED SEMANTIC COMMIT MESSAGE\n\n\`[${parsed.data.traceId}] ${type}(${scope}): ${summary}\`\n\n**Files Analyzed:**\n${files.map(f => `- ${f}`).join("\n")}` }] };
|
|
46
46
|
}
|
|
47
|
-
catch
|
|
48
|
-
return { content: [{ type: "text", text: "Failed to generate commit message." }] };
|
|
47
|
+
catch {
|
|
48
|
+
return { content: [{ type: "text", text: "Failed to generate semantic commit message." }] };
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
51
|
};
|
|
@@ -76,7 +76,7 @@ export const knowledgeHandlers = {
|
|
|
76
76
|
}).filter(Boolean);
|
|
77
77
|
return { content: [{ type: "text", text: results.length > 0 ? `### KNOWLEDGE BASE SEARCH RESULTS\n\n${results.join("\n\n---\n\n")}` : "No matching knowledge entries found." }] };
|
|
78
78
|
}
|
|
79
|
-
catch
|
|
79
|
+
catch {
|
|
80
80
|
return { content: [{ type: "text", text: "Knowledge base search failed." }] };
|
|
81
81
|
}
|
|
82
82
|
},
|
|
@@ -90,7 +90,7 @@ export const knowledgeHandlers = {
|
|
|
90
90
|
if (!fs.existsSync(kbDir))
|
|
91
91
|
fs.mkdirSync(kbDir, { recursive: true });
|
|
92
92
|
const fileName = parsed.data.topic.replace(/[^a-z0-9]/gi, "_").toLowerCase() + ".md";
|
|
93
|
-
const tags = parsed.data.tags;
|
|
93
|
+
const tags = parsed.data.tags || [];
|
|
94
94
|
let finalContent = parsed.data.content;
|
|
95
95
|
if (!finalContent.startsWith("---")) {
|
|
96
96
|
const frontmatter = `---\ntitle: ${parsed.data.topic}\ntags: [${tags.join(", ")}]\nlast_updated: ${new Date().toISOString()}\n---\n\n`;
|
|
@@ -99,7 +99,7 @@ export const knowledgeHandlers = {
|
|
|
99
99
|
fs.writeFileSync(path.join(kbDir, fileName), finalContent);
|
|
100
100
|
return { content: [{ type: "text", text: `Obsidian Wiki updated: ${parsed.data.topic}` }] };
|
|
101
101
|
}
|
|
102
|
-
catch
|
|
102
|
+
catch {
|
|
103
103
|
return { content: [{ type: "text", text: "Failed to update knowledge base." }] };
|
|
104
104
|
}
|
|
105
105
|
},
|
|
@@ -126,8 +126,8 @@ export const knowledgeHandlers = {
|
|
|
126
126
|
});
|
|
127
127
|
return { content: [{ type: "text", text: `### KNOWLEDGE GRAPH (Mermaid)\n\n\`\`\`mermaid\n${mermaid}\n\`\`\`` }] };
|
|
128
128
|
}
|
|
129
|
-
catch
|
|
130
|
-
return { content: [{ type: "text", text: "Failed to
|
|
129
|
+
catch {
|
|
130
|
+
return { content: [{ type: "text", text: "Failed to get knowledge graph." }] };
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
};
|
|
@@ -57,7 +57,7 @@ export const memoryHandlers = {
|
|
|
57
57
|
updated = true;
|
|
58
58
|
break;
|
|
59
59
|
}
|
|
60
|
-
catch
|
|
60
|
+
catch {
|
|
61
61
|
// Ignore section not found
|
|
62
62
|
}
|
|
63
63
|
}
|
|
@@ -75,10 +75,8 @@ export const memoryHandlers = {
|
|
|
75
75
|
fs.unlinkSync(lockPath);
|
|
76
76
|
return { content: [{ type: "text", text: `Section ${section} updated successfully.` }] };
|
|
77
77
|
}
|
|
78
|
-
catch
|
|
79
|
-
|
|
80
|
-
fs.unlinkSync(lockPath);
|
|
81
|
-
return { content: [{ type: "text", text: `Memory update failed: ${error instanceof Error ? error.message : "Unknown error"}` }] };
|
|
78
|
+
catch {
|
|
79
|
+
return { content: [{ type: "text", text: "Failed to update project memory." }] };
|
|
82
80
|
}
|
|
83
81
|
},
|
|
84
82
|
read_project_memory: async (args, projectRoot) => {
|
|
@@ -89,8 +87,8 @@ export const memoryHandlers = {
|
|
|
89
87
|
return { content: [{ type: "text", text: `ERROR: ${frameworkDir}/PROJECT_MEMORY.md not found.` }] };
|
|
90
88
|
return { content: [{ type: "text", text: fs.readFileSync(memoryPath, "utf-8") }] };
|
|
91
89
|
}
|
|
92
|
-
catch
|
|
93
|
-
return { content: [{ type: "text", text: "
|
|
90
|
+
catch {
|
|
91
|
+
return { content: [{ type: "text", text: "Failed to read project memory." }] };
|
|
94
92
|
}
|
|
95
93
|
},
|
|
96
94
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { getFrameworkDir } from "../utils.js";
|
|
4
|
-
import { SEND_AGENT_MESSAGE_ARGS_SCHEMA, READ_AGENT_MESSAGES_ARGS_SCHEMA, GET_AGENT_INBOX_STATS_ARGS_SCHEMA } from "../schemas.js";
|
|
4
|
+
import { SEND_AGENT_MESSAGE_ARGS_SCHEMA, READ_AGENT_MESSAGES_ARGS_SCHEMA, GET_AGENT_INBOX_STATS_ARGS_SCHEMA, UPDATE_AGENT_MESSAGE_STATUS_ARGS_SCHEMA } from "../schemas.js";
|
|
5
5
|
export const messageTools = [
|
|
6
6
|
{
|
|
7
7
|
name: "send_agent_message",
|
|
@@ -9,13 +9,14 @@ export const messageTools = [
|
|
|
9
9
|
inputSchema: {
|
|
10
10
|
type: "object",
|
|
11
11
|
properties: {
|
|
12
|
+
from: { type: "string", description: "Sender agent name (e.g. @manager, @backend)" },
|
|
12
13
|
to: { type: "string", description: "Recipient agent name" },
|
|
13
14
|
message: { type: "string", description: "The message content" },
|
|
14
15
|
traceId: { type: "string", description: "The active Trace ID" },
|
|
15
16
|
category: { type: "string", enum: ["ACTION", "DELEGATION", "INFO", "ALERT"], default: "INFO" },
|
|
16
17
|
priority: { type: "string", enum: ["LOW", "MEDIUM", "HIGH", "URGENT"], default: "MEDIUM" },
|
|
17
18
|
},
|
|
18
|
-
required: ["to", "message", "traceId"],
|
|
19
|
+
required: ["from", "to", "message", "traceId"],
|
|
19
20
|
},
|
|
20
21
|
},
|
|
21
22
|
{
|
|
@@ -41,6 +42,19 @@ export const messageTools = [
|
|
|
41
42
|
required: ["agent"],
|
|
42
43
|
},
|
|
43
44
|
},
|
|
45
|
+
{
|
|
46
|
+
name: "update_agent_message_status",
|
|
47
|
+
description: "Allows an agent to acknowledge, complete, or reject a Hermes message by Trace ID (advances the orchestration lifecycle).",
|
|
48
|
+
inputSchema: {
|
|
49
|
+
type: "object",
|
|
50
|
+
properties: {
|
|
51
|
+
agent: { type: "string", description: "The agent performing the status update" },
|
|
52
|
+
traceId: { type: "string", description: "Trace ID of the message to update" },
|
|
53
|
+
newStatus: { type: "string", enum: ["ACKNOWLEDGED", "COMPLETED", "REJECTED"], description: "New lifecycle status" },
|
|
54
|
+
},
|
|
55
|
+
required: ["agent", "traceId", "newStatus"],
|
|
56
|
+
},
|
|
57
|
+
},
|
|
44
58
|
];
|
|
45
59
|
function normalizeAgentName(agent) {
|
|
46
60
|
const normalized = agent.replace(/^@/, "").trim();
|
|
@@ -61,20 +75,22 @@ export const messageHandlers = {
|
|
|
61
75
|
fs.mkdirSync(messagesDir, { recursive: true });
|
|
62
76
|
const messagePath = path.join(messagesDir, `${recipient}.json`);
|
|
63
77
|
const messages = (fs.existsSync(messagePath) ? JSON.parse(fs.readFileSync(messagePath, "utf-8")) : []);
|
|
78
|
+
const sender = normalizeAgentName(parsed.data.from) || "unknown";
|
|
64
79
|
messages.push({
|
|
65
80
|
timestamp: new Date().toISOString(),
|
|
66
|
-
from:
|
|
81
|
+
from: sender,
|
|
82
|
+
to: recipient,
|
|
67
83
|
traceId: parsed.data.traceId,
|
|
68
84
|
category: parsed.data.category,
|
|
69
85
|
priority: parsed.data.priority,
|
|
70
86
|
content: parsed.data.message,
|
|
71
|
-
|
|
87
|
+
status: "PENDING"
|
|
72
88
|
});
|
|
73
89
|
fs.writeFileSync(messagePath, JSON.stringify(messages, null, 2));
|
|
74
90
|
return { content: [{ type: "text", text: `Hermes: Message sent to @${recipient} [${parsed.data.category} | ${parsed.data.priority}].` }] };
|
|
75
91
|
}
|
|
76
|
-
catch
|
|
77
|
-
return { content: [{ type: "text", text: "Failed to send message
|
|
92
|
+
catch {
|
|
93
|
+
return { content: [{ type: "text", text: "Failed to send agent message." }] };
|
|
78
94
|
}
|
|
79
95
|
},
|
|
80
96
|
read_agent_messages: async (args, projectRoot) => {
|
|
@@ -90,15 +106,24 @@ export const messageHandlers = {
|
|
|
90
106
|
if (!fs.existsSync(messagePath))
|
|
91
107
|
return { content: [{ type: "text", text: "No messages found." }] };
|
|
92
108
|
const messages = JSON.parse(fs.readFileSync(messagePath, "utf-8"));
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
109
|
+
const pending = messages.filter((m) => (m.status === "PENDING" || m.status === "ACKNOWLEDGED") &&
|
|
110
|
+
(!parsed.data.traceId || m.traceId === parsed.data.traceId));
|
|
111
|
+
// Transition PENDING → READ (Do not regress ACKNOWLEDGED messages back to READ)
|
|
112
|
+
const updated = messages.map((m) => {
|
|
113
|
+
if (m.status === "PENDING" &&
|
|
114
|
+
(!parsed.data.traceId || m.traceId === parsed.data.traceId)) {
|
|
115
|
+
return { ...m, status: "READ" };
|
|
116
|
+
}
|
|
117
|
+
return m;
|
|
118
|
+
});
|
|
119
|
+
fs.writeFileSync(messagePath, JSON.stringify(updated, null, 2));
|
|
120
|
+
if (pending.length === 0)
|
|
96
121
|
return { content: [{ type: "text", text: "No new messages." }] };
|
|
97
|
-
const formatted =
|
|
122
|
+
const formatted = pending.map((m) => `- [${m.priority}] **${m.from}** (${m.category}): ${m.content} *(Trace: ${m.traceId})*`).join("\n");
|
|
98
123
|
return { content: [{ type: "text", text: `### HERMES INBOX: @${agentName}\n\n${formatted}` }] };
|
|
99
124
|
}
|
|
100
|
-
catch
|
|
101
|
-
return { content: [{ type: "text", text: "Failed to
|
|
125
|
+
catch {
|
|
126
|
+
return { content: [{ type: "text", text: "Failed to send agent message." }] };
|
|
102
127
|
}
|
|
103
128
|
},
|
|
104
129
|
get_agent_inbox_stats: async (args, projectRoot) => {
|
|
@@ -114,20 +139,55 @@ export const messageHandlers = {
|
|
|
114
139
|
if (!fs.existsSync(messagePath))
|
|
115
140
|
return { content: [{ type: "text", text: "Inbox empty." }] };
|
|
116
141
|
const messages = JSON.parse(fs.readFileSync(messagePath, "utf-8"));
|
|
117
|
-
const
|
|
142
|
+
const pendingOrRead = messages.filter(m => m.status === "PENDING" || m.status === "READ");
|
|
118
143
|
const priorityDist = messages.reduce((acc, m) => {
|
|
119
144
|
acc[m.priority] = (acc[m.priority] || 0) + 1;
|
|
120
145
|
return acc;
|
|
121
146
|
}, {});
|
|
147
|
+
const statusDist = messages.reduce((acc, m) => {
|
|
148
|
+
acc[m.status] = (acc[m.status] || 0) + 1;
|
|
149
|
+
return acc;
|
|
150
|
+
}, {});
|
|
122
151
|
return {
|
|
123
152
|
content: [{
|
|
124
153
|
type: "text",
|
|
125
|
-
text: `### INBOX STATS: @${agentName}\n- Total: ${messages.length}\n-
|
|
154
|
+
text: `### INBOX STATS: @${agentName}\n- Total: ${messages.length}\n- Pending/Read: ${pendingOrRead.length}\n- By Priority: ${JSON.stringify(priorityDist)}\n- By Status: ${JSON.stringify(statusDist)}`
|
|
126
155
|
}]
|
|
127
156
|
};
|
|
128
157
|
}
|
|
129
|
-
catch
|
|
130
|
-
return { content: [{ type: "text", text: "Failed to
|
|
158
|
+
catch {
|
|
159
|
+
return { content: [{ type: "text", text: "Failed to read agent messages." }] };
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
update_agent_message_status: async (args, projectRoot) => {
|
|
163
|
+
const parsed = UPDATE_AGENT_MESSAGE_STATUS_ARGS_SCHEMA.safeParse(args ?? {});
|
|
164
|
+
if (!parsed.success)
|
|
165
|
+
return { content: [{ type: "text", text: "Invalid arguments for status update." }] };
|
|
166
|
+
try {
|
|
167
|
+
const frameworkDir = getFrameworkDir(projectRoot);
|
|
168
|
+
const agentName = normalizeAgentName(parsed.data.agent);
|
|
169
|
+
if (!agentName)
|
|
170
|
+
return { content: [{ type: "text", text: "Invalid agent name." }] };
|
|
171
|
+
const messagePath = path.join(projectRoot, frameworkDir, "messages", `${agentName}.json`);
|
|
172
|
+
if (!fs.existsSync(messagePath))
|
|
173
|
+
return { content: [{ type: "text", text: "No messages for this agent." }] };
|
|
174
|
+
const messages = JSON.parse(fs.readFileSync(messagePath, "utf-8"));
|
|
175
|
+
let updated = false;
|
|
176
|
+
const newMessages = messages.map((m) => {
|
|
177
|
+
if (m.traceId === parsed.data.traceId) {
|
|
178
|
+
updated = true;
|
|
179
|
+
return { ...m, status: parsed.data.newStatus };
|
|
180
|
+
}
|
|
181
|
+
return m;
|
|
182
|
+
});
|
|
183
|
+
if (!updated) {
|
|
184
|
+
return { content: [{ type: "text", text: `No message found with Trace ID ${parsed.data.traceId}.` }] };
|
|
185
|
+
}
|
|
186
|
+
fs.writeFileSync(messagePath, JSON.stringify(newMessages, null, 2));
|
|
187
|
+
return { content: [{ type: "text", text: `Hermes: Message ${parsed.data.traceId} marked as ${parsed.data.newStatus}.` }] };
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return { content: [{ type: "text", text: "Failed to update message status." }] };
|
|
131
191
|
}
|
|
132
192
|
}
|
|
133
193
|
};
|
|
@@ -66,8 +66,8 @@ export const repositoryHandlers = {
|
|
|
66
66
|
results.push(runScript("build"));
|
|
67
67
|
return { content: [{ type: "text", text: `### REPOSITORY HEALTH REPORT\n\n` + results.map(r => `- **${r.name.toUpperCase()}** (${r.script}): ${r.status}${r.status === "FAILED" && r.details ? `\n ${r.details}` : ""}`).join("\n") }] };
|
|
68
68
|
}
|
|
69
|
-
catch
|
|
70
|
-
return { content: [{ type: "text", text: "
|
|
69
|
+
catch {
|
|
70
|
+
return { content: [{ type: "text", text: "Failed to analyze repository health." }] };
|
|
71
71
|
}
|
|
72
72
|
},
|
|
73
73
|
analyze_documentation_debt: async (args, projectRoot) => {
|
|
@@ -94,8 +94,8 @@ export const repositoryHandlers = {
|
|
|
94
94
|
const majorDirs = ["apps/backend", "apps/web", "framework-mcp"], missingREADME = majorDirs.filter(dir => fs.existsSync(path.join(projectRoot, dir)) && !fs.existsSync(path.join(projectRoot, dir, "README.md")));
|
|
95
95
|
return { content: [{ type: "text", text: `### DOCUMENTATION DEBT REPORT\n\n**Missing JSDoc:**\n${missingJSDoc.slice(0, 10).join("\n")}\n\n**Missing README.md:**\n${missingREADME.join("\n")}` }] };
|
|
96
96
|
}
|
|
97
|
-
catch
|
|
98
|
-
return { content: [{ type: "text", text: "
|
|
97
|
+
catch {
|
|
98
|
+
return { content: [{ type: "text", text: "Failed to analyze documentation debt." }] };
|
|
99
99
|
}
|
|
100
100
|
},
|
|
101
101
|
};
|
|
@@ -60,7 +60,8 @@ export const scaffoldHandlers = {
|
|
|
60
60
|
return { content: [{ type: "text", text: `Successfully instantiated blueprint '${blueprintPath}' into '${targetPath}'` }] };
|
|
61
61
|
}
|
|
62
62
|
catch (error) {
|
|
63
|
-
|
|
63
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
64
|
+
return { content: [{ type: "text", text: `Scaffold failed: ${message}` }] };
|
|
64
65
|
}
|
|
65
66
|
},
|
|
66
67
|
save_as_blueprint: async (args, projectRoot) => {
|
|
@@ -83,7 +84,8 @@ export const scaffoldHandlers = {
|
|
|
83
84
|
return { content: [{ type: "text", text: `Successfully saved blueprint to '${blueprintPath}'` }] };
|
|
84
85
|
}
|
|
85
86
|
catch (error) {
|
|
86
|
-
|
|
87
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
88
|
+
return { content: [{ type: "text", text: `Failed to save blueprint: ${message}` }] };
|
|
87
89
|
}
|
|
88
90
|
},
|
|
89
91
|
list_blueprints: async (args, projectRoot) => {
|
|
@@ -123,7 +125,8 @@ export const scaffoldHandlers = {
|
|
|
123
125
|
return { content: [{ type: "text", text: `### AVAILABLE BLUEPRINTS\n\n` + available.map(b => `- ${b}`).join("\n") }] };
|
|
124
126
|
}
|
|
125
127
|
catch (error) {
|
|
126
|
-
|
|
128
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
129
|
+
return { content: [{ type: "text", text: `Listing blueprints failed: ${message}` }] };
|
|
127
130
|
}
|
|
128
131
|
},
|
|
129
132
|
};
|
|
@@ -77,8 +77,8 @@ export const securityHandlers = {
|
|
|
77
77
|
}
|
|
78
78
|
return { content: [{ type: "text", text: vulnerabilities.length > 0 ? `### ADVANCED SECURITY AUDIT RESULTS\n\n${Array.from(new Set(vulnerabilities)).join("\n\n")}` : "No security patterns or rule violations detected (Regex & AST verified)." }] };
|
|
79
79
|
}
|
|
80
|
-
catch
|
|
81
|
-
return { content: [{ type: "text", text: "Security
|
|
80
|
+
catch {
|
|
81
|
+
return { content: [{ type: "text", text: "Security audit failed." }] };
|
|
82
82
|
}
|
|
83
83
|
},
|
|
84
84
|
analyze_constitution_compliance: async (args, projectRoot) => {
|
|
@@ -130,8 +130,8 @@ export const securityHandlers = {
|
|
|
130
130
|
}
|
|
131
131
|
return { content: [{ type: "text", text: `### CONSTITUTION COMPLIANCE REPORT\n\n` + (violations.length > 0 ? `⚠️ **VIOLATIONS FOUND:**\n${violations.map(v => `- ${v}`).join("\n")}` : "✅ **ALL SYSTEMS COMPLIANT:** AST and Regex scans passed. No rule violations detected.") }] };
|
|
132
132
|
}
|
|
133
|
-
catch
|
|
134
|
-
return { content: [{ type: "text", text: "
|
|
133
|
+
catch {
|
|
134
|
+
return { content: [{ type: "text", text: "Constitution compliance check failed." }] };
|
|
135
135
|
}
|
|
136
136
|
},
|
|
137
137
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import fs from "fs";
|
|
3
|
-
export const FRAMEWORK_VERSION = "0.6.
|
|
3
|
+
export const FRAMEWORK_VERSION = "0.6.5";
|
|
4
4
|
export function getFrameworkDir(projectRoot) {
|
|
5
|
-
const adapters = [".
|
|
5
|
+
const adapters = [".antigravitycli", ".claude", ".cursor", ".enderun", ".codex"];
|
|
6
6
|
for (const adp of adapters) {
|
|
7
7
|
const fullPath = path.join(projectRoot, adp);
|
|
8
8
|
if (fs.existsSync(fullPath) && fs.lstatSync(fullPath).isDirectory()) {
|
|
@@ -32,6 +32,7 @@ export const LOG_AGENT_ACTION_ARGS_SCHEMA = z.object({
|
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
export const SEND_AGENT_MESSAGE_ARGS_SCHEMA = z.object({
|
|
35
|
+
from: z.string().min(1),
|
|
35
36
|
to: z.string().min(1),
|
|
36
37
|
message: z.string().min(1),
|
|
37
38
|
traceId: z.string().min(1),
|
|
@@ -39,6 +40,12 @@ export const SEND_AGENT_MESSAGE_ARGS_SCHEMA = z.object({
|
|
|
39
40
|
priority: z.enum(["LOW", "MEDIUM", "HIGH", "URGENT"]).default("MEDIUM"),
|
|
40
41
|
});
|
|
41
42
|
|
|
43
|
+
export const UPDATE_AGENT_MESSAGE_STATUS_ARGS_SCHEMA = z.object({
|
|
44
|
+
agent: z.string().min(1),
|
|
45
|
+
traceId: z.string().min(1),
|
|
46
|
+
newStatus: z.enum(["ACKNOWLEDGED", "COMPLETED", "REJECTED"]),
|
|
47
|
+
});
|
|
48
|
+
|
|
42
49
|
export const SEARCH_KNOWLEDGE_BASE_ARGS_SCHEMA = z.object({
|
|
43
50
|
query: z.string().min(1),
|
|
44
51
|
});
|
|
@@ -3,8 +3,6 @@ import path from "path";
|
|
|
3
3
|
import { getFrameworkDir } from "../utils.js";
|
|
4
4
|
import {
|
|
5
5
|
GET_ACADEMY_PERFORMANCE_ARGS_SCHEMA,
|
|
6
|
-
GENERATE_STRATEGIC_BRIEFING_ARGS_SCHEMA,
|
|
7
|
-
GENERATE_ACADEMY_PROGRESS_REPORT_ARGS_SCHEMA,
|
|
8
6
|
GET_AGENT_AUDIT_REPORT_ARGS_SCHEMA,
|
|
9
7
|
LOG_AGENT_ACTION_ARGS_SCHEMA
|
|
10
8
|
} from "../schemas.js";
|
|
@@ -112,7 +110,7 @@ export const academyHandlers = {
|
|
|
112
110
|
return { agent: file.replace(".json", ""), actions: logs.length, successRate: (success / (logs.length || 1)) * 100 };
|
|
113
111
|
}).filter(s => s.actions > 0);
|
|
114
112
|
return { content: [{ type: "text", text: `### GLOBAL ACADEMY PERFORMANCE REPORT\n\n- **Analysis Period:** Last ${days} days\n- **Global Success Rate:** ${((totalSuccess / (totalActions || 1)) * 100).toFixed(1)}%\n\n**Agent Breakdown:**\n` + agentStats.map(s => `- **@${s.agent}:** ${s.actions} actions (${s.successRate.toFixed(1)}% success)`).join("\n") }] };
|
|
115
|
-
} catch
|
|
113
|
+
} catch {
|
|
116
114
|
return { content: [{ type: "text", text: "Failed to generate global performance report." }] };
|
|
117
115
|
}
|
|
118
116
|
},
|
|
@@ -129,13 +127,11 @@ export const academyHandlers = {
|
|
|
129
127
|
}
|
|
130
128
|
const successRate = (successCount / (totalActions || 1)) * 100;
|
|
131
129
|
return { content: [{ type: "text", text: `# 🏛️ ACADEMY STRATEGIC BRIEFING\n\n- **Academy Health:** ${successRate.toFixed(1)}% Success Rate over ${totalActions} actions.\n**Status:** READY FOR PHASE_1 COMPLETION.` }] };
|
|
132
|
-
} catch
|
|
130
|
+
} catch {
|
|
133
131
|
return { content: [{ type: "text", text: "Strategic briefing generation failed." }] };
|
|
134
132
|
}
|
|
135
133
|
},
|
|
136
134
|
generate_academy_progress_report: async (args: unknown, projectRoot: string) => {
|
|
137
|
-
const parsed = GENERATE_ACADEMY_PROGRESS_REPORT_ARGS_SCHEMA.safeParse(args ?? {});
|
|
138
|
-
const days = parsed.success ? parsed.data.days : 7;
|
|
139
135
|
try {
|
|
140
136
|
const frameworkDir = getFrameworkDir(projectRoot);
|
|
141
137
|
const memoryPath = path.join(projectRoot, frameworkDir, "PROJECT_MEMORY.md");
|
|
@@ -143,7 +139,7 @@ export const academyHandlers = {
|
|
|
143
139
|
const historySection = memoryContent.match(/## HISTORY[\s\S]*$/m)?.[0] ?? "";
|
|
144
140
|
const milestones = Array.from(historySection.matchAll(/### (.*)\n\n- \*\*Agent:\*\* (.*)\n- \*\*Trace ID:\*\* (.*)\n- \*\*Action:\*\* (.*)/g)).slice(0, 5).map(m => `- [${m[1]}] **${m[2]}:** ${m[4]}`).join("\n");
|
|
145
141
|
return { content: [{ type: "text", text: `# 🎓 AGENT ACADEMY PROGRESS REPORT\n\n## 🏆 Key Milestones Achieved\n${milestones}` }] };
|
|
146
|
-
} catch
|
|
142
|
+
} catch {
|
|
147
143
|
return { content: [{ type: "text", text: "Failed to generate progress report." }] };
|
|
148
144
|
}
|
|
149
145
|
},
|
|
@@ -158,7 +154,7 @@ export const academyHandlers = {
|
|
|
158
154
|
const logs = (JSON.parse(fs.readFileSync(logPath, "utf-8")) as AgentLog[]).filter((l) => new Date(l.timestamp) >= cutoff);
|
|
159
155
|
const successCount = logs.filter((l) => l.status === "SUCCESS").length;
|
|
160
156
|
return { content: [{ type: "text", text: `### AGENT AUDIT REPORT: ${parsed.data.agent.toUpperCase()}\n\n- **Total Actions:** ${logs.length}\n- **Success Rate:** ${((successCount / (logs.length || 1)) * 100).toFixed(1)}%` }] };
|
|
161
|
-
} catch
|
|
157
|
+
} catch {
|
|
162
158
|
return { content: [{ type: "text", text: "Failed to generate audit report." }] };
|
|
163
159
|
}
|
|
164
160
|
},
|
|
@@ -177,7 +173,7 @@ export const academyHandlers = {
|
|
|
177
173
|
logs.push({ timestamp: new Date().toISOString(), ...parsed.data });
|
|
178
174
|
fs.writeFileSync(logPath, JSON.stringify(logs, null, 2));
|
|
179
175
|
return { content: [{ type: "text", text: `SUCCESS: Logged action to ${parsed.data.agent}.json` }] };
|
|
180
|
-
} catch
|
|
176
|
+
} catch {
|
|
181
177
|
return { content: [{ type: "text", text: "Failed to log action." }] };
|
|
182
178
|
}
|
|
183
179
|
},
|
|
@@ -145,7 +145,7 @@ export const codebaseHandlers = {
|
|
|
145
145
|
let queryRegex: RegExp;
|
|
146
146
|
try {
|
|
147
147
|
queryRegex = new RegExp(query, "i");
|
|
148
|
-
} catch
|
|
148
|
+
} catch {
|
|
149
149
|
return { content: [{ type: "text", text: "Invalid regex pattern in query." }] };
|
|
150
150
|
}
|
|
151
151
|
const matches: string[] = [];
|
|
@@ -164,7 +164,7 @@ export const codebaseHandlers = {
|
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
return { content: [{ type: "text", text: matches.join("\n") || "No matches found." }] };
|
|
167
|
-
} catch
|
|
167
|
+
} catch {
|
|
168
168
|
return { content: [{ type: "text", text: "Search failed." }] };
|
|
169
169
|
}
|
|
170
170
|
},
|
|
@@ -223,7 +223,7 @@ export const codebaseHandlers = {
|
|
|
223
223
|
});
|
|
224
224
|
}
|
|
225
225
|
return { content: [{ type: "text", text: `### CODEBASE INTELLIGENCE REPORT\n\n**Complexity Spikes (>300 lines):**\n${complexitySpikes.length > 0 ? complexitySpikes.map(s => `- ${s}`).join("\n") : "- None detected"}\n\n**Potential Dead Code (Unused Exports):**\n${potentialDeadCode.length > 0 ? potentialDeadCode.map(d => `- ${d}`).join("\n") : "- None detected (within scanned scope)"}` }] };
|
|
226
|
-
} catch
|
|
226
|
+
} catch {
|
|
227
227
|
return { content: [{ type: "text", text: "Intelligence scan failed." }] };
|
|
228
228
|
}
|
|
229
229
|
},
|
|
@@ -247,8 +247,8 @@ export const codebaseHandlers = {
|
|
|
247
247
|
const referenceFunctions = referenceFile.getFunctions().map(f => f.getName());
|
|
248
248
|
if (referenceFunctions.some(f => f?.startsWith("get")) && !targetFunctions.some(f => f?.startsWith("get"))) deviations.push("Reference uses 'get' prefix for functions, target does not.");
|
|
249
249
|
return { content: [{ type: "text", text: deviations.length > 0 ? `### PROCEDURAL CONTINUITY DEVIATIONS\n\n${deviations.map(d => `- ${d}`).join("\n")}` : "No major structural or stylistic deviations detected based on reference." }] };
|
|
250
|
-
} catch
|
|
251
|
-
return { content: [{ type: "text", text: "
|
|
250
|
+
} catch {
|
|
251
|
+
return { content: [{ type: "text", text: "Dependency analysis failed." }] };
|
|
252
252
|
}
|
|
253
253
|
},
|
|
254
254
|
generate_dependency_graph: async (args: unknown, projectRoot: string) => {
|
|
@@ -277,7 +277,7 @@ export const codebaseHandlers = {
|
|
|
277
277
|
if (uniqueEdges.length === 0) return { content: [{ type: "text", text: "No internal dependencies found in the scanned path." }] };
|
|
278
278
|
mermaid += uniqueEdges.join("\n");
|
|
279
279
|
return { content: [{ type: "text", text: `### VISUAL ARCHITECTURE MAP: ${targetPath}\n\n\`\`\`mermaid\n${mermaid}\n\`\`\`\n\n**Total Nodes:** ${new Set(edges.flatMap(e => [e.from, e.to])).size} | **Total Edges:** ${uniqueEdges.length}` }] };
|
|
280
|
-
} catch
|
|
280
|
+
} catch {
|
|
281
281
|
return { content: [{ type: "text", text: "Dependency graph generation failed." }] };
|
|
282
282
|
}
|
|
283
283
|
},
|