@codizelabs/agent-rewake 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -37,7 +37,7 @@ Coding agents stop at their usage limit: Claude's 5-hour session limit, Codex's
37
37
  - **Automatic, if you want.** Choose *from now on* when Rewake asks (Claude Code), turn on auto-resume in Zed's **Rewake** menu, or run `agent-rewake continue --always` (Copilot CLI, Gemini CLI, Grok Build, Qwen Code, OpenCode, Antigravity CLI), and Rewake resumes without asking. It asks first when a limit resets more than a day away, never resumes credit or billing limits (and says so), and never approves permission prompts for you.
38
38
  - **Sends messages later.** `/rewake in 1h Run the tests` schedules a message into the session (Zed and Claude Code; in Zed also on a repeat, such as every weekday at 9).
39
39
  - **Keeps the computer awake, if it can.** On macOS, and on Linux (with systemd) and Windows, while a resume is due within six hours, Rewake stops the computer from idling to sleep (while plugged in, by default). Only macOS has been tried by hand. Closing the lid still puts it to sleep. `agent-rewake doctor` tells you if your computer's own sleep settings would let it sleep, and [which settings to change](https://codizelabs.github.io/agent-rewake/docs/#keep-your-computer-awake).
40
- - **Warns you before a usage limit (Claude Code).** The plugin tells you when a usage window passes 80% and 95%, and when your pace over the last hour would reach the limit within about 45 minutes (as a range, "in about 20 to 40 minutes"). It uses only the usage figures Claude Code already gives it: no network, no sign-in. It only tells you; at 80% it mentions `/compact` (which uses some quota itself), at 95% it says it is a good moment to wrap up. It never types or compacts for you. Turn it off with `agent-rewake settings usage-warnings off`. Not built for Codex and the other agents.
40
+ - **Warns you before a usage limit (Claude Code and Codex).** The plugin tells you when a usage window passes 80% and 95%, and when your pace over the last hour would reach the limit within about 45 minutes (as a range, "in about 20 to 40 minutes"). It uses only the usage figures Claude Code or Codex already gives it: no network, no sign-in. It only tells you; in Claude Code, at 80% it mentions `/compact` (which uses some quota itself); at 95% it says it is a good moment to wrap up. It never types or compacts for you. Turn it off with `agent-rewake settings usage-warnings off`. In Codex the same warnings come with your next prompt, from the figures of Codex's previous reply (so one turn late); they are not yet seen in the Codex desktop app or the VS Code extension. Not built for the other agents.
41
41
  - **Tells you when the project changed while it waited.** Outside Zed, if the session's folder is a git folder and its latest commit or tracked files differ by the time Rewake sends, it tells you, and sends the message as it was. You can instead have it add one fixed line asking the agent to re-read files (`agent-rewake settings workspace-changed note`), or send nothing and ask you first (`hold`). It reads only the commit id and the names-and-status summary from `git status --porcelain --untracked-files=no`, never file contents, and only on your computer.
42
42
  - **Same agent, same session.** No new agent to pick: Rewake works inside or in front of the agents you already use, so your sessions and threads keep going where they stopped.
43
43
 
@@ -152,7 +152,7 @@ This takes Rewake out of every place it's set up in, after showing every change
152
152
 
153
153
  ## Status
154
154
 
155
- Version 0.5. Rewake's automated tests run on macOS, Linux and Windows with Node.js 22 and 24, and the author uses it daily in Zed. The previews outside Zed are new: the docs say [what's been tried for each](https://codizelabs.github.io/agent-rewake/docs/#outside-zed-previews), and what hasn't. What changed in each release is in the [changelog](https://codizelabs.github.io/agent-rewake/changelog/). Feedback and bug reports are very welcome: [open an issue](https://github.com/codizelabs/agent-rewake/issues/new/choose). If Rewake missed a usage limit, [say how](https://codizelabs.github.io/agent-rewake/docs/#report-a-limit-rewake-missed).
155
+ Version 0.6. Rewake's automated tests run on macOS, Linux and Windows with Node.js 22 and 24, and the author uses it daily in Zed. The previews outside Zed are new: the docs say [what's been tried for each](https://codizelabs.github.io/agent-rewake/docs/#outside-zed-previews), and what hasn't. What changed in each release is in the [changelog](https://codizelabs.github.io/agent-rewake/changelog/). Feedback and bug reports are very welcome: [open an issue](https://github.com/codizelabs/agent-rewake/issues/new/choose). If Rewake missed a usage limit, [say how](https://codizelabs.github.io/agent-rewake/docs/#report-a-limit-rewake-missed).
156
156
 
157
157
  ## Contributing
158
158
 
@@ -4968,7 +4968,7 @@ var init_version = __esm({
4968
4968
  return {};
4969
4969
  }
4970
4970
  })();
