@sleds/cli 0.1.23 → 0.1.24

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 +1609 -1134
  2. package/package.json +1 -1
package/dist/sleds.mjs CHANGED
@@ -1,22 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  // @bun
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
- });
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;
17
13
  };
18
- var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
19
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
20
14
 
21
15
  // ../../packages/shared/src/mcp/format.ts
22
16
  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) => {
@@ -158,8 +152,8 @@ var init_format = __esm(() => {
158
152
  import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
159
153
  import { dirname, join, resolve } from "node:path";
160
154
  var BASE_URL_KEY = "ANTHROPIC_BASE_URL", AUTH_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN", claudeDir = (home, env = process.env) => {
161
- const o2 = env.CLAUDE_CONFIG_DIR?.trim();
162
- return o2 ? resolve(home, o2) : join(home, ".claude");
155
+ const o = env.CLAUDE_CONFIG_DIR?.trim();
156
+ return o ? resolve(home, o) : join(home, ".claude");
163
157
  }, 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) => {
164
158
  const parts = [];
165
159
  const sh = env.SLEDS_HOME?.trim();
@@ -189,8 +183,8 @@ var BASE_URL_KEY = "ANTHROPIC_BASE_URL", AUTH_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN"
189
183
  visit(settings.hooks);
190
184
  return out;
191
185
  }, 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) => {
192
- const o2 = env.SLEDS_HOME?.trim();
193
- return o2 ? join(resolve(home, o2), ".sleds-token") : join(home, ".sleds-token");
186
+ const o = env.SLEDS_HOME?.trim();
187
+ return o ? join(resolve(home, o), ".sleds-token") : join(home, ".sleds-token");
194
188
  }, 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) => {
195
189
  const env = settings.env;
196
190
  return isObject5(env) ? env : {};
@@ -326,12 +320,12 @@ var BASE_URL_KEY = "ANTHROPIC_BASE_URL", AUTH_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN"
326
320
  return withEventHooks(settings, event, list.filter((e) => !ours(e)));
327
321
  }, 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)}
328
322
  `, hasClaudeCode = (home) => existsSync(claudeDir(home)), readSettings = (home) => {
329
- const p2 = settingsPath(home);
330
- if (!existsSync(p2))
323
+ const p = settingsPath(home);
324
+ if (!existsSync(p))
331
325
  return {};
332
- const parsed = JSON.parse(readFileSync(p2, "utf8"));
326
+ const parsed = JSON.parse(readFileSync(p, "utf8"));
333
327
  if (!isObject5(parsed))
334
- throw new Error(`${p2} is not a JSON object`);
328
+ throw new Error(`${p} is not a JSON object`);
335
329
  return parsed;
336
330
  }, writeSettings = (home, settings) => {
337
331
  mkdirSync(claudeDir(home), { recursive: true });
@@ -347,10 +341,10 @@ var BASE_URL_KEY = "ANTHROPIC_BASE_URL", AUTH_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN"
347
341
  writeFileSync(dst, readFileSync(src));
348
342
  return true;
349
343
  }, readTokenFile = (home) => {
350
- const p2 = tokenPath(home);
351
- if (!existsSync(p2))
344
+ const p = tokenPath(home);
345
+ if (!existsSync(p))
352
346
  return null;
353
- const t = readFileSync(p2, "utf8").trim();
347
+ const t = readFileSync(p, "utf8").trim();
354
348
  return t.length > 0 ? t : null;
355
349
  }, writeTokenFile = (home, token) => {
356
350
  mkdirSync(dirname(tokenPath(home)), { recursive: true, mode: 448 });
@@ -366,8 +360,8 @@ var init_settings = __esm(() => {
366
360
  import { chmodSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "node:fs";
367
361
  import { join as join2, resolve as resolve2 } from "node:path";
368
362
  var configDir = (home, env = process.env) => {
369
- const o2 = env.SLEDS_HOME?.trim();
370
- return o2 ? resolve2(home, o2) : join2(home, ".config", "sleds");
363
+ const o = env.SLEDS_HOME?.trim();
364
+ return o ? resolve2(home, o) : join2(home, ".config", "sleds");
371
365
  }, 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) => {
372
366
  if (!isObject6(v))
373
367
  return;
@@ -444,15 +438,15 @@ var configDir = (home, env = process.env) => {
444
438
  return;
445
439
  const name = optString(raw.name);
446
440
  const slug = optString(raw.slug);
447
- const plan2 = raw.plan;
441
+ const plan = raw.plan;
448
442
  const c = raw.capabilities;
449
443
  const src = c.upstreamSource;
450
- if (name === undefined || slug === undefined || plan2 !== "team" && plan2 !== "enterprise" && plan2 !== "api" && plan2 !== "individual" || src !== "own" && src !== "shared" && src !== "none")
444
+ if (name === undefined || slug === undefined || plan !== "team" && plan !== "enterprise" && plan !== "api" && plan !== "individual" || src !== "own" && src !== "shared" && src !== "none")
451
445
  return;
452
446
  return {
453
447
  name,
454
448
  slug,
455
- plan: plan2,
449
+ plan,
456
450
  capabilities: {
457
451
  upstreamKey: c.upstreamKey === true,
458
452
  adminKey: c.adminKey === true,
@@ -462,10 +456,10 @@ var configDir = (home, env = process.env) => {
462
456
  mode: raw.mode === "proxy-always" ? "proxy-always" : "seat-first"
463
457
  };
464
458
  }, readConfig = (home) => {
465
- const p2 = configPath(home);
466
- if (!existsSync2(p2))
459
+ const p = configPath(home);
460
+ if (!existsSync2(p))
467
461
  return {};
468
- return parseConfig(JSON.parse(readFileSync2(p2, "utf8")));
462
+ return parseConfig(JSON.parse(readFileSync2(p, "utf8")));
469
463
  }, writeConfig = (home, cfg) => {
470
464
  const dir = configDir(home);
471
465
  mkdirSync2(dir, { recursive: true, mode: 448 });
@@ -484,11 +478,11 @@ var init_config = __esm(() => {
484
478
  import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync4, renameSync as renameSync3, writeFileSync as writeFileSync4 } from "node:fs";
485
479
  import { join as join4 } from "node:path";
486
480
  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) => {
487
- const p2 = meterCachePath(home);
488
- if (!existsSync4(p2))
481
+ const p = meterCachePath(home);
482
+ if (!existsSync4(p))
489
483
  return null;
490
484
  try {
491
- const raw = JSON.parse(readFileSync4(p2, "utf8"));
485
+ const raw = JSON.parse(readFileSync4(p, "utf8"));
492
486
  if (!isObject7(raw))
493
487
  return null;
494
488
  const fetchedAt = num4(raw.fetchedAt);
@@ -507,12 +501,12 @@ var METER_CACHE_TTL_MS = 1e4, METER_TIMEOUT_MS = 1500, isObject7 = (v) => typeof
507
501
  writeFileSync4(tmp, `${JSON.stringify(file)}
