pi-crew 0.9.28 → 0.9.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1810,7 +1810,7 @@ var init_iterator2 = __esm({
1810
1810
  function RequiredArray(properties) {
1811
1811
  return globalThis.Object.keys(properties).filter((key) => !IsOptional(properties[key]));
1812
1812
  }
1813
- function _Object(properties, options) {
1813
+ function _Object_(properties, options) {
1814
1814
  const required = RequiredArray(properties);
1815
1815
  const schema = required.length > 0 ? { [Kind]: "Object", type: "object", required, properties } : { [Kind]: "Object", type: "object", properties };
1816
1816
  return CreateType(schema, options);
@@ -1821,7 +1821,7 @@ var init_object = __esm({
1821
1821
  init_type2();
1822
1822
  init_symbols2();
1823
1823
  init_kind();
1824
- Object2 = _Object;
1824
+ Object2 = _Object_;
1825
1825
  }
1826
1826
  });
1827
1827
 
@@ -4435,6 +4435,12 @@ var init_esm = __esm({
4435
4435
  }
4436
4436
  });
4437
4437
 
4438
+ // node_modules/@sinclair/typebox/build/esm/system/evaluate.mjs
4439
+ var init_evaluate = __esm({
4440
+ "node_modules/@sinclair/typebox/build/esm/system/evaluate.mjs"() {
4441
+ }
4442
+ });
4443
+
4438
4444
  // node_modules/@sinclair/typebox/build/esm/system/system.mjs
4439
4445
  var TypeSystemDuplicateTypeKind, TypeSystemDuplicateFormat, TypeSystem;
4440
4446
  var init_system = __esm({
@@ -4475,6 +4481,7 @@ var init_system = __esm({
4475
4481
  // node_modules/@sinclair/typebox/build/esm/system/index.mjs
4476
4482
  var init_system2 = __esm({
4477
4483
  "node_modules/@sinclair/typebox/build/esm/system/index.mjs"() {
4484
+ init_evaluate();
4478
4485
  init_policy();
4479
4486
  init_system();
4480
4487
  }
@@ -8522,8 +8529,8 @@ function sleepSync(ms) {
8522
8529
  }
8523
8530
  function sleep(ms, signal) {
8524
8531
  if (signal?.aborted) return Promise.reject(new Error("aborted"));
8525
- return new Promise((resolve21, reject) => {
8526
- const timer = setTimeout(resolve21, ms);
8532
+ return new Promise((resolve22, reject) => {
8533
+ const timer = setTimeout(resolve22, ms);
8527
8534
  signal?.addEventListener(
8528
8535
  "abort",
8529
8536
  () => {
@@ -8567,9 +8574,9 @@ function getWorker() {
8567
8574
  return worker;
8568
8575
  }
8569
8576
  function dispatch(kind, payload) {
8570
- return new Promise((resolve21, reject) => {
8577
+ return new Promise((resolve22, reject) => {
8571
8578
  const id = nextRequestId++;
8572
- pending.set(id, { resolve: resolve21, reject });
8579
+ pending.set(id, { resolve: resolve22, reject });
8573
8580
  try {
8574
8581
  getWorker().postMessage({ kind, id, ...payload });
8575
8582
  } catch (error) {
@@ -8802,7 +8809,7 @@ function isSymlinkSafeDirCached(filePath) {
8802
8809
  return verdict;
8803
8810
  }
8804
8811
  function sleep2(ms) {
8805
- return new Promise((resolve21) => setTimeout(resolve21, ms));
8812
+ return new Promise((resolve22) => setTimeout(resolve22, ms));
8806
8813
  }
8807
8814
  function isRetryableRenameError(error) {
8808
8815
  return Boolean(
@@ -8819,16 +8826,25 @@ function renameWithLinkSync(tempPath, filePath, retries = 8) {
8819
8826
  for (let attempt = 0; attempt <= retries; attempt++) {
8820
8827
  try {
8821
8828
  if (process.platform === "win32") {
8822
- try {
8823
- fs2.unlinkSync(filePath);
8824
- } catch {
8825
- }
8826
8829
  try {
8827
8830
  fs2.renameSync(tempPath, filePath);
8828
8831
  return;
8829
8832
  } catch (renameError) {
8830
8833
  lastError = renameError;
8831
- if (!isRetryableLinkError(renameError) || attempt === retries) break;
8834
+ if (isRetryableLinkError(renameError) && attempt !== retries) {
8835
+ } else {
8836
+ try {
8837
+ fs2.unlinkSync(filePath);
8838
+ } catch {
8839
+ }
8840
+ try {
8841
+ fs2.renameSync(tempPath, filePath);
8842
+ return;
8843
+ } catch (renameError2) {
8844
+ lastError = renameError2;
8845
+ if (!isRetryableLinkError(renameError2) || attempt === retries) break;
8846
+ }
8847
+ }
8832
8848
  }
8833
8849
  } else {
8834
8850
  try {
@@ -8854,16 +8870,25 @@ async function renameWithLinkAsync(tempPath, filePath, retries = 8) {
8854
8870
  for (let attempt = 0; attempt <= retries; attempt++) {
8855
8871
  try {
8856
8872
  if (process.platform === "win32") {
8857
- try {
8858
- await fs2.promises.unlink(filePath);
8859
- } catch {
8860
- }
8861
8873
  try {
8862
8874
  await fs2.promises.rename(tempPath, filePath);
8863
8875
  return;
8864
8876
  } catch (renameError) {
8865
8877
  lastError = renameError;
8866
- if (!isRetryableLinkError(renameError) || attempt === retries) break;
8878
+ if (isRetryableLinkError(renameError) && attempt !== retries) {
8879
+ } else {
8880
+ try {
8881
+ await fs2.promises.unlink(filePath);
8882
+ } catch {
8883
+ }
8884
+ try {
8885
+ await fs2.promises.rename(tempPath, filePath);
8886
+ return;
8887
+ } catch (renameError2) {
8888
+ lastError = renameError2;
8889
+ if (!isRetryableLinkError(renameError2) || attempt === retries) break;
8890
+ }
8891
+ }
8867
8892
  }
8868
8893
  } else {
8869
8894
  try {
@@ -9400,7 +9425,7 @@ function acquireLockWithRetry(filePath, staleMs, kind = "file") {
9400
9425
  }
9401
9426
  }
9402
9427
  function sleep3(ms) {
9403
- return new Promise((resolve21) => setTimeout(resolve21, ms));
9428
+ return new Promise((resolve22) => setTimeout(resolve22, ms));
9404
9429
  }
9405
9430
  async function acquireLockWithRetryAsync(filePath, staleMs, kind = "file") {
9406
9431
  let attempt = 0;
@@ -9533,7 +9558,8 @@ function packageRoot() {
9533
9558
  return path4.resolve(path4.dirname(fileURLToPath(import.meta.url)), "..", "..");
9534
9559
  }
9535
9560
  function userPiRoot() {
9536
- const home = process.env.PI_TEAMS_HOME?.trim() || os2.homedir();
9561
+ const rawHome = process.env.PI_TEAMS_HOME?.trim();
9562
+ const home = rawHome && rawHome !== "undefined" ? rawHome : os2.homedir();
9537
9563
  const resolved = path4.join(home, ".pi", "agent");
9538
9564
  let isSymlink2 = false;
9539
9565
  try {
@@ -10254,7 +10280,11 @@ function parseWorktreeConfig(value) {
10254
10280
  const worktree = {
10255
10281
  setupHook: setupHook ? setupHook : void 0,
10256
10282
  setupHookTimeoutMs: parsePositiveInteger(obj.setupHookTimeoutMs, 3e5),
10257
- linkNodeModules: parseWithSchema(Type.Boolean(), obj.linkNodeModules)
10283
+ linkNodeModules: parseWithSchema(Type.Boolean(), obj.linkNodeModules),
10284
+ // C6: seedPaths was declared in the type + schema but never parsed here, so
10285
+ // loadedConfig.config.worktree?.seedPaths was always undefined -> the global
10286
+ // worktree seed overlay (worktree-manager.ts) silently never applied.
10287
+ seedPaths: parseStringList(obj.seedPaths)
10258
10288
  };
10259
10289
  return Object.values(worktree).some((entry) => entry !== void 0) ? worktree : void 0;
10260
10290
  }
@@ -11300,7 +11330,7 @@ function redactInlineSecrets(value) {
11300
11330
  if (keyLen > 0 && j < value.length && (value[j] === "=" || value[j] === ":")) {
11301
11331
  const key = value.substring(i2, j);
11302
11332
  if (isSecretKey(key)) {
11303
- const sep9 = value[j];
11333
+ const sep10 = value[j];
11304
11334
  let k = j + 1;
11305
11335
  let valLen = 0;
11306
11336
  while (k < value.length && valLen < 500 && value[k] !== " " && value[k] !== "," && value[k] !== ";" && value[k] !== '"' && value[k] !== "\r" && value[k] !== "\n") {
@@ -11309,7 +11339,7 @@ function redactInlineSecrets(value) {
11309
11339
  }
11310
11340
  if (valLen > 0) {
11311
11341
  result4.push(key);
11312
- result4.push(sep9);
11342
+ result4.push(sep10);
11313
11343
  result4.push("***");
11314
11344
  i2 = k;
11315
11345
  redacted = true;
@@ -11676,6 +11706,19 @@ function persistSequence(eventsPath, seq) {
11676
11706
  logInternalError("event-log.persist-sequence-file", error, `eventsPath=${eventsPath}`);
11677
11707
  }
11678
11708
  }
11709
+ function reserveSequence(eventsPath) {
11710
+ let last = seqCounters.get(eventsPath);
11711
+ if (last === void 0) {
11712
+ last = nextSequence(eventsPath) - 1;
11713
+ }
11714
+ const next = last + 1;
11715
+ seqCounters.set(eventsPath, next);
11716
+ return next;
11717
+ }
11718
+ function advanceSequenceCounter(eventsPath, seq) {
11719
+ const last = seqCounters.get(eventsPath);
11720
+ if (last === void 0 || seq > last) seqCounters.set(eventsPath, seq);
11721
+ }
11679
11722
  function computeEventFingerprint(event) {
11680
11723
  return createHash2("sha256").update(
11681
11724
  JSON.stringify({
@@ -11717,11 +11760,14 @@ async function withEventLogLockAsync(eventsPath, fn) {
11717
11760
  try {
11718
11761
  await next;
11719
11762
  } finally {
11720
- asyncLocks.delete(queueKey);
11763
+ if (asyncLocks.get(queueKey) === next) {
11764
+ asyncLocks.delete(queueKey);
11765
+ }
11721
11766
  }
11722
11767
  }
11723
11768
  function resetEventLogMode() {
11724
11769
  asyncQueues.clear();
11770
+ seqCounters.clear();
11725
11771
  }
11726
11772
  async function appendEventAsync(eventsPath, event) {
11727
11773
  const queueKey = eventsPath;
@@ -11732,8 +11778,9 @@ async function appendEventAsync(eventsPath, event) {
11732
11778
  let seq;
11733
11779
  if (baseMetadata?.seq !== void 0) {
11734
11780
  seq = baseMetadata.seq;
11781
+ advanceSequenceCounter(eventsPath, seq);
11735
11782
  } else {
11736
- seq = nextSequence(eventsPath);
11783
+ seq = reserveSequence(eventsPath);
11737
11784
  }
11738
11785
  let metadata = {
11739
11786
  seq,
@@ -11857,21 +11904,21 @@ async function appendEventAsync(eventsPath, event) {
11857
11904
  }
11858
11905
  return fullEvent;
11859
11906
  });
11860
- asyncQueues.set(
11861
- queueKey,
11862
- next.then(
11863
- () => {
11907
+ const tail = next.then(
11908
+ () => {
11909
+ if (asyncQueues.get(queueKey) === tail) {
11864
11910
  asyncQueues.delete(queueKey);
11865
- },
11866
- (error) => {
11867
- try {
11868
- logInternalError("event-log.async-queue", error, eventsPath);
11869
- } catch {
11870
- }
11871
- asyncQueues.set(queueKey, Promise.resolve());
11872
11911
  }
11873
- )
11912
+ },
11913
+ (error) => {
11914
+ try {
11915
+ logInternalError("event-log.async-queue", error, eventsPath);
11916
+ } catch {
11917
+ }
11918
+ asyncQueues.set(queueKey, Promise.resolve());
11919
+ }
11874
11920
  );
11921
+ asyncQueues.set(queueKey, tail);
11875
11922
  return next;
11876
11923
  }
11877
11924
  async function appendEventBatchInsideLock(eventsPath, queue) {
@@ -11895,7 +11942,7 @@ async function appendEventBatchInsideLock(eventsPath, queue) {
11895
11942
  } catch (error) {
11896
11943
  logInternalError("event-log.batch-size-check", error, `eventsPath=${eventsPath}`);
11897
11944
  }
11898
- const startingSeq = queue[0]?.event.metadata?.seq ?? nextSequence(eventsPath);
11945
+ const startingSeq = queue[0]?.event.metadata?.seq ?? reserveSequence(eventsPath);
11899
11946
  let nextSeq = startingSeq;
11900
11947
  const finalized = [];
11901
11948
  let lastSeq = 0;
@@ -11931,6 +11978,7 @@ async function appendEventBatchInsideLock(eventsPath, queue) {
11931
11978
  `, fullEvent });
11932
11979
  lastSeq = seq;
11933
11980
  }
11981
+ advanceSequenceCounter(eventsPath, lastSeq);
11934
11982
  try {
11935
11983
  if (fs8.existsSync(eventsPath) && fs8.statSync(eventsPath).size > MAX_EVENTS_BYTES) {
11936
11984
  logInternalError(
@@ -11972,8 +12020,11 @@ async function appendEventBatchInsideLock(eventsPath, queue) {
11972
12020
  function appendEventInsideLock(eventsPath, event) {
11973
12021
  fs8.mkdirSync(path6.dirname(eventsPath), { recursive: true });
11974
12022
  const baseMetadata = event.metadata;
12023
+ const explicitSeq = baseMetadata?.seq;
12024
+ const seq = explicitSeq ?? reserveSequence(eventsPath);
12025
+ if (explicitSeq !== void 0) advanceSequenceCounter(eventsPath, seq);
11975
12026
  let metadata = {
11976
- seq: baseMetadata?.seq ?? nextSequence(eventsPath),
12027
+ seq,
11977
12028
  provenance: baseMetadata?.provenance ?? "team_runner",
11978
12029
  ...baseMetadata?.parentEventId ? { parentEventId: baseMetadata.parentEventId } : {},
11979
12030
  ...baseMetadata?.attemptId ? { attemptId: baseMetadata.attemptId } : {},
@@ -12028,7 +12079,6 @@ function appendEventInsideLock(eventsPath, event) {
12028
12079
  } catch (error) {
12029
12080
  logInternalError("event-log.size-check", error, `eventsPath=${eventsPath}`);
12030
12081
  }
12031
- const seq = fullEvent.metadata?.seq ?? 0;
12032
12082
  if (!skippedDueToSize) {
12033
12083
  fs8.appendFileSync(eventsPath, `${JSON.stringify(redactSecrets(fullEvent))}
12034
12084
  `, "utf-8");
@@ -12080,9 +12130,9 @@ function appendEventBuffered(eventsPath, event, bufferMs = DEFAULT_BUFFER_MS) {
12080
12130
  }
12081
12131
  return Promise.resolve(appendEvent(eventsPath, event));
12082
12132
  }
12083
- return new Promise((resolve21, reject) => {
12133
+ return new Promise((resolve22, reject) => {
12084
12134
  const queue = bufferedQueues.get(eventsPath) ?? [];
12085
- queue.push({ event, resolve: resolve21, reject });
12135
+ queue.push({ event, resolve: resolve22, reject });
12086
12136
  bufferedQueues.set(eventsPath, queue);
12087
12137
  if (!bufferedTimers.has(eventsPath)) {
12088
12138
  const timer = setTimeout(() => {
@@ -12195,7 +12245,7 @@ function dedupeTerminalEvents(events) {
12195
12245
  }
12196
12246
  return output;
12197
12247
  }
12198
- var TERMINAL_EVENT_TYPES, MAX_EVENTS_BYTES, sequenceCache, MAX_SEQUENCE_CACHE_ENTRIES, appendCounter, overflowCounter, MAX_SEQUENCE_CACHE_ENTRIES_VALUE, asyncQueues, asyncLocks, bufferedQueues, bufferedTimers, DEFAULT_BUFFER_MS;
12248
+ var TERMINAL_EVENT_TYPES, MAX_EVENTS_BYTES, sequenceCache, MAX_SEQUENCE_CACHE_ENTRIES, appendCounter, overflowCounter, MAX_SEQUENCE_CACHE_ENTRIES_VALUE, seqCounters, asyncQueues, asyncLocks, bufferedQueues, bufferedTimers, DEFAULT_BUFFER_MS;
12199
12249
  var init_event_log = __esm({
12200
12250
  "src/state/event-log.ts"() {
12201
12251
  "use strict";
@@ -12216,6 +12266,7 @@ var init_event_log = __esm({
12216
12266
  appendCounter = 0;
12217
12267
  overflowCounter = 0;
12218
12268
  MAX_SEQUENCE_CACHE_ENTRIES_VALUE = MAX_SEQUENCE_CACHE_ENTRIES;
12269
+ seqCounters = /* @__PURE__ */ new Map();
12219
12270
  asyncQueues = /* @__PURE__ */ new Map();
12220
12271
  asyncLocks = /* @__PURE__ */ new Map();
12221
12272
  bufferedQueues = /* @__PURE__ */ new Map();
@@ -12428,8 +12479,8 @@ function resolveContainedPath(baseDir, targetPath2) {
12428
12479
  const resolved = path7.isAbsolute(targetPath2) ? path7.resolve(targetPath2) : path7.resolve(base, targetPath2);
12429
12480
  const baseNorm = resolveCanonicalPath(base);
12430
12481
  const resolvedNorm = resolveCanonicalPath(resolved);
12431
- const relative8 = process.platform === "win32" ? path7.relative(baseNorm.toLowerCase(), resolvedNorm.toLowerCase()) : path7.relative(baseNorm, resolvedNorm);
12432
- if (relative8.startsWith("..") || path7.isAbsolute(relative8)) throw new Error(`Path is outside ${baseDir}: ${targetPath2}`);
12482
+ const relative9 = process.platform === "win32" ? path7.relative(baseNorm.toLowerCase(), resolvedNorm.toLowerCase()) : path7.relative(baseNorm, resolvedNorm);
12483
+ if (relative9.startsWith("..") || path7.isAbsolute(relative9)) throw new Error(`Path is outside ${baseDir}: ${targetPath2}`);
12433
12484
  return resolved;
12434
12485
  }
12435
12486
  function resolveCanonicalPath(p) {
@@ -12611,8 +12662,8 @@ function resolveRealContainedPath(baseDir, targetPath2) {
12611
12662
  throw new Error(`Path is outside ${baseDir}: ${targetPath2}`);
12612
12663
  }
12613
12664
  } else {
12614
- const relative8 = path7.relative(realBase, realTarget);
12615
- if (relative8.startsWith("..") || path7.isAbsolute(relative8)) throw new Error(`Path is outside ${baseDir}: ${targetPath2}`);
12665
+ const relative9 = path7.relative(realBase, realTarget);
12666
+ if (relative9.startsWith("..") || path7.isAbsolute(relative9)) throw new Error(`Path is outside ${baseDir}: ${targetPath2}`);
12616
12667
  }
12617
12668
  return realTarget;
12618
12669
  }
@@ -14562,7 +14613,7 @@ async function respondAsBackground(targetAgentId, fromId, message, opts) {
14562
14613
  return awaitPendingReply(corrId, targetAgentId, fromId, timeoutMs, opts?.signal);
14563
14614
  }
14564
14615
  function awaitPendingReply(corrId, targetAgentId, fromId, timeoutMs, signal) {
14565
- return new Promise((resolve21) => {
14616
+ return new Promise((resolve22) => {
14566
14617
  const deadline = Date.now() + timeoutMs;
14567
14618
  let settled = false;
14568
14619
  let timer;
@@ -14576,7 +14627,7 @@ function awaitPendingReply(corrId, targetAgentId, fromId, timeoutMs, signal) {
14576
14627
  const set2 = pendingRepliesByTarget.get(targetAgentId);
14577
14628
  set2?.delete(corrId);
14578
14629
  if (set2 && set2.size === 0) pendingRepliesByTarget.delete(targetAgentId);
14579
- resolve21(result4);
14630
+ resolve22(result4);
14580
14631
  };
14581
14632
  timer = setTimeout(() => finish({ ok: false, corrId, timedOut: true }), timeoutMs);
14582
14633
  if (signal) {
@@ -14748,7 +14799,9 @@ function checkResultFile(manifest, tasks) {
14748
14799
  (t2) => t2.status === "completed" || t2.status === "failed" || t2.status === "cancelled" || t2.status === "skipped" || t2.status === "needs_attention"
14749
14800
  );
14750
14801
  if (allTerminal) {
14751
- manifest.status = "completed";
14802
+ const hasFailed = tasks.some((t2) => t2.status === "failed");
14803
+ const onlyCancelledOrSkipped = tasks.every((t2) => t2.status === "cancelled" || t2.status === "skipped");
14804
+ manifest.status = hasFailed ? "failed" : onlyCancelledOrSkipped ? "cancelled" : "completed";
14752
14805
  saveRunManifest(manifest);
14753
14806
  for (const task of tasks) {
14754
14807
  try {
@@ -14994,14 +15047,14 @@ function reconcileOrphanedTempWorkspaces(now = Date.now(), options) {
14994
15047
  const tasks = JSON.parse(fs16.readFileSync(tasksPath, "utf-8"));
14995
15048
  const result4 = reconcileStaleRun(manifest, tasks, now);
14996
15049
  if (result4.repaired && result4.repairedTasks) {
14997
- fs16.writeFileSync(tasksPath, JSON.stringify(result4.repairedTasks, null, 2));
15050
+ atomicWriteJson(tasksPath, result4.repairedTasks);
14998
15051
  const updated = {
14999
15052
  ...manifest,
15000
15053
  status: "cancelled",
15001
15054
  updatedAt: new Date(now).toISOString(),
15002
15055
  summary: `Stale run reconciled: ${result4.detail}`
15003
15056
  };
15004
- fs16.writeFileSync(manifestPath, JSON.stringify(updated, null, 2));
15057
+ atomicWriteJson(manifestPath, updated);
15005
15058
  for (const task of result4.repairedTasks) {
15006
15059
  try {
15007
15060
  upsertCrewAgent(updated, recordFromTask(updated, task, "scaffold"));
@@ -15152,6 +15205,7 @@ var init_stale_reconciler = __esm({
15152
15205
  "src/runtime/stale-reconciler.ts"() {
15153
15206
  "use strict";
15154
15207
  init_errors3();
15208
+ init_atomic_write();
15155
15209
  init_state_store();
15156
15210
  init_crew_agent_records();
15157
15211
  init_process_status();
@@ -17307,7 +17361,7 @@ var init_subagent_manager = __esm({
17307
17361
  await record.promise.catch((error) => {
17308
17362
  logInternalError("subagent-manager.waitForRecord", error, `id=${id}`);
17309
17363
  });
17310
- else await new Promise((resolve21) => setTimeout(resolve21, 100));
17364
+ else await new Promise((resolve22) => setTimeout(resolve22, 100));
17311
17365
  }
17312
17366
  }
17313
17367
  setMaxConcurrent(value) {
@@ -17405,7 +17459,7 @@ var init_subagent_manager = __esm({
17405
17459
  while (record.runId && (record.status === "running" || record.status === "blocked")) {
17406
17460
  const loaded = loadRunManifestById(cwd, record.runId);
17407
17461
  if (!loaded) {
17408
- await new Promise((resolve21) => setTimeout(resolve21, this.pollIntervalMs));
17462
+ await new Promise((resolve22) => setTimeout(resolve22, this.pollIntervalMs));
17409
17463
  continue;
17410
17464
  }
17411
17465
  if (loaded.manifest.status === "completed") {
@@ -17438,7 +17492,7 @@ var init_subagent_manager = __esm({
17438
17492
  savePersistedSubagentRecord(cwd, record);
17439
17493
  return;
17440
17494
  }
17441
- await new Promise((resolve21) => setTimeout(resolve21, this.pollIntervalMs));
17495
+ await new Promise((resolve22) => setTimeout(resolve22, this.pollIntervalMs));
17442
17496
  }
17443
17497
  }
17444
17498
  scheduleBlockedTerminalPoll(cwd, record) {
@@ -17786,8 +17840,8 @@ var init_deduplicate_stage = __esm({
17786
17840
  const cur = lines[i2];
17787
17841
  if (cur !== out[out.length - 1]) out.push(cur);
17788
17842
  }
17789
- const sep9 = text.includes("\r\n") ? "\r\n" : "\n";
17790
- return out.join(sep9);
17843
+ const sep10 = text.includes("\r\n") ? "\r\n" : "\n";
17844
+ return out.join(sep10);
17791
17845
  }
17792
17846
  };
17793
17847
  DEDUPLICATE_STAGE = new DeduplicateStage();
@@ -18257,13 +18311,20 @@ function attachPostExitStdioGuard(child, options) {
18257
18311
  stderrEnded = true;
18258
18312
  if (stdoutEnded && stderrEnded) clearTimers();
18259
18313
  });
18260
- child.on("exit", () => {
18261
- exited = true;
18262
- armIdleTimer();
18314
+ const armHardTimer = () => {
18263
18315
  if (hardTimer) return;
18264
18316
  hardTimer = setTimeout(destroyUnendedStdio, hardMs);
18265
18317
  hardTimer.unref();
18266
- });
18318
+ };
18319
+ const onExit = () => {
18320
+ exited = true;
18321
+ armIdleTimer();
18322
+ armHardTimer();
18323
+ };
18324
+ if (child.exitCode != null || child.signalCode != null) {
18325
+ onExit();
18326
+ }
18327
+ child.on("exit", onExit);
18267
18328
  child.on("close", clearTimers);
18268
18329
  child.on("error", clearTimers);
18269
18330
  return clearTimers;
@@ -18295,14 +18356,20 @@ function clearHardKillTimer(pid) {
18295
18356
  clearTimeout(timer);
18296
18357
  childHardKillTimers.delete(pid);
18297
18358
  }
18359
+ function spawnTaskkillSafe(pid) {
18360
+ const taskkillChild = spawn("taskkill", ["/pid", String(pid), "/t", "/f"], {
18361
+ stdio: "ignore",
18362
+ windowsHide: true
18363
+ });
18364
+ taskkillChild.on("error", (err2) => {
18365
+ logInternalError("child-pi.taskkill-spawn-error", err2 instanceof Error ? err2 : new Error(String(err2)), `pid=${pid}`);
18366
+ });
18367
+ }
18298
18368
  function killProcessPid(pid) {
18299
18369
  if (!Number.isInteger(pid) || pid <= 0) return;
18300
18370
  try {
18301
18371
  if (process.platform === "win32") {
18302
- spawn("taskkill", ["/pid", String(pid), "/t", "/f"], {
18303
- stdio: "ignore",
18304
- windowsHide: true
18305
- });
18372
+ spawnTaskkillSafe(pid);
18306
18373
  const verifyTimer = setTimeout(() => {
18307
18374
  try {
18308
18375
  process.kill(pid, 0);
@@ -18312,10 +18379,7 @@ function killProcessPid(pid) {
18312
18379
  `pid=${pid}`
18313
18380
  );
18314
18381
  try {
18315
- spawn("taskkill", ["/pid", String(pid), "/t", "/f"], {
18316
- stdio: "ignore",
18317
- windowsHide: true
18318
- });
18382
+ spawnTaskkillSafe(pid);
18319
18383
  } catch {
18320
18384
  }
18321
18385
  } catch {
@@ -18716,9 +18780,18 @@ ${JSON.stringify({ type: "message_end", usage: { input: 10, output: 5, cost: 1e-
18716
18780
  role: input.role
18717
18781
  });
18718
18782
  if (input.steeringFile) built.env.PI_CREW_STEERING_FILE = input.steeringFile;
18783
+ if (input.signal?.aborted) {
18784
+ return {
18785
+ exitCode: null,
18786
+ stdout: "",
18787
+ stderr: "",
18788
+ error: "Aborted before spawn (parent AbortSignal already aborted)",
18789
+ aborted: true
18790
+ };
18791
+ }
18719
18792
  const spawnSpec = getPiSpawnCommand(built.args);
18720
18793
  try {
18721
- return await new Promise((resolve21) => {
18794
+ return await new Promise((resolve22) => {
18722
18795
  for (const key of Object.keys(built.env)) {
18723
18796
  if (!key.startsWith("PI_CREW_") && !key.startsWith("PI_TEAMS_")) {
18724
18797
  throw new Error(
@@ -18889,29 +18962,23 @@ ${JSON.stringify({ type: "message_end", usage: { input: 10, output: 5, cost: 1e-
18889
18962
  turnCount += 1;
18890
18963
  if (maxTurns !== void 0 && !softLimitReached && turnCount >= maxTurns) {
18891
18964
  softLimitReached = true;
18892
- if (child.stdin?.writable) {
18893
- const steerPayload = JSON.stringify({
18894
- type: "steer",
18895
- message: "You have reached your turn limit. Wrap up immediately \u2014 provide your final answer now."
18896
- }) + "\n";
18897
- const writeSucceeded = child.stdin.write(steerPayload);
18898
- if (!writeSucceeded) {
18965
+ if (input.steeringFile) {
18966
+ try {
18967
+ fs27.appendFileSync(
18968
+ input.steeringFile,
18969
+ JSON.stringify({
18970
+ type: "steer",
18971
+ message: "You have reached your turn limit. Wrap up immediately \u2014 provide your final answer now."
18972
+ }) + "\n",
18973
+ "utf-8"
18974
+ );
18975
+ } catch (err2) {
18899
18976
  logInternalError(
18900
- "child-pi.steer-backpressure",
18901
- new Error(
18902
- "stdin write returned false (normal backpressure); steer buffered, worker NOT killed"
18903
- ),
18977
+ "child-pi.steer-write-failed",
18978
+ err2 instanceof Error ? err2 : new Error(String(err2)),
18904
18979
  `pid=${child.pid}`
18905
18980
  );
18906
18981
  }
18907
- } else {
18908
- logInternalError(
18909
- "child-pi.steer-not-writable",
18910
- new Error(
18911
- "stdin not writable when attempting steer injection (worker may be done); worker NOT killed"
18912
- ),
18913
- `pid=${child.pid}`
18914
- );
18915
18982
  }
18916
18983
  } else if (maxTurns !== void 0 && softLimitReached && turnCount >= maxTurns + (graceTurns ?? 5)) {
18917
18984
  try {
@@ -19042,7 +19109,7 @@ ${JSON.stringify({ type: "message_end", usage: { input: 10, output: 5, cost: 1e-
19042
19109
  cleanupErrors.push(error instanceof Error ? error.message : String(error));
19043
19110
  }
19044
19111
  try {
19045
- resolve21({
19112
+ resolve22({
19046
19113
  ...result4,
19047
19114
  rawFinalText: lineObserver.getRawFinalText(),
19048
19115
  intermediateFindings: lineObserver.getIntermediateFindings(),
@@ -19556,6 +19623,12 @@ function setExtensionWidget(ctx, key, content, options) {
19556
19623
  const { persist: _persist, ...widgetOptions } = options ?? {};
19557
19624
  ctx.ui.setWidget(key, content, widgetOptions);
19558
19625
  }
19626
+ function setFooter(ctx, factory) {
19627
+ if (!ctx) return;
19628
+ const record = maybeRecord(ctx.ui);
19629
+ const fn = record?.setFooter;
19630
+ if (typeof fn === "function") fn.call(ctx.ui, factory);
19631
+ }
19559
19632
  function showCustom(ctx, factory, options) {
19560
19633
  const custom = ctx.ui.custom;
19561
19634
  return custom(factory, options);
@@ -19695,7 +19768,7 @@ var init_cancellation_token = __esm({
19695
19768
  wait(ms) {
19696
19769
  this.throwIfCancelled();
19697
19770
  if (ms <= 0) return Promise.resolve();
19698
- return new Promise((resolve21, reject) => {
19771
+ return new Promise((resolve22, reject) => {
19699
19772
  let timeout;
19700
19773
  const cleanup = () => {
19701
19774
  if (timeout) clearTimeout(timeout);
@@ -19707,7 +19780,7 @@ var init_cancellation_token = __esm({
19707
19780
  };
19708
19781
  timeout = setTimeout(() => {
19709
19782
  cleanup();
19710
- resolve21();
19783
+ resolve22();
19711
19784
  }, ms);
19712
19785
  this.signal.addEventListener("abort", onAbort, { once: true });
19713
19786
  });
@@ -23641,8 +23714,8 @@ function taskMailboxDir(manifest, taskId, create = false) {
23641
23714
  const tasksRoot = safeMailboxTasksRoot(manifest, create);
23642
23715
  const normalizedTaskId = safeTaskId(taskId);
23643
23716
  const resolved = path32.resolve(tasksRoot, normalizedTaskId);
23644
- const relative8 = path32.relative(tasksRoot, resolved);
23645
- if (relative8.startsWith("..") || path32.isAbsolute(relative8)) throw new Error(`Invalid mailbox task id: ${taskId}`);
23717
+ const relative9 = path32.relative(tasksRoot, resolved);
23718
+ if (relative9.startsWith("..") || path32.isAbsolute(relative9)) throw new Error(`Invalid mailbox task id: ${taskId}`);
23646
23719
  if (create) fs37.mkdirSync(resolved, { recursive: true });
23647
23720
  if (fs37.existsSync(resolved) && fs37.lstatSync(resolved).isSymbolicLink()) throw new Error(`Invalid mailbox task directory: ${resolved}`);
23648
23721
  return resolved;
@@ -23876,8 +23949,8 @@ function appendMailboxMessage(manifest, message) {
23876
23949
  `,
23877
23950
  "utf-8"
23878
23951
  );
23952
+ rotateMailboxFileIfNeeded(mailboxFile(manifest, complete.direction, complete.taskId));
23879
23953
  });
23880
- rotateMailboxFileIfNeeded(mailboxFile(manifest, complete.direction, complete.taskId));
23881
23954
  withFileLockSync(deliveryFile(manifest, true), () => {
23882
23955
  const delivery = readDeliveryState(manifest);
23883
23956
  delivery.messages[complete.id] = complete.status;
@@ -26087,9 +26160,9 @@ async function isLiveSessionRuntimeAvailable(timeoutMs = 1500, env = process.env
26087
26160
  try {
26088
26161
  return await Promise.race([
26089
26162
  probe(),
26090
- new Promise((resolve21) => {
26163
+ new Promise((resolve22) => {
26091
26164
  timer = setTimeout(
26092
- () => resolve21({
26165
+ () => resolve22({
26093
26166
  available: false,
26094
26167
  reason: `Timed out probing optional Pi SDK live-session runtime after ${timeoutMs}ms.`
26095
26168
  }),
@@ -28632,9 +28705,9 @@ var require_stringifyNumber = __commonJS({
28632
28705
  function stringifyNumber({ format: format2, minFractionDigits, tag, value }) {
28633
28706
  if (typeof value === "bigint")
28634
28707
  return String(value);
28635
- const num = typeof value === "number" ? value : Number(value);
28636
- if (!isFinite(num))
28637
- return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf";
28708
+ const num2 = typeof value === "number" ? value : Number(value);
28709
+ if (!isFinite(num2))
28710
+ return isNaN(num2) ? ".nan" : num2 < 0 ? "-.inf" : ".inf";
28638
28711
  let n = Object.is(value, -0) ? "-0" : JSON.stringify(value);
28639
28712
  if (!format2 && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^-?\d/.test(n) && !n.includes("e")) {
28640
28713
  let i2 = n.indexOf(".");
@@ -28674,8 +28747,8 @@ var require_float = __commonJS({
28674
28747
  test: /^[-+]?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)[eE][-+]?[0-9]+$/,
28675
28748
  resolve: (str) => parseFloat(str),
28676
28749
  stringify(node) {
28677
- const num = Number(node.value);
28678
- return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node);
28750
+ const num2 = Number(node.value);
28751
+ return isFinite(num2) ? num2.toExponential() : stringifyNumber.stringifyNumber(node);
28679
28752
  }
28680
28753
  };
28681
28754
  var float = {
@@ -29114,8 +29187,8 @@ var require_float2 = __commonJS({
29114
29187
  test: /^[-+]?(?:[0-9][0-9_]*)?(?:\.[0-9_]*)?[eE][-+]?[0-9]+$/,
29115
29188
  resolve: (str) => parseFloat(str.replace(/_/g, "")),
29116
29189
  stringify(node) {
29117
- const num = Number(node.value);
29118
- return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node);
29190
+ const num2 = Number(node.value);
29191
+ return isFinite(num2) ? num2.toExponential() : stringifyNumber.stringifyNumber(node);
29119
29192
  }
29120
29193
  };
29121
29194
  var float = {
@@ -29317,23 +29390,23 @@ var require_timestamp = __commonJS({
29317
29390
  function parseSexagesimal(str, asBigInt) {
29318
29391
  const sign = str[0];
29319
29392
  const parts = sign === "-" || sign === "+" ? str.substring(1) : str;
29320
- const num = (n) => asBigInt ? BigInt(n) : Number(n);
29321
- const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 * num(60) + num(p), num(0));
29322
- return sign === "-" ? num(-1) * res : res;
29393
+ const num2 = (n) => asBigInt ? BigInt(n) : Number(n);
29394
+ const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 * num2(60) + num2(p), num2(0));
29395
+ return sign === "-" ? num2(-1) * res : res;
29323
29396
  }
29324
29397
  function stringifySexagesimal(node) {
29325
29398
  let { value } = node;
29326
- let num = (n) => n;
29399
+ let num2 = (n) => n;
29327
29400
  if (typeof value === "bigint")
29328
- num = (n) => BigInt(n);
29401
+ num2 = (n) => BigInt(n);
29329
29402
  else if (isNaN(value) || !isFinite(value))
29330
29403
  return stringifyNumber.stringifyNumber(node);
29331
29404
  let sign = "";
29332
29405
  if (value < 0) {
29333
29406
  sign = "-";
29334
- value *= num(-1);
29407
+ value *= num2(-1);
29335
29408
  }
29336
- const _60 = num(60);
29409
+ const _60 = num2(60);
29337
29410
  const parts = [value % _60];
29338
29411
  if (value < 60) {
29339
29412
  parts.unshift(0);
@@ -30250,10 +30323,10 @@ var require_resolve_block_map = __commonJS({
30250
30323
  let offset2 = bm.offset;
30251
30324
  let commentEnd = null;
30252
30325
  for (const collItem of bm.items) {
30253
- const { start, key, sep: sep9, value } = collItem;
30326
+ const { start, key, sep: sep10, value } = collItem;
30254
30327
  const keyProps = resolveProps.resolveProps(start, {
30255
30328
  indicator: "explicit-key-ind",
30256
- next: key ?? sep9?.[0],
30329
+ next: key ?? sep10?.[0],
30257
30330
  offset: offset2,
30258
30331
  onError,
30259
30332
  parentIndent: bm.indent,
@@ -30267,7 +30340,7 @@ var require_resolve_block_map = __commonJS({
30267
30340
  else if ("indent" in key && key.indent !== bm.indent)
30268
30341
  onError(offset2, "BAD_INDENT", startColMsg);
30269
30342
  }
30270
- if (!keyProps.anchor && !keyProps.tag && !sep9) {
30343
+ if (!keyProps.anchor && !keyProps.tag && !sep10) {
30271
30344
  commentEnd = keyProps.end;
30272
30345
  if (keyProps.comment) {
30273
30346
  if (map3.comment)
@@ -30291,7 +30364,7 @@ var require_resolve_block_map = __commonJS({
30291
30364
  ctx.atKey = false;
30292
30365
  if (utilMapIncludes.mapIncludes(ctx, map3.items, keyNode))
30293
30366
  onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
30294
- const valueProps = resolveProps.resolveProps(sep9 ?? [], {
30367
+ const valueProps = resolveProps.resolveProps(sep10 ?? [], {
30295
30368
  indicator: "map-value-ind",
30296
30369
  next: value,
30297
30370
  offset: keyNode.range[2],
@@ -30307,7 +30380,7 @@ var require_resolve_block_map = __commonJS({
30307
30380
  if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024)
30308
30381
  onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key");
30309
30382
  }
30310
- const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset2, sep9, null, valueProps, onError);
30383
+ const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset2, sep10, null, valueProps, onError);
30311
30384
  if (ctx.schema.compat)
30312
30385
  utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError);
30313
30386
  offset2 = valueNode.range[2];
@@ -30398,7 +30471,7 @@ var require_resolve_end = __commonJS({
30398
30471
  let comment = "";
30399
30472
  if (end) {
30400
30473
  let hasSpace = false;
30401
- let sep9 = "";
30474
+ let sep10 = "";
30402
30475
  for (const token of end) {
30403
30476
  const { source, type } = token;
30404
30477
  switch (type) {
@@ -30412,13 +30485,13 @@ var require_resolve_end = __commonJS({
30412
30485
  if (!comment)
30413
30486
  comment = cb;
30414
30487
  else
30415
- comment += sep9 + cb;
30416
- sep9 = "";
30488
+ comment += sep10 + cb;
30489
+ sep10 = "";
30417
30490
  break;
30418
30491
  }
30419
30492
  case "newline":
30420
30493
  if (comment)
30421
- sep9 += source;
30494
+ sep10 += source;
30422
30495
  hasSpace = true;
30423
30496
  break;
30424
30497
  default:
@@ -30461,18 +30534,18 @@ var require_resolve_flow_collection = __commonJS({
30461
30534
  let offset2 = fc.offset + fc.start.source.length;
30462
30535
  for (let i2 = 0; i2 < fc.items.length; ++i2) {
30463
30536
  const collItem = fc.items[i2];
30464
- const { start, key, sep: sep9, value } = collItem;
30537
+ const { start, key, sep: sep10, value } = collItem;
30465
30538
  const props = resolveProps.resolveProps(start, {
30466
30539
  flow: fcName,
30467
30540
  indicator: "explicit-key-ind",
30468
- next: key ?? sep9?.[0],
30541
+ next: key ?? sep10?.[0],
30469
30542
  offset: offset2,
30470
30543
  onError,
30471
30544
  parentIndent: fc.indent,
30472
30545
  startOnNewline: false
30473
30546
  });
30474
30547
  if (!props.found) {
30475
- if (!props.anchor && !props.tag && !sep9 && !value) {
30548
+ if (!props.anchor && !props.tag && !sep10 && !value) {
30476
30549
  if (i2 === 0 && props.comma)
30477
30550
  onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`);
30478
30551
  else if (i2 < fc.items.length - 1)
@@ -30526,8 +30599,8 @@ var require_resolve_flow_collection = __commonJS({
30526
30599
  }
30527
30600
  }
30528
30601
  }
30529
- if (!isMap && !sep9 && !props.found) {
30530
- const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep9, null, props, onError);
30602
+ if (!isMap && !sep10 && !props.found) {
30603
+ const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep10, null, props, onError);
30531
30604
  coll.items.push(valueNode);
30532
30605
  offset2 = valueNode.range[2];
30533
30606
  if (isBlock(value))
@@ -30539,7 +30612,7 @@ var require_resolve_flow_collection = __commonJS({
30539
30612
  if (isBlock(key))
30540
30613
  onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg);
30541
30614
  ctx.atKey = false;
30542
- const valueProps = resolveProps.resolveProps(sep9 ?? [], {
30615
+ const valueProps = resolveProps.resolveProps(sep10 ?? [], {
30543
30616
  flow: fcName,
30544
30617
  indicator: "map-value-ind",
30545
30618
  next: value,
@@ -30550,8 +30623,8 @@ var require_resolve_flow_collection = __commonJS({
30550
30623
  });
30551
30624
  if (valueProps.found) {
30552
30625
  if (!isMap && !props.found && ctx.options.strict) {
30553
- if (sep9)
30554
- for (const st of sep9) {
30626
+ if (sep10)
30627
+ for (const st of sep10) {
30555
30628
  if (st === valueProps.found)
30556
30629
  break;
30557
30630
  if (st.type === "newline") {
@@ -30568,7 +30641,7 @@ var require_resolve_flow_collection = __commonJS({
30568
30641
  else
30569
30642
  onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`);
30570
30643
  }
30571
- const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep9, null, valueProps, onError) : null;
30644
+ const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep10, null, valueProps, onError) : null;
30572
30645
  if (valueNode) {
30573
30646
  if (isBlock(value))
30574
30647
  onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg);
@@ -30748,7 +30821,7 @@ var require_resolve_block_scalar = __commonJS({
30748
30821
  chompStart = i2 + 1;
30749
30822
  }
30750
30823
  let value = "";
30751
- let sep9 = "";
30824
+ let sep10 = "";
30752
30825
  let prevMoreIndented = false;
30753
30826
  for (let i2 = 0; i2 < contentStart; ++i2)
30754
30827
  value += lines[i2][0].slice(trimIndent) + "\n";
@@ -30765,24 +30838,24 @@ var require_resolve_block_scalar = __commonJS({
30765
30838
  indent = "";
30766
30839
  }
30767
30840
  if (type === Scalar.Scalar.BLOCK_LITERAL) {
30768
- value += sep9 + indent.slice(trimIndent) + content;
30769
- sep9 = "\n";
30841
+ value += sep10 + indent.slice(trimIndent) + content;
30842
+ sep10 = "\n";
30770
30843
  } else if (indent.length > trimIndent || content[0] === " ") {
30771
- if (sep9 === " ")
30772
- sep9 = "\n";
30773
- else if (!prevMoreIndented && sep9 === "\n")
30774
- sep9 = "\n\n";
30775
- value += sep9 + indent.slice(trimIndent) + content;
30776
- sep9 = "\n";
30844
+ if (sep10 === " ")
30845
+ sep10 = "\n";
30846
+ else if (!prevMoreIndented && sep10 === "\n")
30847
+ sep10 = "\n\n";
30848
+ value += sep10 + indent.slice(trimIndent) + content;
30849
+ sep10 = "\n";
30777
30850
  prevMoreIndented = true;
30778
30851
  } else if (content === "") {
30779
- if (sep9 === "\n")
30852
+ if (sep10 === "\n")
30780
30853
  value += "\n";
30781
30854
  else
30782
- sep9 = "\n";
30855
+ sep10 = "\n";
30783
30856
  } else {
30784
- value += sep9 + content;
30785
- sep9 = " ";
30857
+ value += sep10 + content;
30858
+ sep10 = " ";
30786
30859
  prevMoreIndented = false;
30787
30860
  }
30788
30861
  }
@@ -30964,25 +31037,25 @@ var require_resolve_flow_scalar = __commonJS({
30964
31037
  if (!match)
30965
31038
  return source;
30966
31039
  let res = match[1];
30967
- let sep9 = " ";
31040
+ let sep10 = " ";
30968
31041
  let pos = first.lastIndex;
30969
31042
  line4.lastIndex = pos;
30970
31043
  while (match = line4.exec(source)) {
30971
31044
  if (match[1] === "") {
30972
- if (sep9 === "\n")
30973
- res += sep9;
31045
+ if (sep10 === "\n")
31046
+ res += sep10;
30974
31047
  else
30975
- sep9 = "\n";
31048
+ sep10 = "\n";
30976
31049
  } else {
30977
- res += sep9 + match[1];
30978
- sep9 = " ";
31050
+ res += sep10 + match[1];
31051
+ sep10 = " ";
30979
31052
  }
30980
31053
  pos = line4.lastIndex;
30981
31054
  }
30982
31055
  const last = /[ \t]*(.*)/sy;
30983
31056
  last.lastIndex = pos;
30984
31057
  match = last.exec(source);
30985
- return res + sep9 + (match?.[1] ?? "");
31058
+ return res + sep10 + (match?.[1] ?? "");
30986
31059
  }
30987
31060
  function doubleQuotedValue(source, onError) {
30988
31061
  let res = "";
@@ -31792,14 +31865,14 @@ var require_cst_stringify = __commonJS({
31792
31865
  }
31793
31866
  }
31794
31867
  }
31795
- function stringifyItem({ start, key, sep: sep9, value }) {
31868
+ function stringifyItem({ start, key, sep: sep10, value }) {
31796
31869
  let res = "";
31797
31870
  for (const st of start)
31798
31871
  res += st.source;
31799
31872
  if (key)
31800
31873
  res += stringifyToken(key);
31801
- if (sep9)
31802
- for (const st of sep9)
31874
+ if (sep10)
31875
+ for (const st of sep10)
31803
31876
  res += st.source;
31804
31877
  if (value)
31805
31878
  res += stringifyToken(value);
@@ -32966,18 +33039,18 @@ var require_parser = __commonJS({
32966
33039
  if (this.type === "map-value-ind") {
32967
33040
  const prev = getPrevProps(this.peek(2));
32968
33041
  const start = getFirstKeyStartProps(prev);
32969
- let sep9;
33042
+ let sep10;
32970
33043
  if (scalar.end) {
32971
- sep9 = scalar.end;
32972
- sep9.push(this.sourceToken);
33044
+ sep10 = scalar.end;
33045
+ sep10.push(this.sourceToken);
32973
33046
  delete scalar.end;
32974
33047
  } else
32975
- sep9 = [this.sourceToken];
33048
+ sep10 = [this.sourceToken];
32976
33049
  const map3 = {
32977
33050
  type: "block-map",
32978
33051
  offset: scalar.offset,
32979
33052
  indent: scalar.indent,
32980
- items: [{ start, key: scalar, sep: sep9 }]
33053
+ items: [{ start, key: scalar, sep: sep10 }]
32981
33054
  };
32982
33055
  this.onKeyLine = true;
32983
33056
  this.stack[this.stack.length - 1] = map3;
@@ -33130,15 +33203,15 @@ var require_parser = __commonJS({
33130
33203
  } else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) {
33131
33204
  const start2 = getFirstKeyStartProps(it.start);
33132
33205
  const key = it.key;
33133
- const sep9 = it.sep;
33134
- sep9.push(this.sourceToken);
33206
+ const sep10 = it.sep;
33207
+ sep10.push(this.sourceToken);
33135
33208
  delete it.key;
33136
33209
  delete it.sep;
33137
33210
  this.stack.push({
33138
33211
  type: "block-map",
33139
33212
  offset: this.offset,
33140
33213
  indent: this.indent,
33141
- items: [{ start: start2, key, sep: sep9 }]
33214
+ items: [{ start: start2, key, sep: sep10 }]
33142
33215
  });
33143
33216
  } else if (start.length > 0) {
33144
33217
  it.sep = it.sep.concat(start, this.sourceToken);
@@ -33332,13 +33405,13 @@ var require_parser = __commonJS({
33332
33405
  const prev = getPrevProps(parent);
33333
33406
  const start = getFirstKeyStartProps(prev);
33334
33407
  fixFlowSeqItems(fc);
33335
- const sep9 = fc.end.splice(1, fc.end.length);
33336
- sep9.push(this.sourceToken);
33408
+ const sep10 = fc.end.splice(1, fc.end.length);
33409
+ sep10.push(this.sourceToken);
33337
33410
  const map3 = {
33338
33411
  type: "block-map",
33339
33412
  offset: fc.offset,
33340
33413
  indent: fc.indent,
33341
- items: [{ start, key: fc, sep: sep9 }]
33414
+ items: [{ start, key: fc, sep: sep10 }]
33342
33415
  };
33343
33416
  this.onKeyLine = true;
33344
33417
  this.stack[this.stack.length - 1] = map3;
@@ -38245,7 +38318,7 @@ var require_compile = __commonJS({
38245
38318
  const schOrFunc = root.refs[ref2];
38246
38319
  if (schOrFunc)
38247
38320
  return schOrFunc;
38248
- let _sch = resolve21.call(this, root, ref2);
38321
+ let _sch = resolve22.call(this, root, ref2);
38249
38322
  if (_sch === void 0) {
38250
38323
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref2];
38251
38324
  const { schemaId } = this.opts;
@@ -38272,7 +38345,7 @@ var require_compile = __commonJS({
38272
38345
  function sameSchemaEnv(s1, s2) {
38273
38346
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
38274
38347
  }
38275
- function resolve21(root, ref2) {
38348
+ function resolve22(root, ref2) {
38276
38349
  let sch;
38277
38350
  while (typeof (sch = this.refs[ref2]) == "string")
38278
38351
  ref2 = sch;
@@ -38903,55 +38976,55 @@ var require_fast_uri = __commonJS({
38903
38976
  }
38904
38977
  return uri;
38905
38978
  }
38906
- function resolve21(baseURI, relativeURI, options) {
38979
+ function resolve22(baseURI, relativeURI, options) {
38907
38980
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
38908
38981
  const resolved = resolveComponent(parse6(baseURI, schemelessOptions), parse6(relativeURI, schemelessOptions), schemelessOptions, true);
38909
38982
  schemelessOptions.skipEscape = true;
38910
38983
  return serialize(resolved, schemelessOptions);
38911
38984
  }
38912
- function resolveComponent(base, relative8, options, skipNormalization) {
38985
+ function resolveComponent(base, relative9, options, skipNormalization) {
38913
38986
  const target = {};
38914
38987
  if (!skipNormalization) {
38915
38988
  base = parse6(serialize(base, options), options);
38916
- relative8 = parse6(serialize(relative8, options), options);
38989
+ relative9 = parse6(serialize(relative9, options), options);
38917
38990
  }
38918
38991
  options = options || {};
38919
- if (!options.tolerant && relative8.scheme) {
38920
- target.scheme = relative8.scheme;
38921
- target.userinfo = relative8.userinfo;
38922
- target.host = relative8.host;
38923
- target.port = relative8.port;
38924
- target.path = removeDotSegments(relative8.path || "");
38925
- target.query = relative8.query;
38992
+ if (!options.tolerant && relative9.scheme) {
38993
+ target.scheme = relative9.scheme;
38994
+ target.userinfo = relative9.userinfo;
38995
+ target.host = relative9.host;
38996
+ target.port = relative9.port;
38997
+ target.path = removeDotSegments(relative9.path || "");
38998
+ target.query = relative9.query;
38926
38999
  } else {
38927
- if (relative8.userinfo !== void 0 || relative8.host !== void 0 || relative8.port !== void 0) {
38928
- target.userinfo = relative8.userinfo;
38929
- target.host = relative8.host;
38930
- target.port = relative8.port;
38931
- target.path = removeDotSegments(relative8.path || "");
38932
- target.query = relative8.query;
39000
+ if (relative9.userinfo !== void 0 || relative9.host !== void 0 || relative9.port !== void 0) {
39001
+ target.userinfo = relative9.userinfo;
39002
+ target.host = relative9.host;
39003
+ target.port = relative9.port;
39004
+ target.path = removeDotSegments(relative9.path || "");
39005
+ target.query = relative9.query;
38933
39006
  } else {
38934
- if (!relative8.path) {
39007
+ if (!relative9.path) {
38935
39008
  target.path = base.path;
38936
- if (relative8.query !== void 0) {
38937
- target.query = relative8.query;
39009
+ if (relative9.query !== void 0) {
39010
+ target.query = relative9.query;
38938
39011
  } else {
38939
39012
  target.query = base.query;
38940
39013
  }
38941
39014
  } else {
38942
- if (relative8.path[0] === "/") {
38943
- target.path = removeDotSegments(relative8.path);
39015
+ if (relative9.path[0] === "/") {
39016
+ target.path = removeDotSegments(relative9.path);
38944
39017
  } else {
38945
39018
  if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
38946
- target.path = "/" + relative8.path;
39019
+ target.path = "/" + relative9.path;
38947
39020
  } else if (!base.path) {
38948
- target.path = relative8.path;
39021
+ target.path = relative9.path;
38949
39022
  } else {
38950
- target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative8.path;
39023
+ target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative9.path;
38951
39024
  }
38952
39025
  target.path = removeDotSegments(target.path);
38953
39026
  }
38954
- target.query = relative8.query;
39027
+ target.query = relative9.query;
38955
39028
  }
38956
39029
  target.userinfo = base.userinfo;
38957
39030
  target.host = base.host;
@@ -38959,7 +39032,7 @@ var require_fast_uri = __commonJS({
38959
39032
  }
38960
39033
  target.scheme = base.scheme;
38961
39034
  }
38962
- target.fragment = relative8.fragment;
39035
+ target.fragment = relative9.fragment;
38963
39036
  return target;
38964
39037
  }
38965
39038
  function equal(uriA, uriB, options) {
@@ -39161,7 +39234,7 @@ var require_fast_uri = __commonJS({
39161
39234
  var fastUri = {
39162
39235
  SCHEMES,
39163
39236
  normalize: normalize2,
39164
- resolve: resolve21,
39237
+ resolve: resolve22,
39165
39238
  resolveComponent,
39166
39239
  equal,
39167
39240
  serialize,
@@ -42638,7 +42711,7 @@ ${input.prompt}` : input.prompt;
42638
42711
  );
42639
42712
  } catch {
42640
42713
  }
42641
- await new Promise((resolve21) => setTimeout(resolve21, DEFAULT_LIVE_SESSION.yieldPollIntervalMs));
42714
+ await new Promise((resolve22) => setTimeout(resolve22, DEFAULT_LIVE_SESSION.yieldPollIntervalMs));
42642
42715
  if (customToolYieldResolved && customToolYieldResult) {
42643
42716
  yieldResult = customToolYieldResult;
42644
42717
  } else {
@@ -42682,7 +42755,7 @@ ${input.prompt}` : input.prompt;
42682
42755
  }
42683
42756
  }
42684
42757
  const pollInterval = DEFAULT_LIVE_SESSION.yieldPollIntervalMs;
42685
- await new Promise((resolve21) => setTimeout(resolve21, pollInterval));
42758
+ await new Promise((resolve22) => setTimeout(resolve22, pollInterval));
42686
42759
  if (customToolYieldResolved && customToolYieldResult) {
42687
42760
  yieldResult = customToolYieldResult;
42688
42761
  break;
@@ -44113,13 +44186,13 @@ var init_checkpoint = __esm({
44113
44186
  import * as fs51 from "node:fs";
44114
44187
  import * as path43 from "node:path";
44115
44188
  function registerRunPromise(runId) {
44116
- let resolve21;
44189
+ let resolve22;
44117
44190
  let reject;
44118
44191
  const promise = new Promise((res, rej) => {
44119
- resolve21 = res;
44192
+ resolve22 = res;
44120
44193
  reject = rej;
44121
44194
  });
44122
- const entry = { promise, resolve: resolve21, reject };
44195
+ const entry = { promise, resolve: resolve22, reject };
44123
44196
  activeRunPromises.set(runId, entry);
44124
44197
  return entry;
44125
44198
  }
@@ -44644,11 +44717,11 @@ function readSkillMarkdown(cwd, name) {
44644
44717
  if (cached) skillReadCache.delete(cacheKey2);
44645
44718
  for (const entry of candidateSkillDirs(cwd)) {
44646
44719
  try {
44647
- const relative8 = path44.join(name, "SKILL.md");
44648
- const contained = resolveContainedPath(entry.root, relative8);
44720
+ const relative9 = path44.join(name, "SKILL.md");
44721
+ const contained = resolveContainedPath(entry.root, relative9);
44649
44722
  if (!fs52.existsSync(contained)) continue;
44650
44723
  if (fs52.lstatSync(contained).isSymbolicLink()) continue;
44651
- const filePath = resolveRealContainedPath(entry.root, relative8);
44724
+ const filePath = resolveRealContainedPath(entry.root, relative9);
44652
44725
  const stat2 = fs52.statSync(filePath);
44653
44726
  return rememberSkill(cacheKey2, {
44654
44727
  path: filePath,
@@ -47069,7 +47142,7 @@ function buildTeamDoctorReport(input) {
47069
47142
  );
47070
47143
  const sections = [
47071
47144
  section("Runtime", () => {
47072
- const git3 = commandExists("git", ["--version"]);
47145
+ const git4 = commandExists("git", ["--version"]);
47073
47146
  const pi = piCommandExists();
47074
47147
  return [
47075
47148
  { label: "cwd", ok: true, detail: input.cwd },
@@ -47079,7 +47152,7 @@ function buildTeamDoctorReport(input) {
47079
47152
  detail: `${process.platform}/${process.arch} node=${process.version}`
47080
47153
  },
47081
47154
  { label: "pi command", ok: pi.ok, detail: pi.detail },
47082
- { label: "git command", ok: git3.ok, detail: git3.detail },
47155
+ { label: "git command", ok: git4.ok, detail: git4.detail },
47083
47156
  {
47084
47157
  label: "config",
47085
47158
  ok: input.configErrors.length === 0,
@@ -49314,6 +49387,21 @@ function cleanupRunWorktrees(manifest, options = {}) {
49314
49387
  const branchName = `pi-crew/${manifest.runId}/${sanitizeBranchPart(entry.name)}`;
49315
49388
  const safeBranchName = sanitizeBranchPart(entry.name);
49316
49389
  if (dirty) {
49390
+ if (!options.force) {
49391
+ const safePreserveName = sanitizeFilename(entry.name);
49392
+ const artifact = writeArtifact(manifest.artifactsRoot, {
49393
+ kind: "diff",
49394
+ relativePath: `cleanup/${safePreserveName}.diff`,
49395
+ content: captureDiff(worktreePath),
49396
+ producer: "worktree-cleanup"
49397
+ });
49398
+ result4.artifactPaths.push(artifact.path);
49399
+ result4.preserved.push({
49400
+ path: worktreePath,
49401
+ reason: "dirty worktree preserved \u2014 pass force=true to auto-commit and remove"
49402
+ });
49403
+ continue;
49404
+ }
49317
49405
  if (options.signal?.aborted) break;
49318
49406
  try {
49319
49407
  const statusBefore = git(worktreePath, ["status", "--porcelain"]);
@@ -54066,8 +54154,8 @@ function safeSharedName(name) {
54066
54154
  function sharedPath(manifest, name) {
54067
54155
  const sharedRoot = path61.resolve(manifest.artifactsRoot, "shared");
54068
54156
  const resolved = path61.resolve(sharedRoot, safeSharedName(name));
54069
- const relative8 = path61.relative(sharedRoot, resolved);
54070
- if (relative8.startsWith("..") || path61.isAbsolute(relative8)) throw new Error(`Invalid shared artifact name: ${name}`);
54157
+ const relative9 = path61.relative(sharedRoot, resolved);
54158
+ if (relative9.startsWith("..") || path61.isAbsolute(relative9)) throw new Error(`Invalid shared artifact name: ${name}`);
54071
54159
  return resolved;
54072
54160
  }
54073
54161
  function tryParseJson(text) {
@@ -54082,8 +54170,8 @@ function listTaskArtifacts(manifest, taskId) {
54082
54170
  const produced = manifest.artifacts.filter((a) => a.producer === taskId);
54083
54171
  if (produced.length === 0) return void 0;
54084
54172
  return produced.map((a) => {
54085
- const relative8 = path61.relative(manifest.artifactsRoot, a.path);
54086
- return relative8.startsWith("..") ? a.path : relative8;
54173
+ const relative9 = path61.relative(manifest.artifactsRoot, a.path);
54174
+ return relative9.startsWith("..") ? a.path : relative9;
54087
54175
  });
54088
54176
  }
54089
54177
  function aggregateUsage2(task) {
@@ -55372,6 +55460,45 @@ import { randomBytes as randomBytes2 } from "node:crypto";
55372
55460
  import * as fs75 from "node:fs";
55373
55461
  import * as path63 from "node:path";
55374
55462
  import { promisify } from "node:util";
55463
+ function git3(cwd, args) {
55464
+ return execFileSync6("git", args, {
55465
+ cwd,
55466
+ encoding: "utf-8",
55467
+ stdio: ["ignore", "pipe", "pipe"],
55468
+ env: {
55469
+ ...sanitizeEnvSecrets(process.env, {
55470
+ allowList: [
55471
+ "PATH",
55472
+ "HOME",
55473
+ "USER",
55474
+ ...WINDOWS_ESSENTIAL_ENV_VARS,
55475
+ "SHELL",
55476
+ "TERM",
55477
+ "LANG",
55478
+ "LC_ALL",
55479
+ "LC_COLLATE",
55480
+ "LC_CTYPE",
55481
+ "LC_MESSAGES",
55482
+ "XDG_CONFIG_HOME",
55483
+ "XDG_DATA_HOME",
55484
+ "XDG_CACHE_HOME",
55485
+ "NVM_BIN",
55486
+ "NVM_DIR",
55487
+ "NODE_PATH",
55488
+ "GIT_CONFIG_GLOBAL",
55489
+ "GIT_CONFIG_SYSTEM",
55490
+ "GIT_AUTHOR_NAME",
55491
+ "GIT_AUTHOR_EMAIL",
55492
+ "GIT_COMMITTER_NAME",
55493
+ "GIT_COMMITTER_EMAIL"
55494
+ ]
55495
+ }),
55496
+ LANG: "en_US.UTF-8",
55497
+ LC_ALL: "en_US.UTF-8"
55498
+ },
55499
+ windowsHide: true
55500
+ }).trim();
55501
+ }
55375
55502
  function gitEnv() {
55376
55503
  return {
55377
55504
  ...sanitizeEnvSecrets(process.env, {
@@ -55458,9 +55585,9 @@ function linkNodeModulesIfPresent(repoRoot, worktreePath) {
55458
55585
  }
55459
55586
  function normalizeSyntheticPath(worktreePath, rawPath) {
55460
55587
  const resolved = path63.resolve(worktreePath, rawPath);
55461
- const relative8 = path63.relative(worktreePath, resolved);
55462
- if (!relative8 || relative8.startsWith("..") || path63.isAbsolute(relative8)) throw new Error(`synthetic path escapes worktree: ${rawPath}`);
55463
- return path63.normalize(relative8);
55588
+ const relative9 = path63.relative(worktreePath, resolved);
55589
+ if (!relative9 || relative9.startsWith("..") || path63.isAbsolute(relative9)) throw new Error(`synthetic path escapes worktree: ${rawPath}`);
55590
+ return path63.normalize(relative9);
55464
55591
  }
55465
55592
  function isAllowedSetupHook(hookPath) {
55466
55593
  if (!hookPath || hookPath.trim().length === 0) return false;
@@ -55690,6 +55817,64 @@ function overlaySeedPaths(repoRoot, worktreePath, seedPaths) {
55690
55817
  });
55691
55818
  }
55692
55819
  }
55820
+ function snapshotDirtyWorktree(manifest, task, worktreePath, dirtyStatus) {
55821
+ try {
55822
+ const parts = [
55823
+ `# Worktree recovery snapshot`,
55824
+ `runId: ${manifest.runId} taskId: ${task.id} path: ${worktreePath}`,
55825
+ `capturedAt: ${(/* @__PURE__ */ new Date()).toISOString()}`,
55826
+ ""
55827
+ ];
55828
+ let trackedDiff = "";
55829
+ try {
55830
+ trackedDiff = git3(worktreePath, ["diff", "HEAD"]);
55831
+ } catch {
55832
+ trackedDiff = "";
55833
+ }
55834
+ if (trackedDiff.trim()) {
55835
+ parts.push("## Tracked changes (`git diff HEAD`)", "```diff", trackedDiff, "```", "");
55836
+ }
55837
+ for (const line4 of dirtyStatus.split("\n")) {
55838
+ if (!line4.startsWith("?? ")) continue;
55839
+ const rel = line4.slice(3).replace(/^"|"$/g, "");
55840
+ if (!rel) continue;
55841
+ try {
55842
+ const abs = path63.join(worktreePath, rel);
55843
+ if (!fs75.existsSync(abs) || fs75.statSync(abs).isDirectory()) continue;
55844
+ const content = fs75.readFileSync(abs, "utf-8");
55845
+ parts.push(`## Untracked file: ${rel}`, "```", content, "```", "");
55846
+ } catch {
55847
+ }
55848
+ }
55849
+ writeArtifact(manifest.artifactsRoot, {
55850
+ kind: "diff",
55851
+ relativePath: `worktree-recovery/${task.id}-${Date.now()}.md`,
55852
+ content: parts.join("\n"),
55853
+ producer: "worktree-manager.snapshotDirtyWorktree",
55854
+ retention: "run"
55855
+ });
55856
+ } catch (err2) {
55857
+ logInternalError(
55858
+ "worktree.recovery.snapshotFailed",
55859
+ err2 instanceof Error ? err2 : new Error(String(err2)),
55860
+ `runId=${manifest.runId}, taskId=${task.id}`
55861
+ );
55862
+ }
55863
+ }
55864
+ async function cleanupCreatedWorktreeAsync(repoRoot, worktreePath, branch) {
55865
+ try {
55866
+ await gitAsync(repoRoot, ["worktree", "remove", "--force", worktreePath]);
55867
+ } catch {
55868
+ try {
55869
+ if (fs75.existsSync(worktreePath)) fs75.rmSync(worktreePath, { recursive: true, force: true });
55870
+ } catch {
55871
+ }
55872
+ }
55873
+ try {
55874
+ await gitAsync(repoRoot, ["branch", "-D", branch]);
55875
+ } catch {
55876
+ }
55877
+ }
55693
55878
  async function prepareTaskWorkspaceAsync(manifest, task, stepSeedPaths) {
55694
55879
  if (manifest.workspaceMode !== "worktree") return { cwd: task.cwd };
55695
55880
  const repoRoot = await findGitRootAsync(manifest.cwd);
@@ -55747,16 +55932,17 @@ async function prepareTaskWorkspaceAsync(manifest, task, stepSeedPaths) {
55747
55932
  }
55748
55933
  const dirtyStatus = await gitAsync(worktreePath, ["status", "--porcelain"]);
55749
55934
  if (dirtyStatus.trim()) {
55935
+ snapshotDirtyWorktree(manifest, task, worktreePath, dirtyStatus);
55750
55936
  logInternalError(
55751
55937
  "worktree.reused.dirty",
55752
- new Error(`Discarding uncommitted changes in reused worktree at ${worktreePath}`),
55938
+ new Error(`Discarding uncommitted changes in reused worktree at ${worktreePath} (snapshot saved to artifacts)`),
55753
55939
  `runId=${manifest.runId}, taskId=${task.id}, dirtyStatus=${dirtyStatus.trim()}`
55754
55940
  );
55755
55941
  await gitAsync(worktreePath, ["checkout", "--", "."]);
55756
55942
  await gitAsync(worktreePath, ["clean", "-fd"]);
55757
55943
  }
55758
- const globalSeedPaths2 = loadedConfig.config.worktree?.seedPaths ?? [];
55759
- const mergedReused = normalizeSeedPaths([...globalSeedPaths2, ...stepSeedPaths ?? []], repoRoot);
55944
+ const globalSeedPaths = loadedConfig.config.worktree?.seedPaths ?? [];
55945
+ const mergedReused = normalizeSeedPaths([...globalSeedPaths, ...stepSeedPaths ?? []], repoRoot);
55760
55946
  if (mergedReused.length > 0) {
55761
55947
  overlaySeedPaths(repoRoot, worktreePath, mergedReused);
55762
55948
  }
@@ -55796,21 +55982,26 @@ async function prepareTaskWorkspaceAsync(manifest, task, stepSeedPaths) {
55796
55982
  }
55797
55983
  throw error;
55798
55984
  }
55799
- const syntheticPaths = runSetupHook(manifest, task, repoRoot, worktreePath, branch);
55800
- const nodeModulesLinked = loadedConfig.config.worktree?.linkNodeModules === true ? linkNodeModulesIfPresent(repoRoot, worktreePath) : false;
55801
- const globalSeedPaths = loadedConfig.config.worktree?.seedPaths ?? [];
55802
- const merged = normalizeSeedPaths([...globalSeedPaths, ...stepSeedPaths ?? []], repoRoot);
55803
- if (merged.length > 0) {
55804
- overlaySeedPaths(repoRoot, worktreePath, merged);
55985
+ try {
55986
+ const syntheticPaths = runSetupHook(manifest, task, repoRoot, worktreePath, branch);
55987
+ const nodeModulesLinked = loadedConfig.config.worktree?.linkNodeModules === true ? linkNodeModulesIfPresent(repoRoot, worktreePath) : false;
55988
+ const globalSeedPaths = loadedConfig.config.worktree?.seedPaths ?? [];
55989
+ const merged = normalizeSeedPaths([...globalSeedPaths, ...stepSeedPaths ?? []], repoRoot);
55990
+ if (merged.length > 0) {
55991
+ overlaySeedPaths(repoRoot, worktreePath, merged);
55992
+ }
55993
+ return {
55994
+ cwd: worktreePath,
55995
+ worktreePath,
55996
+ branch,
55997
+ reused: false,
55998
+ nodeModulesLinked,
55999
+ syntheticPaths
56000
+ };
56001
+ } catch (setupError) {
56002
+ await cleanupCreatedWorktreeAsync(repoRoot, worktreePath, branch);
56003
+ throw setupError;
55805
56004
  }
55806
- return {
55807
- cwd: worktreePath,
55808
- worktreePath,
55809
- branch,
55810
- reused: false,
55811
- nodeModulesLinked,
55812
- syntheticPaths
55813
- };
55814
56005
  }
55815
56006
  async function captureWorktreeDiffStatAsync(worktreePath) {
55816
56007
  try {
@@ -56870,7 +57061,7 @@ import * as fs79 from "node:fs";
56870
57061
  import * as path67 from "node:path";
56871
57062
  async function detectRipgrep() {
56872
57063
  if (cachedRgCheck !== void 0) return cachedRgCheck;
56873
- return await new Promise((resolve21) => {
57064
+ return await new Promise((resolve22) => {
56874
57065
  let settled = false;
56875
57066
  try {
56876
57067
  const child = spawn3("rg", ["--version"], { stdio: ["ignore", "pipe", "pipe"] });
@@ -56882,7 +57073,7 @@ async function detectRipgrep() {
56882
57073
  if (settled) return;
56883
57074
  settled = true;
56884
57075
  cachedRgCheck = { available: false };
56885
- resolve21(cachedRgCheck);
57076
+ resolve22(cachedRgCheck);
56886
57077
  });
56887
57078
  child.on("close", (code) => {
56888
57079
  if (settled) return;
@@ -56892,13 +57083,13 @@ async function detectRipgrep() {
56892
57083
  } else {
56893
57084
  cachedRgCheck = { available: false };
56894
57085
  }
56895
- resolve21(cachedRgCheck);
57086
+ resolve22(cachedRgCheck);
56896
57087
  });
56897
57088
  } catch {
56898
57089
  if (settled) return;
56899
57090
  settled = true;
56900
57091
  cachedRgCheck = { available: false };
56901
- resolve21(cachedRgCheck);
57092
+ resolve22(cachedRgCheck);
56902
57093
  }
56903
57094
  });
56904
57095
  }
@@ -56915,7 +57106,7 @@ function reasonFor(file, keywords2) {
56915
57106
  return `keyword match: ${hits.join(", ")}`;
56916
57107
  }
56917
57108
  function runRipgrep(args, cwd) {
56918
- return new Promise((resolve21, reject) => {
57109
+ return new Promise((resolve22, reject) => {
56919
57110
  let settled = false;
56920
57111
  let stdout = "";
56921
57112
  let stderr = "";
@@ -56936,7 +57127,7 @@ function runRipgrep(args, cwd) {
56936
57127
  if (settled) return;
56937
57128
  settled = true;
56938
57129
  if (code === 0 || code === 1) {
56939
- resolve21(stdout);
57130
+ resolve22(stdout);
56940
57131
  } else {
56941
57132
  reject(new Error(`rg exited ${code}: ${stderr.slice(0, 200)}`));
56942
57133
  }
@@ -57213,9 +57404,9 @@ function artifactReference(artifactsRoot, artifact) {
57213
57404
  if (!artifact) return void 0;
57214
57405
  const root = path68.resolve(artifactsRoot);
57215
57406
  const target = path68.resolve(artifact.path);
57216
- const relative8 = path68.relative(root, target);
57217
- if (!relative8 || relative8.startsWith("..") || path68.isAbsolute(relative8)) return void 0;
57218
- return relative8.replaceAll("\\", "/");
57407
+ const relative9 = path68.relative(root, target);
57408
+ if (!relative9 || relative9.startsWith("..") || path68.isAbsolute(relative9)) return void 0;
57409
+ return relative9.replaceAll("\\", "/");
57219
57410
  }
57220
57411
  function buildWorkerPromptPipeline(input) {
57221
57412
  return {
@@ -57427,7 +57618,7 @@ async function executeCommand(command, cwd, timeoutMs = 12e4) {
57427
57618
  const start = Date.now();
57428
57619
  let output = "";
57429
57620
  let exitCode = null;
57430
- return new Promise((resolve21) => {
57621
+ return new Promise((resolve22) => {
57431
57622
  const shell = spawn4("sh", ["-c", command], {
57432
57623
  cwd,
57433
57624
  timeout: timeoutMs,
@@ -57441,7 +57632,7 @@ async function executeCommand(command, cwd, timeoutMs = 12e4) {
57441
57632
  });
57442
57633
  const timer = setTimeout(() => {
57443
57634
  shell.kill("SIGKILL");
57444
- resolve21({
57635
+ resolve22({
57445
57636
  exitCode: -1,
57446
57637
  output: output + "\n[TIMEOUT: Command exceeded limit]",
57447
57638
  durationMs: Date.now() - start
@@ -57452,7 +57643,7 @@ async function executeCommand(command, cwd, timeoutMs = 12e4) {
57452
57643
  shell.on("close", (code) => {
57453
57644
  clearTimer();
57454
57645
  exitCode = code;
57455
- resolve21({
57646
+ resolve22({
57456
57647
  exitCode,
57457
57648
  output: output.slice(-1e5),
57458
57649
  // Cap at 100KB
@@ -57461,7 +57652,7 @@ async function executeCommand(command, cwd, timeoutMs = 12e4) {
57461
57652
  });
57462
57653
  shell.on("error", (err2) => {
57463
57654
  clearTimer();
57464
- resolve21({
57655
+ resolve22({
57465
57656
  exitCode: -1,
57466
57657
  output: `Execution error: ${err2.message}`,
57467
57658
  durationMs: Date.now() - start
@@ -60200,8 +60391,9 @@ async function executeTeamRun(input) {
60200
60391
  } catch (error) {
60201
60392
  stopTeamHeartbeat();
60202
60393
  const message = error instanceof Error ? error.message : String(error);
60203
- const freshManifest = manifest;
60204
- const freshTasks = refreshTaskGraphQueues(input.tasks);
60394
+ const fresh = loadRunManifestById(manifest.cwd, manifest.runId);
60395
+ const freshManifest = fresh?.manifest ?? manifest;
60396
+ const freshTasks = refreshTaskGraphQueues(fresh?.tasks ?? input.tasks);
60205
60397
  const failedAt = (/* @__PURE__ */ new Date()).toISOString();
60206
60398
  const tasks = freshTasks.map(
60207
60399
  (task) => task.status === "running" || task.status === "queued" || task.status === "waiting" ? {
@@ -61787,19 +61979,19 @@ function parseRoot(start) {
61787
61979
  function safeJoin(...parts) {
61788
61980
  const filtered = parts.filter(Boolean);
61789
61981
  if (filtered.length === 0) return "";
61790
- const sep9 = filtered.some((p) => p.includes("\\")) ? "\\" : "/";
61982
+ const sep10 = filtered.some((p) => p.includes("\\")) ? "\\" : "/";
61791
61983
  const firstPart = filtered[0];
61792
61984
  let leading = "";
61793
- if (sep9 === "\\") {
61985
+ if (sep10 === "\\") {
61794
61986
  if (firstPart.startsWith("\\\\")) leading = "\\\\";
61795
61987
  else if (firstPart.startsWith("\\")) leading = "\\";
61796
61988
  } else if (firstPart.startsWith("/")) {
61797
61989
  leading = "/";
61798
61990
  }
61799
- const firstPartStripped = sep9 === "\\" ? firstPart.replace(/^\\{1,2}/, "") : firstPart.replace(/^\/+/, "");
61991
+ const firstPartStripped = sep10 === "\\" ? firstPart.replace(/^\\{1,2}/, "") : firstPart.replace(/^\/+/, "");
61800
61992
  const rest = filtered.slice(1);
61801
- const joined = [firstPartStripped, ...rest].filter(Boolean).join(sep9);
61802
- const collapsed = joined.replace(new RegExp(`${sep9 === "\\" ? "\\\\" : "/"}{2,}`, "g"), sep9);
61993
+ const joined = [firstPartStripped, ...rest].filter(Boolean).join(sep10);
61994
+ const collapsed = joined.replace(new RegExp(`${sep10 === "\\" ? "\\\\" : "/"}{2,}`, "g"), sep10);
61803
61995
  return leading + collapsed;
61804
61996
  }
61805
61997
  function safeDirname(p) {
@@ -61956,12 +62148,12 @@ function tokenize(input) {
61956
62148
  continue;
61957
62149
  }
61958
62150
  if (/[0-9]/.test(input[i2])) {
61959
- let num = "";
62151
+ let num2 = "";
61960
62152
  while (i2 < input.length && /[0-9]/.test(input[i2])) {
61961
- num += input[i2];
62153
+ num2 += input[i2];
61962
62154
  i2++;
61963
62155
  }
61964
- tokens.push({ type: "NUMBER", value: num });
62156
+ tokens.push({ type: "NUMBER", value: num2 });
61965
62157
  continue;
61966
62158
  }
61967
62159
  if (/[a-zA-Z_]/.test(input[i2])) {
@@ -62027,8 +62219,8 @@ var init_chain_parser = __esm({
62027
62219
  while (this.pos < this.tokens.length) {
62028
62220
  if (this.peek("COLON")) {
62029
62221
  this.consume("COLON");
62030
- const num = this.consume("NUMBER");
62031
- step.loopCount = Number.parseInt(num.value, 10);
62222
+ const num2 = this.consume("NUMBER");
62223
+ step.loopCount = Number.parseInt(num2.value, 10);
62032
62224
  } else if (this.peek("FLAG", "with-context")) {
62033
62225
  this.consume("FLAG");
62034
62226
  step.withContext = true;
@@ -68932,7 +69124,7 @@ var init_deterministic_ast = __esm({
68932
69124
  });
68933
69125
 
68934
69126
  // src/runtime/dwf-state-store.ts
68935
- import { existsSync as existsSync70, mkdirSync as mkdirSync42, readFileSync as readFileSync68, unlinkSync as unlinkSync11 } from "node:fs";
69127
+ import { existsSync as existsSync70, mkdirSync as mkdirSync42, readFileSync as readFileSync69, unlinkSync as unlinkSync11 } from "node:fs";
68936
69128
  import { dirname as dirname37 } from "node:path";
68937
69129
  var DwfStore;
68938
69130
  var init_dwf_state_store = __esm({
@@ -68953,7 +69145,7 @@ var init_dwf_state_store = __esm({
68953
69145
  const path81 = this.path;
68954
69146
  try {
68955
69147
  if (!existsSync70(path81)) return void 0;
68956
- const raw = readFileSync68(path81, "utf-8");
69148
+ const raw = readFileSync69(path81, "utf-8");
68957
69149
  const parsed = JSON.parse(raw);
68958
69150
  if (!parsed || typeof parsed !== "object" || typeof parsed.runId !== "string") return void 0;
68959
69151
  return parsed;
@@ -69164,14 +69356,14 @@ var init_semaphore = __esm({
69164
69356
  if (this.#queue.length >= _Semaphore.MAX_QUEUE) {
69165
69357
  throw new Error(`Semaphore queue full: ${this.#queue.length} waiters (max ${_Semaphore.MAX_QUEUE}); cannot acquire slot`);
69166
69358
  }
69167
- const { promise, resolve: resolve21 } = (() => {
69359
+ const { promise, resolve: resolve22 } = (() => {
69168
69360
  let res;
69169
69361
  const p = new Promise((r) => {
69170
69362
  res = r;
69171
69363
  });
69172
69364
  return { promise: p, resolve: res };
69173
69365
  })();
69174
- this.#queue.push(resolve21);
69366
+ this.#queue.push(resolve22);
69175
69367
  return promise;
69176
69368
  }
69177
69369
  release() {
@@ -69775,7 +69967,7 @@ var dynamic_workflow_runner_exports = {};
69775
69967
  __export(dynamic_workflow_runner_exports, {
69776
69968
  runDynamicWorkflow: () => runDynamicWorkflow
69777
69969
  });
69778
- import { readFileSync as readFileSync69 } from "node:fs";
69970
+ import { readFileSync as readFileSync70 } from "node:fs";
69779
69971
  import { join as join71 } from "node:path";
69780
69972
  function assertStructuredCloneable(value, name) {
69781
69973
  try {
@@ -69800,7 +69992,7 @@ function resolveScriptPath(workflow, cwd) {
69800
69992
  );
69801
69993
  }
69802
69994
  async function loadWorkflowModule(scriptPath) {
69803
- const scriptSource = readFileSync69(scriptPath, "utf-8");
69995
+ const scriptSource = readFileSync70(scriptPath, "utf-8");
69804
69996
  if (isDeterminismCheckEnabled()) {
69805
69997
  assertDeterministicScript(scriptSource);
69806
69998
  }
@@ -69923,7 +70115,7 @@ async function runDynamicWorkflow(input) {
69923
70115
  }
69924
70116
  function readFinalArtifact(artifactPath) {
69925
70117
  try {
69926
- return readFileSync69(artifactPath, "utf-8");
70118
+ return readFileSync70(artifactPath, "utf-8");
69927
70119
  } catch (error) {
69928
70120
  logInternalError("dynamic-workflow-runner.readFinal", error, `artifactPath=${artifactPath}`);
69929
70121
  return `(failed to read final artifact ${artifactPath})`;
@@ -73818,7 +74010,7 @@ var init_run_dashboard = __esm({
73818
74010
  const borderFill = (count2) => new DynamicCrewBorder(this.theme).render(count2)[0];
73819
74011
  const border2 = (left, right) => `${fg("border", left)}${borderFill(borderWidth)}${fg("border", right)}`;
73820
74012
  const row = (text) => `\u2502 ${pad(truncate(text, innerWidth - 1), innerWidth - 1)}\u2502`;
73821
- const sep9 = () => border2("\u251C", "\u2524");
74013
+ const sep10 = () => border2("\u251C", "\u2524");
73822
74014
  const lines = [];
73823
74015
  if (this.showHelp) {
73824
74016
  lines.push(...new HelpOverlay(this.theme).render(width));
@@ -73828,7 +74020,7 @@ var init_run_dashboard = __esm({
73828
74020
  row(
73829
74021
  `${fg("accent", "\u2590")} ${this.theme.bold("pi-crew")} \xB7 ${this.runs.length} runs ${fg("dim", "1-6 pane \xB7 \u2191\u2193 \xB7 Enter \xB7 ? help \xB7 Esc")}`
73830
74022
  ),
73831
- sep9()
74023
+ sep10()
73832
74024
  );
73833
74025
  if (this.runs.length === 0) {
73834
74026
  lines.push(row(fg("dim", "No runs yet.")));
@@ -73876,7 +74068,7 @@ var init_run_dashboard = __esm({
73876
74068
  const agents = snap?.agents ?? agentsFor2(selectedRun, this.options.snapshotCache);
73877
74069
  const statusStr = isLikelyOrphanedActiveRun(r, agents) ? "stale" : r.status;
73878
74070
  const selectedTasks = snap?.tasks ?? readRunTasks2(r, this.options.snapshotCache);
73879
- lines.push(sep9());
74071
+ lines.push(sep10());
73880
74072
  lines.push(row(`${fg("accent", "\u25B8")} ${truncate(sanitizeLine(r.goal), innerWidth - 6)}`));
73881
74073
  const isTerminal = statusStr === "failed" || statusStr === "cancelled" || statusStr === "stopped";
73882
74074
  const reason = isTerminal ? summarizeTerminalReason(r, selectedTasks, snap?.cancellationReason) : void 0;
@@ -75808,10 +76000,10 @@ var init_settings_overlay = __esm({
75808
76000
  typeof current2 === "number" ? String(current2) : "",
75809
76001
  this.theme,
75810
76002
  (value) => {
75811
- const num = value === "" ? void 0 : Number(value);
75812
- if (num !== void 0 && !Number.isNaN(num)) {
75813
- this.changedValues.set(def.id, num);
75814
- this.callbacks.onChange(def.id, num);
76003
+ const num2 = value === "" ? void 0 : Number(value);
76004
+ if (num2 !== void 0 && !Number.isNaN(num2)) {
76005
+ this.changedValues.set(def.id, num2);
76006
+ this.callbacks.onChange(def.id, num2);
75815
76007
  } else if (value === "") {
75816
76008
  this.changedValues.set(def.id, void 0);
75817
76009
  this.callbacks.onChange(def.id, void 0);
@@ -81702,12 +81894,15 @@ function registerCrewShortcuts(pi) {
81702
81894
  }
81703
81895
  var CREW_SHORTCUT_KEYS = CREW_SHORTCUTS.map((s) => s.key);
81704
81896
 
81897
+ // src/extension/crew-vibes/index.ts
81898
+ init_pi_ui_compat();
81899
+
81705
81900
  // src/extension/crew-vibes/config.ts
81706
- import { existsSync as existsSync76, mkdirSync as mkdirSync45, readFileSync as readFileSync75, writeFileSync as writeFileSync33 } from "node:fs";
81901
+ import { existsSync as existsSync76, mkdirSync as mkdirSync45, readFileSync as readFileSync76, writeFileSync as writeFileSync33 } from "node:fs";
81707
81902
  import { dirname as dirname39, join as join76 } from "node:path";
81708
81903
 
81709
81904
  // src/extension/crew-vibes/font-detect.ts
81710
- import { existsSync as existsSync75, readFileSync as readFileSync74 } from "node:fs";
81905
+ import { existsSync as existsSync75, readFileSync as readFileSync75 } from "node:fs";
81711
81906
  import { homedir as homedir11, platform } from "node:os";
81712
81907
  import { join as join75 } from "node:path";
81713
81908
  function fontPath() {
@@ -81734,10 +81929,10 @@ function isWebTerminal() {
81734
81929
  try {
81735
81930
  let pid = process.pid;
81736
81931
  for (let i2 = 0; i2 < 6 && pid > 1; i2++) {
81737
- const cgroup = readFileSync74(`/proc/${pid}/cgroup`, "utf8");
81932
+ const cgroup = readFileSync75(`/proc/${pid}/cgroup`, "utf8");
81738
81933
  if (cgroup.includes("gotty") || cgroup.includes("wetty")) return true;
81739
81934
  const match = cgroup.match(/\d+:.*:(.*)/);
81740
- const status = readFileSync74(`/proc/${pid}/status`, "utf8");
81935
+ const status = readFileSync75(`/proc/${pid}/status`, "utf8");
81741
81936
  const ppid = status.match(/^PPid:\s+(\d+)/m);
81742
81937
  pid = ppid ? Number.parseInt(ppid[1], 10) : 1;
81743
81938
  }
@@ -81780,7 +81975,7 @@ var DEFAULT_CONFIG2 = {
81780
81975
  labels: ["Orbit", "Cruise", "Warp", "Black Hole", "Supernova", "Big Bang"],
81781
81976
  icons: ["\uE710", "\uE711", "\uE712", "\uE713", "\uE714", "\uE715"],
81782
81977
  providerUsage: true,
81783
- providerRefreshMs: 3e5
81978
+ providerRefreshMs: 12e4
81784
81979
  }
81785
81980
  };
81786
81981
  var FALLBACK_CAPACITY_ICONS = [
@@ -81869,7 +82064,7 @@ function loadConfig2() {
81869
82064
  try {
81870
82065
  const path81 = configPath2();
81871
82066
  if (!existsSync76(path81)) return normalizeConfig(void 0);
81872
- return normalizeConfig(JSON.parse(readFileSync75(path81, "utf8")));
82067
+ return normalizeConfig(JSON.parse(readFileSync76(path81, "utf8")));
81873
82068
  } catch {
81874
82069
  return normalizeConfig(void 0);
81875
82070
  }
@@ -81881,8 +82076,379 @@ function saveConfig(config) {
81881
82076
  `);
81882
82077
  }
81883
82078
 
82079
+ // src/extension/crew-vibes/figures.ts
82080
+ var BRAILLE_FRAMES = [
82081
+ "\u280B ",
82082
+ // ⠋
82083
+ "\u2819 ",
82084
+ // ⠙
82085
+ "\u2839 ",
82086
+ // ⠹
82087
+ "\u2838 ",
82088
+ // ⠸
82089
+ "\u283C ",
82090
+ // ⠼
82091
+ "\u2834 ",
82092
+ // ⠴
82093
+ "\u2826 ",
82094
+ // ⠦
82095
+ "\u2827 ",
82096
+ // ⠧
82097
+ "\u2807 ",
82098
+ // ⠇
82099
+ "\u280F "
82100
+ // ⠏
82101
+ ];
82102
+ var PUA_CREW_FRAMES = [
82103
+ "\uE700 ",
82104
+ "\uE701 ",
82105
+ "\uE702 ",
82106
+ "\uE703 ",
82107
+ "\uE704 ",
82108
+ "\uE705 ",
82109
+ "\uE706 ",
82110
+ "\uE707 ",
82111
+ "\uE708 ",
82112
+ "\uE709 ",
82113
+ "\uE70A ",
82114
+ "\uE70B ",
82115
+ "\uE70C ",
82116
+ "\uE70D ",
82117
+ "\uE70E ",
82118
+ "\uE70F "
82119
+ ];
82120
+ function crewFrames(style = "pua") {
82121
+ if (style === "pua" && isWebTerminal()) return BRAILLE_FRAMES;
82122
+ return style === "pua" ? PUA_CREW_FRAMES : BRAILLE_FRAMES;
82123
+ }
82124
+ function intervalForSpeed(config, speed) {
82125
+ if (speed === null || !Number.isFinite(speed) || speed <= 0) return config.defaultIntervalMs;
82126
+ return Math.max(config.minIntervalMs, Math.min(config.maxIntervalMs, Math.round(config.scale / speed)));
82127
+ }
82128
+ function capacityIndex(percent, levels = 6) {
82129
+ if (percent === null || percent === void 0 || !Number.isFinite(percent)) return 0;
82130
+ return Math.max(0, Math.min(levels - 1, Math.floor(Math.max(0, Math.min(100, percent)) / 100 * levels)));
82131
+ }
82132
+ function isDangerStage(index, levels) {
82133
+ return index >= Math.max(0, levels - 2);
82134
+ }
82135
+
82136
+ // src/extension/crew-vibes/footer.ts
82137
+ init_pi_ui_compat();
82138
+ init_theme_adapter();
82139
+ init_visual();
82140
+ import { isAbsolute as isAbsolute10, relative as relative8, resolve as resolve20, sep as sep9 } from "node:path";
82141
+
82142
+ // src/extension/crew-vibes/render.ts
82143
+ function formatCount(value) {
82144
+ if (value < 1e3) return value.toString();
82145
+ if (value < 1e4) return `${(value / 1e3).toFixed(1)}k`;
82146
+ if (value < 1e6) return `${Math.round(value / 1e3)}k`;
82147
+ if (value < 1e7) return `${(value / 1e6).toFixed(1)}M`;
82148
+ return `${Math.round(value / 1e6)}M`;
82149
+ }
82150
+ function asCrewTheme2(theme) {
82151
+ if (theme && typeof theme === "object" && typeof theme.fg === "function") {
82152
+ return theme;
82153
+ }
82154
+ return void 0;
82155
+ }
82156
+ function getCapacityUsage(ctx) {
82157
+ const fn = ctx.getContextUsage;
82158
+ const usage = typeof fn === "function" ? fn.call(ctx) : null;
82159
+ return {
82160
+ tokens: typeof usage?.tokens === "number" && Number.isFinite(usage.tokens) ? usage.tokens : null,
82161
+ percent: typeof usage?.percent === "number" && Number.isFinite(usage.percent) ? usage.percent : null
82162
+ };
82163
+ }
82164
+ function formatSpeed(config, speed) {
82165
+ return speed === null ? `-- ${config.label}` : `${speed.toFixed(1)} ${config.label}`;
82166
+ }
82167
+ function renderSpeedFooter(theme, config, speed) {
82168
+ const value = speed === null ? "--" : speed.toFixed(1);
82169
+ const valueTone = speed === null ? "dim" : "accent";
82170
+ const styled = theme ? `${theme.fg(valueTone, value)} ${theme.fg("dim", config.label)}` : `${value} ${config.label}`;
82171
+ return styled;
82172
+ }
82173
+ function renderWorkingMessage(theme, config, speed) {
82174
+ const left = "Working";
82175
+ const speedText = theme ? `${theme.fg(speed === null ? "dim" : "accent", speed === null ? "--" : speed.toFixed(1))} ${theme.fg("dim", config.label)}` : `${speed === null ? "--" : speed.toFixed(1)} ${config.label}`;
82176
+ return theme ? `${theme.fg("muted", left)} ${speedText}` : `${left} ${speedText}`;
82177
+ }
82178
+ function crewIndicatorFrames(theme) {
82179
+ const frames = crewFrames();
82180
+ if (!theme) return [...frames];
82181
+ return frames.map((frame) => theme.fg("accent", frame));
82182
+ }
82183
+ function formatCapacityPrefix(config, usage) {
82184
+ const display = config.tokenDisplay;
82185
+ if (display === "off") return "";
82186
+ if (display === "percentage") {
82187
+ return `${usage.percent === null ? "?" : Math.round(Math.max(0, Math.min(999, usage.percent)))}% `;
82188
+ }
82189
+ return `${usage.tokens === null ? "?" : formatCount(usage.tokens)} `;
82190
+ }
82191
+ function colorStage(theme, index, levels, text) {
82192
+ if (!theme || text.length === 0) return text;
82193
+ return theme.fg(isDangerStage(index, levels) ? "error" : "success", text);
82194
+ }
82195
+ function renderCapacity(theme, config, usage) {
82196
+ const icons = capacityIcons();
82197
+ const levels = icons.length;
82198
+ const index = capacityIndex(usage.percent, levels);
82199
+ const icon = icons[index] ?? icons[0];
82200
+ const label = config.labels[index] ?? config.labels[0];
82201
+ const prefix = theme ? theme.fg("muted", formatCapacityPrefix(config, usage)) : formatCapacityPrefix(config, usage);
82202
+ const coloredIcon = colorStage(theme, index, levels, icon);
82203
+ const afterIcon = config.showLabel ? ` ${colorStage(theme, index, levels, label)}` : " ";
82204
+ return `${prefix}${coloredIcon}${afterIcon}`;
82205
+ }
82206
+ function setSpeedStatus(ctx, config, text) {
82207
+ if (!ctx?.hasUI) return;
82208
+ if (!config.enabled || !config.speed.enabled || !config.speed.footer) {
82209
+ ctx.ui.setStatus(SPEED_STATUS_ID, void 0);
82210
+ return;
82211
+ }
82212
+ ctx.ui.setStatus(SPEED_STATUS_ID, text);
82213
+ }
82214
+ function clearVibesStatus(ctx) {
82215
+ if (!ctx?.hasUI) return;
82216
+ ctx.ui.setStatus(SPEED_STATUS_ID, void 0);
82217
+ ctx.ui.setStatus(CAPACITY_STATUS_ID, void 0);
82218
+ ctx.ui.setStatus(PROVIDER_STATUS_ID, void 0);
82219
+ if (ctx.ui.setWorkingIndicator) ctx.ui.setWorkingIndicator();
82220
+ if (ctx.ui.setWorkingMessage) ctx.ui.setWorkingMessage();
82221
+ }
82222
+ function formatResetTimer(resetAt) {
82223
+ if (!resetAt) return null;
82224
+ const diffMs = new Date(resetAt).getTime() - Date.now();
82225
+ if (diffMs < 0) return null;
82226
+ const mins = Math.floor(diffMs / 6e4);
82227
+ if (mins < 60) return `${mins}m`;
82228
+ const hours = Math.floor(mins / 60);
82229
+ if (hours < 48) {
82230
+ const remMins = mins % 60;
82231
+ return remMins > 0 ? `${hours}h${remMins}m` : `${hours}h`;
82232
+ }
82233
+ const days = Math.floor(hours / 24);
82234
+ const remHours = hours % 24;
82235
+ return remHours > 0 ? `${days}d${remHours}h` : `${days}d`;
82236
+ }
82237
+ function renderBar(percent, width = 8) {
82238
+ const clamped = Math.max(0, Math.min(100, percent));
82239
+ const filled = Math.round(clamped / 100 * width);
82240
+ return `${"\u2501".repeat(filled)}${"\u2504".repeat(width - filled)}`;
82241
+ }
82242
+ function renderProviderUsage(theme, usage) {
82243
+ if (!usage) return void 0;
82244
+ const parts = [];
82245
+ if (usage.providerName) {
82246
+ const nameText = usage.providerName;
82247
+ parts.push(theme ? theme.fg("muted", nameText) : nameText);
82248
+ }
82249
+ const fiveHourBar = renderBar(usage.fiveHourPercent);
82250
+ const fiveHourRounded = Math.round(usage.fiveHourPercent);
82251
+ const fiveHourReset = formatResetTimer(usage.fiveHourResetAt);
82252
+ const fiveHourText = `5h ${fiveHourBar} ${fiveHourRounded}%${fiveHourReset ? " " + fiveHourReset : ""}`;
82253
+ const fiveHourColor = usage.fiveHourPercent >= 80 ? "error" : "accent";
82254
+ parts.push(theme ? theme.fg(fiveHourColor, fiveHourText) : fiveHourText);
82255
+ const weeklyBar = renderBar(usage.weeklyPercent);
82256
+ const weeklyRounded = Math.round(usage.weeklyPercent);
82257
+ const weeklyReset = formatResetTimer(usage.weeklyResetAt);
82258
+ const weeklyText = `Wk ${weeklyBar} ${weeklyRounded}%${weeklyReset ? " " + weeklyReset : ""}`;
82259
+ parts.push(theme ? theme.fg("dim", weeklyText) : weeklyText);
82260
+ if (typeof usage.copilotMonthlyPercent === "number" && Number.isFinite(usage.copilotMonthlyPercent)) {
82261
+ const monthlyRounded = Math.round(usage.copilotMonthlyPercent);
82262
+ const monthlyText = `Mo: ${monthlyRounded}%`;
82263
+ parts.push(theme ? theme.fg("dim", monthlyText) : monthlyText);
82264
+ }
82265
+ return parts.join(" ");
82266
+ }
82267
+
82268
+ // src/extension/crew-vibes/footer.ts
82269
+ function num(value) {
82270
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
82271
+ }
82272
+ function formatCwdForFooter(cwd, home) {
82273
+ if (!home) return cwd;
82274
+ const resolvedCwd = resolve20(cwd);
82275
+ const resolvedHome = resolve20(home);
82276
+ const rel = relative8(resolvedHome, resolvedCwd);
82277
+ const inside = rel === "" || rel !== ".." && !rel.startsWith(`..${sep9}`) && !isAbsolute10(rel);
82278
+ if (!inside) return cwd;
82279
+ return rel === "" ? "~" : `~${sep9}${rel}`;
82280
+ }
82281
+ function sanitizeStatusText(text) {
82282
+ return text.replace(/[\r\n\t]/g, " ").replace(/ +/g, " ").trim();
82283
+ }
82284
+ function asFooterData(value) {
82285
+ if (!value || typeof value !== "object") return void 0;
82286
+ const record = value;
82287
+ if (typeof record.getGitBranch !== "function" || typeof record.getExtensionStatuses !== "function" || typeof record.getAvailableProviderCount !== "function" || typeof record.onBranchChange !== "function") {
82288
+ return void 0;
82289
+ }
82290
+ return value;
82291
+ }
82292
+ function computeTotals(entries) {
82293
+ const totals = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
82294
+ for (const entry of entries) {
82295
+ const rec = entry;
82296
+ if (rec?.type !== "message" || rec.message?.role !== "assistant") continue;
82297
+ const usage = rec.message.usage;
82298
+ if (!usage) continue;
82299
+ totals.input += num(usage.input);
82300
+ totals.output += num(usage.output);
82301
+ totals.cacheRead += num(usage.cacheRead);
82302
+ totals.cacheWrite += num(usage.cacheWrite);
82303
+ totals.cost += num(usage.cost?.total);
82304
+ }
82305
+ return totals;
82306
+ }
82307
+ var CrewVibesFooter = class {
82308
+ theme;
82309
+ footerData;
82310
+ ctx;
82311
+ source;
82312
+ tui;
82313
+ unsubscribeBranch;
82314
+ constructor(deps) {
82315
+ this.theme = asCrewTheme(deps.theme);
82316
+ this.footerData = asFooterData(deps.footerData);
82317
+ this.ctx = deps.ctx;
82318
+ this.source = deps.source;
82319
+ this.tui = deps.tui;
82320
+ this.unsubscribeBranch = this.footerData ? this.footerData.onBranchChange(() => requestRenderTarget(this.tui)) : () => {
82321
+ };
82322
+ }
82323
+ invalidate() {
82324
+ }
82325
+ dispose() {
82326
+ this.unsubscribeBranch();
82327
+ }
82328
+ buildPwdLine(width) {
82329
+ const sm = this.ctx.sessionManager;
82330
+ let pwd = formatCwdForFooter(sm.getCwd(), process.env.HOME || process.env.USERPROFILE);
82331
+ const branch = this.footerData?.getGitBranch();
82332
+ if (branch) pwd = `${pwd} (${branch})`;
82333
+ const sessionName = sm.getSessionName?.();
82334
+ if (sessionName) pwd = `${pwd} \u2022 ${sessionName}`;
82335
+ return truncateToWidth(this.theme.fg("dim", pwd), width, this.theme.fg("dim", "..."));
82336
+ }
82337
+ buildStatsLine(width) {
82338
+ const theme = this.theme;
82339
+ const model = this.ctx.model;
82340
+ const totals = computeTotals(this.ctx.sessionManager.getEntries());
82341
+ const contextUsage = this.ctx.getContextUsage?.();
82342
+ const contextWindow2 = contextUsage?.contextWindow ?? model?.contextWindow ?? 0;
82343
+ const percentValue = contextUsage?.percent ?? 0;
82344
+ const percentKnown = contextUsage?.percent !== null && contextUsage?.percent !== void 0;
82345
+ const statsParts = [];
82346
+ if (totals.input) statsParts.push(`\u2191${formatCount(totals.input)}`);
82347
+ if (totals.output) statsParts.push(`\u2193${formatCount(totals.output)}`);
82348
+ if (totals.cacheRead) statsParts.push(`R${formatCount(totals.cacheRead)}`);
82349
+ if (totals.cacheWrite) statsParts.push(`W${formatCount(totals.cacheWrite)}`);
82350
+ const usingSubscription = !!(model && this.ctx.modelRegistry?.isUsingOAuth?.(this.ctx.model));
82351
+ if (totals.cost || usingSubscription) {
82352
+ statsParts.push(`$${totals.cost.toFixed(3)}${usingSubscription ? " (sub)" : ""}`);
82353
+ }
82354
+ const autoIndicator = " (auto)";
82355
+ const contextDisplay = percentKnown ? `${percentValue.toFixed(1)}%/${formatCount(contextWindow2)}${autoIndicator}` : `?/${formatCount(contextWindow2)}${autoIndicator}`;
82356
+ const contextColored = percentValue > 90 ? theme.fg("error", contextDisplay) : percentValue > 70 ? theme.fg("warning", contextDisplay) : contextDisplay;
82357
+ statsParts.push(contextColored);
82358
+ let statsLeft = statsParts.join(" ");
82359
+ let statsLeftWidth = visibleWidth(statsLeft);
82360
+ if (statsLeftWidth > width) {
82361
+ statsLeft = truncateToWidth(statsLeft, width, "...");
82362
+ statsLeftWidth = visibleWidth(statsLeft);
82363
+ }
82364
+ const minPadding = 2;
82365
+ const modelName = model?.id || "no-model";
82366
+ let rightSideWithoutProvider = modelName;
82367
+ if (model?.reasoning) {
82368
+ let level = this.source.getThinkingLevel();
82369
+ try {
82370
+ const ctx = this.ctx.sessionManager;
82371
+ if (typeof ctx.buildSessionContext === "function") {
82372
+ const resolved = ctx.buildSessionContext()?.thinkingLevel;
82373
+ if (resolved) level = resolved;
82374
+ }
82375
+ } catch {
82376
+ }
82377
+ const finalLevel = level || "off";
82378
+ rightSideWithoutProvider = finalLevel === "off" ? `${modelName} \u2022 thinking off` : `${modelName} \u2022 ${finalLevel}`;
82379
+ }
82380
+ let rightSide = rightSideWithoutProvider;
82381
+ const providerCount = this.footerData?.getAvailableProviderCount() ?? 0;
82382
+ if (providerCount > 1 && model?.provider) {
82383
+ rightSide = `(${model.provider}) ${rightSideWithoutProvider}`;
82384
+ if (statsLeftWidth + minPadding + visibleWidth(rightSide) > width) rightSide = rightSideWithoutProvider;
82385
+ }
82386
+ const rightSideWidth = visibleWidth(rightSide);
82387
+ let statsLine;
82388
+ if (statsLeftWidth + minPadding + rightSideWidth <= width) {
82389
+ statsLine = statsLeft + " ".repeat(width - statsLeftWidth - rightSideWidth) + rightSide;
82390
+ } else {
82391
+ const availableForRight = width - statsLeftWidth - minPadding;
82392
+ if (availableForRight > 0) {
82393
+ const truncatedRight = truncateToWidth(rightSide, availableForRight, "");
82394
+ const padding = " ".repeat(Math.max(0, width - statsLeftWidth - visibleWidth(truncatedRight)));
82395
+ statsLine = statsLeft + padding + truncatedRight;
82396
+ } else {
82397
+ statsLine = statsLeft;
82398
+ }
82399
+ }
82400
+ const dimStatsLeft = theme.fg("dim", statsLeft);
82401
+ const dimRemainder = theme.fg("dim", statsLine.slice(statsLeft.length));
82402
+ return dimStatsLeft + dimRemainder;
82403
+ }
82404
+ buildStatusLine(width) {
82405
+ if (!this.footerData) return void 0;
82406
+ const statuses = this.footerData.getExtensionStatuses();
82407
+ if (statuses.size === 0) return void 0;
82408
+ const joined = Array.from(statuses.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([, text]) => sanitizeStatusText(text)).join(" ");
82409
+ if (!joined) return void 0;
82410
+ return truncateToWidth(joined, width, this.theme.fg("dim", "..."));
82411
+ }
82412
+ rightAlign(text, width) {
82413
+ const w = visibleWidth(text);
82414
+ if (w >= width) return truncateToWidth(text, width, "\u2026");
82415
+ return " ".repeat(width - w) + text;
82416
+ }
82417
+ /** Capacity + provider quota. Uses the REAL render width, so the quota is
82418
+ * never chopped. When both do not fit on one line, wrap to two lines
82419
+ * (capacity above, quota right-aligned below) per the chosen behavior. */
82420
+ buildMeterLines(width) {
82421
+ const config = this.source.getConfig();
82422
+ if (!config.enabled) return [];
82423
+ const capText = config.capacity.enabled ? renderCapacity(this.theme, config.capacity, getCapacityUsage(this.ctx)) : void 0;
82424
+ const quotaText = config.capacity.providerUsage ? renderProviderUsage(this.theme, this.source.getQuotaUsage()) : void 0;
82425
+ if (!capText && !quotaText) return [];
82426
+ if (capText && !quotaText) return [truncateToWidth(capText, width, "\u2026")];
82427
+ if (!capText && quotaText) return [this.rightAlign(quotaText, width)];
82428
+ const cap = capText;
82429
+ const quota = quotaText;
82430
+ const capWidth = visibleWidth(cap);
82431
+ const quotaWidth = visibleWidth(quota);
82432
+ if (capWidth + 1 + quotaWidth <= width) {
82433
+ const pad2 = Math.max(1, width - capWidth - quotaWidth);
82434
+ return [cap + " ".repeat(pad2) + quota];
82435
+ }
82436
+ return [truncateToWidth(cap, width, "\u2026"), this.rightAlign(quota, width)];
82437
+ }
82438
+ render(width) {
82439
+ const lines = [this.buildPwdLine(width), this.buildStatsLine(width)];
82440
+ const statusLine = this.buildStatusLine(width);
82441
+ if (statusLine) lines.push(statusLine);
82442
+ lines.push(...this.buildMeterLines(width));
82443
+ return lines;
82444
+ }
82445
+ };
82446
+ function createCrewVibesFooter(deps) {
82447
+ return new CrewVibesFooter(deps);
82448
+ }
82449
+
81884
82450
  // src/extension/crew-vibes/provider-usage.ts
81885
- import { readFileSync as readFileSync76 } from "node:fs";
82451
+ import { readFileSync as readFileSync77 } from "node:fs";
81886
82452
  import { homedir as homedir12 } from "node:os";
81887
82453
  import { join as join77 } from "node:path";
81888
82454
  function withTimeout(ms, fn) {
@@ -81897,7 +82463,7 @@ function loadAnthropicToken() {
81897
82463
  const envToken = process.env.ANTHROPIC_OAUTH_TOKEN?.trim();
81898
82464
  if (envToken) return envToken;
81899
82465
  try {
81900
- const data2 = JSON.parse(readFileSync76(piAuthPath(), "utf8"));
82466
+ const data2 = JSON.parse(readFileSync77(piAuthPath(), "utf8"));
81901
82467
  const token = data2.anthropic?.access;
81902
82468
  return typeof token === "string" && token.length > 0 ? token : void 0;
81903
82469
  } catch {
@@ -81908,7 +82474,7 @@ function loadZaiToken() {
81908
82474
  const envKey = process.env.ZAI_API_KEY?.trim() || process.env.Z_AI_API_KEY?.trim();
81909
82475
  if (envKey) return envKey;
81910
82476
  try {
81911
- const data2 = JSON.parse(readFileSync76(piAuthPath(), "utf8"));
82477
+ const data2 = JSON.parse(readFileSync77(piAuthPath(), "utf8"));
81912
82478
  const key = data2["z-ai"]?.access || data2["z-ai"]?.key || data2.zai?.access || data2.zai?.key;
81913
82479
  return typeof key === "string" && key.length > 0 ? key : void 0;
81914
82480
  } catch {
@@ -81919,7 +82485,7 @@ function loadMinimaxToken() {
81919
82485
  const envKey = process.env.MINIMAX_API_KEY?.trim();
81920
82486
  if (envKey) return envKey;
81921
82487
  try {
81922
- const data2 = JSON.parse(readFileSync76(piAuthPath(), "utf8"));
82488
+ const data2 = JSON.parse(readFileSync77(piAuthPath(), "utf8"));
81923
82489
  const key = data2.minimax?.key;
81924
82490
  return typeof key === "string" && key.length > 0 ? key : void 0;
81925
82491
  } catch {
@@ -81940,7 +82506,7 @@ function loadLegacyCopilotToken() {
81940
82506
  const candidates = [join77(configHome, "github-copilot", "hosts.json"), join77(homedir12(), ".github-copilot", "hosts.json")];
81941
82507
  for (const hostsPath of candidates) {
81942
82508
  try {
81943
- const data2 = JSON.parse(readFileSync76(hostsPath, "utf8"));
82509
+ const data2 = JSON.parse(readFileSync77(hostsPath, "utf8"));
81944
82510
  if (!data2 || typeof data2 !== "object") continue;
81945
82511
  const normalized = {};
81946
82512
  for (const [host, entry] of Object.entries(data2)) {
@@ -81961,7 +82527,7 @@ function loadCopilotToken() {
81961
82527
  const envToken = (process.env.COPILOT_GITHUB_TOKEN || process.env.GH_TOKEN || process.env.GITHUB_TOKEN || "").trim();
81962
82528
  if (envToken) return envToken;
81963
82529
  try {
81964
- const data2 = JSON.parse(readFileSync76(piAuthPath(), "utf8"));
82530
+ const data2 = JSON.parse(readFileSync77(piAuthPath(), "utf8"));
81965
82531
  const piToken = data2["github-copilot"]?.refresh || data2["github-copilot"]?.access;
81966
82532
  if (typeof piToken === "string" && piToken.length > 0) return piToken;
81967
82533
  } catch {
@@ -82135,197 +82701,6 @@ async function fetchProviderUsage(maxAgeMs = 3e5, provider) {
82135
82701
  }
82136
82702
  }
82137
82703
 
82138
- // src/extension/crew-vibes/figures.ts
82139
- var BRAILLE_FRAMES = [
82140
- "\u280B ",
82141
- // ⠋
82142
- "\u2819 ",
82143
- // ⠙
82144
- "\u2839 ",
82145
- // ⠹
82146
- "\u2838 ",
82147
- // ⠸
82148
- "\u283C ",
82149
- // ⠼
82150
- "\u2834 ",
82151
- // ⠴
82152
- "\u2826 ",
82153
- // ⠦
82154
- "\u2827 ",
82155
- // ⠧
82156
- "\u2807 ",
82157
- // ⠇
82158
- "\u280F "
82159
- // ⠏
82160
- ];
82161
- var PUA_CREW_FRAMES = [
82162
- "\uE700 ",
82163
- "\uE701 ",
82164
- "\uE702 ",
82165
- "\uE703 ",
82166
- "\uE704 ",
82167
- "\uE705 ",
82168
- "\uE706 ",
82169
- "\uE707 ",
82170
- "\uE708 ",
82171
- "\uE709 ",
82172
- "\uE70A ",
82173
- "\uE70B ",
82174
- "\uE70C ",
82175
- "\uE70D ",
82176
- "\uE70E ",
82177
- "\uE70F "
82178
- ];
82179
- function crewFrames(style = "pua") {
82180
- if (style === "pua" && isWebTerminal()) return BRAILLE_FRAMES;
82181
- return style === "pua" ? PUA_CREW_FRAMES : BRAILLE_FRAMES;
82182
- }
82183
- function intervalForSpeed(config, speed) {
82184
- if (speed === null || !Number.isFinite(speed) || speed <= 0) return config.defaultIntervalMs;
82185
- return Math.max(config.minIntervalMs, Math.min(config.maxIntervalMs, Math.round(config.scale / speed)));
82186
- }
82187
- function capacityIndex(percent, levels = 6) {
82188
- if (percent === null || percent === void 0 || !Number.isFinite(percent)) return 0;
82189
- return Math.max(0, Math.min(levels - 1, Math.floor(Math.max(0, Math.min(100, percent)) / 100 * levels)));
82190
- }
82191
- function isDangerStage(index, levels) {
82192
- return index >= Math.max(0, levels - 2);
82193
- }
82194
-
82195
- // src/extension/crew-vibes/render.ts
82196
- function formatCount(value) {
82197
- if (value < 1e3) return value.toString();
82198
- if (value < 1e4) return `${(value / 1e3).toFixed(1)}k`;
82199
- if (value < 1e6) return `${Math.round(value / 1e3)}k`;
82200
- if (value < 1e7) return `${(value / 1e6).toFixed(1)}M`;
82201
- return `${Math.round(value / 1e6)}M`;
82202
- }
82203
- function asCrewTheme2(theme) {
82204
- if (theme && typeof theme === "object" && typeof theme.fg === "function") {
82205
- return theme;
82206
- }
82207
- return void 0;
82208
- }
82209
- function getCapacityUsage(ctx) {
82210
- const fn = ctx.getContextUsage;
82211
- const usage = typeof fn === "function" ? fn.call(ctx) : null;
82212
- return {
82213
- tokens: typeof usage?.tokens === "number" && Number.isFinite(usage.tokens) ? usage.tokens : null,
82214
- percent: typeof usage?.percent === "number" && Number.isFinite(usage.percent) ? usage.percent : null
82215
- };
82216
- }
82217
- function formatSpeed(config, speed) {
82218
- return speed === null ? `-- ${config.label}` : `${speed.toFixed(1)} ${config.label}`;
82219
- }
82220
- function renderSpeedFooter(theme, config, speed) {
82221
- const value = speed === null ? "--" : speed.toFixed(1);
82222
- const valueTone = speed === null ? "dim" : "accent";
82223
- const styled = theme ? `${theme.fg(valueTone, value)} ${theme.fg("dim", config.label)}` : `${value} ${config.label}`;
82224
- return styled;
82225
- }
82226
- function renderWorkingMessage(theme, config, speed) {
82227
- const left = "Working";
82228
- const speedText = theme ? `${theme.fg(speed === null ? "dim" : "accent", speed === null ? "--" : speed.toFixed(1))} ${theme.fg("dim", config.label)}` : `${speed === null ? "--" : speed.toFixed(1)} ${config.label}`;
82229
- return theme ? `${theme.fg("muted", left)} ${speedText}` : `${left} ${speedText}`;
82230
- }
82231
- function crewIndicatorFrames(theme) {
82232
- const frames = crewFrames();
82233
- if (!theme) return [...frames];
82234
- return frames.map((frame) => theme.fg("accent", frame));
82235
- }
82236
- function formatCapacityPrefix(config, usage) {
82237
- const display = config.tokenDisplay;
82238
- if (display === "off") return "";
82239
- if (display === "percentage") {
82240
- return `${usage.percent === null ? "?" : Math.round(Math.max(0, Math.min(999, usage.percent)))}% `;
82241
- }
82242
- return `${usage.tokens === null ? "?" : formatCount(usage.tokens)} `;
82243
- }
82244
- function colorStage(theme, index, levels, text) {
82245
- if (!theme || text.length === 0) return text;
82246
- return theme.fg(isDangerStage(index, levels) ? "error" : "success", text);
82247
- }
82248
- function renderCapacity(theme, config, usage) {
82249
- const icons = capacityIcons();
82250
- const levels = icons.length;
82251
- const index = capacityIndex(usage.percent, levels);
82252
- const icon = icons[index] ?? icons[0];
82253
- const label = config.labels[index] ?? config.labels[0];
82254
- const prefix = theme ? theme.fg("muted", formatCapacityPrefix(config, usage)) : formatCapacityPrefix(config, usage);
82255
- const coloredIcon = colorStage(theme, index, levels, icon);
82256
- const afterIcon = config.showLabel ? ` ${colorStage(theme, index, levels, label)}` : " ";
82257
- return `${prefix}${coloredIcon}${afterIcon}`;
82258
- }
82259
- function setSpeedStatus(ctx, config, text) {
82260
- if (!ctx?.hasUI) return;
82261
- if (!config.enabled || !config.speed.enabled || !config.speed.footer) {
82262
- ctx.ui.setStatus(SPEED_STATUS_ID, void 0);
82263
- return;
82264
- }
82265
- ctx.ui.setStatus(SPEED_STATUS_ID, text);
82266
- }
82267
- function setCapacityStatus(ctx, config, text) {
82268
- if (!ctx?.hasUI) return;
82269
- if (!config.enabled || !config.capacity.enabled) {
82270
- ctx.ui.setStatus(CAPACITY_STATUS_ID, void 0);
82271
- return;
82272
- }
82273
- ctx.ui.setStatus(CAPACITY_STATUS_ID, text);
82274
- }
82275
- function clearVibesStatus(ctx) {
82276
- if (!ctx?.hasUI) return;
82277
- ctx.ui.setStatus(SPEED_STATUS_ID, void 0);
82278
- ctx.ui.setStatus(CAPACITY_STATUS_ID, void 0);
82279
- ctx.ui.setStatus(PROVIDER_STATUS_ID, void 0);
82280
- if (ctx.ui.setWorkingIndicator) ctx.ui.setWorkingIndicator();
82281
- if (ctx.ui.setWorkingMessage) ctx.ui.setWorkingMessage();
82282
- }
82283
- function formatResetTimer(resetAt) {
82284
- if (!resetAt) return null;
82285
- const diffMs = new Date(resetAt).getTime() - Date.now();
82286
- if (diffMs < 0) return null;
82287
- const mins = Math.floor(diffMs / 6e4);
82288
- if (mins < 60) return `${mins}m`;
82289
- const hours = Math.floor(mins / 60);
82290
- if (hours < 48) {
82291
- const remMins = mins % 60;
82292
- return remMins > 0 ? `${hours}h${remMins}m` : `${hours}h`;
82293
- }
82294
- const days = Math.floor(hours / 24);
82295
- const remHours = hours % 24;
82296
- return remHours > 0 ? `${days}d${remHours}h` : `${days}d`;
82297
- }
82298
- function renderBar(percent, width = 8) {
82299
- const clamped = Math.max(0, Math.min(100, percent));
82300
- const filled = Math.round(clamped / 100 * width);
82301
- return `${"\u2501".repeat(filled)}${"\u2504".repeat(width - filled)}`;
82302
- }
82303
- function renderProviderUsage(theme, usage) {
82304
- if (!usage) return void 0;
82305
- const parts = [];
82306
- if (usage.providerName) {
82307
- const nameText = usage.providerName;
82308
- parts.push(theme ? theme.fg("muted", nameText) : nameText);
82309
- }
82310
- const fiveHourBar = renderBar(usage.fiveHourPercent);
82311
- const fiveHourRounded = Math.round(usage.fiveHourPercent);
82312
- const fiveHourReset = formatResetTimer(usage.fiveHourResetAt);
82313
- const fiveHourText = `5h ${fiveHourBar} ${fiveHourRounded}%${fiveHourReset ? " " + fiveHourReset : ""}`;
82314
- const fiveHourColor = usage.fiveHourPercent >= 80 ? "error" : "accent";
82315
- parts.push(theme ? theme.fg(fiveHourColor, fiveHourText) : fiveHourText);
82316
- const weeklyBar = renderBar(usage.weeklyPercent);
82317
- const weeklyRounded = Math.round(usage.weeklyPercent);
82318
- const weeklyReset = formatResetTimer(usage.weeklyResetAt);
82319
- const weeklyText = `Wk ${weeklyBar} ${weeklyRounded}%${weeklyReset ? " " + weeklyReset : ""}`;
82320
- parts.push(theme ? theme.fg("dim", weeklyText) : weeklyText);
82321
- if (typeof usage.copilotMonthlyPercent === "number" && Number.isFinite(usage.copilotMonthlyPercent)) {
82322
- const monthlyRounded = Math.round(usage.copilotMonthlyPercent);
82323
- const monthlyText = `Mo: ${monthlyRounded}%`;
82324
- parts.push(theme ? theme.fg("dim", monthlyText) : monthlyText);
82325
- }
82326
- return parts.join(" ");
82327
- }
82328
-
82329
82704
  // src/extension/crew-vibes/speed.ts
82330
82705
  var COMPACTION_THRESHOLD = 5e3;
82331
82706
  function estimateTokensFromDelta(text) {
@@ -82564,28 +82939,31 @@ function registerCrewVibes(pi) {
82564
82939
  let footerTimer;
82565
82940
  let capacityTimer;
82566
82941
  let providerTimer;
82567
- let lastProviderText;
82942
+ let lastProviderUsage = null;
82568
82943
  let currentProvider;
82569
- function visibleLen(text) {
82570
- return text.replace(/\x1b\[[0-9;]*m/g, "").length;
82571
- }
82572
- function spreadLine(left, right) {
82573
- const cols = process.stdout.columns || 120;
82574
- const padding = Math.max(2, cols - visibleLen(left) - visibleLen(right));
82575
- return left + "\xA0".repeat(padding) + right;
82576
- }
82944
+ let currentThinkingLevel;
82577
82945
  function themeOf(ctx) {
82578
82946
  return asCrewTheme2(ctx.hasUI ? ctx.ui.theme : void 0);
82579
82947
  }
82580
- function publishCapacity(ctx) {
82948
+ const footerSource = {
82949
+ getConfig: () => config,
82950
+ getQuotaUsage: () => lastProviderUsage,
82951
+ getThinkingLevel: () => currentThinkingLevel
82952
+ };
82953
+ function metersActive() {
82954
+ return config.enabled && (config.capacity.enabled || config.capacity.providerUsage);
82955
+ }
82956
+ function installFooter(ctx) {
82581
82957
  if (!ctx?.hasUI) return;
82582
- if (!config.enabled || !config.capacity.enabled) {
82583
- setCapacityStatus(ctx, config, void 0);
82958
+ if (!metersActive()) {
82959
+ setFooter(ctx, void 0);
82584
82960
  return;
82585
82961
  }
82586
- const capText = renderCapacity(themeOf(ctx), config.capacity, getCapacityUsage(ctx));
82587
- const combined = lastProviderText ? spreadLine(capText, lastProviderText) : capText;
82588
- setCapacityStatus(ctx, config, combined);
82962
+ setFooter(ctx, (tui, theme, footerData) => createCrewVibesFooter({ tui, theme, footerData, ctx, source: footerSource }));
82963
+ requestRender(ctx);
82964
+ }
82965
+ function refreshFooter(ctx) {
82966
+ if (ctx?.hasUI) requestRender(ctx);
82589
82967
  }
82590
82968
  function publishSpeedFooter(ctx, speed = footerAnimator.value()) {
82591
82969
  if (!config.enabled || !config.speed.enabled || !config.speed.footer) {
@@ -82660,29 +83038,28 @@ function registerCrewVibes(pi) {
82660
83038
  function startCapacityTimer(ctx) {
82661
83039
  if (capacityTimer) return;
82662
83040
  const interval = Math.max(250, config.capacity.refreshIntervalMs);
82663
- capacityTimer = setInterval(() => publishCapacity(ctx), interval);
83041
+ capacityTimer = setInterval(() => refreshFooter(ctx), interval);
82664
83042
  capacityTimer.unref?.();
82665
83043
  }
83044
+ async function fetchProviderAndRefresh(ctx) {
83045
+ if (!config.enabled || !config.capacity.providerUsage) {
83046
+ lastProviderUsage = null;
83047
+ refreshFooter(ctx);
83048
+ return;
83049
+ }
83050
+ try {
83051
+ lastProviderUsage = await fetchProviderUsage(config.capacity.providerRefreshMs, currentProvider);
83052
+ } catch {
83053
+ lastProviderUsage = null;
83054
+ }
83055
+ refreshFooter(ctx);
83056
+ }
82666
83057
  function startProviderTimer(ctx) {
82667
83058
  if (providerTimer) return;
82668
83059
  if (!config.capacity.providerUsage) return;
82669
83060
  const interval = Math.max(1e4, config.capacity.providerRefreshMs);
82670
- async function tick() {
82671
- if (!config.enabled || !config.capacity.providerUsage) {
82672
- stopProviderTimer();
82673
- return;
82674
- }
82675
- try {
82676
- const usage = await fetchProviderUsage(config.capacity.providerRefreshMs, currentProvider);
82677
- lastProviderText = renderProviderUsage(themeOf(ctx), usage);
82678
- publishCapacity(ctx);
82679
- } catch {
82680
- lastProviderText = void 0;
82681
- publishCapacity(ctx);
82682
- }
82683
- }
82684
- tick();
82685
- providerTimer = setInterval(tick, interval);
83061
+ fetchProviderAndRefresh(ctx);
83062
+ providerTimer = setInterval(() => fetchProviderAndRefresh(ctx), interval);
82686
83063
  providerTimer.unref?.();
82687
83064
  }
82688
83065
  function resetWorking(ctx) {
@@ -82698,11 +83075,13 @@ function registerCrewVibes(pi) {
82698
83075
  stopFooterTimer();
82699
83076
  stopCapacityTimer();
82700
83077
  stopProviderTimer();
83078
+ setFooter(ctx, void 0);
82701
83079
  clearVibesStatus(ctx);
82702
83080
  return;
82703
83081
  }
82704
- publishCapacity(ctx);
83082
+ installFooter(ctx);
82705
83083
  publishSpeedFooter(ctx);
83084
+ startCapacityTimer(ctx);
82706
83085
  if (config.capacity.providerUsage) startProviderTimer(ctx);
82707
83086
  }
82708
83087
  pi.on("session_start", (_event, ctx) => {
@@ -82717,11 +83096,13 @@ function registerCrewVibes(pi) {
82717
83096
  footerAnimator.reset(null);
82718
83097
  clearProviderUsageCache();
82719
83098
  currentProvider = ctx.model?.provider;
83099
+ currentThinkingLevel = void 0;
82720
83100
  if (!config.enabled) {
83101
+ setFooter(ctx, void 0);
82721
83102
  clearVibesStatus(ctx);
82722
83103
  return;
82723
83104
  }
82724
- publishCapacity(ctx);
83105
+ installFooter(ctx);
82725
83106
  publishSpeedFooter(ctx);
82726
83107
  startCapacityTimer(ctx);
82727
83108
  startProviderTimer(ctx);
@@ -82760,7 +83141,7 @@ function registerCrewVibes(pi) {
82760
83141
  });
82761
83142
  pi.on("message_end", (event, ctx) => {
82762
83143
  if (!isAssistantMessage(event.message)) return;
82763
- publishCapacity(ctx);
83144
+ refreshFooter(ctx);
82764
83145
  if (!config.enabled || !config.speed.enabled || !speedTracker.isStreaming) return;
82765
83146
  const completed = speedTracker.finishMessage(assistantUsageOutput(event.message) ?? 0, assistantStopReason(event.message));
82766
83147
  if (!completed) return;
@@ -82784,15 +83165,20 @@ function registerCrewVibes(pi) {
82784
83165
  pi.on("model_select", (event, ctx) => {
82785
83166
  currentProvider = event.model?.provider;
82786
83167
  clearProviderUsageCache();
82787
- publishCapacity(ctx);
83168
+ fetchProviderAndRefresh(ctx);
83169
+ });
83170
+ pi.on("thinking_level_select", (event, ctx) => {
83171
+ currentThinkingLevel = event.level;
83172
+ refreshFooter(ctx);
82788
83173
  });
82789
- pi.on("session_compact", (_event, ctx) => publishCapacity(ctx));
82790
- pi.on("session_tree", (_event, ctx) => publishCapacity(ctx));
83174
+ pi.on("session_compact", (_event, ctx) => refreshFooter(ctx));
83175
+ pi.on("session_tree", (_event, ctx) => refreshFooter(ctx));
82791
83176
  pi.on("session_shutdown", (_event, ctx) => {
82792
83177
  stopLiveTimer();
82793
83178
  stopFooterTimer();
82794
83179
  stopCapacityTimer();
82795
83180
  stopProviderTimer();
83181
+ setFooter(ctx, void 0);
82796
83182
  clearVibesStatus(ctx);
82797
83183
  });
82798
83184
  async function handleCommand(args, ctx) {
@@ -83687,7 +84073,7 @@ function registerSubagentTools(pi, subagentManager, options = {}) {
83687
84073
  savePersistedSubagentRecord(ctx.cwd, current2);
83688
84074
  break;
83689
84075
  }
83690
- await new Promise((resolve21) => setTimeout(resolve21, 1e3));
84076
+ await new Promise((resolve22) => setTimeout(resolve22, 1e3));
83691
84077
  current2 = refreshPersistedSubagentRecord(ctx, current2);
83692
84078
  if (!current2.runId) break;
83693
84079
  }
@@ -84329,7 +84715,7 @@ Subagent may need manual intervention.`
84329
84715
  if (!loaded) return true;
84330
84716
  return !loaded.tasks.some((t2) => t2.status === "running" || t2.status === "queued");
84331
84717
  };
84332
- while (!check()) await new Promise((resolve21) => setTimeout(resolve21, 500));
84718
+ while (!check()) await new Promise((resolve22) => setTimeout(resolve22, 500));
84333
84719
  };
84334
84720
  validatedRegistry.hasRunning = async (runId) => {
84335
84721
  const manifest = manifestCacheForRegistry.get(runId);