4971
- VERSION2 = pkg.version ?? (true ? "0.5.0" : "0.0.0");
4971
+ VERSION2 = pkg.version ?? (true ? "0.6.0" : "0.0.0");
4972
4972
  PACKAGE_NAME = pkg.name ?? "@codizelabs/agent-rewake";
4973
4973
  FROM_SOURCE = false;
4974
4974
  }
@@ -6930,6 +6930,31 @@ function chooseSnapshot(snapshots, last, nowSec) {
6930
6930
  const fullest = (s) => Math.max(...[s.primary, s.secondary].map((w) => w ? w.used_percent ?? 0 : 0));
6931
6931
  return withWindows.reduce((a, b) => fullest(b) > fullest(a) ? b : a);
6932
6932
  }
6933
+ function lastWindows(tail) {
6934
+ let found;
6935
+ for (const line2 of tail.split("\n")) {
6936
+ if (!line2.startsWith("{") || !line2.includes('"token_count"')) continue;
6937
+ try {
6938
+ const p = JSON.parse(line2).payload;
6939
+ const rl = p?.type === "token_count" ? p.rate_limits : void 0;
6940
+ if (!rl || typeof rl !== "object") continue;
6941
+ if (rl.limit_id && rl.limit_id !== "codex") continue;
6942
+ if (rl.primary || rl.secondary) found = rl;
6943
+ } catch {
6944
+ }
6945
+ }
6946
+ const out = [];
6947
+ for (const w of [found?.primary, found?.secondary]) {
6948
+ const m = w?.window_minutes;
6949
+ if (!w || typeof m !== "number" || !(m > 0) || typeof w.used_percent !== "number") continue;
6950
+ out.push({
6951
+ kind: m >= 270 && m <= 330 ? "five_hour" : m >= 9e3 && m <= 11e3 ? "seven_day" : `${Math.max(1, Math.round(m / 60))}_hour`,
6952
+ percentUsed: w.used_percent,
6953
+ ...typeof w.resets_at === "number" && { resetsAt: w.resets_at * 1e3 }
6954
+ });
6955
+ }
6956
+ return out;
6957
+ }
6933
6958
  function spentWindowReset(rl, nowSec) {
6934
6959
  if (!rl || !/^workspace_/.test(rl.rate_limit_reached_type ?? "") || rl.spend_control_reached)
6935
6960
  return {};
@@ -15757,8 +15782,8 @@ var init_settings_command = __esm({
15757
15782
  {
15758
15783
  name: "usage-warnings",
15759
15784
  title: "Warnings before a usage limit",
15760
- short: "Whether Claude Code warns you when a usage window is nearly used up.",
15761
- more: "Claude Code only. It uses Claude Code's own usage figures, with no network and no sign-in, and never types anything for you.",
15785
+ short: "Warn me when a usage limit is nearly used up (Claude Code, at the moment it happens; Codex, with your next prompt).",
15786
+ more: "Claude Code and Codex only. It uses their own usage figures, with no network and no sign-in, and never types anything for you.",
15762
15787
  choices: () => [
15763
15788
  ["on", "Warn at 80% and 95% used, and when the pace would reach the limit within 45 minutes"],
15764
15789
  ["off", "Don't warn"]
@@ -15768,7 +15793,7 @@ var init_settings_command = __esm({
15768
15793
  ...s,
15769
15794
  usageWarnings: USAGE_WARNINGS_VALUES.find((x) => x === v) ?? s.usageWarnings
15770
15795
  }),
15771
- saved: (v) => v === "off" ? "Claude Code won't warn you before a usage limit." : "Claude Code warns you at 80% and 95% of a usage limit, and when your pace would reach it within 45 minutes."
15796
+ saved: (v) => v === "off" ? "Claude Code and Codex won't warn you before a usage limit." : "Claude Code and Codex warn you at 80% and 95% of a usage limit, and when your pace would reach it within 45 minutes."
15772
15797
  }
15773
15798
  ];
15774
15799
  orList2 = (v) => v.length < 2 ? v[0] ?? "" : `${v.slice(0, -1).join(", ")} or ${v.at(-1)}`;
@@ -17261,6 +17286,132 @@ var init_adapter = __esm({
17261
17286
  }
17262
17287
  });
17263
17288
 
17289
+ // src/core/usage-warn.ts
17290
+ function timeToLimit(samples, now) {
17291
+ const recent = samples.filter((s) => s.t >= now - RECENT_MS);
17292
+ const first = recent[0];
17293
+ const last = recent.at(-1);
17294
+ if (!first || !last || last.t !== now || last.t - first.t < MIN_SPAN_MS) return void 0;
17295
+ const rate = (last.p - first.p) / (last.t - first.t);
17296
+ return rate > 0 ? (100 - last.p) / rate : void 0;
17297
+ }
17298
+ function paceRange(ms) {
17299
+ const five = (x) => Math.max(5, Math.round(x / (5 * MINUTE)) * 5);
17300
+ const lo = five(ms * 0.7);
17301
+ return [lo, Math.max(lo + 5, five(ms * 1.4))];
17302
+ }
17303
+ function usageStep(prev, w, now) {
17304
+ const reset = w.resetsAt;
17305
+ const p = w.percentUsed;
17306
+ if (reset === void 0 || !Number.isFinite(reset) || reset <= now) return void 0;
17307
+ if (!(p >= 0 && p < 100)) return void 0;
17308
+ const base = prev && Math.abs(prev.resetsAt - reset) < SAME_WINDOW_MS ? prev : { resetsAt: reset, samples: [], high: 0, pace: false };
17309
+ const last = base.samples.at(-1);
17310
+ const samples = last?.p === p ? base.samples : [...last && p < last.p ? [] : base.samples, { t: now, p }];
17311
+ const kept = samples.slice(-SAMPLE_CAP);
17312
+ const level = WARN_AT.filter((l) => p >= l && l > base.high).pop();
17313
+ const ms = level === void 0 && !base.pace ? timeToLimit(kept, now) : void 0;
17314
+ const pace = ms !== void 0 && ms <= PACE_HORIZON_MS && now + ms < reset ? paceRange(ms) : void 0;
17315
+ return {
17316
+ next: {
17317
+ resetsAt: reset,
17318
+ samples: kept,
17319
+ high: level ?? base.high,
17320
+ pace: base.pace || pace !== void 0
17321
+ },
17322
+ ...level !== void 0 && { level },
17323
+ ...pace && { pace }
17324
+ };
17325
+ }
17326
+ function limitName(kind) {
17327
+ const hours = /^(\d+)_hour$/.exec(kind)?.[1];
17328
+ const name = kind === "five_hour" ? "5-hour" : kind === "seven_day" ? "7-day" : hours ? `${hours}-hour` : kind.replace(/_/g, " ");
17329
+ return `${name} limit`;
17330
+ }
17331
+ function levelText(kind, percent, reset) {
17332
+ const head = `${limitName(kind)}: ${Math.floor(percent)}% used, as of Codex's last reply. It resets ${reset}.`;
17333
+ return percent >= 95 ? `${head} A good moment to wrap up or save what you are doing.` : head;
17334
+ }
17335
+ function paceText(kind, [lo, hi], reset) {
17336
+ return `At this pace you reach the ${limitName(kind)} in about ${lo} to ${hi} minutes, before it resets ${reset}.`;
17337
+ }
17338
+ var MINUTE, WARN_AT, SAMPLE_CAP, MIN_SPAN_MS, RECENT_MS, PACE_HORIZON_MS, SAME_WINDOW_MS;
17339
+ var init_usage_warn = __esm({
17340
+ "src/core/usage-warn.ts"() {
17341
+ "use strict";
17342
+ MINUTE = 6e4;
17343
+ WARN_AT = [80, 95];
17344
+ SAMPLE_CAP = 12;
17345
+ MIN_SPAN_MS = 10 * MINUTE;
17346
+ RECENT_MS = 60 * MINUTE;
17347
+ PACE_HORIZON_MS = 45 * MINUTE;
17348
+ SAME_WINDOW_MS = 30 * MINUTE;
17349
+ }
17350
+ });
17351
+
17352
+ // src/hosts/codex/usage.ts
17353
+ import { readFileSync as readFileSync33 } from "node:fs";
17354
+ import { dirname as dirname7, join as join44 } from "node:path";
17355
+ function validStored(v) {
17356
+ const out = {};
17357
+ for (const [home, kinds] of Object.entries(v)) {
17358
+ if (typeof kinds !== "object" || kinds === null || Array.isArray(kinds)) continue;
17359
+ const ok = {};
17360
+ for (const [kind, st] of Object.entries(kinds)) {
17361
+ const s = st;
17362
+ if (s && typeof s.resetsAt === "number" && Array.isArray(s.samples)) ok[kind] = st;
17363
+ }
17364
+ out[home] = ok;
17365
+ }
17366
+ return out;
17367
+ }
17368
+ function load(file) {
17369
+ try {
17370
+ const v = JSON.parse(readFileSync33(file, "utf8"));
17371
+ return typeof v === "object" && v !== null && !Array.isArray(v) ? validStored(v) : {};
17372
+ } catch {
17373
+ return {};
17374
+ }
17375
+ }
17376
+ function usageWarning(stateDir2, rollout, now) {
17377
+ try {
17378
+ const windows = lastWindows(readTail2(rollout));
17379
+ if (windows.length === 0) return void 0;
17380
+ const file = join44(stateDir2, FILE);
17381
+ const all = load(file);
17382
+ const home = homeOf(rollout);
17383
+ const kept = { ...all[home] };
17384
+ const texts = [];
17385
+ for (const w of windows) {
17386
+ const step = usageStep(kept[w.kind], w, now);
17387
+ if (!step) continue;
17388
+ kept[w.kind] = step.next;
17389
+ const reset = formatAt(step.next.resetsAt, now);
17390
+ if (step.level !== void 0) texts.push(levelText(w.kind, w.percentUsed, reset));
17391
+ else if (step.pace) texts.push(paceText(w.kind, step.pace, reset));
17392
+ }
17393
+ ensurePrivateDir(stateDir2);
17394
+ replaceFileExclusive(file, `${JSON.stringify({ ...all, [home]: kept })}
17395
+ `, 384);
17396
+ return texts.length > 0 ? JSON.stringify({ systemMessage: `Rewake: ${texts.join(" ")}` }) : void 0;
17397
+ } catch {
17398
+ return void 0;
17399
+ }
17400
+ }
17401
+ var FILE, homeOf;
17402
+ var init_usage = __esm({
17403
+ "src/hosts/codex/usage.ts"() {
17404
+ "use strict";
17405
+ init_time();
17406
+ init_usage_warn();
17407
+ init_fs();
17408
+ init_paths();
17409
+ init_rollout();
17410
+ FILE = "codex-usage.json";
17411
+ homeOf = (rollout) => dirname7(dirname7(dirname7(dirname7(dirname7(rollout)))));
17412
+ }
17413
+ });
17414
+
17264
17415
  // src/hosts/codex/hooks.ts
17265
17416
  import { basename as basename9 } from "node:path";
17266
17417
  function threadOf(input) {
@@ -17332,7 +17483,7 @@ function codexHooks(deps) {
17332
17483
  const m = REWAKE2.exec(prompt2);
17333
17484
  if (!m) {
17334
17485
  cancelPending(ctx, thread.id);
17335
- return void 0;
17486
+ return applySettings(ctx.stateDir).usageWarnings === "off" ? void 0 : usageWarning(ctx.stateDir, thread.path, ctx.now);
17336
17487
  }
17337
17488
  const c = continueOnly(CODEX_PLACE, m[1] ?? "", ctx.now);
17338
17489
  if (c.kind === "reply") return block2(c.text);
@@ -17434,6 +17585,7 @@ var init_hooks = __esm({
17434
17585
  init_time();
17435
17586
  init_closed();
17436
17587
  init_rollout();
17588
+ init_usage();
17437
17589
  CODEX_PLACE = { name: "Codex", typed: "rewake", features: /* @__PURE__ */ new Set() };
17438
17590
  REWAKE2 = /^\s*\/?rewake(?:\s+([\s\S]*?))?\s*$/i;
17439
17591
  block2 = (reason) => JSON.stringify({ decision: "block", reason });
@@ -17592,14 +17744,14 @@ var init_host6 = __esm({
17592
17744
  });
17593
17745
 
17594
17746
  // src/hosts/cursor/host.ts
17595
- import { readFileSync as readFileSync33 } from "node:fs";
17747
+ import { readFileSync as readFileSync34 } from "node:fs";
17596
17748
  function block3(userMessage) {
17597
17749
  return JSON.stringify({ continue: false, user_message: userMessage });
17598
17750
  }
17599
17751
  function transcriptError(path) {
17600
17752
  if (typeof path !== "string" || path === "") return void 0;
17601
17753
  try {
17602
- const lines = readFileSync33(path, "utf8").trimEnd().split("\n");
17754
+ const lines = readFileSync34(path, "utf8").trimEnd().split("\n");
17603
17755
  const last = JSON.parse(lines.at(-1) ?? "{}");
17604
17756
  if (last.type === "turn_ended" && last.status === "error" && typeof last.error === "string")
17605
17757
  return last.error;
@@ -18177,7 +18329,7 @@ var init_hook = __esm({
18177
18329
 
18178
18330
  // src/install/cleanup.ts
18179
18331
  import { existsSync as existsSync27, readdirSync as readdirSync19, rmdirSync, rmSync as rmSync20 } from "node:fs";
18180
- import { dirname as dirname7, join as join44 } from "node:path";
18332
+ import { dirname as dirname8, join as join45 } from "node:path";
18181
18333
  function removeHelpers(stateDir2, timers) {
18182
18334
  for (const id of timerIds(timers)) {
18183
18335
  try {
@@ -18185,16 +18337,16 @@ function removeHelpers(stateDir2, timers) {
18185
18337
  } catch {
18186
18338
  }
18187
18339
  }
18188
- rmSync20(join44(stateDir2, "timers"), { recursive: true, force: true });
18340
+ rmSync20(join45(stateDir2, "timers"), { recursive: true, force: true });
18189
18341
  for (const file of [launcherPath(stateDir2), nodeShimPath(stateDir2)])
18190
18342
  rmSync20(file, { force: true });
18191
- rmSync20(join44(stateDir2, "bin", "agent-rewake.version"), { force: true });
18343
+ rmSync20(join45(stateDir2, "bin", "agent-rewake.version"), { force: true });
18192
18344
  try {
18193
- rmdirSync(join44(stateDir2, "bin"));
18345
+ rmdirSync(join45(stateDir2, "bin"));
18194
18346
  } catch {
18195
18347
  }
18196
- rmSync20(join44(stateDir2, "locks"), { recursive: true, force: true });
18197
- rmSync20(join44(stateDir2, ".pruned"), { force: true });
18348
+ rmSync20(join45(stateDir2, "locks"), { recursive: true, force: true });
18349
+ rmSync20(join45(stateDir2, ".pruned"), { force: true });
18198
18350
  }
18199
18351
  function findBackups(dirs) {
18200
18352
  const found = /* @__PURE__ */ new Set();
@@ -18205,7 +18357,7 @@ function findBackups(dirs) {
18205
18357
  } catch {
18206
18358
  continue;
18207
18359
  }
18208
- for (const n of names) if (n.includes(".agent-rewake-backup-")) found.add(join44(dir, n));
18360
+ for (const n of names) if (n.includes(".agent-rewake-backup-")) found.add(join45(dir, n));
18209
18361
  }
18210
18362
  return [...found].sort();
18211
18363
  }
@@ -18216,11 +18368,11 @@ Rewake is still set up in ${list(f.stillIn)}, so its folder and the files those
18216
18368
  `;
18217
18369
  const kept = [];
18218
18370
  const mine = [
18219
- hasFiles(join44(f.stateDir, "schedules")) && "your scheduled messages",
18220
- hasFiles(join44(f.stateDir, "threads")) && "your thread settings",
18221
- existsSync27(join44(f.stateDir, "settings.json")) && "your settings",
18222
- hasFiles(join44(f.stateDir, "logs")) && "logs (no message text)",
18223
- hasFiles(join44(f.stateDir, "agents")) && "agents Rewake downloaded"
18371
+ hasFiles(join45(f.stateDir, "schedules")) && "your scheduled messages",
18372
+ hasFiles(join45(f.stateDir, "threads")) && "your thread settings",
18373
+ existsSync27(join45(f.stateDir, "settings.json")) && "your settings",
18374
+ hasFiles(join45(f.stateDir, "logs")) && "logs (no message text)",
18375
+ hasFiles(join45(f.stateDir, "agents")) && "agents Rewake downloaded"
18224
18376
  ].filter((x) => typeof x === "string");
18225
18377
  if (mine.length > 0) kept.push(`Rewake's folder, with ${list(mine)}: ${f.stateDir}`);
18226
18378
  for (const b of f.backups) kept.push(`A copy Rewake made of a file before changing it: ${b}`);
@@ -18246,9 +18398,9 @@ function finishUninstall(o) {
18246
18398
  removeHelpers(state, defaultTimerHost(state, "node", launcherPath(state)));
18247
18399
  const backups = findBackups([
18248
18400
  zedDir,
18249
- join44(o.home, ".cursor"),
18250
- dirname7(jetbrainsFile(o.env, o.home)),
18251
- dirname7(devinFile(o.env, o.home, o.platform))
18401
+ join45(o.home, ".cursor"),
18402
+ dirname8(jetbrainsFile(o.env, o.home)),
18403
+ dirname8(devinFile(o.env, o.home, o.platform))
18252
18404
  ]);
18253
18405
  const text2 = existsSync27(state) ? leftoverText({ stateDir: state, stillIn, backups, codex: o.chosen.includes("codex") }) : void 0;
18254
18406
  return { text: text2, remains: stillIn.length > 0 };
@@ -19063,11 +19215,11 @@ var init_report = __esm({
19063
19215
  });
19064
19216
 
19065
19217
  // src/timers/slots.ts
19066
- import { readFileSync as readFileSync34 } from "node:fs";
19067
- import { join as join45 } from "node:path";
19218
+ import { readFileSync as readFileSync35 } from "node:fs";
19219
+ import { join as join46 } from "node:path";
19068
19220
  function lastStart2(stateDir2) {
19069
19221
  try {
19070
- const n = Number(readFileSync34(join45(stateDir2, "locks", START_FILE), "utf8").trim());
19222
+ const n = Number(readFileSync35(join46(stateDir2, "locks", START_FILE), "utf8").trim());
19071
19223
  return Number.isFinite(n) ? n : 0;
19072
19224
  } catch {
19073
19225
  return 0;
@@ -19097,7 +19249,7 @@ async function takeSlot(o) {
19097
19249
  try {
19098
19250
  const wait2 = Math.min(lastStart2(o.stateDir) + gap - clock2(), deadline - clock2());
19099
19251
  if (wait2 > 0) await sleep(wait2);
19100
- writeFileAtomic(ensurePrivateDir(join45(o.stateDir, "locks")), START_FILE, `${clock2()}
19252
+ writeFileAtomic(ensurePrivateDir(join46(o.stateDir, "locks")), START_FILE, `${clock2()}
19101
19253
  `);
19102
19254
  } finally {
19103
19255
  if (mine) lock.release(START_KEY);
@@ -19614,14 +19766,14 @@ var init_fire = __esm({
19614
19766
  });
19615
19767
 
19616
19768
  // src/timers/login.ts
19617
- import { existsSync as existsSync29, mkdirSync as mkdirSync3, readFileSync as readFileSync35, rmSync as rmSync21 } from "node:fs";
19618
- import { dirname as dirname8, join as join46 } from "node:path";
19769
+ import { existsSync as existsSync29, mkdirSync as mkdirSync3, readFileSync as readFileSync36, rmSync as rmSync21 } from "node:fs";
19770
+ import { dirname as dirname9, join as join47 } from "node:path";
19619
19771
  function loginItem(h) {
19620
19772
  const args = [h.node, h.cli, "sweep", "--state-dir", h.stateDir];
19621
19773
  if (h.platform === "darwin")
19622
19774
  return {
19623
19775
  kind: "launchd",
19624
- path: join46(h.home, "Library", "LaunchAgents", `${LOGIN_LABEL}.plist`),
19776
+ path: join47(h.home, "Library", "LaunchAgents", `${LOGIN_LABEL}.plist`),
19625
19777
  text: `<?xml version="1.0" encoding="UTF-8"?>
19626
19778
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
19627
19779
  <plist version="1.0">
@@ -19641,7 +19793,7 @@ function loginItem(h) {
19641
19793
  if (h.exists("/run/systemd/system"))
19642
19794
  return {
19643
19795
  kind: "systemd",
19644
- path: join46(h.home, ".config", "systemd", "user", UNIT),
19796
+ path: join47(h.home, ".config", "systemd", "user", UNIT),
19645
19797
  text: `[Unit]
19646
19798
  Description=Agent Rewake: set planned resumes again after a restart
19647
19799
 
@@ -19655,7 +19807,7 @@ WantedBy=default.target
19655
19807
  };
19656
19808
  return {
19657
19809
  kind: "autostart",
19658
- path: join46(h.home, ".config", "autostart", "agent-rewake-sweep.desktop"),
19810
+ path: join47(h.home, ".config", "autostart", "agent-rewake-sweep.desktop"),
19659
19811
  text: `[Desktop Entry]
19660
19812
  Type=Application
19661
19813
  Name=Agent Rewake
@@ -19673,9 +19825,9 @@ function syncLoginItem(h, wanted) {
19673
19825
  if (!item) return "unchanged";
19674
19826
  const had = existsSync29(item.path);
19675
19827
  if (wanted) {
19676
- const same2 = had && readFileSync35(item.path, "utf8") === item.text;
19828
+ const same2 = had && readFileSync36(item.path, "utf8") === item.text;
19677
19829
  if (same2) return "unchanged";
19678
- mkdirSync3(dirname8(item.path), { recursive: true });
19830
+ mkdirSync3(dirname9(item.path), { recursive: true });
19679
19831
  replaceFileExclusive(item.path, item.text, 420);
19680
19832
  if (item.kind === "systemd") {
19681
19833
  h.run("systemctl", ["--user", "daemon-reload"]);
@@ -19721,10 +19873,10 @@ import {
19721
19873
  utimesSync,
19722
19874
  writeFileSync as writeFileSync7
19723
19875
  } from "node:fs";
19724
- import { join as join47 } from "node:path";
19876
+ import { join as join48 } from "node:path";
19725
19877
  function pruneState(stateDir2, now, opts = {}) {
19726
19878
  if (!existsSync30(stateDir2)) return void 0;
19727
- const marker = join47(stateDir2, MARKER);
19879
+ const marker = join48(stateDir2, MARKER);
19728
19880
  if (!opts.force)
19729
19881
  try {
19730
19882
  if (now - statSync10(marker).mtimeMs < EVERY_MS) return void 0;
@@ -19755,7 +19907,7 @@ function pruneState(stateDir2, now, opts = {}) {
19755
19907
  function pruneSessions(stateDir2, cutoff, needed) {
19756
19908
  let hosts;
19757
19909
  try {
19758
- hosts = readdirSync20(join47(stateDir2, "hosts"));
19910
+ hosts = readdirSync20(join48(stateDir2, "hosts"));
19759
19911
  } catch {
19760
19912
  return 0;
19761
19913
  }
@@ -19771,7 +19923,7 @@ function pruneSessions(stateDir2, cutoff, needed) {
19771
19923
  return n;
19772
19924
  }
19773
19925
  function pruneLogs(stateDir2, cutoff) {
19774
- const dir = join47(stateDir2, "logs");
19926
+ const dir = join48(stateDir2, "logs");
19775
19927
  let names;
19776
19928
  try {
19777
19929
  names = readdirSync20(dir);
@@ -19784,7 +19936,7 @@ function pruneLogs(stateDir2, cutoff) {
19784
19936
  const m = /^rewake-(\d{4}-\d{2}-\d{2})\.jsonl$/.exec(f);
19785
19937
  if (!m || m[1] >= oldest) continue;
19786
19938
  try {
19787
- rmSync22(join47(dir, f), { force: true });
19939
+ rmSync22(join48(dir, f), { force: true });
19788
19940
  n++;
19789
19941
  } catch {
19790
19942
  }
@@ -19800,7 +19952,7 @@ function pruneTemp(dir, before, depth = 0) {
19800
19952
  }
19801
19953
  let n = 0;
19802
19954
  for (const e of entries) {
19803
- const path = join47(dir, e.name);
19955
+ const path = join48(dir, e.name);
19804
19956
  if (e.isDirectory()) {
19805
19957
  if (depth < MAX_DEPTH && !SKIP_DIRS.has(e.name)) n += pruneTemp(path, before, depth + 1);
19806
19958
  } else if (e.isFile() && TEMP_NAME.test(e.name)) {
@@ -20055,8 +20207,8 @@ var init_input = __esm({
20055
20207
  });
20056
20208
 
20057
20209
  // src/ui/page.ts
20058
- import { readFileSync as readFileSync36, writeFileSync as writeFileSync8 } from "node:fs";
20059
- import { join as join48 } from "node:path";
20210
+ import { readFileSync as readFileSync37, writeFileSync as writeFileSync8 } from "node:fs";
20211
+ import { join as join49 } from "node:path";
20060
20212
  function rowLabel(r) {
20061
20213
  if (r.host === void 0) return threadLabel(r.thread, r.schedule);
20062
20214
  const title = r.thread?.title ?? `${capital(r.noun)} ${r.schedule.sessionId.slice(0, 8)}`;
@@ -20314,11 +20466,11 @@ var init_page = __esm({
20314
20466
  return this.rows[this.selected];
20315
20467
  }
20316
20468
  prefsFile() {
20317
- return join48(this.opts.stateDir, "ui.json");
20469
+ return join49(this.opts.stateDir, "ui.json");
20318
20470
  }
20319
20471
  prefs() {
20320
20472
  try {
20321
- return JSON.parse(readFileSync36(this.prefsFile(), "utf8"));
20473
+ return JSON.parse(readFileSync37(this.prefsFile(), "utf8"));
20322
20474
  } catch {
20323
20475
  return {};
20324
20476
  }
@@ -21248,11 +21400,11 @@ var init_tui = __esm({
21248
21400
  });
21249
21401
 
21250
21402
  // src/util/installed.ts
21251
- import { readFileSync as readFileSync37 } from "node:fs";
21252
- import { join as join49 } from "node:path";
21403
+ import { readFileSync as readFileSync38 } from "node:fs";
21404
+ import { join as join50 } from "node:path";
21253
21405
  function readInstalled(stateDir2) {
21254
21406
  try {
21255
- const v = JSON.parse(readFileSync37(join49(stateDir2, FILE), "utf8"));
21407
+ const v = JSON.parse(readFileSync38(join50(stateDir2, FILE2), "utf8"));
21256
21408
  return typeof v.version === "string" && typeof v.at === "number" && Number.isFinite(v.at) ? { version: v.version, at: v.at } : void 0;
21257
21409
  } catch {
21258
21410
  return void 0;
@@ -21263,26 +21415,26 @@ function recordInstall(stateDir2, version, now) {
21263
21415
  try {
21264
21416
  writeFileAtomic(
21265
21417
  ensurePrivateDir(stateDir2),
21266
- FILE,
21418
+ FILE2,
21267
21419
  `${JSON.stringify({ version, at: now })}
21268
21420
  `
21269
21421
  );
21270
21422
  } catch {
21271
21423
  }
21272
21424
  }
21273
- var FILE;
21425
+ var FILE2;
21274
21426
  var init_installed = __esm({
21275
21427
  "src/util/installed.ts"() {
21276
21428
  "use strict";
21277
21429
  init_store();
21278
21430
  init_paths();
21279
- FILE = "installed.json";
21431
+ FILE2 = "installed.json";
21280
21432
  }
21281
21433
  });
21282
21434
 
21283
21435
  // src/util/log.ts
21284
21436
  import { appendFileSync } from "node:fs";
21285
- import { join as join50 } from "node:path";
21437
+ import { join as join51 } from "node:path";
21286
21438
  var Logger;
21287
21439
  var init_log = __esm({
21288
21440
  "src/util/log.ts"() {
@@ -21319,8 +21471,8 @@ var init_log = __esm({
21319
21471
  try {
21320
21472
  const day = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
21321
21473
  if (!this.file?.endsWith(`rewake-${day}.jsonl`)) {
21322
- const dir = ensurePrivateDir(join50(stateDir(this.env), "logs"));
21323
- this.file = join50(dir, `rewake-${day}.jsonl`);
21474
+ const dir = ensurePrivateDir(join51(stateDir(this.env), "logs"));
21475
+ this.file = join51(dir, `rewake-${day}.jsonl`);
21324
21476
  }
21325
21477
  const record = { t: (/* @__PURE__ */ new Date()).toISOString(), level, event, pid: process.pid, ...fields2 };
21326
21478
  appendFileSync(this.file, `${JSON.stringify(record)}
@@ -21343,9 +21495,9 @@ __export(cli_exports, {
21343
21495
  testTiming: () => testTiming
21344
21496
  });
21345
21497
  import { spawnSync as spawnSync13 } from "node:child_process";
21346
- import { existsSync as existsSync31, readdirSync as readdirSync21, readFileSync as readFileSync38 } from "node:fs";
21498
+ import { existsSync as existsSync31, readdirSync as readdirSync21, readFileSync as readFileSync39 } from "node:fs";
21347
21499
  import { homedir as homedir23 } from "node:os";
21348
- import { join as join51 } from "node:path";
21500
+ import { join as join52 } from "node:path";
21349
21501
  import { emitKeypressEvents } from "node:readline";
21350
21502
  import { createInterface as createInterface6 } from "node:readline/promises";
21351
21503
  function agentIdentity(target, env) {
@@ -21754,7 +21906,7 @@ ${loginItemPlanText(process.platform)}`);
21754
21906
  const sep2 = argv.indexOf("--");
21755
21907
  if (wrap) {
21756
21908
  try {
21757
- const npmLog = join51(ensurePrivateDir(join51(stateDir(env), "logs")), "npm-install.log");
21909
+ const npmLog = join52(ensurePrivateDir(join52(stateDir(env), "logs")), "npm-install.log");
21758
21910
  agent = await wrappedAgentCommand(wrap.target, wrap.extra, env, stateDir(env), npmLog);
21759
21911
  } catch (err) {
21760
21912
  log.error("agent.resolve_failed", {
@@ -21960,7 +22112,7 @@ ${usageText()}
21960
22112
  return void 0;
21961
22113
  }
21962
22114
  function toolsRefused(state) {
21963
- const dir = join51(state, "logs");
22115
+ const dir = join52(state, "logs");
21964
22116
  const agents = /* @__PURE__ */ new Set();
21965
22117
  let files = [];
21966
22118
  try {
@@ -21969,7 +22121,7 @@ function toolsRefused(state) {
21969
22121
  return [];
21970
22122
  }
21971
22123
  for (const f of files) {
21972
- for (const line2 of readFileSync38(join51(dir, f), "utf8").split("\n")) {
22124
+ for (const line2 of readFileSync39(join52(dir, f), "utf8").split("\n")) {
21973
22125
  if (!line2.includes("agent_tools.refused")) continue;
21974
22126
  try {
21975
22127
  const r = JSON.parse(line2);
@@ -21996,7 +22148,7 @@ function setupZedText(env) {
21996
22148
  const task = [taskEntry(launch)];
21997
22149
  const keymap = [{ bindings: { [keyChord()]: ["task::Spawn", { task_name: TASK_LABEL }] } }];
21998
22150
  const dir = zedConfigDir();
21999
- const file = (name) => join51(dir, name);
22151
+ const file = (name) => join52(dir, name);
22000
22152
  return [
22001
22153
  `Easiest: run \`${rewake("install")}\`, which adds these for you after asking.`,
22002
22154
  "To add them by hand instead:",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rewake",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Continues this session when your usage limit resets, and sends messages you schedule into it.",
5
5
  "author": {
6
6
  "name": "Codize Labs",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codizelabs/agent-rewake",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Auto-resume Claude Code, Codex, Gemini CLI, GitHub Copilot, Grok or any Zed agent when its usage limit resets, in the same session, and schedule messages into your agent threads.",
5
5
  "keywords": [
6
6
  "zed",