@phamvuhoang/otto-core 0.1.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.
Files changed (103) hide show
  1. package/README.md +53 -0
  2. package/dist/branch.d.ts +61 -0
  3. package/dist/branch.d.ts.map +1 -0
  4. package/dist/branch.js +215 -0
  5. package/dist/branch.js.map +1 -0
  6. package/dist/cli-help.d.ts +68 -0
  7. package/dist/cli-help.d.ts.map +1 -0
  8. package/dist/cli-help.js +365 -0
  9. package/dist/cli-help.js.map +1 -0
  10. package/dist/detach.d.ts +36 -0
  11. package/dist/detach.d.ts.map +1 -0
  12. package/dist/detach.js +52 -0
  13. package/dist/detach.js.map +1 -0
  14. package/dist/gh-main.d.ts +5 -0
  15. package/dist/gh-main.d.ts.map +1 -0
  16. package/dist/gh-main.js +16 -0
  17. package/dist/gh-main.js.map +1 -0
  18. package/dist/git.d.ts +14 -0
  19. package/dist/git.d.ts.map +1 -0
  20. package/dist/git.js +50 -0
  21. package/dist/git.js.map +1 -0
  22. package/dist/index.d.ts +8 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +8 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/keepalive.d.ts +24 -0
  27. package/dist/keepalive.d.ts.map +1 -0
  28. package/dist/keepalive.js +138 -0
  29. package/dist/keepalive.js.map +1 -0
  30. package/dist/loop.d.ts +42 -0
  31. package/dist/loop.d.ts.map +1 -0
  32. package/dist/loop.js +238 -0
  33. package/dist/loop.js.map +1 -0
  34. package/dist/main.d.ts +5 -0
  35. package/dist/main.d.ts.map +1 -0
  36. package/dist/main.js +16 -0
  37. package/dist/main.js.map +1 -0
  38. package/dist/notify.d.ts +28 -0
  39. package/dist/notify.d.ts.map +1 -0
  40. package/dist/notify.js +119 -0
  41. package/dist/notify.js.map +1 -0
  42. package/dist/pacing.d.ts +8 -0
  43. package/dist/pacing.d.ts.map +1 -0
  44. package/dist/pacing.js +33 -0
  45. package/dist/pacing.js.map +1 -0
  46. package/dist/panel.d.ts +24 -0
  47. package/dist/panel.d.ts.map +1 -0
  48. package/dist/panel.js +202 -0
  49. package/dist/panel.js.map +1 -0
  50. package/dist/rate-limit.d.ts +16 -0
  51. package/dist/rate-limit.d.ts.map +1 -0
  52. package/dist/rate-limit.js +35 -0
  53. package/dist/rate-limit.js.map +1 -0
  54. package/dist/render.d.ts +8 -0
  55. package/dist/render.d.ts.map +1 -0
  56. package/dist/render.js +130 -0
  57. package/dist/render.js.map +1 -0
  58. package/dist/retry.d.ts +17 -0
  59. package/dist/retry.d.ts.map +1 -0
  60. package/dist/retry.js +34 -0
  61. package/dist/retry.js.map +1 -0
  62. package/dist/run-bin.d.ts +35 -0
  63. package/dist/run-bin.d.ts.map +1 -0
  64. package/dist/run-bin.js +241 -0
  65. package/dist/run-bin.js.map +1 -0
  66. package/dist/runner.d.ts +55 -0
  67. package/dist/runner.d.ts.map +1 -0
  68. package/dist/runner.js +297 -0
  69. package/dist/runner.js.map +1 -0
  70. package/dist/stage-exec.d.ts +16 -0
  71. package/dist/stage-exec.d.ts.map +1 -0
  72. package/dist/stage-exec.js +35 -0
  73. package/dist/stage-exec.js.map +1 -0
  74. package/dist/stages.d.ts +38 -0
  75. package/dist/stages.d.ts.map +1 -0
  76. package/dist/stages.js +38 -0
  77. package/dist/stages.js.map +1 -0
  78. package/dist/state.d.ts +25 -0
  79. package/dist/state.d.ts.map +1 -0
  80. package/dist/state.js +30 -0
  81. package/dist/state.js.map +1 -0
  82. package/dist/stream-render.d.ts +68 -0
  83. package/dist/stream-render.d.ts.map +1 -0
  84. package/dist/stream-render.js +162 -0
  85. package/dist/stream-render.js.map +1 -0
  86. package/dist/watch.d.ts +22 -0
  87. package/dist/watch.d.ts.map +1 -0
  88. package/dist/watch.js +93 -0
  89. package/dist/watch.js.map +1 -0
  90. package/package.json +67 -0
  91. package/templates/afk.md +21 -0
  92. package/templates/apply-review.md +71 -0
  93. package/templates/ghafk-issue.md +29 -0
  94. package/templates/ghafk.md +29 -0
  95. package/templates/ghprompt-workflow.md +83 -0
  96. package/templates/ghprompt.md +39 -0
  97. package/templates/prompt.md +97 -0
  98. package/templates/review-lens.md +41 -0
  99. package/templates/review-synth.md +29 -0
  100. package/templates/review-verify.md +52 -0
  101. package/templates/review.md +62 -0
  102. package/templates/superpowers.md +70 -0
  103. package/templates/verify.md +74 -0