508
502
  `, { mode: 384 });
509
503
  renameSync3(tmp, target);
510
- }, fetchMeter = async (o2) => {
511
- const fetchFn = o2.fetchFn ?? fetch;
504
+ }, fetchMeter = async (o) => {
505
+ const fetchFn = o.fetchFn ?? fetch;
512
506
  try {
513
- const res = await fetchFn(`${o2.proxyUrl}/proxy/meter`, {
514
- headers: { authorization: `Bearer ${o2.token}` },
515
- signal: AbortSignal.timeout(o2.timeoutMs ?? METER_TIMEOUT_MS)
507
+ const res = await fetchFn(`${o.proxyUrl}/proxy/meter`, {
508
+ headers: { authorization: `Bearer ${o.token}` },
509
+ signal: AbortSignal.timeout(o.timeoutMs ?? METER_TIMEOUT_MS)
516
510
  });
517
511
  if (!res.ok)
518
512
  return null;
@@ -527,18 +521,18 @@ var METER_CACHE_TTL_MS = 1e4, METER_TIMEOUT_MS = 1500, isObject7 = (v) => typeof
527
521
  const proxyUrl = inSettings?.url ?? cfg.proxyUrl;
528
522
  const token = inSettings?.token ?? cfg.token;
529
523
  return proxyUrl && token ? { proxyUrl, token } : null;
530
- }, loadMeter = async (o2) => {
531
- const now = o2.now ?? Date.now();
532
- const cached = readMeterCache(o2.home, now);
524
+ }, loadMeter = async (o) => {
525
+ const now = o.now ?? Date.now();
526
+ const cached = readMeterCache(o.home, now);
533
527
  if (cached)
534
528
  return cached;
535
- const src = meterSource(o2.home);
529
+ const src = meterSource(o.home);
536
530
  if (!src)
537
531
  return null;
538
- const fresh = await fetchMeter({ ...src, fetchFn: o2.fetchFn, timeoutMs: o2.timeoutMs });
532
+ const fresh = await fetchMeter({ ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs });
539
533
  if (fresh) {
540
534
  try {
541
- writeMeterCache(o2.home, fresh, now);
535
+ writeMeterCache(o.home, fresh, now);
542
536
  } catch {}
543
537
  }
544
538
  return fresh;
@@ -551,432 +545,9 @@ var init_meter = __esm(() => {
551
545
  });
552
546
 
553
547
  // src/seat.ts
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 existsSync17,
591
- mkdirSync as mkdirSync14,
592
- openSync as openSync2,
593
- readFileSync as readFileSync14,
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, isObject14 = (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 n = num7(v);
602
- if (n !== undefined) {
603
- if (n <= 0)
604
- return;
605
- return n >= EPOCH_MS_THRESHOLD2 ? Math.round(n) : Math.round(n * 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 (!isObject14(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 (!isObject14(raw))
622
- return null;
623
- const limits = isObject14(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 (isObject14(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 (!isObject14(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 (!isObject14(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 (isObject14(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 (isObject14(raw.warned)) {
717
- const warned = {};
718
- for (const bar of ["fiveHour", "sevenDay", "spendLimit", "credits"]) {
719
- const w = raw.warned[bar];
720
- if (!isObject14(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 (isObject14(raw.told)) {
734
- const told = {};
735
- for (const bar of ["fiveHour", "sevenDay", "spendLimit", "credits"]) {
736
- const w = raw.told[bar];
737
- if (!isObject14(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 (isObject14(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 (isObject14(raw.cockpit) && raw.cockpit.kind === "cmux") {
763
- const c = { kind: "cmux" };
764
- if (isObject14(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 (isObject14(f) && num7(f.at) !== undefined) {
775
- const strs = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
776
- const j = isObject14(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 (isObject14(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 (!isObject14(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 (!isObject14(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 = isObject14(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 (!existsSync17(p2))
845
- return {};
846
- try {
847
- return parseSeatState2(JSON.parse(readFileSync14(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 isObject14(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) => {
548
+ var SEAT_POST_MIN_INTERVAL_MS2, readStdin2 = (timeoutMs, stdin = process.stdin) => new Promise((resolve) => {
978
549
  if (stdin.isTTY) {
979
- resolve6("");
550
+ resolve("");
980
551
  return;
981
552
  }
982
553
  let data = "";
@@ -986,7 +557,7 @@ var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_
986
557
  return;
987
558
  settled = true;
988
559
  clearTimeout(timer);
989
- resolve6(data);
560
+ resolve(data);
990
561
  };
991
562
  const timer = setTimeout(finish, timeoutMs);
992
563
  stdin.setEncoding("utf8");
@@ -1001,12 +572,11 @@ var init_seat = __esm(() => {
1001
572
  init_config();
1002
573
  init_meter();
1003
574
  SEAT_POST_MIN_INTERVAL_MS2 = 5 * 60000;
1004
- CREDITS_PATTERN2 = /out of usage credits|cc_cli_limit_message/i;
1005
575
  });
1006
576
 
1007
577
  // src/index.ts
1008
578
  import { realpathSync } from "fs";
1009
- import { homedir, platform as platform2 } from "os";
579
+ import { homedir, platform as platform3 } from "os";
1010
580
  import { fileURLToPath } from "url";
1011
581
  // ../../packages/shared/src/pricing.ts
1012
582
  var p = (input, output, cacheWrite, cacheWrite1h, cacheRead) => ({ input, output, cacheWrite, cacheWrite1h, cacheRead });
@@ -1183,6 +753,33 @@ var AUDIENCE_LABEL = {
1183
753
  everyone: "Each person, once"
1184
754
  };
1185
755
  var GUIDES = [
756
+ {
757
+ id: "board",
758
+ title: "Read the board",
759
+ 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.",
761
+ steps: [
762
+ {
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."
766
+ },
767
+ {
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`."
770
+ },
771
+ {
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."
774
+ },
775
+ {
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."
778
+ }
779
+ ],
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."
782
+ },
1186
783
  {
1187
784
  id: "workspace",
1188
785
  title: "Set up a workspace",
@@ -1243,9 +840,14 @@ var GUIDES = [
1243
840
  note: "Your seat's bars and credits come back. If it answers that it does not know you, your invite and your Claude sign-in are different addresses — an admin fixes the member row."
1244
841
  },
1245
842
  {
1246
- do: "Claude Code and the terminal: install the CLI and sign in with your seat token",
843
+ do: "Start asks in a Claude Project that has SLEDS on",
844
+ where: "claude.ai › Projects › New project › Connectors: SLEDS",
845
+ note: 'One line of project instructions — "Work described here goes on the sled: use SLEDS intake" — and nobody has to remember the connector. Anywhere else, "put it on the sled" is enough; the brief appearing as a card is the sign SLEDS is on.'
846
+ },
847
+ {
848
+ do: "Claude Code and the terminal: install the CLI and sign in through the browser",
1247
849
  where: "`curl -fsSL sleds.ai/install | sh` then `sleds init`",
1248
- note: "Installs the `/sled` skill and the status line. `sleds doctor` says what is wired."
850
+ note: "`sleds init` opens app.sleds.ai/connect with a code; you sign in there (the same 6-digit code to your work email), approve, and the machine is your seat — nothing to paste, and you are in the workspace on the web in the same motion. Installs the `/sled` skill and the status line; `sleds doctor` says what is wired."
1249
851
  },
1250
852
  {
1251
853
  do: "Optional: SLEDS for Chrome — the meter, and the Team roster and credits when you administer the plan",
@@ -1262,18 +864,9 @@ var GUIDES = [
1262
864
  lede: "One GitHub App installation on your org: SLEDS sees PRs open and merge, and — only when someone asks — files and runs work from issues.",
1263
865
  steps: [
1264
866
  {
1265
- do: "Install the `sleds-ai` GitHub App on your org",
1266
- where: "github.com/apps/sleds-ai › `Install` › your org › `Only select repositories`",
1267
- note: "Pick the engineering repos. Do this signed in as an owner of the org; a personal account's settings page will not list an app installed on the org."
1268
- },
1269
- {
1270
- do: "Copy the installation number",
1271
- note: "The page you land on ends in `/installations/<number>`. That number is the installation id."
1272
- },
1273
- {
1274
- do: "Connect the installation to this workspace",
1275
- where: "Settings › Integrations › GitHub › `Connect`",
1276
- note: "`App installation id` is the number; `Default repo (owner/name)` is where an ask filed from chat would land if it named no repo. The webhook secret is set on the app, not here — leave it blank."
867
+ do: "Install on GitHub",
868
+ where: "Settings › Integrations › GitHub › `Install on GitHub`",
869
+ note: "GitHub's own screen opens: pick your org and `Only select repositories` → the engineering repos. Do this signed in as an owner of the org (GitHub asks a member to request it instead — if that happens, come back and click the button again once an owner has installed). You land back here with the org named on the tile; nothing to copy."
1277
870
  },
1278
871
  {
1279
872
  do: "Read-only for now: skip `Send test`",
@@ -1282,7 +875,7 @@ var GUIDES = [
1282
875
  {
1283
876
  do: "To keep every PR in sync: add the ledger workflow to each repo",
1284
877
  where: "`.github/workflows/sleds-ledger.yml` from the SLEDS repo, with a repo secret `SLEDS_TOKEN`",
1285
- note: "Mint a seat for CI (Settings › People, under a service person) and store its token as the secret. Then every PR is sized when it opens and closed on merge with the `SLEDS-Actual:` line from its body — the est → actual column on the Sled. It comments on the PR; leave it out while strictly read-only."
878
+ note: "Mint a seat for CI (Settings › People, under a service person) and store its token as the secret. Then every PR is sized when it opens and closed on merge with the `SLEDS-Actual:` line from its body — the est → actual column on Record › Receipts. It comments on the PR; leave it out while strictly read-only."
1286
879
  },
1287
880
  {
1288
881
  do: "When you are ready to write: add a `sled` label to the repos",
@@ -1311,7 +904,7 @@ var GUIDES = [
1311
904
  {
1312
905
  do: "Connect the site and name the projects to mirror",
1313
906
  where: "Settings › Integrations › Jira › `Connect`",
1314
- note: "`Site` is the host (`acme.atlassian.net`). `Projects to mirror, read-only` takes project keys (`WORK, CORE`): their open issues become sized rows on the Sled, 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."
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."
1315
908
  },
1316
909
  {
1317
910
  do: "Prove the token with `Send test` and the issue key left blank",
@@ -1323,47 +916,48 @@ var GUIDES = [
1323
916
  note: "URL `https://<your deployment>.convex.site/jira/webhook` with a secret; events `issue created`, `issue updated`. Paste the same secret in `Webhook secret` on the Connect form. It acts only on issues labelled `sled`, `sled:now` or `intake`, or moved to `Ready for Sleds`."
1324
917
  }
1325
918
  ],
1326
- 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 Sled lists the mirrored project's open issues with a band on each.",
919
+ 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.",
1327
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."
1328
921
  },
1329
922
  {
1330
923
  id: "slack",
1331
924
  title: "Connect Slack",
1332
925
  audience: "admin",
1333
- lede: "A channel webhook for hand-offs, walls and digests; a bot token if you want the named person DMed.",
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.",
1334
927
  steps: [
1335
928
  {
1336
- do: "Create a Slack app in your workspace and turn on Incoming Webhooks",
1337
- where: "api.slack.com/apps › `Create New App` › `Incoming Webhooks`",
1338
- note: "Add one to the channel where the team talks about work in flight; copy its URL."
1339
- },
1340
- {
1341
- do: "Store the webhook",
1342
- where: "Settings › Platform keys › provider `slack_webhook`"
929
+ do: "Add to Slack",
930
+ where: "Settings › Integrations › Slack › `Add to Slack`",
931
+ note: "Slack's own screen opens: choose the channel SLEDS posts to (private channels you belong to are in the list) and allow. You are back here in a few seconds with the team and channel shown on the tile. Nobody at SLEDS is involved and no key touches a clipboard."
1343
932
  },
1344
933
  {
1345
- do: "Optional: the bot, for DMs",
1346
- where: "The same app › `OAuth & Permissions` › bot scopes `chat:write`, `users:read`, `users:read.email` › install › the `xoxb-` token",
1347
- note: "Store it under provider `slack`. With it, a hand-off to a named coworker DMs them; without it, hand-offs post to the channel only."
934
+ do: "If the channel is private, let the bot in",
935
+ where: "In Slack, in that channel: `/invite @SLEDS`",
936
+ note: "Posting to the channel works from the moment you allowed; reading and replying in a private channel needs the bot as a member."
1348
937
  },
1349
938
  {
1350
939
  do: "Send one test post",
1351
940
  where: "Settings › Integrations › Slack › `Send test`",
1352
- note: "One real post to the channel (and a DM when the bot is on). Safe to run any time."
941
+ note: "One real post to the channel and one DM to you. Safe to run any time. If the DM part says Slack has no user for your email, you sign in to Slack with another address — set it under Settings › People › you › `Slack email, if different`."
942
+ },
943
+ {
944
+ do: "Advanced: keys by hand",
945
+ where: "Settings › Platform keys › `slack_webhook` and `slack`",
946
+ note: "Only for a Slack app of your own. Add to Slack writes both of these for you."
1353
947
  }
1354
948
  ],
1355
- check: "The test post arrives in the channel. From then on a hand-off, a wall, and a product decision each post there."
949
+ check: "The test post arrives in the channel and the DM in your Slack. From then on a hand-off, a wall, and a product decision each post there; a hand-off to a named coworker reaches them directly."
1356
950
  },
1357
951
  {
1358
952
  id: "approval",
1359
953
  title: "Turn on product's yes",
1360
954
  audience: "admin",
1361
- lede: "The gate in front of ready: asks wait until someone product-side says yes or defers with a note.",
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.",
1362
956
  steps: [
1363
957
  {
1364
- do: "Choose which asks wait",
1365
- where: "Policy › Product's yes › `Which asks wait`",
1366
- note: "`Asks from intake wait for product` gates what the business side files; `Every row waits for product` gates engineers' own briefs too. `Never` is the default."
958
+ do: "Choose who needs a yes",
959
+ where: "Policy › Product's yes › `Who needs a yes`",
960
+ note: "`Asks filed from chat` gates what the business side files and leaves tracker issues ready as they are; `Everything` gates engineers' own briefs too. `Nobody` is the default — everything filed is ready to take."
1367
961
  },
1368
962
  {
1369
963
  do: "Name the approvers",
@@ -1371,8 +965,8 @@ var GUIDES = [
1371
965
  note: "Emails, comma-separated. Blank means any owner or admin of the workspace."
1372
966
  },
1373
967
  {
1374
- do: "Approvers decide from the Sled or Jobs",
1375
- where: "Jobs › `Waiting on you`, or the row on the Sled",
968
+ do: "Approvers decide from the board or the Overview",
969
+ where: "Overview › `Waiting on product`, or the row on Sleds",
1376
970
  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?}`."
1377
971
  },
1378
972
  {
@@ -1380,7 +974,7 @@ var GUIDES = [
1380
974
  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."
1381
975
  }
1382
976
  ],
1383
- check: "File an ask from chat (`intake`): its row shows waiting for product on the Sled, the approvers see it under Waiting on you, and `run` on it is refused until someone approves."
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."
1384
978
  }
1385
979
  ];
1386
980
  function findGuides(filter) {
@@ -1436,7 +1030,7 @@ var VERBS = [
1436
1030
  want: "See my overnight jobs and the team's open hand-offs",
1437
1031
  mcp: "jobs · handoffs",
1438
1032
  claudeCode: "/sled jobs · `sleds handoffs`",
1439
- app: "Jobs",
1033
+ app: "Sleds › Moving (the runs on each seat's sled) · Record › Receipts once they land",
1440
1034
  how: "Newest first: id, status, route, cost. Hand-offs meant for you are listed first."
1441
1035
  },
1442
1036
  {
@@ -1444,16 +1038,40 @@ var VERBS = [
1444
1038
  group: "meter",
1445
1039
  want: "Ask where a piece of work is",
1446
1040
  mcp: "status {q?} — a tracker key or a word from the title; nothing for what you filed",
1447
- app: "Sled (the lane strip and the row's stage: waiting for product · deferred · ready · on the sled · landed)",
1041
+ app: "Sleds (the strip and the row's stage: waiting for product · deferred · ready · moving · landed)",
1448
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."
1449
1043
  },
1044
+ {
1045
+ id: "touches",
1046
+ group: "meter",
1047
+ want: "Know what a change reaches before I ask for it",
1048
+ mcp: "touches {text}",
1049
+ app: "Settings › Integrations › GitHub › Product map",
1050
+ how: "The product map — built from the workspace's repos on every merge, no model — says which areas of the product an ask reaches, which of the product's own documents it names, and what on the board collides with it. Your AI reads it back in plain words; intake reads it on its own while drafting."
1051
+ },
1052
+ {
1053
+ id: "attach",
1054
+ 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."
1059
+ },
1060
+ {
1061
+ id: "answer",
1062
+ group: "meter",
1063
+ want: "Answer a question engineering asked about something I filed",
1064
+ 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."
1067
+ },
1450
1068
  {
1451
1069
  id: "estimate",
1452
1070
  group: "size",
1453
1071
  want: "Size a task before running it",
1454
1072
  mcp: "estimate {spec, source?}",
1455
1073
  claudeCode: "/sled estimate · `sleds spec-event` fires on save of a spec file",
1456
- app: "Sled (rows appear as specs and issues are sized)",
1074
+ app: "Sleds (rows appear in the pool as specs and issues are sized)",
1457
1075
  how: "A cost and time band with confidence and a spend cap — never a point figure. Recorded; the actual calibrates the next one."
1458
1076
  },
1459
1077
  {
@@ -1462,7 +1080,7 @@ var VERBS = [
1462
1080
  want: "Say what the work is worth to the business",
1463
1081
  mcp: "a `Worth:` line in the spec you estimate (`Worth: high · $5k–20k · Acme renewal`)",
1464
1082
  claudeCode: "`SLEDS-Worth:` line in the PR body",
1465
- app: "Sled › … › Worth",
1083
+ app: "Sleds › the row › Worth…",
1466
1084
  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."
1467
1085
  },
1468
1086
  {
@@ -1470,15 +1088,15 @@ var VERBS = [
1470
1088
  group: "send",
1471
1089
  want: "Send an ask to engineering from the conversation I already have open",
1472
1090
  mcp: "intake {text, links?, answers?, confirm?, parent?}",
1473
- app: "Sled (the row lands sized, filed by you, at waiting or ready)",
1474
- 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 — 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."
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."
1475
1093
  },
1476
1094
  {
1477
1095
  id: "plan",
1478
1096
  group: "send",
1479
1097
  want: "Turn an approved epic into milestones I can send one at a time",
1480
1098
  mcp: "plan {q | project, milestones?, confirm?} — the epic first, then the drafted list",
1481
- app: "Sled (the epic row expands into its milestones; the tree under its band)",
1099
+ app: "Sleds (the epic row expands into its milestones; the tree under its band) · Approve · plan it",
1482
1100
  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."
1483
1101
  },
1484
1102
  {
@@ -1486,7 +1104,7 @@ var VERBS = [
1486
1104
  group: "send",
1487
1105
  want: "Give product's yes on an ask — or say not now, and why",
1488
1106
  mcp: "approve {q, decision: approve | defer, note?}",
1489
- app: "Sled › Approve / Defer on a waiting row · Jobs › Waiting on you · Policy › Product's yes",
1107
+ app: "Sleds › Approve / Defer on a waiting row · Overview › Waiting on product · Policy › Product's yes",
1490
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."
1491
1109
  },
1492
1110
  {
@@ -1494,7 +1112,7 @@ var VERBS = [
1494
1112
  group: "send",
1495
1113
  want: "Ask where a piece of work should go, then send it there",
1496
1114
  mcp: "dispatch {q, run?: now | park | overnight, to?} — suggests with no run",
1497
- app: "Sled › Take it (onto your own seat) · Sled › … › Send to… (the park path)",
1115
+ app: "Sleds › Take it (onto your own sled) · Sleds › the row › Send to… · Put it on <seat>'s sled",
1498
1116
  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."
1499
1117
  },
1500
1118
  {
@@ -1503,7 +1121,7 @@ var VERBS = [
1503
1121
  want: "Send work to a teammate or a buddy",
1504
1122
  mcp: "handoff {task, to?, note?}",
1505
1123
  claudeCode: "/sled handoff · the wall popup",
1506
- app: "Sled › … › Send to… · extension › Park hand-off",
1124
+ app: "Sleds › the row › Send to… · extension › Park hand-off",
1507
1125
  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."
1508
1126
  },
1509
1127
  {
@@ -1512,7 +1130,7 @@ var VERBS = [
1512
1130
  want: "Take back a hand-off nobody has picked up",
1513
1131
  mcp: "withdraw {handoffId}",
1514
1132
  claudeCode: "`sleds withdraw <id>`",
1515
- app: "Jobs",
1133
+ app: "Sleds › the row › Withdraw",
1516
1134
  how: "Only the seat that parked it can withdraw it. Unpicked hand-offs are reminded after a day and expire after three."
1517
1135
  },
1518
1136
  {
@@ -1521,7 +1139,7 @@ var VERBS = [
1521
1139
  want: "Pick up work someone sent me",
1522
1140
  mcp: "handoffs → pickup {handoffId}",
1523
1141
  claudeCode: "/sled pickup <id> · `sleds pickup <id>`",
1524
- app: "Jobs › Take it · extension › Take it",
1142
+ app: "Sleds › Take it · extension › Take it",
1525
1143
  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."
1526
1144
  },
1527
1145
  {
@@ -1531,7 +1149,7 @@ var VERBS = [
1531
1149
  mcp: "defer {at?, task?}",
1532
1150
  claudeCode: "`sleds defer` · the wall popup › Hold until reset",
1533
1151
  app: "wall popup",
1534
- how: "The musher on your Mac resumes the session at the binding reset; no key needed. `+2h` or an ISO time overrides the reset."
1152
+ how: "The night shift on your Mac resumes the session at the binding reset; no key needed. `+2h` or an ISO time overrides the reset."
1535
1153
  },
1536
1154
  {
1537
1155
  id: "overnight",
@@ -1539,7 +1157,7 @@ var VERBS = [
1539
1157
  want: "Run this tonight at half price",
1540
1158
  mcp: "overnight {spec, source?}",
1541
1159
  claudeCode: "/sled overnight",
1542
- app: "wall popup › Run overnight · Jobs",
1160
+ app: "wall popup › Run overnight · Sleds › the Night shift seat",
1543
1161
  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."
1544
1162
  },
1545
1163
  {
@@ -1547,8 +1165,8 @@ var VERBS = [
1547
1165
  group: "defer",
1548
1166
  want: "Pull a finished overnight job into my session",
1549
1167
  claudeCode: "/sled resume <id>",
1550
- app: "Jobs",
1551
- how: "The delivered result, with its attestation — which model built it, signed by the workspace key."
1168
+ app: "Sleds › the Night shift seat › the row",
1169
+ how: "The delivered result, with its record of which model built it — which model built it, signed by the workspace key."
1552
1170
  },
1553
1171
  {
1554
1172
  id: "actual",
@@ -1556,7 +1174,7 @@ var VERBS = [
1556
1174
  want: "Record what a piece of work actually took",
1557
1175
  mcp: "actual {source, minutes?, usd?, model?, ref?}",
1558
1176
  claudeCode: "`SLEDS-Actual: 45 min · $7` in the PR body — the ledger posts it on merge",
1559
- app: "Sled › Est → actual (fills in on merge or delivery)",
1177
+ app: "Record › Receipts (fills in on merge or delivery) · Sleds › Landed this week",
1560
1178
  how: "Closes the estimate: est → actual per row, and the calibration for the next band. A delivered job closes its own."
1561
1179
  },
1562
1180
  {
@@ -1564,7 +1182,7 @@ var VERBS = [
1564
1182
  group: "close",
1565
1183
  want: "Check which model built something",
1566
1184
  claudeCode: "`sleds verify <jobId | SLEDS-Attest line>`",
1567
- app: "Jobs › attestation",
1185
+ app: "Record › Receipts › the row's ref",
1568
1186
  how: "Ed25519 over the job, model, outputs and time; verified locally against the workspace's public key."
1569
1187
  },
1570
1188
  {
@@ -1579,7 +1197,7 @@ var VERBS = [
1579
1197
  id: "team",
1580
1198
  group: "people",
1581
1199
  want: "See the team's work — in flight, landed, waiting",
1582
- app: "Overview · Sled · Jobs at the team zoom",
1200
+ app: "Overview · Sleds · Record at the team zoom",
1583
1201
  how: "Same screens, wider scope, summed by role. Aggregates by default; nobody's spend to anyone else without their say-so."
1584
1202
  },
1585
1203
  {
@@ -1686,14 +1304,16 @@ var QUESTIONS = {
1686
1304
  done: "What does done look like, in your words?",
1687
1305
  smallest: "What is the smallest version still worth doing?",
1688
1306
  worth: "What is it worth to the business — a customer, a number, or high / medium / low?",
1689
- touches: "What does it touch — which product areas or systems?"
1307
+ touches: "What does it touch — which product areas or systems, and where in the product would it live?",
1308
+ demand: "How many people have asked for this, or how often does it come up?"
1690
1309
  };
1691
1310
  var REQUIRED_TO_FILE = ["why", "done"];
1692
1311
  var HEADINGS = {
1693
1312
  why: /^(why|context|background|problem|motivation|goal|purpose|the ask)\b/i,
1694
1313
  done: /^(done|done when|definition of done|acceptance|acceptance criteria|success|outcome|expected)\b/i,
1695
1314
  smallest: /^(smallest|smallest version|mvp|minimum|minimal|first cut|v1|scope|in scope)\b/i,
1696
- touches: /^(touches|affects|areas?|surfaces?|systems?|components?|files)\b/i
1315
+ touches: /^(touches|affects|areas?|surfaces?|systems?|components?|files|where)\b/i,
1316
+ demand: /^(demand|requests?|requested by|how many|how often|asked for|frequency|volume)\b/i
1697
1317
  };
1698
1318
  var clip = (s, max) => {
1699
1319
  const t = s.replace(/\s+/g, " ").trim();
@@ -1788,13 +1408,14 @@ function draftBrief(input) {
1788
1408
  done: pick("done"),
1789
1409
  smallest: pick("smallest"),
1790
1410
  touches: pick("touches"),
1411
+ demand: pick("demand"),
1791
1412
  ...worth ? { worth } : {},
1792
1413
  sources
1793
1414
  };
1794
1415
  }
1795
1416
  function openQuestions(b) {
1796
1417
  const out = [];
1797
- const keys = ["why", "done", "smallest", "worth", "touches"];
1418
+ const keys = ["why", "done", "smallest", "worth", "touches", "demand"];
1798
1419
  for (const key of keys) {
1799
1420
  const missing = key === "worth" ? !b.worth : !b[key];
1800
1421
  if (missing)
@@ -1805,7 +1426,7 @@ function openQuestions(b) {
1805
1426
  function missingToFile(b) {
1806
1427
  return REQUIRED_TO_FILE.filter((k) => !b[k]);
1807
1428
  }
1808
- var fromWord = (f) => f.from === "filer" ? "from you" : "from what you sent";
1429
+ var fromWord = (f) => f.from === "filer" ? "from you" : f.from === "product" ? "from the product map" : "from what you sent";
1809
1430
  function renderBrief(b) {
1810
1431
  const parts = [b.ask.text];
1811
1432
  if (b.why)
@@ -1817,10 +1438,42 @@ function renderBrief(b) {
1817
1438
  parts.push(`worth: ${b.worth ? worthLabel(b.worth) : "not stated"}`);
1818
1439
  if (b.touches)
1819
1440
  parts.push(`touches (${fromWord(b.touches)}): ${b.touches.text}`);
1441
+ if (b.demand)
1442
+ parts.push(`demand (${fromWord(b.demand)}): ${b.demand.text}`);
1820
1443
  if (b.sources.length > 0)
1821
1444
  parts.push(`sources: ${b.sources.join(", ")}`);
1822
1445
  return `Brief: ${parts.join(" · ")}`;
1823
1446
  }
1447
+ function renderBriefMarkdown(b, open, extra = {}) {
1448
+ const cell = (t) => t.replace(/\|/g, "\\|").replace(/\s+/g, " ").trim();
1449
+ const rows = ["| | | |", "|---|---|---|"];
1450
+ const field = (label, f) => {
1451
+ if (f)
1452
+ rows.push(`| **${label}** | ${cell(f.text)} | ${fromWord(f)} |`);
1453
+ };
1454
+ 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);
1458
+ rows.push(`| **Worth** | ${b.worth ? cell(worthLabel(b.worth)) : "_not stated_"} | ${b.worth ? "stated" : ""} |`);
1459
+ field("Touches", b.touches);
1460
+ field("Demand", b.demand);
1461
+ if (b.due)
1462
+ rows.push(`| **Due** | ${b.due} | from your words |`);
1463
+ const out = ["**Brief — draft**", "", ...rows];
1464
+ const files = [...b.sources, ...extra.files ?? []];
1465
+ if (files.length)
1466
+ out.push("", `Files: ${files.map((f) => `\`${cell(f)}\``).join(" · ")}`);
1467
+ if (extra.map)
1468
+ out.push("", `> ${cell(extra.map)}`);
1469
+ if (open.length) {
1470
+ out.push("", `**Still open** — ${open.length === 1 ? "one question" : `${open.length} questions`}:`, "");
1471
+ open.forEach((q, i) => out.push(`${i + 1}. ${q.question}`));
1472
+ } else
1473
+ out.push("", "Nothing open — say yes to file it.");
1474
+ return out.join(`
1475
+ `);
1476
+ }
1824
1477
  function briefToSpec(b) {
1825
1478
  const lines = [b.ask.text, ""];
1826
1479
  if (b.why)
@@ -1831,6 +1484,8 @@ function briefToSpec(b) {
1831
1484
  lines.push(`Smallest: ${b.smallest.text}`);
1832
1485
  if (b.touches)
1833
1486
  lines.push(`Touches: ${b.touches.text}`);
1487
+ if (b.demand)
1488
+ lines.push(`Demand: ${b.demand.text}`);
1834
1489
  if (b.worth)
1835
1490
  lines.push(`Worth: ${worthLabel(b.worth)}`);
1836
1491
  if (b.sources.length > 0)
@@ -1844,7 +1499,8 @@ function intakeReply(i) {
1844
1499
  const blank = i.blank.length > 0 ? ` Still blank: ${i.blank.join(", ")} — say them any time and the row updates.` : "";
1845
1500
  const under = i.parent ? ` under ${i.parent}` : "";
1846
1501
  const next = i.needsApproval ? "It waits for product's yes — they approve or defer it with a note; status tells you which." : "An engineer takes it from the sled; you will hear when it lands.";
1847
- return `On the sled as ${i.title}${under} — filed in ${i.filedIn}. ${size}, ${worth}.${blank} ${next}`;
1502
+ const name = i.key ? `${i.key} · ${i.title}` : i.title;
1503
+ return `On the sled as ${name}${under} — filed in ${i.filedIn}. ${size}, ${worth}.${blank} ${next}`;
1848
1504
  }
1849
1505
  function parseWorthBand(raw) {
1850
1506
  if (typeof raw !== "object" || raw === null)
@@ -1868,9 +1524,140 @@ function parseWorthBand(raw) {
1868
1524
  ...typeof w.note === "string" && w.note.trim() ? { note: clip(w.note, 140) } : {}
1869
1525
  };
1870
1526
  }
1527
+ // ../../packages/shared/src/intake/related.ts
1528
+ var STOP = new Set([
1529
+ "a",
1530
+ "an",
1531
+ "the",
1532
+ "and",
1533
+ "or",
1534
+ "of",
1535
+ "to",
1536
+ "in",
1537
+ "on",
1538
+ "for",
1539
+ "with",
1540
+ "by",
1541
+ "at",
1542
+ "from",
1543
+ "as",
1544
+ "is",
1545
+ "are",
1546
+ "be",
1547
+ "it",
1548
+ "its",
1549
+ "this",
1550
+ "that",
1551
+ "we",
1552
+ "our",
1553
+ "add",
1554
+ "new",
1555
+ "make",
1556
+ "fix",
1557
+ "update",
1558
+ "support",
1559
+ "when",
1560
+ "into",
1561
+ "up",
1562
+ "via"
1563
+ ]);
1871
1564
  // ../../packages/shared/src/dispatch/plan.ts
1872
1565
  init_format();
1873
1566
 
1567
+ // ../../packages/shared/src/intake/due.ts
1568
+ var MONTHS = {
1569
+ jan: 1,
1570
+ january: 1,
1571
+ feb: 2,
1572
+ february: 2,
1573
+ mar: 3,
1574
+ march: 3,
1575
+ apr: 4,
1576
+ april: 4,
1577
+ may: 5,
1578
+ jun: 6,
1579
+ june: 6,
1580
+ jul: 7,
1581
+ july: 7,
1582
+ aug: 8,
1583
+ august: 8,
1584
+ sep: 9,
1585
+ sept: 9,
1586
+ september: 9,
1587
+ oct: 10,
1588
+ october: 10,
1589
+ nov: 11,
1590
+ november: 11,
1591
+ dec: 12,
1592
+ december: 12
1593
+ };
1594
+ var MONTH_RE = "(jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may|jun(?:e)?|jul(?:y)?|aug(?:ust)?|sep(?:t(?:ember)?)?|oct(?:ober)?|nov(?:ember)?|dec(?:ember)?)";
1595
+ var LEAD = "(?:by|before|due|until|no later than|target(?:ed)?(?: for)?|deadline(?: is)?|needed(?: by)?|end of|eo)\\s*(?:the\\s+)?";
1596
+ var iso = (y, m, d) => {
1597
+ if (m < 1 || m > 12 || d < 1 || d > 31)
1598
+ return null;
1599
+ const t = Date.UTC(y, m - 1, d);
1600
+ const dt = new Date(t);
1601
+ if (dt.getUTCMonth() !== m - 1 || dt.getUTCDate() !== d)
1602
+ return null;
1603
+ return dt.toISOString().slice(0, 10);
1604
+ };
1605
+ var lastDay = (y, m) => new Date(Date.UTC(y, m, 0)).getUTCDate();
1606
+ function yearFor(m, d, now) {
1607
+ const n = new Date(now);
1608
+ const y = n.getUTCFullYear();
1609
+ const candidate = Date.UTC(y, m - 1, d);
1610
+ return candidate < Date.UTC(y, n.getUTCMonth(), n.getUTCDate()) - 86400000 ? y + 1 : y;
1611
+ }
1612
+ function parseDue(text, now = Date.now()) {
1613
+ if (!text)
1614
+ return null;
1615
+ const t = text.replace(/\s+/g, " ");
1616
+ const isoM = new RegExp(`(?:${LEAD})?(\\d{4})-(\\d{2})-(\\d{2})\\b`, "i").exec(t);
1617
+ if (isoM) {
1618
+ const r = iso(Number(isoM[1]), Number(isoM[2]), Number(isoM[3]));
1619
+ if (r)
1620
+ return r;
1621
+ }
1622
+ const mdy = new RegExp(`${LEAD}${MONTH_RE}\\.?\\s+(\\d{1,2})(?:st|nd|rd|th)?(?:,?\\s+(\\d{4}))?\\b`, "i").exec(t);
1623
+ if (mdy) {
1624
+ const m = MONTHS[(mdy[1] ?? "").toLowerCase()] ?? 0;
1625
+ const d = Number(mdy[2]);
1626
+ const y = mdy[3] ? Number(mdy[3]) : yearFor(m, d, now);
1627
+ const r = iso(y, m, d);
1628
+ if (r)
1629
+ return r;
1630
+ }
1631
+ const dmy = new RegExp(`${LEAD}(\\d{1,2})(?:st|nd|rd|th)?\\s+(?:of\\s+)?${MONTH_RE}\\.?(?:,?\\s+(\\d{4}))?\\b`, "i").exec(t);
1632
+ if (dmy) {
1633
+ const d = Number(dmy[1]);
1634
+ const m = MONTHS[(dmy[2] ?? "").toLowerCase()] ?? 0;
1635
+ const y = dmy[3] ? Number(dmy[3]) : yearFor(m, d, now);
1636
+ const r = iso(y, m, d);
1637
+ if (r)
1638
+ return r;
1639
+ }
1640
+ const eom = new RegExp(`(?:end of|eo)\\s*(?:the\\s+)?(?:${MONTH_RE}|month|m\\b)`, "i").exec(t);
1641
+ if (eom) {
1642
+ const n = new Date(now);
1643
+ const named = eom[1] ? MONTHS[eom[1].toLowerCase()] ?? 0 : 0;
1644
+ const m = named || n.getUTCMonth() + 1;
1645
+ const y = named ? yearFor(m, 1, now) : n.getUTCFullYear();
1646
+ return iso(y, m, lastDay(y, m));
1647
+ }
1648
+ const slash = new RegExp(`${LEAD}(\\d{1,2})/(\\d{1,2})(?:/(\\d{2,4}))?\\b`, "i").exec(t);
1649
+ if (slash) {
1650
+ const m = Number(slash[1]);
1651
+ const d = Number(slash[2]);
1652
+ const yy = slash[3] ? Number(slash[3]) : null;
1653
+ const y = yy === null ? yearFor(m, d, now) : yy < 100 ? 2000 + yy : yy;
1654
+ const r = iso(y, m, d);
1655
+ if (r)
1656
+ return r;
1657
+ }
1658
+ return null;
1659
+ }
1660
+
1874
1661
  // ../../packages/shared/src/mcp/server.ts
1875
1662
  init_format();
1876
1663
 
@@ -1879,56 +1666,112 @@ init_format();
1879
1666
  var isObject2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
1880
1667
  var str2 = (v) => typeof v === "string" ? v : undefined;
1881
1668
  var num2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined;
1882
- function statusName(row2) {
1883
- const source = isObject2(row2.source) ? row2.source : {};
1669
+ function statusName(row) {
1670
+ const source = isObject2(row.source) ? row.source : {};
1884
1671
  const kind = str2(source.kind);
1885
1672
  const doc = str2(source.doc);
1886
- const title = str2(row2.title) ?? str2(row2.project) ?? "this row";
1887
- if ((kind === "linear" || kind === "github") && doc)
1673
+ const title = str2(row.title) ?? str2(row.project) ?? "this row";
1674
+ if ((kind === "linear" || kind === "github" || kind === "jira") && doc)
1888
1675
  return `${doc} · ${title}`;
1889
- return title;
1676
+ const key = str2(row.key);
1677
+ return key ? `${key} · ${title}` : title;
1890
1678
  }
1891
- function stateTail(row2) {
1892
- const retired = str2(row2.retired);
1679
+ function stateTail(row) {
1680
+ const retired = str2(row.retired);
1893
1681
  if (retired)
1894
1682
  return ` Retired: ${retired}.`;
1895
- if (row2.stale === true)
1683
+ if (row.stale === true)
1896
1684
  return " Stale: no movement in 14 days.";
1897
1685
  return "";
1898
1686
  }
1899
- function statusLine(row2, now) {
1900
- return `${statusBody(row2, now)}${stateTail(row2)}`;
1687
+ function statusLine(row, now) {
1688
+ return `${statusBody(row, now)}${trackerTail(row)}${stateTail(row)}${assetsTail(row)}`;
1689
+ }
1690
+ function assetsTail(row) {
1691
+ const assets = Array.isArray(row.assets) ? row.assets.filter(isObject2) : [];
1692
+ if (assets.length === 0)
1693
+ return "";
1694
+ const parts = assets.map((a) => {
1695
+ const name = str2(a.name) ?? "file";
1696
+ const kind = str2(a.kind);
1697
+ const url = str2(a.url);
1698
+ return `${name}${kind && kind !== "other" ? ` (${kind})` : ""}${url ? ` ${url}` : ""}`;
1699
+ });
1700
+ return ` On the row: ${parts.join(" · ")}.`;
1901
1701
  }
1902
- function statusBody(row2, now) {
1903
- const name = statusName(row2);
1904
- const stage = str2(row2.stage) ?? "ready";
1905
- const handoff = isObject2(row2.handoff) ? row2.handoff : null;
1906
- const work = isObject2(row2.work) ? row2.work : null;
1907
- const worth = isObject2(row2.worth) ? row2.worth : null;
1908
- const band = bandLine(row2.band);
1702
+ function trackerTail(row) {
1703
+ const source = isObject2(row.source) ? row.source : {};
1704
+ const kind = str2(source.kind);
1705
+ if (kind !== "github" && kind !== "jira" && kind !== "linear")
1706
+ return "";
1707
+ const bits = [];
1708
+ const openedBy = str2(source.openedBy);
1709
+ const assignee = str2(source.assignee);
1710
+ if (openedBy)
1711
+ bits.push(`opened by ${openedBy}`);
1712
+ if (assignee)
1713
+ bits.push(`assigned to ${assignee}`);
1714
+ else if (openedBy)
1715
+ bits.push("nobody assigned");
1716
+ const issueType = str2(source.issueType);
1717
+ const parentKey = str2(source.parentKey);
1718
+ if (issueType)
1719
+ bits.push(parentKey ? `${issueType.toLowerCase()} under ${parentKey}` : issueType.toLowerCase());
1720
+ const mergedBy = str2(source.mergedBy);
1721
+ if (mergedBy)
1722
+ bits.push(`merged by ${mergedBy}`);
1723
+ const agent = str2(source.agent);
1724
+ if (agent)
1725
+ bits.push(`built with ${agent}`);
1726
+ const diff = isObject2(source.diff) ? source.diff : null;
1727
+ if (diff) {
1728
+ const add = num2(diff.additions) ?? 0;
1729
+ const del = num2(diff.deletions) ?? 0;
1730
+ const files = num2(diff.files) ?? 0;
1731
+ const commits = num2(diff.commits) ?? 0;
1732
+ bits.push(`+${add} −${del}${files ? ` · ${files} ${files === 1 ? "file" : "files"}` : ""}${commits ? ` · ${commits} ${commits === 1 ? "commit" : "commits"}` : ""}`);
1733
+ }
1734
+ const blockedBy = Array.isArray(source.blockedBy) ? source.blockedBy.filter((b) => typeof b === "string") : [];
1735
+ if (blockedBy.length)
1736
+ bits.push(`blocked by ${blockedBy.join(", ")}`);
1737
+ const held = str2(source.held);
1738
+ if (held)
1739
+ bits.push(`held: "${held}"`);
1740
+ if (bits.length === 0)
1741
+ return "";
1742
+ const where = kind === "github" ? "GitHub" : kind === "jira" ? "Jira" : "Linear";
1743
+ return ` In ${where}: ${bits.join(" · ")}.`;
1744
+ }
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);
1909
1752
  const sized = band ? `Sized ${band}` : "Not sized";
1910
1753
  const worthText = worth ? `worth ${worthLabel(worth)}` : "worth not stated";
1911
1754
  const ago = (ts) => num2(ts) !== undefined ? `${shortDuration(Math.max(0, now - num2(ts)))} ago` : "";
1912
1755
  if (stage === "landed") {
1913
- const mins = num2(row2.actualMinutes);
1914
- const dollars2 = num2(row2.actualUsd);
1756
+ const mins = num2(row.actualMinutes);
1757
+ const dollars = num2(row.actualUsd);
1915
1758
  const credits = work ? num2(work.seatCreditsUsd) : undefined;
1916
1759
  const bits = [
1917
1760
  mins !== undefined ? `${Math.round(mins)} min` : null,
1918
- dollars2 !== undefined && dollars2 > 0 ? usd2(dollars2) : null,
1761
+ dollars !== undefined && dollars > 0 ? usd2(dollars) : null,
1919
1762
  credits !== undefined ? `${usd2(credits)} credits` : null,
1920
1763
  work ? str2(work.model) : null
1921
1764
  ].filter((b) => Boolean(b));
1922
1765
  const ref = work ? str2(work.ref) : undefined;
1923
1766
  return `${name} — landed${bits.length ? `: ${bits.join(" · ")}` : ""}${ref ? ` · ${ref}` : ""}. ${sized}, ${worthText}.`;
1924
1767
  }
1925
- const tree = isObject2(row2.tree) ? row2.tree : null;
1768
+ const tree = isObject2(row.tree) ? row.tree : null;
1926
1769
  const treeText = treeLine(tree);
1927
1770
  if (stage === "on_sled" && handoff) {
1928
1771
  const who = str2(handoff.pickedBy) ?? "someone";
1929
1772
  return `${name} — on the sled: ${who} has it (picked ${ago(handoff.at)}). ${sized}, ${worthText}.${treeText} You will hear when it lands.`;
1930
1773
  }
1931
- const approval = isObject2(row2.approval) ? row2.approval : null;
1774
+ const approval = isObject2(row.approval) ? row.approval : null;
1932
1775
  if (stage === "deferred") {
1933
1776
  const by = approval ? str2(approval.by) ?? "product" : "product";
1934
1777
  const note = approval ? str2(approval.note) : undefined;
@@ -2043,12 +1886,139 @@ function planReply(epic, rows, total) {
2043
1886
  `);
2044
1887
  }
2045
1888
 
1889
+ // ../../packages/shared/src/mcp/ui/intakeView.ts
1890
+ var INTAKE_VIEW_URI = "ui://sleds/intake";
1891
+ var MCP_APP_MIME = "text/html;profile=mcp-app";
1892
+ var INTAKE_VIEW_HTML = `<!doctype html>
1893
+ <html lang="en"><head><meta charset="utf-8"><title>SLEDS · intake</title>
1894
+ <style>
1895
+ :root{
1896
+ --bg:var(--color-background-primary,light-dark(#ffffff,#0f1116));
1897
+ --bg2:var(--color-background-secondary,light-dark(#f4f5f2,#14171e));
1898
+ --fg:var(--color-text-primary,light-dark(#171717,#f4f5f2));
1899
+ --fg2:var(--color-text-secondary,light-dark(#555a66,#9aa0ad));
1900
+ --fg3:light-dark(#7a7f8a,#6b6f7b);
1901
+ --line:var(--color-border-primary,light-dark(#e3e4e0,#2a2e38));
1902
+ --line2:light-dark(#c9cbd1,#3a4050);
1903
+ --volt:#d9dd45;--volt-ink:#0b0d11;--volt-dim:rgba(217,221,69,.12);
1904
+ --peri:#8b90ff;--peri-dim:rgba(139,144,255,.14);--warning:#ffb02e;--warning-dim:rgba(255,176,46,.12);--success:#55e08c;--success-dim:rgba(85,224,140,.12);--error:#ff5c47;
1905
+ --sans:var(--font-sans,system-ui,-apple-system,"Segoe UI",sans-serif);--mono:var(--font-mono,ui-monospace,SFMono-Regular,Menlo,monospace);
1906
+ color-scheme:light dark;
1907
+ }
1908
+ *{box-sizing:border-box}
1909
+ html,body{margin:0;background:transparent;color:var(--fg);font:14px/1.45 var(--sans)}
1910
+ .card{border:1px solid var(--line);border-radius:10px;background:var(--bg);padding:14px 16px}
1911
+ .h{display:flex;justify-content:space-between;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:8px}
1912
+ .l{font:600 11px var(--mono);letter-spacing:.06em;text-transform:uppercase;color:var(--fg3)}.l b{color:var(--volt);font-weight:600}
1913
+ .title{font-weight:600;font-size:16px;line-height:1.3}.meta{font:11px var(--mono);color:var(--fg3);margin-top:3px}
1914
+ .kv{display:grid;grid-template-columns:88px 1fr;gap:6px 12px;margin:12px 0;font-size:13px}
1915
+ .k{font:11px var(--mono);letter-spacing:.06em;text-transform:uppercase;color:var(--fg3);padding-top:3px}
1916
+ .from{font:11px var(--mono);color:var(--fg3);margin-left:6px;white-space:nowrap}
1917
+ .v[contenteditable]{border-bottom:1px dashed var(--line2);outline:none;padding-bottom:1px}
1918
+ .v[contenteditable]:focus{border-bottom-color:var(--volt)}
1919
+ .q{border:1px solid var(--line);border-radius:8px;padding:10px 12px;margin:8px 0}
1920
+ .q .t{font-weight:500}
1921
+ .q textarea{width:100%;margin-top:8px;background:var(--bg2);border:1px solid var(--line2);border-radius:6px;color:var(--fg);padding:8px 10px;font:14px var(--sans);resize:vertical;min-height:38px}
1922
+ .q textarea:focus{outline:none;border-color:var(--volt)}
1923
+ .opt{display:flex;gap:6px;flex-wrap:wrap;margin-top:8px}
1924
+ .opt button{font:12px var(--sans);border:1px solid var(--line2);border-radius:14px;padding:3px 10px;color:var(--fg2);background:transparent;cursor:pointer}
1925
+ .opt button.on{border-color:var(--volt);color:var(--fg);background:var(--volt-dim)}
1926
+ .chips{display:flex;gap:6px;flex-wrap:wrap;margin:6px 0 10px}
1927
+ .chip{font:11px var(--mono);padding:2px 7px;border-radius:4px;border:1px solid var(--line2);color:var(--fg2)}
1928
+ .chip.w{border-color:var(--warning);color:var(--warning);background:var(--warning-dim)}.chip.p{border-color:var(--peri);color:var(--peri);background:var(--peri-dim)}.chip.s{border-color:var(--success);color:var(--success);background:var(--success-dim)}.chip.v{border-color:var(--volt);color:var(--volt);background:var(--volt-dim)}
1929
+ .map{font-size:12px;color:var(--fg2);border-left:2px solid var(--line2);padding:2px 10px;margin:8px 0}
1930
+ .acts{display:flex;gap:8px;align-items:center;border-top:1px solid var(--line);padding-top:12px;margin-top:6px;flex-wrap:wrap}
1931
+ .verb{border:1px solid var(--line2);border-radius:6px;padding:6px 12px;font:500 13px var(--sans);background:transparent;color:var(--fg);cursor:pointer}
1932
+ .verb.pri{background:var(--volt);color:var(--volt-ink);border-color:var(--volt);font-weight:600}
1933
+ .verb:disabled{opacity:.5;cursor:default}
1934
+ .n{font:11px var(--mono);color:var(--fg3);margin-left:auto}
1935
+ .err{color:var(--error);font-size:12px;margin-top:8px}
1936
+ a{color:var(--peri)}
1937
+ </style></head><body>
1938
+ <div id="root" class="card"><div class="l"><b>SLEDS</b> · intake</div><div class="meta">waiting for the brief…</div></div>
1939
+ <script>
1940
+ (function(){
1941
+ var seq=1, pending={}, args={}, state=null, busy=false, err="";
1942
+ var parent=window.parent;
1943
+ function send(method, params){ var id=seq++; return new Promise(function(res,rej){ pending[id]={res:res,rej:rej}; parent.postMessage({jsonrpc:"2.0",id:id,method:method,params:params||{}},"*"); }); }
1944
+ function notify(method, params){ parent.postMessage({jsonrpc:"2.0",method:method,params:params||{}},"*"); }
1945
+ window.addEventListener("message", function(ev){
1946
+ var m=ev.data; if(!m||m.jsonrpc!=="2.0") return;
1947
+ if(m.id!==undefined && (m.result!==undefined||m.error!==undefined) && pending[m.id]){ var p=pending[m.id]; delete pending[m.id]; m.error?p.rej(m.error):p.res(m.result); return; }
1948
+ if(m.method==="ui/notifications/tool-input"){ args=(m.params&&m.params.arguments)||{}; return; }
1949
+ if(m.method==="ui/notifications/tool-result"){ onResult(m.params||{}); return; }
1950
+ if(m.method==="ui/notifications/host-context-changed"){ applyStyles(m.params||{}); return; }
1951
+ });
1952
+ function applyStyles(hc){ var vars=(hc&&hc.styles&&hc.styles.variables)||{}; var root=document.documentElement; Object.keys(vars).forEach(function(k){ root.style.setProperty(k, vars[k]); }); if(hc&&hc.theme) root.style.colorScheme=hc.theme; }
1953
+ function onResult(r){ var s=r.structuredContent; if(s&&s.kind){ state=s; } else { state={kind:"text", text:((r.content||[]).map(function(c){return c.text||"";}).join("\\n"))}; } busy=false; err=r.isError?"":err; render(); }
1954
+ function esc(s){ return String(s==null?"":s).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;"); }
1955
+ function fit(){ var h=document.documentElement.scrollHeight; notify("ui/notifications/size-changed",{height:h}); }
1956
+ var FIELDS=["ask","why","done","smallest","touches","demand"];
1957
+ var LABEL={ask:"Ask",why:"Why",done:"Done",smallest:"Smallest",touches:"Touches",demand:"Demand",worth:"Worth"};
1958
+ var WORTH=["critical","high","medium","low"];
1959
+ var picked={worth:null};
1960
+ function render(){
1961
+ var root=document.getElementById("root");
1962
+ if(!state){ return; }
1963
+ if(state.kind==="text"){ root.innerHTML='<div class="l"><b>SLEDS</b> · intake</div><div style="white-space:pre-wrap;margin-top:8px">'+esc(state.text)+'</div>'; fit(); return; }
1964
+ if(state.kind==="filed"){
1965
+ root.innerHTML='<div class="h"><span class="l"><b>SLEDS</b> · on the sled</span><span class="chip '+(state.needsApproval?'w':'s')+'">'+(state.needsApproval?"waits for product's yes":"ready")+'</span></div>'
1966
+ +'<div class="title">'+esc(state.title)+'</div><div class="meta">'+esc(state.band||"")+(state.worth?' · worth '+esc(state.worth):' · worth not stated')+' · filed in '+esc(state.filedIn||"SLEDS")+'</div>'
1967
+ +(state.attached&&state.attached.length?'<div class="chips">'+state.attached.map(function(a){return '<span class="chip '+(a.ok?'p':'')+'">'+esc(a.name)+(a.ok?'':' · not stored')+'</span>';}).join("")+'</div>':'')
1968
+ +(state.blank&&state.blank.length?'<div class="map">Still blank: '+esc(state.blank.join(", "))+' — engineering may ask.</div>':'')
1969
+ +'<div class="acts">'+(state.rowUrl?'<button class="verb pri" id="open">Open the row</button>':'')+'<span class="n">'+esc(state.project||"")+'</span></div>';
1970
+ var o=document.getElementById("open"); if(o) o.onclick=function(){ send("ui/open-link",{url:state.rowUrl}); };
1971
+ fit(); return;
1972
+ }
1973
+ var b=state.brief||{}; var open=state.open||[];
1974
+ var html='<div class="h"><span class="l"><b>SLEDS</b> · intake · draft</span>'+(open.length?'<span class="chip w">'+open.length+' question'+(open.length===1?'':'s')+' open</span>':'<span class="chip s">ready to file</span>')+'</div>';
1975
+ html+='<div class="title">'+esc(b.ask&&b.ask.text||"")+'</div><div class="meta">from '+esc(b.ask&&b.ask.from||"the artifact")+(state.needed&&state.needed.length?' · needs '+esc(state.needed.join(" and "))+' to file':' · nothing is filed until you confirm')+'</div>';
1976
+ html+='<div class="kv">';
1977
+ FIELDS.slice(1).forEach(function(k){ var f=b[k]; if(!f) return; html+='<div class="k">'+LABEL[k]+'</div><div><span class="v" contenteditable="true" data-k="'+k+'">'+esc(f.text)+'</span><span class="from">from the '+esc(f.from)+'</span></div>'; });
1978
+ if(b.worth){ html+='<div class="k">Worth</div><div><span class="chip v">'+esc(b.worth.level)+'</span>'+(b.worth.note?'<span class="from">'+esc(b.worth.note)+'</span>':'')+'</div>'; }
1979
+ if(b.due){ html+='<div class="k">Due</div><div>'+esc(b.due)+'<span class="from">from your words</span></div>'; }
1980
+ html+='</div>';
1981
+ if(state.map){ html+='<div class="map">What the product says: '+esc(state.map)+'</div>'; }
1982
+ open.forEach(function(q){
1983
+ html+='<div class="q"><div class="t">'+esc(q.question)+'</div>';
1984
+ if(q.key==="worth"){ html+='<div class="opt">'+WORTH.map(function(w){return '<button type="button" data-w="'+w+'" class="'+(picked.worth===w?'on':'')+'">'+w+'</button>';}).join("")+'</div><textarea data-q="worth" rows="1" placeholder="…or a customer, a number, a renewal"></textarea>'; }
1985
+ else { html+='<textarea data-q="'+esc(q.key)+'" rows="2" placeholder="In your words"></textarea>'; }
1986
+ html+='</div>';
1987
+ });
1988
+ var files=(state.links||[]).map(function(l){return '<span class="chip">'+esc(l)+'</span>';}).concat((state.attachments||[]).map(function(a){return '<span class="chip p">'+esc(a)+'</span>';}));
1989
+ if(files.length) html+='<div class="chips">'+files.join("")+'</div>';
1990
+ html+='<div class="acts"><button class="verb pri" id="confirm"'+(busy?' disabled':'')+'>'+(busy?"Filing…":"Put it on the sled")+'</button><span class="n">'+(state.needsApproval?"waits for product's yes":"")+'</span></div>';
1991
+ if(err) html+='<div class="err">'+esc(err)+'</div>';
1992
+ root.innerHTML=html;
1993
+ root.querySelectorAll("[data-w]").forEach(function(btn){ btn.onclick=function(){ picked.worth=btn.getAttribute("data-w"); root.querySelectorAll("[data-w]").forEach(function(x){x.classList.toggle("on",x===btn);}); }; });
1994
+ root.querySelectorAll("textarea").forEach(function(t){ t.oninput=fit; });
1995
+ document.getElementById("confirm").onclick=confirm;
1996
+ fit();
1997
+ }
1998
+ function collect(){
1999
+ var answers={}; var root=document.getElementById("root");
2000
+ var a=(args&&args.answers)||{}; Object.keys(a).forEach(function(k){ if(a[k]) answers[k]=a[k]; });
2001
+ root.querySelectorAll(".v[contenteditable]").forEach(function(el){ var k=el.getAttribute("data-k"); var t=el.textContent.trim(); var b=(state.brief||{})[k]; if(t && (!b || b.text!==t || b.from==="filer")) answers[k]=t; });
2002
+ root.querySelectorAll("textarea[data-q]").forEach(function(t){ var k=t.getAttribute("data-q"); var v=t.value.trim(); if(k==="worth"){ var w=[picked.worth,v].filter(Boolean).join(" · "); if(w) answers.worth=w; } else if(v) answers[k]=v; });
2003
+ return answers;
2004
+ }
2005
+ function confirm(){
2006
+ if(busy) return; busy=true; err=""; render();
2007
+ var call={ text:args.text||"", confirm:true, answers:collect() };
2008
+ if(args.links) call.links=args.links; if(args.attachments) call.attachments=args.attachments; if(args.added) call.added=args.added; if(args.due) call.due=args.due; if(args.parent) call.parent=args.parent;
2009
+ send("tools/call",{name:"intake",arguments:call}).then(function(r){ onResult(r||{}); if(r&&r.isError){ err=((r.content||[]).map(function(c){return c.text||"";}).join(" ")); busy=false; render(); } })
2010
+ .catch(function(e){ err=(e&&e.message)||"failed"; busy=false; render(); });
2011
+ }
2012
+ send("ui/initialize",{appCapabilities:{availableDisplayModes:["inline"]}}).then(function(r){ applyStyles((r&&r.hostContext)||{}); notify("ui/notifications/initialized",{}); }).catch(function(){ notify("ui/notifications/initialized",{}); });
2013
+ })();
2014
+ </script></body></html>`;
2015
+
2046
2016
  // ../../packages/shared/src/mcp/server.ts
2047
- function fetchClient(o2) {
2048
- const fetchFn = o2.fetchFn ?? fetch;
2049
- const base = o2.proxyUrl.replace(/\/+$/, "");
2050
- const headers = { "content-type": "application/json", authorization: `Bearer ${o2.token}` };
2051
- const timeout = () => AbortSignal.timeout(o2.timeoutMs ?? MCP_TIMEOUT_MS);
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);
2052
2022
  return {
2053
2023
  async post(path, body) {
2054
2024
  try {
@@ -2111,10 +2081,10 @@ var SERVER_INFO = {
2111
2081
  ]
2112
2082
  };
2113
2083
  var SERVER_INSTRUCTIONS = [
2114
- "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 from the conversation.",
2115
- '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) two questions at a time, and writes nothing until they confirm. Never guess worth: leave it blank and say so. Afterwards tell them in plain words that it is on the sled, the band, and whether it waits for product\'s yes. They never need to learn a verb.',
2116
- "When you are about to build something that will take more than a few minutes: `status` first if a row may already 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 something already landed is a new `item` under the same doc, never an overwrite. An `actual` with no estimate still records — the row is created and shown as landed unsized — but the band you skipped is the point, so estimate first.",
2117
- "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; if that fails, move on and tell the person the row is still owed. Never invent a number to get past an error."
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."
2118
2088
  ].join(`
2119
2089
 
2120
2090
  `);
@@ -2162,7 +2132,8 @@ var TOOLS = [
2162
2132
  },
2163
2133
  {
2164
2134
  name: "intake",
2165
- 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. 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) — 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.',
2135
+ _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.',
2166
2137
  inputSchema: {
2167
2138
  type: "object",
2168
2139
  properties: {
@@ -2175,6 +2146,28 @@ var TOOLS = [
2175
2146
  items: { type: "string" },
2176
2147
  description: "Links and file names that came with it (a Figma frame, a Loom, a doc)."
2177
2148
  },
2149
+ attachments: {
2150
+ type: "array",
2151
+ description: 'The files themselves, stored on the row so anyone on the board can open them: the spec or PRD as markdown, a prototype you made as a single HTML page, a screenshot. Sent only on `confirm`. Up to 4 MB each; text as is, an image as base64 with `encoding: "base64"`. The same name again replaces.',
2152
+ items: {
2153
+ type: "object",
2154
+ properties: {
2155
+ name: {
2156
+ type: "string",
2157
+ description: "File name with extension: spec.md, prototype.html, before.png."
2158
+ },
2159
+ kind: {
2160
+ type: "string",
2161
+ enum: ["spec", "prototype", "screenshot", "doc", "other"],
2162
+ description: "Read from the name and mime when omitted."
2163
+ },
2164
+ mime: { type: "string", description: "text/markdown, text/html, image/png…" },
2165
+ content: { type: "string", description: "The file: text as is, or base64." },
2166
+ encoding: { type: "string", enum: ["text", "base64"] }
2167
+ },
2168
+ required: ["name", "content"]
2169
+ }
2170
+ },
2178
2171
  answers: {
2179
2172
  type: "object",
2180
2173
  description: "The person's answers, in their words. Each overrides what the artifact said.",
@@ -2187,9 +2180,25 @@ var TOOLS = [
2187
2180
  type: "string",
2188
2181
  description: "A level (high / medium / low), dollars, a customer — e.g. 'high · Acme renewal'."
2189
2182
  },
2190
- touches: { type: "string", description: "Areas or systems it touches." }
2183
+ touches: {
2184
+ type: "string",
2185
+ description: "Areas or systems it touches, and where in the product it would live."
2186
+ },
2187
+ demand: {
2188
+ type: "string",
2189
+ description: "How many have asked for it, or how often it comes up."
2190
+ }
2191
2191
  }
2192
2192
  },
2193
+ added: {
2194
+ type: "array",
2195
+ 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."
2197
+ },
2198
+ due: {
2199
+ type: "string",
2200
+ description: "A due day the person stated, as YYYY-MM-DD — only when they said one ('by Oct 10', 'end of the month'). Never ask for it. Read from the text on its own when you leave it out."
2201
+ },
2193
2202
  confirm: {
2194
2203
  type: "boolean",
2195
2204
  description: "True only after the person said yes to the brief. Files the row."
@@ -2295,6 +2304,64 @@ var TOOLS = [
2295
2304
  }
2296
2305
  }
2297
2306
  },
2307
+ {
2308
+ 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.",
2310
+ inputSchema: {
2311
+ type: "object",
2312
+ properties: {
2313
+ text: { type: "string", description: "The ask or the question, in the person's words." }
2314
+ },
2315
+ required: ["text"]
2316
+ }
2317
+ },
2318
+ {
2319
+ 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.",
2321
+ inputSchema: {
2322
+ type: "object",
2323
+ properties: {
2324
+ project: {
2325
+ type: "string",
2326
+ description: "The row's project key, e.g. manual:intake/help-and-support or jira:WORK/WORK-7551."
2327
+ },
2328
+ name: {
2329
+ type: "string",
2330
+ description: "File name with extension: spec.md, prototype.html, before.png."
2331
+ },
2332
+ kind: {
2333
+ type: "string",
2334
+ enum: ["spec", "prototype", "screenshot", "doc", "other"],
2335
+ description: "Read from the name and mime when omitted."
2336
+ },
2337
+ mime: { type: "string", description: "text/markdown, text/html, image/png…" },
2338
+ content: {
2339
+ type: "string",
2340
+ description: "The file: text as is, or base64 with encoding base64."
2341
+ },
2342
+ encoding: { type: "string", enum: ["text", "base64"] }
2343
+ },
2344
+ required: ["project", "name", "content"]
2345
+ }
2346
+ },
2347
+ {
2348
+ 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.",
2350
+ inputSchema: {
2351
+ type: "object",
2352
+ properties: {
2353
+ answer: { type: "string", description: "The reply, in the person's words." },
2354
+ project: {
2355
+ type: "string",
2356
+ description: "The row's project key (from status or the question list) when more than one question is open."
2357
+ },
2358
+ id: {
2359
+ type: "string",
2360
+ description: "The question's id from the list, when more than one is open on the same row."
2361
+ }
2362
+ }
2363
+ }
2364
+ },
2298
2365
  {
2299
2366
  name: "handoffs",
2300
2367
  description: "The team's open hand-offs: tasks and walled sessions parked for pickup, the ones meant for this seat first. Each has an id for the pickup tool (or /sled pickup <id> in Claude Code).",
@@ -2484,6 +2551,10 @@ async function usageTool(ctx) {
2484
2551
  if (!ctx.client)
2485
2552
  return NOT_SET_UP;
2486
2553
  const raw = await ctx.client.get("/proxy/meter");
2554
+ if (raw && typeof raw.note === "string" && raw.seat?.filer === true)
2555
+ return {
2556
+ text: `${raw.note}. Intake, status, answer, touches and attach are yours; taking or sizing work is a seat's.`
2557
+ };
2487
2558
  const m = raw ? parseMeter(raw) : null;
2488
2559
  if (!m)
2489
2560
  return { text: "sleds offline (proxy did not answer).", isError: true };
@@ -2538,12 +2609,12 @@ function byModelText(raw) {
2538
2609
  if (!Array.isArray(raw))
2539
2610
  return "";
2540
2611
  const parts = [];
2541
- for (const row2 of raw) {
2542
- if (!isObject4(row2) || !isObject4(row2.standard))
2612
+ for (const row of raw) {
2613
+ if (!isObject4(row) || !isObject4(row.standard))
2543
2614
  continue;
2544
- const model = str4(row2.model);
2545
- const low = num3(row2.standard.lowUsd);
2546
- const high = num3(row2.standard.highUsd);
2615
+ const model = str4(row.model);
2616
+ const low = num3(row.standard.lowUsd);
2617
+ const high = num3(row.standard.highUsd);
2547
2618
  if (!model || low === undefined || high === undefined)
2548
2619
  continue;
2549
2620
  const m = /^claude-([a-z]+)-(\d+)(?:-(\d+))?/.exec(model);
@@ -2587,13 +2658,35 @@ function briefAnswers(raw) {
2587
2658
  if (!isObject4(raw))
2588
2659
  return {};
2589
2660
  const out = {};
2590
- for (const k of ["ask", "why", "done", "smallest", "worth", "touches"]) {
2661
+ for (const k of ["ask", "why", "done", "smallest", "worth", "touches", "demand"]) {
2591
2662
  const v = str4(raw[k])?.trim();
2592
2663
  if (v)
2593
2664
  out[k] = v;
2594
2665
  }
2595
2666
  return out;
2596
2667
  }
2668
+ async function readTouches(ctx, text) {
2669
+ if (!ctx.client || !text.trim())
2670
+ return null;
2671
+ const r = await ctx.client.get(`/proxy/touches?q=${encodeURIComponent(text.slice(0, 2000))}`);
2672
+ if (!r)
2673
+ return null;
2674
+ const mapped = r.mapped === true;
2675
+ const areas = Array.isArray(r.areas) ? r.areas : [];
2676
+ 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 };
2678
+ }
2679
+ async function touchesTool(ctx, args) {
2680
+ if (!ctx.client)
2681
+ return NOT_SET_UP;
2682
+ const text = str4(args.text)?.trim();
2683
+ if (!text)
2684
+ return { text: "Say what the ask or the question is (text).", isError: true };
2685
+ const r = await readTouches(ctx, text);
2686
+ if (!r)
2687
+ return UNREACHABLE;
2688
+ return { text: r.text || "Nothing in the product map matches these words." };
2689
+ }
2597
2690
  async function intakeTool(ctx, args) {
2598
2691
  if (!ctx.client)
2599
2692
  return NOT_SET_UP;
@@ -2607,18 +2700,35 @@ async function intakeTool(ctx, args) {
2607
2700
  const brief = draftBrief({ text, links, answers: briefAnswers(args.answers) });
2608
2701
  if (!brief)
2609
2702
  return { text: "Nothing to file. Pass text: the ask, a spec, or a paragraph.", isError: true };
2703
+ const map = await readTouches(ctx, [brief.ask.text, brief.why?.text, brief.done?.text].filter(Boolean).join(`
2704
+ `));
2705
+ if (map?.touchesLine && !brief.touches)
2706
+ brief.touches = { text: map.touchesLine, from: "product" };
2610
2707
  const open = openQuestions(brief);
2611
2708
  if (args.confirm !== true) {
2612
- const lines = [renderBrief(brief)];
2613
- if (open.length > 0) {
2614
- lines.push("", "Still open (ask at most two at a time, in your own words; skip what they already said):");
2615
- for (const q of open)
2616
- lines.push(`- ${q.key}: ${q.question}`);
2617
- }
2618
- const needed2 = missingToFile(brief);
2619
- lines.push("", 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. Show them the brief; when they say yes, call intake again with the same text, their answers, and confirm: true.");
2709
+ const needed = missingToFile(brief);
2710
+ const attachments = parseAttachments(args.attachments).map((a) => a.name || "file");
2711
+ const structured = {
2712
+ kind: "draft",
2713
+ brief: briefForView(brief),
2714
+ open,
2715
+ needed,
2716
+ map: map?.text ?? null,
2717
+ links,
2718
+ attachments
2719
+ };
2720
+ const lines = [
2721
+ renderBriefMarkdown(brief, open, { map: map?.text ?? null, files: attachments }),
2722
+ "",
2723
+ 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.",
2725
+ "",
2726
+ "```json sleds-draft",
2727
+ JSON.stringify(structured),
2728
+ "```"
2729
+ ];
2620
2730
  return { text: lines.join(`
2621
- `) };
2731
+ `), structured };
2622
2732
  }
2623
2733
  const needed = missingToFile(brief);
2624
2734
  if (needed.length > 0)
@@ -2627,8 +2737,14 @@ async function intakeTool(ctx, args) {
2627
2737
  isError: true
2628
2738
  };
2629
2739
  const parent = str4(args.parent)?.trim() || undefined;
2740
+ const answers = briefAnswers(args.answers);
2741
+ const asked = Object.keys(QUESTIONS).filter((k) => answers[k]).map((k) => ({ q: QUESTIONS[k], a: answers[k] }));
2742
+ const added = Array.isArray(args.added) ? args.added.filter((l) => typeof l === "string" && l.trim().length > 0).map((l) => l.trim().slice(0, 200)).slice(0, 12) : [];
2743
+ const dueArg = typeof args.due === "string" && /^\d{4}-\d{2}-\d{2}$/.test(args.due) ? args.due : null;
2744
+ const due = dueArg ?? parseDue([text, ...Object.values(answers), ...added].filter(Boolean).join(`
2745
+ `));
2630
2746
  const r = await ctx.client.postFull("/proxy/intake", {
2631
- brief,
2747
+ brief: { ...brief, intake: { asked, added, related: [] }, ...due ? { due } : {} },
2632
2748
  ...parent ? { parent } : {}
2633
2749
  });
2634
2750
  if (!r)
@@ -2641,6 +2757,7 @@ async function intakeTool(ctx, args) {
2641
2757
  const j = r.json ?? {};
2642
2758
  const worth = parseWorthBand(j.worth) ?? brief.worth ?? null;
2643
2759
  const blank = open.filter((q) => q.key !== "worth" || !worth).map((q) => q.key);
2760
+ const attached = str4(j.project) ? await sendAttachments(ctx, str4(j.project), args.attachments) : [];
2644
2761
  return {
2645
2762
  text: intakeReply({
2646
2763
  title: briefToSpec(brief).title,
@@ -2649,8 +2766,132 @@ async function intakeTool(ctx, args) {
2649
2766
  blank,
2650
2767
  filedIn: str4(j.filedIn) ?? "SLEDS (no tracker connected)",
2651
2768
  needsApproval: j.needsApproval === true,
2652
- ...str4(j.parent) ? { parent: str4(j.parent) } : {}
2653
- })
2769
+ ...str4(j.parent) ? { parent: str4(j.parent) } : {},
2770
+ key: str4(j.key) ?? null
2771
+ }) + attachedLine(attached),
2772
+ structured: {
2773
+ kind: "filed",
2774
+ project: str4(j.project) ?? null,
2775
+ key: str4(j.key) ?? null,
2776
+ title: briefToSpec(brief).title,
2777
+ band: bandLine(j.band),
2778
+ worth: worth ? worthLabel(worth) : null,
2779
+ blank,
2780
+ filedIn: str4(j.filedIn) ?? "SLEDS",
2781
+ needsApproval: j.needsApproval === true,
2782
+ rowUrl: str4(j.url) ?? null,
2783
+ attached
2784
+ }
2785
+ };
2786
+ }
2787
+ function briefForView(b) {
2788
+ const f = (x) => x ? { text: x.text, from: x.from } : undefined;
2789
+ return {
2790
+ ask: f(b.ask),
2791
+ why: f(b.why),
2792
+ done: f(b.done),
2793
+ smallest: f(b.smallest),
2794
+ touches: f(b.touches),
2795
+ demand: f(b.demand),
2796
+ worth: b.worth ? { level: b.worth.level, note: b.worth.note ?? null } : undefined,
2797
+ due: b.due ?? null
2798
+ };
2799
+ }
2800
+ function parseAttachments(raw) {
2801
+ if (!Array.isArray(raw))
2802
+ return [];
2803
+ const out = [];
2804
+ for (const a of raw) {
2805
+ if (!isObject4(a))
2806
+ continue;
2807
+ const content = str4(a.content);
2808
+ if (!content)
2809
+ continue;
2810
+ const mime = str4(a.mime)?.trim() || mimeFromName(str4(a.name) ?? "");
2811
+ out.push({
2812
+ name: str4(a.name)?.trim() || "",
2813
+ mime,
2814
+ content,
2815
+ ...str4(a.kind) ? { kind: str4(a.kind) } : {},
2816
+ ...a.encoding === "base64" ? { encoding: "base64" } : {}
2817
+ });
2818
+ }
2819
+ return out.slice(0, 12);
2820
+ }
2821
+ function mimeFromName(name) {
2822
+ const ext = name.toLowerCase().split(".").pop() ?? "";
2823
+ return {
2824
+ html: "text/html",
2825
+ htm: "text/html",
2826
+ md: "text/markdown",
2827
+ markdown: "text/markdown",
2828
+ txt: "text/plain",
2829
+ csv: "text/csv",
2830
+ json: "application/json",
2831
+ pdf: "application/pdf",
2832
+ png: "image/png",
2833
+ jpg: "image/jpeg",
2834
+ jpeg: "image/jpeg",
2835
+ gif: "image/gif",
2836
+ webp: "image/webp",
2837
+ svg: "image/svg+xml"
2838
+ }[ext] ?? "text/plain";
2839
+ }
2840
+ async function sendAttachments(ctx, project, raw) {
2841
+ const files = parseAttachments(raw);
2842
+ const out = [];
2843
+ for (const f of files) {
2844
+ const r = await ctx.client.postFull("/proxy/asset", { project, ...f });
2845
+ if (!r)
2846
+ out.push({ name: f.name || "file", ok: false, why: "unreachable" });
2847
+ else if (r.status !== 200)
2848
+ out.push({
2849
+ name: f.name || "file",
2850
+ ok: false,
2851
+ why: errorMessage(r.json) ?? `HTTP ${r.status}`
2852
+ });
2853
+ else
2854
+ out.push({
2855
+ name: str4(r.json?.name) ?? f.name,
2856
+ ok: true,
2857
+ ...str4(r.json?.url) ? { url: str4(r.json?.url) } : {}
2858
+ });
2859
+ }
2860
+ return out;
2861
+ }
2862
+ function attachedLine(sent) {
2863
+ if (sent.length === 0)
2864
+ return "";
2865
+ const ok = sent.filter((s) => s.ok).map((s) => s.name);
2866
+ const bad = sent.filter((s) => !s.ok);
2867
+ const parts = [];
2868
+ if (ok.length)
2869
+ parts.push(`On the row: ${ok.join(", ")}.`);
2870
+ for (const b of bad)
2871
+ parts.push(`${b.name} was not stored (${b.why ?? "failed"}).`);
2872
+ return ` ${parts.join(" ")}`;
2873
+ }
2874
+ async function attachTool(ctx, args) {
2875
+ if (!ctx.client)
2876
+ return NOT_SET_UP;
2877
+ const project = str4(args.project)?.trim();
2878
+ if (!project)
2879
+ return {
2880
+ text: "Which row? Pass project: the key status shows (or what intake said).",
2881
+ isError: true
2882
+ };
2883
+ const files = parseAttachments([args]);
2884
+ if (files.length === 0)
2885
+ return {
2886
+ text: "Nothing to store. Pass name and content (text as is, or base64 with encoding base64).",
2887
+ isError: true
2888
+ };
2889
+ const sent = await sendAttachments(ctx, project, files);
2890
+ const first = sent[0];
2891
+ if (!first?.ok)
2892
+ return { text: `Not stored: ${first?.why ?? "failed"}.`, isError: true };
2893
+ return {
2894
+ text: `On the row ${project}: ${first.name}${first.url ? ` — ${first.url}` : ""}. Anyone on the board can open it from the row.`
2654
2895
  };
2655
2896
  }
2656
2897
  async function overnightTool(ctx, args) {
@@ -2767,8 +3008,8 @@ async function dispatchTool(ctx, args) {
2767
3008
  }
2768
3009
  return { text: reply };
2769
3010
  }
2770
- const row2 = isObject4(j.row) ? j.row : {};
2771
- const title = str4(row2.title) ?? "the row";
3011
+ const row = isObject4(j.row) ? j.row : {};
3012
+ const title = str4(row.title) ?? "the row";
2772
3013
  if (action.kind === "parked") {
2773
3014
  const dest = str4(action.to);
2774
3015
  return {
@@ -2811,9 +3052,9 @@ async function approveTool(ctx, args) {
2811
3052
  isError: true
2812
3053
  };
2813
3054
  const j = r.json ?? {};
2814
- const row2 = isObject4(j.row) ? j.row : {};
2815
- const title = str4(row2.title) ?? "the row";
2816
- const filedBy = str4(row2.filedBy);
3055
+ const row = isObject4(j.row) ? j.row : {};
3056
+ const title = str4(row.title) ?? "the row";
3057
+ const filedBy = str4(row.filedBy);
2817
3058
  if (decision === "approved")
2818
3059
  return {
2819
3060
  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.`
@@ -2865,6 +3106,50 @@ async function statusTool(ctx, args) {
2865
3106
  const rows = Array.isArray(r.rows) ? r.rows.filter(isObject4) : [];
2866
3107
  return { text: statusText(rows, (ctx.now ?? Date.now)(), q) };
2867
3108
  }
3109
+ function questionsText(open, now) {
3110
+ if (open.length === 0)
3111
+ return "Nothing is waiting on your answer.";
3112
+ const lines = open.map((q) => {
3113
+ 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) ?? "?"})`;
3115
+ });
3116
+ return `${open.length === 1 ? "One question is" : `${open.length} questions are`} waiting on you:
3117
+ ${lines.join(`
3118
+ `)}
3119
+ Answer with the answer tool; your reply goes on the row.`;
3120
+ }
3121
+ async function answerTool(ctx, args) {
3122
+ if (!ctx.client)
3123
+ return NOT_SET_UP;
3124
+ const now = (ctx.now ?? Date.now)();
3125
+ const answer = str4(args.answer)?.trim();
3126
+ if (!answer) {
3127
+ const r = await ctx.client.get("/proxy/questions");
3128
+ if (!r)
3129
+ return UNREACHABLE;
3130
+ const open = Array.isArray(r.open) ? r.open.filter(isObject4) : [];
3131
+ return { text: questionsText(open, now) };
3132
+ }
3133
+ const project = str4(args.project)?.trim() || undefined;
3134
+ const questionId = str4(args.id)?.trim() || undefined;
3135
+ const r = await ctx.client.postFull("/proxy/answer", {
3136
+ answer,
3137
+ ...project ? { project } : {},
3138
+ ...questionId ? { questionId } : {}
3139
+ });
3140
+ if (!r)
3141
+ return { text: `Nothing was answered. ${UNREACHABLE.text}`, isError: true };
3142
+ if (r.status !== 200) {
3143
+ const open = Array.isArray(r.json?.open) ? (r.json?.open).filter(isObject4) : [];
3144
+ const msg = errorMessage(r.json) ?? `Nothing was answered (HTTP ${r.status}).`;
3145
+ return { text: open.length ? `${msg}
3146
+ ${questionsText(open, now)}` : msg, isError: true };
3147
+ }
3148
+ const asker = str4(r.json?.asker) ?? "the person who asked";
3149
+ return {
3150
+ text: `On the row. ${asker} hears now: "${str4(r.json?.question) ?? "the question"}" → ${answer}`
3151
+ };
3152
+ }
2868
3153
  async function handoffsTool(ctx) {
2869
3154
  if (!ctx.client)
2870
3155
  return NOT_SET_UP;
@@ -2875,7 +3160,7 @@ async function handoffsTool(ctx) {
2875
3160
  if (handoffs.length === 0)
2876
3161
  return { text: "No open hand-offs on your team. Park one with the handoff tool." };
2877
3162
  const now = (ctx.now ?? Date.now)();
2878
- const packets = handoffs.map((h) => str4(h.packet)).filter((p2) => Boolean(p2)).join(`
3163
+ const packets = handoffs.map((h) => str4(h.packet)).filter((p) => Boolean(p)).join(`
2879
3164
 
2880
3165
  ---
2881
3166
 
@@ -3088,10 +3373,10 @@ async function buddyTool(ctx, args) {
3088
3373
  return NOT_SET_UP;
3089
3374
  const email = str4(args.email)?.trim();
3090
3375
  if (!email) {
3091
- const r2 = await ctx.client.get("/proxy/buddy");
3092
- if (!r2)
3376
+ const r = await ctx.client.get("/proxy/buddy");
3377
+ if (!r)
3093
3378
  return UNREACHABLE;
3094
- const list = Array.isArray(r2.buddies) ? r2.buddies : [];
3379
+ const list = Array.isArray(r.buddies) ? r.buddies : [];
3095
3380
  if (list.length === 0)
3096
3381
  return {
3097
3382
  text: "No buddies yet. `buddy {email}` sends someone an invite; once they accept, `handoff {to: email}` reaches them in their own org."
@@ -3102,10 +3387,10 @@ ${lines.join(`
3102
3387
  `)}` };
3103
3388
  }
3104
3389
  if (args.remove === true) {
3105
- const r2 = await ctx.client.postFull("/proxy/buddy", { email, remove: true });
3106
- if (!r2)
3390
+ const r = await ctx.client.postFull("/proxy/buddy", { email, remove: true });
3391
+ if (!r)
3107
3392
  return UNREACHABLE;
3108
- return { text: r2.json?.ok ? `Unpaired from ${email}.` : `${email} was not a buddy.` };
3393
+ return { text: r.json?.ok ? `Unpaired from ${email}.` : `${email} was not a buddy.` };
3109
3394
  }
3110
3395
  const r = await ctx.client.postFull("/proxy/buddy", { email });
3111
3396
  if (!r)
@@ -3133,6 +3418,12 @@ async function callTool(name, args, ctx) {
3133
3418
  return jobsTool(ctx);
3134
3419
  case "status":
3135
3420
  return statusTool(ctx, args);
3421
+ case "touches":
3422
+ return touchesTool(ctx, args);
3423
+ case "attach":
3424
+ return attachTool(ctx, args);
3425
+ case "answer":
3426
+ return answerTool(ctx, args);
3136
3427
  case "dispatch":
3137
3428
  return dispatchTool(ctx, args);
3138
3429
  case "approve":
@@ -3161,6 +3452,15 @@ async function callTool(name, args, ctx) {
3161
3452
  return { text: `Unknown tool: ${name}`, isError: true };
3162
3453
  }
3163
3454
  }
3455
+ var UI_RESOURCES = [
3456
+ {
3457
+ uri: INTAKE_VIEW_URI,
3458
+ name: "sleds_intake",
3459
+ description: "The intake brief as a form: edit, answer what is open, confirm.",
3460
+ mimeType: MCP_APP_MIME,
3461
+ html: INTAKE_VIEW_HTML
3462
+ }
3463
+ ];
3164
3464
  var response = (id, result) => JSON.stringify({ jsonrpc: "2.0", id, result });
3165
3465
  var errorResponse = (id, code, message) => JSON.stringify({ jsonrpc: "2.0", id, error: { code, message } });
3166
3466
  async function handleLine(line, ctx) {
@@ -3188,7 +3488,7 @@ async function handleLine(line, ctx) {
3188
3488
  const requested = str4(params.protocolVersion);
3189
3489
  return response(id, {
3190
3490
  protocolVersion: requested ?? PROTOCOL_VERSION,
3191
- capabilities: { tools: {} },
3491
+ capabilities: { tools: {}, resources: {} },
3192
3492
  serverInfo: SERVER_INFO,
3193
3493
  instructions: SERVER_INSTRUCTIONS
3194
3494
  });
@@ -3197,12 +3497,33 @@ async function handleLine(line, ctx) {
3197
3497
  return response(id, {});
3198
3498
  case "tools/list":
3199
3499
  return response(id, { tools: TOOLS });
3500
+ case "resources/list":
3501
+ return response(id, { resources: UI_RESOURCES.map(({ html: _html, ...r }) => r) });
3502
+ case "resources/templates/list":
3503
+ return response(id, { resourceTemplates: [] });
3504
+ case "resources/read": {
3505
+ const uri = str4(params.uri) ?? "";
3506
+ const r = UI_RESOURCES.find((x) => x.uri === uri);
3507
+ if (!r)
3508
+ return errorResponse(id, -32002, `resource not found: ${uri}`);
3509
+ return response(id, { contents: [{ uri: r.uri, mimeType: r.mimeType, text: r.html }] });
3510
+ }
3200
3511
  case "tools/call": {
3201
3512
  const name = str4(params.name) ?? "";
3202
3513
  const args = isObject4(params.arguments) ? params.arguments : {};
3514
+ const t0 = (ctx.now ?? Date.now)();
3203
3515
  const r = await callTool(name, args, ctx);
3516
+ try {
3517
+ ctx.observe?.({
3518
+ tool: name,
3519
+ args,
3520
+ result: r,
3521
+ ms: Math.max(0, (ctx.now ?? Date.now)() - t0)
3522
+ });
3523
+ } catch {}
3204
3524
  return response(id, {
3205
3525
  content: [{ type: "text", text: r.text }],
3526
+ ...r.structured ? { structuredContent: r.structured } : {},
3206
3527
  ...r.isError ? { isError: true } : {}
3207
3528
  });
3208
3529
  }
@@ -3213,6 +3534,8 @@ async function handleLine(line, ctx) {
3213
3534
 
3214
3535
  // ../../packages/shared/src/dispatch/plan.ts
3215
3536
  var FRESH_MS = 24 * 3600000;
3537
+ // ../../packages/shared/src/dispatch/recommend.ts
3538
+ var QA_TASK_TYPES = new Set(["tests", "bugfix"]);
3216
3539
  // src/codex.ts
3217
3540
  init_config();
3218
3541
  import { spawnSync } from "node:child_process";
@@ -3253,7 +3576,7 @@ ${out.startsWith(`
3253
3576
  ${providerBlock(proxyUrl)}
3254
3577
  `;
3255
3578
  }
3256
- function removeCodexProvider(toml, o2 = {}) {
3579
+ function removeCodexProvider(toml, o = {}) {
3257
3580
  const lines = toml.split(`
3258
3581
  `);
3259
3582
  const out = [];
@@ -3266,7 +3589,7 @@ function removeCodexProvider(toml, o2 = {}) {
3266
3589
  }
3267
3590
  let text = out.join(`
3268
3591
  `);
3269
- if (!o2.keepPrior) {
3592
+ if (!o.keepPrior) {
3270
3593
  const prior = text.match(/^# sleds-prior-model-provider = "([^"]*)"\s*\n/m);
3271
3594
  if (prior) {
3272
3595
  text = text.replace(prior[0], "");
@@ -3283,22 +3606,22 @@ function topLevel(toml) {
3283
3606
  const i = toml.search(/^\s*\[/m);
3284
3607
  return i === -1 ? toml : toml.slice(0, i);
3285
3608
  }
3286
- function initCodex(o2) {
3609
+ function initCodex(o) {
3287
3610
  const wrote = [];
3288
- const path = codexConfigPath(o2.home);
3611
+ const path = codexConfigPath(o.home);
3289
3612
  const prior = existsSync3(path) ? readFileSync3(path, "utf8") : "";
3290
3613
  mkdirSync3(dirname2(path), { recursive: true });
3291
- writeFileSync3(path, applyCodexProvider(prior, o2.proxyUrl), { mode: 384 });
3292
- wrote.push(`${prior ? "updated" : "created"} ${path}: provider "${PROVIDER_ID}" → ${o2.proxyUrl}/v1 (key from $${TOKEN_ENV}), model_provider = "${PROVIDER_ID}"`);
3293
- const cfg = readConfig(o2.home);
3294
- writeConfig(o2.home, {
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, {
3295
3618
  ...cfg,
3296
- token: o2.token,
3297
- proxyUrl: o2.proxyUrl,
3619
+ token: o.token,
3620
+ proxyUrl: o.proxyUrl,
3298
3621
  agentKind: "codex",
3299
- ...o2.email ? { email: o2.email } : {}
3622
+ ...o.email ? { email: o.email } : {}
3300
3623
  });
3301
- wrote.push(`saved token and proxy URL to ${configPath(o2.home)} (0600)`);
3624
+ wrote.push(`saved token and proxy URL to ${configPath(o.home)} (0600)`);
3302
3625
  return wrote;
3303
3626
  }
3304
3627
  function runCodex(home, args, deps = {}) {
@@ -3344,7 +3667,7 @@ var musherLabel = (env = process.env) => {
3344
3667
  return `${MUSHER_LABEL}.${tag}`;
3345
3668
  };
3346
3669
  var musherPlistPath = (home, env = process.env) => join5(home, "Library", "LaunchAgents", `${musherLabel(env)}.plist`);
3347
- var musherLogPath = (configDir2) => join5(configDir2, "musher.log");
3670
+ var musherLogPath = (configDir) => join5(configDir, "musher.log");
3348
3671
  var xmlEscape = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
3349
3672
  var musherPath = (home, execPath, currentPath = "") => {
3350
3673
  const parts = [
@@ -3360,11 +3683,11 @@ var musherPath = (home, execPath, currentPath = "") => {
3360
3683
  ];
3361
3684
  return [...new Set(parts)].join(":");
3362
3685
  };
3363
- var buildMusherPlist = (o2) => {
3364
- const label = o2.label ?? MUSHER_LABEL;
3365
- const args = [...o2.sledsArgv, "musher", "tick"].map((a) => ` <string>${xmlEscape(a)}</string>`).join(`
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(`
3366
3689
  `);
3367
- const env = Object.entries(o2.env).filter(([, v]) => v !== "").map(([k, v]) => ` <key>${xmlEscape(k)}</key>
3690
+ const env = Object.entries(o.env).filter(([, v]) => v !== "").map(([k, v]) => ` <key>${xmlEscape(k)}</key>
3368
3691
  <string>${xmlEscape(v)}</string>`).join(`
3369
3692
  `);
3370
3693
  return `<?xml version="1.0" encoding="UTF-8"?>
@@ -3386,9 +3709,9 @@ ${env}
3386
3709
  <key>RunAtLoad</key>
3387
3710
  <true/>
3388
3711
  <key>StandardOutPath</key>
3389
- <string>${xmlEscape(o2.logPath)}</string>
3712
+ <string>${xmlEscape(o.logPath)}</string>
3390
3713
  <key>StandardErrorPath</key>
3391
- <string>${xmlEscape(o2.logPath)}</string>
3714
+ <string>${xmlEscape(o.logPath)}</string>
3392
3715
  </dict>
3393
3716
  </plist>
3394
3717
  `;
@@ -3405,15 +3728,15 @@ var NO_SESSION_PROMPT = [
3405
3728
  "SLEDS musher: a deferred job started on this seat, but no Claude Code session was recorded",
3406
3729
  "to resume. Do not guess at work. Reply with one line saying so, and stop."
3407
3730
  ].join(" ");
3408
- var claudeArgs = (o2) => [
3731
+ var claudeArgs = (o) => [
3409
3732
  "-p",
3410
- o2.sessionId ? RESUME_PROMPT : NO_SESSION_PROMPT,
3411
- ...o2.sessionId ? ["--resume", o2.sessionId] : [],
3412
- ...o2.model ? ["--model", o2.model] : [],
3733
+ o.sessionId ? RESUME_PROMPT : NO_SESSION_PROMPT,
3734
+ ...o.sessionId ? ["--resume", o.sessionId] : [],
3735
+ ...o.model ? ["--model", o.model] : [],
3413
3736
  "--output-format",
3414
3737
  "json",
3415
3738
  "--permission-mode",
3416
- o2.permissionMode
3739
+ o.permissionMode
3417
3740
  ];
3418
3741
  var parseClaudeOutput = (stdout, exitCode) => {
3419
3742
  const lines = stdout.split(`
@@ -3443,11 +3766,11 @@ var parseClaudeOutput = (stdout, exitCode) => {
3443
3766
  const tail = lines.slice(-5).join(" ").slice(0, SUMMARY_MAX);
3444
3767
  return { ok: false, summary: tail || `claude exited ${exitCode ?? "without a code"}` };
3445
3768
  };
3446
- var defaultExecCapture = (cmd, args, o2) => {
3769
+ var defaultExecCapture = (cmd, args, o) => {
3447
3770
  const r = spawnSync2(cmd, [...args], {
3448
- cwd: o2.cwd,
3771
+ cwd: o.cwd,
3449
3772
  encoding: "utf8",
3450
- timeout: o2.timeoutMs,
3773
+ timeout: o.timeoutMs,
3451
3774
  maxBuffer: 64 * 1024 * 1024,
3452
3775
  stdio: ["ignore", "pipe", "pipe"],
3453
3776
  env: { ...process.env, SLEDS_MUSHER: "1" }
@@ -3459,16 +3782,16 @@ var defaultExecCapture = (cmd, args, o2) => {
3459
3782
  ...r.error ? { error: r.error.message } : {}
3460
3783
  };
3461
3784
  };
3462
- var musherClient = (o2) => {
3463
- const fetchFn = o2.fetchFn ?? fetch;
3464
- const headers = { authorization: `Bearer ${o2.token}`, "content-type": "application/json" };
3785
+ var musherClient = (o) => {
3786
+ const fetchFn = o.fetchFn ?? fetch;
3787
+ const headers = { authorization: `Bearer ${o.token}`, "content-type": "application/json" };
3465
3788
  const call = async (path, body) => {
3466
3789
  try {
3467
- const res = await fetchFn(`${o2.proxyUrl}${path}`, {
3790
+ const res = await fetchFn(`${o.proxyUrl}${path}`, {
3468
3791
  method: body === undefined ? "GET" : "POST",
3469
3792
  headers,
3470
3793
  ...body === undefined ? {} : { body: JSON.stringify(body) },
3471
- signal: AbortSignal.timeout(o2.timeoutMs ?? 1e4)
3794
+ signal: AbortSignal.timeout(o.timeoutMs ?? 1e4)
3472
3795
  });
3473
3796
  if (!res.ok)
3474
3797
  return null;
@@ -3510,33 +3833,33 @@ var collectOutputs = (commits, summary) => {
3510
3833
  out.push(`pr:${m[0]}`);
3511
3834
  return out.slice(0, 50);
3512
3835
  };
3513
- var runJob = async (o2) => {
3514
- const log = o2.log ?? (() => {});
3515
- const sessionId = o2.job.claudeSessionId ?? o2.fallbackSession?.id ?? null;
3516
- const cwd = o2.job.cwd ?? o2.fallbackSession?.cwd ?? undefined;
3517
- const started = await o2.client.start(o2.job.jobId, {
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, {
3518
3841
  ...sessionId ? { claudeSessionId: sessionId } : {},
3519
3842
  ...cwd ? { cwd } : {},
3520
- host: o2.host
3843
+ host: o.host
3521
3844
  });
3522
3845
  if (!started)
3523
3846
  return { kind: "skipped", reason: "control plane unreachable" };
3524
3847
  if (!started.ok)
3525
3848
  return { kind: "skipped", reason: started.reason };
3526
- const model = o2.job.model ?? started.model ?? o2.fallbackSession?.model ?? null;
3527
- const args = claudeArgs({ sessionId, model, permissionMode: o2.permissionMode });
3528
- log(`musher: running job ${o2.job.jobId} (${sessionId ? `resume ${sessionId}` : "no session"}${model ? ` · ${model}` : ""}${cwd ? ` · ${cwd}` : ""})`);
3529
- const exec = o2.exec ?? defaultExecCapture;
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;
3530
3853
  const headBefore = gitHead(cwd, exec);
3531
3854
  const t0 = Date.now();
3532
- const r = exec(o2.claudePath, args, { cwd, timeoutMs: o2.timeoutMs });
3855
+ const r = exec(o.claudePath, args, { cwd, timeoutMs: o.timeoutMs });
3533
3856
  const durationMs = Date.now() - t0;
3534
- const result = r.error ? { ok: false, summary: `could not run ${o2.claudePath}: ${r.error}` } : parseClaudeOutput(r.stdout, r.status);
3857
+ const result = r.error ? { ok: false, summary: `could not run ${o.claudePath}: ${r.error}` } : parseClaudeOutput(r.stdout, r.status);
3535
3858
  if (!r.error && r.status === null)
3536
- result.summary = `timed out after ${Math.round(o2.timeoutMs / 60000)} min. ${result.summary}`.trim();
3859
+ result.summary = `timed out after ${Math.round(o.timeoutMs / 60000)} min. ${result.summary}`.trim();
3537
3860
  const outputs = collectOutputs(gitNewCommits(cwd, headBefore, gitHead(cwd, exec), exec), result.summary);
3538
- await o2.client.finish({
3539
- jobId: o2.job.jobId,
3861
+ await o.client.finish({
3862
+ jobId: o.job.jobId,
3540
3863
  runId: started.runId,
3541
3864
  ok: result.ok,
3542
3865
  summary: result.summary,
@@ -3545,7 +3868,7 @@ var runJob = async (o2) => {
3545
3868
  durationMs: result.durationMs ?? durationMs,
3546
3869
  ...result.sessionId ?? sessionId ? { claudeSessionId: result.sessionId ?? sessionId } : {}
3547
3870
  });
3548
- log(`musher: job ${o2.job.jobId} ${result.ok ? "delivered" : "failed"} in ${Math.round(durationMs / 1000)}s`);
3871
+ log(`musher: job ${o.job.jobId} ${result.ok ? "delivered" : "failed"} in ${Math.round(durationMs / 1000)}s`);
3549
3872
  return { kind: "ran", ok: result.ok, summary: result.summary, durationMs, outputs };
3550
3873
  };
3551
3874
  var verifyAttestation = (payload, sig, publicKeyB64) => {
@@ -3849,11 +4172,11 @@ var bindingResetAt = (w, now) => {
3849
4172
  return pick.length ? Math.min(...pick) : undefined;
3850
4173
  };
3851
4174
  var readSeatState = (home) => {
3852
- const p2 = seatStatePath(home);
3853
- if (!existsSync5(p2))
4175
+ const p = seatStatePath(home);
4176
+ if (!existsSync5(p))
3854
4177
  return {};
3855
4178
  try {
3856
- return parseSeatState(JSON.parse(readFileSync5(p2, "utf8")));
4179
+ return parseSeatState(JSON.parse(readFileSync5(p, "utf8")));
3857
4180
  } catch {
3858
4181
  return {};
3859
4182
  }
@@ -3978,28 +4301,28 @@ var seatWindowBody = (w) => ({
3978
4301
  ...w.sevenDay ? { sevenDay: w.sevenDay } : {},
3979
4302
  ...w.spendLimit ? { spendLimit: w.spendLimit } : {}
3980
4303
  });
3981
- var postJson = async (o2, path, body) => {
3982
- const fetchFn = o2.fetchFn ?? fetch;
4304
+ var postJson = async (o, path, body) => {
4305
+ const fetchFn = o.fetchFn ?? fetch;
3983
4306
  try {
3984
- const res = await fetchFn(`${o2.proxyUrl}${path}`, {
4307
+ const res = await fetchFn(`${o.proxyUrl}${path}`, {
3985
4308
  method: "POST",
3986
- headers: { authorization: `Bearer ${o2.token}`, "content-type": "application/json" },
4309
+ headers: { authorization: `Bearer ${o.token}`, "content-type": "application/json" },
3987
4310
  body: JSON.stringify(body),
3988
- signal: AbortSignal.timeout(o2.timeoutMs ?? SEAT_POST_TIMEOUT_MS)
4311
+ signal: AbortSignal.timeout(o.timeoutMs ?? SEAT_POST_TIMEOUT_MS)
3989
4312
  });
3990
4313
  return res.ok;
3991
4314
  } catch {
3992
4315
  return false;
3993
4316
  }
3994
4317
  };
3995
- var postSeatWindow = (o2, w) => postJson(o2, "/proxy/seat", seatWindowBody(w));
3996
- var postSeatWall = (o2, input) => postJson(o2, "/proxy/seat-wall", { source: "claude", ...input });
4318
+ var postSeatWindow = (o, w) => postJson(o, "/proxy/seat", seatWindowBody(w));
4319
+ var postSeatWall = (o, input) => postJson(o, "/proxy/seat-wall", { source: "claude", ...input });
3997
4320
  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.";
3998
4321
  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.";
3999
4322
  var seatWallLine = (hasUpstreamKey) => hasUpstreamKey ? SEAT_WALL_LINE : SEAT_WALL_LINE_NO_KEY;
4000
- var readStdin = (timeoutMs, stdin = process.stdin) => new Promise((resolve3) => {
4323
+ var readStdin = (timeoutMs, stdin = process.stdin) => new Promise((resolve) => {
4001
4324
  if (stdin.isTTY) {
4002
- resolve3("");
4325
+ resolve("");
4003
4326
  return;
4004
4327
  }
4005
4328
  let data = "";
@@ -4009,7 +4332,7 @@ var readStdin = (timeoutMs, stdin = process.stdin) => new Promise((resolve3) =>
4009
4332
  return;
4010
4333
  settled = true;
4011
4334
  clearTimeout(timer);
4012
- resolve3(data);
4335
+ resolve(data);
4013
4336
  };
4014
4337
  const timer = setTimeout(finish, timeoutMs);
4015
4338
  stdin.setEncoding("utf8");
@@ -4127,21 +4450,21 @@ var parseFlags = (argv) => {
4127
4450
  }
4128
4451
  return flags;
4129
4452
  };
4130
- var defer = async (o2) => {
4131
- const log = o2.log ?? console.log;
4132
- const error = o2.error ?? console.error;
4133
- const now = (o2.now ?? Date.now)();
4134
- const flags = parseFlags(o2.argv);
4135
- const client = o2.client ?? (() => {
4136
- const src = meterSource(o2.home);
4137
- return src ? musherClient({ ...src, fetchFn: o2.fetchFn }) : null;
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;
4138
4461
  })();
4139
4462
  if (!client) {
4140
4463
  error("No seat on this machine. Run `sleds init` first.");
4141
4464
  return 1;
4142
4465
  }
4143
- const state = readSeatState(o2.home);
4144
- const piped = o2.stdin ? seatSessionFromStdin(o2.stdin, now) : null;
4466
+ const state = readSeatState(o.home);
4467
+ const piped = o.stdin ? seatSessionFromStdin(o.stdin, now) : null;
4145
4468
  const remembered = piped ?? state.session;
4146
4469
  const sessionId = flags.session ?? remembered?.id;
4147
4470
  const cwd = flags.cwd ?? remembered?.cwd;
@@ -4162,7 +4485,7 @@ var defer = async (o2) => {
4162
4485
  ...model ? { model } : {},
4163
4486
  ...sessionId ? { claudeSessionId: sessionId } : {},
4164
4487
  ...cwd ? { cwd } : {},
4165
- host: o2.host ?? hostname()
4488
+ host: o.host ?? hostname()
4166
4489
  });
4167
4490
  if (!r) {
4168
4491
  error("Could not reach the SLEDS proxy; nothing was scheduled.");
@@ -4171,8 +4494,8 @@ var defer = async (o2) => {
4171
4494
  const when = r.scheduledAt <= now + 60000 ? "as soon as the musher ticks (within 5 min)" : `${formatLocal(r.scheduledAt)} (in ${formatDuration(r.scheduledAt - now)})`;
4172
4495
  log(`Deferred. This Mac resumes ${sessionId ? `session ${sessionId.slice(0, 8)}…` : "the work"}${model ? ` on ${model}` : ""} ${when}.`);
4173
4496
  log(` job ${r.jobId}${cwd ? ` · ${cwd}` : ""} · runs on your seat, no key · sleds musher --status to watch`);
4174
- const cfg = readConfig(o2.home);
4175
- if (process.platform === "darwin" && !existsSync6(musherPlistPath(o2.home)) && cfg.musher?.agent !== "launchd")
4497
+ const cfg = readConfig(o.home);
4498
+ if (process.platform === "darwin" && !existsSync6(musherPlistPath(o.home)) && cfg.musher?.agent !== "launchd")
4176
4499
  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.`);
4177
4500
  log(" Keep this Mac awake at that time (System Settings → Battery → prevent sleeping on power adapter).");
4178
4501
  return 0;
@@ -4352,7 +4675,7 @@ var renderCockpit = (cockpit, window, events, prior, now = Date.now(), extra = {
4352
4675
  `/sled pickup ${newHandoffs[0]}`,
4353
4676
  ...ws(cockpit)
4354
4677
  ]);
4355
- for (const id of extra.feed.delivered.filter((id2) => !(before?.delivered ?? []).includes(id2)))
4678
+ for (const id of extra.feed.delivered.filter((id) => !(before?.delivered ?? []).includes(id)))
4356
4679
  argv.push([
4357
4680
  "log",
4358
4681
  "--level",
@@ -4362,7 +4685,7 @@ var renderCockpit = (cockpit, window, events, prior, now = Date.now(), extra = {
4362
4685
  `overnight job done · /sled resume ${id}`,
4363
4686
  ...ws(cockpit)
4364
4687
  ]);
4365
- for (const id of extra.feed.failed.filter((id2) => !(before?.failed ?? []).includes(id2)))
4688
+ for (const id of extra.feed.failed.filter((id) => !(before?.failed ?? []).includes(id)))
4366
4689
  argv.push([
4367
4690
  "notify",
4368
4691
  "--title",
@@ -4419,15 +4742,15 @@ var feedFromJobs = (body, me, now) => {
4419
4742
  }
4420
4743
  return f;
4421
4744
  };
4422
- var fetchFeed = async (o2, now) => {
4745
+ var fetchFeed = async (o, now) => {
4423
4746
  try {
4424
- const res = await (o2.fetchFn ?? fetch)(`${o2.proxyUrl}/proxy/jobs`, {
4425
- headers: { authorization: `Bearer ${o2.token}` },
4426
- signal: AbortSignal.timeout(o2.timeoutMs ?? 1500)
4747
+ const res = await (o.fetchFn ?? fetch)(`${o.proxyUrl}/proxy/jobs`, {
4748
+ headers: { authorization: `Bearer ${o.token}` },
4749
+ signal: AbortSignal.timeout(o.timeoutMs ?? 1500)
4427
4750
  });
4428
4751
  if (!res.ok)
4429
4752
  return null;
4430
- return feedFromJobs(await res.json(), o2.email, now);
4753
+ return feedFromJobs(await res.json(), o.email, now);
4431
4754
  } catch {
4432
4755
  return null;
4433
4756
  }
@@ -4463,9 +4786,9 @@ init_meter();
4463
4786
  var defaultWhich = (cmd) => {
4464
4787
  const path = process.env.PATH ?? "";
4465
4788
  for (const dir of path.split(":").filter(Boolean)) {
4466
- const p2 = `${dir}/${cmd}`;
4467
- if (existsSync7(p2))
4468
- return p2;
4789
+ const p = `${dir}/${cmd}`;
4790
+ if (existsSync7(p))
4791
+ return p;
4469
4792
  }
4470
4793
  return null;
4471
4794
  };
@@ -4475,14 +4798,14 @@ var findClaude = (home, configured, which) => {
4475
4798
  const found = which("claude");
4476
4799
  if (found)
4477
4800
  return found;
4478
- for (const p2 of [
4801
+ for (const p of [
4479
4802
  `${home}/.local/bin/claude`,
4480
4803
  "/opt/homebrew/bin/claude",
4481
4804
  "/usr/local/bin/claude",
4482
4805
  `${home}/.npm-global/bin/claude`
4483
4806
  ])
4484
- if (existsSync7(p2))
4485
- return p2;
4807
+ if (existsSync7(p))
4808
+ return p;
4486
4809
  return null;
4487
4810
  };
4488
4811
  var musherAgentState = (home, env = process.env) => {
@@ -4545,10 +4868,10 @@ function defaultWhich2(cmd) {
4545
4868
  return [];
4546
4869
  }
4547
4870
  }
4548
- async function runDoctor(o2) {
4549
- const env = o2.env ?? process.env;
4871
+ async function runDoctor(o) {
4872
+ const env = o.env ?? process.env;
4550
4873
  const checks = [];
4551
- const home = o2.home;
4874
+ const home = o.home;
4552
4875
  const overrides = [
4553
4876
  env.SLEDS_HOME?.trim() ? `SLEDS_HOME=${env.SLEDS_HOME.trim()}` : null,
4554
4877
  env.CLAUDE_CONFIG_DIR?.trim() ? `CLAUDE_CONFIG_DIR=${env.CLAUDE_CONFIG_DIR.trim()}` : null
@@ -4598,7 +4921,7 @@ async function runDoctor(o2) {
4598
4921
  }
4599
4922
  const src = meterSource(home);
4600
4923
  if (src) {
4601
- const health = await checkHealth(src.proxyUrl, o2.fetchFn, o2.timeoutMs);
4924
+ const health = await checkHealth(src.proxyUrl, o.fetchFn, o.timeoutMs);
4602
4925
  checks.push({
4603
4926
  level: health === "ok" ? "ok" : "fail",
4604
4927
  name: "proxy",
@@ -4607,15 +4930,15 @@ async function runDoctor(o2) {
4607
4930
  });
4608
4931
  }
4609
4932
  if (settings) {
4610
- const seat2 = hasSeatHook(settings);
4933
+ const seat = hasSeatHook(settings);
4611
4934
  const spec = hasSpecHook(settings);
4612
4935
  const session = hasSessionHook(settings);
4613
4936
  const stop = hasStopHook(settings);
4614
- const all = seat2 && spec && session && stop;
4937
+ const all = seat && spec && session && stop;
4615
4938
  checks.push({
4616
4939
  level: all ? "ok" : "warn",
4617
4940
  name: "hooks",
4618
- detail: `seat-event ${seat2 ? "on" : "missing"} · spec-event ${spec ? "on" : "missing"} · session-event ${session ? "on" : "missing"} · stop-event ${stop ? "on" : "missing"}`,
4941
+ detail: `seat-event ${seat ? "on" : "missing"} · spec-event ${spec ? "on" : "missing"} · session-event ${session ? "on" : "missing"} · stop-event ${stop ? "on" : "missing"}`,
4619
4942
  fix: all ? undefined : "sleds hooks"
4620
4943
  });
4621
4944
  const want = env.SLEDS_HOME?.trim() ? resolve3(home, env.SLEDS_HOME.trim()) : undefined;
@@ -4646,18 +4969,18 @@ async function runDoctor(o2) {
4646
4969
  name: "cockpit",
4647
4970
  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)"
4648
4971
  });
4649
- const nodeVersion = o2.nodeVersion ?? process.version;
4972
+ const nodeVersion = o.nodeVersion ?? process.version;
4650
4973
  checks.push({
4651
4974
  level: nodeOk(nodeVersion) ? "ok" : "warn",
4652
4975
  name: "node",
4653
4976
  detail: `${nodeVersion}${nodeOk(nodeVersion) ? "" : ` — seat-sync needs ≥ ${NODE_MIN} (node:sqlite)`}`,
4654
4977
  fix: nodeOk(nodeVersion) ? undefined : `brew install node@22 && echo 'export PATH="/opt/homebrew/opt/node@22/bin:$PATH"' >> ~/.zshrc`
4655
4978
  });
4656
- const which = (o2.which ?? defaultWhich2)("sleds");
4657
- const stale = which.filter((p2) => {
4979
+ const which = (o.which ?? defaultWhich2)("sleds");
4980
+ const stale = which.filter((p) => {
4658
4981
  try {
4659
- const target = lstatSync(p2).isSymbolicLink() ? readlinkSync(p2) : p2;
4660
- return /\/src\/index\.ts$/.test(target) || /\.bun\/bin\/sleds$/.test(p2);
4982
+ const target = lstatSync(p).isSymbolicLink() ? readlinkSync(p) : p;
4983
+ return /\/src\/index\.ts$/.test(target) || /\.bun\/bin\/sleds$/.test(p);
4661
4984
  } catch {
4662
4985
  return false;
4663
4986
  }
@@ -4679,7 +5002,7 @@ async function runDoctor(o2) {
4679
5002
  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)}%` : ""}`,
4680
5003
  fix: age === null ? "open claude, say hi, exit — the status line posts the seat's quota" : undefined
4681
5004
  });
4682
- if ((o2.platform ?? process.platform) === "darwin") {
5005
+ if ((o.platform ?? process.platform) === "darwin") {
4683
5006
  const agent = musherAgentState(home, env);
4684
5007
  checks.push(!agent.installed ? {
4685
5008
  level: "warn",
@@ -4696,14 +5019,14 @@ async function runDoctor(o2) {
4696
5019
  name: "musher",
4697
5020
  detail: `launchd agent ${musherLabel(env)}, every 5 min`
4698
5021
  });
4699
- const claude = findClaude(home, readConfig(home).musher?.claudePath, (c) => (o2.which ?? defaultWhich2)(c)[0] ?? null);
5022
+ const claude = findClaude(home, readConfig(home).musher?.claudePath, (c) => (o.which ?? defaultWhich2)(c)[0] ?? null);
4700
5023
  checks.push(claude ? { level: "ok", name: "claude cli", detail: claude } : {
4701
5024
  level: "warn",
4702
5025
  name: "claude cli",
4703
5026
  detail: "`claude` not on PATH — the musher cannot run Claude Code",
4704
5027
  fix: 'sleds musher --claude "$(which claude)"'
4705
5028
  });
4706
- const sleep = (o2.pmset ?? defaultPmset)();
5029
+ const sleep = (o.pmset ?? defaultPmset)();
4707
5030
  if (sleep !== null)
4708
5031
  checks.push(sleep.acSleepMinutes === 0 ? {
4709
5032
  level: "ok",
@@ -4733,9 +5056,9 @@ var defaultPmset = () => {
4733
5056
  return null;
4734
5057
  }
4735
5058
  };
4736
- var doctor = async (o2) => {
4737
- const log = o2.log ?? console.log;
4738
- const checks = await runDoctor(o2);
5059
+ var doctor = async (o) => {
5060
+ const log = o.log ?? console.log;
5061
+ const checks = await runDoctor(o);
4739
5062
  for (const line of formatDoctor(checks))
4740
5063
  log(line);
4741
5064
  return checks.some((c) => c.level === "fail") ? 1 : 0;
@@ -4763,21 +5086,21 @@ function sourceKey(s) {
4763
5086
  function sourceText2(s) {
4764
5087
  return [`${str5(s.kind) ?? "?"} ${str5(s.project) ?? "?"}`, str5(s.doc), str5(s.item)].filter(Boolean).join(" › ");
4765
5088
  }
4766
- var readJson = (p2) => {
4767
- if (!existsSync9(p2))
5089
+ var readJson = (p) => {
5090
+ if (!existsSync9(p))
4768
5091
  return null;
4769
5092
  try {
4770
- return JSON.parse(readFileSync8(p2, "utf8"));
5093
+ return JSON.parse(readFileSync8(p, "utf8"));
4771
5094
  } catch {
4772
5095
  return null;
4773
5096
  }
4774
5097
  };
4775
- var writeJson = (p2, v) => {
4776
- mkdirSync7(dirname4(p2), { recursive: true, mode: 448 });
4777
- const tmp = `${p2}.${process.pid}.tmp`;
5098
+ var writeJson = (p, v) => {
5099
+ mkdirSync7(dirname4(p), { recursive: true, mode: 448 });
5100
+ const tmp = `${p}.${process.pid}.tmp`;
4778
5101
  writeFileSync7(tmp, `${JSON.stringify(v, null, 2)}
4779
5102
  `, { mode: 384 });
4780
- renameSync5(tmp, p2);
5103
+ renameSync5(tmp, p);
4781
5104
  };
4782
5105
  var readOwed = (home, now = Date.now()) => {
4783
5106
  const f = readJson(owedPath(home));
@@ -4792,14 +5115,14 @@ var readSession = (home) => {
4792
5115
  var writeSession = (home, s) => writeJson(sessionPath(home), s);
4793
5116
  function withEstimated(rows, a) {
4794
5117
  const key = sourceKey(a.source);
4795
- const row2 = {
5118
+ const row = {
4796
5119
  key,
4797
5120
  source: a.source,
4798
5121
  ...a.title ? { title: a.title } : {},
4799
5122
  ts: a.now,
4800
5123
  ...a.sessionId ? { sessionId: a.sessionId } : {}
4801
5124
  };
4802
- return [...rows.filter((r) => r.key !== key), row2];
5125
+ return [...rows.filter((r) => r.key !== key), row];
4803
5126
  }
4804
5127
  function withActual(rows, a) {
4805
5128
  if (a.state === "started")
@@ -4897,59 +5220,59 @@ var callTool2 = (name, args, deps) => callTool(name, args, contextFor(deps));
4897
5220
  var handleLine2 = (line, deps) => handleLine(line, contextFor(deps));
4898
5221
 
4899
5222
  // src/commands/handoffs.ts
4900
- var handoffsCmd = async (o2) => {
4901
- const r = await callTool2("handoffs", {}, { home: o2.home });
4902
- (r.isError ? o2.error ?? console.error : o2.log ?? console.log)(r.text);
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);
4903
5226
  return r.isError ? 1 : 0;
4904
5227
  };
4905
- var pickupCmd = async (o2) => {
4906
- const id = o2.argv[0];
5228
+ var pickupCmd = async (o) => {
5229
+ const id = o.argv[0];
4907
5230
  if (!id) {
4908
- (o2.error ?? console.error)("usage: sleds pickup <handoffId> (ids from `sleds handoffs`)");
5231
+ (o.error ?? console.error)("usage: sleds pickup <handoffId> (ids from `sleds handoffs`)");
4909
5232
  return 2;
4910
5233
  }
4911
- const r = await callTool2("pickup", { handoffId: id }, { home: o2.home });
4912
- (r.isError ? o2.error ?? console.error : o2.log ?? console.log)(r.text);
5234
+ const r = await callTool2("pickup", { handoffId: id }, { home: o.home });
5235
+ (r.isError ? o.error ?? console.error : o.log ?? console.log)(r.text);
4913
5236
  return r.isError ? 1 : 0;
4914
5237
  };
4915
- var withdrawCmd = async (o2) => {
4916
- const id = o2.argv[0];
5238
+ var withdrawCmd = async (o) => {
5239
+ const id = o.argv[0];
4917
5240
  if (!id) {
4918
- (o2.error ?? console.error)("usage: sleds withdraw <handoffId> (one you parked)");
5241
+ (o.error ?? console.error)("usage: sleds withdraw <handoffId> (one you parked)");
4919
5242
  return 2;
4920
5243
  }
4921
- const r = await callTool2("withdraw", { handoffId: id }, { home: o2.home });
4922
- (r.isError ? o2.error ?? console.error : o2.log ?? console.log)(r.text);
5244
+ const r = await callTool2("withdraw", { handoffId: id }, { home: o.home });
5245
+ (r.isError ? o.error ?? console.error : o.log ?? console.log)(r.text);
4923
5246
  return r.isError ? 1 : 0;
4924
5247
  };
4925
5248
 
4926
5249
  // src/commands/hooks.ts
4927
5250
  init_settings();
4928
- var hooks = (o2) => {
4929
- const log = o2.log ?? console.log;
4930
- const error = o2.error ?? console.error;
4931
- if (!hasClaudeCode(o2.home)) {
4932
- error(`Claude Code not found (${claudeDir(o2.home)} does not exist). Install it, then re-run.`);
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.`);
4933
5256
  return 1;
4934
5257
  }
4935
- const settings = readSettings(o2.home);
5258
+ const settings = readSettings(o.home);
4936
5259
  const had = hasSeatHook(settings);
4937
5260
  const hadSpec = hasSpecHook(settings);
4938
- const withSeat = applySeatHook(settings, o2.seatEventCommand ?? sledsCommand("seat-event", o2.home, o2.env));
5261
+ const withSeat = applySeatHook(settings, o.seatEventCommand ?? sledsCommand("seat-event", o.home, o.env));
4939
5262
  const hadPrompt = hasPromptHook(settings);
4940
5263
  const hadSession = hasSessionHook(settings);
4941
5264
  const hadStop = hasStopHook(settings);
4942
- const withPrompt = applyPromptHook(applySpecHook(withSeat, o2.specEventCommand ?? sledsCommand("spec-event", o2.home, o2.env)), o2.promptEventCommand ?? sledsCommand("prompt-event", o2.home, o2.env));
4943
- const withSpec = applyStopHook(applySessionHook(withPrompt, o2.sessionEventCommand ?? sledsCommand("session-event", o2.home, o2.env)), o2.stopEventCommand ?? sledsCommand("stop-event", o2.home, o2.env));
4944
- const hadLine = o2.refreshStatusLine !== false && isSledsStatusLine(withSpec[STATUS_LINE_KEY]);
4945
- writeSettings(o2.home, hadLine ? applyStatusLine(withSpec, sledsCommand("statusline", o2.home, o2.env)).settings : withSpec);
4946
- log(`${had ? "refreshed" : "set"} hooks.Notification (${SEAT_EVENT_MATCHER}) in ${settingsPath(o2.home)}`);
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)}`);
4947
5270
  if (hadLine)
4948
- log(`refreshed statusLine in ${settingsPath(o2.home)}`);
4949
- log(`${hadSpec ? "refreshed" : "set"} hooks.PostToolUse (${SPEC_EVENT_MATCHER} → estimate spec files) in ${settingsPath(o2.home)}`);
4950
- log(`${hadPrompt ? "refreshed" : "set"} hooks.UserPromptSubmit (the seat speaks up in the session at 90 % and at a wall) in ${settingsPath(o2.home)}`);
4951
- log(`${hadSession ? "refreshed" : "set"} hooks.SessionStart (the loop and the rows this machine owes, before the first turn) in ${settingsPath(o2.home)}`);
4952
- log(`${hadStop ? "refreshed" : "set"} hooks.Stop (a session that estimated and never posted its actual is asked once) in ${settingsPath(o2.home)}`);
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)}`);
4953
5276
  return 0;
4954
5277
  };
4955
5278
 
@@ -5100,43 +5423,172 @@ From Cowork or any MCP client (\`sleds mcp\`): the \`handoffs\` tool lists what
5100
5423
  A buddy is one person outside your org you can hand work to and from. Pair once (MCP \`buddy {email}\` — they click Accept in the mail); then \`handoff {task, to: "<their email>"}\` lands in their Waiting for you, in whatever org their seat is, and \`pickup\` there runs it on their seat, metered to them. Only the packet crosses — never keys, usage or membership. \`buddy\` with no email lists your pairs; \`buddy {email, remove: true}\` unpairs. A teammate is never a buddy: hand off to them directly.
5101
5424
  `;
5102
5425
 
5426
+ // src/commands/device.ts
5427
+ import { spawnSync as spawnSync3 } from "node:child_process";
5428
+ import { hostname as hostname2, platform } from "node:os";
5429
+ var DEVICE_REASONS = {
5430
+ unreachable: "could not reach the proxy.",
5431
+ rate_limited: "too many sign-ins are open right now. Try again in a few minutes.",
5432
+ denied: "that sign-in was declined on the web.",
5433
+ expired: "the code expired before it was approved (they last fifteen minutes). Run `sleds init` again.",
5434
+ bad_answer: "the proxy gave an answer this CLI does not understand — update it: curl -fsSL sleds.ai/install | sh"
5435
+ };
5436
+ function deviceFailureText(reason) {
5437
+ return `Sign-in did not complete: ${DEVICE_REASONS[reason]} Or mint a seat on the dashboard (Settings › Your seat & tools) and run \`sleds init --token …\`.`;
5438
+ }
5439
+ function parseStart(body) {
5440
+ if (typeof body !== "object" || body === null)
5441
+ return null;
5442
+ const b = body;
5443
+ if (typeof b.deviceCode !== "string" || typeof b.userCode !== "string")
5444
+ return null;
5445
+ if (typeof b.verifyUrl !== "string" || !/^https?:\/\//.test(b.verifyUrl))
5446
+ return null;
5447
+ const expiresIn = typeof b.expiresIn === "number" && b.expiresIn > 0 ? Math.min(b.expiresIn, 3600) : 900;
5448
+ const interval = typeof b.interval === "number" && b.interval >= 1 ? Math.min(b.interval, 30) : 3;
5449
+ return {
5450
+ deviceCode: b.deviceCode,
5451
+ userCode: b.userCode,
5452
+ verifyUrl: b.verifyUrl,
5453
+ expiresIn,
5454
+ interval
5455
+ };
5456
+ }
5457
+ function parsePoll(body) {
5458
+ if (typeof body !== "object" || body === null)
5459
+ return null;
5460
+ const b = body;
5461
+ if (b.status === "pending" || b.status === "denied" || b.status === "expired")
5462
+ return { status: b.status };
5463
+ if (b.status !== "approved")
5464
+ return null;
5465
+ const org = b.org;
5466
+ if (typeof b.token !== "string" || typeof b.email !== "string" || !org)
5467
+ return null;
5468
+ if (typeof org.name !== "string" || typeof org.slug !== "string")
5469
+ return null;
5470
+ return {
5471
+ status: "approved",
5472
+ approved: {
5473
+ token: b.token,
5474
+ email: b.email,
5475
+ org: { name: org.name, slug: org.slug, plan: typeof org.plan === "string" ? org.plan : "" },
5476
+ workspaceUrl: typeof b.workspaceUrl === "string" ? b.workspaceUrl : ""
5477
+ }
5478
+ };
5479
+ }
5480
+ 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];
5484
+ try {
5485
+ const r = spawnSync3(cmd, args, { stdio: "ignore", timeout: 5000 });
5486
+ return r.error === undefined && r.status === 0;
5487
+ } catch {
5488
+ return false;
5489
+ }
5490
+ }
5491
+ 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;
5497
+ let started = null;
5498
+ try {
5499
+ const res = await fetchFn(`${o.proxyUrl}/device/start`, {
5500
+ method: "POST",
5501
+ headers: { "content-type": "application/json" },
5502
+ body: JSON.stringify({ agent: o.agent ?? "claude-code", host: o.host ?? hostname2() }),
5503
+ signal: AbortSignal.timeout(20000)
5504
+ });
5505
+ if (res.status === 429)
5506
+ return { ok: false, reason: "rate_limited" };
5507
+ if (!res.ok)
5508
+ return { ok: false, reason: "unreachable" };
5509
+ started = parseStart(await res.json().catch(() => null));
5510
+ } catch {
5511
+ return { ok: false, reason: "unreachable" };
5512
+ }
5513
+ if (!started)
5514
+ return { ok: false, reason: "bad_answer" };
5515
+ const opened = o.noBrowser ? false : (o.openBrowser ?? openInBrowser)(started.verifyUrl);
5516
+ log("");
5517
+ log(opened ? "Opening your browser to sign in to SLEDS." : "Open this in your browser to sign in to SLEDS:");
5518
+ log(` ${started.verifyUrl}`);
5519
+ log("");
5520
+ log(` Your code: ${started.userCode} — the page shows the same one.`);
5521
+ log("");
5522
+ log("Waiting for the approval…");
5523
+ const deadline = now() + started.expiresIn * 1000;
5524
+ let intervalMs = started.interval * 1000;
5525
+ while (now() < deadline) {
5526
+ await sleep(intervalMs);
5527
+ let body;
5528
+ try {
5529
+ const res = await fetchFn(`${o.proxyUrl}/device/poll`, {
5530
+ method: "POST",
5531
+ headers: { "content-type": "application/json" },
5532
+ body: JSON.stringify({ deviceCode: started.deviceCode }),
5533
+ signal: AbortSignal.timeout(20000)
5534
+ });
5535
+ if (res.status === 429) {
5536
+ intervalMs = Math.min(intervalMs * 2, 30000);
5537
+ continue;
5538
+ }
5539
+ body = await res.json().catch(() => null);
5540
+ } catch {
5541
+ continue;
5542
+ }
5543
+ const r = parsePoll(body);
5544
+ if (!r)
5545
+ return { ok: false, reason: "bad_answer" };
5546
+ if (r.status === "pending")
5547
+ continue;
5548
+ if (r.status === "approved")
5549
+ return { ok: true, approved: r.approved };
5550
+ return { ok: false, reason: r.status };
5551
+ }
5552
+ return { ok: false, reason: "expired" };
5553
+ }
5554
+
5103
5555
  // src/commands/hooks.ts
5104
5556
  init_settings();
5105
- var hooks2 = (o2) => {
5106
- const log = o2.log ?? console.log;
5107
- const error = o2.error ?? console.error;
5108
- if (!hasClaudeCode(o2.home)) {
5109
- error(`Claude Code not found (${claudeDir(o2.home)} does not exist). Install it, then re-run.`);
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.`);
5110
5562
  return 1;
5111
5563
  }
5112
- const settings = readSettings(o2.home);
5564
+ const settings = readSettings(o.home);
5113
5565
  const had = hasSeatHook(settings);
5114
5566
  const hadSpec = hasSpecHook(settings);
5115
- const withSeat = applySeatHook(settings, o2.seatEventCommand ?? sledsCommand("seat-event", o2.home, o2.env));
5567
+ const withSeat = applySeatHook(settings, o.seatEventCommand ?? sledsCommand("seat-event", o.home, o.env));
5116
5568
  const hadPrompt = hasPromptHook(settings);
5117
5569
  const hadSession = hasSessionHook(settings);
5118
5570
  const hadStop = hasStopHook(settings);
5119
- const withPrompt = applyPromptHook(applySpecHook(withSeat, o2.specEventCommand ?? sledsCommand("spec-event", o2.home, o2.env)), o2.promptEventCommand ?? sledsCommand("prompt-event", o2.home, o2.env));
5120
- const withSpec = applyStopHook(applySessionHook(withPrompt, o2.sessionEventCommand ?? sledsCommand("session-event", o2.home, o2.env)), o2.stopEventCommand ?? sledsCommand("stop-event", o2.home, o2.env));
5121
- const hadLine = o2.refreshStatusLine !== false && isSledsStatusLine(withSpec[STATUS_LINE_KEY]);
5122
- writeSettings(o2.home, hadLine ? applyStatusLine(withSpec, sledsCommand("statusline", o2.home, o2.env)).settings : withSpec);
5123
- log(`${had ? "refreshed" : "set"} hooks.Notification (${SEAT_EVENT_MATCHER}) in ${settingsPath(o2.home)}`);
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)}`);
5124
5576
  if (hadLine)
5125
- log(`refreshed statusLine in ${settingsPath(o2.home)}`);
5126
- log(`${hadSpec ? "refreshed" : "set"} hooks.PostToolUse (${SPEC_EVENT_MATCHER} → estimate spec files) in ${settingsPath(o2.home)}`);
5127
- log(`${hadPrompt ? "refreshed" : "set"} hooks.UserPromptSubmit (the seat speaks up in the session at 90 % and at a wall) in ${settingsPath(o2.home)}`);
5128
- log(`${hadSession ? "refreshed" : "set"} hooks.SessionStart (the loop and the rows this machine owes, before the first turn) in ${settingsPath(o2.home)}`);
5129
- log(`${hadStop ? "refreshed" : "set"} hooks.Stop (a session that estimated and never posted its actual is asked once) in ${settingsPath(o2.home)}`);
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)}`);
5130
5582
  return 0;
5131
5583
  };
5132
5584
 
5133
5585
  // src/commands/init.ts
5134
5586
  var DEFAULT_PROXY_URL = "https://proxy.sleds.ai";
5135
- var promptStdin = (question) => new Promise((resolve4) => {
5587
+ var promptStdin = (question) => new Promise((resolve) => {
5136
5588
  const rl = createInterface({ input: process.stdin, output: process.stderr });
5137
5589
  rl.question(question, (answer) => {
5138
5590
  rl.close();
5139
- resolve4(answer.trim());
5591
+ resolve(answer.trim());
5140
5592
  });
5141
5593
  });
5142
5594
  async function signupIndividual(proxyUrl, email, credential, fetchFn = fetch) {
@@ -5233,56 +5685,70 @@ async function probeProxy(url, fetchFn = fetch, opts = {}) {
5233
5685
  }
5234
5686
  return false;
5235
5687
  }
5236
- var resolveToken = async (o2) => {
5237
- const env = o2.env ?? process.env;
5238
- if (o2.token)
5239
- return { token: o2.token.trim(), source: "flag" };
5688
+ var resolveToken = async (o) => {
5689
+ const env = o.env ?? process.env;
5690
+ if (o.token)
5691
+ return { token: o.token.trim(), source: "flag" };
5240
5692
  if (env.SLEDS_TOKEN)
5241
5693
  return { token: env.SLEDS_TOKEN.trim(), source: "env" };
5242
- const fromFile = readTokenFile(o2.home);
5694
+ const fromFile = readTokenFile(o.home);
5243
5695
  if (fromFile)
5244
5696
  return { token: fromFile, source: "file" };
5245
- if (o2.nonInteractive || !o2.prompt)
5697
+ if (o.nonInteractive)
5246
5698
  return null;
5247
- if (!o2.signup) {
5248
- const typed = (await o2.prompt("Paste your SLEDS seat token (sled_…), or press enter to sign up as an individual: ")).trim();
5249
- if (typed)
5250
- return { token: typed, source: "prompt" };
5251
- }
5252
- const url = normalizeProxyUrl(o2.url ?? env.SLEDS_PROXY_URL ?? readConfig(o2.home).proxyUrl ?? DEFAULT_PROXY_URL);
5699
+ const url = normalizeProxyUrl(o.url ?? env.SLEDS_PROXY_URL ?? readConfig(o.home).proxyUrl ?? DEFAULT_PROXY_URL);
5253
5700
  if (!url)
5254
5701
  return null;
5255
- const log = o2.log ?? console.log;
5256
- const err = o2.error ?? console.error;
5257
- let team = o2.team?.trim() || null;
5258
- if (team === null && !o2.email) {
5259
- const which = (await o2.prompt("Set up SLEDS for (1) just you, or (2) your team? [1/2]: ")).trim();
5702
+ const log = o.log ?? console.log;
5703
+ const err = o.error ?? console.error;
5704
+ if (!o.signup) {
5705
+ const r = await deviceFlow({
5706
+ proxyUrl: url,
5707
+ agent: "claude-code",
5708
+ host: o.host,
5709
+ fetchFn: o.fetchFn,
5710
+ openBrowser: o.openBrowser ?? openInBrowser,
5711
+ noBrowser: o.noBrowser,
5712
+ sleep: o.sleep,
5713
+ log
5714
+ });
5715
+ if (!r.ok) {
5716
+ err(deviceFailureText(r.reason));
5717
+ return null;
5718
+ }
5719
+ return { token: r.approved.token, source: "device", approved: r.approved };
5720
+ }
5721
+ if (!o.prompt)
5722
+ 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();
5260
5726
  if (which === "2") {
5261
- team = (await o2.prompt("Team name (e.g. your company): ")).trim();
5727
+ team = (await o.prompt("Team name (e.g. your company): ")).trim();
5262
5728
  if (!team)
5263
5729
  return null;
5264
5730
  }
5265
5731
  }
5266
- const email = (o2.email ?? await o2.prompt(team ? "Your work email: " : "Your email: ")).trim();
5732
+ const email = (o.email ?? await o.prompt(team ? "Your work email: " : "Your email: ")).trim();
5267
5733
  if (!email.includes("@"))
5268
5734
  return null;
5269
5735
  if (team) {
5270
- 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();
5271
- const r2 = await signupTeam(url, email, team, credential2 || null, o2.fetchFn);
5272
- if (!r2.ok) {
5273
- err(signupFailureText(r2));
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));
5274
5740
  return null;
5275
5741
  }
5276
- 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."}`);
5277
- if (r2.verifyPending)
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)
5278
5744
  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).`);
5279
5745
  log("Invite your team on the dashboard (Members → invite by email, Admin → Mint a seat); each person runs `sleds init --token …`.");
5280
- return { token: r2.token, source: "signup" };
5746
+ return { token: r.token, source: "signup" };
5281
5747
  }
5282
- const credential = (await o2.prompt("Your Anthropic API key (sk-ant-…, stored encrypted, spends only your account): ")).trim();
5748
+ const credential = (await o.prompt("Your Anthropic API key (sk-ant-…, stored encrypted, spends only your account): ")).trim();
5283
5749
  if (!credential)
5284
5750
  return null;
5285
- const r = await signupIndividual(url, email, credential, o2.fetchFn);
5751
+ const r = await signupIndividual(url, email, credential, o.fetchFn);
5286
5752
  if (!r.ok) {
5287
5753
  err(signupFailureText(r));
5288
5754
  return null;
@@ -5290,66 +5756,66 @@ var resolveToken = async (o2) => {
5290
5756
  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.`);
5291
5757
  return { token: r.token, source: "signup" };
5292
5758
  };
5293
- var init = async (o2) => {
5294
- const log = o2.log ?? console.log;
5295
- const error = o2.error ?? console.error;
5296
- const env = o2.env ?? process.env;
5297
- if (!hasClaudeCode(o2.home)) {
5298
- error(`Claude Code not found (${claudeDir(o2.home)} does not exist). Install it, then re-run.`);
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.`);
5299
5765
  return 1;
5300
5766
  }
5301
- const resolved = await resolveToken(o2);
5767
+ const resolved = await resolveToken(o);
5302
5768
  if (!resolved) {
5303
- error(o2.signup ? "Signup did not complete. Nothing was changed." : "No seat token. Pass --token, set SLEDS_TOKEN, run without --non-interactive, or use --signup.");
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.");
5304
5770
  return 2;
5305
5771
  }
5306
5772
  if (!isSeatToken(resolved.token)) {
5307
5773
  error("Invalid seat token: expected something like sled_xxxxxxxx.");
5308
5774
  return 2;
5309
5775
  }
5310
- const url = normalizeProxyUrl(o2.url ?? env.SLEDS_PROXY_URL ?? readConfig(o2.home).proxyUrl ?? DEFAULT_PROXY_URL);
5776
+ const url = normalizeProxyUrl(o.url ?? env.SLEDS_PROXY_URL ?? readConfig(o.home).proxyUrl ?? DEFAULT_PROXY_URL);
5311
5777
  if (!url) {
5312
- error(`Invalid proxy URL: ${JSON.stringify(o2.url ?? env.SLEDS_PROXY_URL ?? "")}. Expected https://proxy.sleds.ai.`);
5778
+ error(`Invalid proxy URL: ${JSON.stringify(o.url ?? env.SLEDS_PROXY_URL ?? "")}. Expected https://proxy.sleds.ai.`);
5313
5779
  return 2;
5314
5780
  }
5315
- if (!o2.noProbe && !await probeProxy(url, o2.fetchFn)) {
5781
+ if (!o.noProbe && !await probeProxy(url, o.fetchFn)) {
5316
5782
  error(`No SLEDS proxy answered at ${url}/healthz. Check the URL (or pass --no-probe to save it anyway).`);
5317
5783
  return 2;
5318
5784
  }
5319
5785
  const wrote = [];
5320
- const seatInfo = await fetchSeatInfo(url, resolved.token, o2.fetchFn);
5786
+ const seatInfo = await fetchSeatInfo(url, resolved.token, o.fetchFn);
5321
5787
  const org = seatInfo.org;
5322
- const wiring = o2.proxyAlways ? "proxy-always" : o2.seatFirst ? "seat-first" : org?.mode ?? "proxy-always";
5788
+ const wiring = o.proxyAlways ? "proxy-always" : o.seatFirst ? "seat-first" : org?.mode ?? "proxy-always";
5323
5789
  if (wiring === "proxy-always" && org && !org.capabilities.upstreamKey) {
5324
5790
  error(`${org.name} has no platform key for the proxy to forward with; --proxy-always would break every request. Using seat-first.`);
5325
5791
  }
5326
5792
  const finalWiring = wiring === "proxy-always" && org && !org.capabilities.upstreamKey ? "seat-first" : wiring;
5327
- if (backupSettings(o2.home))
5328
- wrote.push(`backed up settings to ${backupPath(o2.home)}`);
5329
- const current = readSettings(o2.home);
5793
+ if (backupSettings(o.home))
5794
+ wrote.push(`backed up settings to ${backupPath(o.home)}`);
5795
+ const current = readSettings(o.home);
5330
5796
  const wired = finalWiring === "proxy-always" ? applyProxy(current, { url, token: resolved.token }) : removeProxy(current);
5331
- const statusLine2 = applyStatusLine(wired, o2.statusLineCommand ?? sledsCommand("statusline", o2.home, o2.env));
5332
- writeSettings(o2.home, statusLine2.settings);
5333
- 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`);
5334
- wrote.push(statusLine2.prior === undefined ? `set statusLine in ${settingsPath(o2.home)}` : `set statusLine in ${settingsPath(o2.home)} (previous one kept for uninstall)`);
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)`);
5335
5801
  hooks2({
5336
- home: o2.home,
5337
- env: o2.env,
5802
+ home: o.home,
5803
+ env: o.env,
5338
5804
  refreshStatusLine: false,
5339
- seatEventCommand: o2.seatEventCommand,
5340
- specEventCommand: o2.specEventCommand,
5341
- promptEventCommand: o2.promptEventCommand,
5342
- sessionEventCommand: o2.sessionEventCommand,
5343
- stopEventCommand: o2.stopEventCommand,
5805
+ seatEventCommand: o.seatEventCommand,
5806
+ specEventCommand: o.specEventCommand,
5807
+ promptEventCommand: o.promptEventCommand,
5808
+ sessionEventCommand: o.sessionEventCommand,
5809
+ stopEventCommand: o.stopEventCommand,
5344
5810
  log: (line) => wrote.push(line),
5345
5811
  error
5346
5812
  });
5347
5813
  if (resolved.source !== "file") {
5348
- writeTokenFile(o2.home, resolved.token);
5349
- wrote.push(`saved token to ${tokenPath(o2.home)} (0600)`);
5814
+ writeTokenFile(o.home, resolved.token);
5815
+ wrote.push(`saved token to ${tokenPath(o.home)} (0600)`);
5350
5816
  }
5351
- const prior = readConfig(o2.home);
5352
- writeConfig(o2.home, {
5817
+ const prior = readConfig(o.home);
5818
+ writeConfig(o.home, {
5353
5819
  ...prior,
5354
5820
  token: resolved.token,
5355
5821
  proxyUrl: url,
@@ -5357,18 +5823,24 @@ var init = async (o2) => {
5357
5823
  agentKind: "claude-code",
5358
5824
  ...seatInfo.email ? { email: seatInfo.email } : {},
5359
5825
  ...org ? { org } : {},
5360
- ...statusLine2.prior !== undefined ? { priorStatusLine: statusLine2.prior } : {}
5826
+ ...statusLine.prior !== undefined ? { priorStatusLine: statusLine.prior } : {}
5361
5827
  });
5362
- wrote.push(`saved token and proxy URL to ${configPath(o2.home)} (0600)`);
5363
- mkdirSync8(skillDir(o2.home), { recursive: true });
5364
- writeFileSync8(skillPath(o2.home), SKILL_MD);
5365
- wrote.push(`installed skill at ${skillPath(o2.home)}`);
5366
- if (o2.installMusher) {
5367
- const r = o2.installMusher();
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();
5368
5834
  if (r)
5369
5835
  wrote.push(r);
5370
5836
  }
5371
5837
  const orgLine = org ? `${org.name} · ${org.plan}${org.capabilities.upstreamKey ? ` · ${org.capabilities.upstreamSource === "own" ? "platform key" : "shared key"}` : " · no platform key"}` : "org unknown (proxy did not answer /proxy/meter)";
5838
+ if (resolved.approved) {
5839
+ log("");
5840
+ log(`Signed in as ${resolved.approved.email} · ${resolved.approved.org.name}. This machine is now a seat there.`);
5841
+ if (resolved.approved.workspaceUrl)
5842
+ log(`Your workspace on the web: ${resolved.approved.workspaceUrl}`);
5843
+ }
5372
5844
  if (finalWiring === "proxy-always") {
5373
5845
  log(`Claude Code now runs through SLEDS on the platform key. (${orgLine})`);
5374
5846
  log("");
@@ -5395,9 +5867,9 @@ var init = async (o2) => {
5395
5867
  };
5396
5868
 
5397
5869
  // src/commands/mcp.ts
5398
- var mcp = async (o2) => {
5399
- const stdin = o2.stdin ?? process.stdin;
5400
- const write = o2.write ?? ((line) => process.stdout.write(`${line}
5870
+ var mcp = async (o) => {
5871
+ const stdin = o.stdin ?? process.stdin;
5872
+ const write = o.write ?? ((line) => process.stdout.write(`${line}
5401
5873
  `));
5402
5874
  let buffer = "";
5403
5875
  for await (const chunk of stdin) {
@@ -5407,7 +5879,7 @@ var mcp = async (o2) => {
5407
5879
  while (nl !== -1) {
5408
5880
  const line = buffer.slice(0, nl);
5409
5881
  buffer = buffer.slice(nl + 1);
5410
- const out = await handleLine2(line, { home: o2.home });
5882
+ const out = await handleLine2(line, { home: o.home });
5411
5883
  if (out !== null)
5412
5884
  write(out);
5413
5885
  nl = buffer.indexOf(`
@@ -5415,7 +5887,7 @@ var mcp = async (o2) => {
5415
5887
  }
5416
5888
  }
5417
5889
  if (buffer.trim().length > 0) {
5418
- const out = await handleLine2(buffer, { home: o2.home });
5890
+ const out = await handleLine2(buffer, { home: o.home });
5419
5891
  if (out !== null)
5420
5892
  write(out);
5421
5893
  }
@@ -5425,15 +5897,15 @@ var mcp = async (o2) => {
5425
5897
  // src/commands/meter.ts
5426
5898
  init_config();
5427
5899
  init_meter();
5428
- var meter = async (o2) => {
5429
- const log = o2.log ?? console.log;
5430
- const now = (o2.now ?? Date.now)();
5431
- const src = meterSource(o2.home);
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);
5432
5904
  if (!src) {
5433
- log(`SLEDS is not set up (${configPath(o2.home)}). Run \`sleds init\` first.`);
5905
+ log(`SLEDS is not set up (${configPath(o.home)}). Run \`sleds init\` first.`);
5434
5906
  return 1;
5435
5907
  }
5436
- const m = await loadMeter({ home: o2.home, now, fetchFn: o2.fetchFn, timeoutMs: o2.timeoutMs });
5908
+ const m = await loadMeter({ home: o.home, now, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs });
5437
5909
  if (!m) {
5438
5910
  log(`sleds offline. ${src.proxyUrl} did not answer → \`sleds status\` for the proxy health.`);
5439
5911
  return 2;
@@ -5446,16 +5918,16 @@ var meter = async (o2) => {
5446
5918
  // src/commands/musher.ts
5447
5919
  init_config();
5448
5920
  init_meter();
5449
- import { spawnSync as spawnSync3 } from "node:child_process";
5921
+ import { spawnSync as spawnSync4 } from "node:child_process";
5450
5922
  import { existsSync as existsSync10, mkdirSync as mkdirSync9, readFileSync as readFileSync9, rmSync as rmSync2, writeFileSync as writeFileSync9 } from "node:fs";
5451
- import { hostname as hostname2 } from "node:os";
5923
+ import { hostname as hostname3 } from "node:os";
5452
5924
  import { dirname as dirname5 } from "node:path";
5453
5925
  var defaultWhich3 = (cmd) => {
5454
5926
  const path = process.env.PATH ?? "";
5455
5927
  for (const dir of path.split(":").filter(Boolean)) {
5456
- const p2 = `${dir}/${cmd}`;
5457
- if (existsSync10(p2))
5458
- return p2;
5928
+ const p = `${dir}/${cmd}`;
5929
+ if (existsSync10(p))
5930
+ return p;
5459
5931
  }
5460
5932
  return null;
5461
5933
  };
@@ -5465,68 +5937,68 @@ var findClaude2 = (home, configured, which) => {
5465
5937
  const found = which("claude");
5466
5938
  if (found)
5467
5939
  return found;
5468
- for (const p2 of [
5940
+ for (const p of [
5469
5941
  `${home}/.local/bin/claude`,
5470
5942
  "/opt/homebrew/bin/claude",
5471
5943
  "/usr/local/bin/claude",
5472
5944
  `${home}/.npm-global/bin/claude`
5473
5945
  ])
5474
- if (existsSync10(p2))
5475
- return p2;
5946
+ if (existsSync10(p))
5947
+ return p;
5476
5948
  return null;
5477
5949
  };
5478
- var musherInstall = (o2) => {
5479
- const log = o2.log ?? console.log;
5480
- const error = o2.error ?? console.error;
5481
- const platform = o2.platform ?? process.platform;
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;
5482
5954
  if (platform !== "darwin") {
5483
5955
  error(`sleds musher --install needs launchd (macOS). On ${platform}, run \`sleds musher tick\` from cron every 5 minutes.`);
5484
5956
  return 1;
5485
5957
  }
5486
- const env = o2.env ?? process.env;
5487
- const plist = musherPlistPath(o2.home, env);
5958
+ const env = o.env ?? process.env;
5959
+ const plist = musherPlistPath(o.home, env);
5488
5960
  const label = musherLabel(env);
5489
- const dir = configDir(o2.home, env);
5961
+ const dir = configDir(o.home, env);
5490
5962
  mkdirSync9(dirname5(plist), { recursive: true });
5491
5963
  mkdirSync9(dir, { recursive: true, mode: 448 });
5492
- const execPath = o2.sledsArgv[0] ?? process.execPath;
5964
+ const execPath = o.sledsArgv[0] ?? process.execPath;
5493
5965
  const agentEnv = {
5494
- PATH: musherPath(o2.home, execPath, env.PATH ?? ""),
5495
- HOME: o2.home,
5966
+ PATH: musherPath(o.home, execPath, env.PATH ?? ""),
5967
+ HOME: o.home,
5496
5968
  ...env.SLEDS_HOME ? { SLEDS_HOME: env.SLEDS_HOME } : {},
5497
5969
  ...env.CLAUDE_CONFIG_DIR ? { CLAUDE_CONFIG_DIR: env.CLAUDE_CONFIG_DIR } : {}
5498
5970
  };
5499
- const exec = o2.exec ?? defaultExecSync;
5971
+ const exec = o.exec ?? defaultExecSync;
5500
5972
  if (existsSync10(plist))
5501
5973
  exec("launchctl", ["unload", plist]);
5502
- writeFileSync9(plist, buildMusherPlist({ sledsArgv: o2.sledsArgv, env: agentEnv, logPath: musherLogPath(dir), label }));
5974
+ writeFileSync9(plist, buildMusherPlist({ sledsArgv: o.sledsArgv, env: agentEnv, logPath: musherLogPath(dir), label }));
5503
5975
  const r = exec("launchctl", ["load", plist]);
5504
5976
  if (!r.ok) {
5505
5977
  error(`launchctl load failed: ${r.stderr.trim() || "unknown"}`);
5506
5978
  return 1;
5507
5979
  }
5508
- const cfg = readConfig(o2.home);
5509
- writeConfig(o2.home, { ...cfg, musher: { ...cfg.musher ?? {}, agent: "launchd" } });
5510
- const claude = findClaude2(o2.home, cfg.musher?.claudePath, o2.which ?? defaultWhich3);
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);
5511
5983
  log(`musher installed: launchd agent ${label}, every 5 min · log ${musherLogPath(dir)}`);
5512
5984
  if (!claude)
5513
5985
  log("warn: `claude` not found on PATH — set it with `sleds musher --claude /path/to/claude`");
5514
5986
  return 0;
5515
5987
  };
5516
- var musherUninstall = (o2) => {
5517
- const log = o2.log ?? console.log;
5518
- const env = o2.env ?? process.env;
5519
- const plist = musherPlistPath(o2.home, env);
5520
- const exec = o2.exec ?? defaultExecSync;
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;
5521
5993
  if (existsSync10(plist)) {
5522
5994
  exec("launchctl", ["unload", plist]);
5523
5995
  rmSync2(plist, { force: true });
5524
5996
  log(`musher removed: ${musherLabel(env)}`);
5525
5997
  } else
5526
5998
  log("musher was not installed.");
5527
- const cfg = readConfig(o2.home);
5999
+ const cfg = readConfig(o.home);
5528
6000
  if (cfg.musher)
5529
- writeConfig(o2.home, { ...cfg, musher: { ...cfg.musher, agent: "none" } });
6001
+ writeConfig(o.home, { ...cfg, musher: { ...cfg.musher, agent: "none" } });
5530
6002
  return 0;
5531
6003
  };
5532
6004
  var formatMusherStatus = (list, now) => {
@@ -5541,23 +6013,23 @@ var formatMusherStatus = (list, now) => {
5541
6013
  lines.push("nothing deferred on this seat.");
5542
6014
  return lines;
5543
6015
  };
5544
- var clientFor = (o2) => {
5545
- if (o2.client)
5546
- return o2.client;
5547
- const src = meterSource(o2.home);
5548
- return src ? musherClient({ ...src, fetchFn: o2.fetchFn }) : null;
5549
- };
5550
- var musherStatus = async (o2) => {
5551
- const log = o2.log ?? console.log;
5552
- const now = (o2.now ?? Date.now)();
5553
- const env = o2.env ?? process.env;
5554
- const plist = musherPlistPath(o2.home, env);
5555
- const cfg = readConfig(o2.home);
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);
5556
6028
  log(existsSync10(plist) ? `agent ${musherLabel(env)} installed (${plist})` : "agent not installed — sleds musher --install");
5557
- const claude = findClaude2(o2.home, cfg.musher?.claudePath, o2.which ?? defaultWhich3);
6029
+ const claude = findClaude2(o.home, cfg.musher?.claudePath, o.which ?? defaultWhich3);
5558
6030
  log(`claude ${claude ?? "not found — sleds musher --claude /path/to/claude"}`);
5559
6031
  log(`mode ${cfg.musher?.permissionMode ?? DEFAULT_PERMISSION_MODE} · timeout ${cfg.musher?.timeoutMinutes ?? DEFAULT_TIMEOUT_MINUTES} min`);
5560
- const client = clientFor(o2);
6032
+ const client = clientFor(o);
5561
6033
  if (!client) {
5562
6034
  log("seat not initialised (sleds init)");
5563
6035
  return 1;
@@ -5571,9 +6043,9 @@ var musherStatus = async (o2) => {
5571
6043
  log(line);
5572
6044
  return 0;
5573
6045
  };
5574
- var musherTick = async (o2) => {
5575
- const log = o2.log ?? console.log;
5576
- const client = clientFor(o2);
6046
+ var musherTick = async (o) => {
6047
+ const log = o.log ?? console.log;
6048
+ const client = clientFor(o);
5577
6049
  if (!client) {
5578
6050
  log("musher: seat not initialised; nothing to do");
5579
6051
  return 1;
@@ -5585,19 +6057,19 @@ var musherTick = async (o2) => {
5585
6057
  }
5586
6058
  if (list.due.length === 0)
5587
6059
  return 0;
5588
- const cfg = readConfig(o2.home);
5589
- const claude = findClaude2(o2.home, cfg.musher?.claudePath, o2.which ?? defaultWhich3) ?? "claude";
5590
- const seat = readSeatState(o2.home);
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);
5591
6063
  for (const job of list.due) {
5592
6064
  const out = await runJob({
5593
6065
  job,
5594
6066
  client,
5595
6067
  fallbackSession: seat.session,
5596
- host: o2.host ?? hostname2(),
6068
+ host: o.host ?? hostname3(),
5597
6069
  claudePath: claude,
5598
6070
  permissionMode: cfg.musher?.permissionMode ?? DEFAULT_PERMISSION_MODE,
5599
6071
  timeoutMs: (cfg.musher?.timeoutMinutes ?? DEFAULT_TIMEOUT_MINUTES) * 60000,
5600
- exec: o2.execCapture,
6072
+ exec: o.execCapture,
5601
6073
  log
5602
6074
  });
5603
6075
  if (out.kind === "skipped")
@@ -5605,9 +6077,9 @@ var musherTick = async (o2) => {
5605
6077
  }
5606
6078
  return 0;
5607
6079
  };
5608
- var musherConfigure = (o2, flags) => {
5609
- const log = o2.log ?? console.log;
5610
- const cfg = readConfig(o2.home);
6080
+ var musherConfigure = (o, flags) => {
6081
+ const log = o.log ?? console.log;
6082
+ const cfg = readConfig(o.home);
5611
6083
  const musher = { ...cfg.musher ?? {} };
5612
6084
  if (flags.claude)
5613
6085
  musher.claudePath = flags.claude;
@@ -5616,23 +6088,23 @@ var musherConfigure = (o2, flags) => {
5616
6088
  if (flags.timeout) {
5617
6089
  const n = Number.parseInt(flags.timeout, 10);
5618
6090
  if (!Number.isFinite(n) || n <= 0) {
5619
- (o2.error ?? console.error)("--timeout takes minutes, a positive number");
6091
+ (o.error ?? console.error)("--timeout takes minutes, a positive number");
5620
6092
  return 1;
5621
6093
  }
5622
6094
  musher.timeoutMinutes = n;
5623
6095
  }
5624
- writeConfig(o2.home, { ...cfg, musher });
6096
+ writeConfig(o.home, { ...cfg, musher });
5625
6097
  log(`musher settings: claude ${musher.claudePath ?? "(PATH)"} · mode ${musher.permissionMode ?? DEFAULT_PERMISSION_MODE} · timeout ${musher.timeoutMinutes ?? DEFAULT_TIMEOUT_MINUTES} min`);
5626
6098
  return 0;
5627
6099
  };
5628
6100
  var defaultExecSync = (cmd, args) => {
5629
- const r = spawnSync3(cmd, [...args], { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" });
6101
+ const r = spawnSync4(cmd, [...args], { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" });
5630
6102
  if (r.error)
5631
6103
  return { ok: false, stderr: r.error.message };
5632
6104
  return { ok: r.status === 0, stderr: r.stderr ?? "" };
5633
6105
  };
5634
- var musher = async (o2) => {
5635
- const args = [...o2.argv];
6106
+ var musher = async (o) => {
6107
+ const args = [...o.argv];
5636
6108
  const flags = {};
5637
6109
  const positional = [];
5638
6110
  for (let i = 0;i < args.length; i++) {
@@ -5648,16 +6120,16 @@ var musher = async (o2) => {
5648
6120
  positional.push(a);
5649
6121
  }
5650
6122
  if (flags.install)
5651
- return musherInstall(o2);
6123
+ return musherInstall(o);
5652
6124
  if (flags.uninstall)
5653
- return musherUninstall(o2);
6125
+ return musherUninstall(o);
5654
6126
  if (flags.claude || flags.mode || flags.timeout)
5655
- return musherConfigure(o2, flags);
6127
+ return musherConfigure(o, flags);
5656
6128
  if (positional[0] === "tick")
5657
- return musherTick(o2);
6129
+ return musherTick(o);
5658
6130
  if (flags.status || positional.length === 0)
5659
- return musherStatus(o2);
5660
- (o2.error ?? console.error)(`sleds musher: unknown argument ${positional[0]}`);
6131
+ return musherStatus(o);
6132
+ (o.error ?? console.error)(`sleds musher: unknown argument ${positional[0]}`);
5661
6133
  return 1;
5662
6134
  };
5663
6135
 
@@ -5705,12 +6177,12 @@ var systemdCalendar = (deadline) => `${deadline.getFullYear()}-${pad2(deadline.g
5705
6177
  var scheduleRevert = (d, deadlineMs) => {
5706
6178
  const deadline = new Date(deadlineMs);
5707
6179
  if (d.platform === "darwin") {
5708
- const p2 = plistPath(d.home);
6180
+ const p = plistPath(d.home);
5709
6181
  try {
5710
6182
  mkdirSync10(join9(d.home, "Library", "LaunchAgents"), { recursive: true });
5711
- d.exec("launchctl", ["unload", p2]);
5712
- writeFileSync10(p2, buildLaunchdPlist(d.sledsArgv, deadline));
5713
- const r = d.exec("launchctl", ["load", p2]);
6183
+ d.exec("launchctl", ["unload", p]);
6184
+ writeFileSync10(p, buildLaunchdPlist(d.sledsArgv, deadline));
6185
+ const r = d.exec("launchctl", ["load", p]);
5714
6186
  if (r.ok)
5715
6187
  return { kind: "launchd", detail: `launchd agent ${REVERT_LABEL}` };
5716
6188
  return { kind: "manual", detail: `launchctl load failed: ${r.stderr.trim() || "unknown"}` };
@@ -5735,10 +6207,10 @@ var scheduleRevert = (d, deadlineMs) => {
5735
6207
  };
5736
6208
  var cancelRevert = (d, kind) => {
5737
6209
  if (kind === "launchd" || d.platform === "darwin") {
5738
- const p2 = plistPath(d.home);
5739
- if (existsSync11(p2)) {
5740
- d.exec("launchctl", ["unload", p2]);
5741
- rmSync3(p2, { force: true });
6210
+ const p = plistPath(d.home);
6211
+ if (existsSync11(p)) {
6212
+ d.exec("launchctl", ["unload", p]);
6213
+ rmSync3(p, { force: true });
5742
6214
  }
5743
6215
  }
5744
6216
  if (kind === "systemd" || d.platform === "linux") {
@@ -5748,15 +6220,15 @@ var cancelRevert = (d, kind) => {
5748
6220
 
5749
6221
  // src/commands/off.ts
5750
6222
  init_settings();
5751
- var off = (o2) => {
5752
- const log = o2.log ?? console.log;
5753
- const cfg = readConfig(o2.home);
5754
- const settings = readSettings(o2.home);
6223
+ var off = (o) => {
6224
+ const log = o.log ?? console.log;
6225
+ const cfg = readConfig(o.home);
6226
+ const settings = readSettings(o.home);
5755
6227
  const wasOn = cfg.mode === "on" || isPointedAtProxy(settings);
5756
6228
  if (wasOn)
5757
- writeSettings(o2.home, switchOff(settings, cfg.priorEnv ?? {}));
5758
- if (o2.scheduler)
5759
- cancelRevert(o2.scheduler, cfg.schedule);
6229
+ writeSettings(o.home, switchOff(settings, cfg.priorEnv ?? {}));
6230
+ if (o.scheduler)
6231
+ cancelRevert(o.scheduler, cfg.schedule);
5760
6232
  const next = {
5761
6233
  ...cfg,
5762
6234
  mode: "off",
@@ -5766,7 +6238,7 @@ var off = (o2) => {
5766
6238
  priorEnv: undefined,
5767
6239
  schedule: undefined
5768
6240
  };
5769
- writeConfig(o2.home, next);
6241
+ writeConfig(o.home, next);
5770
6242
  if (!wasOn) {
5771
6243
  log("Sleds already off. Claude Code is on your seat.");
5772
6244
  return 0;
@@ -5785,19 +6257,19 @@ init_settings();
5785
6257
  var DEFAULT_PROXY_URL2 = "https://proxy.sleds.ai";
5786
6258
 
5787
6259
  // src/commands/on.ts
5788
- var on = (o2) => {
5789
- const log = o2.log ?? console.log;
5790
- const error = o2.error ?? console.error;
5791
- const env = o2.env ?? process.env;
5792
- const now = (o2.now ?? Date.now)();
5793
- if (!hasClaudeCode(o2.home)) {
5794
- error(`Claude Code not found (${claudeDir(o2.home)} does not exist). Install it, then re-run.`);
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.`);
5795
6267
  return 1;
5796
6268
  }
5797
- const cfg = readConfig(o2.home);
5798
- const token = cfg.token ?? readTokenFile(o2.home) ?? env.SLEDS_TOKEN?.trim();
6269
+ const cfg = readConfig(o.home);
6270
+ const token = cfg.token ?? readTokenFile(o.home) ?? env.SLEDS_TOKEN?.trim();
5799
6271
  if (!token) {
5800
- error(`No seat token in ${configPath(o2.home)}. Run \`sleds init\` first.`);
6272
+ error(`No seat token in ${configPath(o.home)}. Run \`sleds init\` first.`);
5801
6273
  return 2;
5802
6274
  }
5803
6275
  if (!isSeatToken(token)) {
@@ -5808,28 +6280,28 @@ var on = (o2) => {
5808
6280
  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.`);
5809
6281
  return 2;
5810
6282
  }
5811
- const url = (o2.url ?? cfg.proxyUrl ?? env.SLEDS_PROXY_URL ?? DEFAULT_PROXY_URL2).replace(/\/+$/, "");
6283
+ const url = (o.url ?? cfg.proxyUrl ?? env.SLEDS_PROXY_URL ?? DEFAULT_PROXY_URL2).replace(/\/+$/, "");
5812
6284
  let untilResetMs;
5813
- if (o2.untilReset !== undefined) {
6285
+ if (o.untilReset !== undefined) {
5814
6286
  try {
5815
- untilResetMs = parseUntilReset(o2.untilReset, now);
6287
+ untilResetMs = parseUntilReset(o.untilReset, now);
5816
6288
  } catch (err) {
5817
6289
  error(err instanceof Error ? err.message : String(err));
5818
6290
  return 2;
5819
6291
  }
5820
6292
  }
5821
- const settings = readSettings(o2.home);
6293
+ const settings = readSettings(o.home);
5822
6294
  const priorEnv = cfg.mode === "on" && cfg.priorEnv ? cfg.priorEnv : capturePriorEnv(settings);
5823
- const backedUp = backupSettings(o2.home);
5824
- writeSettings(o2.home, switchOn(settings, { url, token }));
6295
+ const backedUp = backupSettings(o.home);
6296
+ writeSettings(o.home, switchOn(settings, { url, token }));
5825
6297
  let schedule;
5826
6298
  let scheduleNote;
5827
- if (untilResetMs !== undefined && !o2.noSchedule && o2.scheduler && hasHomeOverride(env)) {
6299
+ if (untilResetMs !== undefined && !o.noSchedule && o.scheduler && hasHomeOverride(env)) {
5828
6300
  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.");
5829
6301
  return 2;
5830
6302
  }
5831
- if (untilResetMs !== undefined && !o2.noSchedule && o2.scheduler) {
5832
- const r = scheduleRevert(o2.scheduler, untilResetMs);
6303
+ if (untilResetMs !== undefined && !o.noSchedule && o.scheduler) {
6304
+ const r = scheduleRevert(o.scheduler, untilResetMs);
5833
6305
  schedule = r.kind;
5834
6306
  scheduleNote = r.kind === "manual" ? r.detail : undefined;
5835
6307
  }
@@ -5845,19 +6317,19 @@ var on = (o2) => {
5845
6317
  next.untilReset = new Date(untilResetMs).toISOString();
5846
6318
  else
5847
6319
  next.untilReset = undefined;
5848
- next.reason = o2.reason?.trim() || undefined;
6320
+ next.reason = o.reason?.trim() || undefined;
5849
6321
  next.schedule = schedule;
5850
- writeConfig(o2.home, next);
6322
+ writeConfig(o.home, next);
5851
6323
  log(`Sleds on. Claude Code now runs on the platform key through ${url}. Restart claude to apply.`);
5852
6324
  if (untilResetMs !== undefined) {
5853
- log(`Reverts at ${formatLocal(untilResetMs, o2.tz)} — run \`sleds off\` earlier if you want.`);
6325
+ log(`Reverts at ${formatLocal(untilResetMs, o.tz)} — run \`sleds off\` earlier if you want.`);
5854
6326
  if (schedule === undefined || schedule === "manual") {
5855
6327
  const why = scheduleNote ? ` (${scheduleNote})` : "";
5856
6328
  log(`Auto-revert not scheduled${why}. Run \`sleds off\` after the reset.`);
5857
6329
  }
5858
6330
  }
5859
6331
  if (backedUp)
5860
- log(`Backed up ${settingsPath(o2.home)} to ${backupPath(o2.home)}.`);
6332
+ log(`Backed up ${settingsPath(o.home)} to ${backupPath(o.home)}.`);
5861
6333
  return 0;
5862
6334
  };
5863
6335
 
@@ -5905,22 +6377,22 @@ var exitsLine = (hasUpstreamKey) => hasUpstreamKey ? "`/sled handoff <what you w
5905
6377
  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.`;
5906
6378
  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.`;
5907
6379
  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.`;
5908
- var promptEvent = async (o2) => {
5909
- const log = o2.log ?? console.log;
5910
- const error = o2.error ?? console.error;
5911
- const now = (o2.now ?? Date.now)();
5912
- if (o2.stdin === undefined)
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)
5913
6385
  await readStdin(HOOK_STDIN_TIMEOUT_MS);
5914
6386
  let state;
5915
6387
  try {
5916
- state = readSeatState(o2.home);
6388
+ state = readSeatState(o.home);
5917
6389
  } catch {
5918
6390
  return 0;
5919
6391
  }
5920
6392
  const pending = untold(state);
5921
6393
  if (pending.length === 0)
5922
6394
  return 0;
5923
- const hasKey = readConfig(o2.home).org?.capabilities.upstreamKey !== false;
6395
+ const hasKey = readConfig(o.home).org?.capabilities.upstreamKey !== false;
5924
6396
  const told = { ...state.told ?? {} };
5925
6397
  const mark = (bar, stage, at) => {
5926
6398
  told[bar] = { ...told[bar] ?? {}, [stage]: at };
@@ -5940,7 +6412,7 @@ var promptEvent = async (o2) => {
5940
6412
  lines.push(e.stage === "approaching" ? approachingContext(e.bar, pct, e.resetsAt, hasKey, now) : hitContext(e.bar, e.resetsAt, hasKey, now));
5941
6413
  }
5942
6414
  try {
5943
- writeSeatState(o2.home, { ...state, told });
6415
+ writeSeatState(o.home, { ...state, told });
5944
6416
  } catch {}
5945
6417
  if (code === 0 && lines.length > 0)
5946
6418
  log(lines.join(`
@@ -5951,17 +6423,17 @@ var promptEvent = async (o2) => {
5951
6423
  // src/commands/run.ts
5952
6424
  init_config();
5953
6425
  init_meter();
5954
- import { hostname as hostname3 } from "node:os";
5955
- var run = async (o2) => {
5956
- const log = o2.log ?? console.log;
5957
- const error = o2.error ?? console.error;
5958
- if (!o2.jobId) {
6426
+ 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) {
5959
6431
  error("usage: sleds run <jobId>");
5960
6432
  return 2;
5961
6433
  }
5962
- const client = o2.client ?? (() => {
5963
- const src = meterSource(o2.home);
5964
- return src ? musherClient({ ...src, fetchFn: o2.fetchFn }) : null;
6434
+ const client = o.client ?? (() => {
6435
+ const src = meterSource(o.home);
6436
+ return src ? musherClient({ ...src, fetchFn: o.fetchFn }) : null;
5965
6437
  })();
5966
6438
  if (!client) {
5967
6439
  error("No seat on this machine. Run `sleds init` first.");
@@ -5972,13 +6444,13 @@ var run = async (o2) => {
5972
6444
  error("Could not reach the SLEDS proxy.");
5973
6445
  return 1;
5974
6446
  }
5975
- const job = [...list.due, ...list.upcoming].find((j) => j.jobId === o2.jobId);
6447
+ const job = [...list.due, ...list.upcoming].find((j) => j.jobId === o.jobId);
5976
6448
  if (!job) {
5977
- error(`Job ${o2.jobId} is not a scheduled local job on this seat (sleds musher --status lists them).`);
6449
+ error(`Job ${o.jobId} is not a scheduled local job on this seat (sleds musher --status lists them).`);
5978
6450
  return 1;
5979
6451
  }
5980
- const cfg = readConfig(o2.home);
5981
- const claude = findClaude(o2.home, cfg.musher?.claudePath, o2.which ?? defaultWhich);
6452
+ const cfg = readConfig(o.home);
6453
+ const claude = findClaude(o.home, cfg.musher?.claudePath, o.which ?? defaultWhich);
5982
6454
  if (!claude) {
5983
6455
  error("`claude` not found. Install Claude Code, or `sleds musher --claude /path/to/claude`.");
5984
6456
  return 1;
@@ -5986,12 +6458,12 @@ var run = async (o2) => {
5986
6458
  const out = await runJob({
5987
6459
  job,
5988
6460
  client,
5989
- fallbackSession: readSeatState(o2.home).session,
5990
- host: o2.host ?? hostname3(),
6461
+ fallbackSession: readSeatState(o.home).session,
6462
+ host: o.host ?? hostname4(),
5991
6463
  claudePath: claude,
5992
6464
  permissionMode: cfg.musher?.permissionMode ?? DEFAULT_PERMISSION_MODE,
5993
6465
  timeoutMs: (cfg.musher?.timeoutMinutes ?? DEFAULT_TIMEOUT_MINUTES) * 60000,
5994
- exec: o2.execCapture,
6466
+ exec: o.execCapture,
5995
6467
  log
5996
6468
  });
5997
6469
  if (out.kind === "skipped") {
@@ -6024,25 +6496,25 @@ var notificationType = (text) => {
6024
6496
  return;
6025
6497
  }
6026
6498
  };
6027
- var seatEvent = async (o2) => {
6028
- const log = o2.log ?? console.log;
6029
- const now = (o2.now ?? Date.now)();
6030
- const input = o2.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS2);
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);
6031
6503
  const kind = notificationType(input);
6032
6504
  try {
6033
- const state = readSeatState(o2.home);
6505
+ const state = readSeatState(o.home);
6034
6506
  const week = state.window?.sevenDay;
6035
- const src = meterSource(o2.home);
6507
+ const src = meterSource(o.home);
6036
6508
  if (src) {
6037
- await postSeatWall({ ...src, fetchFn: o2.fetchFn, timeoutMs: o2.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, {
6509
+ await postSeatWall({ ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, {
6038
6510
  ...week ? { resetsAt: week.resetsAt, usedPct: week.usedPct } : {},
6039
6511
  ...kind ? { notification: kind } : {}
6040
6512
  });
6041
6513
  }
6042
6514
  if (week && week.resetsAt > now)
6043
- writeSeatState(o2.home, { ...state, lastWallResetsAt: week.resetsAt });
6515
+ writeSeatState(o.home, { ...state, lastWallResetsAt: week.resetsAt });
6044
6516
  } catch {}
6045
- const line = seatWallLine(readConfig(o2.home).org?.capabilities.upstreamKey !== false);
6517
+ const line = seatWallLine(readConfig(o.home).org?.capabilities.upstreamKey !== false);
6046
6518
  log(line);
6047
6519
  return 0;
6048
6520
  };
@@ -6050,7 +6522,7 @@ var seatEvent = async (o2) => {
6050
6522
  // src/commands/seatSync.ts
6051
6523
  import { execFileSync as execFileSync2 } from "node:child_process";
6052
6524
  import { existsSync as existsSync12, mkdirSync as mkdirSync11, readFileSync as readFileSync10, readdirSync as readdirSync2, writeFileSync as writeFileSync11 } from "node:fs";
6053
- import { platform } from "node:os";
6525
+ import { platform as platform2 } from "node:os";
6054
6526
  import { join as join11 } from "node:path";
6055
6527
 
6056
6528
  // src/cookieDb.ts
@@ -6062,11 +6534,11 @@ var isBun = () => typeof globalThis.Bun !== "undefined";
6062
6534
  async function queryRows(dbPath) {
6063
6535
  if (isBun()) {
6064
6536
  const { Database } = await import("bun:sqlite");
6065
- const conn2 = new Database(dbPath, { readonly: true });
6537
+ const conn = new Database(dbPath, { readonly: true });
6066
6538
  try {
6067
- return conn2.query(QUERY).all();
6539
+ return conn.query(QUERY).all();
6068
6540
  } finally {
6069
- conn2.close();
6541
+ conn.close();
6070
6542
  }
6071
6543
  }
6072
6544
  const mod = nodeSqlite();
@@ -6177,9 +6649,9 @@ function resolveProfileDir(localStateJson, profile) {
6177
6649
  if (localStateJson) {
6178
6650
  try {
6179
6651
  const raw = JSON.parse(localStateJson);
6180
- const p2 = isObject11(raw) && isObject11(raw.profile) ? raw.profile : null;
6181
- if (p2 && isObject11(p2.info_cache))
6182
- cache = p2.info_cache;
6652
+ const p = isObject11(raw) && isObject11(raw.profile) ? raw.profile : null;
6653
+ if (p && isObject11(p.info_cache))
6654
+ cache = p.info_cache;
6183
6655
  } catch {}
6184
6656
  }
6185
6657
  if (Object.hasOwn(cache, profile))
@@ -6370,11 +6842,11 @@ function findFile(root, name) {
6370
6842
  continue;
6371
6843
  }
6372
6844
  for (const e of entries) {
6373
- const p2 = join11(dir, e.name);
6845
+ const p = join11(dir, e.name);
6374
6846
  if (e.isDirectory())
6375
- stack.push(p2);
6847
+ stack.push(p);
6376
6848
  else if (e.name === name)
6377
- return p2;
6849
+ return p;
6378
6850
  }
6379
6851
  }
6380
6852
  return null;
@@ -6385,10 +6857,10 @@ function findCookieDb(home, source) {
6385
6857
  if (!existsSync12(root))
6386
6858
  throw new Error(`no ${source.browser} data at ${root} (is it installed?)`);
6387
6859
  if (!b.profiled) {
6388
- const db2 = findFile(root, "Cookies");
6389
- if (!db2)
6860
+ const db = findFile(root, "Cookies");
6861
+ if (!db)
6390
6862
  throw new Error(`no cookie store under ${root}`);
6391
- return db2;
6863
+ return db;
6392
6864
  }
6393
6865
  const localStatePath = join11(root, "Local State");
6394
6866
  const localState = existsSync12(localStatePath) ? readFileSync10(localStatePath, "utf8") : null;
@@ -6398,7 +6870,7 @@ function findCookieDb(home, source) {
6398
6870
  throw new Error(`"${wanted}" names ${ambiguous.length} ${source.browser} profiles (${ambiguous.join(", ")}) — pass the directory or the account email instead`);
6399
6871
  if (!dir)
6400
6872
  throw new Error(`no ${source.browser} profile named "${wanted}" (check chrome://version)`);
6401
- const db = cookieDbCandidates(join11(root, dir)).find((p2) => existsSync12(p2));
6873
+ const db = cookieDbCandidates(join11(root, dir)).find((p) => existsSync12(p));
6402
6874
  if (!db)
6403
6875
  throw new Error(`profile "${dir}" has no cookie store yet (open claude.ai in it once)`);
6404
6876
  return db;
@@ -6429,8 +6901,8 @@ var curlUsage = (org, cookie) => {
6429
6901
  return { status: 0, body: "" };
6430
6902
  }
6431
6903
  };
6432
- function fetchSeatUsage(jar, org, now, run2) {
6433
- const { status, body } = run2(org, cookieHeader(jar, org));
6904
+ function fetchSeatUsage(jar, org, now, run) {
6905
+ const { status, body } = run(org, cookieHeader(jar, org));
6434
6906
  if (status !== 200)
6435
6907
  return { usage: null, status };
6436
6908
  try {
@@ -6491,24 +6963,24 @@ function uninstall(home, source, log) {
6491
6963
  log(existsSync12(path) ? `seat-sync timer removed (${path} can be deleted)` : "no seat-sync timer");
6492
6964
  return 0;
6493
6965
  }
6494
- var seatSync = async (o2) => {
6495
- const log = o2.log ?? console.log;
6496
- const now = (o2.now ?? Date.now)();
6497
- const parsed = parseSeatSyncArgs(o2.argv ?? process.argv.slice(3));
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));
6498
6970
  if ("error" in parsed) {
6499
6971
  log(`seat-sync: ${parsed.error}`);
6500
6972
  return 1;
6501
6973
  }
6502
6974
  const { source } = parsed;
6503
6975
  if (source.uninstall)
6504
- return uninstall(o2.home, source, log);
6976
+ return uninstall(o.home, source, log);
6505
6977
  if (source.install)
6506
- return install(o2.home, [process.execPath, process.argv[1] ?? ""], source, log);
6507
- if (platform() !== "darwin" && !o2.readCookieRows) {
6978
+ return install(o.home, [process.execPath, process.argv[1] ?? ""], source, log);
6979
+ if (platform2() !== "darwin" && !o.readCookieRows) {
6508
6980
  log("seat-sync is macOS-only for now.");
6509
6981
  return 2;
6510
6982
  }
6511
- const saved = meterSource(o2.home);
6983
+ const saved = meterSource(o.home);
6512
6984
  const proxyUrl = saved?.proxyUrl;
6513
6985
  const token = source.token ?? saved?.token;
6514
6986
  if (!proxyUrl || !token) {
@@ -6517,13 +6989,13 @@ var seatSync = async (o2) => {
6517
6989
  }
6518
6990
  let rows;
6519
6991
  try {
6520
- rows = o2.readCookieRows ? o2.readCookieRows(o2.home, source) : await readCookieRowsBun(o2.home, source);
6992
+ rows = o.readCookieRows ? o.readCookieRows(o.home, source) : await readCookieRowsBun(o.home, source);
6521
6993
  } catch (e) {
6522
6994
  log(`seat-sync: ${e instanceof Error ? e.message : String(e)}`);
6523
6995
  return 1;
6524
6996
  }
6525
6997
  const service = BROWSERS[source.browser].keychainService;
6526
- const pw = (o2.safeStorageKey ?? keychainKey)(service);
6998
+ const pw = (o.safeStorageKey ?? keychainKey)(service);
6527
6999
  if (!pw) {
6528
7000
  log(`seat-sync: could not read the "${service}" key from your Keychain.`);
6529
7001
  return 1;
@@ -6543,16 +7015,16 @@ var seatSync = async (o2) => {
6543
7015
  log("seat-sync: no active org in the session cookie (pass --org <uuid>).");
6544
7016
  return 1;
6545
7017
  }
6546
- const { usage, status } = fetchSeatUsage(jar, org, now, o2.runUsage ?? curlUsage);
7018
+ const { usage, status } = fetchSeatUsage(jar, org, now, o.runUsage ?? curlUsage);
6547
7019
  if (!usage) {
6548
7020
  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}.`;
6549
7021
  log(`seat-sync: usage read failed — ${why}`);
6550
7022
  return 2;
6551
7023
  }
6552
7024
  try {
6553
- writeSeatState(o2.home, { ...readSeatState(o2.home), window: usageAsWindow(usage) });
7025
+ writeSeatState(o.home, { ...readSeatState(o.home), window: usageAsWindow(usage) });
6554
7026
  } catch {}
6555
- const posted = await postSeat(proxyUrl, token, seatSyncBody(usage), o2.fetchFn ?? fetch);
7027
+ const posted = await postSeat(proxyUrl, token, seatSyncBody(usage), o.fetchFn ?? fetch);
6556
7028
  const wk = usage.sevenDay ? `wk ${Math.round(usage.sevenDay.usedPct)}%` : "wk —";
6557
7029
  const models = Object.keys(usage.models).length;
6558
7030
  const credits = usage.usedCreditsUsd !== undefined ? ` · $${usage.usedCreditsUsd.toFixed(2)}` : "";
@@ -6604,14 +7076,14 @@ function sessionContext(a) {
6604
7076
  return lines.join(`
6605
7077
  `);
6606
7078
  }
6607
- var sessionEvent = async (o2) => {
6608
- const log = o2.log ?? console.log;
6609
- const now = (o2.now ?? Date.now)();
6610
- const raw = o2.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS);
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);
6611
7083
  const payload = parseSessionPayload(raw);
6612
7084
  if (payload.session_id) {
6613
7085
  try {
6614
- writeSession(o2.home, {
7086
+ writeSession(o.home, {
6615
7087
  sessionId: payload.session_id,
6616
7088
  startedAt: now,
6617
7089
  ...payload.source ? { source: payload.source } : {}
@@ -6621,13 +7093,13 @@ var sessionEvent = async (o2) => {
6621
7093
  let email;
6622
7094
  let org;
6623
7095
  try {
6624
- const cfg = readConfig(o2.home);
7096
+ const cfg = readConfig(o.home);
6625
7097
  email = cfg.email;
6626
7098
  org = cfg.org?.slug;
6627
7099
  } catch {}
6628
7100
  let owed = [];
6629
7101
  try {
6630
- owed = readOwed(o2.home, now);
7102
+ owed = readOwed(o.home, now);
6631
7103
  } catch {
6632
7104
  owed = [];
6633
7105
  }
@@ -6672,13 +7144,13 @@ function parseHookInput(raw) {
6672
7144
  }
6673
7145
  if (typeof parsed !== "object" || parsed === null)
6674
7146
  return null;
6675
- const p2 = parsed;
6676
- const toolName = typeof p2.tool_name === "string" ? p2.tool_name : "";
6677
- const input = typeof p2.tool_input === "object" && p2.tool_input !== null ? p2.tool_input : {};
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 : {};
6678
7150
  const filePath = typeof input.file_path === "string" ? input.file_path : "";
6679
7151
  if (!filePath)
6680
7152
  return null;
6681
- const cwd = typeof p2.cwd === "string" ? p2.cwd : "";
7153
+ const cwd = typeof p.cwd === "string" ? p.cwd : "";
6682
7154
  return { toolName, filePath, cwd };
6683
7155
  }
6684
7156
  function projectKey(filePath, cwd) {
@@ -6698,16 +7170,16 @@ function readFileOrNull(path) {
6698
7170
  return null;
6699
7171
  }
6700
7172
  }
6701
- var specEvent = async (o2) => {
6702
- const log = o2.log ?? console.log;
6703
- const raw = o2.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS3);
7173
+ var specEvent = async (o) => {
7174
+ const log = o.log ?? console.log;
7175
+ const raw = o.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS3);
6704
7176
  const input = raw ? parseHookInput(raw) : null;
6705
7177
  if (!input || !isSpecPath(input.filePath))
6706
7178
  return 0;
6707
- const src = meterSource(o2.home);
7179
+ const src = meterSource(o.home);
6708
7180
  if (!src)
6709
7181
  return 0;
6710
- const body = (o2.readFile ?? readFileOrNull)(input.filePath);
7182
+ const body = (o.readFile ?? readFileOrNull)(input.filePath);
6711
7183
  if (!body || !looksLikeSpec(body))
6712
7184
  return 0;
6713
7185
  const project = projectKey(input.filePath, input.cwd);
@@ -6717,7 +7189,7 @@ var specEvent = async (o2) => {
6717
7189
  repoHint: input.cwd ? basename2(input.cwd) : undefined
6718
7190
  };
6719
7191
  try {
6720
- const res = await (o2.fetchFn ?? fetch)(`${src.proxyUrl}/proxy/estimate`, {
7192
+ const res = await (o.fetchFn ?? fetch)(`${src.proxyUrl}/proxy/estimate`, {
6721
7193
  method: "POST",
6722
7194
  headers: { authorization: `Bearer ${src.token}`, "content-type": "application/json" },
6723
7195
  body: JSON.stringify({ spec, project }),
@@ -6732,15 +7204,15 @@ var specEvent = async (o2) => {
6732
7204
  log(`sleds: ${project} sized at $${b.lowUsd.toFixed(2)}–${b.highUsd.toFixed(2)} · ${min(b.lowMinutes)}–${min(b.highMinutes)} min`);
6733
7205
  if (typeof b.lowMinutes === "number" && typeof b.highMinutes === "number") {
6734
7206
  try {
6735
- writeSeatState(o2.home, {
6736
- ...readSeatState(o2.home),
7207
+ writeSeatState(o.home, {
7208
+ ...readSeatState(o.home),
6737
7209
  estimate: {
6738
7210
  project,
6739
7211
  lowUsd: b.lowUsd,
6740
7212
  highUsd: b.highUsd,
6741
7213
  lowMin: b.lowMinutes,
6742
7214
  highMin: b.highMinutes,
6743
- at: (o2.now ?? Date.now)()
7215
+ at: (o.now ?? Date.now)()
6744
7216
  }
6745
7217
  });
6746
7218
  } catch {}
@@ -6823,16 +7295,16 @@ var formatStatus = (s) => {
6823
7295
  }
6824
7296
  return lines;
6825
7297
  };
6826
- var status = async (o2) => {
6827
- const log = o2.log ?? console.log;
6828
- const nowMs = (o2.now ?? Date.now)();
6829
- const cfg = readConfig(o2.home);
6830
- const settings = existsSync13(settingsPath(o2.home)) ? readSettings(o2.home) : {};
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) : {};
6831
7303
  const inSettings = readProxy(settings);
6832
7304
  const proxyUrl = inSettings?.url ?? cfg.proxyUrl;
6833
7305
  const token = inSettings?.token ?? cfg.token;
6834
7306
  if (!proxyUrl && !token && cfg.mode === undefined) {
6835
- log(`SLEDS is not set up (${configPath(o2.home)} and ${settingsPath(o2.home)}).`);
7307
+ log(`SLEDS is not set up (${configPath(o.home)} and ${settingsPath(o.home)}).`);
6836
7308
  log("Run `sleds init` first.");
6837
7309
  return 1;
6838
7310
  }
@@ -6840,8 +7312,8 @@ var status = async (o2) => {
6840
7312
  mode: cfg.mode ?? "off",
6841
7313
  pointed: inSettings !== null,
6842
7314
  nowMs,
6843
- settingsFile: settingsPath(o2.home),
6844
- configFile: configPath(o2.home)
7315
+ settingsFile: settingsPath(o.home),
7316
+ configFile: configPath(o.home)
6845
7317
  };
6846
7318
  if (proxyUrl)
6847
7319
  info.proxyUrl = proxyUrl;
@@ -6861,8 +7333,8 @@ var status = async (o2) => {
6861
7333
  };
6862
7334
  if (cfg.reason)
6863
7335
  info.reason = cfg.reason;
6864
- if (o2.tz)
6865
- info.tz = o2.tz;
7336
+ if (o.tz)
7337
+ info.tz = o.tz;
6866
7338
  const since = cfg.since ? Date.parse(cfg.since) : Number.NaN;
6867
7339
  if (!Number.isNaN(since))
6868
7340
  info.sinceMs = since;
@@ -6871,8 +7343,8 @@ var status = async (o2) => {
6871
7343
  info.untilResetMs = until;
6872
7344
  if (proxyUrl) {
6873
7345
  const [health, proxy] = await Promise.all([
6874
- checkHealth2(proxyUrl, o2.fetchFn, o2.timeoutMs),
6875
- fetchProxyStatus(proxyUrl, o2.fetchFn, o2.timeoutMs)
7346
+ checkHealth2(proxyUrl, o.fetchFn, o.timeoutMs),
7347
+ fetchProxyStatus(proxyUrl, o.fetchFn, o.timeoutMs)
6876
7348
  ]);
6877
7349
  info.health = health;
6878
7350
  info.proxy = proxy;
@@ -6885,62 +7357,62 @@ var status = async (o2) => {
6885
7357
  // src/commands/statusline.ts
6886
7358
  init_config();
6887
7359
  init_meter();
6888
- var statusline = async (o2) => {
6889
- const log = o2.log ?? console.log;
6890
- const now = (o2.now ?? Date.now)();
6891
- const input = o2.stdin ?? await readStdin(STDIN_TIMEOUT_MS);
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);
6892
7364
  const window = seatWindowFromStdin(input, now);
6893
7365
  const session = seatSessionFromStdin(input, now);
6894
7366
  let posts = [];
6895
7367
  let events = [];
6896
7368
  try {
6897
7369
  if (window)
6898
- ({ posts, events } = rememberSeat(o2, window, session, now, input));
7370
+ ({ posts, events } = rememberSeat(o, window, session, now, input));
6899
7371
  else {
6900
- const st = readSeatState(o2.home);
6901
- const credits = creditsEvent(o2, st, input, now);
7372
+ const st = readSeatState(o.home);
7373
+ const credits = creditsEvent(o, st, input, now);
6902
7374
  if (session || credits.scan) {
6903
7375
  const next = {
6904
7376
  ...st,
6905
7377
  ...session ? { session } : {},
6906
7378
  ...credits.scan ? { transcriptScan: credits.scan } : {}
6907
7379
  };
6908
- writeSeatState(o2.home, credits.event ? markWarned(next, [credits.event]) : next);
7380
+ writeSeatState(o.home, credits.event ? markWarned(next, [credits.event]) : next);
6909
7381
  }
6910
7382
  if (credits.event) {
6911
7383
  events = [credits.event];
6912
- posts = announce(o2, events);
7384
+ posts = announce(o, events);
6913
7385
  }
6914
7386
  }
6915
7387
  } catch {}
6916
7388
  const seatFragment = formatSeatFragment(window, now);
6917
7389
  let line;
6918
7390
  try {
6919
- const meter2 = await loadMeter({
6920
- home: o2.home,
7391
+ const meter = await loadMeter({
7392
+ home: o.home,
6921
7393
  now,
6922
- fetchFn: o2.fetchFn,
6923
- timeoutMs: o2.timeoutMs
7394
+ fetchFn: o.fetchFn,
7395
+ timeoutMs: o.timeoutMs
6924
7396
  });
6925
- line = formatMeterLine(meter2, now, seatFragment);
7397
+ line = formatMeterLine(meter, now, seatFragment);
6926
7398
  } catch {
6927
7399
  line = formatMeterLine(null, now, seatFragment);
6928
7400
  }
6929
7401
  log(line);
6930
- posts.push(speakToCockpit(o2, window, events, now));
7402
+ posts.push(speakToCockpit(o, window, events, now));
6931
7403
  await Promise.allSettled(posts);
6932
7404
  return 0;
6933
7405
  };
6934
- var speakToCockpit = async (o2, window, events, now) => {
6935
- const cockpit = detectCockpit(o2.env ?? process.env);
7406
+ var speakToCockpit = async (o, window, events, now) => {
7407
+ const cockpit = detectCockpit(o.env ?? process.env);
6936
7408
  if (!cockpit)
6937
7409
  return;
6938
7410
  try {
6939
- const st = readSeatState(o2.home);
7411
+ const st = readSeatState(o.home);
6940
7412
  let feed;
6941
- const src = meterSource(o2.home);
7413
+ const src = meterSource(o.home);
6942
7414
  if (src && now - (st.cockpit?.feed?.at ?? 0) >= FEED_MIN_INTERVAL_MS) {
6943
- const got = await fetchFeed({ ...src, email: readConfig(o2.home).email, fetchFn: o2.fetchFn, timeoutMs: o2.timeoutMs }, now);
7415
+ const got = await fetchFeed({ ...src, email: readConfig(o.home).email, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs }, now);
6944
7416
  if (got)
6945
7417
  feed = got;
6946
7418
  }
@@ -6951,32 +7423,32 @@ var speakToCockpit = async (o2, window, events, now) => {
6951
7423
  const changed = r.argv.length > 0 || feed !== undefined;
6952
7424
  if (!changed)
6953
7425
  return;
6954
- writeSeatState(o2.home, { ...readSeatState(o2.home), cockpit: r.state });
7426
+ writeSeatState(o.home, { ...readSeatState(o.home), cockpit: r.state });
6955
7427
  if (r.argv.length)
6956
- await runCockpit(cockpit, r.argv, { spawn: o2.spawn });
7428
+ await runCockpit(cockpit, r.argv, { spawn: o.spawn });
6957
7429
  } catch {}
6958
7430
  };
6959
- var rememberSeat = (o2, window, session, now, input) => {
6960
- const prior = readSeatState(o2.home);
7431
+ var rememberSeat = (o, window, session, now, input) => {
7432
+ const prior = readSeatState(o.home);
6961
7433
  let state = { ...prior, window, ...session ? { session } : {} };
6962
- const src = meterSource(o2.home);
7434
+ const src = meterSource(o.home);
6963
7435
  const posts = [];
6964
7436
  if (src && shouldPostSeat(prior, window, now)) {
6965
7437
  state = markPosted(state, window, now);
6966
- posts.push(postSeatWindow({ ...src, fetchFn: o2.fetchFn, timeoutMs: o2.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, window));
7438
+ posts.push(postSeatWindow({ ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, window));
6967
7439
  }
6968
7440
  const events = seatWallEvents(prior, window);
6969
- const credits = creditsEvent(o2, prior, input, now);
7441
+ const credits = creditsEvent(o, prior, input, now);
6970
7442
  if (credits.scan)
6971
7443
  state = { ...state, transcriptScan: credits.scan };
6972
7444
  if (credits.event)
6973
7445
  events.push(credits.event);
6974
7446
  state = markWarned(state, events);
6975
- posts.push(...announce(o2, events));
6976
- writeSeatState(o2.home, state);
7447
+ posts.push(...announce(o, events));
7448
+ writeSeatState(o.home, state);
6977
7449
  return { posts, events };
6978
7450
  };
6979
- var creditsEvent = (o2, prior, input, now) => {
7451
+ var creditsEvent = (o, prior, input, now) => {
6980
7452
  const path = transcriptPathFromStdin(input);
6981
7453
  if (!path)
6982
7454
  return { event: null, scan: undefined };
@@ -6986,13 +7458,13 @@ var creditsEvent = (o2, prior, input, now) => {
6986
7458
  return { event: null, scan: next };
6987
7459
  return { event: { bar: "credits", stage: "hit", usedPct: 100, resetsAt: now }, scan: next };
6988
7460
  };
6989
- var announce = (o2, events) => {
7461
+ var announce = (o, events) => {
6990
7462
  if (events.length === 0)
6991
7463
  return [];
6992
- const src = meterSource(o2.home);
7464
+ const src = meterSource(o.home);
6993
7465
  if (!src)
6994
7466
  return [];
6995
- return events.map((e) => postSeatWall({ ...src, fetchFn: o2.fetchFn, timeoutMs: o2.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, {
7467
+ return events.map((e) => postSeatWall({ ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, {
6996
7468
  bar: e.bar,
6997
7469
  stage: e.stage,
6998
7470
  usedPct: e.usedPct,
@@ -7026,14 +7498,14 @@ function decideStop(a) {
7026
7498
  rows: markAsked(a.rows, due.map((r) => r.key), a.now)
7027
7499
  };
7028
7500
  }
7029
- var stopEvent = async (o2) => {
7030
- const log = o2.log ?? console.log;
7031
- const now = (o2.now ?? Date.now)();
7032
- const raw = o2.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS);
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);
7033
7505
  const payload = parseStopPayload(raw);
7034
7506
  let rows;
7035
7507
  try {
7036
- rows = readOwed(o2.home, now);
7508
+ rows = readOwed(o.home, now);
7037
7509
  } catch {
7038
7510
  return 0;
7039
7511
  }
@@ -7046,14 +7518,14 @@ var stopEvent = async (o2) => {
7046
7518
  if (!d.block)
7047
7519
  return 0;
7048
7520
  try {
7049
- writeOwed(o2.home, d.rows);
7521
+ writeOwed(o.home, d.rows);
7050
7522
  } catch {}
7051
7523
  log(JSON.stringify({ decision: "block", reason: d.reason }));
7052
7524
  return 0;
7053
7525
  };
7054
7526
 
7055
7527
  // src/commands/uninstall.ts
7056
- import { spawnSync as spawnSync4 } from "node:child_process";
7528
+ import { spawnSync as spawnSync5 } from "node:child_process";
7057
7529
  import { existsSync as existsSync15, rmSync as rmSync5 } from "node:fs";
7058
7530
 
7059
7531
  // src/codex.ts
@@ -7063,7 +7535,7 @@ import { dirname as dirname6, join as join12 } from "node:path";
7063
7535
  var PROVIDER_ID2 = "sleds";
7064
7536
  var codexDir2 = (home) => join12(home, ".codex");
7065
7537
  var codexConfigPath2 = (home) => join12(codexDir2(home), "config.toml");
7066
- function removeCodexProvider2(toml, o2 = {}) {
7538
+ function removeCodexProvider2(toml, o = {}) {
7067
7539
  const lines = toml.split(`
7068
7540
  `);
7069
7541
  const out = [];
@@ -7076,7 +7548,7 @@ function removeCodexProvider2(toml, o2 = {}) {
7076
7548
  }
7077
7549
  let text = out.join(`
7078
7550
  `);
7079
- if (!o2.keepPrior) {
7551
+ if (!o.keepPrior) {
7080
7552
  const prior = text.match(/^# sleds-prior-model-provider = "([^"]*)"\s*\n/m);
7081
7553
  if (prior) {
7082
7554
  text = text.replace(prior[0], "");
@@ -7106,74 +7578,74 @@ init_config();
7106
7578
  init_meter();
7107
7579
  init_settings();
7108
7580
  var defaultUnload = (plist) => {
7109
- spawnSync4("launchctl", ["unload", plist], { stdio: "ignore" });
7581
+ spawnSync5("launchctl", ["unload", plist], { stdio: "ignore" });
7110
7582
  };
7111
- var uninstall2 = (o2) => {
7112
- const codexLine = uninstallCodex(o2.home);
7583
+ var uninstall2 = (o) => {
7584
+ const codexLine = uninstallCodex(o.home);
7113
7585
  if (codexLine)
7114
- (o2.log ?? console.log)(` ${codexLine}`);
7115
- const log = o2.log ?? console.log;
7586
+ (o.log ?? console.log)(` ${codexLine}`);
7587
+ const log = o.log ?? console.log;
7116
7588
  const removed = [];
7117
- if (existsSync15(settingsPath(o2.home))) {
7118
- let settings = readSettings(o2.home);
7589
+ if (existsSync15(settingsPath(o.home))) {
7590
+ let settings = readSettings(o.home);
7119
7591
  let changed = false;
7120
7592
  if (hasEitherKey(settings)) {
7121
7593
  settings = removeProxy(settings);
7122
7594
  changed = true;
7123
- removed.push(`env.ANTHROPIC_BASE_URL and env.ANTHROPIC_AUTH_TOKEN from ${settingsPath(o2.home)}`);
7595
+ removed.push(`env.ANTHROPIC_BASE_URL and env.ANTHROPIC_AUTH_TOKEN from ${settingsPath(o.home)}`);
7124
7596
  }
7125
7597
  if (isSledsStatusLine(settings[STATUS_LINE_KEY])) {
7126
- const cfg = readConfig(o2.home);
7598
+ const cfg = readConfig(o.home);
7127
7599
  settings = removeStatusLine(settings, cfg.priorStatusLine);
7128
7600
  changed = true;
7129
- removed.push(cfg.priorStatusLine === undefined ? `statusLine from ${settingsPath(o2.home)}` : `statusLine from ${settingsPath(o2.home)} (previous one restored)`);
7601
+ removed.push(cfg.priorStatusLine === undefined ? `statusLine from ${settingsPath(o.home)}` : `statusLine from ${settingsPath(o.home)} (previous one restored)`);
7130
7602
  if (cfg.priorStatusLine !== undefined) {
7131
7603
  const { priorStatusLine: _restored, ...rest } = cfg;
7132
- writeConfig(o2.home, rest);
7604
+ writeConfig(o.home, rest);
7133
7605
  }
7134
7606
  }
7135
7607
  if (hasSeatHook(settings)) {
7136
7608
  settings = removeSeatHook(settings);
7137
7609
  changed = true;
7138
- removed.push(`hooks.Notification seat-event entry from ${settingsPath(o2.home)}`);
7610
+ removed.push(`hooks.Notification seat-event entry from ${settingsPath(o.home)}`);
7139
7611
  }
7140
7612
  if (hasPromptHook(settings)) {
7141
7613
  settings = removePromptHook(settings);
7142
7614
  changed = true;
7143
- removed.push(`hooks.UserPromptSubmit prompt-event entry from ${settingsPath(o2.home)}`);
7615
+ removed.push(`hooks.UserPromptSubmit prompt-event entry from ${settingsPath(o.home)}`);
7144
7616
  }
7145
7617
  if (hasSpecHook(settings)) {
7146
7618
  settings = removeSpecHook(settings);
7147
7619
  changed = true;
7148
- removed.push(`hooks.PostToolUse spec-event entry from ${settingsPath(o2.home)}`);
7620
+ removed.push(`hooks.PostToolUse spec-event entry from ${settingsPath(o.home)}`);
7149
7621
  }
7150
7622
  if (hasSessionHook(settings)) {
7151
7623
  settings = removeSessionHook(settings);
7152
7624
  changed = true;
7153
- removed.push(`hooks.SessionStart session-event entry from ${settingsPath(o2.home)}`);
7625
+ removed.push(`hooks.SessionStart session-event entry from ${settingsPath(o.home)}`);
7154
7626
  }
7155
7627
  if (hasStopHook(settings)) {
7156
7628
  settings = removeStopHook(settings);
7157
7629
  changed = true;
7158
- removed.push(`hooks.Stop stop-event entry from ${settingsPath(o2.home)}`);
7630
+ removed.push(`hooks.Stop stop-event entry from ${settingsPath(o.home)}`);
7159
7631
  }
7160
7632
  if (changed)
7161
- writeSettings(o2.home, settings);
7633
+ writeSettings(o.home, settings);
7162
7634
  }
7163
- for (const file of [meterCachePath(o2.home), seatStatePath(o2.home)]) {
7635
+ for (const file of [meterCachePath(o.home), seatStatePath(o.home)]) {
7164
7636
  if (existsSync15(file)) {
7165
7637
  rmSync5(file, { force: true });
7166
7638
  removed.push(file);
7167
7639
  }
7168
7640
  }
7169
- const skill = skillDir(o2.home);
7641
+ const skill = skillDir(o.home);
7170
7642
  if (existsSync15(skill)) {
7171
7643
  rmSync5(skill, { recursive: true, force: true });
7172
7644
  removed.push(skill);
7173
7645
  }
7174
- const plist = musherPlistPath(o2.home);
7646
+ const plist = musherPlistPath(o.home);
7175
7647
  if (existsSync15(plist)) {
7176
- (o2.unloadAgent ?? defaultUnload)(plist);
7648
+ (o.unloadAgent ?? defaultUnload)(plist);
7177
7649
  rmSync5(plist, { force: true });
7178
7650
  removed.push(`launchd agent ${musherLabel()}`);
7179
7651
  }
@@ -7184,7 +7656,7 @@ var uninstall2 = (o2) => {
7184
7656
  for (const r of removed)
7185
7657
  log(` ${r}`);
7186
7658
  }
7187
- log(`Kept ${tokenPath(o2.home)} (delete it yourself if you want the token gone).`);
7659
+ log(`Kept ${tokenPath(o.home)} (delete it yourself if you want the token gone).`);
7188
7660
  return 0;
7189
7661
  };
7190
7662
  var hasEitherKey = (settings) => {
@@ -7203,17 +7675,17 @@ var jobIdFromTarget = (target) => {
7203
7675
  return m[1];
7204
7676
  return /^[A-Za-z0-9_-]{3,64}$/.test(t) ? t : null;
7205
7677
  };
7206
- var verify = async (o2) => {
7207
- const log = o2.log ?? console.log;
7208
- const error = o2.error ?? console.error;
7209
- const jobId = o2.target ? jobIdFromTarget(o2.target) : null;
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;
7210
7682
  if (!jobId) {
7211
7683
  error("usage: sleds verify <jobId> (or paste the SLEDS-Attest line)");
7212
7684
  return 2;
7213
7685
  }
7214
- const client = o2.client ?? (() => {
7215
- const src = meterSource(o2.home);
7216
- return src ? musherClient({ ...src, fetchFn: o2.fetchFn }) : null;
7686
+ const client = o.client ?? (() => {
7687
+ const src = meterSource(o.home);
7688
+ return src ? musherClient({ ...src, fetchFn: o.fetchFn }) : null;
7217
7689
  })();
7218
7690
  if (!client) {
7219
7691
  error("No seat on this machine. Run `sleds init` first.");
@@ -7247,8 +7719,8 @@ init_settings();
7247
7719
  import { chmodSync as chmodSync2, existsSync as existsSync16, mkdirSync as mkdirSync13, readFileSync as readFileSync13, renameSync as renameSync6, writeFileSync as writeFileSync13 } from "node:fs";
7248
7720
  import { join as join13, resolve as resolve4 } from "node:path";
7249
7721
  var configDir2 = (home, env = process.env) => {
7250
- const o2 = env.SLEDS_HOME?.trim();
7251
- return o2 ? resolve4(home, o2) : join13(home, ".config", "sleds");
7722
+ const o = env.SLEDS_HOME?.trim();
7723
+ return o ? resolve4(home, o) : join13(home, ".config", "sleds");
7252
7724
  };
7253
7725
  var configPath2 = (home) => join13(configDir2(home), "config.json");
7254
7726
  var isObject12 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
@@ -7307,9 +7779,9 @@ var parseConfig2 = (raw) => {
7307
7779
  const org = parseOrgInfo2(raw.org);
7308
7780
  if (org)
7309
7781
  cfg.org = org;
7310
- const musher2 = parseMusher2(raw.musher);
7311
- if (musher2)
7312
- cfg.musher = musher2;
7782
+ const musher = parseMusher2(raw.musher);
7783
+ if (musher)
7784
+ cfg.musher = musher;
7313
7785
  return cfg;
7314
7786
  };
7315
7787
  var parseMusher2 = (raw) => {
@@ -7332,16 +7804,16 @@ var parseOrgInfo2 = (raw) => {
7332
7804
  if (!isObject12(raw) || !isObject12(raw.capabilities))
7333
7805
  return;
7334
7806
  const name = optString2(raw.name);
7335
- const slug2 = optString2(raw.slug);
7336
- const plan2 = raw.plan;
7807
+ const slug = optString2(raw.slug);
7808
+ const plan = raw.plan;
7337
7809
  const c = raw.capabilities;
7338
7810
  const src = c.upstreamSource;
7339
- if (name === undefined || slug2 === undefined || plan2 !== "team" && plan2 !== "enterprise" && plan2 !== "api" && plan2 !== "individual" || src !== "own" && src !== "shared" && src !== "none")
7811
+ if (name === undefined || slug === undefined || plan !== "team" && plan !== "enterprise" && plan !== "api" && plan !== "individual" || src !== "own" && src !== "shared" && src !== "none")
7340
7812
  return;
7341
7813
  return {
7342
7814
  name,
7343
- slug: slug2,
7344
- plan: plan2,
7815
+ slug,
7816
+ plan,
7345
7817
  capabilities: {
7346
7818
  upstreamKey: c.upstreamKey === true,
7347
7819
  adminKey: c.adminKey === true,
@@ -7352,16 +7824,16 @@ var parseOrgInfo2 = (raw) => {
7352
7824
  };
7353
7825
  };
7354
7826
  var readConfig2 = (home) => {
7355
- const p2 = configPath2(home);
7356
- if (!existsSync16(p2))
7827
+ const p = configPath2(home);
7828
+ if (!existsSync16(p))
7357
7829
  return {};
7358
- return parseConfig2(JSON.parse(readFileSync13(p2, "utf8")));
7830
+ return parseConfig2(JSON.parse(readFileSync13(p, "utf8")));
7359
7831
  };
7360
7832
 
7361
7833
  // src/schedule.ts
7362
- import { spawnSync as spawnSync5 } from "node:child_process";
7834
+ import { spawnSync as spawnSync6 } from "node:child_process";
7363
7835
  var defaultExec = (cmd, args) => {
7364
- const r = spawnSync5(cmd, [...args], { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" });
7836
+ const r = spawnSync6(cmd, [...args], { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" });
7365
7837
  if (r.error)
7366
7838
  return { ok: false, stderr: r.error.message };
7367
7839
  return { ok: r.status === 0, stderr: r.stderr ?? "" };
@@ -7395,13 +7867,15 @@ var isSledsStopHook2 = isSledsHookFor2("stop-event");
7395
7867
  var USAGE = `usage: sleds <command> [options]
7396
7868
 
7397
7869
  commands:
7398
- init Point Claude Code at a SLEDS proxy, install the /sled skill and the status line.
7399
- --token <sled_\u2026> seat token (else SLEDS_TOKEN, ~/.sleds-token, or prompt)
7870
+ init Sign in through the browser, make this machine a seat, install the /sled skill
7871
+ and the status line. No flags needed: it opens app.sleds.ai/connect and waits.
7872
+ --token <sled_\u2026> skip the browser: a seat token (else SLEDS_TOKEN, ~/.sleds-token)
7400
7873
  --url <proxy-url> proxy base URL (else SLEDS_PROXY_URL, default ${DEFAULT_PROXY_URL})
7874
+ --no-browser print the sign-in URL and code instead of opening a browser
7401
7875
  --no-probe skip the /healthz check of the proxy URL
7402
7876
  --seat-first keep Claude Code on your seat; the proxy only at a wall (default for seat plans)
7403
7877
  --proxy-always every request through the proxy on the platform key (default for API-plan orgs)
7404
- --non-interactive never prompt; exit 2 if no token is available
7878
+ --non-interactive never open a browser or prompt; exit 2 if no token is available
7405
7879
  --agent codex Codex CLI instead of Claude Code: writes ~/.codex/config.toml
7406
7880
  (provider "sleds" \u2192 the proxy, key from $SLEDS_TOKEN); no hooks/status line
7407
7881
  --signup no org? create one: (1) just you \u2014 your email + your own Anthropic
@@ -7490,8 +7964,8 @@ var stringFlag = (flags, name) => {
7490
7964
  var hookStdin = async () => {
7491
7965
  if (process.stdin.isTTY)
7492
7966
  return;
7493
- const { readStdin: readStdin3 } = await Promise.resolve().then(() => (init_seat(), exports_seat));
7494
- const text = await readStdin3(300);
7967
+ await Promise.resolve().then(() => init_seat());
7968
+ const text = await readStdin2(300);
7495
7969
  return text.trim() ? text : undefined;
7496
7970
  };
7497
7971
  var sledsArgv = () => {
@@ -7509,7 +7983,7 @@ var main = async (argv) => {
7509
7983
  const { cmd, flags } = parseArgs(argv);
7510
7984
  const home = homedir();
7511
7985
  const scheduler = {
7512
- platform: platform2(),
7986
+ platform: platform3(),
7513
7987
  home,
7514
7988
  sledsArgv: sledsArgv(),
7515
7989
  exec: defaultExec
@@ -7557,10 +8031,11 @@ var main = async (argv) => {
7557
8031
  team: typeof flags.team === "string" ? flags.team : undefined,
7558
8032
  email: typeof flags.email === "string" ? flags.email : undefined,
7559
8033
  noProbe: flags["no-probe"] === true,
8034
+ noBrowser: flags["no-browser"] === true,
7560
8035
  proxyAlways: flags["proxy-always"] === true,
7561
8036
  seatFirst: flags["seat-first"] === true,
7562
8037
  installMusher: () => {
7563
- if (platform2() !== "darwin")
8038
+ if (platform3() !== "darwin")
7564
8039
  return null;
7565
8040
  const lines = [];
7566
8041
  const code = musherInstall({
@@ -7643,7 +8118,7 @@ ${renderHelpText(argv[1])}`);
7643
8118
  case "version":
7644
8119
  case "--version":
7645
8120
  case "-v":
7646
- console.log("sleds 0.1.23");
8121
+ console.log("sleds 0.1.24");
7647
8122
  return 0;
7648
8123
  default:
7649
8124
  console.error(`unknown command: ${cmd}