aoaoe 0.54.0 → 0.56.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 +2 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.js +36 -5
- package/dist/index.js +272 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -241,7 +241,9 @@ commands:
|
|
|
241
241
|
status quick daemon health check (is it running? what's it doing?)
|
|
242
242
|
config show the effective resolved config (defaults + file)
|
|
243
243
|
config --validate validate config + check tool availability
|
|
244
|
+
config --diff show only fields that differ from defaults
|
|
244
245
|
notify-test send a test notification to configured webhooks
|
|
246
|
+
doctor comprehensive health check (config, tools, daemon, disk)
|
|
245
247
|
task manage tasks and sessions (list, start, stop, new, rm, edit)
|
|
246
248
|
tasks show task progress (from aoaoe.tasks.json)
|
|
247
249
|
history review recent actions (from ~/.aoaoe/actions.log)
|
package/dist/config.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AoaoeConfig } from "./types.js";
|
|
2
|
+
export declare const DEFAULTS: AoaoeConfig;
|
|
2
3
|
export declare function findConfigFile(): string | null;
|
|
3
4
|
export declare function configFileExists(): boolean;
|
|
4
5
|
export declare function defaultConfigPath(): string;
|
|
@@ -9,6 +10,11 @@ export declare function warnUnknownKeys(raw: unknown, source: string): void;
|
|
|
9
10
|
export declare function validateConfig(config: AoaoeConfig): void;
|
|
10
11
|
export declare function validateEnvironment(config: AoaoeConfig): Promise<void>;
|
|
11
12
|
export declare function deepMerge(...objects: Record<string, unknown>[]): AoaoeConfig;
|
|
13
|
+
export declare function computeConfigDiff(current: Record<string, unknown>, defaults: Record<string, unknown>, prefix?: string): Array<{
|
|
14
|
+
path: string;
|
|
15
|
+
current: unknown;
|
|
16
|
+
default: unknown;
|
|
17
|
+
}>;
|
|
12
18
|
export declare function parseCliArgs(argv: string[]): {
|
|
13
19
|
overrides: Partial<AoaoeConfig>;
|
|
14
20
|
help: boolean;
|
|
@@ -21,7 +27,9 @@ export declare function parseCliArgs(argv: string[]): {
|
|
|
21
27
|
showStatus: boolean;
|
|
22
28
|
showConfig: boolean;
|
|
23
29
|
configValidate: boolean;
|
|
30
|
+
configDiff: boolean;
|
|
24
31
|
notifyTest: boolean;
|
|
32
|
+
runDoctor: boolean;
|
|
25
33
|
runInit: boolean;
|
|
26
34
|
initForce: boolean;
|
|
27
35
|
runTaskCli: boolean;
|
package/dist/config.js
CHANGED
|
@@ -9,7 +9,7 @@ const AOAOE_DIR = join(homedir(), ".aoaoe");
|
|
|
9
9
|
const CONFIG_NAMES = ["aoaoe.config.json", ".aoaoe.json"];
|
|
10
10
|
// search order: ~/.aoaoe/ first (canonical), then cwd (local override for dev)
|
|
11
11
|
const CONFIG_SEARCH_DIRS = [AOAOE_DIR, process.cwd()];
|
|
12
|
-
const DEFAULTS = {
|
|
12
|
+
export const DEFAULTS = {
|
|
13
13
|
reasoner: "opencode",
|
|
14
14
|
pollIntervalMs: 10_000,
|
|
15
15
|
opencode: {
|
|
@@ -259,6 +259,31 @@ export function deepMerge(...objects) {
|
|
|
259
259
|
}
|
|
260
260
|
return result;
|
|
261
261
|
}
|
|
262
|
+
// compute fields that differ between two config objects (flat dot-notation paths)
|
|
263
|
+
// exported for testing
|
|
264
|
+
export function computeConfigDiff(current, defaults, prefix = "") {
|
|
265
|
+
const diffs = [];
|
|
266
|
+
const allKeys = new Set([...Object.keys(current), ...Object.keys(defaults)]);
|
|
267
|
+
for (const key of allKeys) {
|
|
268
|
+
const fullPath = prefix ? `${prefix}.${key}` : key;
|
|
269
|
+
const curVal = current[key];
|
|
270
|
+
const defVal = defaults[key];
|
|
271
|
+
// both are plain objects — recurse
|
|
272
|
+
if (curVal && defVal &&
|
|
273
|
+
typeof curVal === "object" && !Array.isArray(curVal) &&
|
|
274
|
+
typeof defVal === "object" && !Array.isArray(defVal)) {
|
|
275
|
+
diffs.push(...computeConfigDiff(curVal, defVal, fullPath));
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
// compare with JSON.stringify for arrays/objects, === for primitives
|
|
279
|
+
const curStr = JSON.stringify(curVal);
|
|
280
|
+
const defStr = JSON.stringify(defVal);
|
|
281
|
+
if (curStr !== defStr) {
|
|
282
|
+
diffs.push({ path: fullPath, current: curVal, default: defVal });
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return diffs;
|
|
286
|
+
}
|
|
262
287
|
function log(msg) {
|
|
263
288
|
console.error(`[config] ${msg}`);
|
|
264
289
|
}
|
|
@@ -274,7 +299,7 @@ export function parseCliArgs(argv) {
|
|
|
274
299
|
let initForce = false;
|
|
275
300
|
let runTaskCli = false;
|
|
276
301
|
let registerTitle;
|
|
277
|
-
const defaults = { overrides, help: false, version: false, register: false, testContext: false, runTest: false, showTasks: false, showHistory: false, showStatus: false, showConfig: false, configValidate: 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, runInit: false, initForce: false, runTaskCli: false };
|
|
278
303
|
// check for subcommand as first non-flag arg
|
|
279
304
|
if (argv[2] === "test-context") {
|
|
280
305
|
return { ...defaults, testContext: true };
|
|
@@ -296,11 +321,15 @@ export function parseCliArgs(argv) {
|
|
|
296
321
|
}
|
|
297
322
|
if (argv[2] === "config") {
|
|
298
323
|
const validate = argv.includes("--validate") || argv.includes("-V");
|
|
299
|
-
|
|
324
|
+
const diff = argv.includes("--diff");
|
|
325
|
+
return { ...defaults, showConfig: true, configValidate: validate, configDiff: diff };
|
|
300
326
|
}
|
|
301
327
|
if (argv[2] === "notify-test") {
|
|
302
328
|
return { ...defaults, notifyTest: true };
|
|
303
329
|
}
|
|
330
|
+
if (argv[2] === "doctor") {
|
|
331
|
+
return { ...defaults, runDoctor: true };
|
|
332
|
+
}
|
|
304
333
|
if (argv[2] === "init") {
|
|
305
334
|
const force = argv.includes("--force") || argv.includes("-f");
|
|
306
335
|
return { ...defaults, runInit: true, initForce: force };
|
|
@@ -390,7 +419,7 @@ export function parseCliArgs(argv) {
|
|
|
390
419
|
break;
|
|
391
420
|
}
|
|
392
421
|
}
|
|
393
|
-
return { overrides, help, version, register: false, testContext: false, runTest: false, showTasks: false, showHistory: false, showStatus: false, showConfig: false, configValidate: false, notifyTest: false, runInit: false, initForce: false, runTaskCli: false };
|
|
422
|
+
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, runInit: false, initForce: false, runTaskCli: false };
|
|
394
423
|
}
|
|
395
424
|
export function printHelp() {
|
|
396
425
|
console.log(`aoaoe - autonomous supervisor for agent-of-empires sessions
|
|
@@ -408,8 +437,10 @@ commands:
|
|
|
408
437
|
(none) start the supervisor daemon (interactive TUI)
|
|
409
438
|
status quick daemon health check (is it running? what's it doing?)
|
|
410
439
|
config show the effective resolved config (defaults + file)
|
|
411
|
-
config --validate validate config
|
|
440
|
+
config --validate validate config + check tool availability
|
|
441
|
+
config --diff show only fields that differ from defaults
|
|
412
442
|
notify-test send a test notification to configured webhooks
|
|
443
|
+
doctor comprehensive health check (config, tools, daemon, disk)
|
|
413
444
|
task manage tasks and sessions (list, start, stop, new, rm, edit)
|
|
414
445
|
tasks show task progress (from aoaoe.tasks.json)
|
|
415
446
|
history review recent actions (from ~/.aoaoe/actions.log)
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { loadConfig, validateEnvironment, parseCliArgs, printHelp, configFileExists, findConfigFile } from "./config.js";
|
|
2
|
+
import { loadConfig, validateEnvironment, parseCliArgs, printHelp, configFileExists, findConfigFile, DEFAULTS, computeConfigDiff } from "./config.js";
|
|
3
3
|
import { Poller, computeTmuxName } from "./poller.js";
|
|
4
4
|
import { createReasoner } from "./reasoner/index.js";
|
|
5
5
|
import { Executor } from "./executor.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, 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, runInit, initForce, runTaskCli: isTaskCli, registerTitle } = parseCliArgs(process.argv);
|
|
32
32
|
if (help) {
|
|
33
33
|
printHelp();
|
|
34
34
|
process.exit(0);
|
|
@@ -73,11 +73,14 @@ async function main() {
|
|
|
73
73
|
showDaemonStatus();
|
|
74
74
|
return;
|
|
75
75
|
}
|
|
76
|
-
// `aoaoe config` -- show effective resolved config (with optional --validate)
|
|
76
|
+
// `aoaoe config` -- show effective resolved config (with optional --validate or --diff)
|
|
77
77
|
if (showConfig) {
|
|
78
78
|
if (configValidate) {
|
|
79
79
|
await runConfigValidation();
|
|
80
80
|
}
|
|
81
|
+
else if (configDiff) {
|
|
82
|
+
showConfigDiff();
|
|
83
|
+
}
|
|
81
84
|
else {
|
|
82
85
|
showEffectiveConfig();
|
|
83
86
|
}
|
|
@@ -88,6 +91,11 @@ async function main() {
|
|
|
88
91
|
await runNotifyTest();
|
|
89
92
|
return;
|
|
90
93
|
}
|
|
94
|
+
// `aoaoe doctor` -- comprehensive health check
|
|
95
|
+
if (runDoctor) {
|
|
96
|
+
await runDoctorCheck();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
91
99
|
// `aoaoe task` -- task management CLI
|
|
92
100
|
if (isTaskCli) {
|
|
93
101
|
await runTaskCli(process.argv);
|
|
@@ -1243,6 +1251,42 @@ function showDaemonStatus() {
|
|
|
1243
1251
|
console.log(` ${statusIcon} ${BOLD}${s.title}${RESET} (${s.tool}) ${s.status}${userTag}${taskTag}`);
|
|
1244
1252
|
}
|
|
1245
1253
|
}
|
|
1254
|
+
// last action from actions.log
|
|
1255
|
+
try {
|
|
1256
|
+
const actionsLogPath = join(homedir(), ".aoaoe", "actions.log");
|
|
1257
|
+
if (existsSync(actionsLogPath)) {
|
|
1258
|
+
const content = readFileSync(actionsLogPath, "utf-8").trim();
|
|
1259
|
+
if (content) {
|
|
1260
|
+
const logLines = content.split("\n").filter((l) => l.trim());
|
|
1261
|
+
// find last non-wait action
|
|
1262
|
+
for (let i = logLines.length - 1; i >= 0; i--) {
|
|
1263
|
+
try {
|
|
1264
|
+
const entry = toActionLogEntry(JSON.parse(logLines[i]));
|
|
1265
|
+
if (!entry || entry.action.action === "wait")
|
|
1266
|
+
continue;
|
|
1267
|
+
const ago = Date.now() - entry.timestamp;
|
|
1268
|
+
const agoStr = ago < 60_000 ? `${Math.floor(ago / 1000)}s ago` :
|
|
1269
|
+
ago < 3_600_000 ? `${Math.floor(ago / 60_000)}m ago` :
|
|
1270
|
+
`${Math.floor(ago / 3_600_000)}h ago`;
|
|
1271
|
+
const icon = entry.success ? `${GREEN}+${RESET}` : `${RED}!${RESET}`;
|
|
1272
|
+
const session = entry.action.session?.slice(0, 8) ?? entry.action.title ?? "";
|
|
1273
|
+
const detail = entry.detail.length > 40 ? entry.detail.slice(0, 37) + "..." : entry.detail;
|
|
1274
|
+
console.log("");
|
|
1275
|
+
console.log(` last action: ${icon} ${entry.action.action} ${session} ${DIM}(${agoStr})${RESET}`);
|
|
1276
|
+
if (detail)
|
|
1277
|
+
console.log(` ${DIM}${detail}${RESET}`);
|
|
1278
|
+
break;
|
|
1279
|
+
}
|
|
1280
|
+
catch {
|
|
1281
|
+
// skip malformed lines
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
catch {
|
|
1288
|
+
// best-effort — actions.log might not exist
|
|
1289
|
+
}
|
|
1246
1290
|
console.log("");
|
|
1247
1291
|
}
|
|
1248
1292
|
// `aoaoe config --validate` -- validate config file, field values, and tool availability
|
|
@@ -1363,6 +1407,231 @@ async function runConfigValidation() {
|
|
|
1363
1407
|
if (failed > 0)
|
|
1364
1408
|
process.exit(1);
|
|
1365
1409
|
}
|
|
1410
|
+
// `aoaoe doctor` -- comprehensive health check: config, tools, daemon, disk, sessions
|
|
1411
|
+
async function runDoctorCheck() {
|
|
1412
|
+
const pkg = readPkgVersion();
|
|
1413
|
+
let checks = 0;
|
|
1414
|
+
let passed = 0;
|
|
1415
|
+
let warnings = 0;
|
|
1416
|
+
console.log("");
|
|
1417
|
+
console.log(` aoaoe${pkg ? ` v${pkg}` : ""} — doctor`);
|
|
1418
|
+
console.log(` ${"─".repeat(50)}`);
|
|
1419
|
+
// ── 1. config ──────────────────────────────────────────────────────────
|
|
1420
|
+
console.log(`\n ${BOLD}config${RESET}`);
|
|
1421
|
+
const configPath = findConfigFile();
|
|
1422
|
+
checks++;
|
|
1423
|
+
if (configPath) {
|
|
1424
|
+
console.log(` ${GREEN}✓${RESET} config file: ${configPath}`);
|
|
1425
|
+
passed++;
|
|
1426
|
+
}
|
|
1427
|
+
else {
|
|
1428
|
+
console.log(` ${YELLOW}!${RESET} no config file (using defaults)`);
|
|
1429
|
+
warnings++;
|
|
1430
|
+
}
|
|
1431
|
+
let config;
|
|
1432
|
+
checks++;
|
|
1433
|
+
try {
|
|
1434
|
+
config = loadConfig();
|
|
1435
|
+
console.log(` ${GREEN}✓${RESET} config validates OK`);
|
|
1436
|
+
passed++;
|
|
1437
|
+
}
|
|
1438
|
+
catch (err) {
|
|
1439
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1440
|
+
console.log(` ${RED}✗${RESET} config invalid: ${msg.split("\n")[0]}`);
|
|
1441
|
+
// use defaults to continue checking other things
|
|
1442
|
+
config = loadConfig({});
|
|
1443
|
+
}
|
|
1444
|
+
// ── 2. tools ───────────────────────────────────────────────────────────
|
|
1445
|
+
console.log(`\n ${BOLD}tools${RESET}`);
|
|
1446
|
+
const toolChecks = [
|
|
1447
|
+
{ cmd: "node", label: "Node.js", versionArg: ["--version"], required: true },
|
|
1448
|
+
{ cmd: "aoe", label: "agent-of-empires", versionArg: ["--version"], required: true },
|
|
1449
|
+
{ cmd: "tmux", label: "terminal multiplexer", versionArg: ["-V"], required: true },
|
|
1450
|
+
];
|
|
1451
|
+
if (config.reasoner === "opencode") {
|
|
1452
|
+
toolChecks.push({ cmd: "opencode", label: "OpenCode CLI", versionArg: ["version"], required: true });
|
|
1453
|
+
}
|
|
1454
|
+
else {
|
|
1455
|
+
toolChecks.push({ cmd: "claude", label: "Claude Code CLI", versionArg: ["--version"], required: true });
|
|
1456
|
+
}
|
|
1457
|
+
for (const tool of toolChecks) {
|
|
1458
|
+
checks++;
|
|
1459
|
+
try {
|
|
1460
|
+
const result = await shellExec(tool.cmd, tool.versionArg);
|
|
1461
|
+
const ver = result.stdout.trim().split("\n")[0].slice(0, 60) || result.stderr.trim().split("\n")[0].slice(0, 60);
|
|
1462
|
+
console.log(` ${GREEN}✓${RESET} ${tool.cmd} — ${ver}`);
|
|
1463
|
+
passed++;
|
|
1464
|
+
}
|
|
1465
|
+
catch {
|
|
1466
|
+
if (tool.required) {
|
|
1467
|
+
console.log(` ${RED}✗${RESET} ${tool.cmd} not found (${tool.label})`);
|
|
1468
|
+
}
|
|
1469
|
+
else {
|
|
1470
|
+
console.log(` ${YELLOW}!${RESET} ${tool.cmd} not found (${tool.label}, optional)`);
|
|
1471
|
+
warnings++;
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
// ── 3. reasoner server ─────────────────────────────────────────────────
|
|
1476
|
+
if (config.reasoner === "opencode") {
|
|
1477
|
+
console.log(`\n ${BOLD}reasoner${RESET}`);
|
|
1478
|
+
checks++;
|
|
1479
|
+
try {
|
|
1480
|
+
const resp = await fetch(`http://127.0.0.1:${config.opencode.port}/health`, {
|
|
1481
|
+
signal: AbortSignal.timeout(3000),
|
|
1482
|
+
});
|
|
1483
|
+
if (resp.ok) {
|
|
1484
|
+
console.log(` ${GREEN}✓${RESET} opencode serve responding on port ${config.opencode.port}`);
|
|
1485
|
+
passed++;
|
|
1486
|
+
}
|
|
1487
|
+
else {
|
|
1488
|
+
console.log(` ${YELLOW}!${RESET} opencode serve on port ${config.opencode.port} returned ${resp.status}`);
|
|
1489
|
+
warnings++;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
catch {
|
|
1493
|
+
console.log(` ${RED}✗${RESET} opencode serve not responding on port ${config.opencode.port}`);
|
|
1494
|
+
console.log(` ${DIM}start with: opencode serve --port ${config.opencode.port}${RESET}`);
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
// ── 4. daemon ──────────────────────────────────────────────────────────
|
|
1498
|
+
console.log(`\n ${BOLD}daemon${RESET}`);
|
|
1499
|
+
const state = readState();
|
|
1500
|
+
const daemonRunning = isDaemonRunningFromState(state);
|
|
1501
|
+
checks++;
|
|
1502
|
+
if (daemonRunning && state) {
|
|
1503
|
+
console.log(` ${GREEN}✓${RESET} daemon running (poll #${state.pollCount}, phase: ${state.phase})`);
|
|
1504
|
+
console.log(` ${state.sessions.length} session(s) monitored`);
|
|
1505
|
+
passed++;
|
|
1506
|
+
}
|
|
1507
|
+
else {
|
|
1508
|
+
console.log(` ${DIM}○${RESET} daemon not running`);
|
|
1509
|
+
passed++; // not running is fine for doctor — just informational
|
|
1510
|
+
}
|
|
1511
|
+
// lock file check
|
|
1512
|
+
const lockPath = join(homedir(), ".aoaoe", "daemon.lock");
|
|
1513
|
+
if (existsSync(lockPath) && !daemonRunning) {
|
|
1514
|
+
checks++;
|
|
1515
|
+
console.log(` ${YELLOW}!${RESET} stale lock file found: ${lockPath}`);
|
|
1516
|
+
console.log(` ${DIM}remove with: rm ${lockPath}${RESET}`);
|
|
1517
|
+
warnings++;
|
|
1518
|
+
}
|
|
1519
|
+
// ── 5. disk / data ─────────────────────────────────────────────────────
|
|
1520
|
+
console.log(`\n ${BOLD}data${RESET}`);
|
|
1521
|
+
const aoaoeDir = join(homedir(), ".aoaoe");
|
|
1522
|
+
if (existsSync(aoaoeDir)) {
|
|
1523
|
+
checks++;
|
|
1524
|
+
try {
|
|
1525
|
+
const files = await import("node:fs").then(fs => fs.readdirSync(aoaoeDir));
|
|
1526
|
+
let totalSize = 0;
|
|
1527
|
+
for (const f of files) {
|
|
1528
|
+
try {
|
|
1529
|
+
totalSize += statSync(join(aoaoeDir, f)).size;
|
|
1530
|
+
}
|
|
1531
|
+
catch { /* skip unreadable */ }
|
|
1532
|
+
}
|
|
1533
|
+
const sizeStr = totalSize < 1024 ? `${totalSize}B` :
|
|
1534
|
+
totalSize < 1_048_576 ? `${(totalSize / 1024).toFixed(1)}KB` :
|
|
1535
|
+
`${(totalSize / 1_048_576).toFixed(1)}MB`;
|
|
1536
|
+
console.log(` ${GREEN}✓${RESET} ~/.aoaoe/ — ${files.length} files, ${sizeStr}`);
|
|
1537
|
+
passed++;
|
|
1538
|
+
}
|
|
1539
|
+
catch {
|
|
1540
|
+
console.log(` ${YELLOW}!${RESET} could not read ~/.aoaoe/`);
|
|
1541
|
+
warnings++;
|
|
1542
|
+
}
|
|
1543
|
+
// actions log stats
|
|
1544
|
+
const actionsPath = join(aoaoeDir, "actions.log");
|
|
1545
|
+
if (existsSync(actionsPath)) {
|
|
1546
|
+
checks++;
|
|
1547
|
+
try {
|
|
1548
|
+
const content = readFileSync(actionsPath, "utf-8").trim();
|
|
1549
|
+
const lineCount = content ? content.split("\n").length : 0;
|
|
1550
|
+
const size = statSync(actionsPath).size;
|
|
1551
|
+
const sizeStr = size < 1024 ? `${size}B` : `${(size / 1024).toFixed(1)}KB`;
|
|
1552
|
+
console.log(` ${GREEN}✓${RESET} actions.log — ${lineCount} entries, ${sizeStr}`);
|
|
1553
|
+
passed++;
|
|
1554
|
+
}
|
|
1555
|
+
catch {
|
|
1556
|
+
console.log(` ${YELLOW}!${RESET} actions.log unreadable`);
|
|
1557
|
+
warnings++;
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
else {
|
|
1562
|
+
console.log(` ${DIM}○${RESET} ~/.aoaoe/ does not exist yet (run 'aoaoe init')`);
|
|
1563
|
+
}
|
|
1564
|
+
// ── 6. aoe sessions ───────────────────────────────────────────────────
|
|
1565
|
+
console.log(`\n ${BOLD}sessions${RESET}`);
|
|
1566
|
+
checks++;
|
|
1567
|
+
try {
|
|
1568
|
+
const listResult = await shellExec("aoe", ["list", "--json"]);
|
|
1569
|
+
if (listResult.exitCode === 0 && listResult.stdout.trim()) {
|
|
1570
|
+
const sessions = JSON.parse(listResult.stdout);
|
|
1571
|
+
if (Array.isArray(sessions) && sessions.length > 0) {
|
|
1572
|
+
console.log(` ${GREEN}✓${RESET} ${sessions.length} aoe session(s) found`);
|
|
1573
|
+
for (const s of sessions.slice(0, 5)) {
|
|
1574
|
+
console.log(` ${DIM}${s.title ?? s.id} (${s.tool ?? "?"})${RESET}`);
|
|
1575
|
+
}
|
|
1576
|
+
if (sessions.length > 5)
|
|
1577
|
+
console.log(` ${DIM}...and ${sessions.length - 5} more${RESET}`);
|
|
1578
|
+
passed++;
|
|
1579
|
+
}
|
|
1580
|
+
else {
|
|
1581
|
+
console.log(` ${DIM}○${RESET} no aoe sessions (start some with 'aoe add')`);
|
|
1582
|
+
passed++;
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
else {
|
|
1586
|
+
console.log(` ${YELLOW}!${RESET} aoe list returned non-zero (is aoe running?)`);
|
|
1587
|
+
warnings++;
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
catch {
|
|
1591
|
+
console.log(` ${RED}✗${RESET} could not run 'aoe list --json'`);
|
|
1592
|
+
}
|
|
1593
|
+
// ── summary ────────────────────────────────────────────────────────────
|
|
1594
|
+
const failed = checks - passed - warnings;
|
|
1595
|
+
console.log("");
|
|
1596
|
+
console.log(` ${"─".repeat(50)}`);
|
|
1597
|
+
if (failed === 0 && warnings === 0) {
|
|
1598
|
+
console.log(` ${GREEN}${BOLD}all ${checks} checks passed${RESET} — looking healthy`);
|
|
1599
|
+
}
|
|
1600
|
+
else if (failed === 0) {
|
|
1601
|
+
console.log(` ${passed}/${checks} passed, ${YELLOW}${warnings} warning(s)${RESET}`);
|
|
1602
|
+
}
|
|
1603
|
+
else {
|
|
1604
|
+
console.log(` ${passed}/${checks} passed, ${RED}${failed} failed${RESET}${warnings > 0 ? `, ${YELLOW}${warnings} warning(s)${RESET}` : ""}`);
|
|
1605
|
+
}
|
|
1606
|
+
console.log("");
|
|
1607
|
+
}
|
|
1608
|
+
// `aoaoe config --diff` -- show only fields that differ from defaults
|
|
1609
|
+
function showConfigDiff() {
|
|
1610
|
+
const configPath = findConfigFile();
|
|
1611
|
+
const configResult = loadConfig();
|
|
1612
|
+
const { _configPath, ...config } = configResult;
|
|
1613
|
+
const diffs = computeConfigDiff(config, DEFAULTS);
|
|
1614
|
+
console.log("");
|
|
1615
|
+
console.log(" aoaoe — config diff (vs. defaults)");
|
|
1616
|
+
console.log(` ${"─".repeat(50)}`);
|
|
1617
|
+
console.log(` source: ${configPath ?? "defaults (no config file found)"}`);
|
|
1618
|
+
console.log("");
|
|
1619
|
+
if (diffs.length === 0) {
|
|
1620
|
+
console.log(" (no differences — config matches defaults)");
|
|
1621
|
+
}
|
|
1622
|
+
else {
|
|
1623
|
+
for (const d of diffs) {
|
|
1624
|
+
const curStr = d.current === undefined ? `${DIM}(not set)${RESET}` : `${GREEN}${JSON.stringify(d.current)}${RESET}`;
|
|
1625
|
+
const defStr = d.default === undefined ? `${DIM}(not set)${RESET}` : `${DIM}${JSON.stringify(d.default)}${RESET}`;
|
|
1626
|
+
console.log(` ${YELLOW}${d.path}${RESET}`);
|
|
1627
|
+
console.log(` current: ${curStr}`);
|
|
1628
|
+
console.log(` default: ${defStr}`);
|
|
1629
|
+
}
|
|
1630
|
+
console.log("");
|
|
1631
|
+
console.log(` ${diffs.length} field(s) differ from defaults`);
|
|
1632
|
+
}
|
|
1633
|
+
console.log("");
|
|
1634
|
+
}
|
|
1366
1635
|
// `aoaoe config` -- show the effective resolved config (defaults + file + any notes)
|
|
1367
1636
|
function showEffectiveConfig() {
|
|
1368
1637
|
const configPath = findConfigFile();
|