aoaoe 0.53.0 → 0.54.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 CHANGED
@@ -240,6 +240,7 @@ commands:
240
240
  init detect tools + sessions, import history, generate config
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
+ config --validate validate config + check tool availability
243
244
  notify-test send a test notification to configured webhooks
244
245
  task manage tasks and sessions (list, start, stop, new, rm, edit)
245
246
  tasks show task progress (from aoaoe.tasks.json)
package/dist/config.d.ts CHANGED
@@ -20,6 +20,7 @@ export declare function parseCliArgs(argv: string[]): {
20
20
  showHistory: boolean;
21
21
  showStatus: boolean;
22
22
  showConfig: boolean;
23
+ configValidate: boolean;
23
24
  notifyTest: boolean;
24
25
  runInit: boolean;
25
26
  initForce: boolean;
package/dist/config.js CHANGED
@@ -274,7 +274,7 @@ export function parseCliArgs(argv) {
274
274
  let initForce = false;
275
275
  let runTaskCli = false;
276
276
  let registerTitle;
277
- const defaults = { overrides, help: false, version: false, register: false, testContext: false, runTest: false, showTasks: false, showHistory: false, showStatus: false, showConfig: false, notifyTest: false, runInit: false, initForce: false, runTaskCli: false };
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 };
278
278
  // check for subcommand as first non-flag arg
279
279
  if (argv[2] === "test-context") {
280
280
  return { ...defaults, testContext: true };
@@ -295,7 +295,8 @@ export function parseCliArgs(argv) {
295
295
  return { ...defaults, showStatus: true };
296
296
  }
297
297
  if (argv[2] === "config") {
298
- return { ...defaults, showConfig: true };
298
+ const validate = argv.includes("--validate") || argv.includes("-V");
299
+ return { ...defaults, showConfig: true, configValidate: validate };
299
300
  }
300
301
  if (argv[2] === "notify-test") {
301
302
  return { ...defaults, notifyTest: true };
@@ -389,7 +390,7 @@ export function parseCliArgs(argv) {
389
390
  break;
390
391
  }
391
392
  }
392
- return { overrides, help, version, register: false, testContext: false, runTest: false, showTasks: false, showHistory: false, showStatus: false, showConfig: false, notifyTest: false, runInit: false, initForce: false, runTaskCli: false };
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 };
393
394
  }
