agent-enderun 0.0.1
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/PROJECT_MEMORY.md +75 -0
- package/.enderun/agents/analyst.md +193 -0
- package/.enderun/agents/backend.md +253 -0
- package/.enderun/agents/explorer.md +99 -0
- package/.enderun/agents/frontend.md +164 -0
- package/.enderun/agents/git.md +95 -0
- package/.enderun/agents/manager.md +180 -0
- package/.enderun/agents/mobile.md +63 -0
- package/.enderun/agents/native.md +62 -0
- package/.enderun/docs/api/README.md +41 -0
- package/CHANGELOG.md +24 -0
- package/ENDERUN.md +72 -0
- package/README.md +293 -0
- package/bin/cli.js +863 -0
- package/package.json +11 -0
- package/packages/framework-mcp/README.md +95 -0
- package/packages/shared-types/README.md +84 -0
- package/pnpm-workspace.yaml +3 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,863 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
console.log("🤖 Agent Enderun CLI — Initializing...");
|
|
3
|
+
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
import crypto from "crypto";
|
|
8
|
+
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(__filename);
|
|
11
|
+
const sourceDir = path.join(__dirname, "..");
|
|
12
|
+
const targetDir = process.cwd();
|
|
13
|
+
|
|
14
|
+
// --- CONSTANTS ---
|
|
15
|
+
const FRAMEWORK_VERSION = getPackageVersion();
|
|
16
|
+
|
|
17
|
+
// --- HELPER FUNCTIONS ---
|
|
18
|
+
|
|
19
|
+
function getPackageVersion() {
|
|
20
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(sourceDir, "package.json"), "utf8"));
|
|
21
|
+
return pkg.version;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getFrameworkDir() {
|
|
25
|
+
const adapters = [".gemini", ".claude", ".cursor", ".codex", ".enderun"];
|
|
26
|
+
for (const adp of adapters) {
|
|
27
|
+
const fullPath = path.join(targetDir, adp);
|
|
28
|
+
if (fs.existsSync(fullPath) && fs.lstatSync(fullPath).isDirectory()) {
|
|
29
|
+
return adp;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return ".enderun";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getMemoryPath() {
|
|
36
|
+
return path.join(targetDir, getFrameworkDir(), "PROJECT_MEMORY.md");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function generateULID(seedTime = Date.now()) {
|
|
40
|
+
const ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
41
|
+
const ENCODING_LEN = ENCODING.length;
|
|
42
|
+
let time = seedTime;
|
|
43
|
+
const timeChars = new Array(10);
|
|
44
|
+
for (let i = 9; i >= 0; i--) {
|
|
45
|
+
timeChars[i] = ENCODING.charAt(time % ENCODING_LEN);
|
|
46
|
+
time = Math.floor(time / ENCODING_LEN);
|
|
47
|
+
}
|
|
48
|
+
const randomChars = new Array(16);
|
|
49
|
+
for (let i = 0; i < 16; i++) {
|
|
50
|
+
randomChars[i] = ENCODING.charAt(Math.floor(Math.random() * ENCODING_LEN));
|
|
51
|
+
}
|
|
52
|
+
return timeChars.join("") + randomChars.join("");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function sleep(ms) {
|
|
56
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function acquireMemoryLock(lockPath, maxRetries = 5) {
|
|
60
|
+
for (let attempt = 0; attempt < maxRetries; attempt += 1) {
|
|
61
|
+
try {
|
|
62
|
+
const fd = fs.openSync(lockPath, "wx");
|
|
63
|
+
fs.closeSync(fd);
|
|
64
|
+
return true;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
if (error?.code !== "EEXIST") throw error;
|
|
67
|
+
if (attempt < maxRetries - 1) sleep(1000);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function releaseMemoryLock(lockPath) {
|
|
74
|
+
if (fs.existsSync(lockPath)) fs.unlinkSync(lockPath);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function insertTaskRow(memoryContent, row) {
|
|
78
|
+
const sectionHeader = "## ACTIVE TASKS";
|
|
79
|
+
const tableDivider = "| :--- | :--- | :--- | :--- | :--- |";
|
|
80
|
+
const sectionIndex = memoryContent.indexOf(sectionHeader);
|
|
81
|
+
if (sectionIndex === -1) return null;
|
|
82
|
+
const dividerIndex = memoryContent.indexOf(tableDivider, sectionIndex);
|
|
83
|
+
if (dividerIndex === -1) return null;
|
|
84
|
+
const dividerLineEnd = memoryContent.indexOf("\n", dividerIndex);
|
|
85
|
+
if (dividerLineEnd === -1) return null;
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
memoryContent.slice(0, dividerLineEnd + 1) +
|
|
89
|
+
`${row}\n` +
|
|
90
|
+
memoryContent.slice(dividerLineEnd + 1)
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function sanitizeTableCell(value) {
|
|
95
|
+
return String(value).replace(/\|/g, "\\|").replace(/\r?\n/g, " ").trim();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function normalizeAgentName(agent) {
|
|
99
|
+
return String(agent || "manager").replace(/^@+/, "").trim() || "manager";
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function normalizePriority(priority) {
|
|
103
|
+
const normalized = String(priority || "P2").toUpperCase().trim();
|
|
104
|
+
return /^P[0-3]$/.test(normalized) ? normalized : "P2";
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function mergePackageJson(targetPath, sourcePath) {
|
|
108
|
+
let targetPkg = {};
|
|
109
|
+
if (fs.existsSync(targetPath)) {
|
|
110
|
+
try {
|
|
111
|
+
targetPkg = JSON.parse(fs.readFileSync(targetPath, "utf8"));
|
|
112
|
+
} catch (e) {
|
|
113
|
+
console.warn("⚠️ Could not parse existing package.json, creating a new one.");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const sourcePkg = JSON.parse(fs.readFileSync(sourcePath, "utf8"));
|
|
118
|
+
|
|
119
|
+
const sanitizeDeps = (deps) => {
|
|
120
|
+
if (!deps) return deps;
|
|
121
|
+
const cleaned = {};
|
|
122
|
+
for (const [name, version] of Object.entries(deps)) {
|
|
123
|
+
cleaned[name] = (typeof version === "string" && version.startsWith("workspace:")) ? "*" : version;
|
|
124
|
+
}
|
|
125
|
+
return cleaned;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
targetPkg.dependencies = sanitizeDeps({
|
|
129
|
+
...targetPkg.dependencies,
|
|
130
|
+
...sourcePkg.dependencies
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Merge scripts
|
|
134
|
+
targetPkg.scripts = {
|
|
135
|
+
...targetPkg.scripts,
|
|
136
|
+
"enderun:status": "agent-enderun status",
|
|
137
|
+
"enderun:trace": "agent-enderun trace:new",
|
|
138
|
+
"enderun:verify": "agent-enderun verify-contract",
|
|
139
|
+
"enderun:build": "npm run build --prefix packages/shared-types && npm run build --prefix packages/framework-mcp",
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
targetPkg.devDependencies = sanitizeDeps({
|
|
143
|
+
...targetPkg.devDependencies,
|
|
144
|
+
"@modelcontextprotocol/sdk": sourcePkg.devDependencies["@modelcontextprotocol/sdk"],
|
|
145
|
+
"zod": sourcePkg.devDependencies["zod"],
|
|
146
|
+
"ts-morph": sourcePkg.devDependencies["ts-morph"],
|
|
147
|
+
"typescript": sourcePkg.devDependencies["typescript"],
|
|
148
|
+
"@types/node": sourcePkg.devDependencies["@types/node"],
|
|
149
|
+
"tsx": sourcePkg.devDependencies["tsx"]
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
if (targetPkg.peerDependencies) targetPkg.peerDependencies = sanitizeDeps(targetPkg.peerDependencies);
|
|
153
|
+
if (targetPkg.optionalDependencies) targetPkg.optionalDependencies = sanitizeDeps(targetPkg.optionalDependencies);
|
|
154
|
+
|
|
155
|
+
// Ensure basic fields
|
|
156
|
+
if (!targetPkg.name) targetPkg.name = path.basename(process.cwd());
|
|
157
|
+
if (!targetPkg.version) targetPkg.version = "0.1.0";
|
|
158
|
+
if (!targetPkg.type) targetPkg.type = "module";
|
|
159
|
+
|
|
160
|
+
// Add workspaces if it's a new project or doesn't have them
|
|
161
|
+
if (!targetPkg.workspaces && !fs.existsSync(path.join(process.cwd(), "pnpm-workspace.yaml"))) {
|
|
162
|
+
targetPkg.workspaces = ["packages/*"];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Add metadata
|
|
166
|
+
targetPkg.enderun = {
|
|
167
|
+
version: sourcePkg.version,
|
|
168
|
+
initializedAt: new Date().toISOString(),
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
fs.writeFileSync(targetPath, JSON.stringify(targetPkg, null, 2));
|
|
172
|
+
console.log("✅ package.json updated with Enderun scripts and dependencies.");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function updateGitIgnore(targetPath, frameworkDir = ".enderun") {
|
|
176
|
+
const IGNORE_LINES = [
|
|
177
|
+
"# AI-Enderun",
|
|
178
|
+
".gemini/logs/*.json",
|
|
179
|
+
".claude/logs/*.json",
|
|
180
|
+
".cursor/logs/*.json",
|
|
181
|
+
".codex/logs/*.json",
|
|
182
|
+
".enderun/logs/*.json",
|
|
183
|
+
".gemini/*.lock",
|
|
184
|
+
".claude/*.lock",
|
|
185
|
+
".cursor/*.lock",
|
|
186
|
+
".codex/*.lock",
|
|
187
|
+
".enderun/*.lock",
|
|
188
|
+
".env",
|
|
189
|
+
".DS_Store"
|
|
190
|
+
];
|
|
191
|
+
|
|
192
|
+
let content = "";
|
|
193
|
+
if (fs.existsSync(targetPath)) {
|
|
194
|
+
content = fs.readFileSync(targetPath, "utf8");
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const lines = content.split("\n").map((l) => l.trim());
|
|
198
|
+
let added = false;
|
|
199
|
+
|
|
200
|
+
for (const line of IGNORE_LINES) {
|
|
201
|
+
if (!lines.includes(line)) {
|
|
202
|
+
content += (content.endsWith("\n") || content === "" ? "" : "\n") + line + "\n";
|
|
203
|
+
added = true;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (added) {
|
|
208
|
+
fs.writeFileSync(targetPath, content);
|
|
209
|
+
console.log("✅ .gitignore updated.");
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Create initial PROJECT_MEMORY.md if missing.
|
|
215
|
+
*/
|
|
216
|
+
function initializeMemory(memoryPath, targetBase) {
|
|
217
|
+
if (fs.existsSync(memoryPath)) return;
|
|
218
|
+
|
|
219
|
+
const traceId = generateULID();
|
|
220
|
+
const date = new Date().toISOString().split("T")[0];
|
|
221
|
+
const template = `# PROJECT MEMORY — Agent Enderun
|
|
222
|
+
|
|
223
|
+
This file is the Single Source of Truth (SSOT) and the persistent memory of the project.
|
|
224
|
+
|
|
225
|
+
## CURRENT STATUS
|
|
226
|
+
|
|
227
|
+
| Active Phase | Profile | Last Update | Active Trace ID | Blockers |
|
|
228
|
+
| :----------- | :------ | :---------- | :-------------- | :------- |
|
|
229
|
+
| PHASE_0 | Lightweight | ${date} | ${traceId} | NONE |
|
|
230
|
+
|
|
231
|
+
## PROJECT DEFINITION
|
|
232
|
+
|
|
233
|
+
| Field | Value |
|
|
234
|
+
| :--- | :--- |
|
|
235
|
+
| Project Name | ${path.basename(process.cwd())} |
|
|
236
|
+
| Platform | Not defined |
|
|
237
|
+
| Frontend | React 19 + Vite + Panda CSS |
|
|
238
|
+
| Backend | Node.js 20+ + Fastify |
|
|
239
|
+
| DB | PostgreSQL |
|
|
240
|
+
|
|
241
|
+
## DOD STATUS
|
|
242
|
+
|
|
243
|
+
| Phase | Status | Note |
|
|
244
|
+
| :--- | :--- | :--- |
|
|
245
|
+
| PHASE_0 | IN_PROGRESS | Initializing project structure |
|
|
246
|
+
| PHASE_1 | PENDING | |
|
|
247
|
+
| PHASE_2 | PENDING | |
|
|
248
|
+
| PHASE_3 | PENDING | |
|
|
249
|
+
| PHASE_4 | PENDING | |
|
|
250
|
+
|
|
251
|
+
## CRITICAL DECISIONS
|
|
252
|
+
|
|
253
|
+
| Date | Decision | Rationale | Agent |
|
|
254
|
+
| :--- | :--- | :--- | :--- |
|
|
255
|
+
| ${date} | Project Initialized | Framework setup via CLI | @manager |
|
|
256
|
+
|
|
257
|
+
## DELIVERABLES
|
|
258
|
+
|
|
259
|
+
| Module | Status | Agent | Date |
|
|
260
|
+
| :--- | :--- | :--- | :--- |
|
|
261
|
+
|
|
262
|
+
## ACTIVE TASKS
|
|
263
|
+
|
|
264
|
+
| Trace ID | Task | Agent | Priority | Status |
|
|
265
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
266
|
+
| ${traceId} | Framework setup and architecture alignment | @manager | P1 | IN_PROGRESS |
|
|
267
|
+
|
|
268
|
+
## HISTORY (Persistent Memory)
|
|
269
|
+
|
|
270
|
+
### ${date} — Framework Initialization
|
|
271
|
+
|
|
272
|
+
- **Agent:** @manager
|
|
273
|
+
- **Trace ID:** ${traceId}
|
|
274
|
+
- **Action:** Initialized Agent Enderun framework and project structure.
|
|
275
|
+
`;
|
|
276
|
+
|
|
277
|
+
const finalTemplate = template.replace(/\{\{FRAMEWORK_DIR\}\}/g, targetBase);
|
|
278
|
+
fs.writeFileSync(memoryPath, finalTemplate);
|
|
279
|
+
console.log(`✅ PROJECT_MEMORY.md initialized in ${targetBase}`);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// --- COMMANDS ---
|
|
283
|
+
|
|
284
|
+
function getPackageManager() {
|
|
285
|
+
const override = process.env.AGENT_ENDERUN_PACKAGE_MANAGER || process.env.AGENT_ENDERUN_PM || process.env.AI_ENDERUN_PACKAGE_MANAGER || process.env.AI_ENDERUN_PM;
|
|
286
|
+
if (override) return override.toLowerCase();
|
|
287
|
+
|
|
288
|
+
const userAgent = process.env.npm_config_user_agent || "";
|
|
289
|
+
if (userAgent.includes("pnpm")) return "pnpm";
|
|
290
|
+
if (userAgent.includes("yarn")) return "yarn";
|
|
291
|
+
if (userAgent.includes("npm")) return "npm";
|
|
292
|
+
|
|
293
|
+
const npmExecPath = process.env.npm_execpath || "";
|
|
294
|
+
if (npmExecPath.includes("pnpm")) return "pnpm";
|
|
295
|
+
if (npmExecPath.includes("yarn")) return "yarn";
|
|
296
|
+
if (npmExecPath.includes("npm")) return "npm";
|
|
297
|
+
|
|
298
|
+
if (fs.existsSync(path.join(process.cwd(), "pnpm-lock.yaml"))) return "pnpm";
|
|
299
|
+
if (fs.existsSync(path.join(process.cwd(), "yarn.lock")) || fs.existsSync(path.join(process.cwd(), "yarn.lock.json"))) return "yarn";
|
|
300
|
+
return "npm";
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Scaffold the framework into the target project.
|
|
305
|
+
*/
|
|
306
|
+
async function initCommand(selectedAdapter) {
|
|
307
|
+
const ADAPTERS = {
|
|
308
|
+
gemini: ["GEMINI.md", "gemini-extension.json"],
|
|
309
|
+
claude: ["CLAUDE.md"],
|
|
310
|
+
cursor: ["CURSOR.md"],
|
|
311
|
+
codex: ["CODEX.md"],
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const targetBase = selectedAdapter ? `.${selectedAdapter}` : ".enderun";
|
|
315
|
+
const targetFrameworkDir = path.join(targetDir, targetBase);
|
|
316
|
+
|
|
317
|
+
const CORE_FILES = [
|
|
318
|
+
".enderun",
|
|
319
|
+
"docs",
|
|
320
|
+
"mcp.json",
|
|
321
|
+
".env.example",
|
|
322
|
+
"ENDERUN.md",
|
|
323
|
+
"packages/framework-mcp",
|
|
324
|
+
"packages/shared-types",
|
|
325
|
+
];
|
|
326
|
+
|
|
327
|
+
const DIRS_TO_CREATE = [
|
|
328
|
+
`${targetBase}/agents`,
|
|
329
|
+
`${targetBase}/docs/api`,
|
|
330
|
+
`${targetBase}/logs`,
|
|
331
|
+
"apps/web",
|
|
332
|
+
"apps/backend",
|
|
333
|
+
"docs",
|
|
334
|
+
"packages/shared-types",
|
|
335
|
+
"packages/framework-mcp",
|
|
336
|
+
];
|
|
337
|
+
|
|
338
|
+
console.log("🚀 Installing Agent Enderun (Smart Mode)...");
|
|
339
|
+
|
|
340
|
+
// Ensure target framework base exists
|
|
341
|
+
if (!fs.existsSync(targetFrameworkDir)) {
|
|
342
|
+
fs.mkdirSync(targetFrameworkDir, { recursive: true });
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Create subdirectories
|
|
346
|
+
for (const dir of DIRS_TO_CREATE) {
|
|
347
|
+
const fullPath = path.join(targetDir, dir);
|
|
348
|
+
if (!fs.existsSync(fullPath)) {
|
|
349
|
+
fs.mkdirSync(fullPath, { recursive: true });
|
|
350
|
+
console.log(`📂 Created directory: ${dir}`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
let filesToProcess = [...CORE_FILES];
|
|
355
|
+
|
|
356
|
+
if (selectedAdapter) {
|
|
357
|
+
if (!ADAPTERS[selectedAdapter]) {
|
|
358
|
+
console.error(`❌ Invalid adapter: ${selectedAdapter}. Available: gemini, claude, cursor, codex`);
|
|
359
|
+
process.exit(1);
|
|
360
|
+
}
|
|
361
|
+
filesToProcess = [...CORE_FILES, ...ADAPTERS[selectedAdapter]];
|
|
362
|
+
} else {
|
|
363
|
+
Object.values(ADAPTERS).forEach(list => filesToProcess.push(...list));
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
for (const item of filesToProcess) {
|
|
367
|
+
const src = path.join(sourceDir, item);
|
|
368
|
+
let dest = path.join(targetDir, item);
|
|
369
|
+
|
|
370
|
+
// Remap core framework files to targetBase
|
|
371
|
+
if (item === ".enderun") dest = targetFrameworkDir;
|
|
372
|
+
if (item === "ENDERUN.md") dest = path.join(targetFrameworkDir, "ENDERUN.md");
|
|
373
|
+
if (ADAPTERS[selectedAdapter]?.includes(item)) {
|
|
374
|
+
dest = path.join(targetDir, item); // Keep adapter linker files in root
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (fs.existsSync(src)) {
|
|
378
|
+
if (fs.lstatSync(src).isDirectory()) {
|
|
379
|
+
// When copying framework dir, skip logs and project-specific state
|
|
380
|
+
const skipFiles = (item === ".enderun") ? ["logs", "PROJECT_MEMORY.md", "BRAIN_DASHBOARD.md", "PROJECT_MEMORY.lock"] : [];
|
|
381
|
+
const isDocs = item === "docs";
|
|
382
|
+
copyDir(src, dest, new Set(skipFiles), isDocs, targetBase);
|
|
383
|
+
} else {
|
|
384
|
+
// Special files handling
|
|
385
|
+
if (item === "package.json") continue;
|
|
386
|
+
if (item === "ENDERUN.md" && fs.existsSync(dest)) {
|
|
387
|
+
console.log(`ℹ️ Skipping ENDERUN.md (already exists in ${targetBase}).`);
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (fs.existsSync(dest)) {
|
|
392
|
+
console.log(`ℹ️ Skipping existing file: ${item}`);
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const ext = path.extname(item);
|
|
397
|
+
const textExtensions = [".md", ".json", ".js", ".ts", ".txt", ""];
|
|
398
|
+
if (textExtensions.includes(ext)) {
|
|
399
|
+
let content = fs.readFileSync(src, "utf8");
|
|
400
|
+
content = content.replace(/\{\{FRAMEWORK_DIR\}\}/g, targetBase);
|
|
401
|
+
fs.writeFileSync(dest, content);
|
|
402
|
+
} else {
|
|
403
|
+
fs.copyFileSync(src, dest);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
console.log(`✅ ${item} processed -> ${path.relative(targetDir, dest)}`);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Smart setup
|
|
411
|
+
mergePackageJson(path.join(targetDir, "package.json"), path.join(sourceDir, "package.json"));
|
|
412
|
+
updateGitIgnore(path.join(targetDir, ".gitignore"), targetBase);
|
|
413
|
+
|
|
414
|
+
const finalMemoryPath = path.join(targetDir, targetBase, "PROJECT_MEMORY.md");
|
|
415
|
+
initializeMemory(finalMemoryPath, targetBase);
|
|
416
|
+
|
|
417
|
+
// Initialize git if missing
|
|
418
|
+
if (!fs.existsSync(path.join(targetDir, ".git"))) {
|
|
419
|
+
try {
|
|
420
|
+
const { execSync } = await import("child_process");
|
|
421
|
+
execSync("git init", { cwd: targetDir, stdio: "ignore" });
|
|
422
|
+
console.log("✅ Git repository initialized.");
|
|
423
|
+
} catch (e) {
|
|
424
|
+
console.warn("⚠️ Could not initialize git automatically. Please run 'git init' manually.");
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// --- Post-Install Hooks (Smart Setup) ---
|
|
429
|
+
|
|
430
|
+
console.log("\n🛠️ Running smart configuration for adapters...");
|
|
431
|
+
|
|
432
|
+
// Universal Gemini Compatibility (Symlink)
|
|
433
|
+
try {
|
|
434
|
+
const geminiDir = path.join(targetDir, ".gemini");
|
|
435
|
+
const geminiAgentsDir = path.join(geminiDir, "agents");
|
|
436
|
+
const frameworkAgentsDir = path.join(targetDir, targetBase, "agents");
|
|
437
|
+
|
|
438
|
+
if (!fs.existsSync(geminiDir)) {
|
|
439
|
+
fs.mkdirSync(geminiDir, { recursive: true });
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (targetBase !== ".gemini" && !fs.existsSync(geminiAgentsDir)) {
|
|
443
|
+
const relativePath = path.relative(geminiDir, frameworkAgentsDir);
|
|
444
|
+
fs.symlinkSync(relativePath, geminiAgentsDir, "dir");
|
|
445
|
+
console.log(`🔗 Omni-Agent: Created symlink from .gemini/agents to ${targetBase}/agents`);
|
|
446
|
+
}
|
|
447
|
+
} catch (err) {
|
|
448
|
+
// Silently ignore if symlink fails (e.g. on non-compatible OS)
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (selectedAdapter === "claude" || !selectedAdapter) {
|
|
452
|
+
const mcpPath = path.join(targetDir, "packages/framework-mcp/src/index.ts");
|
|
453
|
+
console.log("\n📝 Claude Code Setup:");
|
|
454
|
+
console.log("To enable Agent Enderun tools in Claude Code, run this command:");
|
|
455
|
+
console.log(`\x1b[36mclaude config add framework-mcp npx tsx ${mcpPath}\x1b[0m`);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (selectedAdapter === "cursor" || !selectedAdapter) {
|
|
459
|
+
console.log("✨ Cursor: Adapter CLAUDE.md and ENDERUN.md are ready to guide your AI.");
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const pkgMgr = getPackageManager();
|
|
463
|
+
console.log(`\nℹ️ Detected package manager: ${pkgMgr}`);
|
|
464
|
+
const installCmd = pkgMgr === "npm" ? "npm install" : `${pkgMgr} install`;
|
|
465
|
+
const buildCmd = pkgMgr === "npm" ? "npm run enderun:build" : `${pkgMgr} run enderun:build`;
|
|
466
|
+
|
|
467
|
+
console.log("\n✨ Framework scaffolded! (v" + FRAMEWORK_VERSION + ")");
|
|
468
|
+
|
|
469
|
+
try {
|
|
470
|
+
const { execSync } = await import("child_process");
|
|
471
|
+
|
|
472
|
+
console.log(`\n📦 Step 1/3: Running '${installCmd}'...`);
|
|
473
|
+
execSync(installCmd, { stdio: "inherit" });
|
|
474
|
+
|
|
475
|
+
console.log(`\n🏗️ Step 2/3: Running '${buildCmd}'...`);
|
|
476
|
+
execSync(buildCmd, { stdio: "inherit" });
|
|
477
|
+
|
|
478
|
+
console.log(`\n🔍 Step 3/3: Running 'agent-enderun check'...`);
|
|
479
|
+
// Run the health check logic directly
|
|
480
|
+
await checkHealth();
|
|
481
|
+
|
|
482
|
+
console.log("\n🚀 Agent Enderun is fully installed, built and verified!");
|
|
483
|
+
} catch (error) {
|
|
484
|
+
console.error("\n❌ Automatic installation failed. Please run manually:");
|
|
485
|
+
console.log(`👉 ${installCmd} && ${buildCmd}`);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Check framework health and MCP status.
|
|
491
|
+
*/
|
|
492
|
+
function checkCommand() {
|
|
493
|
+
console.log(`🔍 Checking Agent Enderun Health (v${FRAMEWORK_VERSION})...`);
|
|
494
|
+
let issues = 0;
|
|
495
|
+
|
|
496
|
+
const frameworkDir = getFrameworkDir();
|
|
497
|
+
const checks = [
|
|
498
|
+
{ name: "Constitution (ENDERUN.md)", path: path.join(frameworkDir, "ENDERUN.md") },
|
|
499
|
+
{ name: "Memory (PROJECT_MEMORY.md)", path: path.join(frameworkDir, "PROJECT_MEMORY.md") },
|
|
500
|
+
{ name: "Shared Types", path: "packages/shared-types/package.json" },
|
|
501
|
+
{ name: "MCP Server", path: "packages/framework-mcp/package.json" },
|
|
502
|
+
{ name: "Tech Stack", path: "docs/tech-stack.md" },
|
|
503
|
+
{ name: "Requirements", path: "docs/project-docs.md" },
|
|
504
|
+
{ name: "API Registry", path: path.join(frameworkDir, "docs/api/README.md") },
|
|
505
|
+
];
|
|
506
|
+
|
|
507
|
+
for (const check of checks) {
|
|
508
|
+
if (fs.existsSync(path.join(process.cwd(), check.path))) {
|
|
509
|
+
console.log(`✅ ${check.name} found.`);
|
|
510
|
+
} else {
|
|
511
|
+
console.log(`❌ ${check.name} MISSING! (${check.path})`);
|
|
512
|
+
issues++;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Dependency Check
|
|
517
|
+
const mcpNodeModules = path.join(process.cwd(), "packages/framework-mcp/node_modules");
|
|
518
|
+
const rootNodeModules = path.join(process.cwd(), "node_modules");
|
|
519
|
+
if (!fs.existsSync(mcpNodeModules) && !fs.existsSync(rootNodeModules)) {
|
|
520
|
+
console.log("❌ Dependencies MISSING! (Run 'npm install')");
|
|
521
|
+
issues++;
|
|
522
|
+
} else {
|
|
523
|
+
console.log("✅ Dependencies found.");
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// MCP Build Check
|
|
527
|
+
const mcpPath = path.join(process.cwd(), "packages/framework-mcp/dist/index.js");
|
|
528
|
+
if (!fs.existsSync(mcpPath)) {
|
|
529
|
+
console.log("❌ MCP Build MISSING! (Run 'npm run enderun:build')");
|
|
530
|
+
issues++;
|
|
531
|
+
} else {
|
|
532
|
+
console.log("✅ MCP Build found.");
|
|
533
|
+
console.log("⏳ Testing MCP Server syntax...");
|
|
534
|
+
try {
|
|
535
|
+
execSync(`node --check ${mcpPath}`, { stdio: "pipe" });
|
|
536
|
+
console.log("✅ MCP Server syntax valid.");
|
|
537
|
+
} catch (e) {
|
|
538
|
+
// If --check fails on ESM, we might skip it or use a better check
|
|
539
|
+
console.log("⚠️ MCP Syntax check skipped (ESM/Environment).");
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
if (issues === 0) {
|
|
544
|
+
console.log("\n🚀 All systems green! Agent Enderun is ready for orchestration.");
|
|
545
|
+
} else {
|
|
546
|
+
console.log(`\n⚠️ Found ${issues} issues. Please fix them before starting.`);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
function copyDir(src, dest, skipSet = new Set(), nonDestructive = false, frameworkDir = ".enderun") {
|
|
551
|
+
const DEFAULT_SKIP = new Set(["node_modules", ".git", ".DS_Store"]);
|
|
552
|
+
const actualSkip = new Set([...DEFAULT_SKIP, ...skipSet]);
|
|
553
|
+
|
|
554
|
+
if (!fs.existsSync(dest)) {
|
|
555
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
fs.readdirSync(src, { withFileTypes: true }).forEach(entry => {
|
|
559
|
+
if (actualSkip.has(entry.name)) return;
|
|
560
|
+
|
|
561
|
+
const srcPath = path.join(src, entry.name);
|
|
562
|
+
const destPath = path.join(dest, entry.name);
|
|
563
|
+
|
|
564
|
+
if (entry.isDirectory()) {
|
|
565
|
+
copyDir(srcPath, destPath, skipSet, nonDestructive, frameworkDir);
|
|
566
|
+
} else {
|
|
567
|
+
if (nonDestructive && fs.existsSync(destPath)) {
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
const ext = path.extname(entry.name);
|
|
572
|
+
const textExtensions = [".md", ".json", ".js", ".ts", ".txt", ""];
|
|
573
|
+
|
|
574
|
+
if (textExtensions.includes(ext)) {
|
|
575
|
+
let content = fs.readFileSync(srcPath, "utf8");
|
|
576
|
+
content = content.replace(/\{\{FRAMEWORK_DIR\}\}/g, frameworkDir);
|
|
577
|
+
fs.writeFileSync(destPath, content);
|
|
578
|
+
} else {
|
|
579
|
+
fs.copyFileSync(srcPath, destPath);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Print the current framework status.
|
|
587
|
+
*/
|
|
588
|
+
function statusCommand() {
|
|
589
|
+
const memoryPath = getMemoryPath();
|
|
590
|
+
const frameworkDir = getFrameworkDir();
|
|
591
|
+
if (!fs.existsSync(memoryPath)) {
|
|
592
|
+
console.error(`❌ Error: ${frameworkDir}/PROJECT_MEMORY.md not found. Please run 'init' first.`);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const content = fs.readFileSync(memoryPath, "utf8");
|
|
597
|
+
const statusMatch = content.match(/\| Active Phase \| Profile \| Last Update \| Active Trace ID \| Blockers \|\n\| :----------- \| :------ \| :---------- \| :-------------- \| :------- \|\n\| (.*?) \| (.*?) \| (.*?) \| (.*?) \| (.*?) \|/);
|
|
598
|
+
|
|
599
|
+
console.log("\n📊 --- PROJECT STATUS ---");
|
|
600
|
+
if (statusMatch) {
|
|
601
|
+
console.log(`🔹 Phase: ${statusMatch[1].trim()}`);
|
|
602
|
+
console.log(`🧭 Profile: ${statusMatch[2].trim()}`);
|
|
603
|
+
console.log(`📅 Update: ${statusMatch[3].trim()}`);
|
|
604
|
+
console.log(`🆔 Trace ID: ${statusMatch[4].trim()}`);
|
|
605
|
+
console.log(`⛔ Blockers: ${statusMatch[5].trim()}`);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const tasksSection = content.match(/## ACTIVE TASKS\n\n([\s\S]*?)\n\n##/);
|
|
609
|
+
if (tasksSection) {
|
|
610
|
+
console.log("\n📋 Active Tasks:");
|
|
611
|
+
console.log(tasksSection[1].trim());
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
console.log("\n-----------------------\n");
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Generate a new Trace ID and add it to project memory.
|
|
619
|
+
*/
|
|
620
|
+
function traceNewCommand(description, agent = "manager", priority = "P2") {
|
|
621
|
+
const memoryPath = getMemoryPath();
|
|
622
|
+
if (!fs.existsSync(memoryPath)) {
|
|
623
|
+
console.error("❌ Error: PROJECT_MEMORY.md not found.");
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
const traceId = generateULID();
|
|
628
|
+
const safeDescription = sanitizeTableCell(description);
|
|
629
|
+
const safeAgent = normalizeAgentName(agent);
|
|
630
|
+
const safePriority = normalizePriority(priority);
|
|
631
|
+
const newTask = `| ${traceId} | ${safeDescription} | @${safeAgent} | ${safePriority} | IN_PROGRESS |`;
|
|
632
|
+
const lockPath = `${memoryPath}.lock`;
|
|
633
|
+
|
|
634
|
+
if (!acquireMemoryLock(lockPath)) {
|
|
635
|
+
console.error("❌ Error: Memory lock timeout (5 retries).");
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
try {
|
|
640
|
+
const content = fs.readFileSync(memoryPath, "utf8");
|
|
641
|
+
const updated = insertTaskRow(content, newTask);
|
|
642
|
+
if (!updated) {
|
|
643
|
+
console.error("❌ Error: ACTIVE TASKS table not found, task could not be added.");
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
fs.writeFileSync(memoryPath, updated);
|
|
648
|
+
console.log(`\n✅ New Trace ID created: ${traceId}`);
|
|
649
|
+
console.log(`📝 Added to task list: ${description}\n`);
|
|
650
|
+
} finally {
|
|
651
|
+
releaseMemoryLock(lockPath);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Verify the shared-types contract hash.
|
|
657
|
+
*/
|
|
658
|
+
function verifyContractCommand() {
|
|
659
|
+
const sharedDir = path.join(targetDir, "packages/shared-types/src");
|
|
660
|
+
const contractPath = path.join(targetDir, "packages/shared-types/contract.version.json");
|
|
661
|
+
|
|
662
|
+
if (!fs.existsSync(sharedDir) || !fs.existsSync(contractPath)) {
|
|
663
|
+
console.error("❌ Error: Shared types or contract.version.json not found.");
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
const walk = (d) => fs.readdirSync(d, { withFileTypes: true }).flatMap((e) => {
|
|
668
|
+
const p = path.join(d, e.name);
|
|
669
|
+
return e.isDirectory() ? walk(p) : (p.endsWith(".ts") ? [p] : []);
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
const files = walk(sharedDir).sort();
|
|
673
|
+
const h = crypto.createHash("sha256");
|
|
674
|
+
for (const f of files) {
|
|
675
|
+
h.update(fs.readFileSync(f));
|
|
676
|
+
}
|
|
677
|
+
const currentHash = h.digest("hex");
|
|
678
|
+
|
|
679
|
+
try {
|
|
680
|
+
const stored = JSON.parse(fs.readFileSync(contractPath, "utf8")).contract_hash;
|
|
681
|
+
if (currentHash === stored) {
|
|
682
|
+
console.log("✅ Contract hash verified! (MATCH)");
|
|
683
|
+
} else {
|
|
684
|
+
console.error(`❌ HASH MISMATCH!\nExpected: ${stored}\nActual: ${currentHash}`);
|
|
685
|
+
process.exit(1);
|
|
686
|
+
}
|
|
687
|
+
} catch (err) {
|
|
688
|
+
console.error("❌ Error reading contract.version.json");
|
|
689
|
+
process.exit(1);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
function logAgentActionCommand(data) {
|
|
694
|
+
const frameworkDir = getFrameworkDir();
|
|
695
|
+
const logsDir = path.join(targetDir, frameworkDir, "logs");
|
|
696
|
+
if (!fs.existsSync(logsDir)) {
|
|
697
|
+
fs.mkdirSync(logsDir, { recursive: true });
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
const agent = normalizeAgentName(data.agent);
|
|
701
|
+
const logPath = path.join(logsDir, `${agent}.json`);
|
|
702
|
+
let logs = [];
|
|
703
|
+
|
|
704
|
+
if (fs.existsSync(logPath)) {
|
|
705
|
+
try {
|
|
706
|
+
logs = JSON.parse(fs.readFileSync(logPath, "utf8"));
|
|
707
|
+
if (!Array.isArray(logs)) logs = [];
|
|
708
|
+
} catch {
|
|
709
|
+
logs = [];
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
const newEntry = {
|
|
714
|
+
timestamp: new Date().toISOString(),
|
|
715
|
+
...data,
|
|
716
|
+
};
|
|
717
|
+
|
|
718
|
+
logs.push(newEntry);
|
|
719
|
+
fs.writeFileSync(logPath, JSON.stringify(logs, null, 2));
|
|
720
|
+
console.log(`✅ Logged action to ${frameworkDir}/logs/${agent}.json`);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
function updateProjectMemoryCommand(section, content) {
|
|
724
|
+
const memoryPath = getMemoryPath();
|
|
725
|
+
if (!fs.existsSync(memoryPath)) {
|
|
726
|
+
console.error("❌ Error: PROJECT_MEMORY.md not found.");
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
const lockPath = `${memoryPath}.lock`;
|
|
731
|
+
if (!acquireMemoryLock(lockPath)) {
|
|
732
|
+
console.error("❌ Error: Memory lock timeout.");
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
try {
|
|
737
|
+
let memoryContent = fs.readFileSync(memoryPath, "utf8");
|
|
738
|
+
|
|
739
|
+
if (section === "HISTORY") {
|
|
740
|
+
const headers = ["## HISTORY (Persistent Memory)", "## HISTORY"];
|
|
741
|
+
let sectionIndex = -1;
|
|
742
|
+
let headerUsed = "";
|
|
743
|
+
for (const h of headers) {
|
|
744
|
+
sectionIndex = memoryContent.indexOf(h);
|
|
745
|
+
if (sectionIndex !== -1) {
|
|
746
|
+
headerUsed = h;
|
|
747
|
+
break;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
if (sectionIndex === -1) {
|
|
752
|
+
console.error("❌ Error: HISTORY section not found.");
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
const headerEnd = memoryContent.indexOf("\n", sectionIndex) + 1;
|
|
756
|
+
memoryContent = memoryContent.slice(0, headerEnd) + "\n" + content.trim() + "\n" + memoryContent.slice(headerEnd);
|
|
757
|
+
} else {
|
|
758
|
+
const escaped = section.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
759
|
+
const sectionRegex = new RegExp(`## ${escaped}[\\s\\S]*?(?=\\n## |$)`, "m");
|
|
760
|
+
if (!sectionRegex.test(memoryContent)) {
|
|
761
|
+
console.error(`❌ Error: Section not found: ${section}`);
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
memoryContent = memoryContent.replace(sectionRegex, `## ${section}\n\n${content.trim()}\n`);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
fs.writeFileSync(memoryPath, memoryContent);
|
|
768
|
+
console.log(`✅ Section ${section} updated in PROJECT_MEMORY.md`);
|
|
769
|
+
} finally {
|
|
770
|
+
releaseMemoryLock(lockPath);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// --- MAIN DISPATCHER ---
|
|
775
|
+
|
|
776
|
+
async function main() {
|
|
777
|
+
const [command, ...args] = process.argv.slice(2);
|
|
778
|
+
|
|
779
|
+
switch (command) {
|
|
780
|
+
case "init":
|
|
781
|
+
await initCommand(args[0]);
|
|
782
|
+
break;
|
|
783
|
+
case "status":
|
|
784
|
+
statusCommand();
|
|
785
|
+
break;
|
|
786
|
+
case "trace:new":
|
|
787
|
+
if (!args[0]) {
|
|
788
|
+
console.error("❌ Usage: agent-enderun trace:new <description> [agent] [priority]");
|
|
789
|
+
} else {
|
|
790
|
+
traceNewCommand(args[0], args[1], args[2]);
|
|
791
|
+
}
|
|
792
|
+
break;
|
|
793
|
+
case "verify-contract":
|
|
794
|
+
verifyContractCommand();
|
|
795
|
+
break;
|
|
796
|
+
case "log_agent_action": {
|
|
797
|
+
// Handle both structured JSON and positional args
|
|
798
|
+
let data = {};
|
|
799
|
+
try {
|
|
800
|
+
if (args[0] && args[0].startsWith("{")) {
|
|
801
|
+
data = JSON.parse(args.join(" "));
|
|
802
|
+
} else {
|
|
803
|
+
data = {
|
|
804
|
+
agent: args[0],
|
|
805
|
+
action: args[1],
|
|
806
|
+
requestId: args[2],
|
|
807
|
+
status: args[3] || "SUCCESS",
|
|
808
|
+
summary: args[4] || "",
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
} catch (e) {
|
|
812
|
+
console.error("❌ Error parsing arguments for log_agent_action");
|
|
813
|
+
process.exit(1);
|
|
814
|
+
}
|
|
815
|
+
logAgentActionCommand(data);
|
|
816
|
+
break;
|
|
817
|
+
}
|
|
818
|
+
case "update_project_memory": {
|
|
819
|
+
let section, content;
|
|
820
|
+
try {
|
|
821
|
+
if (args[0] && args[0].startsWith("{")) {
|
|
822
|
+
const data = JSON.parse(args.join(" "));
|
|
823
|
+
section = data.section;
|
|
824
|
+
content = data.content;
|
|
825
|
+
} else {
|
|
826
|
+
section = args[0];
|
|
827
|
+
content = args.slice(1).join(" ");
|
|
828
|
+
}
|
|
829
|
+
} catch (e) {
|
|
830
|
+
console.error("❌ Error parsing arguments for update_project_memory");
|
|
831
|
+
process.exit(1);
|
|
832
|
+
}
|
|
833
|
+
updateProjectMemoryCommand(section, content);
|
|
834
|
+
break;
|
|
835
|
+
}
|
|
836
|
+
case "check":
|
|
837
|
+
checkCommand();
|
|
838
|
+
break;
|
|
839
|
+
case "version":
|
|
840
|
+
case "-v":
|
|
841
|
+
case "--version":
|
|
842
|
+
console.log(`v${FRAMEWORK_VERSION}`);
|
|
843
|
+
break;
|
|
844
|
+
default:
|
|
845
|
+
console.log(`
|
|
846
|
+
🤖 Agent Enderun CLI (v${FRAMEWORK_VERSION})
|
|
847
|
+
|
|
848
|
+
Available Commands:
|
|
849
|
+
init [adapter] Initialize the framework (gemini, claude, cursor, codex)
|
|
850
|
+
check Verify framework health and MCP server status
|
|
851
|
+
status Show current phase and task status
|
|
852
|
+
trace:new <desc> Generate a new Trace ID and add the task to memory
|
|
853
|
+
verify-contract Check if shared types match the stored hash
|
|
854
|
+
version Show version information
|
|
855
|
+
|
|
856
|
+
Example:
|
|
857
|
+
agent-enderun trace:new "Auth module design" backend P1
|
|
858
|
+
`);
|
|
859
|
+
break;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
main().catch(console.error);
|