aoaoe 0.55.0 → 0.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.js +31 -2
- package/dist/console.d.ts +6 -0
- package/dist/console.js +19 -0
- package/dist/index.js +308 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -243,6 +243,11 @@ commands:
|
|
|
243
243
|
config --validate validate config + check tool availability
|
|
244
244
|
config --diff show only fields that differ from defaults
|
|
245
245
|
notify-test send a test notification to configured webhooks
|
|
246
|
+
doctor comprehensive health check (config, tools, daemon, disk)
|
|
247
|
+
logs show recent conversation log entries
|
|
248
|
+
logs --actions show action log entries (from ~/.aoaoe/actions.log)
|
|
249
|
+
logs --grep <pattern> filter log entries by substring or regex
|
|
250
|
+
logs -n <count> number of entries to show (default: 50)
|
|
246
251
|
task manage tasks and sessions (list, start, stop, new, rm, edit)
|
|
247
252
|
tasks show task progress (from aoaoe.tasks.json)
|
|
248
253
|
history review recent actions (from ~/.aoaoe/actions.log)
|
package/dist/config.d.ts
CHANGED
|
@@ -29,6 +29,11 @@ export declare function parseCliArgs(argv: string[]): {
|
|
|
29
29
|
configValidate: boolean;
|
|
30
30
|
configDiff: boolean;
|
|
31
31
|
notifyTest: boolean;
|
|
32
|
+
runDoctor: boolean;
|
|
33
|
+
runLogs: boolean;
|
|
34
|
+
logsActions: boolean;
|
|
35
|
+
logsGrep?: string;
|
|
36
|
+
logsCount?: number;
|
|
32
37
|
runInit: boolean;
|
|
33
38
|
initForce: boolean;
|
|
34
39
|
runTaskCli: boolean;
|
package/dist/config.js
CHANGED
|
@@ -299,7 +299,7 @@ export function parseCliArgs(argv) {
|
|
|
299
299
|
let initForce = false;
|
|
300
300
|
let runTaskCli = false;
|
|
301
301
|
let registerTitle;
|
|
302
|
-
const defaults = { overrides, help: false, version: false, register: false, testContext: false, runTest: false, showTasks: false, showHistory: false, showStatus: false, showConfig: false, configValidate: false, configDiff: false, notifyTest: false, runInit: false, initForce: false, runTaskCli: false };
|
|
302
|
+
const defaults = { overrides, help: false, version: false, register: false, testContext: false, runTest: false, showTasks: false, showHistory: false, showStatus: false, showConfig: false, configValidate: false, configDiff: false, notifyTest: false, runDoctor: false, runLogs: false, logsActions: false, logsGrep: undefined, logsCount: undefined, runInit: false, initForce: false, runTaskCli: false };
|
|
303
303
|
// check for subcommand as first non-flag arg
|
|
304
304
|
if (argv[2] === "test-context") {
|
|
305
305
|
return { ...defaults, testContext: true };
|
|
@@ -327,6 +327,25 @@ export function parseCliArgs(argv) {
|
|
|
327
327
|
if (argv[2] === "notify-test") {
|
|
328
328
|
return { ...defaults, notifyTest: true };
|
|
329
329
|
}
|
|
330
|
+
if (argv[2] === "doctor") {
|
|
331
|
+
return { ...defaults, runDoctor: true };
|
|
332
|
+
}
|
|
333
|
+
if (argv[2] === "logs") {
|
|
334
|
+
const actions = argv.includes("--actions") || argv.includes("-a");
|
|
335
|
+
let grep;
|
|
336
|
+
let count;
|
|
337
|
+
for (let i = 3; i < argv.length; i++) {
|
|
338
|
+
if ((argv[i] === "--grep" || argv[i] === "-g") && argv[i + 1]) {
|
|
339
|
+
grep = argv[++i];
|
|
340
|
+
}
|
|
341
|
+
else if ((argv[i] === "-n" || argv[i] === "--count") && argv[i + 1]) {
|
|
342
|
+
const val = parseInt(argv[++i], 10);
|
|
343
|
+
if (!isNaN(val) && val > 0)
|
|
344
|
+
count = val;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return { ...defaults, runLogs: true, logsActions: actions, logsGrep: grep, logsCount: count };
|
|
348
|
+
}
|
|
330
349
|
if (argv[2] === "init") {
|
|
331
350
|
const force = argv.includes("--force") || argv.includes("-f");
|
|
332
351
|
return { ...defaults, runInit: true, initForce: force };
|
|
@@ -416,7 +435,7 @@ export function parseCliArgs(argv) {
|
|
|
416
435
|
break;
|
|
417
436
|
}
|
|
418
437
|
}
|
|
419
|
-
return { overrides, help, version, register: false, testContext: false, runTest: false, showTasks: false, showHistory: false, showStatus: false, showConfig: false, configValidate: false, configDiff: false, notifyTest: false, runInit: false, initForce: false, runTaskCli: false };
|
|
438
|
+
return { overrides, help, version, register: false, testContext: false, runTest: false, showTasks: false, showHistory: false, showStatus: false, showConfig: false, configValidate: false, configDiff: false, notifyTest: false, runDoctor: false, runLogs: false, logsActions: false, logsGrep: undefined, logsCount: undefined, runInit: false, initForce: false, runTaskCli: false };
|
|
420
439
|
}
|
|
421
440
|
export function printHelp() {
|
|
422
441
|
console.log(`aoaoe - autonomous supervisor for agent-of-empires sessions
|
|
@@ -437,6 +456,11 @@ commands:
|
|
|
437
456
|
config --validate validate config + check tool availability
|
|
438
457
|
config --diff show only fields that differ from defaults
|
|
439
458
|
notify-test send a test notification to configured webhooks
|
|
459
|
+
doctor comprehensive health check (config, tools, daemon, disk)
|
|
460
|
+
logs show recent conversation log entries (last 50)
|
|
461
|
+
logs --actions show action log entries (from ~/.aoaoe/actions.log)
|
|
462
|
+
logs --grep <pattern> filter log entries by substring or regex
|
|
463
|
+
logs -n <count> number of entries to show (default: 50)
|
|
440
464
|
task manage tasks and sessions (list, start, stop, new, rm, edit)
|
|
441
465
|
tasks show task progress (from aoaoe.tasks.json)
|
|
442
466
|
history review recent actions (from ~/.aoaoe/actions.log)
|
|
@@ -463,6 +487,11 @@ options:
|
|
|
463
487
|
init options:
|
|
464
488
|
--force, -f overwrite existing config
|
|
465
489
|
|
|
490
|
+
logs options:
|
|
491
|
+
--actions, -a show action log instead of conversation log
|
|
492
|
+
--grep, -g <pattern> filter entries by substring or regex
|
|
493
|
+
-n, --count <number> number of entries to show (default: 50)
|
|
494
|
+
|
|
466
495
|
register options:
|
|
467
496
|
--title, -t <name> session title in AoE (default: aoaoe)
|
|
468
497
|
|
package/dist/console.d.ts
CHANGED
|
@@ -65,5 +65,11 @@ export declare function summarizeRecentActions(logLines: string[], windowMs?: nu
|
|
|
65
65
|
* Pure function: takes stderr text, returns a plain-English explanation.
|
|
66
66
|
*/
|
|
67
67
|
export declare function friendlyError(stderr: string): string;
|
|
68
|
+
/**
|
|
69
|
+
* Filter log lines by a grep pattern (substring or regex).
|
|
70
|
+
* Tries the pattern as a regex first; falls back to plain case-insensitive substring match
|
|
71
|
+
* if the pattern is not valid regex syntax.
|
|
72
|
+
*/
|
|
73
|
+
export declare function filterLogLines(lines: string[], pattern: string): string[];
|
|
68
74
|
export declare function colorizeConsoleLine(line: string): string;
|
|
69
75
|
//# sourceMappingURL=console.d.ts.map
|
package/dist/console.js
CHANGED
|
@@ -365,6 +365,25 @@ export function friendlyError(stderr) {
|
|
|
365
365
|
const firstLine = s.split("\n")[0].trim();
|
|
366
366
|
return firstLine.length > 120 ? firstLine.slice(0, 117) + "..." : firstLine;
|
|
367
367
|
}
|
|
368
|
+
/**
|
|
369
|
+
* Filter log lines by a grep pattern (substring or regex).
|
|
370
|
+
* Tries the pattern as a regex first; falls back to plain case-insensitive substring match
|
|
371
|
+
* if the pattern is not valid regex syntax.
|
|
372
|
+
*/
|
|
373
|
+
export function filterLogLines(lines, pattern) {
|
|
374
|
+
let re = null;
|
|
375
|
+
try {
|
|
376
|
+
re = new RegExp(pattern, "i");
|
|
377
|
+
}
|
|
378
|
+
catch {
|
|
379
|
+
// invalid regex — use substring
|
|
380
|
+
}
|
|
381
|
+
return lines.filter((line) => {
|
|
382
|
+
if (re)
|
|
383
|
+
return re.test(line);
|
|
384
|
+
return line.toLowerCase().includes(pattern.toLowerCase());
|
|
385
|
+
});
|
|
386
|
+
}
|
|
368
387
|
// colorize a single console line for inline terminal output
|
|
369
388
|
// applied to each line as it's written (not batch like chat.ts colorize)
|
|
370
389
|
export function colorizeConsoleLine(line) {
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { printDashboard } from "./dashboard.js";
|
|
|
7
7
|
import { InputReader } from "./input.js";
|
|
8
8
|
import { ReasonerConsole } from "./console.js";
|
|
9
9
|
import { writeState, buildSessionStates, checkInterrupt, clearInterrupt, cleanupState, acquireLock, readState } from "./daemon-state.js";
|
|
10
|
-
import { formatSessionSummaries, formatActionDetail, formatPlainEnglishAction, narrateObservation, summarizeRecentActions, friendlyError } from "./console.js";
|
|
10
|
+
import { formatSessionSummaries, formatActionDetail, formatPlainEnglishAction, narrateObservation, summarizeRecentActions, friendlyError, colorizeConsoleLine, filterLogLines } from "./console.js";
|
|
11
11
|
import { loadGlobalContext, resolveProjectDirWithSource, discoverContextFiles, loadSessionContext } from "./context.js";
|
|
12
12
|
import { tick as loopTick } from "./loop.js";
|
|
13
13
|
import { exec as shellExec } from "./shell.js";
|
|
@@ -28,7 +28,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
28
28
|
const AOAOE_DIR = join(homedir(), ".aoaoe"); // watch dir for wakeable sleep
|
|
29
29
|
const INPUT_FILE = join(AOAOE_DIR, "pending-input.txt"); // file IPC from chat.ts
|
|
30
30
|
async function main() {
|
|
31
|
-
const { overrides, help, version, register, testContext: isTestContext, runTest, showTasks, showHistory, showStatus, showConfig, configValidate, configDiff, notifyTest, runInit, initForce, runTaskCli: isTaskCli, registerTitle } = parseCliArgs(process.argv);
|
|
31
|
+
const { overrides, help, version, register, testContext: isTestContext, runTest, showTasks, showHistory, showStatus, showConfig, configValidate, configDiff, notifyTest, runDoctor, runLogs, logsActions, logsGrep, logsCount, runInit, initForce, runTaskCli: isTaskCli, registerTitle } = parseCliArgs(process.argv);
|
|
32
32
|
if (help) {
|
|
33
33
|
printHelp();
|
|
34
34
|
process.exit(0);
|
|
@@ -91,6 +91,16 @@ async function main() {
|
|
|
91
91
|
await runNotifyTest();
|
|
92
92
|
return;
|
|
93
93
|
}
|
|
94
|
+
// `aoaoe doctor` -- comprehensive health check
|
|
95
|
+
if (runDoctor) {
|
|
96
|
+
await runDoctorCheck();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
// `aoaoe logs` -- show conversation or action log entries
|
|
100
|
+
if (runLogs) {
|
|
101
|
+
await showLogs(logsActions, logsGrep, logsCount);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
94
104
|
// `aoaoe task` -- task management CLI
|
|
95
105
|
if (isTaskCli) {
|
|
96
106
|
await runTaskCli(process.argv);
|
|
@@ -1183,6 +1193,104 @@ async function showActionHistory() {
|
|
|
1183
1193
|
console.log(` breakdown: ${breakdown}`);
|
|
1184
1194
|
console.log("");
|
|
1185
1195
|
}
|
|
1196
|
+
// `aoaoe logs` -- show conversation or action log entries
|
|
1197
|
+
async function showLogs(actions, grep, count) {
|
|
1198
|
+
const n = count ?? 50;
|
|
1199
|
+
if (actions) {
|
|
1200
|
+
// show action log entries (JSONL from ~/.aoaoe/actions.log)
|
|
1201
|
+
const logFile = join(homedir(), ".aoaoe", "actions.log");
|
|
1202
|
+
if (!existsSync(logFile)) {
|
|
1203
|
+
console.log("no action log found (no actions have been taken yet)");
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
let lines;
|
|
1207
|
+
try {
|
|
1208
|
+
lines = readFileSync(logFile, "utf-8").trim().split("\n").filter((l) => l.trim());
|
|
1209
|
+
}
|
|
1210
|
+
catch {
|
|
1211
|
+
console.error("failed to read action log");
|
|
1212
|
+
return;
|
|
1213
|
+
}
|
|
1214
|
+
if (lines.length === 0) {
|
|
1215
|
+
console.log("action log is empty");
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
// apply grep filter before slicing
|
|
1219
|
+
if (grep) {
|
|
1220
|
+
lines = filterLogLines(lines, grep);
|
|
1221
|
+
if (lines.length === 0) {
|
|
1222
|
+
console.log(`no action log entries matching '${grep}'`);
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
const recent = lines.slice(-n);
|
|
1227
|
+
console.log("");
|
|
1228
|
+
console.log(` action log (last ${recent.length} of ${lines.length}${grep ? ` matching '${grep}'` : ""})`);
|
|
1229
|
+
console.log(` ${"─".repeat(70)}`);
|
|
1230
|
+
for (const line of recent) {
|
|
1231
|
+
try {
|
|
1232
|
+
const entry = toActionLogEntry(JSON.parse(line));
|
|
1233
|
+
if (!entry)
|
|
1234
|
+
continue;
|
|
1235
|
+
const time = new Date(entry.timestamp).toLocaleTimeString();
|
|
1236
|
+
const date = new Date(entry.timestamp).toLocaleDateString();
|
|
1237
|
+
const icon = entry.success ? `${GREEN}+${RESET}` : `${RED}!${RESET}`;
|
|
1238
|
+
const actionName = entry.action.action;
|
|
1239
|
+
const session = entry.action.session?.slice(0, 8) ?? entry.action.title ?? "";
|
|
1240
|
+
const detail = entry.detail.length > 50 ? entry.detail.slice(0, 47) + "..." : entry.detail;
|
|
1241
|
+
console.log(` ${icon} ${DIM}${date} ${time}${RESET} ${YELLOW}${actionName.padEnd(16)}${RESET} ${session.padEnd(10)} ${detail}`);
|
|
1242
|
+
}
|
|
1243
|
+
catch {
|
|
1244
|
+
// skip unparseable lines
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
console.log(` ${"─".repeat(70)}`);
|
|
1248
|
+
console.log("");
|
|
1249
|
+
}
|
|
1250
|
+
else {
|
|
1251
|
+
// show conversation log entries (text from ~/.aoaoe/conversation.log)
|
|
1252
|
+
const logFile = join(homedir(), ".aoaoe", "conversation.log");
|
|
1253
|
+
if (!existsSync(logFile)) {
|
|
1254
|
+
console.log("no conversation log found (daemon hasn't run yet)");
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
let lines;
|
|
1258
|
+
try {
|
|
1259
|
+
const content = readFileSync(logFile, "utf-8");
|
|
1260
|
+
lines = content.split("\n");
|
|
1261
|
+
}
|
|
1262
|
+
catch {
|
|
1263
|
+
console.error("failed to read conversation log");
|
|
1264
|
+
return;
|
|
1265
|
+
}
|
|
1266
|
+
// remove trailing empty line from split
|
|
1267
|
+
if (lines.length > 0 && lines[lines.length - 1] === "") {
|
|
1268
|
+
lines.pop();
|
|
1269
|
+
}
|
|
1270
|
+
if (lines.length === 0) {
|
|
1271
|
+
console.log("conversation log is empty");
|
|
1272
|
+
return;
|
|
1273
|
+
}
|
|
1274
|
+
// apply grep filter before slicing
|
|
1275
|
+
if (grep) {
|
|
1276
|
+
lines = filterLogLines(lines, grep);
|
|
1277
|
+
if (lines.length === 0) {
|
|
1278
|
+
console.log(`no conversation log entries matching '${grep}'`);
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
const recent = lines.slice(-n);
|
|
1283
|
+
console.log("");
|
|
1284
|
+
console.log(` conversation log (last ${recent.length} of ${lines.length}${grep ? ` matching '${grep}'` : ""})`);
|
|
1285
|
+
console.log(` ${"─".repeat(70)}`);
|
|
1286
|
+
// colorize using the same function as the inline console output
|
|
1287
|
+
for (const line of recent) {
|
|
1288
|
+
console.log(` ${colorizeConsoleLine(line)}`);
|
|
1289
|
+
}
|
|
1290
|
+
console.log(` ${"─".repeat(70)}`);
|
|
1291
|
+
console.log("");
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1186
1294
|
// `aoaoe test` -- dynamically import and run the integration test
|
|
1187
1295
|
async function runIntegrationTest() {
|
|
1188
1296
|
const testModule = resolve(__dirname, "integration-test.js");
|
|
@@ -1402,6 +1510,204 @@ async function runConfigValidation() {
|
|
|
1402
1510
|
if (failed > 0)
|
|
1403
1511
|
process.exit(1);
|
|
1404
1512
|
}
|
|
1513
|
+
// `aoaoe doctor` -- comprehensive health check: config, tools, daemon, disk, sessions
|
|
1514
|
+
async function runDoctorCheck() {
|
|
1515
|
+
const pkg = readPkgVersion();
|
|
1516
|
+
let checks = 0;
|
|
1517
|
+
let passed = 0;
|
|
1518
|
+
let warnings = 0;
|
|
1519
|
+
console.log("");
|
|
1520
|
+
console.log(` aoaoe${pkg ? ` v${pkg}` : ""} — doctor`);
|
|
1521
|
+
console.log(` ${"─".repeat(50)}`);
|
|
1522
|
+
// ── 1. config ──────────────────────────────────────────────────────────
|
|
1523
|
+
console.log(`\n ${BOLD}config${RESET}`);
|
|
1524
|
+
const configPath = findConfigFile();
|
|
1525
|
+
checks++;
|
|
1526
|
+
if (configPath) {
|
|
1527
|
+
console.log(` ${GREEN}✓${RESET} config file: ${configPath}`);
|
|
1528
|
+
passed++;
|
|
1529
|
+
}
|
|
1530
|
+
else {
|
|
1531
|
+
console.log(` ${YELLOW}!${RESET} no config file (using defaults)`);
|
|
1532
|
+
warnings++;
|
|
1533
|
+
}
|
|
1534
|
+
let config;
|
|
1535
|
+
checks++;
|
|
1536
|
+
try {
|
|
1537
|
+
config = loadConfig();
|
|
1538
|
+
console.log(` ${GREEN}✓${RESET} config validates OK`);
|
|
1539
|
+
passed++;
|
|
1540
|
+
}
|
|
1541
|
+
catch (err) {
|
|
1542
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1543
|
+
console.log(` ${RED}✗${RESET} config invalid: ${msg.split("\n")[0]}`);
|
|
1544
|
+
// use defaults to continue checking other things
|
|
1545
|
+
config = loadConfig({});
|
|
1546
|
+
}
|
|
1547
|
+
// ── 2. tools ───────────────────────────────────────────────────────────
|
|
1548
|
+
console.log(`\n ${BOLD}tools${RESET}`);
|
|
1549
|
+
const toolChecks = [
|
|
1550
|
+
{ cmd: "node", label: "Node.js", versionArg: ["--version"], required: true },
|
|
1551
|
+
{ cmd: "aoe", label: "agent-of-empires", versionArg: ["--version"], required: true },
|
|
1552
|
+
{ cmd: "tmux", label: "terminal multiplexer", versionArg: ["-V"], required: true },
|
|
1553
|
+
];
|
|
1554
|
+
if (config.reasoner === "opencode") {
|
|
1555
|
+
toolChecks.push({ cmd: "opencode", label: "OpenCode CLI", versionArg: ["version"], required: true });
|
|
1556
|
+
}
|
|
1557
|
+
else {
|
|
1558
|
+
toolChecks.push({ cmd: "claude", label: "Claude Code CLI", versionArg: ["--version"], required: true });
|
|
1559
|
+
}
|
|
1560
|
+
for (const tool of toolChecks) {
|
|
1561
|
+
checks++;
|
|
1562
|
+
try {
|
|
1563
|
+
const result = await shellExec(tool.cmd, tool.versionArg);
|
|
1564
|
+
const ver = result.stdout.trim().split("\n")[0].slice(0, 60) || result.stderr.trim().split("\n")[0].slice(0, 60);
|
|
1565
|
+
console.log(` ${GREEN}✓${RESET} ${tool.cmd} — ${ver}`);
|
|
1566
|
+
passed++;
|
|
1567
|
+
}
|
|
1568
|
+
catch {
|
|
1569
|
+
if (tool.required) {
|
|
1570
|
+
console.log(` ${RED}✗${RESET} ${tool.cmd} not found (${tool.label})`);
|
|
1571
|
+
}
|
|
1572
|
+
else {
|
|
1573
|
+
console.log(` ${YELLOW}!${RESET} ${tool.cmd} not found (${tool.label}, optional)`);
|
|
1574
|
+
warnings++;
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
// ── 3. reasoner server ─────────────────────────────────────────────────
|
|
1579
|
+
if (config.reasoner === "opencode") {
|
|
1580
|
+
console.log(`\n ${BOLD}reasoner${RESET}`);
|
|
1581
|
+
checks++;
|
|
1582
|
+
try {
|
|
1583
|
+
const resp = await fetch(`http://127.0.0.1:${config.opencode.port}/health`, {
|
|
1584
|
+
signal: AbortSignal.timeout(3000),
|
|
1585
|
+
});
|
|
1586
|
+
if (resp.ok) {
|
|
1587
|
+
console.log(` ${GREEN}✓${RESET} opencode serve responding on port ${config.opencode.port}`);
|
|
1588
|
+
passed++;
|
|
1589
|
+
}
|
|
1590
|
+
else {
|
|
1591
|
+
console.log(` ${YELLOW}!${RESET} opencode serve on port ${config.opencode.port} returned ${resp.status}`);
|
|
1592
|
+
warnings++;
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
catch {
|
|
1596
|
+
console.log(` ${RED}✗${RESET} opencode serve not responding on port ${config.opencode.port}`);
|
|
1597
|
+
console.log(` ${DIM}start with: opencode serve --port ${config.opencode.port}${RESET}`);
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
// ── 4. daemon ──────────────────────────────────────────────────────────
|
|
1601
|
+
console.log(`\n ${BOLD}daemon${RESET}`);
|
|
1602
|
+
const state = readState();
|
|
1603
|
+
const daemonRunning = isDaemonRunningFromState(state);
|
|
1604
|
+
checks++;
|
|
1605
|
+
if (daemonRunning && state) {
|
|
1606
|
+
console.log(` ${GREEN}✓${RESET} daemon running (poll #${state.pollCount}, phase: ${state.phase})`);
|
|
1607
|
+
console.log(` ${state.sessions.length} session(s) monitored`);
|
|
1608
|
+
passed++;
|
|
1609
|
+
}
|
|
1610
|
+
else {
|
|
1611
|
+
console.log(` ${DIM}○${RESET} daemon not running`);
|
|
1612
|
+
passed++; // not running is fine for doctor — just informational
|
|
1613
|
+
}
|
|
1614
|
+
// lock file check
|
|
1615
|
+
const lockPath = join(homedir(), ".aoaoe", "daemon.lock");
|
|
1616
|
+
if (existsSync(lockPath) && !daemonRunning) {
|
|
1617
|
+
checks++;
|
|
1618
|
+
console.log(` ${YELLOW}!${RESET} stale lock file found: ${lockPath}`);
|
|
1619
|
+
console.log(` ${DIM}remove with: rm ${lockPath}${RESET}`);
|
|
1620
|
+
warnings++;
|
|
1621
|
+
}
|
|
1622
|
+
// ── 5. disk / data ─────────────────────────────────────────────────────
|
|
1623
|
+
console.log(`\n ${BOLD}data${RESET}`);
|
|
1624
|
+
const aoaoeDir = join(homedir(), ".aoaoe");
|
|
1625
|
+
if (existsSync(aoaoeDir)) {
|
|
1626
|
+
checks++;
|
|
1627
|
+
try {
|
|
1628
|
+
const files = await import("node:fs").then(fs => fs.readdirSync(aoaoeDir));
|
|
1629
|
+
let totalSize = 0;
|
|
1630
|
+
for (const f of files) {
|
|
1631
|
+
try {
|
|
1632
|
+
totalSize += statSync(join(aoaoeDir, f)).size;
|
|
1633
|
+
}
|
|
1634
|
+
catch { /* skip unreadable */ }
|
|
1635
|
+
}
|
|
1636
|
+
const sizeStr = totalSize < 1024 ? `${totalSize}B` :
|
|
1637
|
+
totalSize < 1_048_576 ? `${(totalSize / 1024).toFixed(1)}KB` :
|
|
1638
|
+
`${(totalSize / 1_048_576).toFixed(1)}MB`;
|
|
1639
|
+
console.log(` ${GREEN}✓${RESET} ~/.aoaoe/ — ${files.length} files, ${sizeStr}`);
|
|
1640
|
+
passed++;
|
|
1641
|
+
}
|
|
1642
|
+
catch {
|
|
1643
|
+
console.log(` ${YELLOW}!${RESET} could not read ~/.aoaoe/`);
|
|
1644
|
+
warnings++;
|
|
1645
|
+
}
|
|
1646
|
+
// actions log stats
|
|
1647
|
+
const actionsPath = join(aoaoeDir, "actions.log");
|
|
1648
|
+
if (existsSync(actionsPath)) {
|
|
1649
|
+
checks++;
|
|
1650
|
+
try {
|
|
1651
|
+
const content = readFileSync(actionsPath, "utf-8").trim();
|
|
1652
|
+
const lineCount = content ? content.split("\n").length : 0;
|
|
1653
|
+
const size = statSync(actionsPath).size;
|
|
1654
|
+
const sizeStr = size < 1024 ? `${size}B` : `${(size / 1024).toFixed(1)}KB`;
|
|
1655
|
+
console.log(` ${GREEN}✓${RESET} actions.log — ${lineCount} entries, ${sizeStr}`);
|
|
1656
|
+
passed++;
|
|
1657
|
+
}
|
|
1658
|
+
catch {
|
|
1659
|
+
console.log(` ${YELLOW}!${RESET} actions.log unreadable`);
|
|
1660
|
+
warnings++;
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
else {
|
|
1665
|
+
console.log(` ${DIM}○${RESET} ~/.aoaoe/ does not exist yet (run 'aoaoe init')`);
|
|
1666
|
+
}
|
|
1667
|
+
// ── 6. aoe sessions ───────────────────────────────────────────────────
|
|
1668
|
+
console.log(`\n ${BOLD}sessions${RESET}`);
|
|
1669
|
+
checks++;
|
|
1670
|
+
try {
|
|
1671
|
+
const listResult = await shellExec("aoe", ["list", "--json"]);
|
|
1672
|
+
if (listResult.exitCode === 0 && listResult.stdout.trim()) {
|
|
1673
|
+
const sessions = JSON.parse(listResult.stdout);
|
|
1674
|
+
if (Array.isArray(sessions) && sessions.length > 0) {
|
|
1675
|
+
console.log(` ${GREEN}✓${RESET} ${sessions.length} aoe session(s) found`);
|
|
1676
|
+
for (const s of sessions.slice(0, 5)) {
|
|
1677
|
+
console.log(` ${DIM}${s.title ?? s.id} (${s.tool ?? "?"})${RESET}`);
|
|
1678
|
+
}
|
|
1679
|
+
if (sessions.length > 5)
|
|
1680
|
+
console.log(` ${DIM}...and ${sessions.length - 5} more${RESET}`);
|
|
1681
|
+
passed++;
|
|
1682
|
+
}
|
|
1683
|
+
else {
|
|
1684
|
+
console.log(` ${DIM}○${RESET} no aoe sessions (start some with 'aoe add')`);
|
|
1685
|
+
passed++;
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
else {
|
|
1689
|
+
console.log(` ${YELLOW}!${RESET} aoe list returned non-zero (is aoe running?)`);
|
|
1690
|
+
warnings++;
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
catch {
|
|
1694
|
+
console.log(` ${RED}✗${RESET} could not run 'aoe list --json'`);
|
|
1695
|
+
}
|
|
1696
|
+
// ── summary ────────────────────────────────────────────────────────────
|
|
1697
|
+
const failed = checks - passed - warnings;
|
|
1698
|
+
console.log("");
|
|
1699
|
+
console.log(` ${"─".repeat(50)}`);
|
|
1700
|
+
if (failed === 0 && warnings === 0) {
|
|
1701
|
+
console.log(` ${GREEN}${BOLD}all ${checks} checks passed${RESET} — looking healthy`);
|
|
1702
|
+
}
|
|
1703
|
+
else if (failed === 0) {
|
|
1704
|
+
console.log(` ${passed}/${checks} passed, ${YELLOW}${warnings} warning(s)${RESET}`);
|
|
1705
|
+
}
|
|
1706
|
+
else {
|
|
1707
|
+
console.log(` ${passed}/${checks} passed, ${RED}${failed} failed${RESET}${warnings > 0 ? `, ${YELLOW}${warnings} warning(s)${RESET}` : ""}`);
|
|
1708
|
+
}
|
|
1709
|
+
console.log("");
|
|
1710
|
+
}
|
|
1405
1711
|
// `aoaoe config --diff` -- show only fields that differ from defaults
|
|
1406
1712
|
function showConfigDiff() {
|
|
1407
1713
|
const configPath = findConfigFile();
|