aoaoe 0.191.0 → 0.192.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/dist/backup.d.ts +11 -0
- package/dist/backup.js +121 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.js +18 -3
- package/dist/export.d.ts +3 -0
- package/dist/export.js +57 -0
- package/dist/index.js +65 -4
- package/dist/task-cli.js +16 -2
- package/dist/task-manager.d.ts +1 -0
- package/dist/task-manager.js +31 -0
- package/package.json +1 -1
package/dist/backup.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function createBackup(outputPath?: string): Promise<string>;
|
|
2
|
+
export declare function restoreBackup(inputPath: string): Promise<{
|
|
3
|
+
restored: string[];
|
|
4
|
+
skipped: string[];
|
|
5
|
+
}>;
|
|
6
|
+
export declare function formatBackupResult(path: string): string;
|
|
7
|
+
export declare function formatRestoreResult(result: {
|
|
8
|
+
restored: string[];
|
|
9
|
+
skipped: string[];
|
|
10
|
+
}): string;
|
|
11
|
+
//# sourceMappingURL=backup.d.ts.map
|
package/dist/backup.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// backup.ts — backup and restore aoaoe state + config for portability.
|
|
2
|
+
// backs up ~/.aoaoe/ contents to a timestamped tarball or directory.
|
|
3
|
+
import { existsSync, mkdirSync, readdirSync, copyFileSync, statSync } from "node:fs";
|
|
4
|
+
import { join, basename } from "node:path";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
import { exec } from "./shell.js";
|
|
7
|
+
import { DIM, GREEN, YELLOW, RESET } from "./colors.js";
|
|
8
|
+
const AOAOE_DIR = join(homedir(), ".aoaoe");
|
|
9
|
+
// files to include in backup (relative to ~/.aoaoe/)
|
|
10
|
+
const BACKUP_FILES = [
|
|
11
|
+
"aoaoe.config.json",
|
|
12
|
+
"task-state.json",
|
|
13
|
+
"supervisor-history.jsonl",
|
|
14
|
+
"pin-presets.json",
|
|
15
|
+
"templates.json",
|
|
16
|
+
"prompt-templates.json",
|
|
17
|
+
"tui-prefs.json",
|
|
18
|
+
];
|
|
19
|
+
export async function createBackup(outputPath) {
|
|
20
|
+
if (!existsSync(AOAOE_DIR)) {
|
|
21
|
+
throw new Error("~/.aoaoe/ does not exist — nothing to back up");
|
|
22
|
+
}
|
|
23
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
24
|
+
const defaultPath = join(process.cwd(), `aoaoe-backup-${timestamp}`);
|
|
25
|
+
const target = outputPath || defaultPath;
|
|
26
|
+
// also include aoaoe.tasks.json from cwd if it exists
|
|
27
|
+
const cwdTasksFile = join(process.cwd(), "aoaoe.tasks.json");
|
|
28
|
+
// try tar first (most portable)
|
|
29
|
+
const tarTarget = target.endsWith(".tar.gz") ? target : `${target}.tar.gz`;
|
|
30
|
+
const filesToBackup = [];
|
|
31
|
+
for (const f of BACKUP_FILES) {
|
|
32
|
+
const full = join(AOAOE_DIR, f);
|
|
33
|
+
if (existsSync(full))
|
|
34
|
+
filesToBackup.push(full);
|
|
35
|
+
}
|
|
36
|
+
if (existsSync(cwdTasksFile))
|
|
37
|
+
filesToBackup.push(cwdTasksFile);
|
|
38
|
+
if (filesToBackup.length === 0) {
|
|
39
|
+
throw new Error("no files to back up");
|
|
40
|
+
}
|
|
41
|
+
const result = await exec("tar", [
|
|
42
|
+
"czf", tarTarget,
|
|
43
|
+
...filesToBackup.map((f) => f),
|
|
44
|
+
]);
|
|
45
|
+
if (result.exitCode === 0) {
|
|
46
|
+
const size = statSync(tarTarget).size;
|
|
47
|
+
const sizeStr = size < 1024 ? `${size}B` : `${(size / 1024).toFixed(1)}KB`;
|
|
48
|
+
return `${tarTarget} (${filesToBackup.length} files, ${sizeStr})`;
|
|
49
|
+
}
|
|
50
|
+
// tar failed — fall back to directory copy
|
|
51
|
+
mkdirSync(target, { recursive: true });
|
|
52
|
+
let count = 0;
|
|
53
|
+
for (const full of filesToBackup) {
|
|
54
|
+
const name = basename(full);
|
|
55
|
+
copyFileSync(full, join(target, name));
|
|
56
|
+
count++;
|
|
57
|
+
}
|
|
58
|
+
return `${target}/ (${count} files copied)`;
|
|
59
|
+
}
|
|
60
|
+
export async function restoreBackup(inputPath) {
|
|
61
|
+
const restored = [];
|
|
62
|
+
const skipped = [];
|
|
63
|
+
if (!existsSync(inputPath)) {
|
|
64
|
+
throw new Error(`backup not found: ${inputPath}`);
|
|
65
|
+
}
|
|
66
|
+
if (!existsSync(AOAOE_DIR)) {
|
|
67
|
+
mkdirSync(AOAOE_DIR, { recursive: true });
|
|
68
|
+
}
|
|
69
|
+
// try tar extraction
|
|
70
|
+
if (inputPath.endsWith(".tar.gz") || inputPath.endsWith(".tgz")) {
|
|
71
|
+
const result = await exec("tar", ["xzf", inputPath, "-C", "/"]);
|
|
72
|
+
if (result.exitCode === 0) {
|
|
73
|
+
// check what was restored
|
|
74
|
+
for (const f of BACKUP_FILES) {
|
|
75
|
+
if (existsSync(join(AOAOE_DIR, f)))
|
|
76
|
+
restored.push(f);
|
|
77
|
+
}
|
|
78
|
+
return { restored, skipped };
|
|
79
|
+
}
|
|
80
|
+
throw new Error(`tar extraction failed: ${result.stderr}`);
|
|
81
|
+
}
|
|
82
|
+
// directory restore
|
|
83
|
+
if (!statSync(inputPath).isDirectory()) {
|
|
84
|
+
throw new Error(`${inputPath} is not a directory or tar.gz archive`);
|
|
85
|
+
}
|
|
86
|
+
const files = readdirSync(inputPath);
|
|
87
|
+
for (const f of files) {
|
|
88
|
+
const src = join(inputPath, f);
|
|
89
|
+
if (f === "aoaoe.tasks.json") {
|
|
90
|
+
// restore to cwd
|
|
91
|
+
copyFileSync(src, join(process.cwd(), f));
|
|
92
|
+
restored.push(f);
|
|
93
|
+
}
|
|
94
|
+
else if (BACKUP_FILES.includes(f)) {
|
|
95
|
+
copyFileSync(src, join(AOAOE_DIR, f));
|
|
96
|
+
restored.push(f);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
skipped.push(f);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return { restored, skipped };
|
|
103
|
+
}
|
|
104
|
+
export function formatBackupResult(path) {
|
|
105
|
+
return ` ${GREEN}✓${RESET} backup created: ${path}`;
|
|
106
|
+
}
|
|
107
|
+
export function formatRestoreResult(result) {
|
|
108
|
+
const lines = [];
|
|
109
|
+
if (result.restored.length > 0) {
|
|
110
|
+
lines.push(` ${GREEN}✓${RESET} restored ${result.restored.length} file(s):`);
|
|
111
|
+
for (const f of result.restored)
|
|
112
|
+
lines.push(` ${DIM}${f}${RESET}`);
|
|
113
|
+
}
|
|
114
|
+
if (result.skipped.length > 0) {
|
|
115
|
+
lines.push(` ${YELLOW}!${RESET} skipped ${result.skipped.length} file(s):`);
|
|
116
|
+
for (const f of result.skipped)
|
|
117
|
+
lines.push(` ${DIM}${f}${RESET}`);
|
|
118
|
+
}
|
|
119
|
+
return lines.join("\n");
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=backup.js.map
|
package/dist/config.d.ts
CHANGED
|
@@ -61,6 +61,10 @@ export declare function parseCliArgs(argv: string[]): {
|
|
|
61
61
|
configDiff: boolean;
|
|
62
62
|
notifyTest: boolean;
|
|
63
63
|
runDoctor: boolean;
|
|
64
|
+
runBackup: boolean;
|
|
65
|
+
backupOutput?: string;
|
|
66
|
+
runRestore: boolean;
|
|
67
|
+
restoreInput?: string;
|
|
64
68
|
runLogs: boolean;
|
|
65
69
|
logsActions: boolean;
|
|
66
70
|
logsGrep?: string;
|
|
@@ -69,6 +73,7 @@ export declare function parseCliArgs(argv: string[]): {
|
|
|
69
73
|
exportFormat?: string;
|
|
70
74
|
exportOutput?: string;
|
|
71
75
|
exportLast?: string;
|
|
76
|
+
exportTasks: boolean;
|
|
72
77
|
runInit: boolean;
|
|
73
78
|
initForce: boolean;
|
|
74
79
|
runTaskCli: boolean;
|
package/dist/config.js
CHANGED
|
@@ -333,7 +333,7 @@ export function parseCliArgs(argv) {
|
|
|
333
333
|
let initForce = false;
|
|
334
334
|
let runTaskCli = false;
|
|
335
335
|
let registerTitle;
|
|
336
|
-
const defaults = { overrides, help: false, version: false, register: false, testContext: false, runTest: false, showTasks: false, showTasksJson: false, runProgress: false, progressSince: undefined, progressJson: false, runHealth: false, healthJson: false, runSummary: false, runAdopt: false, adoptTemplate: undefined, showHistory: false, showStatus: false, runRunbook: false, runbookJson: false, runbookSection: undefined, runIncident: false, incidentSince: undefined, incidentLimit: undefined, incidentJson: false, incidentNdjson: false, incidentWatch: false, incidentChangesOnly: false, incidentHeartbeatSec: undefined, incidentIntervalMs: undefined, runSupervisor: false, supervisorAll: false, supervisorSince: undefined, supervisorLimit: undefined, supervisorJson: false, supervisorNdjson: false, supervisorWatch: false, supervisorChangesOnly: false, supervisorHeartbeatSec: undefined, supervisorIntervalMs: undefined, showConfig: false, configValidate: false, configDiff: false, notifyTest: false, runDoctor: false, runLogs: false, logsActions: false, logsGrep: undefined, logsCount: undefined, runExport: false, exportFormat: undefined, exportOutput: undefined, exportLast: undefined, runInit: false, initForce: false, runTaskCli: false, runTail: false, tailFollow: false, tailCount: undefined, runStats: false, statsLast: undefined, runReplay: false, replaySpeed: undefined, replayLast: undefined };
|
|
336
|
+
const defaults = { overrides, help: false, version: false, register: false, testContext: false, runTest: false, showTasks: false, showTasksJson: false, runProgress: false, progressSince: undefined, progressJson: false, runHealth: false, healthJson: false, runSummary: false, runAdopt: false, adoptTemplate: undefined, showHistory: false, showStatus: false, runRunbook: false, runbookJson: false, runbookSection: undefined, runIncident: false, incidentSince: undefined, incidentLimit: undefined, incidentJson: false, incidentNdjson: false, incidentWatch: false, incidentChangesOnly: false, incidentHeartbeatSec: undefined, incidentIntervalMs: undefined, runSupervisor: false, supervisorAll: false, supervisorSince: undefined, supervisorLimit: undefined, supervisorJson: false, supervisorNdjson: false, supervisorWatch: false, supervisorChangesOnly: false, supervisorHeartbeatSec: undefined, supervisorIntervalMs: undefined, showConfig: false, configValidate: false, configDiff: false, notifyTest: false, runDoctor: false, runBackup: false, backupOutput: undefined, runRestore: false, restoreInput: undefined, runLogs: false, logsActions: false, logsGrep: undefined, logsCount: undefined, runExport: false, exportFormat: undefined, exportOutput: undefined, exportLast: undefined, exportTasks: false, runInit: false, initForce: false, runTaskCli: false, runTail: false, tailFollow: false, tailCount: undefined, runStats: false, statsLast: undefined, runReplay: false, replaySpeed: undefined, replayLast: undefined };
|
|
337
337
|
// check for subcommand as first non-flag arg
|
|
338
338
|
if (argv[2] === "test-context") {
|
|
339
339
|
return { ...defaults, testContext: true };
|
|
@@ -531,6 +531,14 @@ export function parseCliArgs(argv) {
|
|
|
531
531
|
if (argv[2] === "doctor") {
|
|
532
532
|
return { ...defaults, runDoctor: true };
|
|
533
533
|
}
|
|
534
|
+
if (argv[2] === "backup") {
|
|
535
|
+
const output = argv[3] && !argv[3].startsWith("-") ? argv[3] : undefined;
|
|
536
|
+
return { ...defaults, runBackup: true, backupOutput: output };
|
|
537
|
+
}
|
|
538
|
+
if (argv[2] === "restore") {
|
|
539
|
+
const input = argv[3] && !argv[3].startsWith("-") ? argv[3] : undefined;
|
|
540
|
+
return { ...defaults, runRestore: true, restoreInput: input };
|
|
541
|
+
}
|
|
534
542
|
if (argv[2] === "logs") {
|
|
535
543
|
const actions = argv.includes("--actions") || argv.includes("-a");
|
|
536
544
|
let grep;
|
|
@@ -551,7 +559,10 @@ export function parseCliArgs(argv) {
|
|
|
551
559
|
let format;
|
|
552
560
|
let output;
|
|
553
561
|
let last;
|
|
562
|
+
const exportTasks = argv.includes("--tasks");
|
|
554
563
|
for (let i = 3; i < argv.length; i++) {
|
|
564
|
+
if (argv[i] === "--tasks")
|
|
565
|
+
continue;
|
|
555
566
|
if ((argv[i] === "--format" || argv[i] === "-f") && argv[i + 1]) {
|
|
556
567
|
format = argv[++i];
|
|
557
568
|
}
|
|
@@ -562,7 +573,7 @@ export function parseCliArgs(argv) {
|
|
|
562
573
|
last = argv[++i];
|
|
563
574
|
}
|
|
564
575
|
}
|
|
565
|
-
return { ...defaults, runExport: true, exportFormat: format, exportOutput: output, exportLast: last };
|
|
576
|
+
return { ...defaults, runExport: true, exportFormat: format, exportOutput: output, exportLast: last, exportTasks };
|
|
566
577
|
}
|
|
567
578
|
if (argv[2] === "init") {
|
|
568
579
|
const force = argv.includes("--force") || argv.includes("-f");
|
|
@@ -717,7 +728,7 @@ export function parseCliArgs(argv) {
|
|
|
717
728
|
break;
|
|
718
729
|
}
|
|
719
730
|
}
|
|
720
|
-
return { overrides, help, version, register: false, testContext: false, runTest: false, showTasks: false, showTasksJson: false, runProgress: false, progressSince: undefined, progressJson: false, runHealth: false, healthJson: false, runSummary: false, runAdopt: false, adoptTemplate: undefined, showHistory: false, showStatus: false, runRunbook: false, runbookJson: false, runbookSection: undefined, runIncident: false, incidentSince: undefined, incidentLimit: undefined, incidentJson: false, incidentNdjson: false, incidentWatch: false, incidentChangesOnly: false, incidentHeartbeatSec: undefined, incidentIntervalMs: undefined, runSupervisor: false, supervisorAll: false, supervisorSince: undefined, supervisorLimit: undefined, supervisorJson: false, supervisorNdjson: false, supervisorWatch: false, supervisorChangesOnly: false, supervisorHeartbeatSec: undefined, supervisorIntervalMs: undefined, showConfig: false, configValidate: false, configDiff: false, notifyTest: false, runDoctor: false, runLogs: false, logsActions: false, logsGrep: undefined, logsCount: undefined, runExport: false, exportFormat: undefined, exportOutput: undefined, exportLast: undefined, runInit: false, initForce: false, runTaskCli: false, runTail: false, tailFollow: false, tailCount: undefined, runStats: false, statsLast: undefined, runReplay: false, replaySpeed: undefined, replayLast: undefined };
|
|
731
|
+
return { overrides, help, version, register: false, testContext: false, runTest: false, showTasks: false, showTasksJson: false, runProgress: false, progressSince: undefined, progressJson: false, runHealth: false, healthJson: false, runSummary: false, runAdopt: false, adoptTemplate: undefined, showHistory: false, showStatus: false, runRunbook: false, runbookJson: false, runbookSection: undefined, runIncident: false, incidentSince: undefined, incidentLimit: undefined, incidentJson: false, incidentNdjson: false, incidentWatch: false, incidentChangesOnly: false, incidentHeartbeatSec: undefined, incidentIntervalMs: undefined, runSupervisor: false, supervisorAll: false, supervisorSince: undefined, supervisorLimit: undefined, supervisorJson: false, supervisorNdjson: false, supervisorWatch: false, supervisorChangesOnly: false, supervisorHeartbeatSec: undefined, supervisorIntervalMs: undefined, showConfig: false, configValidate: false, configDiff: false, notifyTest: false, runDoctor: false, runBackup: false, backupOutput: undefined, runRestore: false, restoreInput: undefined, runLogs: false, logsActions: false, logsGrep: undefined, logsCount: undefined, runExport: false, exportFormat: undefined, exportOutput: undefined, exportLast: undefined, exportTasks: false, runInit: false, initForce: false, runTaskCli: false, runTail: false, tailFollow: false, tailCount: undefined, runStats: false, statsLast: undefined, runReplay: false, replaySpeed: undefined, replayLast: undefined };
|
|
721
732
|
}
|
|
722
733
|
export function printHelp() {
|
|
723
734
|
console.log(`aoaoe - autonomous supervisor for agent-of-empires sessions
|
|
@@ -761,6 +772,8 @@ commands:
|
|
|
761
772
|
config --validate validate config + check tool availability
|
|
762
773
|
config --diff show only fields that differ from defaults
|
|
763
774
|
notify-test send a test notification to configured webhooks
|
|
775
|
+
backup [path] backup ~/.aoaoe/ state + config to tarball
|
|
776
|
+
restore <path> restore from backup tarball or directory
|
|
764
777
|
doctor comprehensive health check (config, tools, daemon, disk)
|
|
765
778
|
logs show recent conversation log entries (last 50)
|
|
766
779
|
logs --actions show action log entries (from ~/.aoaoe/actions.log)
|
|
@@ -770,6 +783,8 @@ commands:
|
|
|
770
783
|
export --format <json|markdown> output format (default: json)
|
|
771
784
|
export --output <file> write to file (default: stdout)
|
|
772
785
|
export --last <duration> time window: 1h, 6h, 24h, 7d (default: 24h)
|
|
786
|
+
export --tasks export task history instead of timeline
|
|
787
|
+
export --tasks --format md export task history as markdown
|
|
773
788
|
stats show aggregate daemon statistics (actions, sessions, activity)
|
|
774
789
|
stats --last <duration> time window: 1h, 6h, 24h, 7d (default: all time)
|
|
775
790
|
replay play back tui-history.jsonl like a movie with simulated timing
|
package/dist/export.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TaskState } from "./types.js";
|
|
1
2
|
import type { HistoryEntry } from "./tui-history.js";
|
|
2
3
|
export interface TimelineEntry {
|
|
3
4
|
ts: number;
|
|
@@ -14,4 +15,6 @@ export declare function filterByAge(entries: TimelineEntry[], maxAgeMs: number,
|
|
|
14
15
|
export declare function parseDuration(input: string): number | null;
|
|
15
16
|
export declare function formatTimelineJson(entries: TimelineEntry[]): string;
|
|
16
17
|
export declare function formatTimelineMarkdown(entries: TimelineEntry[]): string;
|
|
18
|
+
export declare function formatTaskExportJson(tasks: TaskState[]): string;
|
|
19
|
+
export declare function formatTaskExportMarkdown(tasks: TaskState[]): string;
|
|
17
20
|
//# sourceMappingURL=export.d.ts.map
|
package/dist/export.js
CHANGED
|
@@ -129,4 +129,61 @@ export function formatTimelineMarkdown(entries) {
|
|
|
129
129
|
lines.push("");
|
|
130
130
|
return lines.join("\n");
|
|
131
131
|
}
|
|
132
|
+
// ── task export ─────────────────────────────────────────────────────────────
|
|
133
|
+
export function formatTaskExportJson(tasks) {
|
|
134
|
+
const payload = tasks.map((t) => ({
|
|
135
|
+
session: t.sessionTitle,
|
|
136
|
+
repo: t.repo,
|
|
137
|
+
status: t.status,
|
|
138
|
+
goal: t.goal,
|
|
139
|
+
dependsOn: t.dependsOn ?? [],
|
|
140
|
+
createdAt: t.createdAt ?? null,
|
|
141
|
+
completedAt: t.completedAt ?? null,
|
|
142
|
+
lastProgressAt: t.lastProgressAt ?? null,
|
|
143
|
+
progressCount: t.progress.length,
|
|
144
|
+
progress: t.progress.map((p) => ({
|
|
145
|
+
at: p.at,
|
|
146
|
+
time: new Date(p.at).toISOString(),
|
|
147
|
+
summary: p.summary,
|
|
148
|
+
})),
|
|
149
|
+
}));
|
|
150
|
+
return JSON.stringify(payload, null, 2);
|
|
151
|
+
}
|
|
152
|
+
export function formatTaskExportMarkdown(tasks) {
|
|
153
|
+
const lines = [];
|
|
154
|
+
lines.push("# Task History Export");
|
|
155
|
+
lines.push("");
|
|
156
|
+
lines.push(`Generated: ${new Date().toISOString()}`);
|
|
157
|
+
lines.push(`Tasks: ${tasks.length}`);
|
|
158
|
+
lines.push("");
|
|
159
|
+
for (const t of tasks) {
|
|
160
|
+
const icon = t.status === "completed" ? "✓"
|
|
161
|
+
: t.status === "active" ? "●"
|
|
162
|
+
: t.status === "paused" ? "◎"
|
|
163
|
+
: t.status === "failed" ? "✗"
|
|
164
|
+
: "○";
|
|
165
|
+
lines.push(`## ${icon} ${t.sessionTitle} (${t.status})`);
|
|
166
|
+
lines.push("");
|
|
167
|
+
lines.push(`- **Goal**: ${t.goal}`);
|
|
168
|
+
lines.push(`- **Repo**: ${t.repo}`);
|
|
169
|
+
if (t.dependsOn?.length)
|
|
170
|
+
lines.push(`- **Depends on**: ${t.dependsOn.join(", ")}`);
|
|
171
|
+
if (t.createdAt)
|
|
172
|
+
lines.push(`- **Created**: ${new Date(t.createdAt).toISOString()}`);
|
|
173
|
+
if (t.completedAt)
|
|
174
|
+
lines.push(`- **Completed**: ${new Date(t.completedAt).toISOString()}`);
|
|
175
|
+
lines.push(`- **Progress entries**: ${t.progress.length}`);
|
|
176
|
+
lines.push("");
|
|
177
|
+
if (t.progress.length > 0) {
|
|
178
|
+
lines.push("### Progress");
|
|
179
|
+
lines.push("");
|
|
180
|
+
for (const p of t.progress) {
|
|
181
|
+
const time = new Date(p.at).toLocaleString();
|
|
182
|
+
lines.push(`- **${time}**: ${p.summary}`);
|
|
183
|
+
}
|
|
184
|
+
lines.push("");
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return lines.join("\n");
|
|
188
|
+
}
|
|
132
189
|
//# sourceMappingURL=export.js.map
|
package/dist/index.js
CHANGED
|
@@ -28,7 +28,7 @@ import { savePreset, deletePreset, getPreset, formatPresetList } from "./pin-pre
|
|
|
28
28
|
import { resolvePromptTemplate, formatPromptTemplateList } from "./reasoner/prompt-templates.js";
|
|
29
29
|
import { formatHealthReport, computeAllHealth } from "./health-score.js";
|
|
30
30
|
import { ConfigWatcher, formatConfigChange } from "./config-watcher.js";
|
|
31
|
-
import { parseActionLogEntries, parseActivityEntries, mergeTimeline, filterByAge, parseDuration, formatTimelineJson, formatTimelineMarkdown } from "./export.js";
|
|
31
|
+
import { parseActionLogEntries, parseActivityEntries, mergeTimeline, filterByAge, parseDuration, formatTimelineJson, formatTimelineMarkdown, formatTaskExportJson, formatTaskExportMarkdown } from "./export.js";
|
|
32
32
|
import { actionSession, actionDetail, toActionLogEntry } from "./types.js";
|
|
33
33
|
import { YELLOW, GREEN, DIM, BOLD, RED, RESET } from "./colors.js";
|
|
34
34
|
import { readFileSync, existsSync, statSync, mkdirSync, writeFileSync, chmodSync } from "node:fs";
|
|
@@ -40,7 +40,7 @@ const AOAOE_DIR = join(homedir(), ".aoaoe"); // watch dir for wakeable sleep
|
|
|
40
40
|
const INPUT_FILE = join(AOAOE_DIR, "pending-input.txt"); // file IPC from chat.ts
|
|
41
41
|
const TASK_RECONCILE_EVERY_POLLS = 6;
|
|
42
42
|
async function main() {
|
|
43
|
-
const { overrides, help, version, register, testContext: isTestContext, runTest, showTasks, showTasksJson, runProgress, progressSince, progressJson, runHealth, healthJson, runSummary, runAdopt, adoptTemplate, showHistory, showStatus, runRunbook, runbookJson, runbookSection, runIncident, incidentSince, incidentLimit, incidentJson, incidentNdjson, incidentWatch, incidentChangesOnly, incidentHeartbeatSec, incidentIntervalMs, runSupervisor, supervisorAll, supervisorSince, supervisorLimit, supervisorJson, supervisorNdjson, supervisorWatch, supervisorChangesOnly, supervisorHeartbeatSec, supervisorIntervalMs, showConfig, configValidate, configDiff, notifyTest, runDoctor, runLogs, logsActions, logsGrep, logsCount, runExport, exportFormat, exportOutput, exportLast, runInit, initForce, runTaskCli: isTaskCli, runTail: isTail, tailFollow, tailCount, runStats: isStats, statsLast, runReplay: isReplay, replaySpeed, replayLast, registerTitle } = parseCliArgs(process.argv);
|
|
43
|
+
const { overrides, help, version, register, testContext: isTestContext, runTest, showTasks, showTasksJson, runProgress, progressSince, progressJson, runHealth, healthJson, runSummary, runAdopt, adoptTemplate, showHistory, showStatus, runRunbook, runbookJson, runbookSection, runIncident, incidentSince, incidentLimit, incidentJson, incidentNdjson, incidentWatch, incidentChangesOnly, incidentHeartbeatSec, incidentIntervalMs, runSupervisor, supervisorAll, supervisorSince, supervisorLimit, supervisorJson, supervisorNdjson, supervisorWatch, supervisorChangesOnly, supervisorHeartbeatSec, supervisorIntervalMs, showConfig, configValidate, configDiff, notifyTest, runDoctor, runBackup, backupOutput, runRestore, restoreInput, runLogs, logsActions, logsGrep, logsCount, runExport, exportFormat, exportOutput, exportLast, runInit, initForce, runTaskCli: isTaskCli, runTail: isTail, tailFollow, tailCount, runStats: isStats, statsLast, runReplay: isReplay, replaySpeed, replayLast, registerTitle } = parseCliArgs(process.argv);
|
|
44
44
|
if (help) {
|
|
45
45
|
printHelp();
|
|
46
46
|
process.exit(0);
|
|
@@ -71,7 +71,7 @@ async function main() {
|
|
|
71
71
|
return;
|
|
72
72
|
}
|
|
73
73
|
// suppress noisy [tasks] [config] log lines in one-shot CLI commands
|
|
74
|
-
if (showTasks || runProgress || runHealth || runSummary || runAdopt || showStatus || runRunbook || runIncident || runSupervisor) {
|
|
74
|
+
if (showTasks || runProgress || runHealth || runSummary || runAdopt || runBackup || runRestore || runExport || showStatus || runRunbook || runIncident || runSupervisor) {
|
|
75
75
|
process.env.AOAOE_QUIET = "1";
|
|
76
76
|
}
|
|
77
77
|
// `aoaoe tasks` -- show current task state
|
|
@@ -147,6 +147,33 @@ async function main() {
|
|
|
147
147
|
await runDoctorCheck();
|
|
148
148
|
return;
|
|
149
149
|
}
|
|
150
|
+
if (runBackup) {
|
|
151
|
+
try {
|
|
152
|
+
const result = await createBackup(backupOutput);
|
|
153
|
+
console.log(formatBackupResult(result));
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
console.error(`backup failed: ${err instanceof Error ? err.message : err}`);
|
|
157
|
+
process.exitCode = 1;
|
|
158
|
+
}
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (runRestore) {
|
|
162
|
+
if (!restoreInput) {
|
|
163
|
+
console.error("usage: aoaoe restore <backup-path>");
|
|
164
|
+
process.exitCode = 1;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
const result = await restoreBackup(restoreInput);
|
|
169
|
+
console.log(formatRestoreResult(result));
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
console.error(`restore failed: ${err instanceof Error ? err.message : err}`);
|
|
173
|
+
process.exitCode = 1;
|
|
174
|
+
}
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
150
177
|
// `aoaoe logs` -- show conversation or action log entries
|
|
151
178
|
if (runLogs) {
|
|
152
179
|
await showLogs(logsActions, logsGrep, logsCount);
|
|
@@ -154,9 +181,42 @@ async function main() {
|
|
|
154
181
|
}
|
|
155
182
|
// `aoaoe export` -- export session timeline as JSON or Markdown
|
|
156
183
|
if (runExport) {
|
|
157
|
-
|
|
184
|
+
const { exportTasks } = parseCliArgs(process.argv);
|
|
185
|
+
if (exportTasks) {
|
|
186
|
+
await runTaskExport(exportFormat, exportOutput);
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
await runTimelineExport(exportFormat, exportOutput, exportLast);
|
|
190
|
+
}
|
|
158
191
|
return;
|
|
159
192
|
}
|
|
193
|
+
async function runTaskExport(format, output) {
|
|
194
|
+
const fmt = format ?? "json";
|
|
195
|
+
if (fmt !== "json" && fmt !== "markdown" && fmt !== "md") {
|
|
196
|
+
console.error(`error: --format must be "json" or "markdown", got "${fmt}"`);
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
const basePath = process.cwd();
|
|
200
|
+
const defs = loadTaskDefinitions(basePath);
|
|
201
|
+
const taskProfiles = resolveProfiles(loadConfig());
|
|
202
|
+
const tm = defs.length > 0 ? new TaskManager(basePath, defs, taskProfiles) : undefined;
|
|
203
|
+
const tasks = tm?.tasks ?? [...loadTaskState().values()];
|
|
204
|
+
if (tasks.length === 0) {
|
|
205
|
+
console.error("no tasks to export");
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const isMarkdown = fmt === "markdown" || fmt === "md";
|
|
209
|
+
const content = isMarkdown ? formatTaskExportMarkdown(tasks) : formatTaskExportJson(tasks);
|
|
210
|
+
if (output) {
|
|
211
|
+
writeFileSync(output, content);
|
|
212
|
+
console.log(`exported ${tasks.length} task(s) to ${output}`);
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
process.stdout.write(content);
|
|
216
|
+
if (!content.endsWith("\n"))
|
|
217
|
+
process.stdout.write("\n");
|
|
218
|
+
}
|
|
219
|
+
}
|
|
160
220
|
// `aoaoe stats` -- show aggregate daemon statistics
|
|
161
221
|
if (isStats) {
|
|
162
222
|
await runStatsCommand(statsLast);
|
|
@@ -4469,6 +4529,7 @@ async function showProgressDigest(since, asJson = false) {
|
|
|
4469
4529
|
console.log(formatProgressDigest(tasks, maxAgeMs));
|
|
4470
4530
|
}
|
|
4471
4531
|
import { resolveTemplate } from "./task-templates.js";
|
|
4532
|
+
import { createBackup, restoreBackup, formatBackupResult, formatRestoreResult } from "./backup.js";
|
|
4472
4533
|
// adopt untracked live AoE sessions as tasks with optional template goal.
|
|
4473
4534
|
async function adoptUntrackedSessions(templateName) {
|
|
4474
4535
|
const basePath = process.cwd();
|
package/dist/task-cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import { resolve, basename } from "node:path";
|
|
|
6
6
|
import { buildProfileListArgs } from "./poller.js";
|
|
7
7
|
import { loadConfig } from "./config.js";
|
|
8
8
|
import { resolveProfiles } from "./tui.js";
|
|
9
|
-
import { loadTaskState, saveTaskState, formatTaskTable, syncTaskDefinitionsFromState, taskStateKey, resolveTaskRepoPath, TaskManager, loadTaskDefinitions, injectGoalToSession } from "./task-manager.js";
|
|
9
|
+
import { loadTaskState, saveTaskState, formatTaskTable, formatTaskHistory, syncTaskDefinitionsFromState, taskStateKey, resolveTaskRepoPath, TaskManager, loadTaskDefinitions, injectGoalToSession } from "./task-manager.js";
|
|
10
10
|
import { goalToList } from "./types.js";
|
|
11
11
|
import { resolveTemplate, formatTemplateList } from "./task-templates.js";
|
|
12
12
|
import { BOLD, DIM, GREEN, YELLOW, RED, RESET } from "./colors.js";
|
|
@@ -26,6 +26,7 @@ function getTaskProfiles() {
|
|
|
26
26
|
function taskCommandHelp(prefix = "aoaoe task") {
|
|
27
27
|
return [
|
|
28
28
|
`${prefix} list show tracked tasks`,
|
|
29
|
+
`${prefix} history [session] show progress history (all or one session)`,
|
|
29
30
|
`${prefix} templates show available task templates`,
|
|
30
31
|
`${prefix} reconcile link/create sessions now`,
|
|
31
32
|
`${prefix} new <title> <path> create task + session`,
|
|
@@ -454,6 +455,13 @@ export async function runTaskCli(argv) {
|
|
|
454
455
|
console.log(formatTemplateList());
|
|
455
456
|
return;
|
|
456
457
|
}
|
|
458
|
+
case "history": {
|
|
459
|
+
const states = loadTaskState();
|
|
460
|
+
const tasks = [...states.values()];
|
|
461
|
+
const sessionFilter = args[0] || undefined;
|
|
462
|
+
console.log(formatTaskHistory(tasks, sessionFilter));
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
457
465
|
case "reconcile": {
|
|
458
466
|
const basePath = process.cwd();
|
|
459
467
|
const defs = loadTaskDefinitions(basePath);
|
|
@@ -623,6 +631,12 @@ export async function handleTaskSlashCommand(args) {
|
|
|
623
631
|
if (sub === "templates" || sub === "template") {
|
|
624
632
|
return formatTemplateList();
|
|
625
633
|
}
|
|
626
|
-
|
|
634
|
+
if (sub === "history") {
|
|
635
|
+
const states = loadTaskState();
|
|
636
|
+
const tasks = [...states.values()];
|
|
637
|
+
const sessionFilter = rest[0] || undefined;
|
|
638
|
+
return formatTaskHistory(tasks, sessionFilter);
|
|
639
|
+
}
|
|
640
|
+
return "usage: /task [list|start|stop|edit|new|rm|reconcile|templates|history|help] [args]";
|
|
627
641
|
}
|
|
628
642
|
//# sourceMappingURL=task-cli.js.map
|
package/dist/task-manager.d.ts
CHANGED
|
@@ -43,6 +43,7 @@ export declare function deriveTitle(repo: string): string;
|
|
|
43
43
|
*/
|
|
44
44
|
export declare function readNextRoadmapItems(basePath: string, maxItems?: number): string;
|
|
45
45
|
export declare function formatTaskTable(states: Map<string, TaskState> | TaskState[]): string;
|
|
46
|
+
export declare function formatTaskHistory(tasks: TaskState[], sessionFilter?: string): string;
|
|
46
47
|
export declare function formatAgo(ms: number): string;
|
|
47
48
|
export declare function formatProgressDigest(tasks: TaskState[], maxAgeMs?: number): string;
|
|
48
49
|
//# sourceMappingURL=task-manager.d.ts.map
|
package/dist/task-manager.js
CHANGED
|
@@ -629,6 +629,37 @@ export function formatTaskTable(states) {
|
|
|
629
629
|
}
|
|
630
630
|
return lines.join("\n");
|
|
631
631
|
}
|
|
632
|
+
// format detailed progress history for one or all tasks.
|
|
633
|
+
export function formatTaskHistory(tasks, sessionFilter) {
|
|
634
|
+
const filtered = sessionFilter
|
|
635
|
+
? tasks.filter((t) => t.sessionTitle.toLowerCase() === sessionFilter.toLowerCase()
|
|
636
|
+
|| t.sessionTitle.toLowerCase().includes(sessionFilter.toLowerCase()))
|
|
637
|
+
: tasks;
|
|
638
|
+
if (filtered.length === 0) {
|
|
639
|
+
return sessionFilter ? ` no task found matching "${sessionFilter}"` : " (no tasks)";
|
|
640
|
+
}
|
|
641
|
+
const lines = [];
|
|
642
|
+
for (const t of filtered) {
|
|
643
|
+
const statusIcon = t.status === "completed" ? "✓"
|
|
644
|
+
: t.status === "active" ? "●"
|
|
645
|
+
: t.status === "paused" ? "◎"
|
|
646
|
+
: t.status === "failed" ? "✗"
|
|
647
|
+
: "○";
|
|
648
|
+
lines.push(` ${statusIcon} ${BOLD}${t.sessionTitle}${RESET} ${DIM}(${t.status})${RESET}`);
|
|
649
|
+
lines.push(` ${DIM}goal: ${t.goal.length > 80 ? t.goal.slice(0, 77) + "..." : t.goal}${RESET}`);
|
|
650
|
+
if (t.progress.length === 0) {
|
|
651
|
+
lines.push(` ${DIM}(no progress entries)${RESET}`);
|
|
652
|
+
}
|
|
653
|
+
else {
|
|
654
|
+
for (const p of t.progress) {
|
|
655
|
+
const ts = new Date(p.at).toLocaleString();
|
|
656
|
+
lines.push(` ${DIM}${ts}${RESET} ${p.summary}`);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
lines.push("");
|
|
660
|
+
}
|
|
661
|
+
return lines.join("\n");
|
|
662
|
+
}
|
|
632
663
|
export function formatAgo(ms) {
|
|
633
664
|
if (ms < 60_000)
|
|
634
665
|
return `${Math.round(ms / 1000)}s ago`;
|