@sleds/cli 0.1.24 → 0.1.26

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 (2) hide show
  1. package/dist/sleds.mjs +1656 -750
  2. package/package.json +1 -1
package/dist/sleds.mjs CHANGED
@@ -1,16 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
  // @bun
3
- var __esm = (fn, res, err) => () => {
4
- if (fn)
5
- try {
6
- res = fn(fn = 0);
7
- } catch (e) {
8
- err = [e];
9
- }
10
- if (err)
11
- throw err[0];
12
- return res;
3
+ import { createRequire } from "node:module";
4
+ var __defProp = Object.defineProperty;
5
+ var __returnValue = (v) => v;
6
+ function __exportSetter(name, newValue) {
7
+ this[name] = __returnValue.bind(null, newValue);
8
+ }
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, {
12
+ get: all[name],
13
+ enumerable: true,
14
+ configurable: true,
15
+ set: __exportSetter.bind(all, name)
16
+ });
13
17
  };
18
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
19
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
14
20
 
15
21
  // ../../packages/shared/src/mcp/format.ts
16
22
  var isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v), num = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined, str = (v) => typeof v === "string" ? v : undefined, parseMeter = (raw) => {
@@ -152,8 +158,8 @@ var init_format = __esm(() => {
152
158
  import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
153
159
  import { dirname, join, resolve } from "node:path";
154
160
  var BASE_URL_KEY = "ANTHROPIC_BASE_URL", AUTH_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN", claudeDir = (home, env = process.env) => {
155
- const o = env.CLAUDE_CONFIG_DIR?.trim();
156
- return o ? resolve(home, o) : join(home, ".claude");
161
+ const o2 = env.CLAUDE_CONFIG_DIR?.trim();
162
+ return o2 ? resolve(home, o2) : join(home, ".claude");
157
163
  }, hasHomeOverride = (env = process.env) => Boolean(env.SLEDS_HOME?.trim() || env.CLAUDE_CONFIG_DIR?.trim()), settingsPath = (home) => join(claudeDir(home), "settings.json"), sledsCommand = (sub, home, env = process.env) => withSeatEnv(`sleds ${sub}`, home, env), withSeatEnv = (command, home, env = process.env) => {
158
164
  const parts = [];
159
165
  const sh = env.SLEDS_HOME?.trim();
@@ -183,8 +189,8 @@ var BASE_URL_KEY = "ANTHROPIC_BASE_URL", AUTH_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN"
183
189
  visit(settings.hooks);
184
190
  return out;
185
191
  }, bakedHome = (command) => command.match(/(?:^|\s)SLEDS_HOME=('([^']*)'|(\S+))/)?.[2] ?? command.match(/(?:^|\s)SLEDS_HOME=(\S+)/)?.[1], shellWord = (v) => /^[\w./~-]+$/.test(v) ? v : `'${v.replace(/'/g, "'\\''")}'`, backupPath = (home) => join(claudeDir(home), "settings.json.sleds-backup"), tokenPath = (home, env = process.env) => {
186
- const o = env.SLEDS_HOME?.trim();
187
- return o ? join(resolve(home, o), ".sleds-token") : join(home, ".sleds-token");
192
+ const o2 = env.SLEDS_HOME?.trim();
193
+ return o2 ? join(resolve(home, o2), ".sleds-token") : join(home, ".sleds-token");
188
194
  }, skillDir = (home) => join(claudeDir(home), "skills", "sled"), skillPath = (home) => join(skillDir(home), "SKILL.md"), isObject5 = (v) => typeof v === "object" && v !== null && !Array.isArray(v), envOf = (settings) => {
189
195
  const env = settings.env;
190
196
  return isObject5(env) ? env : {};
@@ -320,12 +326,12 @@ var BASE_URL_KEY = "ANTHROPIC_BASE_URL", AUTH_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN"
320
326
  return withEventHooks(settings, event, list.filter((e) => !ours(e)));
321
327
  }, applySessionHook = (settings, command) => applyOnce(settings, SESSION_EVENT, isSledsSessionHook, sessionHookEntry(command)), removeSessionHook = (settings) => removeOurs(settings, SESSION_EVENT, isSledsSessionHook), hasSessionHook = (settings) => eventHooks(settings, SESSION_EVENT).some(isSledsSessionHook), applyStopHook = (settings, command) => applyOnce(settings, STOP_EVENT, isSledsStopHook, stopHookEntry(command)), removeStopHook = (settings) => removeOurs(settings, STOP_EVENT, isSledsStopHook), hasStopHook = (settings) => eventHooks(settings, STOP_EVENT).some(isSledsStopHook), isSeatToken = (token) => /^sled_[A-Za-z0-9_-]+$/.test(token), maskToken = (token) => token.length <= 9 ? `${token.slice(0, 5)}…` : `${token.slice(0, 9)}…`, serialize = (settings) => `${JSON.stringify(settings, null, 2)}
322
328
  `, hasClaudeCode = (home) => existsSync(claudeDir(home)), readSettings = (home) => {
323
- const p = settingsPath(home);
324
- if (!existsSync(p))
329
+ const p2 = settingsPath(home);
330
+ if (!existsSync(p2))
325
331
  return {};
326
- const parsed = JSON.parse(readFileSync(p, "utf8"));
332
+ const parsed = JSON.parse(readFileSync(p2, "utf8"));
327
333
  if (!isObject5(parsed))
328
- throw new Error(`${p} is not a JSON object`);
334
+ throw new Error(`${p2} is not a JSON object`);
329
335
  return parsed;
330
336
  }, writeSettings = (home, settings) => {
331
337
  mkdirSync(claudeDir(home), { recursive: true });
@@ -341,10 +347,10 @@ var BASE_URL_KEY = "ANTHROPIC_BASE_URL", AUTH_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN"
341
347
  writeFileSync(dst, readFileSync(src));
342
348
  return true;
343
349
  }, readTokenFile = (home) => {
344
- const p = tokenPath(home);
345
- if (!existsSync(p))
350
+ const p2 = tokenPath(home);
351
+ if (!existsSync(p2))
346
352
  return null;
347
- const t = readFileSync(p, "utf8").trim();
353
+ const t = readFileSync(p2, "utf8").trim();
348
354
  return t.length > 0 ? t : null;
349
355
  }, writeTokenFile = (home, token) => {
350
356
  mkdirSync(dirname(tokenPath(home)), { recursive: true, mode: 448 });
@@ -360,8 +366,8 @@ var init_settings = __esm(() => {
360
366
  import { chmodSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "node:fs";
361
367
  import { join as join2, resolve as resolve2 } from "node:path";
362
368
  var configDir = (home, env = process.env) => {
363
- const o = env.SLEDS_HOME?.trim();
364
- return o ? resolve2(home, o) : join2(home, ".config", "sleds");
369
+ const o2 = env.SLEDS_HOME?.trim();
370
+ return o2 ? resolve2(home, o2) : join2(home, ".config", "sleds");
365
371
  }, configPath = (home) => join2(configDir(home), "config.json"), isObject6 = (v) => typeof v === "object" && v !== null && !Array.isArray(v), optString = (v) => typeof v === "string" ? v : undefined, parsePriorEnv = (v) => {
366
372
  if (!isObject6(v))
367
373
  return;
@@ -438,15 +444,15 @@ var configDir = (home, env = process.env) => {
438
444
  return;
439
445
  const name = optString(raw.name);
440
446
  const slug = optString(raw.slug);
441
- const plan = raw.plan;
447
+ const plan2 = raw.plan;
442
448
  const c = raw.capabilities;
443
449
  const src = c.upstreamSource;
444
- if (name === undefined || slug === undefined || plan !== "team" && plan !== "enterprise" && plan !== "api" && plan !== "individual" || src !== "own" && src !== "shared" && src !== "none")
450
+ if (name === undefined || slug === undefined || plan2 !== "team" && plan2 !== "enterprise" && plan2 !== "api" && plan2 !== "individual" || src !== "own" && src !== "shared" && src !== "none")
445
451
  return;
446
452
  return {
447
453
  name,
448
454
  slug,
449
- plan,
455
+ plan: plan2,
450
456
  capabilities: {
451
457
  upstreamKey: c.upstreamKey === true,
452
458
  adminKey: c.adminKey === true,
@@ -456,10 +462,10 @@ var configDir = (home, env = process.env) => {
456
462
  mode: raw.mode === "proxy-always" ? "proxy-always" : "seat-first"
457
463
  };
458
464
  }, readConfig = (home) => {
459
- const p = configPath(home);
460
- if (!existsSync2(p))
465
+ const p2 = configPath(home);
466
+ if (!existsSync2(p2))
461
467
  return {};
462
- return parseConfig(JSON.parse(readFileSync2(p, "utf8")));
468
+ return parseConfig(JSON.parse(readFileSync2(p2, "utf8")));
463
469
  }, writeConfig = (home, cfg) => {
464
470
  const dir = configDir(home);
465
471
  mkdirSync2(dir, { recursive: true, mode: 448 });
@@ -478,11 +484,11 @@ var init_config = __esm(() => {
478
484
  import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync4, renameSync as renameSync3, writeFileSync as writeFileSync4 } from "node:fs";
479
485
  import { join as join4 } from "node:path";
480
486
  var METER_CACHE_TTL_MS = 1e4, METER_TIMEOUT_MS = 1500, isObject7 = (v) => typeof v === "object" && v !== null && !Array.isArray(v), num4 = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined, meterCachePath = (home) => join4(configDir(home), "meter.json"), readMeterCache = (home, now) => {
481
- const p = meterCachePath(home);
482
- if (!existsSync4(p))
487
+ const p2 = meterCachePath(home);
488
+ if (!existsSync4(p2))
483
489
  return null;
484
490
  try {
485
- const raw = JSON.parse(readFileSync4(p, "utf8"));
491
+ const raw = JSON.parse(readFileSync4(p2, "utf8"));
486
492
  if (!isObject7(raw))
487
493
  return null;
488
494
  const fetchedAt = num4(raw.fetchedAt);
@@ -501,12 +507,12 @@ var METER_CACHE_TTL_MS = 1e4, METER_TIMEOUT_MS = 1500, isObject7 = (v) => typeof
501
507
  writeFileSync4(tmp, `${JSON.stringify(file)}
502
508
  `, { mode: 384 });
503
509
  renameSync3(tmp, target);
504
- }, fetchMeter = async (o) => {
505
- const fetchFn = o.fetchFn ?? fetch;
510
+ }, fetchMeter = async (o2) => {
511
+ const fetchFn = o2.fetchFn ?? fetch;
506
512
  try {
507
- const res = await fetchFn(`${o.proxyUrl}/proxy/meter`, {
508
- headers: { authorization: `Bearer ${o.token}` },
509
- signal: AbortSignal.timeout(o.timeoutMs ?? METER_TIMEOUT_MS)
513
+ const res = await fetchFn(`${o2.proxyUrl}/proxy/meter`, {
514
+ headers: { authorization: `Bearer ${o2.token}` },
515
+ signal: AbortSignal.timeout(o2.timeoutMs ?? METER_TIMEOUT_MS)
510
516
  });
511
517
  if (!res.ok)
512
518
  return null;
@@ -521,18 +527,18 @@ var METER_CACHE_TTL_MS = 1e4, METER_TIMEOUT_MS = 1500, isObject7 = (v) => typeof
521
527
  const proxyUrl = inSettings?.url ?? cfg.proxyUrl;
522
528
  const token = inSettings?.token ?? cfg.token;
523
529
  return proxyUrl && token ? { proxyUrl, token } : null;
524
- }, loadMeter = async (o) => {
525
- const now = o.now ?? Date.now();
526
- const cached = readMeterCache(o.home, now);
530
+ }, loadMeter = async (o2) => {
531
+ const now = o2.now ?? Date.now();
532
+ const cached = readMeterCache(o2.home, now);
527
533
  if (cached)
528
534
  return cached;
529
- const src = meterSource(o.home);
535
+ const src = meterSource(o2.home);
530
536
  if (!src)
531
537
  return null;
532
- const fresh = await fetchMeter({ ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs });
538
+ const fresh = await fetchMeter({ ...src, fetchFn: o2.fetchFn, timeoutMs: o2.timeoutMs });
533
539
  if (fresh) {
534
540
  try {
535
- writeMeterCache(o.home, fresh, now);
541
+ writeMeterCache(o2.home, fresh, now);
536
542
  } catch {}
537
543
  }
538
544
  return fresh;
@@ -545,9 +551,432 @@ var init_meter = __esm(() => {
545
551
  });
546
552
 
547
553
  // src/seat.ts
548
- var SEAT_POST_MIN_INTERVAL_MS2, readStdin2 = (timeoutMs, stdin = process.stdin) => new Promise((resolve) => {
554
+ var exports_seat = {};
555
+ __export(exports_seat, {
556
+ APPROACHING_PCT: () => APPROACHING_PCT2,
557
+ CREDITS_PATTERN: () => CREDITS_PATTERN2,
558
+ SEAT_POST_MIN_DELTA_PCT: () => SEAT_POST_MIN_DELTA_PCT2,
559
+ SEAT_POST_MIN_INTERVAL_MS: () => SEAT_POST_MIN_INTERVAL_MS2,
560
+ SEAT_POST_TIMEOUT_MS: () => SEAT_POST_TIMEOUT_MS2,
561
+ SEAT_WALL_LINE: () => SEAT_WALL_LINE2,
562
+ SEAT_WALL_LINE_NO_KEY: () => SEAT_WALL_LINE_NO_KEY2,
563
+ STDIN_TIMEOUT_MS: () => STDIN_TIMEOUT_MS2,
564
+ bindingResetAt: () => bindingResetAt2,
565
+ creditsHitIn: () => creditsHitIn2,
566
+ formatSeatFragment: () => formatSeatFragment2,
567
+ markPosted: () => markPosted2,
568
+ markWarned: () => markWarned2,
569
+ parseResetsAt: () => parseResetsAt2,
570
+ parseSeatState: () => parseSeatState2,
571
+ parseSeatWindow: () => parseSeatWindow2,
572
+ postSeatWall: () => postSeatWall2,
573
+ postSeatWindow: () => postSeatWindow2,
574
+ readSeatState: () => readSeatState2,
575
+ readStdin: () => readStdin2,
576
+ scanTranscriptForCredits: () => scanTranscriptForCredits2,
577
+ seatSessionFromStdin: () => seatSessionFromStdin2,
578
+ seatStatePath: () => seatStatePath2,
579
+ seatWallDue: () => seatWallDue,
580
+ seatWallEvents: () => seatWallEvents2,
581
+ seatWallLine: () => seatWallLine2,
582
+ seatWindowBody: () => seatWindowBody2,
583
+ seatWindowFromStdin: () => seatWindowFromStdin2,
584
+ shouldPostSeat: () => shouldPostSeat2,
585
+ transcriptPathFromStdin: () => transcriptPathFromStdin2,
586
+ writeSeatState: () => writeSeatState2
587
+ });
588
+ import {
589
+ closeSync as closeSync2,
590
+ existsSync as existsSync18,
591
+ mkdirSync as mkdirSync14,
592
+ openSync as openSync2,
593
+ readFileSync as readFileSync15,
594
+ readSync as readSync2,
595
+ renameSync as renameSync7,
596
+ statSync as statSync3,
597
+ writeFileSync as writeFileSync14
598
+ } from "node:fs";
599
+ import { join as join15 } from "node:path";
600
+ var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_MS2 = 1500, STDIN_TIMEOUT_MS2 = 300, isObject15 = (v) => typeof v === "object" && v !== null && !Array.isArray(v), num7 = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined, EPOCH_MS_THRESHOLD2 = 100000000000, parseResetsAt2 = (v) => {
601
+ const n2 = num7(v);
602
+ if (n2 !== undefined) {
603
+ if (n2 <= 0)
604
+ return;
605
+ return n2 >= EPOCH_MS_THRESHOLD2 ? Math.round(n2) : Math.round(n2 * 1000);
606
+ }
607
+ if (typeof v === "string" && v.trim() !== "") {
608
+ const parsed = Date.parse(v);
609
+ return Number.isFinite(parsed) ? parsed : undefined;
610
+ }
611
+ return;
612
+ }, parseBar2 = (v) => {
613
+ if (!isObject15(v))
614
+ return;
615
+ const usedPct = num7(v.used_percentage) ?? num7(v.usedPct);
616
+ const resetsAt = parseResetsAt2(v.resets_at ?? v.resetsAt);
617
+ if (usedPct === undefined || usedPct < 0 || resetsAt === undefined)
618
+ return;
619
+ return { usedPct, resetsAt };
620
+ }, parseSeatWindow2 = (raw, capturedAt) => {
621
+ if (!isObject15(raw))
622
+ return null;
623
+ const limits = isObject15(raw.rate_limits) ? raw.rate_limits : raw;
624
+ const fiveHour = parseBar2(limits.five_hour ?? limits.fiveHour);
625
+ const sevenDay = parseBar2(limits.seven_day ?? limits.sevenDay);
626
+ const spendLimit = parseBar2(limits.spend_limit ?? limits.spendLimit);
627
+ const models = {};
628
+ if (isObject15(limits.models))
629
+ for (const [k, v] of Object.entries(limits.models)) {
630
+ const bar = parseBar2(v);
631
+ if (bar)
632
+ models[k] = bar;
633
+ }
634
+ const usedCreditsUsd = num7(limits.usedCreditsUsd);
635
+ const hasModels = Object.keys(models).length > 0;
636
+ if (!fiveHour && !sevenDay && !spendLimit && !hasModels && usedCreditsUsd === undefined)
637
+ return null;
638
+ const out = { capturedAt };
639
+ if (fiveHour)
640
+ out.fiveHour = fiveHour;
641
+ if (sevenDay)
642
+ out.sevenDay = sevenDay;
643
+ if (spendLimit)
644
+ out.spendLimit = spendLimit;
645
+ if (hasModels)
646
+ out.models = models;
647
+ if (usedCreditsUsd !== undefined)
648
+ out.usedCreditsUsd = usedCreditsUsd;
649
+ return out;
650
+ }, seatWindowFromStdin2 = (text, capturedAt) => {
651
+ const trimmed = text.trim();
652
+ if (!trimmed)
653
+ return null;
654
+ try {
655
+ return parseSeatWindow2(JSON.parse(trimmed), capturedAt);
656
+ } catch {
657
+ return null;
658
+ }
659
+ }, formatSeatFragment2 = (w, now = Date.now()) => {
660
+ if (!w)
661
+ return null;
662
+ const parts = [];
663
+ if (w.sevenDay)
664
+ parts.push(`wk ${Math.round(w.sevenDay.usedPct)}%`);
665
+ if (w.fiveHour)
666
+ parts.push(`5h ${Math.round(w.fiveHour.usedPct)}%`);
667
+ if (w.spendLimit)
668
+ parts.push(`spend ${Math.round(w.spendLimit.usedPct)}%`);
669
+ if (parts.length === 0)
670
+ return null;
671
+ const hot = [
672
+ ["wk", w.sevenDay],
673
+ ["5h", w.fiveHour],
674
+ ["spend", w.spendLimit]
675
+ ].filter((x) => x[1] !== undefined && x[1].usedPct >= APPROACHING_PCT2).sort((a, b) => b[1].usedPct - a[1].usedPct)[0];
676
+ if (hot) {
677
+ const mins = Math.max(0, Math.round((hot[1].resetsAt - now) / 60000));
678
+ const reset = mins >= 60 ? `${Math.floor(mins / 60)}h${mins % 60 ? `${mins % 60}m` : ""}` : `${mins}m`;
679
+ const verb = hot[1].usedPct >= 100 ? "at the wall" : `${Math.round(hot[1].usedPct)}%`;
680
+ return `⚠ seat ${hot[0]} ${verb}${SEP}resets ${reset}${SEP}/sled handoff · sleds defer`;
681
+ }
682
+ return `seat ${parts.join(SEP)}`;
683
+ }, APPROACHING_PCT2 = 90, seatStatePath2 = (home) => join15(configDir(home), "seat.json"), parseWindowStored2 = (v) => {
684
+ if (!isObject15(v))
685
+ return;
686
+ const capturedAt = num7(v.capturedAt);
687
+ if (capturedAt === undefined)
688
+ return;
689
+ return parseSeatWindow2(v, capturedAt) ?? undefined;
690
+ }, parseSeatState2 = (raw) => {
691
+ if (!isObject15(raw))
692
+ return {};
693
+ const out = {};
694
+ const window = parseWindowStored2(raw.window);
695
+ if (window)
696
+ out.window = window;
697
+ const lastPostedAt = num7(raw.lastPostedAt);
698
+ if (lastPostedAt !== undefined)
699
+ out.lastPostedAt = lastPostedAt;
700
+ if (isObject15(raw.lastPosted)) {
701
+ const lp = {};
702
+ const f = num7(raw.lastPosted.fiveHour);
703
+ const s = num7(raw.lastPosted.sevenDay);
704
+ const sp = num7(raw.lastPosted.spendLimit);
705
+ if (f !== undefined)
706
+ lp.fiveHour = f;
707
+ if (s !== undefined)
708
+ lp.sevenDay = s;
709
+ if (sp !== undefined)
710
+ lp.spendLimit = sp;
711
+ out.lastPosted = lp;
712
+ }
713
+ const lastWallResetsAt = num7(raw.lastWallResetsAt);
714
+ if (lastWallResetsAt !== undefined)
715
+ out.lastWallResetsAt = lastWallResetsAt;
716
+ if (isObject15(raw.warned)) {
717
+ const warned = {};
718
+ for (const bar of ["fiveHour", "sevenDay", "spendLimit", "credits"]) {
719
+ const w = raw.warned[bar];
720
+ if (!isObject15(w))
721
+ continue;
722
+ const entry = {};
723
+ const ap = num7(w.approaching);
724
+ const hit = num7(w.hit);
725
+ if (ap !== undefined)
726
+ entry.approaching = ap;
727
+ if (hit !== undefined)
728
+ entry.hit = hit;
729
+ warned[bar] = entry;
730
+ }
731
+ out.warned = warned;
732
+ }
733
+ if (isObject15(raw.told)) {
734
+ const told = {};
735
+ for (const bar of ["fiveHour", "sevenDay", "spendLimit", "credits"]) {
736
+ const w = raw.told[bar];
737
+ if (!isObject15(w))
738
+ continue;
739
+ const entry = {};
740
+ const ap = num7(w.approaching);
741
+ const hit = num7(w.hit);
742
+ if (ap !== undefined)
743
+ entry.approaching = ap;
744
+ if (hit !== undefined)
745
+ entry.hit = hit;
746
+ told[bar] = entry;
747
+ }
748
+ out.told = told;
749
+ }
750
+ if (isObject15(raw.transcriptScan) && typeof raw.transcriptScan.path === "string") {
751
+ const offset = num7(raw.transcriptScan.offset);
752
+ const hitAt = num7(raw.transcriptScan.hitAt);
753
+ out.transcriptScan = {
754
+ path: raw.transcriptScan.path,
755
+ offset: offset ?? 0,
756
+ ...hitAt !== undefined ? { hitAt } : {}
757
+ };
758
+ }
759
+ const session = parseSeatSession2(raw.session);
760
+ if (session)
761
+ out.session = session;
762
+ if (isObject15(raw.cockpit) && raw.cockpit.kind === "cmux") {
763
+ const c = { kind: "cmux" };
764
+ if (isObject15(raw.cockpit.lines)) {
765
+ const lines = {};
766
+ for (const [k, v] of Object.entries(raw.cockpit.lines))
767
+ if (typeof v === "string")
768
+ lines[k] = v;
769
+ c.lines = lines;
770
+ }
771
+ if (raw.cockpit.progress === true)
772
+ c.progress = true;
773
+ const f = raw.cockpit.feed;
774
+ if (isObject15(f) && num7(f.at) !== undefined) {
775
+ const strs = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
776
+ const j = isObject15(f.jobs) ? f.jobs : {};
777
+ c.feed = {
778
+ at: num7(f.at),
779
+ handoffs: strs(f.handoffs),
780
+ jobs: {
781
+ scheduled: num7(j.scheduled) ?? 0,
782
+ running: num7(j.running) ?? 0,
783
+ delivered: num7(j.delivered) ?? 0,
784
+ failed: num7(j.failed) ?? 0
785
+ },
786
+ delivered: strs(f.delivered),
787
+ failed: strs(f.failed)
788
+ };
789
+ }
790
+ out.cockpit = c;
791
+ }
792
+ if (isObject15(raw.estimate)) {
793
+ const e = raw.estimate;
794
+ const project = typeof e.project === "string" ? e.project : undefined;
795
+ const [lo, hi, lm, hm, at] = [e.lowUsd, e.highUsd, e.lowMin, e.highMin, e.at].map(num7);
796
+ if (project && lo !== undefined && hi !== undefined && lm !== undefined && hm !== undefined && at !== undefined)
797
+ out.estimate = { project, lowUsd: lo, highUsd: hi, lowMin: lm, highMin: hm, at };
798
+ }
799
+ return out;
800
+ }, parseSeatSession2 = (v) => {
801
+ if (!isObject15(v) || typeof v.id !== "string" || !v.id)
802
+ return;
803
+ const at = num7(v.at);
804
+ if (at === undefined)
805
+ return;
806
+ const out = { id: v.id, at };
807
+ if (typeof v.cwd === "string" && v.cwd)
808
+ out.cwd = v.cwd;
809
+ if (typeof v.model === "string" && v.model)
810
+ out.model = v.model;
811
+ return out;
812
+ }, seatSessionFromStdin2 = (text, now) => {
813
+ const trimmed = text.trim();
814
+ if (!trimmed)
815
+ return null;
816
+ try {
817
+ const raw = JSON.parse(trimmed);
818
+ if (!isObject15(raw) || typeof raw.session_id !== "string" || !raw.session_id)
819
+ return null;
820
+ const out = { id: raw.session_id, at: now };
821
+ if (typeof raw.cwd === "string" && raw.cwd)
822
+ out.cwd = raw.cwd;
823
+ const model = isObject15(raw.model) ? raw.model.id : raw.model;
824
+ if (typeof model === "string" && model)
825
+ out.model = model;
826
+ return out;
827
+ } catch {
828
+ return null;
829
+ }
830
+ }, bindingResetAt2 = (w, now) => {
831
+ if (!w)
832
+ return;
833
+ const bars = [
834
+ ...w.fiveHour ? [w.fiveHour] : [],
835
+ ...w.sevenDay ? [w.sevenDay] : [],
836
+ ...w.spendLimit ? [w.spendLimit] : [],
837
+ ...Object.values(w.models ?? {})
838
+ ].filter((b) => b.resetsAt > now);
839
+ const full = bars.filter((b) => b.usedPct >= 100);
840
+ const pick = (full.length ? full : bars).map((b) => b.resetsAt);
841
+ return pick.length ? Math.min(...pick) : undefined;
842
+ }, readSeatState2 = (home) => {
843
+ const p2 = seatStatePath2(home);
844
+ if (!existsSync18(p2))
845
+ return {};
846
+ try {
847
+ return parseSeatState2(JSON.parse(readFileSync15(p2, "utf8")));
848
+ } catch {
849
+ return {};
850
+ }
851
+ }, writeSeatState2 = (home, state) => {
852
+ const dir = configDir(home);
853
+ mkdirSync14(dir, { recursive: true, mode: 448 });
854
+ const target = seatStatePath2(home);
855
+ const tmp = `${target}.${process.pid}.tmp`;
856
+ writeFileSync14(tmp, `${JSON.stringify(state)}
857
+ `, { mode: 384 });
858
+ renameSync7(tmp, target);
859
+ }, pctOf2 = (b) => b?.usedPct, shouldPostSeat2 = (state, next, now) => {
860
+ if (state.lastPostedAt === undefined || !state.lastPosted)
861
+ return true;
862
+ if (now - state.lastPostedAt >= SEAT_POST_MIN_INTERVAL_MS2)
863
+ return true;
864
+ const pairs = [
865
+ [state.lastPosted.fiveHour, pctOf2(next.fiveHour)],
866
+ [state.lastPosted.sevenDay, pctOf2(next.sevenDay)],
867
+ [state.lastPosted.spendLimit, pctOf2(next.spendLimit)]
868
+ ];
869
+ for (const [prev, cur] of pairs) {
870
+ if (prev === undefined && cur === undefined)
871
+ continue;
872
+ if (prev === undefined || cur === undefined)
873
+ return true;
874
+ if (Math.abs(cur - prev) >= SEAT_POST_MIN_DELTA_PCT2)
875
+ return true;
876
+ }
877
+ return false;
878
+ }, markPosted2 = (state, w, now) => {
879
+ const lastPosted = {};
880
+ if (w.fiveHour)
881
+ lastPosted.fiveHour = w.fiveHour.usedPct;
882
+ if (w.sevenDay)
883
+ lastPosted.sevenDay = w.sevenDay.usedPct;
884
+ if (w.spendLimit)
885
+ lastPosted.spendLimit = w.spendLimit.usedPct;
886
+ return { ...state, window: w, lastPostedAt: now, lastPosted };
887
+ }, seatWallDue = (state, w) => {
888
+ if (!w?.sevenDay || w.sevenDay.usedPct < 100)
889
+ return false;
890
+ return state.lastWallResetsAt === undefined || state.lastWallResetsAt < w.sevenDay.resetsAt;
891
+ }, seatWallEvents2 = (state, w) => {
892
+ if (!w)
893
+ return [];
894
+ const out = [];
895
+ const bars = [
896
+ ["fiveHour", w.fiveHour],
897
+ ["sevenDay", w.sevenDay],
898
+ ["spendLimit", w.spendLimit]
899
+ ];
900
+ for (const [bar, b] of bars) {
901
+ if (!b)
902
+ continue;
903
+ const seen = state.warned?.[bar] ?? {};
904
+ const weeklyOld = bar === "sevenDay" && state.lastWallResetsAt !== undefined && state.lastWallResetsAt >= b.resetsAt;
905
+ if (b.usedPct >= APPROACHING_PCT2 && (seen.approaching ?? -1) < b.resetsAt && !weeklyOld)
906
+ out.push({ bar, stage: "approaching", usedPct: b.usedPct, resetsAt: b.resetsAt });
907
+ if (b.usedPct >= 100 && (seen.hit ?? -1) < b.resetsAt && !weeklyOld)
908
+ out.push({ bar, stage: "hit", usedPct: b.usedPct, resetsAt: b.resetsAt });
909
+ }
910
+ return out;
911
+ }, markWarned2 = (state, events) => {
912
+ if (events.length === 0)
913
+ return state;
914
+ const warned = { ...state.warned ?? {} };
915
+ let lastWallResetsAt = state.lastWallResetsAt;
916
+ for (const e of events) {
917
+ warned[e.bar] = { ...warned[e.bar] ?? {}, [e.stage]: e.resetsAt };
918
+ if (e.bar === "sevenDay" && e.stage === "hit")
919
+ lastWallResetsAt = e.resetsAt;
920
+ }
921
+ return { ...state, warned, ...lastWallResetsAt !== undefined ? { lastWallResetsAt } : {} };
922
+ }, CREDITS_PATTERN2, creditsHitIn2 = (text) => CREDITS_PATTERN2.test(text), transcriptPathFromStdin2 = (text) => {
923
+ try {
924
+ const raw = JSON.parse(text.trim());
925
+ return isObject15(raw) && typeof raw.transcript_path === "string" && raw.transcript_path ? raw.transcript_path : null;
926
+ } catch {
927
+ return null;
928
+ }
929
+ }, scanTranscriptForCredits2 = (path, prior) => {
930
+ const from = prior && prior.path === path ? prior.offset : 0;
931
+ try {
932
+ const size = statSync3(path).size;
933
+ if (size <= from)
934
+ return {
935
+ hit: false,
936
+ next: { path, offset: size, ...prior?.hitAt ? { hitAt: prior.hitAt } : {} }
937
+ };
938
+ const start = Math.max(from, size - 256 * 1024);
939
+ const fd = openSync2(path, "r");
940
+ try {
941
+ const buf = Buffer.alloc(size - start);
942
+ readSync2(fd, buf, 0, buf.length, start);
943
+ const hit = creditsHitIn2(buf.toString("utf8"));
944
+ return {
945
+ hit,
946
+ next: {
947
+ path,
948
+ offset: size,
949
+ ...hit ? { hitAt: Date.now() } : prior?.hitAt ? { hitAt: prior.hitAt } : {}
950
+ }
951
+ };
952
+ } finally {
953
+ closeSync2(fd);
954
+ }
955
+ } catch {
956
+ return { hit: false, next: prior && prior.path === path ? prior : { path, offset: 0 } };
957
+ }
958
+ }, seatWindowBody2 = (w) => ({
959
+ capturedAt: w.capturedAt,
960
+ source: "claude",
961
+ ...w.fiveHour ? { fiveHour: w.fiveHour } : {},
962
+ ...w.sevenDay ? { sevenDay: w.sevenDay } : {},
963
+ ...w.spendLimit ? { spendLimit: w.spendLimit } : {}
964
+ }), postJson2 = async (o2, path, body) => {
965
+ const fetchFn = o2.fetchFn ?? fetch;
966
+ try {
967
+ const res = await fetchFn(`${o2.proxyUrl}${path}`, {
968
+ method: "POST",
969
+ headers: { authorization: `Bearer ${o2.token}`, "content-type": "application/json" },
970
+ body: JSON.stringify(body),
971
+ signal: AbortSignal.timeout(o2.timeoutMs ?? SEAT_POST_TIMEOUT_MS2)
972
+ });
973
+ return res.ok;
974
+ } catch {
975
+ return false;
976
+ }
977
+ }, postSeatWindow2 = (o2, w) => postJson2(o2, "/proxy/seat", seatWindowBody2(w)), postSeatWall2 = (o2, input) => postJson2(o2, "/proxy/seat-wall", { source: "claude", ...input }), SEAT_WALL_LINE2 = "Seat limit hit. sleds on --until-reset to continue on the platform key, or sleds defer to resume here at the reset.", SEAT_WALL_LINE_NO_KEY2 = "Seat limit hit. sleds defer resumes this session on this Mac at the reset; /sled 5 to hand off — this org has no platform key to switch to.", seatWallLine2 = (hasUpstreamKey) => hasUpstreamKey ? SEAT_WALL_LINE2 : SEAT_WALL_LINE_NO_KEY2, readStdin2 = (timeoutMs, stdin = process.stdin) => new Promise((resolve6) => {
549
978
  if (stdin.isTTY) {
550
- resolve("");
979
+ resolve6("");
551
980
  return;
552
981
  }
553
982
  let data = "";
@@ -557,7 +986,7 @@ var SEAT_POST_MIN_INTERVAL_MS2, readStdin2 = (timeoutMs, stdin = process.stdin)
557
986
  return;
558
987
  settled = true;
559
988
  clearTimeout(timer);
560
- resolve(data);
989
+ resolve6(data);
561
990
  };
562
991
  const timer = setTimeout(finish, timeoutMs);
563
992
  stdin.setEncoding("utf8");
@@ -572,6 +1001,7 @@ var init_seat = __esm(() => {
572
1001
  init_config();
573
1002
  init_meter();
574
1003
  SEAT_POST_MIN_INTERVAL_MS2 = 5 * 60000;
1004
+ CREDITS_PATTERN2 = /out of usage credits|cc_cli_limit_message/i;
575
1005
  });
576
1006
 
577
1007
  // src/index.ts
@@ -757,28 +1187,34 @@ var GUIDES = [
757
1187
  id: "board",
758
1188
  title: "Read the board",
759
1189
  audience: "everyone",
760
- lede: "Sleds is the board: the pool of sized work, a sled per seat with the jobs moving on it, and what landed this week.",
1190
+ lede: "Work is the items: the pool of sized work and what landed this week. Sleds is who pulls them — a person or an agent on its own — and Seats what each sled pulls with.",
761
1191
  steps: [
762
1192
  {
763
- do: "Open `Sleds` and read the strip",
764
- where: "Sleds",
765
- note: "Five lanes: waiting for product · ready · moving · landed this week · worth landed. Click a lane to narrow the page; `Mine` shows your sled, the rows proposed for you and the asks you filed."
1193
+ do: "Open `Work` and read the strip",
1194
+ where: "Work",
1195
+ note: "Five lanes: waiting for product · ready · moving · landed this week · worth landed. Click a lane to narrow the page; `Mine` shows your sled, the items proposed for you and the asks you filed."
1196
+ },
1197
+ {
1198
+ do: "Read a pool item: the band, the worth, the chip, the readiness",
1199
+ note: "`proposed` names the sled the rules (and the model) suggest, with why; `waiting for product` needs a yes; `N gaps` says what is missing before anyone could start; an amber note says a related or duplicate item exists. `Take it` puts it on your sled; an approver can `Put it on <name>'s sled`."
766
1200
  },
767
1201
  {
768
- do: "Read a pool row: the band, the worth, the chip",
769
- note: "`proposed` names the seat the rules (and the model) suggest, with why; `waiting for product` needs a yes; an amber note says a related or duplicate row exists. `Take it` puts it on your sled; an approver can `Put it on <seat>'s sled`."
1202
+ do: "Read a sled card: the seats, the jobs, the band spent",
1203
+ where: "Sleds",
1204
+ note: "Each seat is one agent on one platform with its own wall; the bar is the window used. A job says which seat it runs on and, at a wall, `paused at a wall` with `Move to <seat>` when the sled has headroom elsewhere."
770
1205
  },
771
1206
  {
772
- do: "Read a seat card: the rails, the jobs, the band spent",
773
- note: "Each rail is one agent on one platform with its own wall; the bar is the window used. A job says which rail it runs on and, at a wall, `paused at a wall` with `Move to <rail>` when the seat has headroom elsewhere."
1207
+ do: "Open an item for the whole story",
1208
+ note: "The modal: the description, the links and files, the brief and who filed it, the child items, the questions, the thread — and every verb that applies to you. `copy as markdown` is the whole item as one document."
774
1209
  },
775
1210
  {
776
- do: "Open a row for the whole story",
777
- note: "The panel: the brief and who filed it, what the AI added, the recommendation, milestones with holders, runs, the receipt when it lands, and every verb that applies to you."
1211
+ do: "File one from here when chat is not at hand",
1212
+ where: "Work › + New item",
1213
+ note: "Title, why, done — the same brief your AI files from chat — then the description, repos, due, worth and a parent if it has one. Sized on file."
778
1214
  }
779
1215
  ],
780
- check: "Your own sled shows under `Mine` with the jobs you hold; a landed row shows its measured time and verdict under `Landed this week` and on Record › Receipts.",
781
- scope: "A product viewer sees seats and holders, never rails, models or spend; the words on the board are the words in chat — sled, seat, rail, job, wall, receipt."
1216
+ check: "Your own sled shows under `Mine` with the jobs you hold; a landed item shows its measured time and verdict under `Landed this week` and on Record › Receipts.",
1217
+ scope: "A product viewer sees sleds and holders, never seats, models or spend; the words on the board are the words in chat — item, sled, seat, job, wall, receipt."
782
1218
  },
783
1219
  {
784
1220
  id: "workspace",
@@ -833,7 +1269,7 @@ var GUIDES = [
833
1269
  {
834
1270
  do: "Add the custom connector",
835
1271
  where: "claude.ai › Settings › Connectors › `Add custom connector`",
836
- note: "URL `https://agent.sleds.ai/mcp`. It signs you in with OAuth — the same email your workspace invited."
1272
+ note: "URL `https://mcp.sleds.ai/mcp`. It signs you in with OAuth — the same email your workspace invited."
837
1273
  },
838
1274
  {
839
1275
  do: "Say `/sled usage` in any chat",
@@ -852,7 +1288,7 @@ var GUIDES = [
852
1288
  {
853
1289
  do: "Optional: SLEDS for Chrome — the meter, and the Team roster and credits when you administer the plan",
854
1290
  where: "The zip your admin sends › unzip › `chrome://extensions` › Developer mode › `Load unpacked` › the `dist` folder",
855
- note: "Then right-click the icon › Options: Proxy URL `https://proxy.sleds.ai`, the same seat token, platform Anthropic, tick the consent box, Save. It reads your seat's bars from claude.ai — the rails the proxy cannot see — and reports them even when Claude Code is closed. Chrome nags about developer-mode extensions on start until the Web Store listing; dismiss it."
1291
+ note: "Then right-click the icon › Options: Proxy URL `https://proxy.sleds.ai`, the same seat token, platform Anthropic, tick the consent box, Save. It reads your seat's bars from claude.ai — the quota the proxy cannot see — and reports them even when Claude Code is closed. Chrome nags about developer-mode extensions on start until the Web Store listing; dismiss it."
856
1292
  }
857
1293
  ],
858
1294
  check: "`/sled usage` answers with your name and workspace; the status line in Claude Code shows the same bars."
@@ -904,7 +1340,7 @@ var GUIDES = [
904
1340
  {
905
1341
  do: "Connect the site and name the projects to mirror",
906
1342
  where: "Settings › Integrations › Jira › `Connect`",
907
- note: "`Site` is the host (`acme.atlassian.net`). `Projects to mirror, read-only` takes project keys (`WORK, CORE`): their open issues become sized rows in the pool on Sleds, epics with their children, and an issue that reaches Done lands its row — pulled every 30 minutes or with `Sync now`. Leave `Project key intake files into` blank for read-only; set it later and intake files a Jira issue and `plan` files sub-tasks."
1343
+ note: "`Site` is the host (`acme.atlassian.net`). `Projects to mirror, read-only` takes project keys (`WORK, CORE`): their open issues become sized items in the pool on Sleds, epics with their children, and an issue that reaches Done lands its row — pulled every 30 minutes or with `Sync now`. Leave `Project key intake files into` blank for read-only; set it later and intake files a Jira issue and `plan` files sub-tasks."
908
1344
  },
909
1345
  {
910
1346
  do: "Prove the token with `Send test` and the issue key left blank",
@@ -917,13 +1353,13 @@ var GUIDES = [
917
1353
  }
918
1354
  ],
919
1355
  check: "The Jira tile says connected with the token's last four, the blank Send test names the account the token belongs to, and after `Sync now` the pool on Sleds lists the mirrored project's open issues with a band on each.",
920
- scope: "With the project key blank and no `sled` labels in use, SLEDS never creates or comments on a Jira issue. The mirror only reads; rows carry the tracker's own state, labels, priority and assignee, never edited here."
1356
+ scope: "With the project key blank and no `sled` labels in use, SLEDS never creates or comments on a Jira issue. The mirror only reads; items carry the tracker's own state, labels, priority and assignee, never edited here."
921
1357
  },
922
1358
  {
923
1359
  id: "slack",
924
1360
  title: "Connect Slack",
925
1361
  audience: "admin",
926
- lede: "One button. SLEDS posts hand-offs, walls and digests to a channel of yours and DMs the person a thing is for — with nothing to create, copy or paste.",
1362
+ lede: "One button. SLEDS posts hand-offs, walls and digests to a channel of yours, DMs the person a thing is for, and answers `@SLEDS` in any channel — with nothing to create, copy or paste.",
927
1363
  steps: [
928
1364
  {
929
1365
  do: "Add to Slack",
@@ -935,6 +1371,11 @@ var GUIDES = [
935
1371
  where: "In Slack, in that channel: `/invite @SLEDS`",
936
1372
  note: "Posting to the channel works from the moment you allowed; reading and replying in a private channel needs the bot as a member."
937
1373
  },
1374
+ {
1375
+ do: "Talk to it in a channel",
1376
+ where: "Any channel the bot is in: `@SLEDS where is QW-142` · `@SLEDS what's open` · `@SLEDS item QW-142` · `@SLEDS create a ticket for this`",
1377
+ note: "It answers in the thread. `create a ticket for this` under someone's message reads that message as the ask, posts the draft in the thread with the questions still open, takes the replies as answers, and files on *yes* — with the person who asked as the filer. A workspace installed before Sept 28 clicks Add to Slack once more so the app has the channel scopes."
1378
+ },
938
1379
  {
939
1380
  do: "Send one test post",
940
1381
  where: "Settings › Integrations › Slack › `Send test`",
@@ -952,7 +1393,7 @@ var GUIDES = [
952
1393
  id: "approval",
953
1394
  title: "Turn on product's yes",
954
1395
  audience: "admin",
955
- lede: "An approval step before engineering can take a row: it waits until someone product-side says yes, or defers it with a note.",
1396
+ lede: "An approval step before engineering can take an item: it waits until someone product-side says yes, or defers it with a note.",
956
1397
  steps: [
957
1398
  {
958
1399
  do: "Choose who needs a yes",
@@ -966,7 +1407,7 @@ var GUIDES = [
966
1407
  },
967
1408
  {
968
1409
  do: "Approvers decide from the board or the Overview",
969
- where: "Overview › `Waiting on product`, or the row on Sleds",
1410
+ where: "Overview › `Waiting on product`, or the item on Work",
970
1411
  note: "`Approve` opens the gate; `Defer` keeps it closed and carries the note back to the filer, who answers in the brief and it comes back. In chat the same is `approve {project, decision, note?}`."
971
1412
  },
972
1413
  {
@@ -974,7 +1415,7 @@ var GUIDES = [
974
1415
  note: "An approved epic can be planned in chat (`plan`): its milestones are sized, filed under it, and inherit the yes — they do not queue again. The epic's cap is the sum."
975
1416
  }
976
1417
  ],
977
- check: "File an ask from chat (`intake`): its row shows waiting for product in the pool on Sleds, the approvers see it under Waiting on product, and `run` on it is refused until someone approves."
1418
+ check: "File an ask from chat (`intake`): its item shows waiting for product in the pool on Work, the approvers see it under Waiting on product, and `run` on it is refused until someone approves."
978
1419
  }
979
1420
  ];
980
1421
  function findGuides(filter) {
@@ -1030,7 +1471,7 @@ var VERBS = [
1030
1471
  want: "See my overnight jobs and the team's open hand-offs",
1031
1472
  mcp: "jobs · handoffs",
1032
1473
  claudeCode: "/sled jobs · `sleds handoffs`",
1033
- app: "Sleds › Moving (the runs on each seat's sled) · Record › Receipts once they land",
1474
+ app: "Work › Moving (the runs on each sled) · Record › Receipts once they land",
1034
1475
  how: "Newest first: id, status, route, cost. Hand-offs meant for you are listed first."
1035
1476
  },
1036
1477
  {
@@ -1038,8 +1479,8 @@ var VERBS = [
1038
1479
  group: "meter",
1039
1480
  want: "Ask where a piece of work is",
1040
1481
  mcp: "status {q?} — a tracker key or a word from the title; nothing for what you filed",
1041
- app: "Sleds (the strip and the row's stage: waiting for product · deferred · ready · moving · landed)",
1042
- how: "One paragraph per row: its stage, who has it, the band, the worth, the tree when it has milestones, the PR when it landed. Yours by default; a lead widens to the team."
1482
+ app: "Work (the strip and the item's stage: waiting for product · deferred · ready · moving · landed)",
1483
+ how: "One paragraph per item: its stage, who has it, the band, the worth, the tree when it has milestones, the PR when it landed. Yours by default; a lead widens to the team."
1043
1484
  },
1044
1485
  {
1045
1486
  id: "touches",
@@ -1052,18 +1493,66 @@ var VERBS = [
1052
1493
  {
1053
1494
  id: "attach",
1054
1495
  group: "meter",
1055
- want: "Put the spec, the prototype or a screenshot on the row so the team can open it",
1056
- mcp: "attach {project, name, content, mime?, kind?} — intake takes the same as `attachments` when filing",
1057
- app: "Sleds › the row › Assets",
1058
- how: "The file itself lives in SLEDS, not only in the chat that made it: a spec as markdown, a prototype as one HTML page (opens and renders in its own tab), a screenshot. Up to 4 MB; the same name again replaces. Anyone on the board opens it from the row; the person who added it, or an admin, can remove it."
1496
+ want: "Put the spec, the prototype, a screenshot or a link on the item so the team can open it",
1497
+ mcp: "attach {project, name, content, mime?, kind?} · attach {project, url, title?} — intake takes the same as `attachments` when filing",
1498
+ app: "Work › the item › Links",
1499
+ how: "The file itself lives in SLEDS, not only in the chat that made it: a spec as markdown, a prototype as one HTML page (opens and renders in its own tab), a screenshot — or a link to a doc, a Figma, a Loom. Up to 4 MB; the same name again replaces. Anyone on the board opens it from the item; the person who added it, or an admin, can remove it."
1500
+ },
1501
+ {
1502
+ id: "update",
1503
+ group: "meter",
1504
+ want: "Change an item — its title, why, done, description, due day, what it sits under, or what it touches",
1505
+ mcp: "update {project, ask?, why?, done?, smallest?, touches?, demand?, description?, due?, parent?, repos?, sensitivity?}",
1506
+ app: "Work › the item › click a field",
1507
+ how: "The filer, an approver or an admin changes an item from chat or by clicking the field in the app; every change says who and through what (via Claude · Cowork, via ChatGPT, in the app) and the record keeps before and after. A change to the ask, done or smallest marks the band stale with a Re-size at hand — never re-sized silently. A mirrored ticket is the tracker's word and is not changed here."
1508
+ },
1509
+ {
1510
+ id: "archive",
1511
+ group: "meter",
1512
+ want: "Put an item away without deleting it",
1513
+ mcp: "archive {project, undo?}",
1514
+ app: "Work › the item › ⋯ › Archive · the archived chip on Work",
1515
+ how: "Archived items leave Work, the sleds and the counts, and keep everything — unarchive brings one back whole. The filer, an approver or an admin. Delete (⋯ › Delete…) is an admin's and only for a hand-filed item that never ran; a mirrored ticket and a landed item are archived, never deleted."
1516
+ },
1517
+ {
1518
+ id: "item",
1519
+ group: "meter",
1520
+ want: "Read an item whole — what an engineer or an agent needs to pick it up cold",
1521
+ mcp: "item {q}",
1522
+ app: "Work › the item · Copy as markdown",
1523
+ how: "One markdown document: the brief, the description, the repo(s), the links and files, what sits under it, the open questions, the thread, and the readiness line — what is still missing before someone could start. It is what an agent reads before building from an item, and what the readiness check reads."
1524
+ },
1525
+ {
1526
+ id: "check",
1527
+ group: "meter",
1528
+ want: "Know whether an item says enough to start — and what is missing",
1529
+ mcp: "check {project}",
1530
+ app: "Work › the item › Readiness › Check",
1531
+ how: "Readiness has two layers. The rules run on every read and cost nothing: no why, no done, no repo, an open question, not sized, waiting for product's yes, or thin. The check is a small model on the workspace's platform key reading the item as an engineer would and naming what it would have to ask before starting; its answer goes on the thread as a comment from SLEDS and rides on the item's readiness from then on. Nothing here judges the idea — only whether the item says enough."
1532
+ },
1533
+ {
1534
+ id: "profile",
1535
+ group: "meter",
1536
+ want: "Know how hard an item is, what it touches, and which model tier fits",
1537
+ mcp: "item {q} (the Profile line) · update {project, sensitivity, sensitivityNote?}",
1538
+ app: "Work › the item › Profile",
1539
+ how: "Spec 18. Rules, never a model: complexity (low · medium · high, with reasons — the kind of work, the steps that want a premium model, files, repos, gaps) maps to a tier (economy · default · premium; economy is Haiku). Sensitivity (public · internal · restricted) is the most restrictive of what a person said, what the repo's `.sled/config.yml` says (`dataClass`, `restrictedPaths`, `publicPaths`), what the product map says, and what the rules detect in the item's own words — personal data, secret shapes, bulk customer data. Anyone can raise it; only an approver can lower it, with a note, on the record. A restricted item's text never goes to a cloud model: no readiness check, no rerank. Routing on it comes with Policy › Routing (P2)."
1540
+ },
1541
+ {
1542
+ id: "comment",
1543
+ group: "meter",
1544
+ want: "Leave a note on an item's thread — for the team, or as your agent's record of what it did",
1545
+ mcp: "comment {project, md}",
1546
+ app: "Work › the item › Thread",
1547
+ how: "The thread is one conversation between the team and its agents: a comment from a person, a note from their Claude, a size from the estimator, a run's receipt, a question and its answer — in order, each signed with who and through what."
1059
1548
  },
1060
1549
  {
1061
1550
  id: "answer",
1062
1551
  group: "meter",
1063
1552
  want: "Answer a question engineering asked about something I filed",
1064
1553
  mcp: "answer {answer, project?, id?} — nothing to see what is waiting on you",
1065
- app: "Sleds › the row › Questions",
1066
- how: "An approver asks on the row; you hear by bell, DM and email. Your reply, here or on the row, goes on the record and the asker hears at once. A worth question answered with a level or a band sets the row's worth."
1554
+ app: "Work › the item › Questions",
1555
+ how: "An approver asks on the item; you hear by bell, DM and email. Your reply, here or on the item, goes on the record and the asker hears at once. A worth question answered with a level or a band sets the item's worth."
1067
1556
  },
1068
1557
  {
1069
1558
  id: "estimate",
@@ -1071,7 +1560,7 @@ var VERBS = [
1071
1560
  want: "Size a task before running it",
1072
1561
  mcp: "estimate {spec, source?}",
1073
1562
  claudeCode: "/sled estimate · `sleds spec-event` fires on save of a spec file",
1074
- app: "Sleds (rows appear in the pool as specs and issues are sized)",
1563
+ app: "Work (items appear in the pool as specs and issues are sized)",
1075
1564
  how: "A cost and time band with confidence and a spend cap — never a point figure. Recorded; the actual calibrates the next one."
1076
1565
  },
1077
1566
  {
@@ -1080,7 +1569,7 @@ var VERBS = [
1080
1569
  want: "Say what the work is worth to the business",
1081
1570
  mcp: "a `Worth:` line in the spec you estimate (`Worth: high · $5k–20k · Acme renewal`)",
1082
1571
  claudeCode: "`SLEDS-Worth:` line in the PR body",
1083
- app: "Sleds › the row › Worth…",
1572
+ app: "Work › the item › Worth…",
1084
1573
  how: "A level, dollars when you know them, a note. Yours to state; the tracker's priority stands in at low confidence; blank stays blank."
1085
1574
  },
1086
1575
  {
@@ -1088,15 +1577,15 @@ var VERBS = [
1088
1577
  group: "send",
1089
1578
  want: "Send an ask to engineering from the conversation I already have open",
1090
1579
  mcp: "intake {text, links?, answers?, confirm?, parent?}",
1091
- app: "Sleds (the row lands in the pool sized, filed by you, at waiting or ready)",
1092
- how: "Your AI reads what you sent and asks only what it cannot answer — who it is for, what done looks like, the smallest version, the worth, what it touches, how many have asked — then shows the brief. On yes the row is filed on the sled and sized. Nothing is written before yes; worth is never guessed. `parent` files it under an epic or the ask it spun off from, so the parent's tree cost is true."
1580
+ app: "Work (the item lands in the pool sized, filed by you, at waiting or ready)",
1581
+ how: "Your AI reads what you sent and asks only what it cannot answer — who it is for, what done looks like, the smallest version, the worth, what it touches, how many have asked — then shows the brief. On yes the item is filed on the sled and sized. Nothing is written before yes; worth is never guessed. `parent` files it under an epic or the ask it spun off from, so the parent's tree cost is true."
1093
1582
  },
1094
1583
  {
1095
1584
  id: "plan",
1096
1585
  group: "send",
1097
1586
  want: "Turn an approved epic into milestones I can send one at a time",
1098
1587
  mcp: "plan {q | project, milestones?, confirm?} — the epic first, then the drafted list",
1099
- app: "Sleds (the epic row expands into its milestones; the tree under its band) · Approve · plan it",
1588
+ app: "Work (the epic expands into its child items; the tree under its band) · Approve · plan it",
1100
1589
  how: "Your AI drafts 3–7 vertical slices from the epic's brief — each something a person could use on its own — and shows them. On yes SLEDS sizes each, files it under the epic with the epic's worth and approval (a Jira sub-task when the epic lives there), and says what the tree will cost. Nothing is filed before yes."
1101
1590
  },
1102
1591
  {
@@ -1104,15 +1593,15 @@ var VERBS = [
1104
1593
  group: "send",
1105
1594
  want: "Give product's yes on an ask — or say not now, and why",
1106
1595
  mcp: "approve {q, decision: approve | defer, note?}",
1107
- app: "Sleds › Approve / Defer on a waiting row · Overview › Waiting on product · Policy › Product's yes",
1108
- how: "Off by default. When Policy says asks from intake (or every row) wait for product, a row sits at waiting until an approver — the named approvers, else an owner or admin — approves it; nobody can run it before. Defer keeps it closed and sends the note to the filer; they answer in the brief and it comes back."
1596
+ app: "Work › Approve / Defer on a waiting item · Overview › Waiting on product · Policy › Product's yes",
1597
+ how: "Off by default. When Policy says asks from intake (or every item) wait for product, an item sits at waiting until an approver — the named approvers, else an owner or admin — approves it; nobody can run it before. Defer keeps it closed and sends the note to the filer; they answer in the brief and it comes back."
1109
1598
  },
1110
1599
  {
1111
1600
  id: "dispatch",
1112
1601
  group: "send",
1113
1602
  want: "Ask where a piece of work should go, then send it there",
1114
1603
  mcp: "dispatch {q, run?: now | park | overnight, to?} — suggests with no run",
1115
- app: "Sleds › Take it (onto your own sled) · Sleds › the row › Send to… · Put it on <seat>'s sled",
1604
+ app: "Work › Take it (onto your own sled) · Work › the item › Send to… · Put it on <name>'s sled",
1116
1605
  how: "Now on the seat with the most weekly headroom, overnight at half price, parked for the team, or not worth it yet — with the reasons. Nothing runs until you say run."
1117
1606
  },
1118
1607
  {
@@ -1121,7 +1610,7 @@ var VERBS = [
1121
1610
  want: "Send work to a teammate or a buddy",
1122
1611
  mcp: "handoff {task, to?, note?}",
1123
1612
  claudeCode: "/sled handoff · the wall popup",
1124
- app: "Sleds › the row › Send to… · extension › Park hand-off",
1613
+ app: "Work › the item › Send to… · extension › Park hand-off",
1125
1614
  how: "Parks a self-contained brief (the picker starts cold). Named by email — a teammate, or a paired buddy in another workspace — or anyone on the team."
1126
1615
  },
1127
1616
  {
@@ -1130,7 +1619,7 @@ var VERBS = [
1130
1619
  want: "Take back a hand-off nobody has picked up",
1131
1620
  mcp: "withdraw {handoffId}",
1132
1621
  claudeCode: "`sleds withdraw <id>`",
1133
- app: "Sleds › the row › Withdraw",
1622
+ app: "Work › the item › Withdraw",
1134
1623
  how: "Only the seat that parked it can withdraw it. Unpicked hand-offs are reminded after a day and expire after three."
1135
1624
  },
1136
1625
  {
@@ -1139,7 +1628,7 @@ var VERBS = [
1139
1628
  want: "Pick up work someone sent me",
1140
1629
  mcp: "handoffs → pickup {handoffId}",
1141
1630
  claudeCode: "/sled pickup <id> · `sleds pickup <id>`",
1142
- app: "Sleds › Take it · extension › Take it",
1631
+ app: "Work › Take it · extension › Take it",
1143
1632
  how: "A parked task comes back verbatim; a walled session comes back as its first and last messages. Runs on your seat, metered to your workspace."
1144
1633
  },
1145
1634
  {
@@ -1157,7 +1646,7 @@ var VERBS = [
1157
1646
  want: "Run this tonight at half price",
1158
1647
  mcp: "overnight {spec, source?}",
1159
1648
  claudeCode: "/sled overnight",
1160
- app: "wall popup › Run overnight · Sleds › the Night shift seat",
1649
+ app: "wall popup › Run overnight · Sleds › the Night shift sled",
1161
1650
  how: "Sized first; the cap bounds the spend. Runs in the workspace's overnight window on the batch tier; `/sled resume <id>` pulls it back in."
1162
1651
  },
1163
1652
  {
@@ -1165,7 +1654,7 @@ var VERBS = [
1165
1654
  group: "defer",
1166
1655
  want: "Pull a finished overnight job into my session",
1167
1656
  claudeCode: "/sled resume <id>",
1168
- app: "Sleds › the Night shift seat › the row",
1657
+ app: "Sleds › the Night shift sled › the item",
1169
1658
  how: "The delivered result, with its record of which model built it — which model built it, signed by the workspace key."
1170
1659
  },
1171
1660
  {
@@ -1174,15 +1663,15 @@ var VERBS = [
1174
1663
  want: "Record what a piece of work actually took",
1175
1664
  mcp: "actual {source, minutes?, usd?, model?, ref?}",
1176
1665
  claudeCode: "`SLEDS-Actual: 45 min · $7` in the PR body — the ledger posts it on merge",
1177
- app: "Record › Receipts (fills in on merge or delivery) · Sleds › Landed this week",
1178
- how: "Closes the estimate: est → actual per row, and the calibration for the next band. A delivered job closes its own."
1666
+ app: "Record › Receipts (fills in on merge or delivery) · Work › Landed this week",
1667
+ how: "Closes the estimate: est → actual per item, and the calibration for the next band. A delivered job closes its own."
1179
1668
  },
1180
1669
  {
1181
1670
  id: "verify",
1182
1671
  group: "close",
1183
1672
  want: "Check which model built something",
1184
1673
  claudeCode: "`sleds verify <jobId | SLEDS-Attest line>`",
1185
- app: "Record › Receipts › the row's ref",
1674
+ app: "Record › Receipts › the item's ref",
1186
1675
  how: "Ed25519 over the job, model, outputs and time; verified locally against the workspace's public key."
1187
1676
  },
1188
1677
  {
@@ -1221,7 +1710,7 @@ var VERBS = [
1221
1710
  id: "connector",
1222
1711
  group: "trust",
1223
1712
  want: "Use SLEDS from chat, Cowork or Claude Design",
1224
- mcp: "add https://agent.sleds.ai/mcp as a connector (OAuth) — then every verb in this column",
1713
+ mcp: "add https://mcp.sleds.ai/mcp as a connector (OAuth) — then every verb in this column",
1225
1714
  how: "The conversation is your cockpit: the same tools, no terminal. Your seat is the account you signed in with."
1226
1715
  },
1227
1716
  {
@@ -1234,6 +1723,10 @@ var VERBS = [
1234
1723
  }
1235
1724
  ];
1236
1725
  var FAQ = [
1726
+ {
1727
+ q: "Item, sled, seat — what are the words?",
1728
+ a: "An item is a unit of work — a feature, a fix, an ask — with a band and a worth; children are child items. A sled is who the work is on: a person, or an agent on its own. A seat is what a sled pulls with: an agent on a platform with a credential (Claude on a Team plan, Codex on an API key), each with its own wall. Work is the page of items, Sleds the page of sleds, Seats the capacity under them."
1729
+ },
1237
1730
  {
1238
1731
  q: "What does SLEDS see, and what does it keep?",
1239
1732
  a: "Requests stream through the proxy to your own Claude or OpenAI seat and back; we keep the numbers — tokens, model, cost, when — and the decisions, never your prompts or code. Two features carry content because they cannot work without it, and both are yours to turn off: a hand-off parks the conversation for a teammate (encrypted, expires in 7 days); an estimate reads a spec to size it. Your tracker and Slack receive what you already see in them."
@@ -1447,17 +1940,17 @@ function renderBrief(b) {
1447
1940
  function renderBriefMarkdown(b, open, extra = {}) {
1448
1941
  const cell = (t) => t.replace(/\|/g, "\\|").replace(/\s+/g, " ").trim();
1449
1942
  const rows = ["| | | |", "|---|---|---|"];
1450
- const field = (label, f) => {
1943
+ const field2 = (label, f) => {
1451
1944
  if (f)
1452
1945
  rows.push(`| **${label}** | ${cell(f.text)} | ${fromWord(f)} |`);
1453
1946
  };
1454
1947
  rows.push(`| **Ask** | ${cell(b.ask.text)} | ${fromWord(b.ask)} |`);
1455
- field("Why", b.why);
1456
- field("Done", b.done);
1457
- field("Smallest", b.smallest);
1948
+ field2("Why", b.why);
1949
+ field2("Done", b.done);
1950
+ field2("Smallest", b.smallest);
1458
1951
  rows.push(`| **Worth** | ${b.worth ? cell(worthLabel(b.worth)) : "_not stated_"} | ${b.worth ? "stated" : ""} |`);
1459
- field("Touches", b.touches);
1460
- field("Demand", b.demand);
1952
+ field2("Touches", b.touches);
1953
+ field2("Demand", b.demand);
1461
1954
  if (b.due)
1462
1955
  rows.push(`| **Due** | ${b.due} | from your words |`);
1463
1956
  const out = ["**Brief — draft**", "", ...rows];
@@ -1561,6 +2054,8 @@ var STOP = new Set([
1561
2054
  "up",
1562
2055
  "via"
1563
2056
  ]);
2057
+ // ../../packages/shared/src/profile/profile.ts
2058
+ var LIGHT = new Set(["summarize", "research", "walkthrough", "docs"]);
1564
2059
  // ../../packages/shared/src/dispatch/plan.ts
1565
2060
  init_format();
1566
2061
 
@@ -1666,29 +2161,50 @@ init_format();
1666
2161
  var isObject2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
1667
2162
  var str2 = (v) => typeof v === "string" ? v : undefined;
1668
2163
  var num2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined;
1669
- function statusName(row) {
1670
- const source = isObject2(row.source) ? row.source : {};
2164
+ function statusName(row2) {
2165
+ const source = isObject2(row2.source) ? row2.source : {};
1671
2166
  const kind = str2(source.kind);
1672
2167
  const doc = str2(source.doc);
1673
- const title = str2(row.title) ?? str2(row.project) ?? "this row";
2168
+ const title = str2(row2.title) ?? str2(row2.project) ?? "this row";
1674
2169
  if ((kind === "linear" || kind === "github" || kind === "jira") && doc)
1675
2170
  return `${doc} · ${title}`;
1676
- const key = str2(row.key);
2171
+ const key = str2(row2.key);
1677
2172
  return key ? `${key} · ${title}` : title;
1678
2173
  }
1679
- function stateTail(row) {
1680
- const retired = str2(row.retired);
2174
+ function stateTail(row2) {
2175
+ const retired = str2(row2.retired);
1681
2176
  if (retired)
1682
2177
  return ` Retired: ${retired}.`;
1683
- if (row.stale === true)
2178
+ if (row2.stale === true)
1684
2179
  return " Stale: no movement in 14 days.";
1685
2180
  return "";
1686
2181
  }
1687
- function statusLine(row, now) {
1688
- return `${statusBody(row, now)}${trackerTail(row)}${stateTail(row)}${assetsTail(row)}`;
2182
+ function statusLine(row2, now) {
2183
+ return `${statusBody(row2, now)}${trackerTail(row2)}${stateTail(row2)}${readinessTail(row2)}${profileTail(row2)}${assetsTail(row2)}`;
2184
+ }
2185
+ function profileTail(row2) {
2186
+ const p2 = isObject2(row2.profile) ? row2.profile : null;
2187
+ if (!p2)
2188
+ return "";
2189
+ const line = str2(p2.line);
2190
+ if (!line)
2191
+ return "";
2192
+ const why = Array.isArray(p2.reasons) ? p2.reasons.filter((x) => typeof x === "string") : [];
2193
+ const restricted = str2(p2.sensitivity) === "restricted";
2194
+ return ` Profile: ${line}${why.length && (restricted || str2(p2.source) !== "default") ? ` (${why.slice(0, 2).join("; ")})` : ""}.${restricted ? " Restricted: cloud routes close for it once Policy › Routing says so; nothing is held yet." : ""}`;
2195
+ }
2196
+ function readinessTail(row2) {
2197
+ const r = isObject2(row2.readiness) ? row2.readiness : null;
2198
+ if (!r || str2(r.state) !== "gaps")
2199
+ return "";
2200
+ const line = str2(r.line);
2201
+ if (!line)
2202
+ return "";
2203
+ const repos = Array.isArray(row2.repos) ? row2.repos.filter((x) => typeof x === "string") : [];
2204
+ return ` Readiness: ${line}.${repos.length ? ` Repo: ${repos.join(", ")}.` : ""} \`item\` reads the whole thing; \`check\` asks the model.`;
1689
2205
  }
1690
- function assetsTail(row) {
1691
- const assets = Array.isArray(row.assets) ? row.assets.filter(isObject2) : [];
2206
+ function assetsTail(row2) {
2207
+ const assets = Array.isArray(row2.assets) ? row2.assets.filter(isObject2) : [];
1692
2208
  if (assets.length === 0)
1693
2209
  return "";
1694
2210
  const parts = assets.map((a) => {
@@ -1699,8 +2215,8 @@ function assetsTail(row) {
1699
2215
  });
1700
2216
  return ` On the row: ${parts.join(" · ")}.`;
1701
2217
  }
1702
- function trackerTail(row) {
1703
- const source = isObject2(row.source) ? row.source : {};
2218
+ function trackerTail(row2) {
2219
+ const source = isObject2(row2.source) ? row2.source : {};
1704
2220
  const kind = str2(source.kind);
1705
2221
  if (kind !== "github" && kind !== "jira" && kind !== "linear")
1706
2222
  return "";
@@ -1742,36 +2258,36 @@ function trackerTail(row) {
1742
2258
  const where = kind === "github" ? "GitHub" : kind === "jira" ? "Jira" : "Linear";
1743
2259
  return ` In ${where}: ${bits.join(" · ")}.`;
1744
2260
  }
1745
- function statusBody(row, now) {
1746
- const name = statusName(row);
1747
- const stage = str2(row.stage) ?? "ready";
1748
- const handoff = isObject2(row.handoff) ? row.handoff : null;
1749
- const work = isObject2(row.work) ? row.work : null;
1750
- const worth = isObject2(row.worth) ? row.worth : null;
1751
- const band = bandLine(row.band);
2261
+ function statusBody(row2, now) {
2262
+ const name = statusName(row2);
2263
+ const stage = str2(row2.stage) ?? "ready";
2264
+ const handoff = isObject2(row2.handoff) ? row2.handoff : null;
2265
+ const work = isObject2(row2.work) ? row2.work : null;
2266
+ const worth = isObject2(row2.worth) ? row2.worth : null;
2267
+ const band = bandLine(row2.band);
1752
2268
  const sized = band ? `Sized ${band}` : "Not sized";
1753
2269
  const worthText = worth ? `worth ${worthLabel(worth)}` : "worth not stated";
1754
2270
  const ago = (ts) => num2(ts) !== undefined ? `${shortDuration(Math.max(0, now - num2(ts)))} ago` : "";
1755
2271
  if (stage === "landed") {
1756
- const mins = num2(row.actualMinutes);
1757
- const dollars = num2(row.actualUsd);
2272
+ const mins = num2(row2.actualMinutes);
2273
+ const dollars2 = num2(row2.actualUsd);
1758
2274
  const credits = work ? num2(work.seatCreditsUsd) : undefined;
1759
2275
  const bits = [
1760
2276
  mins !== undefined ? `${Math.round(mins)} min` : null,
1761
- dollars !== undefined && dollars > 0 ? usd2(dollars) : null,
2277
+ dollars2 !== undefined && dollars2 > 0 ? usd2(dollars2) : null,
1762
2278
  credits !== undefined ? `${usd2(credits)} credits` : null,
1763
2279
  work ? str2(work.model) : null
1764
2280
  ].filter((b) => Boolean(b));
1765
2281
  const ref = work ? str2(work.ref) : undefined;
1766
2282
  return `${name} — landed${bits.length ? `: ${bits.join(" · ")}` : ""}${ref ? ` · ${ref}` : ""}. ${sized}, ${worthText}.`;
1767
2283
  }
1768
- const tree = isObject2(row.tree) ? row.tree : null;
2284
+ const tree = isObject2(row2.tree) ? row2.tree : null;
1769
2285
  const treeText = treeLine(tree);
1770
2286
  if (stage === "on_sled" && handoff) {
1771
2287
  const who = str2(handoff.pickedBy) ?? "someone";
1772
2288
  return `${name} — on the sled: ${who} has it (picked ${ago(handoff.at)}). ${sized}, ${worthText}.${treeText} You will hear when it lands.`;
1773
2289
  }
1774
- const approval = isObject2(row.approval) ? row.approval : null;
2290
+ const approval = isObject2(row2.approval) ? row2.approval : null;
1775
2291
  if (stage === "deferred") {
1776
2292
  const by = approval ? str2(approval.by) ?? "product" : "product";
1777
2293
  const note = approval ? str2(approval.note) : undefined;
@@ -1787,7 +2303,7 @@ function statusBody(row, now) {
1787
2303
  const to = str2(handoff.to);
1788
2304
  return `${name} — ready, parked ${to ? `for ${to}` : "for anyone on the team"} ${ago(handoff.at)}; nobody has taken it yet. ${sized}, ${worthText}.${okText}${treeText}`;
1789
2305
  }
1790
- return `${name} — ready; nobody has it yet. ${sized}, ${worthText}.${okText}${treeText} An engineer takes it from the sled (Projects › Take it, or /sled pickup after a Send to…).`;
2306
+ return `${name} — ready; nobody has it yet. ${sized}, ${worthText}.${okText}${treeText} An engineer takes it from the pool (Work › Take it, or /sled pickup after a Send to…).`;
1791
2307
  }
1792
2308
  function treeLine(tree) {
1793
2309
  if (!tree)
@@ -2014,11 +2530,11 @@ a{color:var(--peri)}
2014
2530
  </script></body></html>`;
2015
2531
 
2016
2532
  // ../../packages/shared/src/mcp/server.ts
2017
- function fetchClient(o) {
2018
- const fetchFn = o.fetchFn ?? fetch;
2019
- const base = o.proxyUrl.replace(/\/+$/, "");
2020
- const headers = { "content-type": "application/json", authorization: `Bearer ${o.token}` };
2021
- const timeout = () => AbortSignal.timeout(o.timeoutMs ?? MCP_TIMEOUT_MS);
2533
+ function fetchClient(o2) {
2534
+ const fetchFn = o2.fetchFn ?? fetch;
2535
+ const base = o2.proxyUrl.replace(/\/+$/, "");
2536
+ const headers = { "content-type": "application/json", authorization: `Bearer ${o2.token}` };
2537
+ const timeout = () => AbortSignal.timeout(o2.timeoutMs ?? MCP_TIMEOUT_MS);
2022
2538
  return {
2023
2539
  async post(path, body) {
2024
2540
  try {
@@ -2076,22 +2592,22 @@ var SERVER_INFO = {
2076
2592
  description: "The cost and limits of AI agent work — estimate, hand off, and track what a task takes.",
2077
2593
  websiteUrl: "https://sleds.ai",
2078
2594
  icons: [
2079
- { src: "https://agent.sleds.ai/icon.png", mimeType: "image/png", sizes: ["192x192"] },
2080
- { src: "https://agent.sleds.ai/icon.svg", mimeType: "image/svg+xml", sizes: ["any"] }
2595
+ { src: "https://mcp.sleds.ai/icon.png", mimeType: "image/png", sizes: ["192x192"] },
2596
+ { src: "https://mcp.sleds.ai/icon.svg", mimeType: "image/svg+xml", sizes: ["any"] }
2081
2597
  ]
2082
2598
  };
2083
2599
  var SERVER_INSTRUCTIONS = [
2084
- "SLEDS is the ledger of engineering work: every piece is sized before it runs (a cost and time band, never a point figure) and closed with a measured actual after. You are the head; SLEDS holds the record. The person may be a PM or designer describing something they want built, or an engineer about to build — read which.",
2085
- 'When someone describes work for engineering (a feature, a fix, a spec, a PRD, "can we…"): offer once to put it on the sled. If they say yes, use `intake` — it drafts the brief, asks only what it cannot answer (who it is for and why now, what done looks like, the smallest version, the worth, how many have asked) two at a time, writes nothing until they confirm. Never guess worth. It also reads the product map — what the ask touches, which product docs it names, what on the board collides with it; read it back plainly, let them correct it (`touches` answers the same for any question). You judge; the map only says what is there. About a page that exists? Ask for its URL (in `links`) and a screenshot to mock the change on. Anything with a screen: offer once a clickable prototype in the workspace\'s design system when intake names one — it is the ask, not the spec. Spec, prototype, screenshot go on the row as files (`attachments` on intake, `attach` after) so anyone on the board can open them; a link only they can. Change a filed ask here, not from Slack. Say it is on the sled, the band, whether it waits for product\'s yes. They never need to learn a verb.',
2086
- "When you are about to build something that will take more than a few minutes: `status` first if a row may exist for it; otherwise `estimate` with a stable `source` (kind · project · doc · item) before the first line of work, and say the band. When it lands: `actual` with the same `source`, the state, where the output lives (`ref`), the model, and your session link (`sessionId`). Do not pass `minutes` unless a person stated them — SLEDS measures the time itself. A follow-up to landed work is a new `item` under the same doc, never an overwrite. An `actual` with no estimate still records (landed unsized), but the band you skipped is the point, so estimate first.",
2087
- "If SLEDS says there is no estimate for the source, size it first and send the actual again. If it says unreachable, try once more; then move on and tell the person the row is still owed. Never invent a number to get past an error."
2600
+ "SLEDS is where the business meets engineering: every piece of work is sized before it runs (a cost and time band, never a point figure) and closed with a measured actual after. You are the head; SLEDS holds the record. The person may be a PM or designer describing what they want built, or an engineer about to build — read which.",
2601
+ 'When someone describes work for engineering (a feature, a fix, a spec, a PRD, "can we…"): offer once to put it on the sled. If they say yes, use `intake` — it drafts the brief, asks only what it cannot answer (who it is for and why now, what done looks like, the smallest version, the worth, how many asked) two at a time, writes nothing until they confirm. Never guess worth. It also reads the product map — what the ask touches, which product docs it names, what on the board collides with it; read it back plainly, let them correct it (`touches` answers the same for any question). You judge; the map only says what is there. About a page that exists? Ask for its URL (in `links`) and a screenshot to mock the change on. Anything with a screen: offer once a clickable prototype in the workspace\'s design system when intake names one — it is the ask, not the spec. Spec, prototype, screenshot go on the item as files (`attachments` on intake, `attach` after) so anyone on the board can open them; a link only they can. Change a filed ask here, not from Slack. Say it is on the sled, the band, whether it waits for product\'s yes. They never need to learn a verb.',
2602
+ "When you are about to build something that will take more than a few minutes: `status` first if an item may exist for it; `item` reads it whole; otherwise `estimate` with a stable `source` (kind · project · doc · item) before the first line of work, and say the band. When it lands: `actual` with the same `source`, the state, where the output lives (`ref`), the model, and your session link (`sessionId`). Do not pass `minutes` unless a person stated them — SLEDS measures time. A follow-up to landed work is a new `item` under the same doc, never an overwrite. An `actual` with no estimate still records, but the band you skipped is the point, so estimate first.",
2603
+ "If SLEDS says there is no estimate for the source, size it first and send the actual again. If it says unreachable, try once more; then move on and tell the person the item is still owed. Never invent a number to get past an error."
2088
2604
  ].join(`
2089
2605
 
2090
2606
  `);
2091
2607
  var MCP_TIMEOUT_MS = 5000;
2092
2608
  var sourceSchema = {
2093
2609
  type: "object",
2094
- description: "Where this item lives, so the Projects page groups it: a Claude Project doc, a repo spec, a Linear/GitHub issue. Same source + same text on a later call returns the same estimate (idempotent sync).",
2610
+ description: "Where this item lives, so the Work page groups it: a Claude Project doc, a repo spec, a Linear/GitHub issue. Same source + same text on a later call returns the same estimate (idempotent sync).",
2095
2611
  properties: {
2096
2612
  kind: {
2097
2613
  type: "string",
@@ -2127,13 +2643,13 @@ var TOOLS = [
2127
2643
  },
2128
2644
  {
2129
2645
  name: "estimate",
2130
- description: "Call this BEFORE starting any build that will take more than a few minutes — before the first line of work, not after. Sizes the task: the org estimator's cost and time band with confidence and a spend cap, never a point figure. Pass a stable `source` (kind · project · doc · item) so the row has a home on the sled and the later `actual` finds it. Recorded, so actuals calibrate the next estimate.",
2646
+ description: "Call this BEFORE starting any build that will take more than a few minutes — before the first line of work, not after. Sizes the task: the org estimator's cost and time band with confidence and a spend cap, never a point figure. Pass a stable `source` (kind · project · doc · item) so the item has a home on the sled and the later `actual` finds it. Recorded, so actuals calibrate the next estimate.",
2131
2647
  inputSchema: specSchema
2132
2648
  },
2133
2649
  {
2134
2650
  name: "intake",
2135
2651
  _meta: { ui: { resourceUri: INTAKE_VIEW_URI } },
2136
- description: 'Use this when a person describes work they want engineering to do — a feature, a fix, a spec, a PRD, "can we…" — and has said yes to putting it on the sled. Sends the ask to engineering from this conversation (spec 13). Pass what the person sent — a paragraph, a spec, a PRD — plus any links (the URL of the page it is about, a prototype you made). If it concerns a page or feature that already exists, ask for its URL and a screenshot first — the screenshot is what you mock the change on. Without `confirm` nothing is written: you get the brief drafted from it and the questions still open (who it is for and why now, what done looks like, the smallest version, the worth, what it touches and where it would live, how many have asked) — ask at most two at a time, in your own words, and skip any the person already answered. Put their answers in `answers`, show them the brief, and when they say yes call again with `confirm: true`: the row is filed on the sled, sized, with their name on it. A brief without a why and a done is a draft and will not file. Worth is never guessed.',
2652
+ description: 'Use this when a person describes work they want engineering to do — a feature, a fix, a spec, a PRD, "can we…" — and has said yes to putting it on the sled. Sends the ask to engineering from this conversation (spec 13). Pass what the person sent — a paragraph, a spec, a PRD — plus any links (the URL of the page it is about, a prototype you made). If it concerns a page or feature that already exists, ask for its URL and a screenshot first — the screenshot is what you mock the change on. Without `confirm` nothing is written: you get the brief drafted from it and the questions still open (who it is for and why now, what done looks like, the smallest version, the worth, what it touches and where it would live, how many have asked) — ask at most two at a time, in your own words, and skip any the person already answered. Put their answers in `answers`, show them the brief, and when they say yes call again with `confirm: true`: the item is filed on the sled, sized, with their name on it. A brief without a why and a done is a draft and will not file. Worth is never guessed.',
2137
2653
  inputSchema: {
2138
2654
  type: "object",
2139
2655
  properties: {
@@ -2193,7 +2709,7 @@ var TOOLS = [
2193
2709
  added: {
2194
2710
  type: "array",
2195
2711
  items: { type: "string" },
2196
- description: "What you added on the way, one line each — milestones you drafted ('M1 — the posting form only'), a decision you flagged for product, the nearest landed work you found. Shown on the row as the AI's contribution; an approver reads it before saying yes."
2712
+ description: "What you added on the way, one line each — milestones you drafted ('M1 — the posting form only'), a decision you flagged for product, the nearest landed work you found. Shown on the item as the AI's contribution; an approver reads it before saying yes."
2197
2713
  },
2198
2714
  due: {
2199
2715
  type: "string",
@@ -2206,6 +2722,15 @@ var TOOLS = [
2206
2722
  parent: {
2207
2723
  type: "string",
2208
2724
  description: "The exact project key (from status) of the row this ask belongs under — an epic's milestone, or a spin-off found while building. The parent's tree cost then includes it."
2725
+ },
2726
+ repos: {
2727
+ type: "array",
2728
+ items: { type: "string" },
2729
+ description: "The repo(s) the work lands in, owner/name, when the person named one. Otherwise the product map's hit is used."
2730
+ },
2731
+ workspace: {
2732
+ type: "string",
2733
+ description: "Spec 19: the workspace (a board inside the org — by name or slug) the item is for. The draft lists the org's workspaces when it has more than one and asks; the person may skip it, and the item is placed later on the Work page."
2209
2734
  }
2210
2735
  },
2211
2736
  required: ["text"]
@@ -2243,7 +2768,7 @@ var TOOLS = [
2243
2768
  },
2244
2769
  {
2245
2770
  name: "approve",
2246
- description: "Product's yes on an ask (or 'not now' with a note). Only when the workspace gates asks (Policy › product's yes: intake or always) do rows wait; then an engineer cannot run one until product approves it. decision 'approve' opens the gate; 'defer' keeps it closed and sends the note back to the filer through status. The seat must be an approver: a named approver in Policy, else an owner or admin. Pass q (a tracker key or a word from the title) or the exact project key from status.",
2771
+ description: "Product's yes on an ask (or 'not now' with a note). Only when the workspace gates asks (Policy › product's yes: intake or always) do items wait; then an engineer cannot run one until product approves it. decision 'approve' opens the gate; 'defer' keeps it closed and sends the note back to the filer through status. The seat must be an approver: a named approver in Policy, else an owner or admin. Pass q (a tracker key or a word from the title) or the exact project key from status.",
2247
2772
  inputSchema: {
2248
2773
  type: "object",
2249
2774
  properties: {
@@ -2293,7 +2818,7 @@ var TOOLS = [
2293
2818
  },
2294
2819
  {
2295
2820
  name: "status",
2296
- description: "Where is it — call this first when you may be picking up work that already has a row (a ticket key, a spec section, a hand-off), so you estimate nothing twice. One paragraph per piece of work: its stage on the sled (waiting for product · deferred · ready · on the sled · landed), who has it, the size band, the worth, the tree when it has milestones, and the PR when it landed. Pass q (a tracker key like ENG-482, or a word from the title); with nothing, the rows you filed through intake, newest first.",
2821
+ description: "Where is it — call this first when you may be picking up work that already has an item (a ticket key, a spec section, a hand-off), so you estimate nothing twice. One paragraph per piece of work: its stage on the sled (waiting for product · deferred · ready · on the sled · landed), who has it, the size band, the worth, the tree when it has milestones, and the PR when it landed. Pass q (a tracker key like ENG-482, or a word from the title); with nothing, the rows you filed through intake, newest first.",
2297
2822
  inputSchema: {
2298
2823
  type: "object",
2299
2824
  properties: {
@@ -2306,7 +2831,7 @@ var TOOLS = [
2306
2831
  },
2307
2832
  {
2308
2833
  name: "touches",
2309
- description: "What the product says an ask reaches: the areas of the product (from the workspace's repos), the product's own documents it names, and the rows on the board it collides with. Call with `text` — the ask, or a question like 'if we change the 4-hour minimum, what else is affected?'. Read it back in plain words; you judge, the map only says what is there. `intake` calls this on its own while drafting.",
2834
+ description: "What the product says an ask reaches: the areas of the product (from the workspace's repos), the product's own documents it names, and the items on the board it collides with. Call with `text` — the ask, or a question like 'if we change the 4-hour minimum, what else is affected?'. Read it back in plain words; you judge, the map only says what is there. `intake` calls this on its own while drafting.",
2310
2835
  inputSchema: {
2311
2836
  type: "object",
2312
2837
  properties: {
@@ -2317,14 +2842,16 @@ var TOOLS = [
2317
2842
  },
2318
2843
  {
2319
2844
  name: "attach",
2320
- description: "Put a file on a row already on the sled so anyone on the board can open it — a spec or PRD as markdown, a prototype you made as one HTML page (it opens and renders in its own tab), a screenshot. Pass the row's project key (from status, or what intake said) and the file. Up to 4 MB; the same name again replaces the earlier one. `intake` takes the same files as `attachments` when filing.",
2845
+ description: "Put a file or a link on an item so anyone on the board can open it — a spec or PRD as markdown, a prototype you made as one HTML page (it opens and renders in its own tab), a screenshot; or a URL (a doc, a Figma, a Loom) with `url` instead of a file. Pass the item's key (QW-142, from status, or what intake said). Files up to 4 MB; the same name again replaces the earlier one. `intake` takes the same files as `attachments` when filing.",
2321
2846
  inputSchema: {
2322
2847
  type: "object",
2323
2848
  properties: {
2324
- project: {
2849
+ project: { type: "string", description: "The item's key — QW-142 — or its project key." },
2850
+ url: {
2325
2851
  type: "string",
2326
- description: "The row's project key, e.g. manual:intake/help-and-support or jira:WORK/WORK-7551."
2852
+ description: "A link instead of a file: the URL. `title` names it."
2327
2853
  },
2854
+ title: { type: "string", description: "With url: what to call the link." },
2328
2855
  name: {
2329
2856
  type: "string",
2330
2857
  description: "File name with extension: spec.md, prototype.html, before.png."
@@ -2341,12 +2868,83 @@ var TOOLS = [
2341
2868
  },
2342
2869
  encoding: { type: "string", enum: ["text", "base64"] }
2343
2870
  },
2344
- required: ["project", "name", "content"]
2871
+ required: ["project"]
2872
+ }
2873
+ },
2874
+ {
2875
+ name: "update",
2876
+ description: "Change an item on the sled — its title (`ask`), why, done, smallest, touches, demand, its description (markdown: the prose the brief does not hold), its due day, which item it sits under, the repo(s) it lands in, or its sensitivity (public · internal · restricted). Pass the item's key and only the fields that change. The filer, an approver or an admin may; a mirrored ticket cannot be changed here. A change to the ask, done or smallest marks the band stale — say so, and offer to re-size. Everything you write is signed as coming through you.",
2877
+ inputSchema: {
2878
+ type: "object",
2879
+ properties: {
2880
+ project: { type: "string", description: "The item's key — QW-142 — or its project key." },
2881
+ ask: { type: "string", description: "The title: what they want, one line." },
2882
+ why: { type: "string" },
2883
+ done: { type: "string" },
2884
+ smallest: { type: "string" },
2885
+ touches: { type: "string" },
2886
+ demand: { type: "string" },
2887
+ description: {
2888
+ type: "string",
2889
+ description: "Markdown. Links and images inline. Empty clears it."
2890
+ },
2891
+ due: { type: ["string", "null"], description: "YYYY-MM-DD, or null to clear." },
2892
+ parent: {
2893
+ type: ["string", "null"],
2894
+ description: "The parent item's key, or null for none."
2895
+ },
2896
+ repos: {
2897
+ type: "array",
2898
+ items: { type: "string" },
2899
+ description: "The repo(s) the work lands in, owner/name. Replaces the list; [] clears it."
2900
+ },
2901
+ sensitivity: {
2902
+ type: ["string", "null"],
2903
+ enum: ["public", "internal", "restricted", null],
2904
+ description: "The person's word on what the work touches: public · internal · restricted. Anyone can raise it; only an approver can lower it below what the rules found, and then `sensitivityNote` says why. Null clears the word and the rules stand."
2905
+ },
2906
+ sensitivityNote: { type: "string", description: "Why, when lowering the class." }
2907
+ },
2908
+ required: ["project"]
2909
+ }
2910
+ },
2911
+ {
2912
+ name: "item",
2913
+ description: "Read one item whole, as one markdown document — everything an engineer or an agent needs to pick the work up cold: the brief, the description, the repo(s), the links and files, what sits under it, the open questions, the thread, and its readiness (what is still missing before someone could start). Pass the item's key (QW-142) or its title. Call this before building from an item, and before `check`.",
2914
+ inputSchema: {
2915
+ type: "object",
2916
+ properties: {
2917
+ q: { type: "string", description: "The item's key — QW-142 — or its title." }
2918
+ },
2919
+ required: ["q"]
2920
+ }
2921
+ },
2922
+ {
2923
+ name: "check",
2924
+ description: "Ask SLEDS to check an item's readiness with a model: it reads the item as an engineer would and names what it would have to ask before starting. The answer lands on the item's thread within a minute (and on `item` / `status` after). The rules-based readiness (no why, no done, no repo, open questions, unsized) is on every read for free; this is the second opinion, on the workspace's platform key. Pass the item's key.",
2925
+ inputSchema: {
2926
+ type: "object",
2927
+ properties: {
2928
+ project: { type: "string", description: "The item's key — QW-142 — or its project key." }
2929
+ },
2930
+ required: ["project"]
2931
+ }
2932
+ },
2933
+ {
2934
+ name: "comment",
2935
+ description: "Say something on an item's thread — a note, a finding, what you did, a question for the team. Markdown. It shows on the item for everyone with who wrote it and that it came through you. For a question that needs the filer's answer use `answer`'s counterpart on the row (an approver's `ask`); this is the plain thread.",
2936
+ inputSchema: {
2937
+ type: "object",
2938
+ properties: {
2939
+ project: { type: "string", description: "The item's key — QW-142 — or its project key." },
2940
+ md: { type: "string", description: "The comment, markdown." }
2941
+ },
2942
+ required: ["project", "md"]
2345
2943
  }
2346
2944
  },
2347
2945
  {
2348
2946
  name: "answer",
2349
- description: "Answer a question engineering or product asked on a row you filed. Call with nothing to see what is waiting on you (each with its id and the row); call with `answer` (and `project` or `id` when more than one is open) to put your reply on the row — it becomes part of the record and the person who asked hears at once. When someone says 'tell them…' or 'the answer is…' about a question SLEDS relayed, this is the tool.",
2947
+ description: "Answer a question engineering or product asked on a row you filed. Call with nothing to see what is waiting on you (each with its id and the item); call with `answer` (and `project` or `id` when more than one is open) to put your reply on the row — it becomes part of the record and the person who asked hears at once. When someone says 'tell them…' or 'the answer is…' about a question SLEDS relayed, this is the tool.",
2350
2948
  inputSchema: {
2351
2949
  type: "object",
2352
2950
  properties: {
@@ -2406,7 +3004,7 @@ var TOOLS = [
2406
3004
  },
2407
3005
  {
2408
3006
  name: "actual",
2409
- description: "Call this WHEN THE WORK LANDS (merged, shipped, done) — the last thing you do on a build, every time, with the same `source` the estimate was filed under. Records what happened: state (started · done · merged · shipped), where the output lives (`ref`: PR url, commit, doc), the model, who did it, dollars and tokens when something counted them, time by phase. Minutes are MEASURED by SLEDS from the pickup (or the estimate) to now — do not invent a number; pass `minutes` only when a person stated it. Pass `sessionId` (your session link) so the session's token spend joins the row. If SLEDS answers that no estimate is on file, call `estimate` with this source first, then send this again.",
3007
+ description: "Call this WHEN THE WORK LANDS (merged, shipped, done) — the last thing you do on a build, every time, with the same `source` the estimate was filed under. Records what happened: state (started · done · merged · shipped), where the output lives (`ref`: PR url, commit, doc), the model, who did it, dollars and tokens when something counted them, time by phase. Minutes are MEASURED by SLEDS from the pickup (or the estimate) to now — do not invent a number; pass `minutes` only when a person stated it. Pass `sessionId` (your session link) so the session's token spend joins the item. If SLEDS answers that no estimate is on file, call `estimate` with this source first, then send this again.",
2410
3008
  inputSchema: {
2411
3009
  type: "object",
2412
3010
  properties: {
@@ -2480,6 +3078,18 @@ var TOOLS = [
2480
3078
  }
2481
3079
  }
2482
3080
  },
3081
+ {
3082
+ name: "archive",
3083
+ description: "Put an item away — off Work, the sleds and the counts, reversible (undo: true brings it back whole). The filer, an approver or an admin. An open hand-off on it is withdrawn and it comes off its sled. Nothing is deleted; deleting is an admin's, in the app.",
3084
+ inputSchema: {
3085
+ type: "object",
3086
+ properties: {
3087
+ project: { type: "string", description: "The item's key (QW-142) or project." },
3088
+ undo: { type: "boolean", description: "Bring an archived item back." }
3089
+ },
3090
+ required: ["project"]
3091
+ }
3092
+ },
2483
3093
  {
2484
3094
  name: "withdraw",
2485
3095
  description: "Take back a hand-off you parked before anyone picks it up (the id from the handoffs tool). Only the seat that parked it can withdraw it.",
@@ -2609,12 +3219,12 @@ function byModelText(raw) {
2609
3219
  if (!Array.isArray(raw))
2610
3220
  return "";
2611
3221
  const parts = [];
2612
- for (const row of raw) {
2613
- if (!isObject4(row) || !isObject4(row.standard))
3222
+ for (const row2 of raw) {
3223
+ if (!isObject4(row2) || !isObject4(row2.standard))
2614
3224
  continue;
2615
- const model = str4(row.model);
2616
- const low = num3(row.standard.lowUsd);
2617
- const high = num3(row.standard.highUsd);
3225
+ const model = str4(row2.model);
3226
+ const low = num3(row2.standard.lowUsd);
3227
+ const high = num3(row2.standard.highUsd);
2618
3228
  if (!model || low === undefined || high === undefined)
2619
3229
  continue;
2620
3230
  const m = /^claude-([a-z]+)-(\d+)(?:-(\d+))?/.exec(model);
@@ -2665,6 +3275,13 @@ function briefAnswers(raw) {
2665
3275
  }
2666
3276
  return out;
2667
3277
  }
3278
+ async function readWorkspaces(ctx) {
3279
+ if (!ctx.client)
3280
+ return [];
3281
+ const r = await ctx.client.get("/proxy/workspaces");
3282
+ const list = r && Array.isArray(r.workspaces) ? r.workspaces : [];
3283
+ return list.map((w) => isObject4(w) ? { slug: str4(w.slug) ?? "", name: str4(w.name) ?? "" } : null).filter((w) => w !== null && w.slug !== "" && w.name !== "");
3284
+ }
2668
3285
  async function readTouches(ctx, text) {
2669
3286
  if (!ctx.client || !text.trim())
2670
3287
  return null;
@@ -2673,8 +3290,9 @@ async function readTouches(ctx, text) {
2673
3290
  return null;
2674
3291
  const mapped = r.mapped === true;
2675
3292
  const areas = Array.isArray(r.areas) ? r.areas : [];
3293
+ const repos = [...new Set(areas.map((a) => str4(a.repo)).filter((x) => Boolean(x)))];
2676
3294
  const touchesLine = areas.length ? areas.map((a) => `${str4(a.area) ?? ""}${str4(a.repo) ? ` (${str4(a.repo).split("/").pop()})` : ""}`).filter(Boolean).join(" · ") : null;
2677
- return { text: str4(r.text) ?? "", touchesLine, mapped };
3295
+ return { text: str4(r.text) ?? "", touchesLine, mapped, repos };
2678
3296
  }
2679
3297
  async function touchesTool(ctx, args) {
2680
3298
  if (!ctx.client)
@@ -2705,23 +3323,28 @@ async function intakeTool(ctx, args) {
2705
3323
  if (map?.touchesLine && !brief.touches)
2706
3324
  brief.touches = { text: map.touchesLine, from: "product" };
2707
3325
  const open = openQuestions(brief);
3326
+ const workspaces = await readWorkspaces(ctx);
3327
+ const workspaceArg = str4(args.workspace)?.trim() || undefined;
3328
+ const workspaceQuestion = workspaces.length > 1 && !workspaceArg ? `Which workspace is this for — ${workspaces.map((w) => w.name).join(", ")}? (skip if you don't know; product or an engineer will place it)` : null;
2708
3329
  if (args.confirm !== true) {
2709
- const needed = missingToFile(brief);
3330
+ const needed2 = missingToFile(brief);
2710
3331
  const attachments = parseAttachments(args.attachments).map((a) => a.name || "file");
2711
3332
  const structured = {
2712
3333
  kind: "draft",
2713
3334
  brief: briefForView(brief),
2714
3335
  open,
2715
- needed,
3336
+ needed: needed2,
2716
3337
  map: map?.text ?? null,
2717
3338
  links,
2718
- attachments
3339
+ attachments,
3340
+ ...workspaceQuestion ? { workspaces: workspaces.map((w) => w.name), workspaceQuestion } : {}
2719
3341
  };
2720
3342
  const lines = [
2721
3343
  renderBriefMarkdown(brief, open, { map: map?.text ?? null, files: attachments }),
2722
3344
  "",
3345
+ ...workspaceQuestion ? [`Also ask: ${workspaceQuestion} Pass the answer as \`workspace\`.`, ""] : [],
2723
3346
  open.length > 0 ? "Show the person the table as is, then ask at most two of the open questions at a time, in your own words; skip what they already said." : "Show the person the table as is.",
2724
- needed.length > 0 ? `Nothing is filed yet. Filing needs: ${needed.join(", ")}. When they say yes to the brief, call intake again with the same text, their answers, and confirm: true.` : "Nothing is filed yet. When they say yes, call intake again with the same text, their answers, and confirm: true.",
3347
+ needed2.length > 0 ? `Nothing is filed yet. Filing needs: ${needed2.join(", ")}. When they say yes to the brief, call intake again with the same text, their answers, and confirm: true.` : "Nothing is filed yet. When they say yes, call intake again with the same text, their answers, and confirm: true.",
2725
3348
  "",
2726
3349
  "```json sleds-draft",
2727
3350
  JSON.stringify(structured),
@@ -2743,9 +3366,12 @@ async function intakeTool(ctx, args) {
2743
3366
  const dueArg = typeof args.due === "string" && /^\d{4}-\d{2}-\d{2}$/.test(args.due) ? args.due : null;
2744
3367
  const due = dueArg ?? parseDue([text, ...Object.values(answers), ...added].filter(Boolean).join(`
2745
3368
  `));
3369
+ const repos = Array.isArray(args.repos) ? args.repos.filter((x) => typeof x === "string").map((x) => x.trim()).filter(Boolean) : typeof args.repos === "string" ? args.repos.split(/[,\s]+/).filter(Boolean) : map?.repos ?? [];
2746
3370
  const r = await ctx.client.postFull("/proxy/intake", {
2747
3371
  brief: { ...brief, intake: { asked, added, related: [] }, ...due ? { due } : {} },
2748
- ...parent ? { parent } : {}
3372
+ ...parent ? { parent } : {},
3373
+ ...workspaceArg ? { workspace: workspaceArg } : {},
3374
+ ...repos.length ? { repos, reposStated: Array.isArray(args.repos) || typeof args.repos === "string" } : {}
2749
3375
  });
2750
3376
  if (!r)
2751
3377
  return { text: `Nothing was filed. ${UNREACHABLE.text}`, isError: true };
@@ -2877,9 +3503,18 @@ async function attachTool(ctx, args) {
2877
3503
  const project = str4(args.project)?.trim();
2878
3504
  if (!project)
2879
3505
  return {
2880
- text: "Which row? Pass project: the key status shows (or what intake said).",
3506
+ text: "Which item? Pass project: the key status shows (or what intake said).",
2881
3507
  isError: true
2882
3508
  };
3509
+ const url = str4(args.url)?.trim();
3510
+ if (url) {
3511
+ const res = await postSaying(ctx, "/proxy/item/link", { project, url, ...str4(args.title) ? { title: str4(args.title) } : {} }, "Could not add the link.");
3512
+ if ("fail" in res)
3513
+ return res.fail;
3514
+ return {
3515
+ text: res.json.added === false ? `That link is already on ${project}.` : `Linked on ${project}: ${url}. Anyone on the board can open it from the item.`
3516
+ };
3517
+ }
2883
3518
  const files = parseAttachments([args]);
2884
3519
  if (files.length === 0)
2885
3520
  return {
@@ -2894,6 +3529,137 @@ async function attachTool(ctx, args) {
2894
3529
  text: `On the row ${project}: ${first.name}${first.url ? ` — ${first.url}` : ""}. Anyone on the board can open it from the row.`
2895
3530
  };
2896
3531
  }
3532
+ async function updateTool(ctx, args) {
3533
+ if (!ctx.client)
3534
+ return NOT_SET_UP;
3535
+ const project = str4(args.project)?.trim();
3536
+ if (!project)
3537
+ return { text: "Which item? Pass project: its key (QW-142).", isError: true };
3538
+ const fields = [
3539
+ "ask",
3540
+ "why",
3541
+ "done",
3542
+ "smallest",
3543
+ "touches",
3544
+ "demand",
3545
+ "description",
3546
+ "due",
3547
+ "parent"
3548
+ ];
3549
+ const patch = {};
3550
+ for (const f of fields) {
3551
+ if (args[f] === null && (f === "due" || f === "parent"))
3552
+ patch[f] = null;
3553
+ else if (typeof args[f] === "string")
3554
+ patch[f] = args[f];
3555
+ }
3556
+ if (Array.isArray(args.repos))
3557
+ patch.repos = args.repos.filter((x) => typeof x === "string");
3558
+ else if (typeof args.repos === "string")
3559
+ patch.repos = args.repos.split(/[,\s]+/).filter(Boolean);
3560
+ if (args.sensitivity === null)
3561
+ patch.sensitivity = null;
3562
+ else if (typeof args.sensitivity === "string" && /^(public|internal|restricted)$/.test(args.sensitivity)) {
3563
+ patch.sensitivity = args.sensitivity;
3564
+ if (typeof args.sensitivityNote === "string" && args.sensitivityNote.trim())
3565
+ patch.sensitivityNote = args.sensitivityNote.trim();
3566
+ }
3567
+ if (Object.keys(patch).length === 0)
3568
+ return {
3569
+ text: "Nothing to change. Pass the fields that change: ask, why, done, smallest, touches, demand, description, due, parent, repos.",
3570
+ isError: true
3571
+ };
3572
+ const res = await postSaying(ctx, "/proxy/item/update", { project, ...patch }, "Could not update the item.");
3573
+ if ("fail" in res)
3574
+ return res.fail;
3575
+ const changed = Array.isArray(res.json.changed) ? res.json.changed.filter((x) => typeof x === "string") : [];
3576
+ const key = str4(res.json.key) ?? project;
3577
+ if (changed.length === 0)
3578
+ return { text: `${key}: nothing differed from what is on the item.` };
3579
+ const stale = res.json.sizeStale === true;
3580
+ return {
3581
+ text: `${key} updated: ${changed.join(", ")}.${stale ? " The band was sized before this change — say so, and offer to re-size (estimate with the same source)." : ""}`
3582
+ };
3583
+ }
3584
+ async function archiveTool(ctx, args) {
3585
+ if (!ctx.client)
3586
+ return NOT_SET_UP;
3587
+ const project = str4(args.project)?.trim();
3588
+ if (!project)
3589
+ return { text: "Which item? Pass project: its key (QW-142).", isError: true };
3590
+ const undo = args.undo === true;
3591
+ const res = await postSaying(ctx, "/proxy/item/archive", { project, undo }, undo ? "Could not unarchive the item." : "Could not archive the item.");
3592
+ if ("fail" in res)
3593
+ return res.fail;
3594
+ const key = str4(res.json.key) ?? project;
3595
+ const title = str4(res.json.title) ?? "";
3596
+ if (res.json.changed !== true)
3597
+ return { text: `${key} was ${undo ? "not archived" : "already archived"} — nothing changed.` };
3598
+ return {
3599
+ text: undo ? `${key} is back on the board${title ? ` — ${title}` : ""}.` : `${key} archived${title ? ` — ${title}` : ""}. Off Work and the sleds; archive with undo: true brings it back.`
3600
+ };
3601
+ }
3602
+ async function itemTool(ctx, args) {
3603
+ if (!ctx.client)
3604
+ return NOT_SET_UP;
3605
+ const q = str4(args.q)?.trim() || str4(args.project)?.trim();
3606
+ if (!q)
3607
+ return { text: "Which item? Pass q: its key (QW-142) or its title.", isError: true };
3608
+ const r = await ctx.client.get(`/proxy/item?q=${encodeURIComponent(q)}`);
3609
+ if (!r)
3610
+ return UNREACHABLE;
3611
+ if (r.found === false)
3612
+ return {
3613
+ text: `Nothing on the sled answers to "${q}". Try the key (QW-142) or a word from the title with status.`,
3614
+ isError: true
3615
+ };
3616
+ const md = str4(r.markdown) ?? "";
3617
+ const readiness = isObject4(r.readiness) ? r.readiness : null;
3618
+ const line = readiness ? str4(readiness.line) : undefined;
3619
+ const state = readiness ? str4(readiness.state) : undefined;
3620
+ const profile = isObject4(r.profile) ? r.profile : null;
3621
+ const restricted = profile ? str4(profile.sensitivity) === "restricted" : false;
3622
+ const tail = state === "gaps" ? `
3623
+
3624
+ ---
3625
+ Before anyone starts: ${line}. Fill the gaps with \`update\` (why, done, repos…)${restricted ? "" : " or ask `check` for the model's read"}.` : state === "ready" ? `
3626
+
3627
+ ---
3628
+ Ready to pick up.${restricted ? "" : " `check` gives the model's second read if you want one."}` : "";
3629
+ const note = restricted ? `
3630
+
3631
+ This item is restricted: its text stays in the workspace — no model check, no rerank — and once Policy › Routing is on, only a local route may run it. Say so before building on a cloud seat.` : "";
3632
+ return { text: `${md}${tail}${note}` };
3633
+ }
3634
+ async function checkTool(ctx, args) {
3635
+ if (!ctx.client)
3636
+ return NOT_SET_UP;
3637
+ const project = str4(args.project)?.trim() || str4(args.q)?.trim();
3638
+ if (!project)
3639
+ return { text: "Which item? Pass project: its key (QW-142).", isError: true };
3640
+ const res = await postSaying(ctx, "/proxy/item/check", { project }, "Could not start the check.");
3641
+ if ("fail" in res)
3642
+ return res.fail;
3643
+ return {
3644
+ text: `Checking ${str4(res.json.key) ?? project}. The model's read lands on the item's thread in under a minute — \`item\` shows it, and the item's readiness carries its gaps from then on.`
3645
+ };
3646
+ }
3647
+ async function commentTool(ctx, args) {
3648
+ if (!ctx.client)
3649
+ return NOT_SET_UP;
3650
+ const project = str4(args.project)?.trim();
3651
+ const md = str4(args.md)?.trim();
3652
+ if (!project)
3653
+ return { text: "Which item? Pass project: its key (QW-142).", isError: true };
3654
+ if (!md)
3655
+ return { text: "Nothing to say. Pass md.", isError: true };
3656
+ const res = await postSaying(ctx, "/proxy/item/comment", { project, md }, "Could not post the comment.");
3657
+ if ("fail" in res)
3658
+ return res.fail;
3659
+ return {
3660
+ text: `On ${str4(res.json.key) ?? project}'s thread. The team sees it on the item, signed as through you.`
3661
+ };
3662
+ }
2897
3663
  async function overnightTool(ctx, args) {
2898
3664
  if (!ctx.client)
2899
3665
  return NOT_SET_UP;
@@ -3008,8 +3774,8 @@ async function dispatchTool(ctx, args) {
3008
3774
  }
3009
3775
  return { text: reply };
3010
3776
  }
3011
- const row = isObject4(j.row) ? j.row : {};
3012
- const title = str4(row.title) ?? "the row";
3777
+ const row2 = isObject4(j.row) ? j.row : {};
3778
+ const title = str4(row2.title) ?? "the row";
3013
3779
  if (action.kind === "parked") {
3014
3780
  const dest = str4(action.to);
3015
3781
  return {
@@ -3052,9 +3818,9 @@ async function approveTool(ctx, args) {
3052
3818
  isError: true
3053
3819
  };
3054
3820
  const j = r.json ?? {};
3055
- const row = isObject4(j.row) ? j.row : {};
3056
- const title = str4(row.title) ?? "the row";
3057
- const filedBy = str4(row.filedBy);
3821
+ const row2 = isObject4(j.row) ? j.row : {};
3822
+ const title = str4(row2.title) ?? "the row";
3823
+ const filedBy = str4(row2.filedBy);
3058
3824
  if (decision === "approved")
3059
3825
  return {
3060
3826
  text: `${title}: approved — it is ready on the sled${filedBy ? `; ${filedBy} is told` : ""}. dispatch says where it should go; an engineer takes it with Take it or /sled pickup.`
@@ -3111,7 +3877,7 @@ function questionsText(open, now) {
3111
3877
  return "Nothing is waiting on your answer.";
3112
3878
  const lines = open.map((q) => {
3113
3879
  const at = typeof q.at === "number" ? ` · ${shortDuration(Math.max(0, now - q.at))} ago` : "";
3114
- return `- ${str4(q.by) ?? "someone"} asked on "${str4(q.title) ?? str4(q.project) ?? "a row"}"${at}: ${str4(q.question) ?? ""} (id ${str4(q.id) ?? "?"})`;
3880
+ return `- ${str4(q.by) ?? "someone"} asked on "${str4(q.title) ?? str4(q.project) ?? "an item"}"${at}: ${str4(q.question) ?? ""} (id ${str4(q.id) ?? "?"})`;
3115
3881
  });
3116
3882
  return `${open.length === 1 ? "One question is" : `${open.length} questions are`} waiting on you:
3117
3883
  ${lines.join(`
@@ -3124,10 +3890,10 @@ async function answerTool(ctx, args) {
3124
3890
  const now = (ctx.now ?? Date.now)();
3125
3891
  const answer = str4(args.answer)?.trim();
3126
3892
  if (!answer) {
3127
- const r = await ctx.client.get("/proxy/questions");
3128
- if (!r)
3893
+ const r2 = await ctx.client.get("/proxy/questions");
3894
+ if (!r2)
3129
3895
  return UNREACHABLE;
3130
- const open = Array.isArray(r.open) ? r.open.filter(isObject4) : [];
3896
+ const open = Array.isArray(r2.open) ? r2.open.filter(isObject4) : [];
3131
3897
  return { text: questionsText(open, now) };
3132
3898
  }
3133
3899
  const project = str4(args.project)?.trim() || undefined;
@@ -3160,7 +3926,7 @@ async function handoffsTool(ctx) {
3160
3926
  if (handoffs.length === 0)
3161
3927
  return { text: "No open hand-offs on your team. Park one with the handoff tool." };
3162
3928
  const now = (ctx.now ?? Date.now)();
3163
- const packets = handoffs.map((h) => str4(h.packet)).filter((p) => Boolean(p)).join(`
3929
+ const packets = handoffs.map((h) => str4(h.packet)).filter((p2) => Boolean(p2)).join(`
3164
3930
 
3165
3931
  ---
3166
3932
 
@@ -3305,20 +4071,20 @@ async function actualTool(ctx, args) {
3305
4071
  text: "`source` is required — the same one the estimate was filed under.",
3306
4072
  isError: true
3307
4073
  };
3308
- const iso = (v) => {
4074
+ const iso2 = (v) => {
3309
4075
  const t = str4(v) ? Date.parse(str4(v)) : Number.NaN;
3310
4076
  return Number.isFinite(t) ? t : undefined;
3311
4077
  };
3312
4078
  const phasesRaw = typeof args.phases === "object" && args.phases !== null ? args.phases : undefined;
3313
4079
  const phases = phasesRaw ? Object.fromEntries(["discussMin", "buildMin", "deployMin", "testMin"].map((k) => [k, num3(phasesRaw[k])]).filter(([, v]) => v !== undefined)) : undefined;
3314
4080
  const work = {
3315
- ...iso(args.startedAt) !== undefined ? { startedAt: iso(args.startedAt) } : {},
3316
- ...iso(args.finishedAt) !== undefined ? { finishedAt: iso(args.finishedAt) } : {},
4081
+ ...iso2(args.startedAt) !== undefined ? { startedAt: iso2(args.startedAt) } : {},
4082
+ ...iso2(args.finishedAt) !== undefined ? { finishedAt: iso2(args.finishedAt) } : {},
3317
4083
  ...str4(args.model) ? { model: str4(args.model) } : {},
3318
4084
  ...num3(args.tokensIn) !== undefined ? { tokensIn: num3(args.tokensIn) } : {},
3319
4085
  ...num3(args.tokensOut) !== undefined ? { tokensOut: num3(args.tokensOut) } : {},
3320
4086
  ...str4(args.by) ? { by: str4(args.by) } : {},
3321
- ...sessionIdOf(args.sessionId) ? { sessionId: sessionIdOf(args.sessionId) } : {},
4087
+ ...sessionIdOf(args.sessionId) ?? ctx.sessionId?.() ? { sessionId: sessionIdOf(args.sessionId) ?? ctx.sessionId?.() } : {},
3322
4088
  ...str4(args.ref) ? { ref: str4(args.ref) } : {},
3323
4089
  ...phases && Object.keys(phases).length > 0 ? { phases } : {},
3324
4090
  ...num3(args.seatCreditsUsd) !== undefined ? { seatCreditsUsd: num3(args.seatCreditsUsd) } : {}
@@ -3364,7 +4130,7 @@ async function actualTool(ctx, args) {
3364
4130
  ].filter(Boolean);
3365
4131
  if (unsized)
3366
4132
  return {
3367
- text: `Recorded: ${bits.join(" · ")}. No estimate came first, so the row was created from this actual and the Sled shows it as landed unsized — the time could only be measured from now, so pass \`minutes\` only if a person states them. Next time, size before you start: \`estimate\` with this source before the first line.`
4133
+ text: `Recorded: ${bits.join(" · ")}. No estimate came first, so the item was created from this actual and the Sled shows it as landed unsized — the time could only be measured from now, so pass \`minutes\` only if a person states them. Next time, size before you start: \`estimate\` with this source before the first line.`
3368
4134
  };
3369
4135
  return { text: `Recorded: ${bits.join(" · ")}. Projects shows it beside the estimate.` };
3370
4136
  }
@@ -3373,10 +4139,10 @@ async function buddyTool(ctx, args) {
3373
4139
  return NOT_SET_UP;
3374
4140
  const email = str4(args.email)?.trim();
3375
4141
  if (!email) {
3376
- const r = await ctx.client.get("/proxy/buddy");
3377
- if (!r)
4142
+ const r2 = await ctx.client.get("/proxy/buddy");
4143
+ if (!r2)
3378
4144
  return UNREACHABLE;
3379
- const list = Array.isArray(r.buddies) ? r.buddies : [];
4145
+ const list = Array.isArray(r2.buddies) ? r2.buddies : [];
3380
4146
  if (list.length === 0)
3381
4147
  return {
3382
4148
  text: "No buddies yet. `buddy {email}` sends someone an invite; once they accept, `handoff {to: email}` reaches them in their own org."
@@ -3387,10 +4153,10 @@ ${lines.join(`
3387
4153
  `)}` };
3388
4154
  }
3389
4155
  if (args.remove === true) {
3390
- const r = await ctx.client.postFull("/proxy/buddy", { email, remove: true });
3391
- if (!r)
4156
+ const r2 = await ctx.client.postFull("/proxy/buddy", { email, remove: true });
4157
+ if (!r2)
3392
4158
  return UNREACHABLE;
3393
- return { text: r.json?.ok ? `Unpaired from ${email}.` : `${email} was not a buddy.` };
4159
+ return { text: r2.json?.ok ? `Unpaired from ${email}.` : `${email} was not a buddy.` };
3394
4160
  }
3395
4161
  const r = await ctx.client.postFull("/proxy/buddy", { email });
3396
4162
  if (!r)
@@ -3422,6 +4188,16 @@ async function callTool(name, args, ctx) {
3422
4188
  return touchesTool(ctx, args);
3423
4189
  case "attach":
3424
4190
  return attachTool(ctx, args);
4191
+ case "update":
4192
+ return updateTool(ctx, args);
4193
+ case "archive":
4194
+ return archiveTool(ctx, args);
4195
+ case "comment":
4196
+ return commentTool(ctx, args);
4197
+ case "item":
4198
+ return itemTool(ctx, args);
4199
+ case "check":
4200
+ return checkTool(ctx, args);
3425
4201
  case "answer":
3426
4202
  return answerTool(ctx, args);
3427
4203
  case "dispatch":
@@ -3576,7 +4352,7 @@ ${out.startsWith(`
3576
4352
  ${providerBlock(proxyUrl)}
3577
4353
  `;
3578
4354
  }
3579
- function removeCodexProvider(toml, o = {}) {
4355
+ function removeCodexProvider(toml, o2 = {}) {
3580
4356
  const lines = toml.split(`
3581
4357
  `);
3582
4358
  const out = [];
@@ -3589,7 +4365,7 @@ function removeCodexProvider(toml, o = {}) {
3589
4365
  }
3590
4366
  let text = out.join(`
3591
4367
  `);
3592
- if (!o.keepPrior) {
4368
+ if (!o2.keepPrior) {
3593
4369
  const prior = text.match(/^# sleds-prior-model-provider = "([^"]*)"\s*\n/m);
3594
4370
  if (prior) {
3595
4371
  text = text.replace(prior[0], "");
@@ -3606,22 +4382,22 @@ function topLevel(toml) {
3606
4382
  const i = toml.search(/^\s*\[/m);
3607
4383
  return i === -1 ? toml : toml.slice(0, i);
3608
4384
  }
3609
- function initCodex(o) {
4385
+ function initCodex(o2) {
3610
4386
  const wrote = [];
3611
- const path = codexConfigPath(o.home);
4387
+ const path = codexConfigPath(o2.home);
3612
4388
  const prior = existsSync3(path) ? readFileSync3(path, "utf8") : "";
3613
4389
  mkdirSync3(dirname2(path), { recursive: true });
3614
- writeFileSync3(path, applyCodexProvider(prior, o.proxyUrl), { mode: 384 });
3615
- wrote.push(`${prior ? "updated" : "created"} ${path}: provider "${PROVIDER_ID}" → ${o.proxyUrl}/v1 (key from $${TOKEN_ENV}), model_provider = "${PROVIDER_ID}"`);
3616
- const cfg = readConfig(o.home);
3617
- writeConfig(o.home, {
4390
+ writeFileSync3(path, applyCodexProvider(prior, o2.proxyUrl), { mode: 384 });
4391
+ wrote.push(`${prior ? "updated" : "created"} ${path}: provider "${PROVIDER_ID}" → ${o2.proxyUrl}/v1 (key from $${TOKEN_ENV}), model_provider = "${PROVIDER_ID}"`);
4392
+ const cfg = readConfig(o2.home);
4393
+ writeConfig(o2.home, {
3618
4394
  ...cfg,
3619
- token: o.token,
3620
- proxyUrl: o.proxyUrl,
4395
+ token: o2.token,
4396
+ proxyUrl: o2.proxyUrl,
3621
4397
  agentKind: "codex",
3622
- ...o.email ? { email: o.email } : {}
4398
+ ...o2.email ? { email: o2.email } : {}
3623
4399
  });
3624
- wrote.push(`saved token and proxy URL to ${configPath(o.home)} (0600)`);
4400
+ wrote.push(`saved token and proxy URL to ${configPath(o2.home)} (0600)`);
3625
4401
  return wrote;
3626
4402
  }
3627
4403
  function runCodex(home, args, deps = {}) {
@@ -3667,7 +4443,7 @@ var musherLabel = (env = process.env) => {
3667
4443
  return `${MUSHER_LABEL}.${tag}`;
3668
4444
  };
3669
4445
  var musherPlistPath = (home, env = process.env) => join5(home, "Library", "LaunchAgents", `${musherLabel(env)}.plist`);
3670
- var musherLogPath = (configDir) => join5(configDir, "musher.log");
4446
+ var musherLogPath = (configDir2) => join5(configDir2, "musher.log");
3671
4447
  var xmlEscape = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
3672
4448
  var musherPath = (home, execPath, currentPath = "") => {
3673
4449
  const parts = [
@@ -3683,11 +4459,11 @@ var musherPath = (home, execPath, currentPath = "") => {
3683
4459
  ];
3684
4460
  return [...new Set(parts)].join(":");
3685
4461
  };
3686
- var buildMusherPlist = (o) => {
3687
- const label = o.label ?? MUSHER_LABEL;
3688
- const args = [...o.sledsArgv, "musher", "tick"].map((a) => ` <string>${xmlEscape(a)}</string>`).join(`
4462
+ var buildMusherPlist = (o2) => {
4463
+ const label = o2.label ?? MUSHER_LABEL;
4464
+ const args = [...o2.sledsArgv, "musher", "tick"].map((a) => ` <string>${xmlEscape(a)}</string>`).join(`
3689
4465
  `);
3690
- const env = Object.entries(o.env).filter(([, v]) => v !== "").map(([k, v]) => ` <key>${xmlEscape(k)}</key>
4466
+ const env = Object.entries(o2.env).filter(([, v]) => v !== "").map(([k, v]) => ` <key>${xmlEscape(k)}</key>
3691
4467
  <string>${xmlEscape(v)}</string>`).join(`
3692
4468
  `);
3693
4469
  return `<?xml version="1.0" encoding="UTF-8"?>
@@ -3709,9 +4485,9 @@ ${env}
3709
4485
  <key>RunAtLoad</key>
3710
4486
  <true/>
3711
4487
  <key>StandardOutPath</key>
3712
- <string>${xmlEscape(o.logPath)}</string>
4488
+ <string>${xmlEscape(o2.logPath)}</string>
3713
4489
  <key>StandardErrorPath</key>
3714
- <string>${xmlEscape(o.logPath)}</string>
4490
+ <string>${xmlEscape(o2.logPath)}</string>
3715
4491
  </dict>
3716
4492
  </plist>
3717
4493
  `;
@@ -3728,15 +4504,15 @@ var NO_SESSION_PROMPT = [
3728
4504
  "SLEDS musher: a deferred job started on this seat, but no Claude Code session was recorded",
3729
4505
  "to resume. Do not guess at work. Reply with one line saying so, and stop."
3730
4506
  ].join(" ");
3731
- var claudeArgs = (o) => [
4507
+ var claudeArgs = (o2) => [
3732
4508
  "-p",
3733
- o.sessionId ? RESUME_PROMPT : NO_SESSION_PROMPT,
3734
- ...o.sessionId ? ["--resume", o.sessionId] : [],
3735
- ...o.model ? ["--model", o.model] : [],
4509
+ o2.sessionId ? RESUME_PROMPT : NO_SESSION_PROMPT,
4510
+ ...o2.sessionId ? ["--resume", o2.sessionId] : [],
4511
+ ...o2.model ? ["--model", o2.model] : [],
3736
4512
  "--output-format",
3737
4513
  "json",
3738
4514
  "--permission-mode",
3739
- o.permissionMode
4515
+ o2.permissionMode
3740
4516
  ];
3741
4517
  var parseClaudeOutput = (stdout, exitCode) => {
3742
4518
  const lines = stdout.split(`
@@ -3766,11 +4542,11 @@ var parseClaudeOutput = (stdout, exitCode) => {
3766
4542
  const tail = lines.slice(-5).join(" ").slice(0, SUMMARY_MAX);
3767
4543
  return { ok: false, summary: tail || `claude exited ${exitCode ?? "without a code"}` };
3768
4544
  };
3769
- var defaultExecCapture = (cmd, args, o) => {
4545
+ var defaultExecCapture = (cmd, args, o2) => {
3770
4546
  const r = spawnSync2(cmd, [...args], {
3771
- cwd: o.cwd,
4547
+ cwd: o2.cwd,
3772
4548
  encoding: "utf8",
3773
- timeout: o.timeoutMs,
4549
+ timeout: o2.timeoutMs,
3774
4550
  maxBuffer: 64 * 1024 * 1024,
3775
4551
  stdio: ["ignore", "pipe", "pipe"],
3776
4552
  env: { ...process.env, SLEDS_MUSHER: "1" }
@@ -3782,16 +4558,16 @@ var defaultExecCapture = (cmd, args, o) => {
3782
4558
  ...r.error ? { error: r.error.message } : {}
3783
4559
  };
3784
4560
  };
3785
- var musherClient = (o) => {
3786
- const fetchFn = o.fetchFn ?? fetch;
3787
- const headers = { authorization: `Bearer ${o.token}`, "content-type": "application/json" };
4561
+ var musherClient = (o2) => {
4562
+ const fetchFn = o2.fetchFn ?? fetch;
4563
+ const headers = { authorization: `Bearer ${o2.token}`, "content-type": "application/json" };
3788
4564
  const call = async (path, body) => {
3789
4565
  try {
3790
- const res = await fetchFn(`${o.proxyUrl}${path}`, {
4566
+ const res = await fetchFn(`${o2.proxyUrl}${path}`, {
3791
4567
  method: body === undefined ? "GET" : "POST",
3792
4568
  headers,
3793
4569
  ...body === undefined ? {} : { body: JSON.stringify(body) },
3794
- signal: AbortSignal.timeout(o.timeoutMs ?? 1e4)
4570
+ signal: AbortSignal.timeout(o2.timeoutMs ?? 1e4)
3795
4571
  });
3796
4572
  if (!res.ok)
3797
4573
  return null;
@@ -3833,33 +4609,33 @@ var collectOutputs = (commits, summary) => {
3833
4609
  out.push(`pr:${m[0]}`);
3834
4610
  return out.slice(0, 50);
3835
4611
  };
3836
- var runJob = async (o) => {
3837
- const log = o.log ?? (() => {});
3838
- const sessionId = o.job.claudeSessionId ?? o.fallbackSession?.id ?? null;
3839
- const cwd = o.job.cwd ?? o.fallbackSession?.cwd ?? undefined;
3840
- const started = await o.client.start(o.job.jobId, {
4612
+ var runJob = async (o2) => {
4613
+ const log = o2.log ?? (() => {});
4614
+ const sessionId = o2.job.claudeSessionId ?? o2.fallbackSession?.id ?? null;
4615
+ const cwd = o2.job.cwd ?? o2.fallbackSession?.cwd ?? undefined;
4616
+ const started = await o2.client.start(o2.job.jobId, {
3841
4617
  ...sessionId ? { claudeSessionId: sessionId } : {},
3842
4618
  ...cwd ? { cwd } : {},
3843
- host: o.host
4619
+ host: o2.host
3844
4620
  });
3845
4621
  if (!started)
3846
4622
  return { kind: "skipped", reason: "control plane unreachable" };
3847
4623
  if (!started.ok)
3848
4624
  return { kind: "skipped", reason: started.reason };
3849
- const model = o.job.model ?? started.model ?? o.fallbackSession?.model ?? null;
3850
- const args = claudeArgs({ sessionId, model, permissionMode: o.permissionMode });
3851
- log(`musher: running job ${o.job.jobId} (${sessionId ? `resume ${sessionId}` : "no session"}${model ? ` · ${model}` : ""}${cwd ? ` · ${cwd}` : ""})`);
3852
- const exec = o.exec ?? defaultExecCapture;
4625
+ const model = o2.job.model ?? started.model ?? o2.fallbackSession?.model ?? null;
4626
+ const args = claudeArgs({ sessionId, model, permissionMode: o2.permissionMode });
4627
+ log(`musher: running job ${o2.job.jobId} (${sessionId ? `resume ${sessionId}` : "no session"}${model ? ` · ${model}` : ""}${cwd ? ` · ${cwd}` : ""})`);
4628
+ const exec = o2.exec ?? defaultExecCapture;
3853
4629
  const headBefore = gitHead(cwd, exec);
3854
4630
  const t0 = Date.now();
3855
- const r = exec(o.claudePath, args, { cwd, timeoutMs: o.timeoutMs });
4631
+ const r = exec(o2.claudePath, args, { cwd, timeoutMs: o2.timeoutMs });
3856
4632
  const durationMs = Date.now() - t0;
3857
- const result = r.error ? { ok: false, summary: `could not run ${o.claudePath}: ${r.error}` } : parseClaudeOutput(r.stdout, r.status);
4633
+ const result = r.error ? { ok: false, summary: `could not run ${o2.claudePath}: ${r.error}` } : parseClaudeOutput(r.stdout, r.status);
3858
4634
  if (!r.error && r.status === null)
3859
- result.summary = `timed out after ${Math.round(o.timeoutMs / 60000)} min. ${result.summary}`.trim();
4635
+ result.summary = `timed out after ${Math.round(o2.timeoutMs / 60000)} min. ${result.summary}`.trim();
3860
4636
  const outputs = collectOutputs(gitNewCommits(cwd, headBefore, gitHead(cwd, exec), exec), result.summary);
3861
- await o.client.finish({
3862
- jobId: o.job.jobId,
4637
+ await o2.client.finish({
4638
+ jobId: o2.job.jobId,
3863
4639
  runId: started.runId,
3864
4640
  ok: result.ok,
3865
4641
  summary: result.summary,
@@ -3868,7 +4644,7 @@ var runJob = async (o) => {
3868
4644
  durationMs: result.durationMs ?? durationMs,
3869
4645
  ...result.sessionId ?? sessionId ? { claudeSessionId: result.sessionId ?? sessionId } : {}
3870
4646
  });
3871
- log(`musher: job ${o.job.jobId} ${result.ok ? "delivered" : "failed"} in ${Math.round(durationMs / 1000)}s`);
4647
+ log(`musher: job ${o2.job.jobId} ${result.ok ? "delivered" : "failed"} in ${Math.round(durationMs / 1000)}s`);
3872
4648
  return { kind: "ran", ok: result.ok, summary: result.summary, durationMs, outputs };
3873
4649
  };
3874
4650
  var verifyAttestation = (payload, sig, publicKeyB64) => {
@@ -4172,11 +4948,11 @@ var bindingResetAt = (w, now) => {
4172
4948
  return pick.length ? Math.min(...pick) : undefined;
4173
4949
  };
4174
4950
  var readSeatState = (home) => {
4175
- const p = seatStatePath(home);
4176
- if (!existsSync5(p))
4951
+ const p2 = seatStatePath(home);
4952
+ if (!existsSync5(p2))
4177
4953
  return {};
4178
4954
  try {
4179
- return parseSeatState(JSON.parse(readFileSync5(p, "utf8")));
4955
+ return parseSeatState(JSON.parse(readFileSync5(p2, "utf8")));
4180
4956
  } catch {
4181
4957
  return {};
4182
4958
  }
@@ -4301,28 +5077,28 @@ var seatWindowBody = (w) => ({
4301
5077
  ...w.sevenDay ? { sevenDay: w.sevenDay } : {},
4302
5078
  ...w.spendLimit ? { spendLimit: w.spendLimit } : {}
4303
5079
  });
4304
- var postJson = async (o, path, body) => {
4305
- const fetchFn = o.fetchFn ?? fetch;
5080
+ var postJson = async (o2, path, body) => {
5081
+ const fetchFn = o2.fetchFn ?? fetch;
4306
5082
  try {
4307
- const res = await fetchFn(`${o.proxyUrl}${path}`, {
5083
+ const res = await fetchFn(`${o2.proxyUrl}${path}`, {
4308
5084
  method: "POST",
4309
- headers: { authorization: `Bearer ${o.token}`, "content-type": "application/json" },
5085
+ headers: { authorization: `Bearer ${o2.token}`, "content-type": "application/json" },
4310
5086
  body: JSON.stringify(body),
4311
- signal: AbortSignal.timeout(o.timeoutMs ?? SEAT_POST_TIMEOUT_MS)
5087
+ signal: AbortSignal.timeout(o2.timeoutMs ?? SEAT_POST_TIMEOUT_MS)
4312
5088
  });
4313
5089
  return res.ok;
4314
5090
  } catch {
4315
5091
  return false;
4316
5092
  }
4317
5093
  };
4318
- var postSeatWindow = (o, w) => postJson(o, "/proxy/seat", seatWindowBody(w));
4319
- var postSeatWall = (o, input) => postJson(o, "/proxy/seat-wall", { source: "claude", ...input });
5094
+ var postSeatWindow = (o2, w) => postJson(o2, "/proxy/seat", seatWindowBody(w));
5095
+ var postSeatWall = (o2, input) => postJson(o2, "/proxy/seat-wall", { source: "claude", ...input });
4320
5096
  var SEAT_WALL_LINE = "Seat limit hit. sleds on --until-reset to continue on the platform key, or sleds defer to resume here at the reset.";
4321
5097
  var SEAT_WALL_LINE_NO_KEY = "Seat limit hit. sleds defer resumes this session on this Mac at the reset; /sled 5 to hand off — this org has no platform key to switch to.";
4322
5098
  var seatWallLine = (hasUpstreamKey) => hasUpstreamKey ? SEAT_WALL_LINE : SEAT_WALL_LINE_NO_KEY;
4323
- var readStdin = (timeoutMs, stdin = process.stdin) => new Promise((resolve) => {
5099
+ var readStdin = (timeoutMs, stdin = process.stdin) => new Promise((resolve3) => {
4324
5100
  if (stdin.isTTY) {
4325
- resolve("");
5101
+ resolve3("");
4326
5102
  return;
4327
5103
  }
4328
5104
  let data = "";
@@ -4332,7 +5108,7 @@ var readStdin = (timeoutMs, stdin = process.stdin) => new Promise((resolve) => {
4332
5108
  return;
4333
5109
  settled = true;
4334
5110
  clearTimeout(timer);
4335
- resolve(data);
5111
+ resolve3(data);
4336
5112
  };
4337
5113
  const timer = setTimeout(finish, timeoutMs);
4338
5114
  stdin.setEncoding("utf8");
@@ -4450,21 +5226,21 @@ var parseFlags = (argv) => {
4450
5226
  }
4451
5227
  return flags;
4452
5228
  };
4453
- var defer = async (o) => {
4454
- const log = o.log ?? console.log;
4455
- const error = o.error ?? console.error;
4456
- const now = (o.now ?? Date.now)();
4457
- const flags = parseFlags(o.argv);
4458
- const client = o.client ?? (() => {
4459
- const src = meterSource(o.home);
4460
- return src ? musherClient({ ...src, fetchFn: o.fetchFn }) : null;
5229
+ var defer = async (o2) => {
5230
+ const log = o2.log ?? console.log;
5231
+ const error = o2.error ?? console.error;
5232
+ const now = (o2.now ?? Date.now)();
5233
+ const flags = parseFlags(o2.argv);
5234
+ const client = o2.client ?? (() => {
5235
+ const src = meterSource(o2.home);
5236
+ return src ? musherClient({ ...src, fetchFn: o2.fetchFn }) : null;
4461
5237
  })();
4462
5238
  if (!client) {
4463
5239
  error("No seat on this machine. Run `sleds init` first.");
4464
5240
  return 1;
4465
5241
  }
4466
- const state = readSeatState(o.home);
4467
- const piped = o.stdin ? seatSessionFromStdin(o.stdin, now) : null;
5242
+ const state = readSeatState(o2.home);
5243
+ const piped = o2.stdin ? seatSessionFromStdin(o2.stdin, now) : null;
4468
5244
  const remembered = piped ?? state.session;
4469
5245
  const sessionId = flags.session ?? remembered?.id;
4470
5246
  const cwd = flags.cwd ?? remembered?.cwd;
@@ -4485,7 +5261,7 @@ var defer = async (o) => {
4485
5261
  ...model ? { model } : {},
4486
5262
  ...sessionId ? { claudeSessionId: sessionId } : {},
4487
5263
  ...cwd ? { cwd } : {},
4488
- host: o.host ?? hostname()
5264
+ host: o2.host ?? hostname()
4489
5265
  });
4490
5266
  if (!r) {
4491
5267
  error("Could not reach the SLEDS proxy; nothing was scheduled.");
@@ -4494,8 +5270,8 @@ var defer = async (o) => {
4494
5270
  const when = r.scheduledAt <= now + 60000 ? "as soon as the musher ticks (within 5 min)" : `${formatLocal(r.scheduledAt)} (in ${formatDuration(r.scheduledAt - now)})`;
4495
5271
  log(`Deferred. This Mac resumes ${sessionId ? `session ${sessionId.slice(0, 8)}…` : "the work"}${model ? ` on ${model}` : ""} ${when}.`);
4496
5272
  log(` job ${r.jobId}${cwd ? ` · ${cwd}` : ""} · runs on your seat, no key · sleds musher --status to watch`);
4497
- const cfg = readConfig(o.home);
4498
- if (process.platform === "darwin" && !existsSync6(musherPlistPath(o.home)) && cfg.musher?.agent !== "launchd")
5273
+ const cfg = readConfig(o2.home);
5274
+ if (process.platform === "darwin" && !existsSync6(musherPlistPath(o2.home)) && cfg.musher?.agent !== "launchd")
4499
5275
  log(` The musher agent is not installed on this Mac: run \`sleds musher --install\` once, or \`sleds run ${r.jobId}\` by hand at the reset.`);
4500
5276
  log(" Keep this Mac awake at that time (System Settings → Battery → prevent sleeping on power adapter).");
4501
5277
  return 0;
@@ -4675,7 +5451,7 @@ var renderCockpit = (cockpit, window, events, prior, now = Date.now(), extra = {
4675
5451
  `/sled pickup ${newHandoffs[0]}`,
4676
5452
  ...ws(cockpit)
4677
5453
  ]);
4678
- for (const id of extra.feed.delivered.filter((id) => !(before?.delivered ?? []).includes(id)))
5454
+ for (const id of extra.feed.delivered.filter((id2) => !(before?.delivered ?? []).includes(id2)))
4679
5455
  argv.push([
4680
5456
  "log",
4681
5457
  "--level",
@@ -4685,7 +5461,7 @@ var renderCockpit = (cockpit, window, events, prior, now = Date.now(), extra = {
4685
5461
  `overnight job done · /sled resume ${id}`,
4686
5462
  ...ws(cockpit)
4687
5463
  ]);
4688
- for (const id of extra.feed.failed.filter((id) => !(before?.failed ?? []).includes(id)))
5464
+ for (const id of extra.feed.failed.filter((id2) => !(before?.failed ?? []).includes(id2)))
4689
5465
  argv.push([
4690
5466
  "notify",
4691
5467
  "--title",
@@ -4742,15 +5518,15 @@ var feedFromJobs = (body, me, now) => {
4742
5518
  }
4743
5519
  return f;
4744
5520
  };
4745
- var fetchFeed = async (o, now) => {
5521
+ var fetchFeed = async (o2, now) => {
4746
5522
  try {
4747
- const res = await (o.fetchFn ?? fetch)(`${o.proxyUrl}/proxy/jobs`, {
4748
- headers: { authorization: `Bearer ${o.token}` },
4749
- signal: AbortSignal.timeout(o.timeoutMs ?? 1500)
5523
+ const res = await (o2.fetchFn ?? fetch)(`${o2.proxyUrl}/proxy/jobs`, {
5524
+ headers: { authorization: `Bearer ${o2.token}` },
5525
+ signal: AbortSignal.timeout(o2.timeoutMs ?? 1500)
4750
5526
  });
4751
5527
  if (!res.ok)
4752
5528
  return null;
4753
- return feedFromJobs(await res.json(), o.email, now);
5529
+ return feedFromJobs(await res.json(), o2.email, now);
4754
5530
  } catch {
4755
5531
  return null;
4756
5532
  }
@@ -4786,9 +5562,9 @@ init_meter();
4786
5562
  var defaultWhich = (cmd) => {
4787
5563
  const path = process.env.PATH ?? "";
4788
5564
  for (const dir of path.split(":").filter(Boolean)) {
4789
- const p = `${dir}/${cmd}`;
4790
- if (existsSync7(p))
4791
- return p;
5565
+ const p2 = `${dir}/${cmd}`;
5566
+ if (existsSync7(p2))
5567
+ return p2;
4792
5568
  }
4793
5569
  return null;
4794
5570
  };
@@ -4798,14 +5574,14 @@ var findClaude = (home, configured, which) => {
4798
5574
  const found = which("claude");
4799
5575
  if (found)
4800
5576
  return found;
4801
- for (const p of [
5577
+ for (const p2 of [
4802
5578
  `${home}/.local/bin/claude`,
4803
5579
  "/opt/homebrew/bin/claude",
4804
5580
  "/usr/local/bin/claude",
4805
5581
  `${home}/.npm-global/bin/claude`
4806
5582
  ])
4807
- if (existsSync7(p))
4808
- return p;
5583
+ if (existsSync7(p2))
5584
+ return p2;
4809
5585
  return null;
4810
5586
  };
4811
5587
  var musherAgentState = (home, env = process.env) => {
@@ -4825,7 +5601,8 @@ var musherAgentState = (home, env = process.env) => {
4825
5601
  // src/commands/status.ts
4826
5602
  init_config();
4827
5603
  init_settings();
4828
- var checkHealth = async (url, fetchFn = fetch, timeoutMs = 1000) => {
5604
+ var HEALTH_TIMEOUT_MS = 4000;
5605
+ var checkHealth = async (url, fetchFn = fetch, timeoutMs = HEALTH_TIMEOUT_MS) => {
4829
5606
  try {
4830
5607
  const res = await fetchFn(`${url}/healthz`, { signal: AbortSignal.timeout(timeoutMs) });
4831
5608
  return res.ok ? "ok" : "unreachable";
@@ -4868,10 +5645,10 @@ function defaultWhich2(cmd) {
4868
5645
  return [];
4869
5646
  }
4870
5647
  }
4871
- async function runDoctor(o) {
4872
- const env = o.env ?? process.env;
5648
+ async function runDoctor(o2) {
5649
+ const env = o2.env ?? process.env;
4873
5650
  const checks = [];
4874
- const home = o.home;
5651
+ const home = o2.home;
4875
5652
  const overrides = [
4876
5653
  env.SLEDS_HOME?.trim() ? `SLEDS_HOME=${env.SLEDS_HOME.trim()}` : null,
4877
5654
  env.CLAUDE_CONFIG_DIR?.trim() ? `CLAUDE_CONFIG_DIR=${env.CLAUDE_CONFIG_DIR.trim()}` : null
@@ -4921,7 +5698,7 @@ async function runDoctor(o) {
4921
5698
  }
4922
5699
  const src = meterSource(home);
4923
5700
  if (src) {
4924
- const health = await checkHealth(src.proxyUrl, o.fetchFn, o.timeoutMs);
5701
+ const health = await checkHealth(src.proxyUrl, o2.fetchFn, o2.timeoutMs);
4925
5702
  checks.push({
4926
5703
  level: health === "ok" ? "ok" : "fail",
4927
5704
  name: "proxy",
@@ -4930,15 +5707,15 @@ async function runDoctor(o) {
4930
5707
  });
4931
5708
  }
4932
5709
  if (settings) {
4933
- const seat = hasSeatHook(settings);
5710
+ const seat2 = hasSeatHook(settings);
4934
5711
  const spec = hasSpecHook(settings);
4935
5712
  const session = hasSessionHook(settings);
4936
5713
  const stop = hasStopHook(settings);
4937
- const all = seat && spec && session && stop;
5714
+ const all = seat2 && spec && session && stop;
4938
5715
  checks.push({
4939
5716
  level: all ? "ok" : "warn",
4940
5717
  name: "hooks",
4941
- detail: `seat-event ${seat ? "on" : "missing"} · spec-event ${spec ? "on" : "missing"} · session-event ${session ? "on" : "missing"} · stop-event ${stop ? "on" : "missing"}`,
5718
+ detail: `seat-event ${seat2 ? "on" : "missing"} · spec-event ${spec ? "on" : "missing"} · session-event ${session ? "on" : "missing"} · stop-event ${stop ? "on" : "missing"}`,
4942
5719
  fix: all ? undefined : "sleds hooks"
4943
5720
  });
4944
5721
  const want = env.SLEDS_HOME?.trim() ? resolve3(home, env.SLEDS_HOME.trim()) : undefined;
@@ -4969,18 +5746,18 @@ async function runDoctor(o) {
4969
5746
  name: "cockpit",
4970
5747
  detail: cockpit ? `${cockpit.kind}${cockpit.workspaceId ? ` (workspace ${cockpit.workspaceId})` : ""} — seat line in the sidebar, ring at the wall` : "none detected (run inside cmux to mirror the seat into its sidebar)"
4971
5748
  });
4972
- const nodeVersion = o.nodeVersion ?? process.version;
5749
+ const nodeVersion = o2.nodeVersion ?? process.version;
4973
5750
  checks.push({
4974
5751
  level: nodeOk(nodeVersion) ? "ok" : "warn",
4975
5752
  name: "node",
4976
5753
  detail: `${nodeVersion}${nodeOk(nodeVersion) ? "" : ` — seat-sync needs ≥ ${NODE_MIN} (node:sqlite)`}`,
4977
5754
  fix: nodeOk(nodeVersion) ? undefined : `brew install node@22 && echo 'export PATH="/opt/homebrew/opt/node@22/bin:$PATH"' >> ~/.zshrc`
4978
5755
  });
4979
- const which = (o.which ?? defaultWhich2)("sleds");
4980
- const stale = which.filter((p) => {
5756
+ const which = (o2.which ?? defaultWhich2)("sleds");
5757
+ const stale = which.filter((p2) => {
4981
5758
  try {
4982
- const target = lstatSync(p).isSymbolicLink() ? readlinkSync(p) : p;
4983
- return /\/src\/index\.ts$/.test(target) || /\.bun\/bin\/sleds$/.test(p);
5759
+ const target = lstatSync(p2).isSymbolicLink() ? readlinkSync(p2) : p2;
5760
+ return /\/src\/index\.ts$/.test(target) || /\.bun\/bin\/sleds$/.test(p2);
4984
5761
  } catch {
4985
5762
  return false;
4986
5763
  }
@@ -5002,7 +5779,7 @@ async function runDoctor(o) {
5002
5779
  detail: age === null ? "no report yet — run one Claude Code turn on this seat" : `last report ${age < 1 ? "just now" : `${age}m ago`} via the status line${seat.window?.sevenDay ? ` · wk ${Math.round(seat.window.sevenDay.usedPct)}%` : ""}`,
5003
5780
  fix: age === null ? "open claude, say hi, exit — the status line posts the seat's quota" : undefined
5004
5781
  });
5005
- if ((o.platform ?? process.platform) === "darwin") {
5782
+ if ((o2.platform ?? process.platform) === "darwin") {
5006
5783
  const agent = musherAgentState(home, env);
5007
5784
  checks.push(!agent.installed ? {
5008
5785
  level: "warn",
@@ -5019,14 +5796,14 @@ async function runDoctor(o) {
5019
5796
  name: "musher",
5020
5797
  detail: `launchd agent ${musherLabel(env)}, every 5 min`
5021
5798
  });
5022
- const claude = findClaude(home, readConfig(home).musher?.claudePath, (c) => (o.which ?? defaultWhich2)(c)[0] ?? null);
5799
+ const claude = findClaude(home, readConfig(home).musher?.claudePath, (c) => (o2.which ?? defaultWhich2)(c)[0] ?? null);
5023
5800
  checks.push(claude ? { level: "ok", name: "claude cli", detail: claude } : {
5024
5801
  level: "warn",
5025
5802
  name: "claude cli",
5026
5803
  detail: "`claude` not on PATH — the musher cannot run Claude Code",
5027
5804
  fix: 'sleds musher --claude "$(which claude)"'
5028
5805
  });
5029
- const sleep = (o.pmset ?? defaultPmset)();
5806
+ const sleep = (o2.pmset ?? defaultPmset)();
5030
5807
  if (sleep !== null)
5031
5808
  checks.push(sleep.acSleepMinutes === 0 ? {
5032
5809
  level: "ok",
@@ -5056,9 +5833,9 @@ var defaultPmset = () => {
5056
5833
  return null;
5057
5834
  }
5058
5835
  };
5059
- var doctor = async (o) => {
5060
- const log = o.log ?? console.log;
5061
- const checks = await runDoctor(o);
5836
+ var doctor = async (o2) => {
5837
+ const log = o2.log ?? console.log;
5838
+ const checks = await runDoctor(o2);
5062
5839
  for (const line of formatDoctor(checks))
5063
5840
  log(line);
5064
5841
  return checks.some((c) => c.level === "fail") ? 1 : 0;
@@ -5086,21 +5863,21 @@ function sourceKey(s) {
5086
5863
  function sourceText2(s) {
5087
5864
  return [`${str5(s.kind) ?? "?"} ${str5(s.project) ?? "?"}`, str5(s.doc), str5(s.item)].filter(Boolean).join(" › ");
5088
5865
  }
5089
- var readJson = (p) => {
5090
- if (!existsSync9(p))
5866
+ var readJson = (p2) => {
5867
+ if (!existsSync9(p2))
5091
5868
  return null;
5092
5869
  try {
5093
- return JSON.parse(readFileSync8(p, "utf8"));
5870
+ return JSON.parse(readFileSync8(p2, "utf8"));
5094
5871
  } catch {
5095
5872
  return null;
5096
5873
  }
5097
5874
  };
5098
- var writeJson = (p, v) => {
5099
- mkdirSync7(dirname4(p), { recursive: true, mode: 448 });
5100
- const tmp = `${p}.${process.pid}.tmp`;
5875
+ var writeJson = (p2, v) => {
5876
+ mkdirSync7(dirname4(p2), { recursive: true, mode: 448 });
5877
+ const tmp = `${p2}.${process.pid}.tmp`;
5101
5878
  writeFileSync7(tmp, `${JSON.stringify(v, null, 2)}
5102
5879
  `, { mode: 384 });
5103
- renameSync5(tmp, p);
5880
+ renameSync5(tmp, p2);
5104
5881
  };
5105
5882
  var readOwed = (home, now = Date.now()) => {
5106
5883
  const f = readJson(owedPath(home));
@@ -5115,14 +5892,14 @@ var readSession = (home) => {
5115
5892
  var writeSession = (home, s) => writeJson(sessionPath(home), s);
5116
5893
  function withEstimated(rows, a) {
5117
5894
  const key = sourceKey(a.source);
5118
- const row = {
5895
+ const row2 = {
5119
5896
  key,
5120
5897
  source: a.source,
5121
5898
  ...a.title ? { title: a.title } : {},
5122
5899
  ts: a.now,
5123
5900
  ...a.sessionId ? { sessionId: a.sessionId } : {}
5124
5901
  };
5125
- return [...rows.filter((r) => r.key !== key), row];
5902
+ return [...rows.filter((r) => r.key !== key), row2];
5126
5903
  }
5127
5904
  function withActual(rows, a) {
5128
5905
  if (a.state === "started")
@@ -5214,65 +5991,70 @@ function contextFor(deps) {
5214
5991
  const src = meterSource(deps.home);
5215
5992
  const now = deps.now ?? Date.now;
5216
5993
  const client = src ? trackingClient(fetchClient({ ...src, fetchFn: deps.fetchFn, timeoutMs: deps.timeoutMs }), deps.home, now) : null;
5217
- return { client, now: deps.now, usage: () => usageTool2(deps) };
5994
+ return {
5995
+ client,
5996
+ now: deps.now,
5997
+ usage: () => usageTool2(deps),
5998
+ sessionId: () => readSession(deps.home)?.sessionId
5999
+ };
5218
6000
  }
5219
6001
  var callTool2 = (name, args, deps) => callTool(name, args, contextFor(deps));
5220
6002
  var handleLine2 = (line, deps) => handleLine(line, contextFor(deps));
5221
6003
 
5222
6004
  // src/commands/handoffs.ts
5223
- var handoffsCmd = async (o) => {
5224
- const r = await callTool2("handoffs", {}, { home: o.home });
5225
- (r.isError ? o.error ?? console.error : o.log ?? console.log)(r.text);
6005
+ var handoffsCmd = async (o2) => {
6006
+ const r = await callTool2("handoffs", {}, { home: o2.home });
6007
+ (r.isError ? o2.error ?? console.error : o2.log ?? console.log)(r.text);
5226
6008
  return r.isError ? 1 : 0;
5227
6009
  };
5228
- var pickupCmd = async (o) => {
5229
- const id = o.argv[0];
6010
+ var pickupCmd = async (o2) => {
6011
+ const id = o2.argv[0];
5230
6012
  if (!id) {
5231
- (o.error ?? console.error)("usage: sleds pickup <handoffId> (ids from `sleds handoffs`)");
6013
+ (o2.error ?? console.error)("usage: sleds pickup <handoffId> (ids from `sleds handoffs`)");
5232
6014
  return 2;
5233
6015
  }
5234
- const r = await callTool2("pickup", { handoffId: id }, { home: o.home });
5235
- (r.isError ? o.error ?? console.error : o.log ?? console.log)(r.text);
6016
+ const r = await callTool2("pickup", { handoffId: id }, { home: o2.home });
6017
+ (r.isError ? o2.error ?? console.error : o2.log ?? console.log)(r.text);
5236
6018
  return r.isError ? 1 : 0;
5237
6019
  };
5238
- var withdrawCmd = async (o) => {
5239
- const id = o.argv[0];
6020
+ var withdrawCmd = async (o2) => {
6021
+ const id = o2.argv[0];
5240
6022
  if (!id) {
5241
- (o.error ?? console.error)("usage: sleds withdraw <handoffId> (one you parked)");
6023
+ (o2.error ?? console.error)("usage: sleds withdraw <handoffId> (one you parked)");
5242
6024
  return 2;
5243
6025
  }
5244
- const r = await callTool2("withdraw", { handoffId: id }, { home: o.home });
5245
- (r.isError ? o.error ?? console.error : o.log ?? console.log)(r.text);
6026
+ const r = await callTool2("withdraw", { handoffId: id }, { home: o2.home });
6027
+ (r.isError ? o2.error ?? console.error : o2.log ?? console.log)(r.text);
5246
6028
  return r.isError ? 1 : 0;
5247
6029
  };
5248
6030
 
5249
6031
  // src/commands/hooks.ts
5250
6032
  init_settings();
5251
- var hooks = (o) => {
5252
- const log = o.log ?? console.log;
5253
- const error = o.error ?? console.error;
5254
- if (!hasClaudeCode(o.home)) {
5255
- error(`Claude Code not found (${claudeDir(o.home)} does not exist). Install it, then re-run.`);
6033
+ var hooks = (o2) => {
6034
+ const log = o2.log ?? console.log;
6035
+ const error = o2.error ?? console.error;
6036
+ if (!hasClaudeCode(o2.home)) {
6037
+ error(`Claude Code not found (${claudeDir(o2.home)} does not exist). Install it, then re-run.`);
5256
6038
  return 1;
5257
6039
  }
5258
- const settings = readSettings(o.home);
6040
+ const settings = readSettings(o2.home);
5259
6041
  const had = hasSeatHook(settings);
5260
6042
  const hadSpec = hasSpecHook(settings);
5261
- const withSeat = applySeatHook(settings, o.seatEventCommand ?? sledsCommand("seat-event", o.home, o.env));
6043
+ const withSeat = applySeatHook(settings, o2.seatEventCommand ?? sledsCommand("seat-event", o2.home, o2.env));
5262
6044
  const hadPrompt = hasPromptHook(settings);
5263
6045
  const hadSession = hasSessionHook(settings);
5264
6046
  const hadStop = hasStopHook(settings);
5265
- const withPrompt = applyPromptHook(applySpecHook(withSeat, o.specEventCommand ?? sledsCommand("spec-event", o.home, o.env)), o.promptEventCommand ?? sledsCommand("prompt-event", o.home, o.env));
5266
- const withSpec = applyStopHook(applySessionHook(withPrompt, o.sessionEventCommand ?? sledsCommand("session-event", o.home, o.env)), o.stopEventCommand ?? sledsCommand("stop-event", o.home, o.env));
5267
- const hadLine = o.refreshStatusLine !== false && isSledsStatusLine(withSpec[STATUS_LINE_KEY]);
5268
- writeSettings(o.home, hadLine ? applyStatusLine(withSpec, sledsCommand("statusline", o.home, o.env)).settings : withSpec);
5269
- log(`${had ? "refreshed" : "set"} hooks.Notification (${SEAT_EVENT_MATCHER}) in ${settingsPath(o.home)}`);
6047
+ const withPrompt = applyPromptHook(applySpecHook(withSeat, o2.specEventCommand ?? sledsCommand("spec-event", o2.home, o2.env)), o2.promptEventCommand ?? sledsCommand("prompt-event", o2.home, o2.env));
6048
+ const withSpec = applyStopHook(applySessionHook(withPrompt, o2.sessionEventCommand ?? sledsCommand("session-event", o2.home, o2.env)), o2.stopEventCommand ?? sledsCommand("stop-event", o2.home, o2.env));
6049
+ const hadLine = o2.refreshStatusLine !== false && isSledsStatusLine(withSpec[STATUS_LINE_KEY]);
6050
+ writeSettings(o2.home, hadLine ? applyStatusLine(withSpec, sledsCommand("statusline", o2.home, o2.env)).settings : withSpec);
6051
+ log(`${had ? "refreshed" : "set"} hooks.Notification (${SEAT_EVENT_MATCHER}) in ${settingsPath(o2.home)}`);
5270
6052
  if (hadLine)
5271
- log(`refreshed statusLine in ${settingsPath(o.home)}`);
5272
- log(`${hadSpec ? "refreshed" : "set"} hooks.PostToolUse (${SPEC_EVENT_MATCHER} → estimate spec files) in ${settingsPath(o.home)}`);
5273
- log(`${hadPrompt ? "refreshed" : "set"} hooks.UserPromptSubmit (the seat speaks up in the session at 90 % and at a wall) in ${settingsPath(o.home)}`);
5274
- log(`${hadSession ? "refreshed" : "set"} hooks.SessionStart (the loop and the rows this machine owes, before the first turn) in ${settingsPath(o.home)}`);
5275
- log(`${hadStop ? "refreshed" : "set"} hooks.Stop (a session that estimated and never posted its actual is asked once) in ${settingsPath(o.home)}`);
6053
+ log(`refreshed statusLine in ${settingsPath(o2.home)}`);
6054
+ log(`${hadSpec ? "refreshed" : "set"} hooks.PostToolUse (${SPEC_EVENT_MATCHER} → estimate spec files) in ${settingsPath(o2.home)}`);
6055
+ log(`${hadPrompt ? "refreshed" : "set"} hooks.UserPromptSubmit (the seat speaks up in the session at 90 % and at a wall) in ${settingsPath(o2.home)}`);
6056
+ log(`${hadSession ? "refreshed" : "set"} hooks.SessionStart (the loop and the rows this machine owes, before the first turn) in ${settingsPath(o2.home)}`);
6057
+ log(`${hadStop ? "refreshed" : "set"} hooks.Stop (a session that estimated and never posted its actual is asked once) in ${settingsPath(o2.home)}`);
5276
6058
  return 0;
5277
6059
  };
5278
6060
 
@@ -5290,7 +6072,7 @@ description: The SLEDS loop for engineering work — size a build before startin
5290
6072
 
5291
6073
  # SLEDS
5292
6074
 
5293
- SLEDS is the ledger of engineering work: every piece is sized before it runs and closed with a measured actual after. In this session the seat is the person's; SLEDS holds the record. It also carries a session past a wall and runs whole tasks overnight with the Musher, a night-shift orchestrator that answers the questions an agent would otherwise stop on.
6075
+ SLEDS is where the business meets engineering: every piece of work is sized before it runs and closed with a measured actual after. In this session the seat is the person's; SLEDS holds the record. It also carries a session past a wall and runs whole tasks overnight with the Musher, a night-shift orchestrator that answers the questions an agent would otherwise stop on.
5294
6076
 
5295
6077
  ## The loop (every build, no exceptions)
5296
6078
 
@@ -5386,7 +6168,7 @@ Resume a parked job. Show the unanswered question and its options, take the answ
5386
6168
 
5387
6169
  ## /sled sync-backlog — price the backlog
5388
6170
 
5389
- With the \`sleds\` MCP server available, size every open issue so the dashboard's Projects page shows the backlog in dollars and hours, not T-shirt sizes or points. SLEDS is not a tracker and never the system of record: Linear and GitHub keep the issues; SLEDS mirrors their words (cycle, labels, priority, assignee, state) and adds the price.
6171
+ With the \`sleds\` MCP server available, size every open issue so the Work page shows the backlog in dollars and hours, not T-shirt sizes or points. SLEDS is not a tracker and never the system of record: Linear and GitHub keep the issues; SLEDS mirrors their words (cycle, labels, priority, assignee, state) and adds the price.
5390
6172
 
5391
6173
  1. Find the issues. In Cowork with a Claude Project attached: each spec doc, each phase or slice inside a spec, and each line of a backlog list (a numbered or bulleted list under a heading like Backlog, Next, To do, Roadmap). With a Linear or GitHub MCP available: the open issues of the team / repo the user names, with their cycle or milestone, labels, priority, assignee and state. Skip anything done, closed or shipped.
5392
6174
  2. For each issue call the \`estimate\` tool with \`spec\` = the title on the first line, then the body (quote the issue or doc, do not paraphrase), and \`source\` = \`{ kind: "linear" | "github" | "claude-project", project: <team, repo or Project name>, doc: <issue key like ENG-42 or #123, or the doc path>, item: <heading or line, only when a doc holds many>, url, cycle, labels, priority, assignee, state }\` — the tracker's values verbatim. The estimator dedupes: an unchanged issue returns its existing estimate, so re-running is safe.
@@ -5478,9 +6260,9 @@ function parsePoll(body) {
5478
6260
  };
5479
6261
  }
5480
6262
  function openInBrowser(url) {
5481
- const p = platform();
5482
- const cmd = p === "darwin" ? "open" : p === "win32" ? "cmd" : "xdg-open";
5483
- const args = p === "win32" ? ["/c", "start", "", url] : [url];
6263
+ const p2 = platform();
6264
+ const cmd = p2 === "darwin" ? "open" : p2 === "win32" ? "cmd" : "xdg-open";
6265
+ const args = p2 === "win32" ? ["/c", "start", "", url] : [url];
5484
6266
  try {
5485
6267
  const r = spawnSync3(cmd, args, { stdio: "ignore", timeout: 5000 });
5486
6268
  return r.error === undefined && r.status === 0;
@@ -5489,17 +6271,17 @@ function openInBrowser(url) {
5489
6271
  }
5490
6272
  }
5491
6273
  var defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
5492
- async function deviceFlow(o) {
5493
- const fetchFn = o.fetchFn ?? fetch;
5494
- const log = o.log ?? console.log;
5495
- const sleep = o.sleep ?? defaultSleep;
5496
- const now = o.now ?? Date.now;
6274
+ async function deviceFlow(o2) {
6275
+ const fetchFn = o2.fetchFn ?? fetch;
6276
+ const log = o2.log ?? console.log;
6277
+ const sleep = o2.sleep ?? defaultSleep;
6278
+ const now = o2.now ?? Date.now;
5497
6279
  let started = null;
5498
6280
  try {
5499
- const res = await fetchFn(`${o.proxyUrl}/device/start`, {
6281
+ const res = await fetchFn(`${o2.proxyUrl}/device/start`, {
5500
6282
  method: "POST",
5501
6283
  headers: { "content-type": "application/json" },
5502
- body: JSON.stringify({ agent: o.agent ?? "claude-code", host: o.host ?? hostname2() }),
6284
+ body: JSON.stringify({ agent: o2.agent ?? "claude-code", host: o2.host ?? hostname2() }),
5503
6285
  signal: AbortSignal.timeout(20000)
5504
6286
  });
5505
6287
  if (res.status === 429)
@@ -5512,7 +6294,7 @@ async function deviceFlow(o) {
5512
6294
  }
5513
6295
  if (!started)
5514
6296
  return { ok: false, reason: "bad_answer" };
5515
- const opened = o.noBrowser ? false : (o.openBrowser ?? openInBrowser)(started.verifyUrl);
6297
+ const opened = o2.noBrowser ? false : (o2.openBrowser ?? openInBrowser)(started.verifyUrl);
5516
6298
  log("");
5517
6299
  log(opened ? "Opening your browser to sign in to SLEDS." : "Open this in your browser to sign in to SLEDS:");
5518
6300
  log(` ${started.verifyUrl}`);
@@ -5526,7 +6308,7 @@ async function deviceFlow(o) {
5526
6308
  await sleep(intervalMs);
5527
6309
  let body;
5528
6310
  try {
5529
- const res = await fetchFn(`${o.proxyUrl}/device/poll`, {
6311
+ const res = await fetchFn(`${o2.proxyUrl}/device/poll`, {
5530
6312
  method: "POST",
5531
6313
  headers: { "content-type": "application/json" },
5532
6314
  body: JSON.stringify({ deviceCode: started.deviceCode }),
@@ -5554,41 +6336,41 @@ async function deviceFlow(o) {
5554
6336
 
5555
6337
  // src/commands/hooks.ts
5556
6338
  init_settings();
5557
- var hooks2 = (o) => {
5558
- const log = o.log ?? console.log;
5559
- const error = o.error ?? console.error;
5560
- if (!hasClaudeCode(o.home)) {
5561
- error(`Claude Code not found (${claudeDir(o.home)} does not exist). Install it, then re-run.`);
6339
+ var hooks2 = (o2) => {
6340
+ const log = o2.log ?? console.log;
6341
+ const error = o2.error ?? console.error;
6342
+ if (!hasClaudeCode(o2.home)) {
6343
+ error(`Claude Code not found (${claudeDir(o2.home)} does not exist). Install it, then re-run.`);
5562
6344
  return 1;
5563
6345
  }
5564
- const settings = readSettings(o.home);
6346
+ const settings = readSettings(o2.home);
5565
6347
  const had = hasSeatHook(settings);
5566
6348
  const hadSpec = hasSpecHook(settings);
5567
- const withSeat = applySeatHook(settings, o.seatEventCommand ?? sledsCommand("seat-event", o.home, o.env));
6349
+ const withSeat = applySeatHook(settings, o2.seatEventCommand ?? sledsCommand("seat-event", o2.home, o2.env));
5568
6350
  const hadPrompt = hasPromptHook(settings);
5569
6351
  const hadSession = hasSessionHook(settings);
5570
6352
  const hadStop = hasStopHook(settings);
5571
- const withPrompt = applyPromptHook(applySpecHook(withSeat, o.specEventCommand ?? sledsCommand("spec-event", o.home, o.env)), o.promptEventCommand ?? sledsCommand("prompt-event", o.home, o.env));
5572
- const withSpec = applyStopHook(applySessionHook(withPrompt, o.sessionEventCommand ?? sledsCommand("session-event", o.home, o.env)), o.stopEventCommand ?? sledsCommand("stop-event", o.home, o.env));
5573
- const hadLine = o.refreshStatusLine !== false && isSledsStatusLine(withSpec[STATUS_LINE_KEY]);
5574
- writeSettings(o.home, hadLine ? applyStatusLine(withSpec, sledsCommand("statusline", o.home, o.env)).settings : withSpec);
5575
- log(`${had ? "refreshed" : "set"} hooks.Notification (${SEAT_EVENT_MATCHER}) in ${settingsPath(o.home)}`);
6353
+ const withPrompt = applyPromptHook(applySpecHook(withSeat, o2.specEventCommand ?? sledsCommand("spec-event", o2.home, o2.env)), o2.promptEventCommand ?? sledsCommand("prompt-event", o2.home, o2.env));
6354
+ const withSpec = applyStopHook(applySessionHook(withPrompt, o2.sessionEventCommand ?? sledsCommand("session-event", o2.home, o2.env)), o2.stopEventCommand ?? sledsCommand("stop-event", o2.home, o2.env));
6355
+ const hadLine = o2.refreshStatusLine !== false && isSledsStatusLine(withSpec[STATUS_LINE_KEY]);
6356
+ writeSettings(o2.home, hadLine ? applyStatusLine(withSpec, sledsCommand("statusline", o2.home, o2.env)).settings : withSpec);
6357
+ log(`${had ? "refreshed" : "set"} hooks.Notification (${SEAT_EVENT_MATCHER}) in ${settingsPath(o2.home)}`);
5576
6358
  if (hadLine)
5577
- log(`refreshed statusLine in ${settingsPath(o.home)}`);
5578
- log(`${hadSpec ? "refreshed" : "set"} hooks.PostToolUse (${SPEC_EVENT_MATCHER} → estimate spec files) in ${settingsPath(o.home)}`);
5579
- log(`${hadPrompt ? "refreshed" : "set"} hooks.UserPromptSubmit (the seat speaks up in the session at 90 % and at a wall) in ${settingsPath(o.home)}`);
5580
- log(`${hadSession ? "refreshed" : "set"} hooks.SessionStart (the loop and the rows this machine owes, before the first turn) in ${settingsPath(o.home)}`);
5581
- log(`${hadStop ? "refreshed" : "set"} hooks.Stop (a session that estimated and never posted its actual is asked once) in ${settingsPath(o.home)}`);
6359
+ log(`refreshed statusLine in ${settingsPath(o2.home)}`);
6360
+ log(`${hadSpec ? "refreshed" : "set"} hooks.PostToolUse (${SPEC_EVENT_MATCHER} → estimate spec files) in ${settingsPath(o2.home)}`);
6361
+ log(`${hadPrompt ? "refreshed" : "set"} hooks.UserPromptSubmit (the seat speaks up in the session at 90 % and at a wall) in ${settingsPath(o2.home)}`);
6362
+ log(`${hadSession ? "refreshed" : "set"} hooks.SessionStart (the loop and the rows this machine owes, before the first turn) in ${settingsPath(o2.home)}`);
6363
+ log(`${hadStop ? "refreshed" : "set"} hooks.Stop (a session that estimated and never posted its actual is asked once) in ${settingsPath(o2.home)}`);
5582
6364
  return 0;
5583
6365
  };
5584
6366
 
5585
6367
  // src/commands/init.ts
5586
6368
  var DEFAULT_PROXY_URL = "https://proxy.sleds.ai";
5587
- var promptStdin = (question) => new Promise((resolve) => {
6369
+ var promptStdin = (question) => new Promise((resolve4) => {
5588
6370
  const rl = createInterface({ input: process.stdin, output: process.stderr });
5589
6371
  rl.question(question, (answer) => {
5590
6372
  rl.close();
5591
- resolve(answer.trim());
6373
+ resolve4(answer.trim());
5592
6374
  });
5593
6375
  });
5594
6376
  async function signupIndividual(proxyUrl, email, credential, fetchFn = fetch) {
@@ -5685,70 +6467,70 @@ async function probeProxy(url, fetchFn = fetch, opts = {}) {
5685
6467
  }
5686
6468
  return false;
5687
6469
  }
5688
- var resolveToken = async (o) => {
5689
- const env = o.env ?? process.env;
5690
- if (o.token)
5691
- return { token: o.token.trim(), source: "flag" };
6470
+ var resolveToken = async (o2) => {
6471
+ const env = o2.env ?? process.env;
6472
+ if (o2.token)
6473
+ return { token: o2.token.trim(), source: "flag" };
5692
6474
  if (env.SLEDS_TOKEN)
5693
6475
  return { token: env.SLEDS_TOKEN.trim(), source: "env" };
5694
- const fromFile = readTokenFile(o.home);
6476
+ const fromFile = readTokenFile(o2.home);
5695
6477
  if (fromFile)
5696
6478
  return { token: fromFile, source: "file" };
5697
- if (o.nonInteractive)
6479
+ if (o2.nonInteractive)
5698
6480
  return null;
5699
- const url = normalizeProxyUrl(o.url ?? env.SLEDS_PROXY_URL ?? readConfig(o.home).proxyUrl ?? DEFAULT_PROXY_URL);
6481
+ const url = normalizeProxyUrl(o2.url ?? env.SLEDS_PROXY_URL ?? readConfig(o2.home).proxyUrl ?? DEFAULT_PROXY_URL);
5700
6482
  if (!url)
5701
6483
  return null;
5702
- const log = o.log ?? console.log;
5703
- const err = o.error ?? console.error;
5704
- if (!o.signup) {
5705
- const r = await deviceFlow({
6484
+ const log = o2.log ?? console.log;
6485
+ const err = o2.error ?? console.error;
6486
+ if (!o2.signup) {
6487
+ const r2 = await deviceFlow({
5706
6488
  proxyUrl: url,
5707
6489
  agent: "claude-code",
5708
- host: o.host,
5709
- fetchFn: o.fetchFn,
5710
- openBrowser: o.openBrowser ?? openInBrowser,
5711
- noBrowser: o.noBrowser,
5712
- sleep: o.sleep,
6490
+ host: o2.host,
6491
+ fetchFn: o2.fetchFn,
6492
+ openBrowser: o2.openBrowser ?? openInBrowser,
6493
+ noBrowser: o2.noBrowser,
6494
+ sleep: o2.sleep,
5713
6495
  log
5714
6496
  });
5715
- if (!r.ok) {
5716
- err(deviceFailureText(r.reason));
6497
+ if (!r2.ok) {
6498
+ err(deviceFailureText(r2.reason));
5717
6499
  return null;
5718
6500
  }
5719
- return { token: r.approved.token, source: "device", approved: r.approved };
6501
+ return { token: r2.approved.token, source: "device", approved: r2.approved };
5720
6502
  }
5721
- if (!o.prompt)
6503
+ if (!o2.prompt)
5722
6504
  return null;
5723
- let team = o.team?.trim() || null;
5724
- if (team === null && !o.email) {
5725
- const which = (await o.prompt("Set up SLEDS for (1) just you, or (2) your team? [1/2]: ")).trim();
6505
+ let team = o2.team?.trim() || null;
6506
+ if (team === null && !o2.email) {
6507
+ const which = (await o2.prompt("Set up SLEDS for (1) just you, or (2) your team? [1/2]: ")).trim();
5726
6508
  if (which === "2") {
5727
- team = (await o.prompt("Team name (e.g. your company): ")).trim();
6509
+ team = (await o2.prompt("Team name (e.g. your company): ")).trim();
5728
6510
  if (!team)
5729
6511
  return null;
5730
6512
  }
5731
6513
  }
5732
- const email = (o.email ?? await o.prompt(team ? "Your work email: " : "Your email: ")).trim();
6514
+ const email = (o2.email ?? await o2.prompt(team ? "Your work email: " : "Your email: ")).trim();
5733
6515
  if (!email.includes("@"))
5734
6516
  return null;
5735
6517
  if (team) {
5736
- const credential = (await o.prompt("Your organization's Anthropic API key, if you have one (sk-ant-…; press enter to skip — a Team plan without one still works, walls offer hold and hand-off): ")).trim();
5737
- const r = await signupTeam(url, email, team, credential || null, o.fetchFn);
5738
- if (!r.ok) {
5739
- err(signupFailureText(r));
6518
+ const credential2 = (await o2.prompt("Your organization's Anthropic API key, if you have one (sk-ant-…; press enter to skip — a Team plan without one still works, walls offer hold and hand-off): ")).trim();
6519
+ const r2 = await signupTeam(url, email, team, credential2 || null, o2.fetchFn);
6520
+ if (!r2.ok) {
6521
+ err(signupFailureText(r2));
5740
6522
  return null;
5741
6523
  }
5742
- log(`Created ${team} with you as owner (${email}).${credential ? " Your platform key is stored encrypted as the org's upstream key." : " No platform key yet — add one on the dashboard (Admin → Keys) to unlock continue / overnight / switch at a wall."}`);
5743
- if (r.verifyPending)
6524
+ log(`Created ${team} with you as owner (${email}).${credential2 ? " Your platform key is stored encrypted as the org's upstream key." : " No platform key yet — add one on the dashboard (Admin → Keys) to unlock continue / overnight / switch at a wall."}`);
6525
+ if (r2.verifyPending)
5744
6526
  log(`Check ${email} for a verification link and click it — inviting members unlocks then (an unverified team is removed after 48 h; your own seat works meanwhile).`);
5745
6527
  log("Invite your team on the dashboard (Members → invite by email, Admin → Mint a seat); each person runs `sleds init --token …`.");
5746
- return { token: r.token, source: "signup" };
6528
+ return { token: r2.token, source: "signup" };
5747
6529
  }
5748
- const credential = (await o.prompt("Your Anthropic API key (sk-ant-…, stored encrypted, spends only your account): ")).trim();
6530
+ const credential = (await o2.prompt("Your Anthropic API key (sk-ant-…, stored encrypted, spends only your account): ")).trim();
5749
6531
  if (!credential)
5750
6532
  return null;
5751
- const r = await signupIndividual(url, email, credential, o.fetchFn);
6533
+ const r = await signupIndividual(url, email, credential, o2.fetchFn);
5752
6534
  if (!r.ok) {
5753
6535
  err(signupFailureText(r));
5754
6536
  return null;
@@ -5756,66 +6538,66 @@ var resolveToken = async (o) => {
5756
6538
  log(r.created ? `Created your individual seat (${email}). Your key is stored encrypted and is the only key this seat spends.` : `Re-issued your individual seat (${email}); the previous token is revoked.`);
5757
6539
  return { token: r.token, source: "signup" };
5758
6540
  };
5759
- var init = async (o) => {
5760
- const log = o.log ?? console.log;
5761
- const error = o.error ?? console.error;
5762
- const env = o.env ?? process.env;
5763
- if (!hasClaudeCode(o.home)) {
5764
- error(`Claude Code not found (${claudeDir(o.home)} does not exist). Install it, then re-run.`);
6541
+ var init = async (o2) => {
6542
+ const log = o2.log ?? console.log;
6543
+ const error = o2.error ?? console.error;
6544
+ const env = o2.env ?? process.env;
6545
+ if (!hasClaudeCode(o2.home)) {
6546
+ error(`Claude Code not found (${claudeDir(o2.home)} does not exist). Install it, then re-run.`);
5765
6547
  return 1;
5766
6548
  }
5767
- const resolved = await resolveToken(o);
6549
+ const resolved = await resolveToken(o2);
5768
6550
  if (!resolved) {
5769
- error(o.signup ? "Signup did not complete. Nothing was changed." : "No seat token. Run `sleds init` without --non-interactive to sign in through the browser, or pass --token / set SLEDS_TOKEN.");
6551
+ error(o2.signup ? "Signup did not complete. Nothing was changed." : "No seat token. Run `sleds init` without --non-interactive to sign in through the browser, or pass --token / set SLEDS_TOKEN.");
5770
6552
  return 2;
5771
6553
  }
5772
6554
  if (!isSeatToken(resolved.token)) {
5773
6555
  error("Invalid seat token: expected something like sled_xxxxxxxx.");
5774
6556
  return 2;
5775
6557
  }
5776
- const url = normalizeProxyUrl(o.url ?? env.SLEDS_PROXY_URL ?? readConfig(o.home).proxyUrl ?? DEFAULT_PROXY_URL);
6558
+ const url = normalizeProxyUrl(o2.url ?? env.SLEDS_PROXY_URL ?? readConfig(o2.home).proxyUrl ?? DEFAULT_PROXY_URL);
5777
6559
  if (!url) {
5778
- error(`Invalid proxy URL: ${JSON.stringify(o.url ?? env.SLEDS_PROXY_URL ?? "")}. Expected https://proxy.sleds.ai.`);
6560
+ error(`Invalid proxy URL: ${JSON.stringify(o2.url ?? env.SLEDS_PROXY_URL ?? "")}. Expected https://proxy.sleds.ai.`);
5779
6561
  return 2;
5780
6562
  }
5781
- if (!o.noProbe && !await probeProxy(url, o.fetchFn)) {
6563
+ if (!o2.noProbe && !await probeProxy(url, o2.fetchFn)) {
5782
6564
  error(`No SLEDS proxy answered at ${url}/healthz. Check the URL (or pass --no-probe to save it anyway).`);
5783
6565
  return 2;
5784
6566
  }
5785
6567
  const wrote = [];
5786
- const seatInfo = await fetchSeatInfo(url, resolved.token, o.fetchFn);
6568
+ const seatInfo = await fetchSeatInfo(url, resolved.token, o2.fetchFn);
5787
6569
  const org = seatInfo.org;
5788
- const wiring = o.proxyAlways ? "proxy-always" : o.seatFirst ? "seat-first" : org?.mode ?? "proxy-always";
6570
+ const wiring = o2.proxyAlways ? "proxy-always" : o2.seatFirst ? "seat-first" : org?.mode ?? "proxy-always";
5789
6571
  if (wiring === "proxy-always" && org && !org.capabilities.upstreamKey) {
5790
6572
  error(`${org.name} has no platform key for the proxy to forward with; --proxy-always would break every request. Using seat-first.`);
5791
6573
  }
5792
6574
  const finalWiring = wiring === "proxy-always" && org && !org.capabilities.upstreamKey ? "seat-first" : wiring;
5793
- if (backupSettings(o.home))
5794
- wrote.push(`backed up settings to ${backupPath(o.home)}`);
5795
- const current = readSettings(o.home);
6575
+ if (backupSettings(o2.home))
6576
+ wrote.push(`backed up settings to ${backupPath(o2.home)}`);
6577
+ const current = readSettings(o2.home);
5796
6578
  const wired = finalWiring === "proxy-always" ? applyProxy(current, { url, token: resolved.token }) : removeProxy(current);
5797
- const statusLine = applyStatusLine(wired, o.statusLineCommand ?? sledsCommand("statusline", o.home, o.env));
5798
- writeSettings(o.home, statusLine.settings);
5799
- wrote.push(finalWiring === "proxy-always" ? `set env.ANTHROPIC_BASE_URL and env.ANTHROPIC_AUTH_TOKEN in ${settingsPath(o.home)} (everything through the proxy)` : `left Claude Code on your seat (no ANTHROPIC_BASE_URL in ${settingsPath(o.home)}); \`sleds on\` switches to the proxy at a wall`);
5800
- wrote.push(statusLine.prior === undefined ? `set statusLine in ${settingsPath(o.home)}` : `set statusLine in ${settingsPath(o.home)} (previous one kept for uninstall)`);
6579
+ const statusLine2 = applyStatusLine(wired, o2.statusLineCommand ?? sledsCommand("statusline", o2.home, o2.env));
6580
+ writeSettings(o2.home, statusLine2.settings);
6581
+ wrote.push(finalWiring === "proxy-always" ? `set env.ANTHROPIC_BASE_URL and env.ANTHROPIC_AUTH_TOKEN in ${settingsPath(o2.home)} (everything through the proxy)` : `left Claude Code on your seat (no ANTHROPIC_BASE_URL in ${settingsPath(o2.home)}); \`sleds on\` switches to the proxy at a wall`);
6582
+ wrote.push(statusLine2.prior === undefined ? `set statusLine in ${settingsPath(o2.home)}` : `set statusLine in ${settingsPath(o2.home)} (previous one kept for uninstall)`);
5801
6583
  hooks2({
5802
- home: o.home,
5803
- env: o.env,
6584
+ home: o2.home,
6585
+ env: o2.env,
5804
6586
  refreshStatusLine: false,
5805
- seatEventCommand: o.seatEventCommand,
5806
- specEventCommand: o.specEventCommand,
5807
- promptEventCommand: o.promptEventCommand,
5808
- sessionEventCommand: o.sessionEventCommand,
5809
- stopEventCommand: o.stopEventCommand,
6587
+ seatEventCommand: o2.seatEventCommand,
6588
+ specEventCommand: o2.specEventCommand,
6589
+ promptEventCommand: o2.promptEventCommand,
6590
+ sessionEventCommand: o2.sessionEventCommand,
6591
+ stopEventCommand: o2.stopEventCommand,
5810
6592
  log: (line) => wrote.push(line),
5811
6593
  error
5812
6594
  });
5813
6595
  if (resolved.source !== "file") {
5814
- writeTokenFile(o.home, resolved.token);
5815
- wrote.push(`saved token to ${tokenPath(o.home)} (0600)`);
6596
+ writeTokenFile(o2.home, resolved.token);
6597
+ wrote.push(`saved token to ${tokenPath(o2.home)} (0600)`);
5816
6598
  }
5817
- const prior = readConfig(o.home);
5818
- writeConfig(o.home, {
6599
+ const prior = readConfig(o2.home);
6600
+ writeConfig(o2.home, {
5819
6601
  ...prior,
5820
6602
  token: resolved.token,
5821
6603
  proxyUrl: url,
@@ -5823,14 +6605,14 @@ var init = async (o) => {
5823
6605
  agentKind: "claude-code",
5824
6606
  ...seatInfo.email ? { email: seatInfo.email } : {},
5825
6607
  ...org ? { org } : {},
5826
- ...statusLine.prior !== undefined ? { priorStatusLine: statusLine.prior } : {}
6608
+ ...statusLine2.prior !== undefined ? { priorStatusLine: statusLine2.prior } : {}
5827
6609
  });
5828
- wrote.push(`saved token and proxy URL to ${configPath(o.home)} (0600)`);
5829
- mkdirSync8(skillDir(o.home), { recursive: true });
5830
- writeFileSync8(skillPath(o.home), SKILL_MD);
5831
- wrote.push(`installed skill at ${skillPath(o.home)}`);
5832
- if (o.installMusher) {
5833
- const r = o.installMusher();
6610
+ wrote.push(`saved token and proxy URL to ${configPath(o2.home)} (0600)`);
6611
+ mkdirSync8(skillDir(o2.home), { recursive: true });
6612
+ writeFileSync8(skillPath(o2.home), SKILL_MD);
6613
+ wrote.push(`installed skill at ${skillPath(o2.home)}`);
6614
+ if (o2.installMusher) {
6615
+ const r = o2.installMusher();
5834
6616
  if (r)
5835
6617
  wrote.push(r);
5836
6618
  }
@@ -5867,9 +6649,9 @@ var init = async (o) => {
5867
6649
  };
5868
6650
 
5869
6651
  // src/commands/mcp.ts
5870
- var mcp = async (o) => {
5871
- const stdin = o.stdin ?? process.stdin;
5872
- const write = o.write ?? ((line) => process.stdout.write(`${line}
6652
+ var mcp = async (o2) => {
6653
+ const stdin = o2.stdin ?? process.stdin;
6654
+ const write = o2.write ?? ((line) => process.stdout.write(`${line}
5873
6655
  `));
5874
6656
  let buffer = "";
5875
6657
  for await (const chunk of stdin) {
@@ -5879,7 +6661,7 @@ var mcp = async (o) => {
5879
6661
  while (nl !== -1) {
5880
6662
  const line = buffer.slice(0, nl);
5881
6663
  buffer = buffer.slice(nl + 1);
5882
- const out = await handleLine2(line, { home: o.home });
6664
+ const out = await handleLine2(line, { home: o2.home });
5883
6665
  if (out !== null)
5884
6666
  write(out);
5885
6667
  nl = buffer.indexOf(`
@@ -5887,7 +6669,7 @@ var mcp = async (o) => {
5887
6669
  }
5888
6670
  }
5889
6671
  if (buffer.trim().length > 0) {
5890
- const out = await handleLine2(buffer, { home: o.home });
6672
+ const out = await handleLine2(buffer, { home: o2.home });
5891
6673
  if (out !== null)
5892
6674
  write(out);
5893
6675
  }
@@ -5897,15 +6679,15 @@ var mcp = async (o) => {
5897
6679
  // src/commands/meter.ts
5898
6680
  init_config();
5899
6681
  init_meter();
5900
- var meter = async (o) => {
5901
- const log = o.log ?? console.log;
5902
- const now = (o.now ?? Date.now)();
5903
- const src = meterSource(o.home);
6682
+ var meter = async (o2) => {
6683
+ const log = o2.log ?? console.log;
6684
+ const now = (o2.now ?? Date.now)();
6685
+ const src = meterSource(o2.home);
5904
6686
  if (!src) {
5905
- log(`SLEDS is not set up (${configPath(o.home)}). Run \`sleds init\` first.`);
6687
+ log(`SLEDS is not set up (${configPath(o2.home)}). Run \`sleds init\` first.`);
5906
6688
  return 1;
5907
6689
  }
5908
- const m = await loadMeter({ home: o.home, now, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs });
6690
+ const m = await loadMeter({ home: o2.home, now, fetchFn: o2.fetchFn, timeoutMs: o2.timeoutMs });
5909
6691
  if (!m) {
5910
6692
  log(`sleds offline. ${src.proxyUrl} did not answer → \`sleds status\` for the proxy health.`);
5911
6693
  return 2;
@@ -5925,9 +6707,9 @@ import { dirname as dirname5 } from "node:path";
5925
6707
  var defaultWhich3 = (cmd) => {
5926
6708
  const path = process.env.PATH ?? "";
5927
6709
  for (const dir of path.split(":").filter(Boolean)) {
5928
- const p = `${dir}/${cmd}`;
5929
- if (existsSync10(p))
5930
- return p;
6710
+ const p2 = `${dir}/${cmd}`;
6711
+ if (existsSync10(p2))
6712
+ return p2;
5931
6713
  }
5932
6714
  return null;
5933
6715
  };
@@ -5937,68 +6719,68 @@ var findClaude2 = (home, configured, which) => {
5937
6719
  const found = which("claude");
5938
6720
  if (found)
5939
6721
  return found;
5940
- for (const p of [
6722
+ for (const p2 of [
5941
6723
  `${home}/.local/bin/claude`,
5942
6724
  "/opt/homebrew/bin/claude",
5943
6725
  "/usr/local/bin/claude",
5944
6726
  `${home}/.npm-global/bin/claude`
5945
6727
  ])
5946
- if (existsSync10(p))
5947
- return p;
6728
+ if (existsSync10(p2))
6729
+ return p2;
5948
6730
  return null;
5949
6731
  };
5950
- var musherInstall = (o) => {
5951
- const log = o.log ?? console.log;
5952
- const error = o.error ?? console.error;
5953
- const platform = o.platform ?? process.platform;
5954
- if (platform !== "darwin") {
5955
- error(`sleds musher --install needs launchd (macOS). On ${platform}, run \`sleds musher tick\` from cron every 5 minutes.`);
6732
+ var musherInstall = (o2) => {
6733
+ const log = o2.log ?? console.log;
6734
+ const error = o2.error ?? console.error;
6735
+ const platform2 = o2.platform ?? process.platform;
6736
+ if (platform2 !== "darwin") {
6737
+ error(`sleds musher --install needs launchd (macOS). On ${platform2}, run \`sleds musher tick\` from cron every 5 minutes.`);
5956
6738
  return 1;
5957
6739
  }
5958
- const env = o.env ?? process.env;
5959
- const plist = musherPlistPath(o.home, env);
6740
+ const env = o2.env ?? process.env;
6741
+ const plist = musherPlistPath(o2.home, env);
5960
6742
  const label = musherLabel(env);
5961
- const dir = configDir(o.home, env);
6743
+ const dir = configDir(o2.home, env);
5962
6744
  mkdirSync9(dirname5(plist), { recursive: true });
5963
6745
  mkdirSync9(dir, { recursive: true, mode: 448 });
5964
- const execPath = o.sledsArgv[0] ?? process.execPath;
6746
+ const execPath = o2.sledsArgv[0] ?? process.execPath;
5965
6747
  const agentEnv = {
5966
- PATH: musherPath(o.home, execPath, env.PATH ?? ""),
5967
- HOME: o.home,
6748
+ PATH: musherPath(o2.home, execPath, env.PATH ?? ""),
6749
+ HOME: o2.home,
5968
6750
  ...env.SLEDS_HOME ? { SLEDS_HOME: env.SLEDS_HOME } : {},
5969
6751
  ...env.CLAUDE_CONFIG_DIR ? { CLAUDE_CONFIG_DIR: env.CLAUDE_CONFIG_DIR } : {}
5970
6752
  };
5971
- const exec = o.exec ?? defaultExecSync;
6753
+ const exec = o2.exec ?? defaultExecSync;
5972
6754
  if (existsSync10(plist))
5973
6755
  exec("launchctl", ["unload", plist]);
5974
- writeFileSync9(plist, buildMusherPlist({ sledsArgv: o.sledsArgv, env: agentEnv, logPath: musherLogPath(dir), label }));
6756
+ writeFileSync9(plist, buildMusherPlist({ sledsArgv: o2.sledsArgv, env: agentEnv, logPath: musherLogPath(dir), label }));
5975
6757
  const r = exec("launchctl", ["load", plist]);
5976
6758
  if (!r.ok) {
5977
6759
  error(`launchctl load failed: ${r.stderr.trim() || "unknown"}`);
5978
6760
  return 1;
5979
6761
  }
5980
- const cfg = readConfig(o.home);
5981
- writeConfig(o.home, { ...cfg, musher: { ...cfg.musher ?? {}, agent: "launchd" } });
5982
- const claude = findClaude2(o.home, cfg.musher?.claudePath, o.which ?? defaultWhich3);
6762
+ const cfg = readConfig(o2.home);
6763
+ writeConfig(o2.home, { ...cfg, musher: { ...cfg.musher ?? {}, agent: "launchd" } });
6764
+ const claude = findClaude2(o2.home, cfg.musher?.claudePath, o2.which ?? defaultWhich3);
5983
6765
  log(`musher installed: launchd agent ${label}, every 5 min · log ${musherLogPath(dir)}`);
5984
6766
  if (!claude)
5985
6767
  log("warn: `claude` not found on PATH — set it with `sleds musher --claude /path/to/claude`");
5986
6768
  return 0;
5987
6769
  };
5988
- var musherUninstall = (o) => {
5989
- const log = o.log ?? console.log;
5990
- const env = o.env ?? process.env;
5991
- const plist = musherPlistPath(o.home, env);
5992
- const exec = o.exec ?? defaultExecSync;
6770
+ var musherUninstall = (o2) => {
6771
+ const log = o2.log ?? console.log;
6772
+ const env = o2.env ?? process.env;
6773
+ const plist = musherPlistPath(o2.home, env);
6774
+ const exec = o2.exec ?? defaultExecSync;
5993
6775
  if (existsSync10(plist)) {
5994
6776
  exec("launchctl", ["unload", plist]);
5995
6777
  rmSync2(plist, { force: true });
5996
6778
  log(`musher removed: ${musherLabel(env)}`);
5997
6779
  } else
5998
6780
  log("musher was not installed.");
5999
- const cfg = readConfig(o.home);
6781
+ const cfg = readConfig(o2.home);
6000
6782
  if (cfg.musher)
6001
- writeConfig(o.home, { ...cfg, musher: { ...cfg.musher, agent: "none" } });
6783
+ writeConfig(o2.home, { ...cfg, musher: { ...cfg.musher, agent: "none" } });
6002
6784
  return 0;
6003
6785
  };
6004
6786
  var formatMusherStatus = (list, now) => {
@@ -6013,23 +6795,23 @@ var formatMusherStatus = (list, now) => {
6013
6795
  lines.push("nothing deferred on this seat.");
6014
6796
  return lines;
6015
6797
  };
6016
- var clientFor = (o) => {
6017
- if (o.client)
6018
- return o.client;
6019
- const src = meterSource(o.home);
6020
- return src ? musherClient({ ...src, fetchFn: o.fetchFn }) : null;
6021
- };
6022
- var musherStatus = async (o) => {
6023
- const log = o.log ?? console.log;
6024
- const now = (o.now ?? Date.now)();
6025
- const env = o.env ?? process.env;
6026
- const plist = musherPlistPath(o.home, env);
6027
- const cfg = readConfig(o.home);
6798
+ var clientFor = (o2) => {
6799
+ if (o2.client)
6800
+ return o2.client;
6801
+ const src = meterSource(o2.home);
6802
+ return src ? musherClient({ ...src, fetchFn: o2.fetchFn }) : null;
6803
+ };
6804
+ var musherStatus = async (o2) => {
6805
+ const log = o2.log ?? console.log;
6806
+ const now = (o2.now ?? Date.now)();
6807
+ const env = o2.env ?? process.env;
6808
+ const plist = musherPlistPath(o2.home, env);
6809
+ const cfg = readConfig(o2.home);
6028
6810
  log(existsSync10(plist) ? `agent ${musherLabel(env)} installed (${plist})` : "agent not installed — sleds musher --install");
6029
- const claude = findClaude2(o.home, cfg.musher?.claudePath, o.which ?? defaultWhich3);
6811
+ const claude = findClaude2(o2.home, cfg.musher?.claudePath, o2.which ?? defaultWhich3);
6030
6812
  log(`claude ${claude ?? "not found — sleds musher --claude /path/to/claude"}`);
6031
6813
  log(`mode ${cfg.musher?.permissionMode ?? DEFAULT_PERMISSION_MODE} · timeout ${cfg.musher?.timeoutMinutes ?? DEFAULT_TIMEOUT_MINUTES} min`);
6032
- const client = clientFor(o);
6814
+ const client = clientFor(o2);
6033
6815
  if (!client) {
6034
6816
  log("seat not initialised (sleds init)");
6035
6817
  return 1;
@@ -6043,9 +6825,9 @@ var musherStatus = async (o) => {
6043
6825
  log(line);
6044
6826
  return 0;
6045
6827
  };
6046
- var musherTick = async (o) => {
6047
- const log = o.log ?? console.log;
6048
- const client = clientFor(o);
6828
+ var musherTick = async (o2) => {
6829
+ const log = o2.log ?? console.log;
6830
+ const client = clientFor(o2);
6049
6831
  if (!client) {
6050
6832
  log("musher: seat not initialised; nothing to do");
6051
6833
  return 1;
@@ -6057,19 +6839,19 @@ var musherTick = async (o) => {
6057
6839
  }
6058
6840
  if (list.due.length === 0)
6059
6841
  return 0;
6060
- const cfg = readConfig(o.home);
6061
- const claude = findClaude2(o.home, cfg.musher?.claudePath, o.which ?? defaultWhich3) ?? "claude";
6062
- const seat = readSeatState(o.home);
6842
+ const cfg = readConfig(o2.home);
6843
+ const claude = findClaude2(o2.home, cfg.musher?.claudePath, o2.which ?? defaultWhich3) ?? "claude";
6844
+ const seat = readSeatState(o2.home);
6063
6845
  for (const job of list.due) {
6064
6846
  const out = await runJob({
6065
6847
  job,
6066
6848
  client,
6067
6849
  fallbackSession: seat.session,
6068
- host: o.host ?? hostname3(),
6850
+ host: o2.host ?? hostname3(),
6069
6851
  claudePath: claude,
6070
6852
  permissionMode: cfg.musher?.permissionMode ?? DEFAULT_PERMISSION_MODE,
6071
6853
  timeoutMs: (cfg.musher?.timeoutMinutes ?? DEFAULT_TIMEOUT_MINUTES) * 60000,
6072
- exec: o.execCapture,
6854
+ exec: o2.execCapture,
6073
6855
  log
6074
6856
  });
6075
6857
  if (out.kind === "skipped")
@@ -6077,9 +6859,9 @@ var musherTick = async (o) => {
6077
6859
  }
6078
6860
  return 0;
6079
6861
  };
6080
- var musherConfigure = (o, flags) => {
6081
- const log = o.log ?? console.log;
6082
- const cfg = readConfig(o.home);
6862
+ var musherConfigure = (o2, flags) => {
6863
+ const log = o2.log ?? console.log;
6864
+ const cfg = readConfig(o2.home);
6083
6865
  const musher = { ...cfg.musher ?? {} };
6084
6866
  if (flags.claude)
6085
6867
  musher.claudePath = flags.claude;
@@ -6088,12 +6870,12 @@ var musherConfigure = (o, flags) => {
6088
6870
  if (flags.timeout) {
6089
6871
  const n = Number.parseInt(flags.timeout, 10);
6090
6872
  if (!Number.isFinite(n) || n <= 0) {
6091
- (o.error ?? console.error)("--timeout takes minutes, a positive number");
6873
+ (o2.error ?? console.error)("--timeout takes minutes, a positive number");
6092
6874
  return 1;
6093
6875
  }
6094
6876
  musher.timeoutMinutes = n;
6095
6877
  }
6096
- writeConfig(o.home, { ...cfg, musher });
6878
+ writeConfig(o2.home, { ...cfg, musher });
6097
6879
  log(`musher settings: claude ${musher.claudePath ?? "(PATH)"} · mode ${musher.permissionMode ?? DEFAULT_PERMISSION_MODE} · timeout ${musher.timeoutMinutes ?? DEFAULT_TIMEOUT_MINUTES} min`);
6098
6880
  return 0;
6099
6881
  };
@@ -6103,8 +6885,8 @@ var defaultExecSync = (cmd, args) => {
6103
6885
  return { ok: false, stderr: r.error.message };
6104
6886
  return { ok: r.status === 0, stderr: r.stderr ?? "" };
6105
6887
  };
6106
- var musher = async (o) => {
6107
- const args = [...o.argv];
6888
+ var musher = async (o2) => {
6889
+ const args = [...o2.argv];
6108
6890
  const flags = {};
6109
6891
  const positional = [];
6110
6892
  for (let i = 0;i < args.length; i++) {
@@ -6120,16 +6902,16 @@ var musher = async (o) => {
6120
6902
  positional.push(a);
6121
6903
  }
6122
6904
  if (flags.install)
6123
- return musherInstall(o);
6905
+ return musherInstall(o2);
6124
6906
  if (flags.uninstall)
6125
- return musherUninstall(o);
6907
+ return musherUninstall(o2);
6126
6908
  if (flags.claude || flags.mode || flags.timeout)
6127
- return musherConfigure(o, flags);
6909
+ return musherConfigure(o2, flags);
6128
6910
  if (positional[0] === "tick")
6129
- return musherTick(o);
6911
+ return musherTick(o2);
6130
6912
  if (flags.status || positional.length === 0)
6131
- return musherStatus(o);
6132
- (o.error ?? console.error)(`sleds musher: unknown argument ${positional[0]}`);
6913
+ return musherStatus(o2);
6914
+ (o2.error ?? console.error)(`sleds musher: unknown argument ${positional[0]}`);
6133
6915
  return 1;
6134
6916
  };
6135
6917
 
@@ -6177,12 +6959,12 @@ var systemdCalendar = (deadline) => `${deadline.getFullYear()}-${pad2(deadline.g
6177
6959
  var scheduleRevert = (d, deadlineMs) => {
6178
6960
  const deadline = new Date(deadlineMs);
6179
6961
  if (d.platform === "darwin") {
6180
- const p = plistPath(d.home);
6962
+ const p2 = plistPath(d.home);
6181
6963
  try {
6182
6964
  mkdirSync10(join9(d.home, "Library", "LaunchAgents"), { recursive: true });
6183
- d.exec("launchctl", ["unload", p]);
6184
- writeFileSync10(p, buildLaunchdPlist(d.sledsArgv, deadline));
6185
- const r = d.exec("launchctl", ["load", p]);
6965
+ d.exec("launchctl", ["unload", p2]);
6966
+ writeFileSync10(p2, buildLaunchdPlist(d.sledsArgv, deadline));
6967
+ const r = d.exec("launchctl", ["load", p2]);
6186
6968
  if (r.ok)
6187
6969
  return { kind: "launchd", detail: `launchd agent ${REVERT_LABEL}` };
6188
6970
  return { kind: "manual", detail: `launchctl load failed: ${r.stderr.trim() || "unknown"}` };
@@ -6207,10 +6989,10 @@ var scheduleRevert = (d, deadlineMs) => {
6207
6989
  };
6208
6990
  var cancelRevert = (d, kind) => {
6209
6991
  if (kind === "launchd" || d.platform === "darwin") {
6210
- const p = plistPath(d.home);
6211
- if (existsSync11(p)) {
6212
- d.exec("launchctl", ["unload", p]);
6213
- rmSync3(p, { force: true });
6992
+ const p2 = plistPath(d.home);
6993
+ if (existsSync11(p2)) {
6994
+ d.exec("launchctl", ["unload", p2]);
6995
+ rmSync3(p2, { force: true });
6214
6996
  }
6215
6997
  }
6216
6998
  if (kind === "systemd" || d.platform === "linux") {
@@ -6220,15 +7002,15 @@ var cancelRevert = (d, kind) => {
6220
7002
 
6221
7003
  // src/commands/off.ts
6222
7004
  init_settings();
6223
- var off = (o) => {
6224
- const log = o.log ?? console.log;
6225
- const cfg = readConfig(o.home);
6226
- const settings = readSettings(o.home);
7005
+ var off = (o2) => {
7006
+ const log = o2.log ?? console.log;
7007
+ const cfg = readConfig(o2.home);
7008
+ const settings = readSettings(o2.home);
6227
7009
  const wasOn = cfg.mode === "on" || isPointedAtProxy(settings);
6228
7010
  if (wasOn)
6229
- writeSettings(o.home, switchOff(settings, cfg.priorEnv ?? {}));
6230
- if (o.scheduler)
6231
- cancelRevert(o.scheduler, cfg.schedule);
7011
+ writeSettings(o2.home, switchOff(settings, cfg.priorEnv ?? {}));
7012
+ if (o2.scheduler)
7013
+ cancelRevert(o2.scheduler, cfg.schedule);
6232
7014
  const next = {
6233
7015
  ...cfg,
6234
7016
  mode: "off",
@@ -6238,7 +7020,7 @@ var off = (o) => {
6238
7020
  priorEnv: undefined,
6239
7021
  schedule: undefined
6240
7022
  };
6241
- writeConfig(o.home, next);
7023
+ writeConfig(o2.home, next);
6242
7024
  if (!wasOn) {
6243
7025
  log("Sleds already off. Claude Code is on your seat.");
6244
7026
  return 0;
@@ -6257,19 +7039,19 @@ init_settings();
6257
7039
  var DEFAULT_PROXY_URL2 = "https://proxy.sleds.ai";
6258
7040
 
6259
7041
  // src/commands/on.ts
6260
- var on = (o) => {
6261
- const log = o.log ?? console.log;
6262
- const error = o.error ?? console.error;
6263
- const env = o.env ?? process.env;
6264
- const now = (o.now ?? Date.now)();
6265
- if (!hasClaudeCode(o.home)) {
6266
- error(`Claude Code not found (${claudeDir(o.home)} does not exist). Install it, then re-run.`);
7042
+ var on = (o2) => {
7043
+ const log = o2.log ?? console.log;
7044
+ const error = o2.error ?? console.error;
7045
+ const env = o2.env ?? process.env;
7046
+ const now = (o2.now ?? Date.now)();
7047
+ if (!hasClaudeCode(o2.home)) {
7048
+ error(`Claude Code not found (${claudeDir(o2.home)} does not exist). Install it, then re-run.`);
6267
7049
  return 1;
6268
7050
  }
6269
- const cfg = readConfig(o.home);
6270
- const token = cfg.token ?? readTokenFile(o.home) ?? env.SLEDS_TOKEN?.trim();
7051
+ const cfg = readConfig(o2.home);
7052
+ const token = cfg.token ?? readTokenFile(o2.home) ?? env.SLEDS_TOKEN?.trim();
6271
7053
  if (!token) {
6272
- error(`No seat token in ${configPath(o.home)}. Run \`sleds init\` first.`);
7054
+ error(`No seat token in ${configPath(o2.home)}. Run \`sleds init\` first.`);
6273
7055
  return 2;
6274
7056
  }
6275
7057
  if (!isSeatToken(token)) {
@@ -6280,28 +7062,28 @@ var on = (o) => {
6280
7062
  error(`${cfg.org.name} has no platform key, so there is no platform key to switch to. Hold until the reset, or hand off with /sled 5. An admin can add a key on the dashboard.`);
6281
7063
  return 2;
6282
7064
  }
6283
- const url = (o.url ?? cfg.proxyUrl ?? env.SLEDS_PROXY_URL ?? DEFAULT_PROXY_URL2).replace(/\/+$/, "");
7065
+ const url = (o2.url ?? cfg.proxyUrl ?? env.SLEDS_PROXY_URL ?? DEFAULT_PROXY_URL2).replace(/\/+$/, "");
6284
7066
  let untilResetMs;
6285
- if (o.untilReset !== undefined) {
7067
+ if (o2.untilReset !== undefined) {
6286
7068
  try {
6287
- untilResetMs = parseUntilReset(o.untilReset, now);
7069
+ untilResetMs = parseUntilReset(o2.untilReset, now);
6288
7070
  } catch (err) {
6289
7071
  error(err instanceof Error ? err.message : String(err));
6290
7072
  return 2;
6291
7073
  }
6292
7074
  }
6293
- const settings = readSettings(o.home);
7075
+ const settings = readSettings(o2.home);
6294
7076
  const priorEnv = cfg.mode === "on" && cfg.priorEnv ? cfg.priorEnv : capturePriorEnv(settings);
6295
- const backedUp = backupSettings(o.home);
6296
- writeSettings(o.home, switchOn(settings, { url, token }));
7077
+ const backedUp = backupSettings(o2.home);
7078
+ writeSettings(o2.home, switchOn(settings, { url, token }));
6297
7079
  let schedule;
6298
7080
  let scheduleNote;
6299
- if (untilResetMs !== undefined && !o.noSchedule && o.scheduler && hasHomeOverride(env)) {
7081
+ if (untilResetMs !== undefined && !o2.noSchedule && o2.scheduler && hasHomeOverride(env)) {
6300
7082
  error("SLEDS_HOME / CLAUDE_CONFIG_DIR is set: the auto-revert timer would act on the default seat. Re-run with --no-schedule and run `sleds off` yourself at the reset.");
6301
7083
  return 2;
6302
7084
  }
6303
- if (untilResetMs !== undefined && !o.noSchedule && o.scheduler) {
6304
- const r = scheduleRevert(o.scheduler, untilResetMs);
7085
+ if (untilResetMs !== undefined && !o2.noSchedule && o2.scheduler) {
7086
+ const r = scheduleRevert(o2.scheduler, untilResetMs);
6305
7087
  schedule = r.kind;
6306
7088
  scheduleNote = r.kind === "manual" ? r.detail : undefined;
6307
7089
  }
@@ -6317,19 +7099,19 @@ var on = (o) => {
6317
7099
  next.untilReset = new Date(untilResetMs).toISOString();
6318
7100
  else
6319
7101
  next.untilReset = undefined;
6320
- next.reason = o.reason?.trim() || undefined;
7102
+ next.reason = o2.reason?.trim() || undefined;
6321
7103
  next.schedule = schedule;
6322
- writeConfig(o.home, next);
7104
+ writeConfig(o2.home, next);
6323
7105
  log(`Sleds on. Claude Code now runs on the platform key through ${url}. Restart claude to apply.`);
6324
7106
  if (untilResetMs !== undefined) {
6325
- log(`Reverts at ${formatLocal(untilResetMs, o.tz)} — run \`sleds off\` earlier if you want.`);
7107
+ log(`Reverts at ${formatLocal(untilResetMs, o2.tz)} — run \`sleds off\` earlier if you want.`);
6326
7108
  if (schedule === undefined || schedule === "manual") {
6327
7109
  const why = scheduleNote ? ` (${scheduleNote})` : "";
6328
7110
  log(`Auto-revert not scheduled${why}. Run \`sleds off\` after the reset.`);
6329
7111
  }
6330
7112
  }
6331
7113
  if (backedUp)
6332
- log(`Backed up ${settingsPath(o.home)} to ${backupPath(o.home)}.`);
7114
+ log(`Backed up ${settingsPath(o2.home)} to ${backupPath(o2.home)}.`);
6333
7115
  return 0;
6334
7116
  };
6335
7117
 
@@ -6377,22 +7159,22 @@ var exitsLine = (hasUpstreamKey) => hasUpstreamKey ? "`/sled handoff <what you w
6377
7159
  var approachingContext = (bar, usedPct, resetsAt, hasUpstreamKey, now) => `[SLEDS] This seat is at ${usedPct !== undefined ? `${Math.round(usedPct)}%` : "90%+"} of its ${LABEL[bar]} limit; it resets in ${fmtIn(resetsAt - now)}. Tell the user in one short line before you answer, and offer the exits: ${exitsLine(hasUpstreamKey)} Do not mention this again unless asked.`;
6378
7160
  var hitContext = (bar, resetsAt, hasUpstreamKey, now) => `[SLEDS] This seat hit its ${LABEL[bar]} limit; it resets in ${fmtIn(resetsAt - now)}. If this reply fails or the user is stuck, the exits are: ${exitsLine(hasUpstreamKey)} Say so in one line.`;
6379
7161
  var creditsBlockText = (hasUpstreamKey) => `SLEDS: this seat is out of usage credits — the next reply would fail, so this prompt was not sent. ${hasUpstreamKey ? "Exits: /sled handoff <task> (a buddy picks it up), sleds on (continue on the workspace's platform key), or /model to a cheaper model." : "Exits: /sled handoff <task> (a buddy picks it up), sleds defer (resume when credits are topped up), or /model to a cheaper model."} Re-send your prompt after choosing; SLEDS will not block again.`;
6380
- var promptEvent = async (o) => {
6381
- const log = o.log ?? console.log;
6382
- const error = o.error ?? console.error;
6383
- const now = (o.now ?? Date.now)();
6384
- if (o.stdin === undefined)
7162
+ var promptEvent = async (o2) => {
7163
+ const log = o2.log ?? console.log;
7164
+ const error = o2.error ?? console.error;
7165
+ const now = (o2.now ?? Date.now)();
7166
+ if (o2.stdin === undefined)
6385
7167
  await readStdin(HOOK_STDIN_TIMEOUT_MS);
6386
7168
  let state;
6387
7169
  try {
6388
- state = readSeatState(o.home);
7170
+ state = readSeatState(o2.home);
6389
7171
  } catch {
6390
7172
  return 0;
6391
7173
  }
6392
7174
  const pending = untold(state);
6393
7175
  if (pending.length === 0)
6394
7176
  return 0;
6395
- const hasKey = readConfig(o.home).org?.capabilities.upstreamKey !== false;
7177
+ const hasKey = readConfig(o2.home).org?.capabilities.upstreamKey !== false;
6396
7178
  const told = { ...state.told ?? {} };
6397
7179
  const mark = (bar, stage, at) => {
6398
7180
  told[bar] = { ...told[bar] ?? {}, [stage]: at };
@@ -6412,7 +7194,7 @@ var promptEvent = async (o) => {
6412
7194
  lines.push(e.stage === "approaching" ? approachingContext(e.bar, pct, e.resetsAt, hasKey, now) : hitContext(e.bar, e.resetsAt, hasKey, now));
6413
7195
  }
6414
7196
  try {
6415
- writeSeatState(o.home, { ...state, told });
7197
+ writeSeatState(o2.home, { ...state, told });
6416
7198
  } catch {}
6417
7199
  if (code === 0 && lines.length > 0)
6418
7200
  log(lines.join(`
@@ -6424,16 +7206,16 @@ var promptEvent = async (o) => {
6424
7206
  init_config();
6425
7207
  init_meter();
6426
7208
  import { hostname as hostname4 } from "node:os";
6427
- var run = async (o) => {
6428
- const log = o.log ?? console.log;
6429
- const error = o.error ?? console.error;
6430
- if (!o.jobId) {
7209
+ var run = async (o2) => {
7210
+ const log = o2.log ?? console.log;
7211
+ const error = o2.error ?? console.error;
7212
+ if (!o2.jobId) {
6431
7213
  error("usage: sleds run <jobId>");
6432
7214
  return 2;
6433
7215
  }
6434
- const client = o.client ?? (() => {
6435
- const src = meterSource(o.home);
6436
- return src ? musherClient({ ...src, fetchFn: o.fetchFn }) : null;
7216
+ const client = o2.client ?? (() => {
7217
+ const src = meterSource(o2.home);
7218
+ return src ? musherClient({ ...src, fetchFn: o2.fetchFn }) : null;
6437
7219
  })();
6438
7220
  if (!client) {
6439
7221
  error("No seat on this machine. Run `sleds init` first.");
@@ -6444,13 +7226,13 @@ var run = async (o) => {
6444
7226
  error("Could not reach the SLEDS proxy.");
6445
7227
  return 1;
6446
7228
  }
6447
- const job = [...list.due, ...list.upcoming].find((j) => j.jobId === o.jobId);
7229
+ const job = [...list.due, ...list.upcoming].find((j) => j.jobId === o2.jobId);
6448
7230
  if (!job) {
6449
- error(`Job ${o.jobId} is not a scheduled local job on this seat (sleds musher --status lists them).`);
7231
+ error(`Job ${o2.jobId} is not a scheduled local job on this seat (sleds musher --status lists them).`);
6450
7232
  return 1;
6451
7233
  }
6452
- const cfg = readConfig(o.home);
6453
- const claude = findClaude(o.home, cfg.musher?.claudePath, o.which ?? defaultWhich);
7234
+ const cfg = readConfig(o2.home);
7235
+ const claude = findClaude(o2.home, cfg.musher?.claudePath, o2.which ?? defaultWhich);
6454
7236
  if (!claude) {
6455
7237
  error("`claude` not found. Install Claude Code, or `sleds musher --claude /path/to/claude`.");
6456
7238
  return 1;
@@ -6458,12 +7240,12 @@ var run = async (o) => {
6458
7240
  const out = await runJob({
6459
7241
  job,
6460
7242
  client,
6461
- fallbackSession: readSeatState(o.home).session,
6462
- host: o.host ?? hostname4(),
7243
+ fallbackSession: readSeatState(o2.home).session,
7244
+ host: o2.host ?? hostname4(),
6463
7245
  claudePath: claude,
6464
7246
  permissionMode: cfg.musher?.permissionMode ?? DEFAULT_PERMISSION_MODE,
6465
7247
  timeoutMs: (cfg.musher?.timeoutMinutes ?? DEFAULT_TIMEOUT_MINUTES) * 60000,
6466
- exec: o.execCapture,
7248
+ exec: o2.execCapture,
6467
7249
  log
6468
7250
  });
6469
7251
  if (out.kind === "skipped") {
@@ -6496,25 +7278,25 @@ var notificationType = (text) => {
6496
7278
  return;
6497
7279
  }
6498
7280
  };
6499
- var seatEvent = async (o) => {
6500
- const log = o.log ?? console.log;
6501
- const now = (o.now ?? Date.now)();
6502
- const input = o.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS2);
7281
+ var seatEvent = async (o2) => {
7282
+ const log = o2.log ?? console.log;
7283
+ const now = (o2.now ?? Date.now)();
7284
+ const input = o2.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS2);
6503
7285
  const kind = notificationType(input);
6504
7286
  try {
6505
- const state = readSeatState(o.home);
7287
+ const state = readSeatState(o2.home);
6506
7288
  const week = state.window?.sevenDay;
6507
- const src = meterSource(o.home);
7289
+ const src = meterSource(o2.home);
6508
7290
  if (src) {
6509
- await postSeatWall({ ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, {
7291
+ await postSeatWall({ ...src, fetchFn: o2.fetchFn, timeoutMs: o2.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, {
6510
7292
  ...week ? { resetsAt: week.resetsAt, usedPct: week.usedPct } : {},
6511
7293
  ...kind ? { notification: kind } : {}
6512
7294
  });
6513
7295
  }
6514
7296
  if (week && week.resetsAt > now)
6515
- writeSeatState(o.home, { ...state, lastWallResetsAt: week.resetsAt });
7297
+ writeSeatState(o2.home, { ...state, lastWallResetsAt: week.resetsAt });
6516
7298
  } catch {}
6517
- const line = seatWallLine(readConfig(o.home).org?.capabilities.upstreamKey !== false);
7299
+ const line = seatWallLine(readConfig(o2.home).org?.capabilities.upstreamKey !== false);
6518
7300
  log(line);
6519
7301
  return 0;
6520
7302
  };
@@ -6534,11 +7316,11 @@ var isBun = () => typeof globalThis.Bun !== "undefined";
6534
7316
  async function queryRows(dbPath) {
6535
7317
  if (isBun()) {
6536
7318
  const { Database } = await import("bun:sqlite");
6537
- const conn = new Database(dbPath, { readonly: true });
7319
+ const conn2 = new Database(dbPath, { readonly: true });
6538
7320
  try {
6539
- return conn.query(QUERY).all();
7321
+ return conn2.query(QUERY).all();
6540
7322
  } finally {
6541
- conn.close();
7323
+ conn2.close();
6542
7324
  }
6543
7325
  }
6544
7326
  const mod = nodeSqlite();
@@ -6644,19 +7426,19 @@ function parseSeatSyncArgs(argv) {
6644
7426
  return { error: `--profile does not apply to --browser ${source.browser}` };
6645
7427
  return { source };
6646
7428
  }
6647
- function resolveProfileDir(localStateJson, profile) {
7429
+ function resolveProfileDir(localStateJson, profile3) {
6648
7430
  let cache = {};
6649
7431
  if (localStateJson) {
6650
7432
  try {
6651
7433
  const raw = JSON.parse(localStateJson);
6652
- const p = isObject11(raw) && isObject11(raw.profile) ? raw.profile : null;
6653
- if (p && isObject11(p.info_cache))
6654
- cache = p.info_cache;
7434
+ const p2 = isObject11(raw) && isObject11(raw.profile) ? raw.profile : null;
7435
+ if (p2 && isObject11(p2.info_cache))
7436
+ cache = p2.info_cache;
6655
7437
  } catch {}
6656
7438
  }
6657
- if (Object.hasOwn(cache, profile))
6658
- return { dir: profile };
6659
- const want = profile.toLowerCase();
7439
+ if (Object.hasOwn(cache, profile3))
7440
+ return { dir: profile3 };
7441
+ const want = profile3.toLowerCase();
6660
7442
  const hits = [];
6661
7443
  for (const [dir, info] of Object.entries(cache)) {
6662
7444
  if (!isObject11(info))
@@ -6672,7 +7454,7 @@ function resolveProfileDir(localStateJson, profile) {
6672
7454
  return { dir: hits[0] ?? null };
6673
7455
  if (hits.length > 1)
6674
7456
  return { dir: null, ambiguous: hits };
6675
- return { dir: Object.keys(cache).length === 0 ? profile : null };
7457
+ return { dir: Object.keys(cache).length === 0 ? profile3 : null };
6676
7458
  }
6677
7459
  function cookieDbCandidates(profileDir) {
6678
7460
  return [`${profileDir}/Network/Cookies`, `${profileDir}/Cookies`];
@@ -6842,11 +7624,11 @@ function findFile(root, name) {
6842
7624
  continue;
6843
7625
  }
6844
7626
  for (const e of entries) {
6845
- const p = join11(dir, e.name);
7627
+ const p2 = join11(dir, e.name);
6846
7628
  if (e.isDirectory())
6847
- stack.push(p);
7629
+ stack.push(p2);
6848
7630
  else if (e.name === name)
6849
- return p;
7631
+ return p2;
6850
7632
  }
6851
7633
  }
6852
7634
  return null;
@@ -6857,10 +7639,10 @@ function findCookieDb(home, source) {
6857
7639
  if (!existsSync12(root))
6858
7640
  throw new Error(`no ${source.browser} data at ${root} (is it installed?)`);
6859
7641
  if (!b.profiled) {
6860
- const db = findFile(root, "Cookies");
6861
- if (!db)
7642
+ const db2 = findFile(root, "Cookies");
7643
+ if (!db2)
6862
7644
  throw new Error(`no cookie store under ${root}`);
6863
- return db;
7645
+ return db2;
6864
7646
  }
6865
7647
  const localStatePath = join11(root, "Local State");
6866
7648
  const localState = existsSync12(localStatePath) ? readFileSync10(localStatePath, "utf8") : null;
@@ -6870,7 +7652,7 @@ function findCookieDb(home, source) {
6870
7652
  throw new Error(`"${wanted}" names ${ambiguous.length} ${source.browser} profiles (${ambiguous.join(", ")}) — pass the directory or the account email instead`);
6871
7653
  if (!dir)
6872
7654
  throw new Error(`no ${source.browser} profile named "${wanted}" (check chrome://version)`);
6873
- const db = cookieDbCandidates(join11(root, dir)).find((p) => existsSync12(p));
7655
+ const db = cookieDbCandidates(join11(root, dir)).find((p2) => existsSync12(p2));
6874
7656
  if (!db)
6875
7657
  throw new Error(`profile "${dir}" has no cookie store yet (open claude.ai in it once)`);
6876
7658
  return db;
@@ -6901,8 +7683,8 @@ var curlUsage = (org, cookie) => {
6901
7683
  return { status: 0, body: "" };
6902
7684
  }
6903
7685
  };
6904
- function fetchSeatUsage(jar, org, now, run) {
6905
- const { status, body } = run(org, cookieHeader(jar, org));
7686
+ function fetchSeatUsage(jar, org, now, run2) {
7687
+ const { status, body } = run2(org, cookieHeader(jar, org));
6906
7688
  if (status !== 200)
6907
7689
  return { usage: null, status };
6908
7690
  try {
@@ -6963,24 +7745,24 @@ function uninstall(home, source, log) {
6963
7745
  log(existsSync12(path) ? `seat-sync timer removed (${path} can be deleted)` : "no seat-sync timer");
6964
7746
  return 0;
6965
7747
  }
6966
- var seatSync = async (o) => {
6967
- const log = o.log ?? console.log;
6968
- const now = (o.now ?? Date.now)();
6969
- const parsed = parseSeatSyncArgs(o.argv ?? process.argv.slice(3));
7748
+ var seatSync = async (o2) => {
7749
+ const log = o2.log ?? console.log;
7750
+ const now = (o2.now ?? Date.now)();
7751
+ const parsed = parseSeatSyncArgs(o2.argv ?? process.argv.slice(3));
6970
7752
  if ("error" in parsed) {
6971
7753
  log(`seat-sync: ${parsed.error}`);
6972
7754
  return 1;
6973
7755
  }
6974
7756
  const { source } = parsed;
6975
7757
  if (source.uninstall)
6976
- return uninstall(o.home, source, log);
7758
+ return uninstall(o2.home, source, log);
6977
7759
  if (source.install)
6978
- return install(o.home, [process.execPath, process.argv[1] ?? ""], source, log);
6979
- if (platform2() !== "darwin" && !o.readCookieRows) {
7760
+ return install(o2.home, [process.execPath, process.argv[1] ?? ""], source, log);
7761
+ if (platform2() !== "darwin" && !o2.readCookieRows) {
6980
7762
  log("seat-sync is macOS-only for now.");
6981
7763
  return 2;
6982
7764
  }
6983
- const saved = meterSource(o.home);
7765
+ const saved = meterSource(o2.home);
6984
7766
  const proxyUrl = saved?.proxyUrl;
6985
7767
  const token = source.token ?? saved?.token;
6986
7768
  if (!proxyUrl || !token) {
@@ -6989,13 +7771,13 @@ var seatSync = async (o) => {
6989
7771
  }
6990
7772
  let rows;
6991
7773
  try {
6992
- rows = o.readCookieRows ? o.readCookieRows(o.home, source) : await readCookieRowsBun(o.home, source);
7774
+ rows = o2.readCookieRows ? o2.readCookieRows(o2.home, source) : await readCookieRowsBun(o2.home, source);
6993
7775
  } catch (e) {
6994
7776
  log(`seat-sync: ${e instanceof Error ? e.message : String(e)}`);
6995
7777
  return 1;
6996
7778
  }
6997
7779
  const service = BROWSERS[source.browser].keychainService;
6998
- const pw = (o.safeStorageKey ?? keychainKey)(service);
7780
+ const pw = (o2.safeStorageKey ?? keychainKey)(service);
6999
7781
  if (!pw) {
7000
7782
  log(`seat-sync: could not read the "${service}" key from your Keychain.`);
7001
7783
  return 1;
@@ -7015,16 +7797,16 @@ var seatSync = async (o) => {
7015
7797
  log("seat-sync: no active org in the session cookie (pass --org <uuid>).");
7016
7798
  return 1;
7017
7799
  }
7018
- const { usage, status } = fetchSeatUsage(jar, org, now, o.runUsage ?? curlUsage);
7800
+ const { usage, status } = fetchSeatUsage(jar, org, now, o2.runUsage ?? curlUsage);
7019
7801
  if (!usage) {
7020
7802
  const why = status === 403 ? "claude.ai refused the read (403) — session expired, or this org is not readable by this session." : status === 0 ? "could not reach claude.ai." : `unexpected HTTP ${status}.`;
7021
7803
  log(`seat-sync: usage read failed — ${why}`);
7022
7804
  return 2;
7023
7805
  }
7024
7806
  try {
7025
- writeSeatState(o.home, { ...readSeatState(o.home), window: usageAsWindow(usage) });
7807
+ writeSeatState(o2.home, { ...readSeatState(o2.home), window: usageAsWindow(usage) });
7026
7808
  } catch {}
7027
- const posted = await postSeat(proxyUrl, token, seatSyncBody(usage), o.fetchFn ?? fetch);
7809
+ const posted = await postSeat(proxyUrl, token, seatSyncBody(usage), o2.fetchFn ?? fetch);
7028
7810
  const wk = usage.sevenDay ? `wk ${Math.round(usage.sevenDay.usedPct)}%` : "wk —";
7029
7811
  const models = Object.keys(usage.models).length;
7030
7812
  const credits = usage.usedCreditsUsd !== undefined ? ` · $${usage.usedCreditsUsd.toFixed(2)}` : "";
@@ -7076,14 +7858,14 @@ function sessionContext(a) {
7076
7858
  return lines.join(`
7077
7859
  `);
7078
7860
  }
7079
- var sessionEvent = async (o) => {
7080
- const log = o.log ?? console.log;
7081
- const now = (o.now ?? Date.now)();
7082
- const raw = o.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS);
7861
+ var sessionEvent = async (o2) => {
7862
+ const log = o2.log ?? console.log;
7863
+ const now = (o2.now ?? Date.now)();
7864
+ const raw = o2.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS);
7083
7865
  const payload = parseSessionPayload(raw);
7084
7866
  if (payload.session_id) {
7085
7867
  try {
7086
- writeSession(o.home, {
7868
+ writeSession(o2.home, {
7087
7869
  sessionId: payload.session_id,
7088
7870
  startedAt: now,
7089
7871
  ...payload.source ? { source: payload.source } : {}
@@ -7093,13 +7875,13 @@ var sessionEvent = async (o) => {
7093
7875
  let email;
7094
7876
  let org;
7095
7877
  try {
7096
- const cfg = readConfig(o.home);
7878
+ const cfg = readConfig(o2.home);
7097
7879
  email = cfg.email;
7098
7880
  org = cfg.org?.slug;
7099
7881
  } catch {}
7100
7882
  let owed = [];
7101
7883
  try {
7102
- owed = readOwed(o.home, now);
7884
+ owed = readOwed(o2.home, now);
7103
7885
  } catch {
7104
7886
  owed = [];
7105
7887
  }
@@ -7144,13 +7926,13 @@ function parseHookInput(raw) {
7144
7926
  }
7145
7927
  if (typeof parsed !== "object" || parsed === null)
7146
7928
  return null;
7147
- const p = parsed;
7148
- const toolName = typeof p.tool_name === "string" ? p.tool_name : "";
7149
- const input = typeof p.tool_input === "object" && p.tool_input !== null ? p.tool_input : {};
7929
+ const p2 = parsed;
7930
+ const toolName = typeof p2.tool_name === "string" ? p2.tool_name : "";
7931
+ const input = typeof p2.tool_input === "object" && p2.tool_input !== null ? p2.tool_input : {};
7150
7932
  const filePath = typeof input.file_path === "string" ? input.file_path : "";
7151
7933
  if (!filePath)
7152
7934
  return null;
7153
- const cwd = typeof p.cwd === "string" ? p.cwd : "";
7935
+ const cwd = typeof p2.cwd === "string" ? p2.cwd : "";
7154
7936
  return { toolName, filePath, cwd };
7155
7937
  }
7156
7938
  function projectKey(filePath, cwd) {
@@ -7170,16 +7952,16 @@ function readFileOrNull(path) {
7170
7952
  return null;
7171
7953
  }
7172
7954
  }
7173
- var specEvent = async (o) => {
7174
- const log = o.log ?? console.log;
7175
- const raw = o.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS3);
7955
+ var specEvent = async (o2) => {
7956
+ const log = o2.log ?? console.log;
7957
+ const raw = o2.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS3);
7176
7958
  const input = raw ? parseHookInput(raw) : null;
7177
7959
  if (!input || !isSpecPath(input.filePath))
7178
7960
  return 0;
7179
- const src = meterSource(o.home);
7961
+ const src = meterSource(o2.home);
7180
7962
  if (!src)
7181
7963
  return 0;
7182
- const body = (o.readFile ?? readFileOrNull)(input.filePath);
7964
+ const body = (o2.readFile ?? readFileOrNull)(input.filePath);
7183
7965
  if (!body || !looksLikeSpec(body))
7184
7966
  return 0;
7185
7967
  const project = projectKey(input.filePath, input.cwd);
@@ -7189,7 +7971,7 @@ var specEvent = async (o) => {
7189
7971
  repoHint: input.cwd ? basename2(input.cwd) : undefined
7190
7972
  };
7191
7973
  try {
7192
- const res = await (o.fetchFn ?? fetch)(`${src.proxyUrl}/proxy/estimate`, {
7974
+ const res = await (o2.fetchFn ?? fetch)(`${src.proxyUrl}/proxy/estimate`, {
7193
7975
  method: "POST",
7194
7976
  headers: { authorization: `Bearer ${src.token}`, "content-type": "application/json" },
7195
7977
  body: JSON.stringify({ spec, project }),
@@ -7204,15 +7986,15 @@ var specEvent = async (o) => {
7204
7986
  log(`sleds: ${project} sized at $${b.lowUsd.toFixed(2)}–${b.highUsd.toFixed(2)} · ${min(b.lowMinutes)}–${min(b.highMinutes)} min`);
7205
7987
  if (typeof b.lowMinutes === "number" && typeof b.highMinutes === "number") {
7206
7988
  try {
7207
- writeSeatState(o.home, {
7208
- ...readSeatState(o.home),
7989
+ writeSeatState(o2.home, {
7990
+ ...readSeatState(o2.home),
7209
7991
  estimate: {
7210
7992
  project,
7211
7993
  lowUsd: b.lowUsd,
7212
7994
  highUsd: b.highUsd,
7213
7995
  lowMin: b.lowMinutes,
7214
7996
  highMin: b.highMinutes,
7215
- at: (o.now ?? Date.now)()
7997
+ at: (o2.now ?? Date.now)()
7216
7998
  }
7217
7999
  });
7218
8000
  } catch {}
@@ -7226,7 +8008,8 @@ var specEvent = async (o) => {
7226
8008
  init_config();
7227
8009
  init_settings();
7228
8010
  import { existsSync as existsSync13 } from "node:fs";
7229
- var checkHealth2 = async (url, fetchFn = fetch, timeoutMs = 1000) => {
8011
+ var HEALTH_TIMEOUT_MS2 = 4000;
8012
+ var checkHealth2 = async (url, fetchFn = fetch, timeoutMs = HEALTH_TIMEOUT_MS2) => {
7230
8013
  try {
7231
8014
  const res = await fetchFn(`${url}/healthz`, { signal: AbortSignal.timeout(timeoutMs) });
7232
8015
  return res.ok ? "ok" : "unreachable";
@@ -7234,7 +8017,7 @@ var checkHealth2 = async (url, fetchFn = fetch, timeoutMs = 1000) => {
7234
8017
  return "unreachable";
7235
8018
  }
7236
8019
  };
7237
- var fetchProxyStatus = async (url, fetchFn = fetch, timeoutMs = 1000) => {
8020
+ var fetchProxyStatus = async (url, fetchFn = fetch, timeoutMs = HEALTH_TIMEOUT_MS2) => {
7238
8021
  try {
7239
8022
  const res = await fetchFn(`${url}/status`, { signal: AbortSignal.timeout(timeoutMs) });
7240
8023
  if (!res.ok)
@@ -7295,16 +8078,16 @@ var formatStatus = (s) => {
7295
8078
  }
7296
8079
  return lines;
7297
8080
  };
7298
- var status = async (o) => {
7299
- const log = o.log ?? console.log;
7300
- const nowMs = (o.now ?? Date.now)();
7301
- const cfg = readConfig(o.home);
7302
- const settings = existsSync13(settingsPath(o.home)) ? readSettings(o.home) : {};
8081
+ var status = async (o2) => {
8082
+ const log = o2.log ?? console.log;
8083
+ const nowMs = (o2.now ?? Date.now)();
8084
+ const cfg = readConfig(o2.home);
8085
+ const settings = existsSync13(settingsPath(o2.home)) ? readSettings(o2.home) : {};
7303
8086
  const inSettings = readProxy(settings);
7304
8087
  const proxyUrl = inSettings?.url ?? cfg.proxyUrl;
7305
8088
  const token = inSettings?.token ?? cfg.token;
7306
8089
  if (!proxyUrl && !token && cfg.mode === undefined) {
7307
- log(`SLEDS is not set up (${configPath(o.home)} and ${settingsPath(o.home)}).`);
8090
+ log(`SLEDS is not set up (${configPath(o2.home)} and ${settingsPath(o2.home)}).`);
7308
8091
  log("Run `sleds init` first.");
7309
8092
  return 1;
7310
8093
  }
@@ -7312,8 +8095,8 @@ var status = async (o) => {
7312
8095
  mode: cfg.mode ?? "off",
7313
8096
  pointed: inSettings !== null,
7314
8097
  nowMs,
7315
- settingsFile: settingsPath(o.home),
7316
- configFile: configPath(o.home)
8098
+ settingsFile: settingsPath(o2.home),
8099
+ configFile: configPath(o2.home)
7317
8100
  };
7318
8101
  if (proxyUrl)
7319
8102
  info.proxyUrl = proxyUrl;
@@ -7333,8 +8116,8 @@ var status = async (o) => {
7333
8116
  };
7334
8117
  if (cfg.reason)
7335
8118
  info.reason = cfg.reason;
7336
- if (o.tz)
7337
- info.tz = o.tz;
8119
+ if (o2.tz)
8120
+ info.tz = o2.tz;
7338
8121
  const since = cfg.since ? Date.parse(cfg.since) : Number.NaN;
7339
8122
  if (!Number.isNaN(since))
7340
8123
  info.sinceMs = since;
@@ -7343,8 +8126,8 @@ var status = async (o) => {
7343
8126
  info.untilResetMs = until;
7344
8127
  if (proxyUrl) {
7345
8128
  const [health, proxy] = await Promise.all([
7346
- checkHealth2(proxyUrl, o.fetchFn, o.timeoutMs),
7347
- fetchProxyStatus(proxyUrl, o.fetchFn, o.timeoutMs)
8129
+ checkHealth2(proxyUrl, o2.fetchFn, o2.timeoutMs),
8130
+ fetchProxyStatus(proxyUrl, o2.fetchFn, o2.timeoutMs)
7348
8131
  ]);
7349
8132
  info.health = health;
7350
8133
  info.proxy = proxy;
@@ -7357,62 +8140,62 @@ var status = async (o) => {
7357
8140
  // src/commands/statusline.ts
7358
8141
  init_config();
7359
8142
  init_meter();
7360
- var statusline = async (o) => {
7361
- const log = o.log ?? console.log;
7362
- const now = (o.now ?? Date.now)();
7363
- const input = o.stdin ?? await readStdin(STDIN_TIMEOUT_MS);
8143
+ var statusline = async (o2) => {
8144
+ const log = o2.log ?? console.log;
8145
+ const now = (o2.now ?? Date.now)();
8146
+ const input = o2.stdin ?? await readStdin(STDIN_TIMEOUT_MS);
7364
8147
  const window = seatWindowFromStdin(input, now);
7365
8148
  const session = seatSessionFromStdin(input, now);
7366
8149
  let posts = [];
7367
8150
  let events = [];
7368
8151
  try {
7369
8152
  if (window)
7370
- ({ posts, events } = rememberSeat(o, window, session, now, input));
8153
+ ({ posts, events } = rememberSeat(o2, window, session, now, input));
7371
8154
  else {
7372
- const st = readSeatState(o.home);
7373
- const credits = creditsEvent(o, st, input, now);
8155
+ const st = readSeatState(o2.home);
8156
+ const credits = creditsEvent(o2, st, input, now);
7374
8157
  if (session || credits.scan) {
7375
8158
  const next = {
7376
8159
  ...st,
7377
8160
  ...session ? { session } : {},
7378
8161
  ...credits.scan ? { transcriptScan: credits.scan } : {}
7379
8162
  };
7380
- writeSeatState(o.home, credits.event ? markWarned(next, [credits.event]) : next);
8163
+ writeSeatState(o2.home, credits.event ? markWarned(next, [credits.event]) : next);
7381
8164
  }
7382
8165
  if (credits.event) {
7383
8166
  events = [credits.event];
7384
- posts = announce(o, events);
8167
+ posts = announce(o2, events);
7385
8168
  }
7386
8169
  }
7387
8170
  } catch {}
7388
8171
  const seatFragment = formatSeatFragment(window, now);
7389
8172
  let line;
7390
8173
  try {
7391
- const meter = await loadMeter({
7392
- home: o.home,
8174
+ const meter2 = await loadMeter({
8175
+ home: o2.home,
7393
8176
  now,
7394
- fetchFn: o.fetchFn,
7395
- timeoutMs: o.timeoutMs
8177
+ fetchFn: o2.fetchFn,
8178
+ timeoutMs: o2.timeoutMs
7396
8179
  });
7397
- line = formatMeterLine(meter, now, seatFragment);
8180
+ line = formatMeterLine(meter2, now, seatFragment);
7398
8181
  } catch {
7399
8182
  line = formatMeterLine(null, now, seatFragment);
7400
8183
  }
7401
8184
  log(line);
7402
- posts.push(speakToCockpit(o, window, events, now));
8185
+ posts.push(speakToCockpit(o2, window, events, now));
7403
8186
  await Promise.allSettled(posts);
7404
8187
  return 0;
7405
8188
  };
7406
- var speakToCockpit = async (o, window, events, now) => {
7407
- const cockpit = detectCockpit(o.env ?? process.env);
8189
+ var speakToCockpit = async (o2, window, events, now) => {
8190
+ const cockpit = detectCockpit(o2.env ?? process.env);
7408
8191
  if (!cockpit)
7409
8192
  return;
7410
8193
  try {
7411
- const st = readSeatState(o.home);
8194
+ const st = readSeatState(o2.home);
7412
8195
  let feed;
7413
- const src = meterSource(o.home);
8196
+ const src = meterSource(o2.home);
7414
8197
  if (src && now - (st.cockpit?.feed?.at ?? 0) >= FEED_MIN_INTERVAL_MS) {
7415
- const got = await fetchFeed({ ...src, email: readConfig(o.home).email, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs }, now);
8198
+ const got = await fetchFeed({ ...src, email: readConfig(o2.home).email, fetchFn: o2.fetchFn, timeoutMs: o2.timeoutMs }, now);
7416
8199
  if (got)
7417
8200
  feed = got;
7418
8201
  }
@@ -7423,32 +8206,32 @@ var speakToCockpit = async (o, window, events, now) => {
7423
8206
  const changed = r.argv.length > 0 || feed !== undefined;
7424
8207
  if (!changed)
7425
8208
  return;
7426
- writeSeatState(o.home, { ...readSeatState(o.home), cockpit: r.state });
8209
+ writeSeatState(o2.home, { ...readSeatState(o2.home), cockpit: r.state });
7427
8210
  if (r.argv.length)
7428
- await runCockpit(cockpit, r.argv, { spawn: o.spawn });
8211
+ await runCockpit(cockpit, r.argv, { spawn: o2.spawn });
7429
8212
  } catch {}
7430
8213
  };
7431
- var rememberSeat = (o, window, session, now, input) => {
7432
- const prior = readSeatState(o.home);
8214
+ var rememberSeat = (o2, window, session, now, input) => {
8215
+ const prior = readSeatState(o2.home);
7433
8216
  let state = { ...prior, window, ...session ? { session } : {} };
7434
- const src = meterSource(o.home);
8217
+ const src = meterSource(o2.home);
7435
8218
  const posts = [];
7436
8219
  if (src && shouldPostSeat(prior, window, now)) {
7437
8220
  state = markPosted(state, window, now);
7438
- posts.push(postSeatWindow({ ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, window));
8221
+ posts.push(postSeatWindow({ ...src, fetchFn: o2.fetchFn, timeoutMs: o2.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, window));
7439
8222
  }
7440
8223
  const events = seatWallEvents(prior, window);
7441
- const credits = creditsEvent(o, prior, input, now);
8224
+ const credits = creditsEvent(o2, prior, input, now);
7442
8225
  if (credits.scan)
7443
8226
  state = { ...state, transcriptScan: credits.scan };
7444
8227
  if (credits.event)
7445
8228
  events.push(credits.event);
7446
8229
  state = markWarned(state, events);
7447
- posts.push(...announce(o, events));
7448
- writeSeatState(o.home, state);
8230
+ posts.push(...announce(o2, events));
8231
+ writeSeatState(o2.home, state);
7449
8232
  return { posts, events };
7450
8233
  };
7451
- var creditsEvent = (o, prior, input, now) => {
8234
+ var creditsEvent = (o2, prior, input, now) => {
7452
8235
  const path = transcriptPathFromStdin(input);
7453
8236
  if (!path)
7454
8237
  return { event: null, scan: undefined };
@@ -7458,13 +8241,13 @@ var creditsEvent = (o, prior, input, now) => {
7458
8241
  return { event: null, scan: next };
7459
8242
  return { event: { bar: "credits", stage: "hit", usedPct: 100, resetsAt: now }, scan: next };
7460
8243
  };
7461
- var announce = (o, events) => {
8244
+ var announce = (o2, events) => {
7462
8245
  if (events.length === 0)
7463
8246
  return [];
7464
- const src = meterSource(o.home);
8247
+ const src = meterSource(o2.home);
7465
8248
  if (!src)
7466
8249
  return [];
7467
- return events.map((e) => postSeatWall({ ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, {
8250
+ return events.map((e) => postSeatWall({ ...src, fetchFn: o2.fetchFn, timeoutMs: o2.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, {
7468
8251
  bar: e.bar,
7469
8252
  stage: e.stage,
7470
8253
  usedPct: e.usedPct,
@@ -7474,6 +8257,128 @@ var announce = (o, events) => {
7474
8257
  };
7475
8258
 
7476
8259
  // src/commands/stopEvent.ts
8260
+ init_meter();
8261
+ import { existsSync as existsSync14, readFileSync as readFileSync12 } from "node:fs";
8262
+
8263
+ // src/transcript.ts
8264
+ var isObject12 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
8265
+ var n = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
8266
+ function sumTranscript(text) {
8267
+ const seen = new Set;
8268
+ const turn = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
8269
+ let sub = 0;
8270
+ let messages = 0;
8271
+ const byModel = new Map;
8272
+ const agents2 = new Set;
8273
+ let anySidechain = false;
8274
+ for (const line of text.split(`
8275
+ `)) {
8276
+ const t = line.trim();
8277
+ if (!t)
8278
+ continue;
8279
+ let j;
8280
+ try {
8281
+ j = JSON.parse(t);
8282
+ } catch {
8283
+ continue;
8284
+ }
8285
+ if (!isObject12(j) || j.type !== "assistant" || !isObject12(j.message))
8286
+ continue;
8287
+ const m = j.message;
8288
+ const id = typeof m.id === "string" ? m.id : typeof j.uuid === "string" ? j.uuid : undefined;
8289
+ if (id) {
8290
+ if (seen.has(id))
8291
+ continue;
8292
+ seen.add(id);
8293
+ }
8294
+ const u = isObject12(m.usage) ? m.usage : undefined;
8295
+ if (!u)
8296
+ continue;
8297
+ const input = n(u.input_tokens);
8298
+ const output = n(u.output_tokens);
8299
+ const cacheRead = n(u.cache_read_input_tokens);
8300
+ const cacheWrite = n(u.cache_creation_input_tokens);
8301
+ messages++;
8302
+ const model2 = typeof m.model === "string" ? m.model : undefined;
8303
+ if (model2)
8304
+ byModel.set(model2, (byModel.get(model2) ?? 0) + output);
8305
+ if (j.isSidechain === true) {
8306
+ anySidechain = true;
8307
+ sub += input + output + cacheRead + cacheWrite;
8308
+ if (typeof j.agentId === "string")
8309
+ agents2.add(j.agentId);
8310
+ } else {
8311
+ turn.input += input;
8312
+ turn.output += output;
8313
+ turn.cacheRead += cacheRead;
8314
+ turn.cacheWrite += cacheWrite;
8315
+ }
8316
+ }
8317
+ let model;
8318
+ let best = -1;
8319
+ for (const [k, v] of byModel)
8320
+ if (v > best)
8321
+ [model, best] = [k, v];
8322
+ return {
8323
+ turnTokens: turn,
8324
+ subagentTokens: sub,
8325
+ messages,
8326
+ ...model ? { model } : {},
8327
+ subagentRuns: agents2.size > 0 ? agents2.size : anySidechain ? 1 : 0
8328
+ };
8329
+ }
8330
+ function sessionBody(sessionId, sum, a) {
8331
+ return {
8332
+ capturedAt: a.capturedAt,
8333
+ sessions: [
8334
+ {
8335
+ sessionId,
8336
+ ...a.title ? { title: a.title.slice(0, 200) } : {},
8337
+ updatedAt: a.capturedAt,
8338
+ subagentTokens: sum.subagentTokens,
8339
+ taskCount: sum.subagentRuns,
8340
+ turnTokens: sum.turnTokens,
8341
+ tasks: []
8342
+ }
8343
+ ]
8344
+ };
8345
+ }
8346
+
8347
+ // src/commands/stopEvent.ts
8348
+ var SESSION_POST_TIMEOUT_MS = 2500;
8349
+ async function postTranscript(o2, payload, now) {
8350
+ const sessionId = payload.session_id?.trim();
8351
+ const path = payload.transcript_path?.trim();
8352
+ if (!sessionId || !path)
8353
+ return false;
8354
+ const src = meterSource(o2.home);
8355
+ if (!src)
8356
+ return false;
8357
+ const read = o2.readFile ?? ((p2) => existsSync14(p2) ? readFileSync12(p2, "utf8") : null);
8358
+ let text;
8359
+ try {
8360
+ text = read(path);
8361
+ } catch {
8362
+ return false;
8363
+ }
8364
+ if (!text)
8365
+ return false;
8366
+ const sum = sumTranscript(text);
8367
+ if (sum.messages === 0)
8368
+ return false;
8369
+ const fetchFn = o2.fetchFn ?? fetch;
8370
+ try {
8371
+ const res = await fetchFn(`${src.proxyUrl.replace(/\/+$/, "")}/proxy/sessions`, {
8372
+ method: "POST",
8373
+ headers: { authorization: `Bearer ${src.token}`, "content-type": "application/json" },
8374
+ body: JSON.stringify(sessionBody(sessionId, sum, { capturedAt: now })),
8375
+ signal: AbortSignal.timeout(SESSION_POST_TIMEOUT_MS)
8376
+ });
8377
+ return res.ok;
8378
+ } catch {
8379
+ return false;
8380
+ }
8381
+ }
7477
8382
  var parseStopPayload = (raw) => {
7478
8383
  try {
7479
8384
  const j = JSON.parse(raw);
@@ -7489,23 +8394,24 @@ function stopReason(rows) {
7489
8394
  function decideStop(a) {
7490
8395
  if (a.stopHookActive || !a.sessionId)
7491
8396
  return { block: false };
7492
- const due = owedBySession(a.rows, a.sessionId);
7493
- if (due.length === 0)
8397
+ const due2 = owedBySession(a.rows, a.sessionId);
8398
+ if (due2.length === 0)
7494
8399
  return { block: false };
7495
8400
  return {
7496
8401
  block: true,
7497
- reason: stopReason(due),
7498
- rows: markAsked(a.rows, due.map((r) => r.key), a.now)
8402
+ reason: stopReason(due2),
8403
+ rows: markAsked(a.rows, due2.map((r) => r.key), a.now)
7499
8404
  };
7500
8405
  }
7501
- var stopEvent = async (o) => {
7502
- const log = o.log ?? console.log;
7503
- const now = (o.now ?? Date.now)();
7504
- const raw = o.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS);
8406
+ var stopEvent = async (o2) => {
8407
+ const log = o2.log ?? console.log;
8408
+ const now = (o2.now ?? Date.now)();
8409
+ const raw = o2.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS);
7505
8410
  const payload = parseStopPayload(raw);
8411
+ await postTranscript(o2, payload, now);
7506
8412
  let rows;
7507
8413
  try {
7508
- rows = readOwed(o.home, now);
8414
+ rows = readOwed(o2.home, now);
7509
8415
  } catch {
7510
8416
  return 0;
7511
8417
  }
@@ -7518,7 +8424,7 @@ var stopEvent = async (o) => {
7518
8424
  if (!d.block)
7519
8425
  return 0;
7520
8426
  try {
7521
- writeOwed(o.home, d.rows);
8427
+ writeOwed(o2.home, d.rows);
7522
8428
  } catch {}
7523
8429
  log(JSON.stringify({ decision: "block", reason: d.reason }));
7524
8430
  return 0;
@@ -7526,16 +8432,16 @@ var stopEvent = async (o) => {
7526
8432
 
7527
8433
  // src/commands/uninstall.ts
7528
8434
  import { spawnSync as spawnSync5 } from "node:child_process";
7529
- import { existsSync as existsSync15, rmSync as rmSync5 } from "node:fs";
8435
+ import { existsSync as existsSync16, rmSync as rmSync5 } from "node:fs";
7530
8436
 
7531
8437
  // src/codex.ts
7532
8438
  init_config();
7533
- import { existsSync as existsSync14, mkdirSync as mkdirSync12, readFileSync as readFileSync12, writeFileSync as writeFileSync12 } from "node:fs";
8439
+ import { existsSync as existsSync15, mkdirSync as mkdirSync12, readFileSync as readFileSync13, writeFileSync as writeFileSync12 } from "node:fs";
7534
8440
  import { dirname as dirname6, join as join12 } from "node:path";
7535
8441
  var PROVIDER_ID2 = "sleds";
7536
8442
  var codexDir2 = (home) => join12(home, ".codex");
7537
8443
  var codexConfigPath2 = (home) => join12(codexDir2(home), "config.toml");
7538
- function removeCodexProvider2(toml, o = {}) {
8444
+ function removeCodexProvider2(toml, o2 = {}) {
7539
8445
  const lines = toml.split(`
7540
8446
  `);
7541
8447
  const out = [];
@@ -7548,7 +8454,7 @@ function removeCodexProvider2(toml, o = {}) {
7548
8454
  }
7549
8455
  let text = out.join(`
7550
8456
  `);
7551
- if (!o.keepPrior) {
8457
+ if (!o2.keepPrior) {
7552
8458
  const prior = text.match(/^# sleds-prior-model-provider = "([^"]*)"\s*\n/m);
7553
8459
  if (prior) {
7554
8460
  text = text.replace(prior[0], "");
@@ -7563,9 +8469,9 @@ function removeCodexProvider2(toml, o = {}) {
7563
8469
  }
7564
8470
  function uninstallCodex(home) {
7565
8471
  const path = codexConfigPath2(home);
7566
- if (!existsSync14(path))
8472
+ if (!existsSync15(path))
7567
8473
  return null;
7568
- const prior = readFileSync12(path, "utf8");
8474
+ const prior = readFileSync13(path, "utf8");
7569
8475
  const next = removeCodexProvider2(prior);
7570
8476
  if (next === prior)
7571
8477
  return null;
@@ -7580,72 +8486,72 @@ init_settings();
7580
8486
  var defaultUnload = (plist) => {
7581
8487
  spawnSync5("launchctl", ["unload", plist], { stdio: "ignore" });
7582
8488
  };
7583
- var uninstall2 = (o) => {
7584
- const codexLine = uninstallCodex(o.home);
8489
+ var uninstall2 = (o2) => {
8490
+ const codexLine = uninstallCodex(o2.home);
7585
8491
  if (codexLine)
7586
- (o.log ?? console.log)(` ${codexLine}`);
7587
- const log = o.log ?? console.log;
8492
+ (o2.log ?? console.log)(` ${codexLine}`);
8493
+ const log = o2.log ?? console.log;
7588
8494
  const removed = [];
7589
- if (existsSync15(settingsPath(o.home))) {
7590
- let settings = readSettings(o.home);
8495
+ if (existsSync16(settingsPath(o2.home))) {
8496
+ let settings = readSettings(o2.home);
7591
8497
  let changed = false;
7592
8498
  if (hasEitherKey(settings)) {
7593
8499
  settings = removeProxy(settings);
7594
8500
  changed = true;
7595
- removed.push(`env.ANTHROPIC_BASE_URL and env.ANTHROPIC_AUTH_TOKEN from ${settingsPath(o.home)}`);
8501
+ removed.push(`env.ANTHROPIC_BASE_URL and env.ANTHROPIC_AUTH_TOKEN from ${settingsPath(o2.home)}`);
7596
8502
  }
7597
8503
  if (isSledsStatusLine(settings[STATUS_LINE_KEY])) {
7598
- const cfg = readConfig(o.home);
8504
+ const cfg = readConfig(o2.home);
7599
8505
  settings = removeStatusLine(settings, cfg.priorStatusLine);
7600
8506
  changed = true;
7601
- removed.push(cfg.priorStatusLine === undefined ? `statusLine from ${settingsPath(o.home)}` : `statusLine from ${settingsPath(o.home)} (previous one restored)`);
8507
+ removed.push(cfg.priorStatusLine === undefined ? `statusLine from ${settingsPath(o2.home)}` : `statusLine from ${settingsPath(o2.home)} (previous one restored)`);
7602
8508
  if (cfg.priorStatusLine !== undefined) {
7603
8509
  const { priorStatusLine: _restored, ...rest } = cfg;
7604
- writeConfig(o.home, rest);
8510
+ writeConfig(o2.home, rest);
7605
8511
  }
7606
8512
  }
7607
8513
  if (hasSeatHook(settings)) {
7608
8514
  settings = removeSeatHook(settings);
7609
8515
  changed = true;
7610
- removed.push(`hooks.Notification seat-event entry from ${settingsPath(o.home)}`);
8516
+ removed.push(`hooks.Notification seat-event entry from ${settingsPath(o2.home)}`);
7611
8517
  }
7612
8518
  if (hasPromptHook(settings)) {
7613
8519
  settings = removePromptHook(settings);
7614
8520
  changed = true;
7615
- removed.push(`hooks.UserPromptSubmit prompt-event entry from ${settingsPath(o.home)}`);
8521
+ removed.push(`hooks.UserPromptSubmit prompt-event entry from ${settingsPath(o2.home)}`);
7616
8522
  }
7617
8523
  if (hasSpecHook(settings)) {
7618
8524
  settings = removeSpecHook(settings);
7619
8525
  changed = true;
7620
- removed.push(`hooks.PostToolUse spec-event entry from ${settingsPath(o.home)}`);
8526
+ removed.push(`hooks.PostToolUse spec-event entry from ${settingsPath(o2.home)}`);
7621
8527
  }
7622
8528
  if (hasSessionHook(settings)) {
7623
8529
  settings = removeSessionHook(settings);
7624
8530
  changed = true;
7625
- removed.push(`hooks.SessionStart session-event entry from ${settingsPath(o.home)}`);
8531
+ removed.push(`hooks.SessionStart session-event entry from ${settingsPath(o2.home)}`);
7626
8532
  }
7627
8533
  if (hasStopHook(settings)) {
7628
8534
  settings = removeStopHook(settings);
7629
8535
  changed = true;
7630
- removed.push(`hooks.Stop stop-event entry from ${settingsPath(o.home)}`);
8536
+ removed.push(`hooks.Stop stop-event entry from ${settingsPath(o2.home)}`);
7631
8537
  }
7632
8538
  if (changed)
7633
- writeSettings(o.home, settings);
8539
+ writeSettings(o2.home, settings);
7634
8540
  }
7635
- for (const file of [meterCachePath(o.home), seatStatePath(o.home)]) {
7636
- if (existsSync15(file)) {
8541
+ for (const file of [meterCachePath(o2.home), seatStatePath(o2.home)]) {
8542
+ if (existsSync16(file)) {
7637
8543
  rmSync5(file, { force: true });
7638
8544
  removed.push(file);
7639
8545
  }
7640
8546
  }
7641
- const skill = skillDir(o.home);
7642
- if (existsSync15(skill)) {
8547
+ const skill = skillDir(o2.home);
8548
+ if (existsSync16(skill)) {
7643
8549
  rmSync5(skill, { recursive: true, force: true });
7644
8550
  removed.push(skill);
7645
8551
  }
7646
- const plist = musherPlistPath(o.home);
7647
- if (existsSync15(plist)) {
7648
- (o.unloadAgent ?? defaultUnload)(plist);
8552
+ const plist = musherPlistPath(o2.home);
8553
+ if (existsSync16(plist)) {
8554
+ (o2.unloadAgent ?? defaultUnload)(plist);
7649
8555
  rmSync5(plist, { force: true });
7650
8556
  removed.push(`launchd agent ${musherLabel()}`);
7651
8557
  }
@@ -7656,7 +8562,7 @@ var uninstall2 = (o) => {
7656
8562
  for (const r of removed)
7657
8563
  log(` ${r}`);
7658
8564
  }
7659
- log(`Kept ${tokenPath(o.home)} (delete it yourself if you want the token gone).`);
8565
+ log(`Kept ${tokenPath(o2.home)} (delete it yourself if you want the token gone).`);
7660
8566
  return 0;
7661
8567
  };
7662
8568
  var hasEitherKey = (settings) => {
@@ -7675,17 +8581,17 @@ var jobIdFromTarget = (target) => {
7675
8581
  return m[1];
7676
8582
  return /^[A-Za-z0-9_-]{3,64}$/.test(t) ? t : null;
7677
8583
  };
7678
- var verify = async (o) => {
7679
- const log = o.log ?? console.log;
7680
- const error = o.error ?? console.error;
7681
- const jobId = o.target ? jobIdFromTarget(o.target) : null;
8584
+ var verify = async (o2) => {
8585
+ const log = o2.log ?? console.log;
8586
+ const error = o2.error ?? console.error;
8587
+ const jobId = o2.target ? jobIdFromTarget(o2.target) : null;
7682
8588
  if (!jobId) {
7683
8589
  error("usage: sleds verify <jobId> (or paste the SLEDS-Attest line)");
7684
8590
  return 2;
7685
8591
  }
7686
- const client = o.client ?? (() => {
7687
- const src = meterSource(o.home);
7688
- return src ? musherClient({ ...src, fetchFn: o.fetchFn }) : null;
8592
+ const client = o2.client ?? (() => {
8593
+ const src = meterSource(o2.home);
8594
+ return src ? musherClient({ ...src, fetchFn: o2.fetchFn }) : null;
7689
8595
  })();
7690
8596
  if (!client) {
7691
8597
  error("No seat on this machine. Run `sleds init` first.");
@@ -7716,17 +8622,17 @@ var verify = async (o) => {
7716
8622
 
7717
8623
  // src/config.ts
7718
8624
  init_settings();
7719
- import { chmodSync as chmodSync2, existsSync as existsSync16, mkdirSync as mkdirSync13, readFileSync as readFileSync13, renameSync as renameSync6, writeFileSync as writeFileSync13 } from "node:fs";
8625
+ import { chmodSync as chmodSync2, existsSync as existsSync17, mkdirSync as mkdirSync13, readFileSync as readFileSync14, renameSync as renameSync6, writeFileSync as writeFileSync13 } from "node:fs";
7720
8626
  import { join as join13, resolve as resolve4 } from "node:path";
7721
8627
  var configDir2 = (home, env = process.env) => {
7722
- const o = env.SLEDS_HOME?.trim();
7723
- return o ? resolve4(home, o) : join13(home, ".config", "sleds");
8628
+ const o2 = env.SLEDS_HOME?.trim();
8629
+ return o2 ? resolve4(home, o2) : join13(home, ".config", "sleds");
7724
8630
  };
7725
8631
  var configPath2 = (home) => join13(configDir2(home), "config.json");
7726
- var isObject12 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
8632
+ var isObject13 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
7727
8633
  var optString2 = (v) => typeof v === "string" ? v : undefined;
7728
8634
  var parsePriorEnv2 = (v) => {
7729
- if (!isObject12(v))
8635
+ if (!isObject13(v))
7730
8636
  return;
7731
8637
  const out = {};
7732
8638
  const url = optString2(v[BASE_URL_KEY]);
@@ -7741,7 +8647,7 @@ var parsePriorEnv2 = (v) => {
7741
8647
  };
7742
8648
  var parseSchedule2 = (v) => v === "launchd" || v === "systemd" || v === "manual" ? v : undefined;
7743
8649
  var parseConfig2 = (raw) => {
7744
- if (!isObject12(raw))
8650
+ if (!isObject13(raw))
7745
8651
  return {};
7746
8652
  const cfg = {};
7747
8653
  const token = optString2(raw.token);
@@ -7779,13 +8685,13 @@ var parseConfig2 = (raw) => {
7779
8685
  const org = parseOrgInfo2(raw.org);
7780
8686
  if (org)
7781
8687
  cfg.org = org;
7782
- const musher = parseMusher2(raw.musher);
7783
- if (musher)
7784
- cfg.musher = musher;
8688
+ const musher2 = parseMusher2(raw.musher);
8689
+ if (musher2)
8690
+ cfg.musher = musher2;
7785
8691
  return cfg;
7786
8692
  };
7787
8693
  var parseMusher2 = (raw) => {
7788
- if (!isObject12(raw))
8694
+ if (!isObject13(raw))
7789
8695
  return;
7790
8696
  const out = {};
7791
8697
  const pm = optString2(raw.permissionMode);
@@ -7801,19 +8707,19 @@ var parseMusher2 = (raw) => {
7801
8707
  return out;
7802
8708
  };
7803
8709
  var parseOrgInfo2 = (raw) => {
7804
- if (!isObject12(raw) || !isObject12(raw.capabilities))
8710
+ if (!isObject13(raw) || !isObject13(raw.capabilities))
7805
8711
  return;
7806
8712
  const name = optString2(raw.name);
7807
- const slug = optString2(raw.slug);
7808
- const plan = raw.plan;
8713
+ const slug2 = optString2(raw.slug);
8714
+ const plan2 = raw.plan;
7809
8715
  const c = raw.capabilities;
7810
8716
  const src = c.upstreamSource;
7811
- if (name === undefined || slug === undefined || plan !== "team" && plan !== "enterprise" && plan !== "api" && plan !== "individual" || src !== "own" && src !== "shared" && src !== "none")
8717
+ if (name === undefined || slug2 === undefined || plan2 !== "team" && plan2 !== "enterprise" && plan2 !== "api" && plan2 !== "individual" || src !== "own" && src !== "shared" && src !== "none")
7812
8718
  return;
7813
8719
  return {
7814
8720
  name,
7815
- slug,
7816
- plan,
8721
+ slug: slug2,
8722
+ plan: plan2,
7817
8723
  capabilities: {
7818
8724
  upstreamKey: c.upstreamKey === true,
7819
8725
  adminKey: c.adminKey === true,
@@ -7824,10 +8730,10 @@ var parseOrgInfo2 = (raw) => {
7824
8730
  };
7825
8731
  };
7826
8732
  var readConfig2 = (home) => {
7827
- const p = configPath2(home);
7828
- if (!existsSync16(p))
8733
+ const p2 = configPath2(home);
8734
+ if (!existsSync17(p2))
7829
8735
  return {};
7830
- return parseConfig2(JSON.parse(readFileSync13(p, "utf8")));
8736
+ return parseConfig2(JSON.parse(readFileSync14(p2, "utf8")));
7831
8737
  };
7832
8738
 
7833
8739
  // src/schedule.ts
@@ -7853,12 +8759,12 @@ var withSeatEnv2 = (command, home, env = process.env) => {
7853
8759
  return parts.join(" ");
7854
8760
  };
7855
8761
  var shellWord2 = (v) => /^[\w./~-]+$/.test(v) ? v : `'${v.replace(/'/g, "'\\''")}'`;
7856
- var isObject13 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
8762
+ var isObject14 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
7857
8763
  var isSledsHookFor2 = (sub) => (entry) => {
7858
- if (!isObject13(entry) || !Array.isArray(entry.hooks))
8764
+ if (!isObject14(entry) || !Array.isArray(entry.hooks))
7859
8765
  return false;
7860
8766
  const re = new RegExp(`\\b${sub}\\b`);
7861
- return entry.hooks.some((h) => isObject13(h) && h.type === "command" && typeof h.command === "string" && re.test(h.command));
8767
+ return entry.hooks.some((h) => isObject14(h) && h.type === "command" && typeof h.command === "string" && re.test(h.command));
7862
8768
  };
7863
8769
  var isSledsSessionHook2 = isSledsHookFor2("session-event");
7864
8770
  var isSledsStopHook2 = isSledsHookFor2("stop-event");
@@ -7964,8 +8870,8 @@ var stringFlag = (flags, name) => {
7964
8870
  var hookStdin = async () => {
7965
8871
  if (process.stdin.isTTY)
7966
8872
  return;
7967
- await Promise.resolve().then(() => init_seat());
7968
- const text = await readStdin2(300);
8873
+ const { readStdin: readStdin3 } = await Promise.resolve().then(() => (init_seat(), exports_seat));
8874
+ const text = await readStdin3(300);
7969
8875
  return text.trim() ? text : undefined;
7970
8876
  };
7971
8877
  var sledsArgv = () => {
@@ -8118,7 +9024,7 @@ ${renderHelpText(argv[1])}`);
8118
9024
  case "version":
8119
9025
  case "--version":
8120
9026
  case "-v":
8121
- console.log("sleds 0.1.24");
9027
+ console.log("sleds 0.1.26");
8122
9028
  return 0;
8123
9029
  default:
8124
9030
  console.error(`unknown command: ${cmd}