@@ -0,0 +1,138 @@
1
+ import { spawn } from "node:child_process";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ const NOOP_RELEASER = { release: () => { } };
4
+ const DEFAULT_REASON = "otto-afk loop";
5
+ function defaultSpawner(command, args, options) {
6
+ return spawn(command, args, {
7
+ detached: options.detached ?? false,
8
+ stdio: options.stdio ?? "ignore",
9
+ windowsHide: true,
10
+ });
11
+ }
12
+ function defaultWslHint() {
13
+ try {
14
+ if (!existsSync("/proc/version"))
15
+ return false;
16
+ return /microsoft/i.test(readFileSync("/proc/version", "utf8"));
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ }
22
+ function defaultWarn(msg) {
23
+ process.stderr.write(`[keepalive] ${msg}\n`);
24
+ }
25
+ /**
26
+ * Build the powershell argv that holds ES_SYSTEM_REQUIRED for the lifetime of
27
+ * the spawned child. Killing the child lets the OS clear ES_CONTINUOUS on
28
+ * process exit (per SetThreadExecutionState docs).
29
+ */
30
+ function windowsArgs() {
31
+ const script = "Add-Type -TypeDefinition 'using System;using System.Runtime.InteropServices;" +
32
+ 'public static class P{[DllImport("kernel32.dll")]' +
33
+ "public static extern uint SetThreadExecutionState(uint e);}';" +
34
+ "[P]::SetThreadExecutionState(0x80000000 -bor 0x00000001) | Out-Null;" +
35
+ "while($true){Start-Sleep -Seconds 60}";
36
+ return {
37
+ cmd: "powershell",
38
+ args: ["-NoProfile", "-NonInteractive", "-Command", script],
39
+ };
40
+ }
41
+ function macosArgs(parentPid) {
42
+ return {
43
+ cmd: "caffeinate",
44
+ args: ["-i", "-w", String(parentPid)],
45
+ };
46
+ }
47
+ function linuxArgs(reason) {
48
+ return {
49
+ cmd: "systemd-inhibit",
50
+ args: [
51
+ "--what=sleep",
52
+ `--why=${reason}`,
53
+ "--mode=block",
54
+ "sleep",
55
+ "infinity",
56
+ ],
57
+ };
58
+ }
59
+ /**
60
+ * Acquire an OS wake-lock for the lifetime of the returned Releaser. Platform
61
+ * dispatch is per-OS; missing utilities degrade to a no-op (warning emitted
62
+ * once) so the loop never crashes on a stripped image.
63
+ */
64
+ export function acquire(opts = {}) {
65
+ const platform = opts.platform ?? process.platform;
66
+ const spawner = opts.spawner ?? defaultSpawner;
67
+ const parentPid = opts.parentPid ?? process.pid;
68
+ const wslHint = opts.wslHint ?? defaultWslHint;
69
+ const warn = opts.warn ?? defaultWarn;
70
+ const reason = opts.reason ?? DEFAULT_REASON;
71
+ if (platform === "linux" && wslHint()) {
72
+ warn("WSL2 detected: systemd-inhibit blocks WSL idle only, not Windows host sleep. " +
73
+ "Configure the Windows power plan to keep the host awake.");
74
+ }
75
+ let spec;
76
+ if (platform === "win32") {
77
+ spec = windowsArgs();
78
+ }
79
+ else if (platform === "darwin") {
80
+ spec = macosArgs(parentPid);
81
+ }
82
+ else if (platform === "linux") {
83
+ spec = linuxArgs(reason);
84
+ }
85
+ else {
86
+ warn(`unsupported platform ${platform}: skipping wake-lock`);
87
+ return NOOP_RELEASER;
88
+ }
89
+ let child;
90
+ try {
91
+ child = spawner(spec.cmd, spec.args, { stdio: "ignore" });
92
+ }
93
+ catch (err) {
94
+ const code = err.code;
95
+ if (code === "ENOENT") {
96
+ warn(`${spec.cmd} not found: continuing without wake-lock`);
97
+ }
98
+ else {
99
+ warn(`failed to spawn ${spec.cmd}: ${err.message}. Continuing without wake-lock.`);
100
+ }
101
+ return NOOP_RELEASER;
102
+ }
103
+ // Swallow spawn-time errors emitted asynchronously (e.g. ENOENT surfaced on
104
+ // the child rather than thrown by spawn itself on some platforms).
105
+ let released = false;
106
+ child.on("error", (err) => {
107
+ if (released)
108
+ return;
109
+ released = true;
110
+ if (err.code === "ENOENT") {
111
+ warn(`${spec.cmd} not found: continuing without wake-lock`);
112
+ }
113
+ else {
114
+ warn(`wake-lock child error: ${err.message}`);
115
+ }
116
+ });
117
+ child.on("close", (code, signal) => {
118
+ if (released)
119
+ return;
120
+ released = true;
121
+ const reason = code == null ? `signal ${signal ?? "unknown"}` : `exit ${code}`;
122
+ warn(`${spec.cmd} wake-lock exited early (${reason}); continuing without wake-lock`);
123
+ });
124
+ return {
125
+ release: () => {
126
+ if (released)
127
+ return;
128
+ released = true;
129
+ try {
130
+ child.kill();
131
+ }
132
+ catch {
133
+ // Already dead; ignore.
134
+ }
135
+ },
136
+ };
137
+ }
138
+ //# sourceMappingURL=keepalive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keepalive.js","sourceRoot":"","sources":["../src/keepalive.ts"],"names":[],"mappings":"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"}
package/dist/loop.d.ts ADDED
@@ -0,0 +1,42 @@
1
+ import type { Stage } from "./stages.js";
2
+ export type LoopOptions = {
3
+ stages: [Stage, ...Stage[]];
4
+ inputs: string;
5
+ iterations: number;
6
+ /** Host repo Claude runs against (cwd). */
7
+ workspaceDir: string;
8
+ /** Installed @phamvuhoang/otto-core dir; stage templates are read from <packageDir>/templates. */
9
+ packageDir: string;
10
+ /** When true, skip OS wake-lock acquisition. Default: false. */
11
+ noKeepAlive?: boolean;
12
+ /** Per-stage retry budget. Default: 3. Set to 0 to disable retries. */
13
+ maxRetries?: number;
14
+ /** When true, fire OS notification + bell on loop terminal events. Default: false. */
15
+ notify?: boolean;
16
+ /** Bin name for the init-time version banner (e.g. "otto-afk"). */
17
+ bin?: string;
18
+ /** CLI version for the init-time version banner. */
19
+ cliVersion?: string;
20
+ /** Stop the loop when cumulative stage cost reaches this USD ceiling. */
21
+ budgetUsd?: number;
22
+ /** Milliseconds to wait between iterations. 0 = no cooldown. */
23
+ cooldownMs?: number;
24
+ /** Opt-in reviewer panel: replace the single reviewer stage with K read-only lens reviewers + one synth commit. */
25
+ reviewLenses?: string[];
26
+ /** Injected AbortSignal for daemon callers (e.g. watch mode). When provided,
27
+ * runLoop skips wake-lock acquisition and process signal handler installation;
28
+ * the caller owns both. */
29
+ signal?: AbortSignal;
30
+ /** Run mode for state.json identity (e.g. "afk" / "ghafk"). Default "afk". */
31
+ mode?: string;
32
+ /** Cap on the rate-limit wait before halting. Default 6h. */
33
+ maxWaitMs?: number;
34
+ /** Force a fresh run, ignoring/clearing prior state. Default false. */
35
+ fresh?: boolean;
36
+ };
37
+ export type LoopOutcome = {
38
+ costUsd: number;
39
+ sentinelHit: boolean;
40
+ };
41
+ export declare function runLoop(opts: LoopOptions): Promise<LoopOutcome>;
42
+ //# sourceMappingURL=loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../src/loop.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAUzC,MAAM,MAAM,WAAW,GAAG;IAGxB,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,YAAY,EAAE,MAAM,CAAC;IACrB,kGAAkG;IAClG,UAAU,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sFAAsF;IACtF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mHAAmH;IACnH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB;;gCAE4B;IAC5B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,8EAA8E;IAC9E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC;AAEpE,wBAAsB,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAuRrE"}
package/dist/loop.js ADDED
@@ -0,0 +1,238 @@
1
+ import { appendFileSync } from "node:fs";
2
+ import { readCoreVersion } from "./cli-help.js";
3
+ import { acquire } from "./keepalive.js";
4
+ import { notifyComplete, notifyError } from "./notify.js";
5
+ import { sleep, isThrottle, nextCooldownFactor } from "./pacing.js";
6
+ import { computeWaitMs } from "./rate-limit.js";
7
+ import { DEFAULT_MAX_RETRIES } from "./retry.js";
8
+ import { stageLogPath } from "./runner.js";
9
+ import { executeStage } from "./stage-exec.js";
10
+ import { clearState, matchesResume, readState, writeState, } from "./state.js";
11
+ import { USE_COLOR, dim, bold, red, greenOut, boldOut, dimOut, SYM, SYM_OUT, } from "./stream-render.js";
12
+ // The agent emits this literal when there is no more work; the same string is
13
+ // mirrored in the playbook templates (prompt.md / ghprompt.md) that instruct it.
14
+ const SENTINEL = "<promise>NO MORE TASKS</promise>";
15
+ const RATE_LIMIT_BUFFER_MS = 30_000;
16
+ const RATE_LIMIT_FALLBACK_MS = 15 * 60_000;
17
+ const DEFAULT_MAX_WAIT_MS = 6 * 3600_000;
18
+ export async function runLoop(opts) {
19
+ const { stages, inputs, iterations, workspaceDir, packageDir, noKeepAlive = false, maxRetries = DEFAULT_MAX_RETRIES, notify = false, bin = "otto", cliVersion = "?", budgetUsd, cooldownMs = 0, reviewLenses, signal: externalSignal, mode = "afk", maxWaitMs = DEFAULT_MAX_WAIT_MS, fresh = false, } = opts;
20
+ const versionLine = `${bin} ${cliVersion} (core ${readCoreVersion()})`;
21
+ process.stderr.write(`${USE_COLOR ? `${dim("━━━")} ${bold(versionLine)} ${dim("━━━")}` : `== ${versionLine} ==`}\n`);
22
+ // When an external signal is injected (daemon/watch mode), the caller owns
23
+ // wake-lock + process signal handlers. Skip both here.
24
+ const releaser = externalSignal || noKeepAlive
25
+ ? { release: () => { } }
26
+ : acquire({ reason: `${bin} loop` });
27
+ const stageAbort = externalSignal ? undefined : new AbortController();
28
+ const activeSignal = externalSignal ?? stageAbort.signal;
29
+ // Single release path: signal handlers and the finally below all funnel
30
+ // through releaseOnce so the wake-lock child is killed exactly once.
31
+ let released = false;
32
+ const releaseOnce = () => {
33
+ if (released)
34
+ return;
35
+ released = true;
36
+ releaser.release();
37
+ };
38
+ let onSigint;
39
+ let onSigterm;
40
+ if (!externalSignal) {
41
+ const abortActiveStage = () => {
42
+ if (!stageAbort.signal.aborted)
43
+ stageAbort.abort();
44
+ };
45
+ onSigint = () => {
46
+ abortActiveStage();
47
+ if (notify)
48
+ notifyError("interrupted (SIGINT)");
49
+ releaseOnce();
50
+ process.exit(130);
51
+ };
52
+ onSigterm = () => {
53
+ abortActiveStage();
54
+ if (notify)
55
+ notifyError("terminated (SIGTERM)");
56
+ releaseOnce();
57
+ process.exit(143);
58
+ };
59
+ process.on("SIGINT", onSigint);
60
+ process.on("SIGTERM", onSigterm);
61
+ }
62
+ let completedIterations = 0;
63
+ let sentinelHit = false;
64
+ let runCostUsd = 0;
65
+ let cooldownFactor = 1;
66
+ // Single source of truth for per-stage accounting: tally cost, report it,
67
+ // advance the adaptive cooldown factor on throttle, and report whether the
68
+ // budget is now exhausted. Used once per non-panel stage AND once per panel
69
+ // sub-agent (passed to runPanel as onStage), so budget + adaptive pacing
70
+ // apply uniformly to lenses, synth, and ordinary stages alike.
71
+ const accountStage = (sr) => {
72
+ runCostUsd += sr.costUsd;
73
+ process.stderr.write(`${dim(`· $${sr.costUsd.toFixed(2)} (run $${runCostUsd.toFixed(2)})`)}\n`);
74
+ cooldownFactor = nextCooldownFactor(cooldownFactor, isThrottle(sr.apiErrorStatus));
75
+ return {
76
+ stop: budgetUsd != null && runCostUsd >= budgetUsd,
77
+ cooldownFactor,
78
+ };
79
+ };
80
+ const nowIso = () => new Date().toISOString();
81
+ if (fresh)
82
+ clearState(workspaceDir);
83
+ const prior = fresh ? null : readState(workspaceDir);
84
+ const resuming = matchesResume(prior, { bin, mode, inputs });
85
+ const startIteration = resuming ? prior.iteration : 1;
86
+ const total = resuming ? prior.of : iterations;
87
+ let resumeNote = "";
88
+ if (resuming) {
89
+ resumeNote = `Resumed run (iteration ${startIteration} of ${total}). Prior work is committed — reconcile against git history and the working tree before acting; do not redo completed tasks.`;
90
+ process.stdout.write(`${greenOut(SYM_OUT.bullet)} ${boldOut("resuming")}${dimOut(` from iteration ${startIteration}/${total}`)}\n`);
91
+ }
92
+ const persist = (iteration, status, resetsAt) => writeState(workspaceDir, {
93
+ bin,
94
+ mode,
95
+ inputs,
96
+ iteration,
97
+ of: total,
98
+ status,
99
+ resetsAt: resetsAt ?? null,
100
+ startedAt: prior?.startedAt ?? nowIso(),
101
+ updatedAt: nowIso(),
102
+ });
103
+ if (resuming && prior.status === "waiting-rate-limit") {
104
+ const waitMs = computeWaitMs(prior.resetsAt ?? null, Date.now(), RATE_LIMIT_BUFFER_MS, 0);
105
+ if (waitMs > 0 && waitMs <= maxWaitMs) {
106
+ process.stderr.write(`${dim(`waiting ${Math.round(waitMs / 60000)}m to clear the prior rate limit`)}\n`);
107
+ await sleep(waitMs, activeSignal);
108
+ }
109
+ }
110
+ try {
111
+ for (let i = startIteration; i <= total; i++) {
112
+ persist(i, "running");
113
+ for (let s = 0; s < stages.length; s++) {
114
+ const stage = stages[s];
115
+ // Budget gate: check before running each stage.
116
+ if (budgetUsd != null && runCostUsd >= budgetUsd) {
117
+ process.stdout.write(`${greenOut(SYM_OUT.bullet)} ${boldOut("budget reached")}${dimOut(` $${runCostUsd.toFixed(2)} ≥ $${budgetUsd.toFixed(2)} after ${i - 1} iterations`)}\n`);
118
+ return { costUsd: runCostUsd, sentinelHit };
119
+ }
120
+ const banner = USE_COLOR
121
+ ? `${dim("━━━")} ${bold(`iteration ${i}/${total}`)} ${dim("·")} ${bold(stage.name)} ${dim(`(stage ${s + 1}/${stages.length})`)} ${dim("━━━")}`
122
+ : `== iteration ${i}/${total} · ${stage.name} (stage ${s + 1}/${stages.length}) ==`;
123
+ process.stderr.write(`\n${banner}\n`);
124
+ const usePanel = reviewLenses && reviewLenses.length > 0 && stage.name === "reviewer";
125
+ let sr;
126
+ const runOnce = async () => {
127
+ if (usePanel) {
128
+ const { runPanel } = await import("./panel.js");
129
+ return runPanel({
130
+ lenses: reviewLenses,
131
+ workspaceDir,
132
+ packageDir,
133
+ iteration: i,
134
+ maxRetries,
135
+ cooldownMs,
136
+ signal: activeSignal,
137
+ onStage: accountStage,
138
+ });
139
+ }
140
+ const r = await executeStage({
141
+ stage,
142
+ vars: { INPUTS: inputs, RESUME: resumeNote },
143
+ workspaceDir,
144
+ packageDir,
145
+ iteration: i,
146
+ maxRetries,
147
+ signal: activeSignal,
148
+ });
149
+ accountStage(r);
150
+ return r;
151
+ };
152
+ try {
153
+ for (;;) {
154
+ try {
155
+ sr = await runOnce();
156
+ break;
157
+ }
158
+ catch (err) {
159
+ if (err?.name !== "RateLimitError")
160
+ throw err;
161
+ const resetsAt = err.resetsAt;
162
+ const waitMs = computeWaitMs(resetsAt, Date.now(), RATE_LIMIT_BUFFER_MS, RATE_LIMIT_FALLBACK_MS);
163
+ if (waitMs > maxWaitMs) {
164
+ persist(i, "interrupted", resetsAt);
165
+ process.stdout.write(`${red(SYM.cross)} ${bold("rate limit")}${dim(` — reset is beyond --max-wait; halting at iteration ${i}. Re-run to resume.`)}\n`);
166
+ return { costUsd: runCostUsd, sentinelHit };
167
+ }
168
+ persist(i, "waiting-rate-limit", resetsAt);
169
+ const mins = Math.round(waitMs / 60000);
170
+ process.stderr.write(`${dim(`⏸ rate limit — waiting ~${mins}m until reset, then resuming`)}\n`);
171
+ await sleep(waitMs, activeSignal);
172
+ persist(i, "running");
173
+ }
174
+ }
175
+ }
176
+ catch (err) {
177
+ if (activeSignal.aborted) {
178
+ return { costUsd: runCostUsd, sentinelHit };
179
+ }
180
+ const stageLog = stageLogPath(workspaceDir, i, stage.name);
181
+ const failureMarker = `[failure] iteration ${i} stage ${stage.name} failed after ${maxRetries} retries: ${err.message}`;
182
+ try {
183
+ appendFileSync(stageLog, failureMarker + "\n");
184
+ }
185
+ catch {
186
+ // log file may be unwritable; stderr still carries the failure.
187
+ }
188
+ const msg = `${red(SYM.cross)} ${bold("iteration " + i + " stage " + stage.name + " failed")} after ${maxRetries} retries: ${err.message}`;
189
+ process.stderr.write(msg + "\n");
190
+ break;
191
+ }
192
+ // Cost/pacing accounting is handled by accountStage — called once per
193
+ // non-panel stage above, and once per sub-agent inside runPanel.
194
+ if (s === 0) {
195
+ if (sr.result.includes(SENTINEL)) {
196
+ const msg = greenOut(SYM_OUT.bullet) +
197
+ " " +
198
+ boldOut("Otto complete") +
199
+ dimOut(" after " + i + " iterations");
200
+ process.stdout.write(msg + "\n");
201
+ sentinelHit = true;
202
+ completedIterations = i;
203
+ persist(i, "complete");
204
+ clearState(workspaceDir);
205
+ return { costUsd: runCostUsd, sentinelHit };
206
+ }
207
+ }
208
+ }
209
+ completedIterations = i;
210
+ // Cooldown between iterations.
211
+ if (cooldownMs > 0 && i < total) {
212
+ const wait = cooldownMs * cooldownFactor;
213
+ if (cooldownFactor > 1) {
214
+ process.stderr.write(`${dim(`cooldown ×${cooldownFactor} → ${wait}ms (throttle backoff)`)}\n`);
215
+ }
216
+ await sleep(wait, activeSignal);
217
+ }
218
+ }
219
+ }
220
+ catch (err) {
221
+ if (notify)
222
+ notifyError(err.message);
223
+ throw err;
224
+ }
225
+ finally {
226
+ if (onSigint)
227
+ process.off("SIGINT", onSigint);
228
+ if (onSigterm)
229
+ process.off("SIGTERM", onSigterm);
230
+ releaseOnce();
231
+ if (notify && (sentinelHit || completedIterations === total)) {
232
+ notifyComplete(completedIterations, sentinelHit);
233
+ }
234
+ }
235
+ clearState(workspaceDir);
236
+ return { costUsd: runCostUsd, sentinelHit };
237
+ }
238
+ //# sourceMappingURL=loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop.js","sourceRoot":"","sources":["../src/loop.ts"],"names":[],"mappings":"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"}
package/dist/main.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export type RunAfkOptions = {
2
+ cliVersion?: string;
3
+ };
4
+ export declare function runAfk(argv: string[], opts?: RunAfkOptions): Promise<void>;
5
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,aAAa,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpD,wBAAsB,MAAM,CAC1B,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,GAAE,aAAkB,GACvB,OAAO,CAAC,IAAI,CAAC,CAYf"}
package/dist/main.js ADDED
@@ -0,0 +1,16 @@
1
+ import { runBin } from "./run-bin.js";
2
+ import { STAGES } from "./stages.js";
3
+ export async function runAfk(argv, opts = {}) {
4
+ await runBin(argv, {
5
+ bin: "otto-afk",
6
+ usage: "<plan-and-prd> <iterations>",
7
+ desc: "plan/PRD-driven Claude Code AFK loop",
8
+ stages: [STAGES.implementer, STAGES.reviewer],
9
+ takesInputArg: true,
10
+ cliVersion: opts.cliVersion,
11
+ verifyStage: STAGES.verifier,
12
+ applyReviewStage: STAGES.applyReviewImplementer,
13
+ mode: "afk",
14
+ });
15
+ }
16
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrC,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,IAAc,EACd,OAAsB,EAAE;IAExB,MAAM,MAAM,CAAC,IAAI,EAAE;QACjB,GAAG,EAAE,UAAU;QACf,KAAK,EAAE,6BAA6B;QACpC,IAAI,EAAE,sCAAsC;QAC5C,MAAM,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC;QAC7C,aAAa,EAAE,IAAI;QACnB,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,WAAW,EAAE,MAAM,CAAC,QAAQ;QAC5B,gBAAgB,EAAE,MAAM,CAAC,sBAAsB;QAC/C,IAAI,EAAE,KAAK;KACZ,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,28 @@
1
+ import { type ChildProcess } from "node:child_process";
2
+ export type NotifyLevel = "info" | "error";
3
+ export type NotifySpawnedChild = Pick<ChildProcess, "on" | "unref">;
4
+ export type NotifySpawner = (command: string, args: readonly string[]) => NotifySpawnedChild;
5
+ export type NotifyOptions = {
6
+ title: string;
7
+ body: string;
8
+ level: NotifyLevel;
9
+ /** When false, suppress the terminal bell. Default: true. */
10
+ sound?: boolean;
11
+ platform?: NodeJS.Platform;
12
+ spawner?: NotifySpawner;
13
+ stderr?: {
14
+ write: (s: string) => void;
15
+ };
16
+ };
17
+ /**
18
+ * Fire a best-effort OS notification + terminal bell. Synchronous return;
19
+ * never blocks on toast delivery, never crashes on a missing utility.
20
+ *
21
+ * Bell (\x07) is written to stderr on every call regardless of OS path.
22
+ * Apostrophes / quotes in `title` / `body` are escaped per-platform so a
23
+ * `'` in the message doesn't break the shell wrapping.
24
+ */
25
+ export declare function notify(opts: NotifyOptions): void;
26
+ export declare function notifyComplete(iterations: number, sentinel: boolean): void;
27
+ export declare function notifyError(message: string): void;
28
+ //# sourceMappingURL=notify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../src/notify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAE9D,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,OAAO,CAAC;AAE3C,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,YAAY,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC;AAEpE,MAAM,MAAM,aAAa,GAAG,CAC1B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,KACpB,kBAAkB,CAAC;AAExB,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,WAAW,CAAC;IACnB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CACzC,CAAC;AAmGF;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAsBhD;AAED,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI,CAQ1E;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAMjD"}
package/dist/notify.js ADDED
@@ -0,0 +1,119 @@
1
+ import { spawn } from "node:child_process";
2
+ function defaultSpawner(command, args) {
3
+ return spawn(command, args, {
4
+ detached: true,
5
+ stdio: "ignore",
6
+ windowsHide: true,
7
+ });
8
+ }
9
+ /**
10
+ * Spawn a fire-and-forget child. Swallows spawn-time + async errors so a
11
+ * missing utility (ENOENT) never crashes the loop.
12
+ */
13
+ function fireAndForget(spawner, cmd, args) {
14
+ try {
15
+ const child = spawner(cmd, args);
16
+ child.on("error", () => {
17
+ // Async ENOENT (Linux): swallow. The whole point is fire-and-forget.
18
+ });
19
+ child.unref();
20
+ return true;
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ }
26
+ function escDoubleQuote(s) {
27
+ return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
28
+ }
29
+ function escPsString(s) {
30
+ // PowerShell single-quoted literal: ' is escaped by doubling.
31
+ return s.replace(/'/g, "''");
32
+ }
33
+ function windowsToast(spawner, title, body) {
34
+ // Try BurntToast first; fall back to msg.exe. Both fire-and-forget; if the
35
+ // module is missing, the powershell child exits non-zero — we don't observe
36
+ // it, the caller has already gotten the bell.
37
+ const psScript = `if (Get-Module -ListAvailable -Name BurntToast) { ` +
38
+ `Import-Module BurntToast; ` +
39
+ `New-BurntToastNotification -Text '${escPsString(title)}','${escPsString(body)}' ` +
40
+ `} else { exit 1 }`;
41
+ const ok = fireAndForget(spawner, "powershell", [
42
+ "-NoProfile",
43
+ "-NonInteractive",
44
+ "-Command",
45
+ psScript,
46
+ ]);
47
+ if (!ok)
48
+ return false;
49
+ // Always also try msg.exe as a secondary path (cheap, mostly a no-op when
50
+ // BurntToast wins). msg.exe is not on every Windows SKU (Home edition lacks
51
+ // it) — swallow failure.
52
+ fireAndForget(spawner, "msg.exe", ["*", `${title}: ${body}`]);
53
+ return true;
54
+ }
55
+ function macosBanner(spawner, title, body, level) {
56
+ const sound = level === "info" ? "Glass" : "Basso";
57
+ const script = `display notification "${escDoubleQuote(body)}" ` +
58
+ `with title "${escDoubleQuote(title)}" ` +
59
+ `sound name "${sound}"`;
60
+ return fireAndForget(spawner, "osascript", ["-e", script]);
61
+ }
62
+ function linuxNotifySend(spawner, title, body, level) {
63
+ const urgency = level === "error" ? "critical" : "normal";
64
+ return fireAndForget(spawner, "notify-send", [
65
+ "--urgency",
66
+ urgency,
67
+ title,
68
+ body,
69
+ ]);
70
+ }
71
+ /**
72
+ * Fire a best-effort OS notification + terminal bell. Synchronous return;
73
+ * never blocks on toast delivery, never crashes on a missing utility.
74
+ *
75
+ * Bell (\x07) is written to stderr on every call regardless of OS path.
76
+ * Apostrophes / quotes in `title` / `body` are escaped per-platform so a
77
+ * `'` in the message doesn't break the shell wrapping.
78
+ */
79
+ export function notify(opts) {
80
+ const platform = opts.platform ?? process.platform;
81
+ const spawner = opts.spawner ?? defaultSpawner;
82
+ const stderr = opts.stderr ?? process.stderr;
83
+ const sound = opts.sound ?? true;
84
+ if (sound) {
85
+ try {
86
+ stderr.write("\x07");
87
+ }
88
+ catch {
89
+ // never crash on a bell write.
90
+ }
91
+ }
92
+ if (platform === "win32") {
93
+ windowsToast(spawner, opts.title, opts.body);
94
+ }
95
+ else if (platform === "darwin") {
96
+ macosBanner(spawner, opts.title, opts.body, opts.level);
97
+ }
98
+ else if (platform === "linux") {
99
+ linuxNotifySend(spawner, opts.title, opts.body, opts.level);
100
+ }
101
+ // unsupported platforms: bell-only, no toast attempt.
102
+ }
103
+ export function notifyComplete(iterations, sentinel) {
104
+ notify({
105
+ level: "info",
106
+ title: "Otto complete",
107
+ body: sentinel
108
+ ? `Sentinel hit after ${iterations} iteration${iterations === 1 ? "" : "s"}.`
109
+ : `Reached iteration cap (${iterations}).`,
110
+ });
111
+ }
112
+ export function notifyError(message) {
113
+ notify({
114
+ level: "error",
115
+ title: "Otto failed",
116
+ body: message,
117
+ });
118
+ }
119
+ //# sourceMappingURL=notify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify.js","sourceRoot":"","sources":["../src/notify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /** Abortable delay. Resolves after `ms`; rejects with an AbortError if `signal` fires. */
2
+ export declare function sleep(ms: number, signal?: AbortSignal): Promise<void>;
3
+ export declare const THROTTLE_RE: RegExp;
4
+ /** True when a result's `api_error_status` looks like provider throttling. */
5
+ export declare function isThrottle(apiErrorStatus: string | null): boolean;
6
+ /** Adaptive cooldown multiplier: reset to 1 when healthy, else double up to `cap`. */
7
+ export declare function nextCooldownFactor(prev: number, throttled: boolean, cap?: number): number;
8
+ //# sourceMappingURL=pacing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pacing.d.ts","sourceRoot":"","sources":["../src/pacing.ts"],"names":[],"mappings":"AAMA,0FAA0F;AAC1F,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAcrE;AAED,eAAO,MAAM,WAAW,QAA8B,CAAC;AAEvD,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAEjE;AAED,sFAAsF;AACtF,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,OAAO,EAClB,GAAG,SAAI,GACN,MAAM,CAER"}