394
395
  export function printHelp() {
395
396
  console.log(`aoaoe - autonomous supervisor for agent-of-empires sessions
@@ -407,6 +408,7 @@ commands:
407
408
  (none) start the supervisor daemon (interactive TUI)
408
409
  status quick daemon health check (is it running? what's it doing?)
409
410
  config show the effective resolved config (defaults + file)
411
+ config --validate validate config file + check tool availability
410
412
  notify-test send a test notification to configured webhooks
411
413
  task manage tasks and sessions (list, start, stop, new, rm, edit)
412
414
  tasks show task progress (from aoaoe.tasks.json)
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ import { runTaskCli, handleTaskSlashCommand } from "./task-cli.js";
18
18
  import { TUI } from "./tui.js";
19
19
  import { isDaemonRunningFromState } from "./chat.js";
20
20
  import { sendNotification, sendTestNotification } from "./notify.js";
21
- import { actionSession, actionDetail } from "./types.js";
21
+ import { actionSession, actionDetail, toActionLogEntry } from "./types.js";
22
22
  import { YELLOW, GREEN, DIM, BOLD, RED, RESET } from "./colors.js";
23
23
  import { readFileSync, existsSync, statSync, mkdirSync, writeFileSync, chmodSync } from "node:fs";
24
24
  import { resolve, dirname, join } from "node:path";
@@ -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, notifyTest, runInit, initForce, runTaskCli: isTaskCli, registerTitle } = parseCliArgs(process.argv);
31
+ const { overrides, help, version, register, testContext: isTestContext, runTest, showTasks, showHistory, showStatus, showConfig, configValidate, notifyTest, runInit, initForce, runTaskCli: isTaskCli, registerTitle } = parseCliArgs(process.argv);
32
32
  if (help) {
33
33
  printHelp();
34
34
  process.exit(0);
@@ -73,9 +73,14 @@ async function main() {
73
73
  showDaemonStatus();
74
74
  return;
75
75
  }
76
- // `aoaoe config` -- show effective resolved config
76
+ // `aoaoe config` -- show effective resolved config (with optional --validate)
77
77
  if (showConfig) {
78
- showEffectiveConfig();
78
+ if (configValidate) {
79
+ await runConfigValidation();
80
+ }
81
+ else {
82
+ showEffectiveConfig();
83
+ }
79
84
  return;
80
85
  }
81
86
  // `aoaoe notify-test` -- send a test notification to configured webhooks
@@ -1136,7 +1141,9 @@ async function showActionHistory() {
1136
1141
  console.log(` ${"─".repeat(70)}`);
1137
1142
  for (const line of recent) {
1138
1143
  try {
1139
- const entry = JSON.parse(line);
1144
+ const entry = toActionLogEntry(JSON.parse(line));
1145
+ if (!entry)
1146
+ continue; // skip malformed lines
1140
1147
  const time = new Date(entry.timestamp).toLocaleTimeString();
1141
1148
  const date = new Date(entry.timestamp).toLocaleDateString();
1142
1149
  const icon = entry.success ? `${GREEN}+${RESET}` : `${RED}!${RESET}`;
@@ -1146,7 +1153,7 @@ async function showActionHistory() {
1146
1153
  console.log(` ${icon} ${DIM}${date} ${time}${RESET} ${YELLOW}${actionName.padEnd(16)}${RESET} ${session.padEnd(10)} ${detail}`);
1147
1154
  }
1148
1155
  catch {
1149
- // skip malformed lines
1156
+ // skip unparseable JSON lines
1150
1157
  }
1151
1158
  }
1152
1159
  console.log(` ${"─".repeat(70)}`);
@@ -1155,14 +1162,18 @@ async function showActionHistory() {
1155
1162
  const actionCounts = new Map();
1156
1163
  for (const line of lines) {
1157
1164
  try {
1158
- const e = JSON.parse(line);
1165
+ const e = toActionLogEntry(JSON.parse(line));
1166
+ if (!e)
1167
+ continue;
1159
1168
  if (e.success)
1160
1169
  successes++;
1161
1170
  else
1162
1171
  failures++;
1163
1172
  actionCounts.set(e.action.action, (actionCounts.get(e.action.action) ?? 0) + 1);
1164
1173
  }
1165
- catch { }
1174
+ catch {
1175
+ // skip unparseable JSON lines
1176
+ }
1166
1177
  }
1167
1178
  const breakdown = [...actionCounts.entries()].sort((a, b) => b[1] - a[1]).map(([k, v]) => `${k}: ${v}`).join(", ");
1168
1179
  console.log(` total: ${lines.length} actions (${GREEN}${successes} ok${RESET}, ${RED}${failures} failed${RESET})`);
@@ -1234,6 +1245,124 @@ function showDaemonStatus() {
1234
1245
  }
1235
1246
  console.log("");
1236
1247
  }
1248
+ // `aoaoe config --validate` -- validate config file, field values, and tool availability
1249
+ async function runConfigValidation() {
1250
+ const configPath = findConfigFile();
1251
+ let checks = 0;
1252
+ let passed = 0;
1253
+ let warnings = 0;
1254
+ console.log("");
1255
+ console.log(" aoaoe — config validation");
1256
+ console.log(` ${"─".repeat(50)}`);
1257
+ // 1. config file exists
1258
+ checks++;
1259
+ if (configPath) {
1260
+ console.log(` ${GREEN}✓${RESET} config file found: ${configPath}`);
1261
+ passed++;
1262
+ }
1263
+ else {
1264
+ console.log(` ${YELLOW}!${RESET} no config file found (using defaults)`);
1265
+ console.log(` ${DIM}run 'aoaoe init' to create one${RESET}`);
1266
+ warnings++;
1267
+ }
1268
+ // 2. config parses + validates
1269
+ checks++;
1270
+ let config;
1271
+ try {
1272
+ const configResult = loadConfig();
1273
+ config = configResult;
1274
+ console.log(` ${GREEN}✓${RESET} config valid (all field values OK)`);
1275
+ passed++;
1276
+ }
1277
+ catch (err) {
1278
+ const msg = err instanceof Error ? err.message : String(err);
1279
+ console.log(` ${RED}✗${RESET} config validation failed:`);
1280
+ for (const line of msg.split("\n")) {
1281
+ console.log(` ${line}`);
1282
+ }
1283
+ console.log("");
1284
+ console.log(` ${passed}/${checks} checks passed, fix config errors and retry`);
1285
+ console.log("");
1286
+ process.exit(1);
1287
+ return; // unreachable, but satisfies TypeScript
1288
+ }
1289
+ // 3. required tools on PATH
1290
+ const tools = [
1291
+ { name: "aoe", label: "agent-of-empires CLI" },
1292
+ { name: "tmux", label: "terminal multiplexer" },
1293
+ ];
1294
+ if (config.reasoner === "opencode") {
1295
+ tools.push({ name: "opencode", label: "OpenCode CLI" });
1296
+ }
1297
+ else if (config.reasoner === "claude-code") {
1298
+ tools.push({ name: "claude", label: "Claude Code CLI" });
1299
+ }
1300
+ for (const tool of tools) {
1301
+ checks++;
1302
+ try {
1303
+ const { execFile: execFileCb } = await import("node:child_process");
1304
+ const { promisify } = await import("node:util");
1305
+ const execFileAsync = promisify(execFileCb);
1306
+ await execFileAsync("which", [tool.name]);
1307
+ console.log(` ${GREEN}✓${RESET} ${tool.name} found on PATH (${tool.label})`);
1308
+ passed++;
1309
+ }
1310
+ catch {
1311
+ console.log(` ${RED}✗${RESET} ${tool.name} not found on PATH (${tool.label})`);
1312
+ }
1313
+ }
1314
+ // 4. notifications config check
1315
+ checks++;
1316
+ if (config.notifications) {
1317
+ const hasWebhook = !!config.notifications.webhookUrl;
1318
+ const hasSlack = !!config.notifications.slackWebhookUrl;
1319
+ if (hasWebhook || hasSlack) {
1320
+ const targets = [hasWebhook && "webhook", hasSlack && "Slack"].filter(Boolean).join(" + ");
1321
+ console.log(` ${GREEN}✓${RESET} notifications configured (${targets})`);
1322
+ console.log(` ${DIM}run 'aoaoe notify-test' to verify delivery${RESET}`);
1323
+ passed++;
1324
+ }
1325
+ else {
1326
+ console.log(` ${YELLOW}!${RESET} notifications block exists but no webhook URLs configured`);
1327
+ warnings++;
1328
+ }
1329
+ }
1330
+ else {
1331
+ console.log(` ${DIM}○${RESET} notifications not configured (optional)`);
1332
+ passed++; // not configured is fine — it's optional
1333
+ }
1334
+ // 5. sessionDirs validation — check that mapped dirs exist
1335
+ if (config.sessionDirs && Object.keys(config.sessionDirs).length > 0) {
1336
+ const basePath = process.cwd();
1337
+ for (const [title, dir] of Object.entries(config.sessionDirs)) {
1338
+ checks++;
1339
+ const resolved = dir.startsWith("/") ? dir : resolve(basePath, dir);
1340
+ if (existsSync(resolved)) {
1341
+ console.log(` ${GREEN}✓${RESET} sessionDirs.${title} → ${resolved}`);
1342
+ passed++;
1343
+ }
1344
+ else {
1345
+ console.log(` ${YELLOW}!${RESET} sessionDirs.${title} → ${resolved} (not found)`);
1346
+ warnings++;
1347
+ }
1348
+ }
1349
+ }
1350
+ // summary
1351
+ const failed = checks - passed - warnings;
1352
+ console.log("");
1353
+ if (failed === 0 && warnings === 0) {
1354
+ console.log(` ${GREEN}${BOLD}all ${checks} checks passed${RESET}`);
1355
+ }
1356
+ else if (failed === 0) {
1357
+ console.log(` ${passed}/${checks} passed, ${YELLOW}${warnings} warning(s)${RESET}`);
1358
+ }
1359
+ else {
1360
+ console.log(` ${passed}/${checks} passed, ${RED}${failed} failed${RESET}${warnings > 0 ? `, ${YELLOW}${warnings} warning(s)${RESET}` : ""}`);
1361
+ }
1362
+ console.log("");
1363
+ if (failed > 0)
1364
+ process.exit(1);
1365
+ }
1237
1366
  // `aoaoe config` -- show the effective resolved config (defaults + file + any notes)
1238
1367
  function showEffectiveConfig() {
1239
1368
  const configPath = findConfigFile();
package/dist/types.d.ts CHANGED
@@ -175,4 +175,16 @@ export declare function toAoeSessionList(raw: unknown): Array<{
175
175
  title: string;
176
176
  }>;
177
177
  export declare function toReasonerBackend(raw: string): ReasonerBackend;
178
+ export interface ActionLogEntry {
179
+ timestamp: number;
180
+ action: {
181
+ action: string;
182
+ session?: string;
183
+ text?: string;
184
+ title?: string;
185
+ };
186
+ success: boolean;
187
+ detail: string;
188
+ }
189
+ export declare function toActionLogEntry(raw: unknown): ActionLogEntry | null;
178
190
  //# sourceMappingURL=types.d.ts.map
package/dist/types.js CHANGED
@@ -112,4 +112,31 @@ export function toReasonerBackend(raw) {
112
112
  return raw;
113
113
  throw new Error(`--reasoner must be "opencode" or "claude-code", got "${raw}"`);
114
114
  }
115
+ export function toActionLogEntry(raw) {
116
+ if (!raw || typeof raw !== "object")
117
+ return null;
118
+ const obj = raw;
119
+ if (typeof obj.timestamp !== "number")
120
+ return null;
121
+ if (typeof obj.success !== "boolean")
122
+ return null;
123
+ if (typeof obj.detail !== "string")
124
+ obj.detail = "";
125
+ if (!obj.action || typeof obj.action !== "object")
126
+ return null;
127
+ const action = obj.action;
128
+ if (typeof action.action !== "string")
129
+ return null;
130
+ return {
131
+ timestamp: obj.timestamp,
132
+ action: {
133
+ action: action.action,
134
+ session: typeof action.session === "string" ? action.session : undefined,
135
+ text: typeof action.text === "string" ? action.text : undefined,
136
+ title: typeof action.title === "string" ? action.title : undefined,
137
+ },
138
+ success: obj.success,
139
+ detail: typeof obj.detail === "string" ? obj.detail : "",
140
+ };
141
+ }
115
142
  //# sourceMappingURL=types.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aoaoe",
3
- "version": "0.53.0",
3
+ "version": "0.54.0",
4
4
  "description": "Autonomous supervisor for agent-of-empires sessions using OpenCode or Claude Code",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",