agent-enderun 1.11.3 ā 1.11.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -277
- package/bin/validate-agent-army.js +0 -1
- package/dist/framework-mcp/src/index.js +11 -1
- package/dist/framework-mcp/src/index.js.map +1 -1
- package/dist/framework-mcp/src/tools/control_plane/locking.js +17 -3
- package/dist/framework-mcp/src/tools/control_plane/locking.js.map +1 -1
- package/dist/framework-mcp/src/tools/definitions.js +0 -10
- package/dist/framework-mcp/src/tools/definitions.js.map +1 -1
- package/dist/framework-mcp/src/tools/index.js +0 -2
- package/dist/framework-mcp/src/tools/index.js.map +1 -1
- package/dist/framework-mcp/src/tools/memory/get_insights.js +4 -4
- package/dist/framework-mcp/src/tools/memory/get_insights.js.map +1 -1
- package/dist/framework-mcp/src/tools/messaging/send_message.js +10 -5
- package/dist/framework-mcp/src/tools/messaging/send_message.js.map +1 -1
- package/dist/framework-mcp/src/tools/search/grep_search.js +2 -3
- package/dist/framework-mcp/src/tools/search/grep_search.js.map +1 -1
- package/dist/framework-mcp/src/utils/compliance.d.ts +1 -1
- package/dist/framework-mcp/src/utils/compliance.js +68 -19
- package/dist/framework-mcp/src/utils/compliance.js.map +1 -1
- package/dist/framework-mcp/tests/tools/messaging/send_message.test.js +7 -7
- package/dist/framework-mcp/tests/tools/messaging/send_message.test.js.map +1 -1
- package/dist/src/cli/adapters/scaffold.d.ts +1 -1
- package/dist/src/cli/adapters/scaffold.js +8 -8
- package/dist/src/cli/adapters/scaffold.js.map +1 -1
- package/dist/src/cli/commands/check.js +25 -40
- package/dist/src/cli/commands/check.js.map +1 -1
- package/dist/src/cli/commands/contract.d.ts +3 -1
- package/dist/src/cli/commands/contract.js +8 -2
- package/dist/src/cli/commands/contract.js.map +1 -1
- package/dist/src/cli/commands/init/scaffold-core.d.ts +1 -0
- package/dist/src/cli/commands/init/scaffold-core.js +14 -1
- package/dist/src/cli/commands/init/scaffold-core.js.map +1 -1
- package/dist/src/cli/commands/init/scaffold-ops.js +0 -2
- package/dist/src/cli/commands/init/scaffold-ops.js.map +1 -1
- package/dist/src/cli/commands/init.d.ts +1 -0
- package/dist/src/cli/commands/init.js +16 -7
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/orchestrate.js +31 -3
- package/dist/src/cli/commands/orchestrate.js.map +1 -1
- package/dist/src/cli/commands/plan.js +1 -2
- package/dist/src/cli/commands/plan.js.map +1 -1
- package/dist/src/cli/commands/status.js.map +1 -1
- package/dist/src/cli/index.js +18 -21
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/shims.js +39 -26
- package/dist/src/cli/shims.js.map +1 -1
- package/dist/src/cli/utils/compliance.d.ts +5 -1
- package/dist/src/cli/utils/compliance.js +90 -42
- package/dist/src/cli/utils/compliance.js.map +1 -1
- package/dist/src/cli/utils/ui.js +11 -11
- package/dist/src/cli/utils/ui.js.map +1 -1
- package/dist/src/modules/adapters/definitions.js +130 -113
- package/dist/src/modules/adapters/definitions.js.map +1 -1
- package/dist/src/modules/adapters/registry.d.ts +12 -0
- package/dist/src/modules/adapters/registry.js +19 -0
- package/dist/src/modules/adapters/registry.js.map +1 -0
- package/dist/src/modules/adapters/shared.js +5 -4
- package/dist/src/modules/adapters/shared.js.map +1 -1
- package/dist/src/modules/adapters/types.d.ts +1 -1
- package/dist/src/modules/adapters/types.js +1 -1
- package/dist/src/modules/adapters/types.js.map +1 -1
- package/dist/src/modules/agents/definitions.d.ts +6 -6
- package/dist/src/modules/agents/definitions.js +35 -18
- package/dist/src/modules/agents/definitions.js.map +1 -1
- package/dist/src/modules/agents/registry/analyst.js +1 -1
- package/dist/src/modules/agents/registry/analyst.js.map +1 -1
- package/dist/src/modules/agents/registry/architect.js +1 -1
- package/dist/src/modules/agents/registry/architect.js.map +1 -1
- package/dist/src/modules/agents/registry/backend.js +1 -1
- package/dist/src/modules/agents/registry/backend.js.map +1 -1
- package/dist/src/modules/agents/registry/database.js +1 -1
- package/dist/src/modules/agents/registry/database.js.map +1 -1
- package/dist/src/modules/agents/registry/devops.js +1 -1
- package/dist/src/modules/agents/registry/devops.js.map +1 -1
- package/dist/src/modules/agents/registry/explorer.js +1 -1
- package/dist/src/modules/agents/registry/explorer.js.map +1 -1
- package/dist/src/modules/agents/registry/frontend.js +1 -1
- package/dist/src/modules/agents/registry/frontend.js.map +1 -1
- package/dist/src/modules/agents/registry/git.js +1 -1
- package/dist/src/modules/agents/registry/git.js.map +1 -1
- package/dist/src/modules/agents/registry/manager.js +1 -2
- package/dist/src/modules/agents/registry/manager.js.map +1 -1
- package/dist/src/modules/agents/registry/mobile.js +1 -1
- package/dist/src/modules/agents/registry/mobile.js.map +1 -1
- package/dist/src/modules/agents/registry/quality.js +1 -1
- package/dist/src/modules/agents/registry/quality.js.map +1 -1
- package/dist/src/modules/agents/registry/security.js +1 -1
- package/dist/src/modules/agents/registry/security.js.map +1 -1
- package/dist/src/shared/constants.d.ts +5 -2
- package/dist/src/shared/constants.js +5 -0
- package/dist/src/shared/constants.js.map +1 -1
- package/dist/src/shared/fs.js +4 -3
- package/dist/src/shared/fs.js.map +1 -1
- package/dist/tests/agents-definitions.test.js +2 -1
- package/dist/tests/agents-definitions.test.js.map +1 -1
- package/dist/tests/approve.test.js +3 -1
- package/dist/tests/approve.test.js.map +1 -1
- package/dist/tests/integration/agent_flow.test.js +2 -0
- package/dist/tests/integration/agent_flow.test.js.map +1 -1
- package/dist/tests/integration/hermes_locking.test.d.ts +1 -0
- package/dist/tests/integration/hermes_locking.test.js +64 -0
- package/dist/tests/integration/hermes_locking.test.js.map +1 -0
- package/dist/tests/memory-utils.test.js +2 -0
- package/dist/tests/memory-utils.test.js.map +1 -1
- package/dist/tests/orchestrate.test.js +11 -6
- package/dist/tests/orchestrate.test.js.map +1 -1
- package/dist/tests/status.test.js +2 -0
- package/dist/tests/status.test.js.map +1 -1
- package/framework-mcp/dist/index.js +11 -1
- package/framework-mcp/dist/tools/control_plane/locking.js +17 -3
- package/framework-mcp/dist/tools/definitions.js +0 -10
- package/framework-mcp/dist/tools/index.js +0 -2
- package/framework-mcp/dist/tools/memory/get_insights.js +4 -4
- package/framework-mcp/dist/tools/messaging/send_message.js +10 -5
- package/framework-mcp/dist/tools/search/grep_search.js +2 -3
- package/framework-mcp/dist/utils/compliance.js +68 -19
- package/framework-mcp/node_modules/.package-lock.json +1179 -0
- package/framework-mcp/package-lock.json +1191 -0
- package/framework-mcp/package.json +2 -2
- package/framework-mcp/src/index.ts +13 -1
- package/framework-mcp/src/tools/control_plane/locking.ts +18 -4
- package/framework-mcp/src/tools/definitions.ts +0 -10
- package/framework-mcp/src/tools/index.ts +0 -2
- package/framework-mcp/src/tools/memory/get_insights.ts +4 -4
- package/framework-mcp/src/tools/messaging/send_message.ts +11 -5
- package/framework-mcp/src/tools/search/grep_search.ts +2 -3
- package/framework-mcp/src/utils/compliance.ts +83 -25
- package/framework-mcp/tests/tools/messaging/send_message.test.ts +7 -7
- package/package.json +5 -5
- package/src/cli/adapters/scaffold.ts +9 -8
- package/src/cli/commands/check.ts +26 -48
- package/src/cli/commands/contract.ts +5 -2
- package/src/cli/commands/init/scaffold-core.ts +18 -2
- package/src/cli/commands/init/scaffold-ops.ts +0 -3
- package/src/cli/commands/init.ts +19 -8
- package/src/cli/commands/orchestrate.ts +36 -3
- package/src/cli/commands/plan.ts +1 -2
- package/src/cli/commands/status.ts +0 -1
- package/src/cli/index.ts +18 -22
- package/src/cli/shims.ts +39 -26
- package/src/cli/utils/compliance.ts +101 -47
- package/src/cli/utils/ui.ts +11 -11
- package/src/modules/adapters/definitions.ts +102 -57
- package/src/modules/adapters/registry.ts +27 -0
- package/src/modules/adapters/shared.ts +5 -4
- package/src/modules/adapters/types.ts +1 -1
- package/src/modules/agents/definitions.ts +35 -17
- package/src/modules/agents/registry/analyst.ts +1 -1
- package/src/modules/agents/registry/architect.ts +1 -1
- package/src/modules/agents/registry/backend.ts +1 -1
- package/src/modules/agents/registry/database.ts +1 -1
- package/src/modules/agents/registry/devops.ts +1 -1
- package/src/modules/agents/registry/explorer.ts +1 -1
- package/src/modules/agents/registry/frontend.ts +1 -1
- package/src/modules/agents/registry/git.ts +1 -1
- package/src/modules/agents/registry/manager.ts +1 -2
- package/src/modules/agents/registry/mobile.ts +1 -1
- package/src/modules/agents/registry/quality.ts +1 -1
- package/src/modules/agents/registry/security.ts +1 -1
- package/src/shared/constants.ts +5 -0
- package/src/shared/fs.ts +4 -3
- package/templates/standards/frontend-standards.md +7 -2
- package/templates/standards/governance-standards.md +27 -1
- package/templates/standards/quality-standards.md +2 -1
- package/bin/enderun-dashboard.js +0 -25
- package/framework-mcp/src/tools/dashboard/start_dashboard.ts +0 -33
- package/src/cli/commands/dashboard/data-service.ts +0 -167
- package/src/cli/commands/dashboard/html-template.ts +0 -696
- package/src/cli/commands/dashboard/server.ts +0 -43
- package/src/cli/commands/dashboard.ts +0 -11
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-enderun/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.5",
|
|
4
4
|
"description": "Agent Enderun Model Context Protocol (MCP) Server",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -14,6 +14,6 @@
|
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@types/node": "^25.9.1",
|
|
17
|
-
"typescript": "^5.
|
|
17
|
+
"typescript": "^5.9.3"
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -57,13 +57,25 @@ function validateArgs(toolName: string, args: Record<string, unknown>): string |
|
|
|
57
57
|
return null;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
60
|
+
server.setRequestHandler(ListToolsRequestSchema, async (request) => {
|
|
61
|
+
// 2026 Stateless Spec: Log client info from metadata if available
|
|
62
|
+
const meta = (request as { _meta?: { client?: { name?: string; version?: string } } })._meta;
|
|
63
|
+
if (meta) {
|
|
64
|
+
process.stderr.write(`[MCP] Stateless ListTools from ${meta.client?.name || "unknown"} v${meta.client?.version || "?.?"}\n`);
|
|
65
|
+
}
|
|
61
66
|
return { tools: TOOLS };
|
|
62
67
|
});
|
|
63
68
|
|
|
64
69
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
65
70
|
const req = request as { params: { name: string, arguments?: Record<string, unknown> } };
|
|
66
71
|
const { name, arguments: args } = req.params;
|
|
72
|
+
const meta = (request as { _meta?: { client?: { name?: string; version?: string } } })._meta;
|
|
73
|
+
|
|
74
|
+
// 2026 Stateless Spec: Prioritize metadata-driven context
|
|
75
|
+
if (meta) {
|
|
76
|
+
process.stderr.write(`[MCP] Stateless CallTool: ${name} (Client: ${meta.client?.name || "unknown"})\n`);
|
|
77
|
+
}
|
|
78
|
+
|
|
67
79
|
const projectRoot = process.env.ENDERUN_PROJECT_ROOT || process.cwd();
|
|
68
80
|
|
|
69
81
|
try {
|
|
@@ -15,6 +15,7 @@ export async function handleAcquireLock(projectRoot: string, args: AcquireLockAr
|
|
|
15
15
|
try {
|
|
16
16
|
if (!fs.existsSync(lockDir)) fs.mkdirSync(lockDir, { recursive: true });
|
|
17
17
|
|
|
18
|
+
// Check for stale lock first
|
|
18
19
|
if (fs.existsSync(lockPath)) {
|
|
19
20
|
const stat = fs.statSync(lockPath);
|
|
20
21
|
const now = new Date().getTime();
|
|
@@ -26,15 +27,28 @@ export async function handleAcquireLock(projectRoot: string, args: AcquireLockAr
|
|
|
26
27
|
content: [{ type: "text", text: `Resource '${resource}' is currently locked by another agent.` }]
|
|
27
28
|
};
|
|
28
29
|
}
|
|
29
|
-
// Lock expired, safe to override
|
|
30
|
-
|
|
30
|
+
// Lock expired, safe to override by deleting first
|
|
31
|
+
try {
|
|
32
|
+
fs.unlinkSync(lockPath);
|
|
33
|
+
} catch (unlinkErr) {
|
|
34
|
+
// If another agent unlinked it first, ignore
|
|
35
|
+
}
|
|
31
36
|
}
|
|
32
37
|
|
|
33
|
-
|
|
38
|
+
// Use 'wx' flag for atomic file creation
|
|
39
|
+
const lockData = JSON.stringify({ agent, timestamp: new Date().toISOString() });
|
|
40
|
+
fs.writeFileSync(lockPath, lockData, { flag: "wx" });
|
|
41
|
+
|
|
34
42
|
return {
|
|
35
43
|
content: [{ type: "text", text: `ā
Lock acquired for resource '${resource}' by ${agent}.` }]
|
|
36
44
|
};
|
|
37
|
-
} catch (e) {
|
|
45
|
+
} catch (e: any) {
|
|
46
|
+
if (e.code === "EEXIST") {
|
|
47
|
+
return {
|
|
48
|
+
isError: true,
|
|
49
|
+
content: [{ type: "text", text: `Resource '${resource}' was just acquired by another agent.` }]
|
|
50
|
+
};
|
|
51
|
+
}
|
|
38
52
|
return {
|
|
39
53
|
isError: true,
|
|
40
54
|
content: [{ type: "text", text: `Failed to acquire lock: ${String(e)}` }]
|
|
@@ -151,16 +151,6 @@ export const TOOLS: ToolDefinition[] = [
|
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
153
|
},
|
|
154
|
-
{
|
|
155
|
-
name: "start_dashboard",
|
|
156
|
-
description: "Start the Enderun Web Dashboard (Command Center) to visualize real-time agent status, logs, and system metrics in the browser.",
|
|
157
|
-
inputSchema: {
|
|
158
|
-
type: "object",
|
|
159
|
-
properties: {
|
|
160
|
-
port: { type: "number", description: "Optional: Port to run the dashboard on (default: 5858)" }
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
},
|
|
164
154
|
{
|
|
165
155
|
name: "get_framework_status",
|
|
166
156
|
description: "Get the current project phase, active traces, and agent states.",
|
|
@@ -14,7 +14,6 @@ import { handleAuditDependencies } from "./framework/audit_deps.js";
|
|
|
14
14
|
import { handleRunTests } from "./framework/run_tests.js";
|
|
15
15
|
import { handleGetSystemHealth } from "./observability/get_health.js";
|
|
16
16
|
import { handleCheckPorts } from "./observability/check_ports.js";
|
|
17
|
-
import { handleStartDashboard } from "./dashboard/start_dashboard.js";
|
|
18
17
|
import { handleOrchestrateLoop } from "./framework/orchestrate.js";
|
|
19
18
|
import { handleUpdateContractHash } from "./framework/update_contract_hash.js";
|
|
20
19
|
import { handleReadProjectMemory } from "./memory/read_memory.js";
|
|
@@ -51,7 +50,6 @@ export const toolHandlers: Record<string, ToolHandler> = {
|
|
|
51
50
|
run_tests: bind(handleRunTests),
|
|
52
51
|
get_system_health: bind(handleGetSystemHealth),
|
|
53
52
|
check_active_ports: bind(handleCheckPorts),
|
|
54
|
-
start_dashboard: bind(handleStartDashboard),
|
|
55
53
|
orchestrate_loop: bind(handleOrchestrateLoop),
|
|
56
54
|
send_agent_message: bind(handleSendAgentMessage),
|
|
57
55
|
log_agent_action: bind(handleLogAgentAction),
|
|
@@ -19,14 +19,14 @@ export function handleGetMemoryInsights(projectRoot: string, _args: ToolArgs): T
|
|
|
19
19
|
const content = fs.readFileSync(memoryPath, "utf8");
|
|
20
20
|
const lines = content.split("\n");
|
|
21
21
|
|
|
22
|
-
const activePhase = lines.find(l => l.includes("
|
|
23
|
-
const activeTrace = lines.find(l => l.includes("
|
|
22
|
+
const activePhase = lines.find(l => l.includes("**Phase:**"))?.split("**Phase:**")[1]?.trim() || "Unknown";
|
|
23
|
+
const activeTrace = lines.find(l => l.includes("**Trace ID:**"))?.split("**Trace ID:**")[1]?.trim() || "None";
|
|
24
24
|
|
|
25
25
|
// Find the last 5 history items (heuristic)
|
|
26
|
-
const historyStartIndex = lines.findIndex(l => l.includes("
|
|
26
|
+
const historyStartIndex = lines.findIndex(l => l.toUpperCase().includes("HISTORY"));
|
|
27
27
|
let recentHistory = "No history found.";
|
|
28
28
|
if (historyStartIndex !== -1) {
|
|
29
|
-
recentHistory = lines.slice(historyStartIndex + 1
|
|
29
|
+
recentHistory = lines.slice(historyStartIndex + 1)
|
|
30
30
|
.filter(l => l.trim().startsWith("-"))
|
|
31
31
|
.slice(-5)
|
|
32
32
|
.join("\n");
|
|
@@ -5,7 +5,7 @@ import { SendAgentMessageArgs, ToolResult } from "../types.js";
|
|
|
5
5
|
import { Metrics } from "../../utils/metrics.js";
|
|
6
6
|
|
|
7
7
|
export async function handleSendAgentMessage(projectRoot: string, args: SendAgentMessageArgs): Promise<ToolResult> {
|
|
8
|
-
const { to, category, content, traceId } = args;
|
|
8
|
+
const { to, category, content, traceId, parentId, requiresApproval } = args;
|
|
9
9
|
const from = args.from || "@mcp";
|
|
10
10
|
|
|
11
11
|
if (!to || !category || !content || !traceId) {
|
|
@@ -20,15 +20,15 @@ export async function handleSendAgentMessage(projectRoot: string, args: SendAgen
|
|
|
20
20
|
const messagePath = path.join(messagesDir, `${agentName}.json`);
|
|
21
21
|
const lockPath = path.join(messagesDir, `${agentName}.lock`);
|
|
22
22
|
|
|
23
|
-
// Hermes Lock Protocol: Retry
|
|
24
|
-
let retries =
|
|
23
|
+
// Hermes Lock Protocol: Retry 20 times with 500ms delay
|
|
24
|
+
let retries = 20;
|
|
25
25
|
let acquired = false;
|
|
26
26
|
while (retries > 0) {
|
|
27
27
|
try {
|
|
28
28
|
if (fs.existsSync(lockPath)) {
|
|
29
29
|
try {
|
|
30
30
|
const stats = fs.statSync(lockPath);
|
|
31
|
-
if (Date.now() - stats.mtimeMs >
|
|
31
|
+
if (Date.now() - stats.mtimeMs > 10000) {
|
|
32
32
|
fs.unlinkSync(lockPath);
|
|
33
33
|
}
|
|
34
34
|
} catch {
|
|
@@ -60,15 +60,21 @@ export async function handleSendAgentMessage(projectRoot: string, args: SendAgen
|
|
|
60
60
|
|
|
61
61
|
try {
|
|
62
62
|
const defaultPriority = (category === "ALERT" || category === "ACTION") ? "HIGH" : "NORMAL";
|
|
63
|
+
const finalRequiresApproval = requiresApproval !== undefined
|
|
64
|
+
? requiresApproval
|
|
65
|
+
: category === "ALERT";
|
|
66
|
+
|
|
63
67
|
const message = {
|
|
64
68
|
timestamp: new Date().toISOString(),
|
|
65
69
|
from,
|
|
66
70
|
to,
|
|
67
71
|
category,
|
|
68
72
|
traceId,
|
|
73
|
+
parentId,
|
|
69
74
|
content,
|
|
70
75
|
priority: args.priority || defaultPriority,
|
|
71
|
-
status: "PENDING"
|
|
76
|
+
status: "PENDING",
|
|
77
|
+
requiresApproval: finalRequiresApproval
|
|
72
78
|
};
|
|
73
79
|
|
|
74
80
|
fs.appendFileSync(messagePath, JSON.stringify(message) + "\n");
|
|
@@ -18,9 +18,8 @@ export function handleGrepSearch(projectRoot: string, args: GrepSearchArgs): Too
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const results: string[] = [];
|
|
21
|
-
let regex: RegExp;
|
|
22
21
|
try {
|
|
23
|
-
|
|
22
|
+
new RegExp(pattern);
|
|
24
23
|
} catch (e) {
|
|
25
24
|
const err = `Invalid regex pattern: ${String(e)}`;
|
|
26
25
|
Metrics.logError(projectRoot, "@mcp", "grep_search", err);
|
|
@@ -53,7 +52,7 @@ export function handleGrepSearch(projectRoot: string, args: GrepSearchArgs): Too
|
|
|
53
52
|
}
|
|
54
53
|
}
|
|
55
54
|
}
|
|
56
|
-
} catch
|
|
55
|
+
} catch {
|
|
57
56
|
// Ignore directories that cannot be read
|
|
58
57
|
}
|
|
59
58
|
};
|
|
@@ -1,37 +1,95 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Enterprise Compliance Guardrail
|
|
3
|
-
* Checks content against corporate standards before allowing file mutations.
|
|
5
|
+
* Checks content against corporate standards using AST analysis before allowing file mutations.
|
|
4
6
|
*/
|
|
5
7
|
export function verifyCorporateCompliance(content: string, filePath: string): void {
|
|
6
|
-
//
|
|
7
|
-
if (
|
|
8
|
-
|
|
9
|
-
throw new Error(
|
|
10
|
-
"ā Corporate Compliance Breach: 'console.log/warn/error' usage is forbidden. " +
|
|
11
|
-
"Use the 'logger' system from '@/shared/logger' instead."
|
|
12
|
-
);
|
|
13
|
-
}
|
|
8
|
+
// Skip compliance checks for non-source files or specific ignored files
|
|
9
|
+
if (filePath.endsWith(".json") || filePath.endsWith(".md") || filePath.endsWith(".env.example")) {
|
|
10
|
+
return;
|
|
14
11
|
}
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
13
|
+
const sourceFile = ts.createSourceFile(
|
|
14
|
+
filePath,
|
|
15
|
+
content,
|
|
16
|
+
ts.ScriptTarget.Latest,
|
|
17
|
+
true
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const errors: string[] = [];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Recursive AST Visitor
|
|
24
|
+
*/
|
|
25
|
+
function visit(node: ts.Node) {
|
|
26
|
+
// 1. Zero Console Policy
|
|
27
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
28
|
+
const expression = node.expression;
|
|
29
|
+
const name = node.name.text;
|
|
30
|
+
if (ts.isIdentifier(expression) && expression.text === "console") {
|
|
31
|
+
if (["log", "warn", "error"].includes(name)) {
|
|
32
|
+
// Check if file is exempt
|
|
33
|
+
if (!filePath.includes("logger.ts") && !filePath.includes("check.ts") && !filePath.includes("cli.ts")) {
|
|
34
|
+
errors.push(`ā Corporate Compliance Breach: 'console.${name}' usage is forbidden at line ${sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1}.`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 2. No Explicit Any Policy
|
|
41
|
+
if (ts.isTypeReferenceNode(node)) {
|
|
42
|
+
if (ts.isIdentifier(node.typeName) && node.typeName.text === "any") {
|
|
43
|
+
if (!filePath.includes("definitions.ts") && !filePath.includes("types.ts")) {
|
|
44
|
+
errors.push(`ā Corporate Compliance Breach: 'any' type is forbidden at line ${sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1}.`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
24
47
|
}
|
|
48
|
+
|
|
49
|
+
// 3. Zero UI Library Policy (No @chakra-ui, mui, @shadcn)
|
|
50
|
+
if (ts.isImportDeclaration(node)) {
|
|
51
|
+
const moduleSpecifier = node.moduleSpecifier;
|
|
52
|
+
if (ts.isStringLiteral(moduleSpecifier)) {
|
|
53
|
+
const forbiddenLibs = ["@chakra-ui", "mui", "@shadcn", "antd", "bootstrap"];
|
|
54
|
+
const lib = forbiddenLibs.find(l => moduleSpecifier.text.includes(l));
|
|
55
|
+
if (lib) {
|
|
56
|
+
errors.push(`ā Corporate Compliance Breach: External UI library '${lib}' usage is FORBIDDEN at line ${sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1}. Build atomic components manually instead.`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Handle 'any' as a keyword type (e.g., parameter: any)
|
|
62
|
+
if (node.kind === ts.SyntaxKind.AnyKeyword) {
|
|
63
|
+
if (!filePath.includes("definitions.ts") && !filePath.includes("types.ts")) {
|
|
64
|
+
errors.push(`ā Corporate Compliance Breach: 'any' keyword is forbidden at line ${sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1}.`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
ts.forEachChild(node, visit);
|
|
25
69
|
}
|
|
26
70
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
71
|
+
visit(sourceFile);
|
|
72
|
+
|
|
73
|
+
// 3. Hardcoded Secrets & PII Guard
|
|
74
|
+
const piiKeywords = [
|
|
75
|
+
{ regex: /API_KEY\s*=\s*['"][^'"]+['"]/i, msg: "Hardcoded API Key" },
|
|
76
|
+
{ regex: /SECRET\s*=\s*['"][^'"]+['"]/i, msg: "Hardcoded Secret" },
|
|
77
|
+
{ regex: /PASSWORD\s*=\s*['"][^'"]+['"]/i, msg: "Hardcoded Password" },
|
|
78
|
+
{ regex: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/, msg: "PII Detected: Email Address" },
|
|
79
|
+
{ regex: /\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/, msg: "PII Detected: Credit Card Pattern" }
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
for (const { regex, msg } of piiKeywords) {
|
|
83
|
+
if (regex.test(content)) {
|
|
84
|
+
// Allow emails in specific files like README or package.json
|
|
85
|
+
if (msg.includes("Email") && (filePath.endsWith("README.md") || filePath.endsWith("package.json") || filePath.includes("CONTRIBUTING"))) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
errors.push(`ā Corporate Compliance Breach: ${msg} detected.`);
|
|
35
89
|
}
|
|
36
90
|
}
|
|
91
|
+
|
|
92
|
+
if (errors.length > 0) {
|
|
93
|
+
throw new Error(errors.join("\n"));
|
|
94
|
+
}
|
|
37
95
|
}
|
|
@@ -72,16 +72,16 @@ describe("Hermes Lock Protocol & Message sending", () => {
|
|
|
72
72
|
|
|
73
73
|
expect(result.isError).toBe(true);
|
|
74
74
|
expect(result.content[0].text).toContain("ā Could not send message to @backend: Hermes lock is busy.");
|
|
75
|
-
// Retries takes
|
|
76
|
-
expect(duration).toBeGreaterThanOrEqual(
|
|
77
|
-
});
|
|
75
|
+
// Retries takes 20 attempts with 500ms delay -> duration should be at least ~9000ms
|
|
76
|
+
expect(duration).toBeGreaterThanOrEqual(9000);
|
|
77
|
+
}, 15000);
|
|
78
78
|
|
|
79
|
-
it("should bypass and acquire lock if existing lock is stale (older than
|
|
79
|
+
it("should bypass and acquire lock if existing lock is stale (older than 10s)", async () => {
|
|
80
80
|
fs.mkdirSync(MESSAGES_DIR, { recursive: true });
|
|
81
|
-
// Set lock file mtime to
|
|
81
|
+
// Set lock file mtime to 15 seconds ago
|
|
82
82
|
fs.writeFileSync(BACKEND_LOCK_FILE, "Locked by @test at " + new Date().toISOString(), "utf8");
|
|
83
|
-
const
|
|
84
|
-
fs.utimesSync(BACKEND_LOCK_FILE,
|
|
83
|
+
const fifteenSecondsAgo = new Date(Date.now() - 15000);
|
|
84
|
+
fs.utimesSync(BACKEND_LOCK_FILE, fifteenSecondsAgo, fifteenSecondsAgo);
|
|
85
85
|
|
|
86
86
|
const args: ToolArgs = {
|
|
87
87
|
to: "@backend",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-enderun",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.5",
|
|
4
4
|
"description": "The Supreme AI Governance & Autonomous Orchestration Framework for Enterprise Development. Acts as a Corporate AI Management Assistant.",
|
|
5
5
|
"author": "Yusuf BEKAR",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"framework"
|
|
25
25
|
],
|
|
26
26
|
"workspaces": [
|
|
27
|
-
"framework-mcp"
|
|
27
|
+
"framework-mcp",
|
|
28
|
+
"apps/*"
|
|
28
29
|
],
|
|
29
30
|
"bin": {
|
|
30
31
|
"agent-enderun": "bin/cli.js"
|
|
@@ -48,7 +49,6 @@
|
|
|
48
49
|
"enderun:verify": "agent-enderun verify-contract",
|
|
49
50
|
"enderun:update-contract": "agent-enderun update-contract",
|
|
50
51
|
"enderun:check": "agent-enderun check",
|
|
51
|
-
"enderun:dashboard": "node bin/enderun-dashboard.js",
|
|
52
52
|
"test:init": "node dist/src/cli/index.js init",
|
|
53
53
|
"enderun:lint": "eslint .",
|
|
54
54
|
"lint": "eslint .",
|
|
@@ -66,12 +66,12 @@
|
|
|
66
66
|
"eslint": "^10.4.0",
|
|
67
67
|
"globals": "^17.6.0",
|
|
68
68
|
"tsx": "^4.22.3",
|
|
69
|
-
"typescript": "^5.
|
|
69
|
+
"typescript": "^5.9.3",
|
|
70
70
|
"typescript-eslint": "^8.0.0",
|
|
71
71
|
"vitest": "^3.0.0"
|
|
72
72
|
},
|
|
73
73
|
"enderun": {
|
|
74
|
-
"version": "1.11.
|
|
74
|
+
"version": "1.11.5",
|
|
75
75
|
"initializedAt": "2026-06-04T18:51:28.970Z",
|
|
76
76
|
"frameworkDir": ".enderun"
|
|
77
77
|
},
|
|
@@ -20,7 +20,8 @@ export function scaffoldAgents(
|
|
|
20
20
|
dryRun: boolean,
|
|
21
21
|
agentsToScaffold?: string[],
|
|
22
22
|
explicitDestDir?: string,
|
|
23
|
-
explicitExt?: string
|
|
23
|
+
explicitExt?: string,
|
|
24
|
+
paths?: Record<string, string>
|
|
24
25
|
): void {
|
|
25
26
|
const adapter = ADAPTERS[adapterId];
|
|
26
27
|
if (!adapter) return;
|
|
@@ -46,27 +47,27 @@ export function scaffoldAgents(
|
|
|
46
47
|
|
|
47
48
|
switch (adapterId) {
|
|
48
49
|
case "gemini":
|
|
49
|
-
content = toGeminiCliMd(agent, baseKnowledgeDir);
|
|
50
|
+
content = toGeminiCliMd(agent, baseKnowledgeDir, paths);
|
|
50
51
|
break;
|
|
51
52
|
case "grok":
|
|
52
53
|
// Grok uses same Gemini-compatible YAML format
|
|
53
|
-
content = toGeminiCliMd(agent, baseKnowledgeDir);
|
|
54
|
+
content = toGeminiCliMd(agent, baseKnowledgeDir, paths);
|
|
54
55
|
break;
|
|
55
56
|
case "claude":
|
|
56
|
-
content = toClaudeCodeMd(agent, baseKnowledgeDir);
|
|
57
|
+
content = toClaudeCodeMd(agent, baseKnowledgeDir, paths);
|
|
57
58
|
break;
|
|
58
59
|
case "cursor":
|
|
59
|
-
content = toCursorMdc(agent, baseKnowledgeDir);
|
|
60
|
+
content = toCursorMdc(agent, baseKnowledgeDir, paths);
|
|
60
61
|
break;
|
|
61
62
|
case "codex":
|
|
62
|
-
content = toCodexMd(agent, baseKnowledgeDir);
|
|
63
|
+
content = toCodexMd(agent, baseKnowledgeDir, paths);
|
|
63
64
|
break;
|
|
64
65
|
case "antigravity-cli": {
|
|
65
66
|
// Antigravity uses nested folders: agents/{name}/agent.json and agents/{name}/agent.md
|
|
66
67
|
const agentDir = path.join(destAgentsDir, agent.name);
|
|
67
68
|
if (!dryRun) fs.mkdirSync(agentDir, { recursive: true });
|
|
68
69
|
|
|
69
|
-
content = toAntigravityJson(agent, baseKnowledgeDir);
|
|
70
|
+
content = toAntigravityJson(agent, baseKnowledgeDir, paths);
|
|
70
71
|
fileName = path.join(agent.name, "agent.json");
|
|
71
72
|
|
|
72
73
|
secondaryContent = `# šļø Agent Enderun ā @${agent.name}\n\n${agent.instructions.identity}\n\n${agent.instructions.mission}`;
|
|
@@ -75,7 +76,7 @@ export function scaffoldAgents(
|
|
|
75
76
|
}
|
|
76
77
|
default:
|
|
77
78
|
// Fallback to Gemini format
|
|
78
|
-
content = toGeminiCliMd(agent, baseKnowledgeDir);
|
|
79
|
+
content = toGeminiCliMd(agent, baseKnowledgeDir, paths);
|
|
79
80
|
break;
|
|
80
81
|
}
|
|
81
82
|
|
|
@@ -2,7 +2,7 @@ import fs from "fs";
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { execSync } from "child_process";
|
|
4
4
|
|
|
5
|
-
import { getFrameworkDir, getMemoryPath, getConfiguredPaths, isFrameworkDevelopmentRepo } from "../utils/memory.js";
|
|
5
|
+
import { getFrameworkDir, getMemoryPath, getConfiguredPaths, isFrameworkDevelopmentRepo, readState } from "../utils/memory.js";
|
|
6
6
|
import { getPackageVersion, getValidatorPath } from "../utils/pkg.js";
|
|
7
7
|
import { UI } from "../utils/ui.js";
|
|
8
8
|
import { logger } from "../../shared/logger.js";
|
|
@@ -10,16 +10,41 @@ import { scanProjectCompliance } from "../utils/compliance.js";
|
|
|
10
10
|
import { ALL_AGENTS } from "../../modules/agents/definitions.js";
|
|
11
11
|
import { detectActiveAgentLayouts, findAgentInstruction } from "../adapters/paths.js";
|
|
12
12
|
|
|
13
|
+
import { verifyApiContractCommand } from "./contract.js";
|
|
14
|
+
|
|
13
15
|
export async function checkCommand() {
|
|
14
16
|
UI.intent("Agent Enderun Health Check", `Checking system health and discipline rules (v${getPackageVersion()})...`);
|
|
15
17
|
let issues = 0;
|
|
16
18
|
|
|
19
|
+
const state = readState();
|
|
20
|
+
if (!state) {
|
|
21
|
+
UI.error("Memory state not found. Run 'init' first.");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
17
25
|
const frameworkDir = getFrameworkDir();
|
|
18
26
|
const memoryPath = getMemoryPath();
|
|
19
27
|
const pathsMap = getConfiguredPaths();
|
|
20
28
|
|
|
21
29
|
UI.success(`Using framework dir: ${frameworkDir}`);
|
|
22
30
|
|
|
31
|
+
// --- Contract Gate (Phase 2+ Discipline) ---
|
|
32
|
+
if (["PHASE_2", "PHASE_3", "PHASE_4"].includes(state.phase)) {
|
|
33
|
+
console.warn("\nš Validating API Contracts (Phase 2+ Required)...");
|
|
34
|
+
try {
|
|
35
|
+
const contractOk = await verifyApiContractCommand({ silent: true });
|
|
36
|
+
if (!contractOk) {
|
|
37
|
+
UI.error("Contract verification FAILED! You cannot be in Phase 2+ with unverified contracts.");
|
|
38
|
+
issues++;
|
|
39
|
+
} else {
|
|
40
|
+
UI.success("API Contracts verified and sealed.");
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
UI.error("Error during contract verification.");
|
|
44
|
+
issues++;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
23
48
|
let knowledgeDir = "knowledge";
|
|
24
49
|
if (frameworkDir === ".github") knowledgeDir = "instructions";
|
|
25
50
|
|
|
@@ -57,7 +82,6 @@ export async function checkCommand() {
|
|
|
57
82
|
{ name: ".env.example", path: ".env.example" },
|
|
58
83
|
...(isFrameworkDevelopment ? [{ name: "MCP Server", path: "framework-mcp/package.json" }] : []),
|
|
59
84
|
{ name: "Panda CSS Config", path: appPandaPath, optional: true },
|
|
60
|
-
{ name: "Brain Dashboard", path: path.join(frameworkDir, "BRAIN_DASHBOARD.md") },
|
|
61
85
|
];
|
|
62
86
|
|
|
63
87
|
for (const check of checks) {
|
|
@@ -135,52 +159,6 @@ export async function checkCommand() {
|
|
|
135
159
|
logger.debug("npx tsc --noEmit check failed", err);
|
|
136
160
|
}
|
|
137
161
|
|
|
138
|
-
interface MetricEntry {
|
|
139
|
-
timestamp: string;
|
|
140
|
-
agent: string;
|
|
141
|
-
action: string;
|
|
142
|
-
estimatedTokens: number;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const metricsPath = path.join(frameworkDir, "observability/metrics.json");
|
|
146
|
-
if (fs.existsSync(metricsPath)) {
|
|
147
|
-
try {
|
|
148
|
-
const raw = fs.readFileSync(metricsPath, "utf8");
|
|
149
|
-
const metrics: MetricEntry[] = JSON.parse(raw);
|
|
150
|
-
if (Array.isArray(metrics) && metrics.length > 0) {
|
|
151
|
-
console.warn("\nš Token Consumption Report:");
|
|
152
|
-
let totalTokens = 0;
|
|
153
|
-
const breakdown: Record<string, { count: number; tokens: number }> = {};
|
|
154
|
-
|
|
155
|
-
for (const entry of metrics) {
|
|
156
|
-
const tokens = typeof entry.estimatedTokens === "number" ? entry.estimatedTokens : 0;
|
|
157
|
-
totalTokens += tokens;
|
|
158
|
-
|
|
159
|
-
const actionName = (entry.action || "unknown").split(":")[0].trim();
|
|
160
|
-
if (!breakdown[actionName]) {
|
|
161
|
-
breakdown[actionName] = { count: 0, tokens: 0 };
|
|
162
|
-
}
|
|
163
|
-
breakdown[actionName].count += 1;
|
|
164
|
-
breakdown[actionName].tokens += tokens;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
console.warn(` - Total Operations: ${metrics.length}`);
|
|
168
|
-
console.warn(` - Total Estimated Tokens: ${totalTokens}`);
|
|
169
|
-
console.warn("\n Breakdown by Action:");
|
|
170
|
-
for (const [action, data] of Object.entries(breakdown)) {
|
|
171
|
-
console.warn(` - ${action}: ${data.count} ops (${data.tokens} tokens)`);
|
|
172
|
-
}
|
|
173
|
-
} else {
|
|
174
|
-
console.warn("\nš Token Consumption Report: No token usage logged yet.");
|
|
175
|
-
}
|
|
176
|
-
} catch (err) {
|
|
177
|
-
console.warn("\nš Token Consumption Report: Could not read or parse metrics file.");
|
|
178
|
-
logger.debug("Failed to read or parse metrics.json in checkCommand", err);
|
|
179
|
-
}
|
|
180
|
-
} else {
|
|
181
|
-
console.warn("\nš Token Consumption Report: No token usage logged yet.");
|
|
182
|
-
}
|
|
183
|
-
|
|
184
162
|
if (issues === 0) {
|
|
185
163
|
UI.success("\nš All systems green! Agent Enderun is ready.");
|
|
186
164
|
} else {
|
|
@@ -7,13 +7,14 @@ import { ValidationError } from "../../shared/errors.js";
|
|
|
7
7
|
/**
|
|
8
8
|
* Verify type safety between backend and frontend contracts.
|
|
9
9
|
*/
|
|
10
|
-
export async function verifyApiContractCommand() {
|
|
10
|
+
export async function verifyApiContractCommand(options: { silent?: boolean } = {}): Promise<boolean> {
|
|
11
11
|
const projectRoot = process.cwd();
|
|
12
12
|
const pathsMap = getConfiguredPaths();
|
|
13
13
|
const sharedDir = path.join(projectRoot, pathsMap.backend, "src/types");
|
|
14
14
|
const contractPath = path.join(projectRoot, pathsMap.backend, "contract.version.json");
|
|
15
15
|
|
|
16
16
|
if (!fs.existsSync(sharedDir) || !fs.existsSync(contractPath)) {
|
|
17
|
+
if (options.silent) return false;
|
|
17
18
|
throw new ValidationError(
|
|
18
19
|
"API types or contract version file missing.",
|
|
19
20
|
null,
|
|
@@ -25,8 +26,10 @@ export async function verifyApiContractCommand() {
|
|
|
25
26
|
const contract = JSON.parse(fs.readFileSync(contractPath, "utf8"));
|
|
26
27
|
|
|
27
28
|
if (contract.contract_hash === currentHash) {
|
|
28
|
-
console.warn("ā
Contract is valid and synchronized.");
|
|
29
|
+
if (!options.silent) console.warn("ā
Contract is valid and synchronized.");
|
|
30
|
+
return true;
|
|
29
31
|
} else {
|
|
32
|
+
if (options.silent) return false;
|
|
30
33
|
throw new ValidationError(
|
|
31
34
|
"Contract drift detected!",
|
|
32
35
|
{ stored: contract.contract_hash, actual: currentHash },
|
|
@@ -45,7 +45,7 @@ export function scaffoldFrameworkConfigs(
|
|
|
45
45
|
adapter: AdapterConfig,
|
|
46
46
|
dryRun: boolean,
|
|
47
47
|
selectedPalette: string,
|
|
48
|
-
options?: { unified?: boolean; adapters?: string[] }
|
|
48
|
+
options?: { unified?: boolean; adapters?: string[]; profile?: string }
|
|
49
49
|
) {
|
|
50
50
|
if (dryRun) return;
|
|
51
51
|
const frameworkDir = path.join(targetDir, fDir);
|
|
@@ -55,6 +55,7 @@ export function scaffoldFrameworkConfigs(
|
|
|
55
55
|
name: FRAMEWORK_NAME,
|
|
56
56
|
version: getPackageVersion(),
|
|
57
57
|
unified: options?.unified || false,
|
|
58
|
+
profile: options?.profile || "full",
|
|
58
59
|
adapters: options?.adapters || [adapter.id],
|
|
59
60
|
theme: {
|
|
60
61
|
palette: selectedPalette,
|
|
@@ -77,10 +78,25 @@ export function scaffoldFrameworkConfigs(
|
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
let statusContent = "# šļø Status\n\n| Agent | State | Active Task | Last Updated | Notes | Extra | Backup |\n|---|---|---|---|---|---|---|\n";
|
|
80
|
-
|
|
81
|
+
|
|
82
|
+
const liteAgents = ["manager", "architect", "frontend"];
|
|
83
|
+
const activeAgents = (options?.profile === "lite")
|
|
84
|
+
? ALL_AGENTS.filter(ag => liteAgents.includes(ag.name))
|
|
85
|
+
: ALL_AGENTS;
|
|
86
|
+
|
|
87
|
+
const initialStatusJson: Record<string, { state: string; task: string; lastUpdated: string }> = {};
|
|
88
|
+
const now = new Date().toISOString();
|
|
89
|
+
|
|
90
|
+
for (const ag of activeAgents) {
|
|
81
91
|
statusContent += "| @" + ag.name + " | READY | Idle | - | - | - | - |\n";
|
|
92
|
+
initialStatusJson[ag.name] = { state: "READY", task: "Idle", lastUpdated: now };
|
|
82
93
|
}
|
|
83
94
|
writeTextFile(path.join(frameworkDir, "STATUS.md"), statusContent);
|
|
95
|
+
|
|
96
|
+
// Populate initial status.json
|
|
97
|
+
const statusJsonPath = path.join(frameworkDir, "memory", "status.json");
|
|
98
|
+
if (!fs.existsSync(path.join(frameworkDir, "memory"))) fs.mkdirSync(path.join(frameworkDir, "memory"), { recursive: true });
|
|
99
|
+
writeJsonFile(statusJsonPath, initialStatusJson, dryRun);
|
|
84
100
|
}
|
|
85
101
|
|
|
86
102
|
export function scaffoldShims(
|
|
@@ -48,9 +48,6 @@ export function scaffoldOps(frameworkDir: string, dryRun: boolean) {
|
|
|
48
48
|
};
|
|
49
49
|
writeJsonFile(path.join(frameworkDir, "cli-commands.json"), cliCommands, dryRun);
|
|
50
50
|
|
|
51
|
-
// Scaffold BRAIN_DASHBOARD.md
|
|
52
|
-
writeTextFile(path.join(frameworkDir, "BRAIN_DASHBOARD.md"), "# š§ Brain Dashboard\n\nActive Trace: T-000\nActive Phase: PHASE_0 (Genesis)\n\n## Agent Statuses\n- @manager: READY\n- @architect: READY\n- @backend: READY\n- @frontend: READY\n- @quality: READY\n");
|
|
53
|
-
|
|
54
51
|
// Scaffold router/routing_rules.md
|
|
55
52
|
const routerDir = path.join(frameworkDir, "router");
|
|
56
53
|
if (!fs.existsSync(routerDir)) fs.mkdirSync(routerDir, { recursive: true });
|