aoaoe 0.190.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/README.md +1 -0
- 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/executor.js +5 -2
- package/dist/export.d.ts +3 -0
- package/dist/export.js +57 -0
- package/dist/index.js +100 -7
- package/dist/notify.js +7 -0
- package/dist/task-cli.js +16 -2
- package/dist/task-manager.d.ts +2 -1
- package/dist/task-manager.js +34 -2
- package/dist/types.d.ts +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -657,6 +657,7 @@ Config lives at `~/.aoaoe/aoaoe.config.json` (canonical, written by `aoaoe init`
|
|
|
657
657
|
| `policies.autoAnswerPermissions` | Auto-approve permission prompts | `true` |
|
|
658
658
|
| `policies.allowDestructive` | Allow `remove_agent` and `stop_session` actions | `false` |
|
|
659
659
|
| `policies.maxStuckNudgesBeforePause` | Auto-pause task after N nudges with no progress (0 = disabled) | `0` |
|
|
660
|
+
| `policies.quietHours` | Skip reasoning during these hours, e.g. `"01:00-06:00"` (polling continues) | (none) |
|
|
660
661
|
| `promptTemplate` | Reasoner prompt strategy: `default`, `hands-off`, `aggressive`, `review-focused`, `shipping` | `"default"` |
|
|
661
662
|
| `policies.userActivityThresholdMs` | Ignore sessions with recent human keystrokes | `30000` |
|
|
662
663
|
| `policies.actionCooldownMs` | Minimum ms between actions on the same session | `30000` |
|
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/executor.js
CHANGED
|
@@ -213,8 +213,11 @@ export class Executor {
|
|
|
213
213
|
}
|
|
214
214
|
const snap = this.resolveSession(sessionId, snapshots);
|
|
215
215
|
const title = snap?.session.title ?? sessionId;
|
|
216
|
-
await this.taskManager.completeTask(title, summary);
|
|
217
|
-
|
|
216
|
+
const unblockedTitles = await this.taskManager.completeTask(title, summary);
|
|
217
|
+
const detail = unblockedTitles.length > 0
|
|
218
|
+
? `task completed for ${title}: ${summary} (unblocked: ${unblockedTitles.join(", ")})`
|
|
219
|
+
: `task completed for ${title}: ${summary}`;
|
|
220
|
+
return this.logAction({ action: "complete_task", session: sessionId, summary }, true, detail);
|
|
218
221
|
}
|
|
219
222
|
// resolve a session reference (exact ID, prefix, or title) to the matching snapshot
|
|
220
223
|
// single source of truth for session resolution — both tmux name and ID derive from this
|
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
|
@@ -18,7 +18,7 @@ import { TaskManager, loadTaskDefinitions, loadTaskState, saveTaskState, formatT
|
|
|
18
18
|
import { goalToList } from "./types.js";
|
|
19
19
|
import { runTaskCli, handleTaskSlashCommand, quickTaskUpdate } from "./task-cli.js";
|
|
20
20
|
import { parsePaneMilestones } from "./task-parser.js";
|
|
21
|
-
import { TUI, hitTestSession, nextSortMode, SORT_MODES, formatUptime, formatClipText, loadTuiPrefs, saveTuiPrefs, validateGroupName, CONTEXT_BURN_THRESHOLD, buildSnapshotData, formatSnapshotJson, formatSnapshotMarkdown, formatBroadcastSummary, rankSessions, TOP_SORT_MODES, formatIdleSince, CONTEXT_CEILING_THRESHOLD, buildSessionStats, formatSessionStatsLines, formatStatsJson, validateSessionTag, validateColorName, computeErrorTrend, parseQuietHoursRange, computeCostSummary, formatSessionReport, formatQuietStatus, formatSessionAge, formatHealthTrendChart, isOverBudget, DRAIN_ICON, formatSessionsTable, buildFanOutTemplate, TRUST_STABLE_TICKS_TO_ESCALATE, formatTrustLadderStatus, computeContextBudgets, formatContextBudgetTable, CTX_BUDGET_DEFAULT_GLOBAL, resolveProfiles, formatProfileSummary, parseContextCeiling, shouldCompactContext, formatCompactionNudge, formatCompactionAlert, buildSessionDependencyGraph, formatDependencyGraph, formatRelayRules, matchRelayRules, detectOOM, shouldRestartOnOOM, formatOOMAlert, searchSessionOutputs, formatSearchResults, formatThrottleConfig, diffSessionOutput, formatSessionDiff, formatAlertPatterns, matchAlertPatterns, formatLifecycleHooks, matchLifecycleHooks, buildHookEnv } from "./tui.js";
|
|
21
|
+
import { TUI, hitTestSession, nextSortMode, SORT_MODES, formatUptime, formatClipText, loadTuiPrefs, saveTuiPrefs, validateGroupName, CONTEXT_BURN_THRESHOLD, buildSnapshotData, formatSnapshotJson, formatSnapshotMarkdown, formatBroadcastSummary, rankSessions, TOP_SORT_MODES, formatIdleSince, CONTEXT_CEILING_THRESHOLD, buildSessionStats, formatSessionStatsLines, formatStatsJson, validateSessionTag, validateColorName, computeErrorTrend, isQuietHour, parseQuietHoursRange, computeCostSummary, formatSessionReport, formatQuietStatus, formatSessionAge, formatHealthTrendChart, isOverBudget, DRAIN_ICON, formatSessionsTable, buildFanOutTemplate, TRUST_STABLE_TICKS_TO_ESCALATE, formatTrustLadderStatus, computeContextBudgets, formatContextBudgetTable, CTX_BUDGET_DEFAULT_GLOBAL, resolveProfiles, formatProfileSummary, parseContextCeiling, shouldCompactContext, formatCompactionNudge, formatCompactionAlert, buildSessionDependencyGraph, formatDependencyGraph, formatRelayRules, matchRelayRules, detectOOM, shouldRestartOnOOM, formatOOMAlert, searchSessionOutputs, formatSearchResults, formatThrottleConfig, diffSessionOutput, formatSessionDiff, formatAlertPatterns, matchAlertPatterns, formatLifecycleHooks, matchLifecycleHooks, buildHookEnv } from "./tui.js";
|
|
22
22
|
import { isDaemonRunningFromState } from "./chat.js";
|
|
23
23
|
import { sendNotification, sendTestNotification, formatNotifyFilters, parseNotifyEvents, shouldNotifySession } from "./notify.js";
|
|
24
24
|
import { startHealthServer } from "./health.js";
|
|
@@ -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);
|
|
@@ -3230,11 +3290,22 @@ async function main() {
|
|
|
3230
3290
|
// - Always reason if there's a user message (immediate response)
|
|
3231
3291
|
// - Always reason if forceDashboard is set
|
|
3232
3292
|
// - Otherwise gate on reasonIntervalMs elapsed since last reasoning call
|
|
3293
|
+
// - Skip if in quiet hours (unless user message forces it)
|
|
3233
3294
|
const msSinceLastReason = Date.now() - lastReasonerAt;
|
|
3234
|
-
const
|
|
3295
|
+
const quietSpec = config.policies.quietHours;
|
|
3296
|
+
const inQuietHours = quietSpec ? isQuietHour(new Date().getHours(), [parseQuietHoursRange(quietSpec)].filter(Boolean)) : false;
|
|
3297
|
+
const reasonDue = (lastReasonerAt === 0
|
|
3235
3298
|
|| msSinceLastReason >= config.reasonIntervalMs
|
|
3236
3299
|
|| !!userMessage
|
|
3237
|
-
|| forceDashboard
|
|
3300
|
+
|| forceDashboard)
|
|
3301
|
+
&& (!inQuietHours || !!userMessage); // user messages always get through even in quiet hours
|
|
3302
|
+
if (inQuietHours && !userMessage && pollCount % 30 === 1) {
|
|
3303
|
+
const msg = `quiet hours active (${quietSpec}) — polling only, no reasoning`;
|
|
3304
|
+
if (tui)
|
|
3305
|
+
tui.log("status", msg);
|
|
3306
|
+
else
|
|
3307
|
+
log(msg);
|
|
3308
|
+
}
|
|
3238
3309
|
if (!reasonDue) {
|
|
3239
3310
|
// Observation-only tick: poll sessions, update TUI state, skip LLM
|
|
3240
3311
|
const observation = await poller.poll();
|
|
@@ -3767,6 +3838,18 @@ async function daemonTick(config, poller, reasoner, executor, reasonerConsole, p
|
|
|
3767
3838
|
});
|
|
3768
3839
|
}
|
|
3769
3840
|
}
|
|
3841
|
+
// notify: task lifecycle events
|
|
3842
|
+
if (entry.success && entry.action.action === "complete_task" && sessionTitle) {
|
|
3843
|
+
const nFilters = tui ? tui.getAllSessionNotifyFilters() : new Map();
|
|
3844
|
+
if (shouldNotifySession("task_completed", sessionTitle, nFilters, config.notifications?.events)) {
|
|
3845
|
+
sendNotification(config, {
|
|
3846
|
+
event: "task_completed",
|
|
3847
|
+
timestamp: Date.now(),
|
|
3848
|
+
session: sessionTitle,
|
|
3849
|
+
detail: `completed: ${actionText ?? "task finished"}`,
|
|
3850
|
+
});
|
|
3851
|
+
}
|
|
3852
|
+
}
|
|
3770
3853
|
}
|
|
3771
3854
|
// auto-pause tracking: record stuck nudges for send_input actions targeting stuck sessions.
|
|
3772
3855
|
// a "stuck nudge" = send_input to a session that hasn't had progress in >30 min.
|
|
@@ -3794,6 +3877,15 @@ async function daemonTick(config, poller, reasoner, executor, reasonerConsole, p
|
|
|
3794
3877
|
else
|
|
3795
3878
|
log(msg);
|
|
3796
3879
|
appendSupervisorEvent({ at: Date.now(), detail: `auto-pause: ${title}` });
|
|
3880
|
+
const nFilters = tui ? tui.getAllSessionNotifyFilters() : new Map();
|
|
3881
|
+
if (shouldNotifySession("task_stuck", title, nFilters, config.notifications?.events)) {
|
|
3882
|
+
sendNotification(config, {
|
|
3883
|
+
event: "task_stuck",
|
|
3884
|
+
timestamp: Date.now(),
|
|
3885
|
+
session: title,
|
|
3886
|
+
detail: `auto-paused after ${task.stuckNudgeCount} stuck nudges with no progress`,
|
|
3887
|
+
});
|
|
3888
|
+
}
|
|
3797
3889
|
}
|
|
3798
3890
|
}
|
|
3799
3891
|
}
|
|
@@ -4437,6 +4529,7 @@ async function showProgressDigest(since, asJson = false) {
|
|
|
4437
4529
|
console.log(formatProgressDigest(tasks, maxAgeMs));
|
|
4438
4530
|
}
|
|
4439
4531
|
import { resolveTemplate } from "./task-templates.js";
|
|
4532
|
+
import { createBackup, restoreBackup, formatBackupResult, formatRestoreResult } from "./backup.js";
|
|
4440
4533
|
// adopt untracked live AoE sessions as tasks with optional template goal.
|
|
4441
4534
|
async function adoptUntrackedSessions(templateName) {
|
|
4442
4535
|
const basePath = process.cwd();
|
package/dist/notify.js
CHANGED
|
@@ -200,6 +200,9 @@ function eventTitle(event) {
|
|
|
200
200
|
case "action_failed": return "Action Failed";
|
|
201
201
|
case "daemon_started": return "Daemon Started";
|
|
202
202
|
case "daemon_stopped": return "Daemon Stopped";
|
|
203
|
+
case "task_completed": return "Task Completed";
|
|
204
|
+
case "task_stuck": return "Task Stuck";
|
|
205
|
+
case "task_unblocked": return "Task Unblocked";
|
|
203
206
|
}
|
|
204
207
|
}
|
|
205
208
|
/**
|
|
@@ -245,6 +248,7 @@ export function formatNotifyFilters(sessionFilters) {
|
|
|
245
248
|
*/
|
|
246
249
|
export const VALID_NOTIFY_EVENTS = [
|
|
247
250
|
"session_error", "session_done", "action_executed", "action_failed", "daemon_started", "daemon_stopped",
|
|
251
|
+
"task_completed", "task_stuck", "task_unblocked",
|
|
248
252
|
];
|
|
249
253
|
export function parseNotifyEvents(eventNames) {
|
|
250
254
|
const valid = new Set(VALID_NOTIFY_EVENTS);
|
|
@@ -264,6 +268,9 @@ function eventIcon(event) {
|
|
|
264
268
|
case "action_failed": return "\u274C"; // ❌
|
|
265
269
|
case "daemon_started": return "\u{1F680}"; // 🚀
|
|
266
270
|
case "daemon_stopped": return "\u{1F6D1}"; // 🛑
|
|
271
|
+
case "task_completed": return "\u{1F3C6}"; // 🏆
|
|
272
|
+
case "task_stuck": return "\u26A0\uFE0F"; // ⚠️
|
|
273
|
+
case "task_unblocked": return "\u{1F513}"; // 🔓
|
|
267
274
|
}
|
|
268
275
|
}
|
|
269
276
|
//# sourceMappingURL=notify.js.map
|
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
|
@@ -32,7 +32,7 @@ export declare class TaskManager {
|
|
|
32
32
|
}>;
|
|
33
33
|
reportProgress(sessionTitle: string, summary: string): void;
|
|
34
34
|
recordStuckNudge(sessionTitle: string, maxNudges: number): boolean;
|
|
35
|
-
completeTask(sessionTitle: string, summary: string, cleanupSession?: boolean): Promise<
|
|
35
|
+
completeTask(sessionTitle: string, summary: string, cleanupSession?: boolean): Promise<string[]>;
|
|
36
36
|
private save;
|
|
37
37
|
}
|
|
38
38
|
export declare function deriveTitle(repo: string): string;
|
|
@@ -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
|
@@ -487,7 +487,7 @@ export class TaskManager {
|
|
|
487
487
|
async completeTask(sessionTitle, summary, cleanupSession = true) {
|
|
488
488
|
const task = this.getTaskForSession(sessionTitle);
|
|
489
489
|
if (!task)
|
|
490
|
-
return;
|
|
490
|
+
return [];
|
|
491
491
|
// find original definition to check continueOnRoadmap
|
|
492
492
|
const def = this.definitions.find((d) => (d.sessionTitle || deriveTitle(d.repo)).toLowerCase() === sessionTitle.toLowerCase());
|
|
493
493
|
if (def?.continueOnRoadmap) {
|
|
@@ -499,7 +499,7 @@ export class TaskManager {
|
|
|
499
499
|
task.lastProgressAt = Date.now();
|
|
500
500
|
log(`continueOnRoadmap: recycled task '${sessionTitle}' with fresh roadmap goal`);
|
|
501
501
|
this.save();
|
|
502
|
-
return;
|
|
502
|
+
return [];
|
|
503
503
|
}
|
|
504
504
|
task.status = "completed";
|
|
505
505
|
task.completedAt = Date.now();
|
|
@@ -516,6 +516,7 @@ export class TaskManager {
|
|
|
516
516
|
log(`dependency met: activated '${downstream.sessionTitle}' (was waiting on '${task.sessionTitle}')`);
|
|
517
517
|
}
|
|
518
518
|
this.save();
|
|
519
|
+
return unblocked.map((t) => t.sessionTitle);
|
|
519
520
|
}
|
|
520
521
|
save() {
|
|
521
522
|
saveTaskState(this.states);
|
|
@@ -628,6 +629,37 @@ export function formatTaskTable(states) {
|
|
|
628
629
|
}
|
|
629
630
|
return lines.join("\n");
|
|
630
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
|
+
}
|
|
631
663
|
export function formatAgo(ms) {
|
|
632
664
|
if (ms < 60_000)
|
|
633
665
|
return `${Math.round(ms / 1000)}s ago`;
|
package/dist/types.d.ts
CHANGED
|
@@ -112,6 +112,7 @@ export interface AoaoeConfig {
|
|
|
112
112
|
userActivityThresholdMs?: number;
|
|
113
113
|
allowDestructive?: boolean;
|
|
114
114
|
maxStuckNudgesBeforePause?: number;
|
|
115
|
+
quietHours?: string;
|
|
115
116
|
};
|
|
116
117
|
contextFiles: string[];
|
|
117
118
|
sessionDirs: Record<string, string>;
|
|
@@ -132,7 +133,7 @@ export interface AoaoeConfig {
|
|
|
132
133
|
healthPort?: number;
|
|
133
134
|
tuiHistoryRetentionDays?: number;
|
|
134
135
|
}
|
|
135
|
-
export type NotificationEvent = "session_error" | "session_done" | "action_executed" | "action_failed" | "daemon_started" | "daemon_stopped";
|
|
136
|
+
export type NotificationEvent = "session_error" | "session_done" | "action_executed" | "action_failed" | "daemon_started" | "daemon_stopped" | "task_completed" | "task_stuck" | "task_unblocked";
|
|
136
137
|
export type DaemonPhase = "sleeping" | "polling" | "reasoning" | "executing" | "interrupted";
|
|
137
138
|
export interface DaemonSessionState {
|
|
138
139
|
id: string;
|