pi-crew 0.9.27 → 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.
Files changed (35) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/build-meta.json +164 -115
  3. package/dist/index.mjs +986 -555
  4. package/dist/index.mjs.map +4 -4
  5. package/docs/REVIEW-FINDINGS-2026-07-CORE.md +139 -0
  6. package/docs/REVIEW-FINDINGS-2026-07.md +125 -0
  7. package/docs/bugs/bug-quota-display-truncation.md +223 -0
  8. package/docs/stories/README.md +3 -1
  9. package/docs/stories/US-DEPS-major-upgrade.md +62 -0
  10. package/package.json +4 -4
  11. package/src/agents/agent-config.ts +2 -0
  12. package/src/agents/discover-agents.ts +4 -0
  13. package/src/config/config.ts +4 -0
  14. package/src/extension/crew-vibes/config.ts +1 -1
  15. package/src/extension/crew-vibes/figures.ts +1 -1
  16. package/src/extension/crew-vibes/footer.ts +292 -0
  17. package/src/extension/crew-vibes/index.ts +74 -70
  18. package/src/extension/crew-vibes/provider-usage.ts +119 -53
  19. package/src/extension/team-tool.ts +11 -0
  20. package/src/prompt/prompt-runtime.ts +65 -0
  21. package/src/runtime/child-pi.ts +58 -43
  22. package/src/runtime/cross-extension-rpc.ts +1 -1
  23. package/src/runtime/pi-args.ts +2 -0
  24. package/src/runtime/post-exit-stdio-guard.ts +22 -4
  25. package/src/runtime/stale-reconciler.ts +9 -4
  26. package/src/runtime/task-runner.ts +1 -0
  27. package/src/runtime/team-runner.ts +11 -16
  28. package/src/state/atomic-write.ts +46 -19
  29. package/src/state/event-log.ts +77 -24
  30. package/src/state/mailbox.ts +6 -3
  31. package/src/ui/pi-ui-compat.ts +11 -0
  32. package/src/utils/conflict-detect.ts +9 -3
  33. package/src/utils/paths.ts +7 -1
  34. package/src/worktree/cleanup.ts +19 -0
  35. package/src/worktree/worktree-manager.ts +145 -34
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();
@@ -16490,7 +16544,9 @@ function buildPiWorkerArgs(input) {
16490
16544
  PI_TEAMS_INHERIT_SKILLS: input.agent.inheritSkills ? "1" : "0",
16491
16545
  PI_TEAMS_DEPTH: String(parentDepth + 1),
16492
16546
  PI_TEAMS_MAX_DEPTH: String(maxDepth),
16493
- PI_TEAMS_ROLE: input.agent.name
16547
+ PI_TEAMS_ROLE: input.agent.name,
16548
+ // maxTokens cap for background workers — prompt-runtime reads this to cap API output
16549
+ ...input.agent.maxTokens ? { PI_CREW_MAX_OUTPUT: String(input.agent.maxTokens) } : {}
16494
16550
  },
16495
16551
  tempDir
16496
16552
  };
@@ -17305,7 +17361,7 @@ var init_subagent_manager = __esm({
17305
17361
  await record.promise.catch((error) => {
17306
17362
  logInternalError("subagent-manager.waitForRecord", error, `id=${id}`);
17307
17363
  });
17308
- else await new Promise((resolve21) => setTimeout(resolve21, 100));
17364
+ else await new Promise((resolve22) => setTimeout(resolve22, 100));
17309
17365
  }
17310
17366
  }
17311
17367
  setMaxConcurrent(value) {
@@ -17403,7 +17459,7 @@ var init_subagent_manager = __esm({
17403
17459
  while (record.runId && (record.status === "running" || record.status === "blocked")) {
17404
17460
  const loaded = loadRunManifestById(cwd, record.runId);
17405
17461
  if (!loaded) {
17406
- await new Promise((resolve21) => setTimeout(resolve21, this.pollIntervalMs));
17462
+ await new Promise((resolve22) => setTimeout(resolve22, this.pollIntervalMs));
17407
17463
  continue;
17408
17464
  }
17409
17465
  if (loaded.manifest.status === "completed") {
@@ -17436,7 +17492,7 @@ var init_subagent_manager = __esm({
17436
17492
  savePersistedSubagentRecord(cwd, record);
17437
17493
  return;
17438
17494
  }
17439
- await new Promise((resolve21) => setTimeout(resolve21, this.pollIntervalMs));
17495
+ await new Promise((resolve22) => setTimeout(resolve22, this.pollIntervalMs));
17440
17496
  }
17441
17497
  }
17442
17498
  scheduleBlockedTerminalPoll(cwd, record) {
@@ -17784,8 +17840,8 @@ var init_deduplicate_stage = __esm({
17784
17840
  const cur = lines[i2];
17785
17841
  if (cur !== out[out.length - 1]) out.push(cur);
17786
17842
  }
17787
- const sep9 = text.includes("\r\n") ? "\r\n" : "\n";
17788
- return out.join(sep9);
17843
+ const sep10 = text.includes("\r\n") ? "\r\n" : "\n";
17844
+ return out.join(sep10);
17789
17845
  }
17790
17846
  };
17791
17847
  DEDUPLICATE_STAGE = new DeduplicateStage();
@@ -18255,13 +18311,20 @@ function attachPostExitStdioGuard(child, options) {
18255
18311
  stderrEnded = true;
18256
18312
  if (stdoutEnded && stderrEnded) clearTimers();
18257
18313
  });
18258
- child.on("exit", () => {
18259
- exited = true;
18260
- armIdleTimer();
18314
+ const armHardTimer = () => {
18261
18315
  if (hardTimer) return;
18262
18316
  hardTimer = setTimeout(destroyUnendedStdio, hardMs);
18263
18317
  hardTimer.unref();
18264
- });
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);
18265
18328
  child.on("close", clearTimers);
18266
18329
  child.on("error", clearTimers);
18267
18330
  return clearTimers;
@@ -18293,14 +18356,20 @@ function clearHardKillTimer(pid) {
18293
18356
  clearTimeout(timer);
18294
18357
  childHardKillTimers.delete(pid);
18295
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
+ }
18296
18368
  function killProcessPid(pid) {
18297
18369
  if (!Number.isInteger(pid) || pid <= 0) return;
18298
18370
  try {
18299
18371
  if (process.platform === "win32") {
18300
- spawn("taskkill", ["/pid", String(pid), "/t", "/f"], {
18301
- stdio: "ignore",
18302
- windowsHide: true
18303
- });
18372
+ spawnTaskkillSafe(pid);
18304
18373
  const verifyTimer = setTimeout(() => {
18305
18374
  try {
18306
18375
  process.kill(pid, 0);
@@ -18310,10 +18379,7 @@ function killProcessPid(pid) {
18310
18379
  `pid=${pid}`
18311
18380
  );
18312
18381
  try {
18313
- spawn("taskkill", ["/pid", String(pid), "/t", "/f"], {
18314
- stdio: "ignore",
18315
- windowsHide: true
18316
- });
18382
+ spawnTaskkillSafe(pid);
18317
18383
  } catch {
18318
18384
  }
18319
18385
  } catch {
@@ -18713,9 +18779,19 @@ ${JSON.stringify({ type: "message_end", usage: { input: 10, output: 5, cost: 1e-
18713
18779
  skillPaths: input.skillPaths,
18714
18780
  role: input.role
18715
18781
  });
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
+ }
18716
18792
  const spawnSpec = getPiSpawnCommand(built.args);
18717
18793
  try {
18718
- return await new Promise((resolve21) => {
18794
+ return await new Promise((resolve22) => {
18719
18795
  for (const key of Object.keys(built.env)) {
18720
18796
  if (!key.startsWith("PI_CREW_") && !key.startsWith("PI_TEAMS_")) {
18721
18797
  throw new Error(
@@ -18886,29 +18962,23 @@ ${JSON.stringify({ type: "message_end", usage: { input: 10, output: 5, cost: 1e-
18886
18962
  turnCount += 1;
18887
18963
  if (maxTurns !== void 0 && !softLimitReached && turnCount >= maxTurns) {
18888
18964
  softLimitReached = true;
18889
- if (child.stdin?.writable) {
18890
- const steerPayload = JSON.stringify({
18891
- type: "steer",
18892
- message: "You have reached your turn limit. Wrap up immediately \u2014 provide your final answer now."
18893
- }) + "\n";
18894
- const writeSucceeded = child.stdin.write(steerPayload);
18895
- 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) {
18896
18976
  logInternalError(
18897
- "child-pi.steer-backpressure",
18898
- new Error(
18899
- "stdin write returned false (normal backpressure); steer buffered, worker NOT killed"
18900
- ),
18977
+ "child-pi.steer-write-failed",
18978
+ err2 instanceof Error ? err2 : new Error(String(err2)),
18901
18979
  `pid=${child.pid}`
18902
18980
  );
18903
18981
  }
18904
- } else {
18905
- logInternalError(
18906
- "child-pi.steer-not-writable",
18907
- new Error(
18908
- "stdin not writable when attempting steer injection (worker may be done); worker NOT killed"
18909
- ),
18910
- `pid=${child.pid}`
18911
- );
18912
18982
  }
18913
18983
  } else if (maxTurns !== void 0 && softLimitReached && turnCount >= maxTurns + (graceTurns ?? 5)) {
18914
18984
  try {
@@ -19039,7 +19109,7 @@ ${JSON.stringify({ type: "message_end", usage: { input: 10, output: 5, cost: 1e-
19039
19109
  cleanupErrors.push(error instanceof Error ? error.message : String(error));
19040
19110
  }
19041
19111
  try {
19042
- resolve21({
19112
+ resolve22({
19043
19113
  ...result4,
19044
19114
  rawFinalText: lineObserver.getRawFinalText(),
19045
19115
  intermediateFindings: lineObserver.getIntermediateFindings(),
@@ -19348,7 +19418,9 @@ var init_child_pi = __esm({
19348
19418
  "PI_TEAMS_INHERIT_SKILLS",
19349
19419
  "PI_TEAMS_PI_BIN",
19350
19420
  "PI_TEAMS_MOCK_CHILD_PI",
19351
- "PI_CREW_ALLOW_MOCK"
19421
+ "PI_CREW_ALLOW_MOCK",
19422
+ "PI_CREW_MAX_OUTPUT",
19423
+ "PI_CREW_STEERING_FILE"
19352
19424
  ];
19353
19425
  ChildPiLineObserver = class _ChildPiLineObserver {
19354
19426
  buffer = "";
@@ -19551,6 +19623,12 @@ function setExtensionWidget(ctx, key, content, options) {
19551
19623
  const { persist: _persist, ...widgetOptions } = options ?? {};
19552
19624
  ctx.ui.setWidget(key, content, widgetOptions);
19553
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
+ }
19554
19632
  function showCustom(ctx, factory, options) {
19555
19633
  const custom = ctx.ui.custom;
19556
19634
  return custom(factory, options);
@@ -19690,7 +19768,7 @@ var init_cancellation_token = __esm({
19690
19768
  wait(ms) {
19691
19769
  this.throwIfCancelled();
19692
19770
  if (ms <= 0) return Promise.resolve();
19693
- return new Promise((resolve21, reject) => {
19771
+ return new Promise((resolve22, reject) => {
19694
19772
  let timeout;
19695
19773
  const cleanup = () => {
19696
19774
  if (timeout) clearTimeout(timeout);
@@ -19702,7 +19780,7 @@ var init_cancellation_token = __esm({
19702
19780
  };
19703
19781
  timeout = setTimeout(() => {
19704
19782
  cleanup();
19705
- resolve21();
19783
+ resolve22();
19706
19784
  }, ms);
19707
19785
  this.signal.addEventListener("abort", onAbort, { once: true });
19708
19786
  });
@@ -22157,6 +22235,10 @@ function parseAgentFile(filePath, source) {
22157
22235
  const n = Number.parseInt(frontmatter.maxTurns, 10);
22158
22236
  return Number.isFinite(n) && n > 0 ? n : void 0;
22159
22237
  })(),
22238
+ maxTokens: (() => {
22239
+ const n = Number.parseInt(frontmatter.maxTokens, 10);
22240
+ return Number.isFinite(n) && n > 0 ? n : void 0;
22241
+ })(),
22160
22242
  effort: frontmatter.effort === "low" || frontmatter.effort === "medium" || frontmatter.effort === "high" ? frontmatter.effort : void 0,
22161
22243
  disabled: frontmatter.disabled === "true" || frontmatter.enabled === "false",
22162
22244
  routing: triggers || useWhen || avoidWhen || cost || category ? { triggers, useWhen, avoidWhen, cost, category } : void 0
@@ -23632,8 +23714,8 @@ function taskMailboxDir(manifest, taskId, create = false) {
23632
23714
  const tasksRoot = safeMailboxTasksRoot(manifest, create);
23633
23715
  const normalizedTaskId = safeTaskId(taskId);
23634
23716
  const resolved = path32.resolve(tasksRoot, normalizedTaskId);
23635
- const relative8 = path32.relative(tasksRoot, resolved);
23636
- 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}`);
23637
23719
  if (create) fs37.mkdirSync(resolved, { recursive: true });
23638
23720
  if (fs37.existsSync(resolved) && fs37.lstatSync(resolved).isSymbolicLink()) throw new Error(`Invalid mailbox task directory: ${resolved}`);
23639
23721
  return resolved;
@@ -23867,8 +23949,8 @@ function appendMailboxMessage(manifest, message) {
23867
23949
  `,
23868
23950
  "utf-8"
23869
23951
  );
23952
+ rotateMailboxFileIfNeeded(mailboxFile(manifest, complete.direction, complete.taskId));
23870
23953
  });
23871
- rotateMailboxFileIfNeeded(mailboxFile(manifest, complete.direction, complete.taskId));
23872
23954
  withFileLockSync(deliveryFile(manifest, true), () => {
23873
23955
  const delivery = readDeliveryState(manifest);
23874
23956
  delivery.messages[complete.id] = complete.status;
@@ -26078,9 +26160,9 @@ async function isLiveSessionRuntimeAvailable(timeoutMs = 1500, env = process.env
26078
26160
  try {
26079
26161
  return await Promise.race([
26080
26162
  probe(),
26081
- new Promise((resolve21) => {
26163
+ new Promise((resolve22) => {
26082
26164
  timer = setTimeout(
26083
- () => resolve21({
26165
+ () => resolve22({
26084
26166
  available: false,
26085
26167
  reason: `Timed out probing optional Pi SDK live-session runtime after ${timeoutMs}ms.`
26086
26168
  }),
@@ -28623,9 +28705,9 @@ var require_stringifyNumber = __commonJS({
28623
28705
  function stringifyNumber({ format: format2, minFractionDigits, tag, value }) {
28624
28706
  if (typeof value === "bigint")
28625
28707
  return String(value);
28626
- const num = typeof value === "number" ? value : Number(value);
28627
- if (!isFinite(num))
28628
- 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";
28629
28711
  let n = Object.is(value, -0) ? "-0" : JSON.stringify(value);
28630
28712
  if (!format2 && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^-?\d/.test(n) && !n.includes("e")) {
28631
28713
  let i2 = n.indexOf(".");
@@ -28665,8 +28747,8 @@ var require_float = __commonJS({
28665
28747
  test: /^[-+]?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)[eE][-+]?[0-9]+$/,
28666
28748
  resolve: (str) => parseFloat(str),
28667
28749
  stringify(node) {
28668
- const num = Number(node.value);
28669
- return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node);
28750
+ const num2 = Number(node.value);
28751
+ return isFinite(num2) ? num2.toExponential() : stringifyNumber.stringifyNumber(node);
28670
28752
  }
28671
28753
  };
28672
28754
  var float = {
@@ -29105,8 +29187,8 @@ var require_float2 = __commonJS({
29105
29187
  test: /^[-+]?(?:[0-9][0-9_]*)?(?:\.[0-9_]*)?[eE][-+]?[0-9]+$/,
29106
29188
  resolve: (str) => parseFloat(str.replace(/_/g, "")),
29107
29189
  stringify(node) {
29108
- const num = Number(node.value);
29109
- return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node);
29190
+ const num2 = Number(node.value);
29191
+ return isFinite(num2) ? num2.toExponential() : stringifyNumber.stringifyNumber(node);
29110
29192
  }
29111
29193
  };
29112
29194
  var float = {
@@ -29308,23 +29390,23 @@ var require_timestamp = __commonJS({
29308
29390
  function parseSexagesimal(str, asBigInt) {
29309
29391
  const sign = str[0];
29310
29392
  const parts = sign === "-" || sign === "+" ? str.substring(1) : str;
29311
- const num = (n) => asBigInt ? BigInt(n) : Number(n);
29312
- const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 * num(60) + num(p), num(0));
29313
- 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;
29314
29396
  }
29315
29397
  function stringifySexagesimal(node) {
29316
29398
  let { value } = node;
29317
- let num = (n) => n;
29399
+ let num2 = (n) => n;
29318
29400
  if (typeof value === "bigint")
29319
- num = (n) => BigInt(n);
29401
+ num2 = (n) => BigInt(n);
29320
29402
  else if (isNaN(value) || !isFinite(value))
29321
29403
  return stringifyNumber.stringifyNumber(node);
29322
29404
  let sign = "";
29323
29405
  if (value < 0) {
29324
29406
  sign = "-";
29325
- value *= num(-1);
29407
+ value *= num2(-1);
29326
29408
  }
29327
- const _60 = num(60);
29409
+ const _60 = num2(60);
29328
29410
  const parts = [value % _60];
29329
29411
  if (value < 60) {
29330
29412
  parts.unshift(0);
@@ -30241,10 +30323,10 @@ var require_resolve_block_map = __commonJS({
30241
30323
  let offset2 = bm.offset;
30242
30324
  let commentEnd = null;
30243
30325
  for (const collItem of bm.items) {
30244
- const { start, key, sep: sep9, value } = collItem;
30326
+ const { start, key, sep: sep10, value } = collItem;
30245
30327
  const keyProps = resolveProps.resolveProps(start, {
30246
30328
  indicator: "explicit-key-ind",
30247
- next: key ?? sep9?.[0],
30329
+ next: key ?? sep10?.[0],
30248
30330
  offset: offset2,
30249
30331
  onError,
30250
30332
  parentIndent: bm.indent,
@@ -30258,7 +30340,7 @@ var require_resolve_block_map = __commonJS({
30258
30340
  else if ("indent" in key && key.indent !== bm.indent)
30259
30341
  onError(offset2, "BAD_INDENT", startColMsg);
30260
30342
  }
30261
- if (!keyProps.anchor && !keyProps.tag && !sep9) {
30343
+ if (!keyProps.anchor && !keyProps.tag && !sep10) {
30262
30344
  commentEnd = keyProps.end;
30263
30345
  if (keyProps.comment) {
30264
30346
  if (map3.comment)
@@ -30282,7 +30364,7 @@ var require_resolve_block_map = __commonJS({
30282
30364
  ctx.atKey = false;
30283
30365
  if (utilMapIncludes.mapIncludes(ctx, map3.items, keyNode))
30284
30366
  onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
30285
- const valueProps = resolveProps.resolveProps(sep9 ?? [], {
30367
+ const valueProps = resolveProps.resolveProps(sep10 ?? [], {
30286
30368
  indicator: "map-value-ind",
30287
30369
  next: value,
30288
30370
  offset: keyNode.range[2],
@@ -30298,7 +30380,7 @@ var require_resolve_block_map = __commonJS({
30298
30380
  if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024)
30299
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");
30300
30382
  }
30301
- 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);
30302
30384
  if (ctx.schema.compat)
30303
30385
  utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError);
30304
30386
  offset2 = valueNode.range[2];
@@ -30389,7 +30471,7 @@ var require_resolve_end = __commonJS({
30389
30471
  let comment = "";
30390
30472
  if (end) {
30391
30473
  let hasSpace = false;
30392
- let sep9 = "";
30474
+ let sep10 = "";
30393
30475
  for (const token of end) {
30394
30476
  const { source, type } = token;
30395
30477
  switch (type) {
@@ -30403,13 +30485,13 @@ var require_resolve_end = __commonJS({
30403
30485
  if (!comment)
30404
30486
  comment = cb;
30405
30487
  else
30406
- comment += sep9 + cb;
30407
- sep9 = "";
30488
+ comment += sep10 + cb;
30489
+ sep10 = "";
30408
30490
  break;
30409
30491
  }
30410
30492
  case "newline":
30411
30493
  if (comment)
30412
- sep9 += source;
30494
+ sep10 += source;
30413
30495
  hasSpace = true;
30414
30496
  break;
30415
30497
  default:
@@ -30452,18 +30534,18 @@ var require_resolve_flow_collection = __commonJS({
30452
30534
  let offset2 = fc.offset + fc.start.source.length;
30453
30535
  for (let i2 = 0; i2 < fc.items.length; ++i2) {
30454
30536
  const collItem = fc.items[i2];
30455
- const { start, key, sep: sep9, value } = collItem;
30537
+ const { start, key, sep: sep10, value } = collItem;
30456
30538
  const props = resolveProps.resolveProps(start, {
30457
30539
  flow: fcName,
30458
30540
  indicator: "explicit-key-ind",
30459
- next: key ?? sep9?.[0],
30541
+ next: key ?? sep10?.[0],
30460
30542
  offset: offset2,
30461
30543
  onError,
30462
30544
  parentIndent: fc.indent,
30463
30545
  startOnNewline: false
30464
30546
  });
30465
30547
  if (!props.found) {
30466
- if (!props.anchor && !props.tag && !sep9 && !value) {
30548
+ if (!props.anchor && !props.tag && !sep10 && !value) {
30467
30549
  if (i2 === 0 && props.comma)
30468
30550
  onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`);
30469
30551
  else if (i2 < fc.items.length - 1)
@@ -30517,8 +30599,8 @@ var require_resolve_flow_collection = __commonJS({
30517
30599
  }
30518
30600
  }
30519
30601
  }
30520
- if (!isMap && !sep9 && !props.found) {
30521
- 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);
30522
30604
  coll.items.push(valueNode);
30523
30605
  offset2 = valueNode.range[2];
30524
30606
  if (isBlock(value))
@@ -30530,7 +30612,7 @@ var require_resolve_flow_collection = __commonJS({
30530
30612
  if (isBlock(key))
30531
30613
  onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg);
30532
30614
  ctx.atKey = false;
30533
- const valueProps = resolveProps.resolveProps(sep9 ?? [], {
30615
+ const valueProps = resolveProps.resolveProps(sep10 ?? [], {
30534
30616
  flow: fcName,
30535
30617
  indicator: "map-value-ind",
30536
30618
  next: value,
@@ -30541,8 +30623,8 @@ var require_resolve_flow_collection = __commonJS({
30541
30623
  });
30542
30624
  if (valueProps.found) {
30543
30625
  if (!isMap && !props.found && ctx.options.strict) {
30544
- if (sep9)
30545
- for (const st of sep9) {
30626
+ if (sep10)
30627
+ for (const st of sep10) {
30546
30628
  if (st === valueProps.found)
30547
30629
  break;
30548
30630
  if (st.type === "newline") {
@@ -30559,7 +30641,7 @@ var require_resolve_flow_collection = __commonJS({
30559
30641
  else
30560
30642
  onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`);
30561
30643
  }
30562
- 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;
30563
30645
  if (valueNode) {
30564
30646
  if (isBlock(value))
30565
30647
  onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg);
@@ -30739,7 +30821,7 @@ var require_resolve_block_scalar = __commonJS({
30739
30821
  chompStart = i2 + 1;
30740
30822
  }
30741
30823
  let value = "";
30742
- let sep9 = "";
30824
+ let sep10 = "";
30743
30825
  let prevMoreIndented = false;
30744
30826
  for (let i2 = 0; i2 < contentStart; ++i2)
30745
30827
  value += lines[i2][0].slice(trimIndent) + "\n";
@@ -30756,24 +30838,24 @@ var require_resolve_block_scalar = __commonJS({
30756
30838
  indent = "";
30757
30839
  }
30758
30840
  if (type === Scalar.Scalar.BLOCK_LITERAL) {
30759
- value += sep9 + indent.slice(trimIndent) + content;
30760
- sep9 = "\n";
30841
+ value += sep10 + indent.slice(trimIndent) + content;
30842
+ sep10 = "\n";
30761
30843
  } else if (indent.length > trimIndent || content[0] === " ") {
30762
- if (sep9 === " ")
30763
- sep9 = "\n";
30764
- else if (!prevMoreIndented && sep9 === "\n")
30765
- sep9 = "\n\n";
30766
- value += sep9 + indent.slice(trimIndent) + content;
30767
- 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";
30768
30850
  prevMoreIndented = true;
30769
30851
  } else if (content === "") {
30770
- if (sep9 === "\n")
30852
+ if (sep10 === "\n")
30771
30853
  value += "\n";
30772
30854
  else
30773
- sep9 = "\n";
30855
+ sep10 = "\n";
30774
30856
  } else {
30775
- value += sep9 + content;
30776
- sep9 = " ";
30857
+ value += sep10 + content;
30858
+ sep10 = " ";
30777
30859
  prevMoreIndented = false;
30778
30860
  }
30779
30861
  }
@@ -30955,25 +31037,25 @@ var require_resolve_flow_scalar = __commonJS({
30955
31037
  if (!match)
30956
31038
  return source;
30957
31039
  let res = match[1];
30958
- let sep9 = " ";
31040
+ let sep10 = " ";
30959
31041
  let pos = first.lastIndex;
30960
31042
  line4.lastIndex = pos;
30961
31043
  while (match = line4.exec(source)) {
30962
31044
  if (match[1] === "") {
30963
- if (sep9 === "\n")
30964
- res += sep9;
31045
+ if (sep10 === "\n")
31046
+ res += sep10;
30965
31047
  else
30966
- sep9 = "\n";
31048
+ sep10 = "\n";
30967
31049
  } else {
30968
- res += sep9 + match[1];
30969
- sep9 = " ";
31050
+ res += sep10 + match[1];
31051
+ sep10 = " ";
30970
31052
  }
30971
31053
  pos = line4.lastIndex;
30972
31054
  }
30973
31055
  const last = /[ \t]*(.*)/sy;
30974
31056
  last.lastIndex = pos;
30975
31057
  match = last.exec(source);
30976
- return res + sep9 + (match?.[1] ?? "");
31058
+ return res + sep10 + (match?.[1] ?? "");
30977
31059
  }
30978
31060
  function doubleQuotedValue(source, onError) {
30979
31061
  let res = "";
@@ -31783,14 +31865,14 @@ var require_cst_stringify = __commonJS({
31783
31865
  }
31784
31866
  }
31785
31867
  }
31786
- function stringifyItem({ start, key, sep: sep9, value }) {
31868
+ function stringifyItem({ start, key, sep: sep10, value }) {
31787
31869
  let res = "";
31788
31870
  for (const st of start)
31789
31871
  res += st.source;
31790
31872
  if (key)
31791
31873
  res += stringifyToken(key);
31792
- if (sep9)
31793
- for (const st of sep9)
31874
+ if (sep10)
31875
+ for (const st of sep10)
31794
31876
  res += st.source;
31795
31877
  if (value)
31796
31878
  res += stringifyToken(value);
@@ -32957,18 +33039,18 @@ var require_parser = __commonJS({
32957
33039
  if (this.type === "map-value-ind") {
32958
33040
  const prev = getPrevProps(this.peek(2));
32959
33041
  const start = getFirstKeyStartProps(prev);
32960
- let sep9;
33042
+ let sep10;
32961
33043
  if (scalar.end) {
32962
- sep9 = scalar.end;
32963
- sep9.push(this.sourceToken);
33044
+ sep10 = scalar.end;
33045
+ sep10.push(this.sourceToken);
32964
33046
  delete scalar.end;
32965
33047
  } else
32966
- sep9 = [this.sourceToken];
33048
+ sep10 = [this.sourceToken];
32967
33049
  const map3 = {
32968
33050
  type: "block-map",
32969
33051
  offset: scalar.offset,
32970
33052
  indent: scalar.indent,
32971
- items: [{ start, key: scalar, sep: sep9 }]
33053
+ items: [{ start, key: scalar, sep: sep10 }]
32972
33054
  };
32973
33055
  this.onKeyLine = true;
32974
33056
  this.stack[this.stack.length - 1] = map3;
@@ -33121,15 +33203,15 @@ var require_parser = __commonJS({
33121
33203
  } else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) {
33122
33204
  const start2 = getFirstKeyStartProps(it.start);
33123
33205
  const key = it.key;
33124
- const sep9 = it.sep;
33125
- sep9.push(this.sourceToken);
33206
+ const sep10 = it.sep;
33207
+ sep10.push(this.sourceToken);
33126
33208
  delete it.key;
33127
33209
  delete it.sep;
33128
33210
  this.stack.push({
33129
33211
  type: "block-map",
33130
33212
  offset: this.offset,
33131
33213
  indent: this.indent,
33132
- items: [{ start: start2, key, sep: sep9 }]
33214
+ items: [{ start: start2, key, sep: sep10 }]
33133
33215
  });
33134
33216
  } else if (start.length > 0) {
33135
33217
  it.sep = it.sep.concat(start, this.sourceToken);
@@ -33323,13 +33405,13 @@ var require_parser = __commonJS({
33323
33405
  const prev = getPrevProps(parent);
33324
33406
  const start = getFirstKeyStartProps(prev);
33325
33407
  fixFlowSeqItems(fc);
33326
- const sep9 = fc.end.splice(1, fc.end.length);
33327
- sep9.push(this.sourceToken);
33408
+ const sep10 = fc.end.splice(1, fc.end.length);
33409
+ sep10.push(this.sourceToken);
33328
33410
  const map3 = {
33329
33411
  type: "block-map",
33330
33412
  offset: fc.offset,
33331
33413
  indent: fc.indent,
33332
- items: [{ start, key: fc, sep: sep9 }]
33414
+ items: [{ start, key: fc, sep: sep10 }]
33333
33415
  };
33334
33416
  this.onKeyLine = true;
33335
33417
  this.stack[this.stack.length - 1] = map3;
@@ -38236,7 +38318,7 @@ var require_compile = __commonJS({
38236
38318
  const schOrFunc = root.refs[ref2];
38237
38319
  if (schOrFunc)
38238
38320
  return schOrFunc;
38239
- let _sch = resolve21.call(this, root, ref2);
38321
+ let _sch = resolve22.call(this, root, ref2);
38240
38322
  if (_sch === void 0) {
38241
38323
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref2];
38242
38324
  const { schemaId } = this.opts;
@@ -38263,7 +38345,7 @@ var require_compile = __commonJS({
38263
38345
  function sameSchemaEnv(s1, s2) {
38264
38346
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
38265
38347
  }
38266
- function resolve21(root, ref2) {
38348
+ function resolve22(root, ref2) {
38267
38349
  let sch;
38268
38350
  while (typeof (sch = this.refs[ref2]) == "string")
38269
38351
  ref2 = sch;
@@ -38894,55 +38976,55 @@ var require_fast_uri = __commonJS({
38894
38976
  }
38895
38977
  return uri;
38896
38978
  }
38897
- function resolve21(baseURI, relativeURI, options) {
38979
+ function resolve22(baseURI, relativeURI, options) {
38898
38980
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
38899
38981
  const resolved = resolveComponent(parse6(baseURI, schemelessOptions), parse6(relativeURI, schemelessOptions), schemelessOptions, true);
38900
38982
  schemelessOptions.skipEscape = true;
38901
38983
  return serialize(resolved, schemelessOptions);
38902
38984
  }
38903
- function resolveComponent(base, relative8, options, skipNormalization) {
38985
+ function resolveComponent(base, relative9, options, skipNormalization) {
38904
38986
  const target = {};
38905
38987
  if (!skipNormalization) {
38906
38988
  base = parse6(serialize(base, options), options);
38907
- relative8 = parse6(serialize(relative8, options), options);
38989
+ relative9 = parse6(serialize(relative9, options), options);
38908
38990
  }
38909
38991
  options = options || {};
38910
- if (!options.tolerant && relative8.scheme) {
38911
- target.scheme = relative8.scheme;
38912
- target.userinfo = relative8.userinfo;
38913
- target.host = relative8.host;
38914
- target.port = relative8.port;
38915
- target.path = removeDotSegments(relative8.path || "");
38916
- 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;
38917
38999
  } else {
38918
- if (relative8.userinfo !== void 0 || relative8.host !== void 0 || relative8.port !== void 0) {
38919
- target.userinfo = relative8.userinfo;
38920
- target.host = relative8.host;
38921
- target.port = relative8.port;
38922
- target.path = removeDotSegments(relative8.path || "");
38923
- 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;
38924
39006
  } else {
38925
- if (!relative8.path) {
39007
+ if (!relative9.path) {
38926
39008
  target.path = base.path;
38927
- if (relative8.query !== void 0) {
38928
- target.query = relative8.query;
39009
+ if (relative9.query !== void 0) {
39010
+ target.query = relative9.query;
38929
39011
  } else {
38930
39012
  target.query = base.query;
38931
39013
  }
38932
39014
  } else {
38933
- if (relative8.path[0] === "/") {
38934
- target.path = removeDotSegments(relative8.path);
39015
+ if (relative9.path[0] === "/") {
39016
+ target.path = removeDotSegments(relative9.path);
38935
39017
  } else {
38936
39018
  if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
38937
- target.path = "/" + relative8.path;
39019
+ target.path = "/" + relative9.path;
38938
39020
  } else if (!base.path) {
38939
- target.path = relative8.path;
39021
+ target.path = relative9.path;
38940
39022
  } else {
38941
- 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;
38942
39024
  }
38943
39025
  target.path = removeDotSegments(target.path);
38944
39026
  }
38945
- target.query = relative8.query;
39027
+ target.query = relative9.query;
38946
39028
  }
38947
39029
  target.userinfo = base.userinfo;
38948
39030
  target.host = base.host;
@@ -38950,7 +39032,7 @@ var require_fast_uri = __commonJS({
38950
39032
  }
38951
39033
  target.scheme = base.scheme;
38952
39034
  }
38953
- target.fragment = relative8.fragment;
39035
+ target.fragment = relative9.fragment;
38954
39036
  return target;
38955
39037
  }
38956
39038
  function equal(uriA, uriB, options) {
@@ -39152,7 +39234,7 @@ var require_fast_uri = __commonJS({
39152
39234
  var fastUri = {
39153
39235
  SCHEMES,
39154
39236
  normalize: normalize2,
39155
- resolve: resolve21,
39237
+ resolve: resolve22,
39156
39238
  resolveComponent,
39157
39239
  equal,
39158
39240
  serialize,
@@ -42629,7 +42711,7 @@ ${input.prompt}` : input.prompt;
42629
42711
  );
42630
42712
  } catch {
42631
42713
  }
42632
- await new Promise((resolve21) => setTimeout(resolve21, DEFAULT_LIVE_SESSION.yieldPollIntervalMs));
42714
+ await new Promise((resolve22) => setTimeout(resolve22, DEFAULT_LIVE_SESSION.yieldPollIntervalMs));
42633
42715
  if (customToolYieldResolved && customToolYieldResult) {
42634
42716
  yieldResult = customToolYieldResult;
42635
42717
  } else {
@@ -42673,7 +42755,7 @@ ${input.prompt}` : input.prompt;
42673
42755
  }
42674
42756
  }
42675
42757
  const pollInterval = DEFAULT_LIVE_SESSION.yieldPollIntervalMs;
42676
- await new Promise((resolve21) => setTimeout(resolve21, pollInterval));
42758
+ await new Promise((resolve22) => setTimeout(resolve22, pollInterval));
42677
42759
  if (customToolYieldResolved && customToolYieldResult) {
42678
42760
  yieldResult = customToolYieldResult;
42679
42761
  break;
@@ -44104,13 +44186,13 @@ var init_checkpoint = __esm({
44104
44186
  import * as fs51 from "node:fs";
44105
44187
  import * as path43 from "node:path";
44106
44188
  function registerRunPromise(runId) {
44107
- let resolve21;
44189
+ let resolve22;
44108
44190
  let reject;
44109
44191
  const promise = new Promise((res, rej) => {
44110
- resolve21 = res;
44192
+ resolve22 = res;
44111
44193
  reject = rej;
44112
44194
  });
44113
- const entry = { promise, resolve: resolve21, reject };
44195
+ const entry = { promise, resolve: resolve22, reject };
44114
44196
  activeRunPromises.set(runId, entry);
44115
44197
  return entry;
44116
44198
  }
@@ -44635,11 +44717,11 @@ function readSkillMarkdown(cwd, name) {
44635
44717
  if (cached) skillReadCache.delete(cacheKey2);
44636
44718
  for (const entry of candidateSkillDirs(cwd)) {
44637
44719
  try {
44638
- const relative8 = path44.join(name, "SKILL.md");
44639
- const contained = resolveContainedPath(entry.root, relative8);
44720
+ const relative9 = path44.join(name, "SKILL.md");
44721
+ const contained = resolveContainedPath(entry.root, relative9);
44640
44722
  if (!fs52.existsSync(contained)) continue;
44641
44723
  if (fs52.lstatSync(contained).isSymbolicLink()) continue;
44642
- const filePath = resolveRealContainedPath(entry.root, relative8);
44724
+ const filePath = resolveRealContainedPath(entry.root, relative9);
44643
44725
  const stat2 = fs52.statSync(filePath);
44644
44726
  return rememberSkill(cacheKey2, {
44645
44727
  path: filePath,
@@ -47060,7 +47142,7 @@ function buildTeamDoctorReport(input) {
47060
47142
  );
47061
47143
  const sections = [
47062
47144
  section("Runtime", () => {
47063
- const git3 = commandExists("git", ["--version"]);
47145
+ const git4 = commandExists("git", ["--version"]);
47064
47146
  const pi = piCommandExists();
47065
47147
  return [
47066
47148
  { label: "cwd", ok: true, detail: input.cwd },
@@ -47070,7 +47152,7 @@ function buildTeamDoctorReport(input) {
47070
47152
  detail: `${process.platform}/${process.arch} node=${process.version}`
47071
47153
  },
47072
47154
  { label: "pi command", ok: pi.ok, detail: pi.detail },
47073
- { label: "git command", ok: git3.ok, detail: git3.detail },
47155
+ { label: "git command", ok: git4.ok, detail: git4.detail },
47074
47156
  {
47075
47157
  label: "config",
47076
47158
  ok: input.configErrors.length === 0,
@@ -49305,6 +49387,21 @@ function cleanupRunWorktrees(manifest, options = {}) {
49305
49387
  const branchName = `pi-crew/${manifest.runId}/${sanitizeBranchPart(entry.name)}`;
49306
49388
  const safeBranchName = sanitizeBranchPart(entry.name);
49307
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
+ }
49308
49405
  if (options.signal?.aborted) break;
49309
49406
  try {
49310
49407
  const statusBefore = git(worktreePath, ["status", "--porcelain"]);
@@ -54057,8 +54154,8 @@ function safeSharedName(name) {
54057
54154
  function sharedPath(manifest, name) {
54058
54155
  const sharedRoot = path61.resolve(manifest.artifactsRoot, "shared");
54059
54156
  const resolved = path61.resolve(sharedRoot, safeSharedName(name));
54060
- const relative8 = path61.relative(sharedRoot, resolved);
54061
- 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}`);
54062
54159
  return resolved;
54063
54160
  }
54064
54161
  function tryParseJson(text) {
@@ -54073,8 +54170,8 @@ function listTaskArtifacts(manifest, taskId) {
54073
54170
  const produced = manifest.artifacts.filter((a) => a.producer === taskId);
54074
54171
  if (produced.length === 0) return void 0;
54075
54172
  return produced.map((a) => {
54076
- const relative8 = path61.relative(manifest.artifactsRoot, a.path);
54077
- return relative8.startsWith("..") ? a.path : relative8;
54173
+ const relative9 = path61.relative(manifest.artifactsRoot, a.path);
54174
+ return relative9.startsWith("..") ? a.path : relative9;
54078
54175
  });
54079
54176
  }
54080
54177
  function aggregateUsage2(task) {
@@ -55363,6 +55460,45 @@ import { randomBytes as randomBytes2 } from "node:crypto";
55363
55460
  import * as fs75 from "node:fs";
55364
55461
  import * as path63 from "node:path";
55365
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
+ }
55366
55502
  function gitEnv() {
55367
55503
  return {
55368
55504
  ...sanitizeEnvSecrets(process.env, {
@@ -55449,9 +55585,9 @@ function linkNodeModulesIfPresent(repoRoot, worktreePath) {
55449
55585
  }
55450
55586
  function normalizeSyntheticPath(worktreePath, rawPath) {
55451
55587
  const resolved = path63.resolve(worktreePath, rawPath);
55452
- const relative8 = path63.relative(worktreePath, resolved);
55453
- if (!relative8 || relative8.startsWith("..") || path63.isAbsolute(relative8)) throw new Error(`synthetic path escapes worktree: ${rawPath}`);
55454
- 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);
55455
55591
  }
55456
55592
  function isAllowedSetupHook(hookPath) {
55457
55593
  if (!hookPath || hookPath.trim().length === 0) return false;
@@ -55681,6 +55817,64 @@ function overlaySeedPaths(repoRoot, worktreePath, seedPaths) {
55681
55817
  });
55682
55818
  }
55683
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
+ }
55684
55878
  async function prepareTaskWorkspaceAsync(manifest, task, stepSeedPaths) {
55685
55879
  if (manifest.workspaceMode !== "worktree") return { cwd: task.cwd };
55686
55880
  const repoRoot = await findGitRootAsync(manifest.cwd);
@@ -55738,16 +55932,17 @@ async function prepareTaskWorkspaceAsync(manifest, task, stepSeedPaths) {
55738
55932
  }
55739
55933
  const dirtyStatus = await gitAsync(worktreePath, ["status", "--porcelain"]);
55740
55934
  if (dirtyStatus.trim()) {
55935
+ snapshotDirtyWorktree(manifest, task, worktreePath, dirtyStatus);
55741
55936
  logInternalError(
55742
55937
  "worktree.reused.dirty",
55743
- 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)`),
55744
55939
  `runId=${manifest.runId}, taskId=${task.id}, dirtyStatus=${dirtyStatus.trim()}`
55745
55940
  );
55746
55941
  await gitAsync(worktreePath, ["checkout", "--", "."]);
55747
55942
  await gitAsync(worktreePath, ["clean", "-fd"]);
55748
55943
  }
55749
- const globalSeedPaths2 = loadedConfig.config.worktree?.seedPaths ?? [];
55750
- const mergedReused = normalizeSeedPaths([...globalSeedPaths2, ...stepSeedPaths ?? []], repoRoot);
55944
+ const globalSeedPaths = loadedConfig.config.worktree?.seedPaths ?? [];
55945
+ const mergedReused = normalizeSeedPaths([...globalSeedPaths, ...stepSeedPaths ?? []], repoRoot);
55751
55946
  if (mergedReused.length > 0) {
55752
55947
  overlaySeedPaths(repoRoot, worktreePath, mergedReused);
55753
55948
  }
@@ -55787,21 +55982,26 @@ async function prepareTaskWorkspaceAsync(manifest, task, stepSeedPaths) {
55787
55982
  }
55788
55983
  throw error;
55789
55984
  }
55790
- const syntheticPaths = runSetupHook(manifest, task, repoRoot, worktreePath, branch);
55791
- const nodeModulesLinked = loadedConfig.config.worktree?.linkNodeModules === true ? linkNodeModulesIfPresent(repoRoot, worktreePath) : false;
55792
- const globalSeedPaths = loadedConfig.config.worktree?.seedPaths ?? [];
55793
- const merged = normalizeSeedPaths([...globalSeedPaths, ...stepSeedPaths ?? []], repoRoot);
55794
- if (merged.length > 0) {
55795
- 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;
55796
56004
  }
55797
- return {
55798
- cwd: worktreePath,
55799
- worktreePath,
55800
- branch,
55801
- reused: false,
55802
- nodeModulesLinked,
55803
- syntheticPaths
55804
- };
55805
56005
  }
55806
56006
  async function captureWorktreeDiffStatAsync(worktreePath) {
55807
56007
  try {
@@ -56861,7 +57061,7 @@ import * as fs79 from "node:fs";
56861
57061
  import * as path67 from "node:path";
56862
57062
  async function detectRipgrep() {
56863
57063
  if (cachedRgCheck !== void 0) return cachedRgCheck;
56864
- return await new Promise((resolve21) => {
57064
+ return await new Promise((resolve22) => {
56865
57065
  let settled = false;
56866
57066
  try {
56867
57067
  const child = spawn3("rg", ["--version"], { stdio: ["ignore", "pipe", "pipe"] });
@@ -56873,7 +57073,7 @@ async function detectRipgrep() {
56873
57073
  if (settled) return;
56874
57074
  settled = true;
56875
57075
  cachedRgCheck = { available: false };
56876
- resolve21(cachedRgCheck);
57076
+ resolve22(cachedRgCheck);
56877
57077
  });
56878
57078
  child.on("close", (code) => {
56879
57079
  if (settled) return;
@@ -56883,13 +57083,13 @@ async function detectRipgrep() {
56883
57083
  } else {
56884
57084
  cachedRgCheck = { available: false };
56885
57085
  }
56886
- resolve21(cachedRgCheck);
57086
+ resolve22(cachedRgCheck);
56887
57087
  });
56888
57088
  } catch {
56889
57089
  if (settled) return;
56890
57090
  settled = true;
56891
57091
  cachedRgCheck = { available: false };
56892
- resolve21(cachedRgCheck);
57092
+ resolve22(cachedRgCheck);
56893
57093
  }
56894
57094
  });
56895
57095
  }
@@ -56906,7 +57106,7 @@ function reasonFor(file, keywords2) {
56906
57106
  return `keyword match: ${hits.join(", ")}`;
56907
57107
  }
56908
57108
  function runRipgrep(args, cwd) {
56909
- return new Promise((resolve21, reject) => {
57109
+ return new Promise((resolve22, reject) => {
56910
57110
  let settled = false;
56911
57111
  let stdout = "";
56912
57112
  let stderr = "";
@@ -56927,7 +57127,7 @@ function runRipgrep(args, cwd) {
56927
57127
  if (settled) return;
56928
57128
  settled = true;
56929
57129
  if (code === 0 || code === 1) {
56930
- resolve21(stdout);
57130
+ resolve22(stdout);
56931
57131
  } else {
56932
57132
  reject(new Error(`rg exited ${code}: ${stderr.slice(0, 200)}`));
56933
57133
  }
@@ -57204,9 +57404,9 @@ function artifactReference(artifactsRoot, artifact) {
57204
57404
  if (!artifact) return void 0;
57205
57405
  const root = path68.resolve(artifactsRoot);
57206
57406
  const target = path68.resolve(artifact.path);
57207
- const relative8 = path68.relative(root, target);
57208
- if (!relative8 || relative8.startsWith("..") || path68.isAbsolute(relative8)) return void 0;
57209
- 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("\\", "/");
57210
57410
  }
57211
57411
  function buildWorkerPromptPipeline(input) {
57212
57412
  return {
@@ -57418,7 +57618,7 @@ async function executeCommand(command, cwd, timeoutMs = 12e4) {
57418
57618
  const start = Date.now();
57419
57619
  let output = "";
57420
57620
  let exitCode = null;
57421
- return new Promise((resolve21) => {
57621
+ return new Promise((resolve22) => {
57422
57622
  const shell = spawn4("sh", ["-c", command], {
57423
57623
  cwd,
57424
57624
  timeout: timeoutMs,
@@ -57432,7 +57632,7 @@ async function executeCommand(command, cwd, timeoutMs = 12e4) {
57432
57632
  });
57433
57633
  const timer = setTimeout(() => {
57434
57634
  shell.kill("SIGKILL");
57435
- resolve21({
57635
+ resolve22({
57436
57636
  exitCode: -1,
57437
57637
  output: output + "\n[TIMEOUT: Command exceeded limit]",
57438
57638
  durationMs: Date.now() - start
@@ -57443,7 +57643,7 @@ async function executeCommand(command, cwd, timeoutMs = 12e4) {
57443
57643
  shell.on("close", (code) => {
57444
57644
  clearTimer();
57445
57645
  exitCode = code;
57446
- resolve21({
57646
+ resolve22({
57447
57647
  exitCode,
57448
57648
  output: output.slice(-1e5),
57449
57649
  // Cap at 100KB
@@ -57452,7 +57652,7 @@ async function executeCommand(command, cwd, timeoutMs = 12e4) {
57452
57652
  });
57453
57653
  shell.on("error", (err2) => {
57454
57654
  clearTimer();
57455
- resolve21({
57655
+ resolve22({
57456
57656
  exitCode: -1,
57457
57657
  output: `Execution error: ${err2.message}`,
57458
57658
  durationMs: Date.now() - start
@@ -58134,6 +58334,7 @@ async function runTeamTask(input) {
58134
58334
  runId: manifest.runId,
58135
58335
  agentId: task.id,
58136
58336
  artifactsRoot: manifest.artifactsRoot,
58337
+ steeringFile: `${manifest.artifactsRoot}/steering/${task.id}.jsonl`,
58137
58338
  onSpawn: (pid) => {
58138
58339
  try {
58139
58340
  ({ task, tasks } = checkpointTask(manifest, tasks, task, "child-spawned", pid));
@@ -60190,8 +60391,9 @@ async function executeTeamRun(input) {
60190
60391
  } catch (error) {
60191
60392
  stopTeamHeartbeat();
60192
60393
  const message = error instanceof Error ? error.message : String(error);
60193
- const freshManifest = manifest;
60194
- 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);
60195
60397
  const failedAt = (/* @__PURE__ */ new Date()).toISOString();
60196
60398
  const tasks = freshTasks.map(
60197
60399
  (task) => task.status === "running" || task.status === "queued" || task.status === "waiting" ? {
@@ -61777,19 +61979,19 @@ function parseRoot(start) {
61777
61979
  function safeJoin(...parts) {
61778
61980
  const filtered = parts.filter(Boolean);
61779
61981
  if (filtered.length === 0) return "";
61780
- const sep9 = filtered.some((p) => p.includes("\\")) ? "\\" : "/";
61982
+ const sep10 = filtered.some((p) => p.includes("\\")) ? "\\" : "/";
61781
61983
  const firstPart = filtered[0];
61782
61984
  let leading = "";
61783
- if (sep9 === "\\") {
61985
+ if (sep10 === "\\") {
61784
61986
  if (firstPart.startsWith("\\\\")) leading = "\\\\";
61785
61987
  else if (firstPart.startsWith("\\")) leading = "\\";
61786
61988
  } else if (firstPart.startsWith("/")) {
61787
61989
  leading = "/";
61788
61990
  }
61789
- const firstPartStripped = sep9 === "\\" ? firstPart.replace(/^\\{1,2}/, "") : firstPart.replace(/^\/+/, "");
61991
+ const firstPartStripped = sep10 === "\\" ? firstPart.replace(/^\\{1,2}/, "") : firstPart.replace(/^\/+/, "");
61790
61992
  const rest = filtered.slice(1);
61791
- const joined = [firstPartStripped, ...rest].filter(Boolean).join(sep9);
61792
- 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);
61793
61995
  return leading + collapsed;
61794
61996
  }
61795
61997
  function safeDirname(p) {
@@ -61946,12 +62148,12 @@ function tokenize(input) {
61946
62148
  continue;
61947
62149
  }
61948
62150
  if (/[0-9]/.test(input[i2])) {
61949
- let num = "";
62151
+ let num2 = "";
61950
62152
  while (i2 < input.length && /[0-9]/.test(input[i2])) {
61951
- num += input[i2];
62153
+ num2 += input[i2];
61952
62154
  i2++;
61953
62155
  }
61954
- tokens.push({ type: "NUMBER", value: num });
62156
+ tokens.push({ type: "NUMBER", value: num2 });
61955
62157
  continue;
61956
62158
  }
61957
62159
  if (/[a-zA-Z_]/.test(input[i2])) {
@@ -62017,8 +62219,8 @@ var init_chain_parser = __esm({
62017
62219
  while (this.pos < this.tokens.length) {
62018
62220
  if (this.peek("COLON")) {
62019
62221
  this.consume("COLON");
62020
- const num = this.consume("NUMBER");
62021
- step.loopCount = Number.parseInt(num.value, 10);
62222
+ const num2 = this.consume("NUMBER");
62223
+ step.loopCount = Number.parseInt(num2.value, 10);
62022
62224
  } else if (this.peek("FLAG", "with-context")) {
62023
62225
  this.consume("FLAG");
62024
62226
  step.withContext = true;
@@ -68922,7 +69124,7 @@ var init_deterministic_ast = __esm({
68922
69124
  });
68923
69125
 
68924
69126
  // src/runtime/dwf-state-store.ts
68925
- 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";
68926
69128
  import { dirname as dirname37 } from "node:path";
68927
69129
  var DwfStore;
68928
69130
  var init_dwf_state_store = __esm({
@@ -68943,7 +69145,7 @@ var init_dwf_state_store = __esm({
68943
69145
  const path81 = this.path;
68944
69146
  try {
68945
69147
  if (!existsSync70(path81)) return void 0;
68946
- const raw = readFileSync68(path81, "utf-8");
69148
+ const raw = readFileSync69(path81, "utf-8");
68947
69149
  const parsed = JSON.parse(raw);
68948
69150
  if (!parsed || typeof parsed !== "object" || typeof parsed.runId !== "string") return void 0;
68949
69151
  return parsed;
@@ -69154,14 +69356,14 @@ var init_semaphore = __esm({
69154
69356
  if (this.#queue.length >= _Semaphore.MAX_QUEUE) {
69155
69357
  throw new Error(`Semaphore queue full: ${this.#queue.length} waiters (max ${_Semaphore.MAX_QUEUE}); cannot acquire slot`);
69156
69358
  }
69157
- const { promise, resolve: resolve21 } = (() => {
69359
+ const { promise, resolve: resolve22 } = (() => {
69158
69360
  let res;
69159
69361
  const p = new Promise((r) => {
69160
69362
  res = r;
69161
69363
  });
69162
69364
  return { promise: p, resolve: res };
69163
69365
  })();
69164
- this.#queue.push(resolve21);
69366
+ this.#queue.push(resolve22);
69165
69367
  return promise;
69166
69368
  }
69167
69369
  release() {
@@ -69765,7 +69967,7 @@ var dynamic_workflow_runner_exports = {};
69765
69967
  __export(dynamic_workflow_runner_exports, {
69766
69968
  runDynamicWorkflow: () => runDynamicWorkflow
69767
69969
  });
69768
- import { readFileSync as readFileSync69 } from "node:fs";
69970
+ import { readFileSync as readFileSync70 } from "node:fs";
69769
69971
  import { join as join71 } from "node:path";
69770
69972
  function assertStructuredCloneable(value, name) {
69771
69973
  try {
@@ -69790,7 +69992,7 @@ function resolveScriptPath(workflow, cwd) {
69790
69992
  );
69791
69993
  }
69792
69994
  async function loadWorkflowModule(scriptPath) {
69793
- const scriptSource = readFileSync69(scriptPath, "utf-8");
69995
+ const scriptSource = readFileSync70(scriptPath, "utf-8");
69794
69996
  if (isDeterminismCheckEnabled()) {
69795
69997
  assertDeterministicScript(scriptSource);
69796
69998
  }
@@ -69913,7 +70115,7 @@ async function runDynamicWorkflow(input) {
69913
70115
  }
69914
70116
  function readFinalArtifact(artifactPath) {
69915
70117
  try {
69916
- return readFileSync69(artifactPath, "utf-8");
70118
+ return readFileSync70(artifactPath, "utf-8");
69917
70119
  } catch (error) {
69918
70120
  logInternalError("dynamic-workflow-runner.readFinal", error, `artifactPath=${artifactPath}`);
69919
70121
  return `(failed to read final artifact ${artifactPath})`;
@@ -71196,6 +71398,15 @@ function handleSteer(params, ctx) {
71196
71398
  }
71197
71399
  task.pendingSteers.push(message);
71198
71400
  saveRunTasks(loaded.manifest, loaded.tasks);
71401
+ try {
71402
+ const steeringDir = `${loaded.manifest.artifactsRoot}/steering`;
71403
+ fs91.mkdirSync(steeringDir, { recursive: true });
71404
+ fs91.appendFileSync(
71405
+ `${steeringDir}/${taskId}.jsonl`,
71406
+ JSON.stringify({ type: "steer", message, ts: (/* @__PURE__ */ new Date()).toISOString() }) + "\n"
71407
+ );
71408
+ } catch {
71409
+ }
71199
71410
  appendEvent(loaded.manifest.eventsPath, {
71200
71411
  type: "task.steer_queued",
71201
71412
  runId,
@@ -73799,7 +74010,7 @@ var init_run_dashboard = __esm({
73799
74010
  const borderFill = (count2) => new DynamicCrewBorder(this.theme).render(count2)[0];
73800
74011
  const border2 = (left, right) => `${fg("border", left)}${borderFill(borderWidth)}${fg("border", right)}`;
73801
74012
  const row = (text) => `\u2502 ${pad(truncate(text, innerWidth - 1), innerWidth - 1)}\u2502`;
73802
- const sep9 = () => border2("\u251C", "\u2524");
74013
+ const sep10 = () => border2("\u251C", "\u2524");
73803
74014
  const lines = [];
73804
74015
  if (this.showHelp) {
73805
74016
  lines.push(...new HelpOverlay(this.theme).render(width));
@@ -73809,7 +74020,7 @@ var init_run_dashboard = __esm({
73809
74020
  row(
73810
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")}`
73811
74022
  ),
73812
- sep9()
74023
+ sep10()
73813
74024
  );
73814
74025
  if (this.runs.length === 0) {
73815
74026
  lines.push(row(fg("dim", "No runs yet.")));
@@ -73857,7 +74068,7 @@ var init_run_dashboard = __esm({
73857
74068
  const agents = snap?.agents ?? agentsFor2(selectedRun, this.options.snapshotCache);
73858
74069
  const statusStr = isLikelyOrphanedActiveRun(r, agents) ? "stale" : r.status;
73859
74070
  const selectedTasks = snap?.tasks ?? readRunTasks2(r, this.options.snapshotCache);
73860
- lines.push(sep9());
74071
+ lines.push(sep10());
73861
74072
  lines.push(row(`${fg("accent", "\u25B8")} ${truncate(sanitizeLine(r.goal), innerWidth - 6)}`));
73862
74073
  const isTerminal = statusStr === "failed" || statusStr === "cancelled" || statusStr === "stopped";
73863
74074
  const reason = isTerminal ? summarizeTerminalReason(r, selectedTasks, snap?.cancellationReason) : void 0;
@@ -75789,10 +76000,10 @@ var init_settings_overlay = __esm({
75789
76000
  typeof current2 === "number" ? String(current2) : "",
75790
76001
  this.theme,
75791
76002
  (value) => {
75792
- const num = value === "" ? void 0 : Number(value);
75793
- if (num !== void 0 && !Number.isNaN(num)) {
75794
- this.changedValues.set(def.id, num);
75795
- 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);
75796
76007
  } else if (value === "") {
75797
76008
  this.changedValues.set(def.id, void 0);
75798
76009
  this.callbacks.onChange(def.id, void 0);
@@ -81683,12 +81894,15 @@ function registerCrewShortcuts(pi) {
81683
81894
  }
81684
81895
  var CREW_SHORTCUT_KEYS = CREW_SHORTCUTS.map((s) => s.key);
81685
81896
 
81897
+ // src/extension/crew-vibes/index.ts
81898
+ init_pi_ui_compat();
81899
+
81686
81900
  // src/extension/crew-vibes/config.ts
81687
- import { existsSync as existsSync76, mkdirSync as mkdirSync44, 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";
81688
81902
  import { dirname as dirname39, join as join76 } from "node:path";
81689
81903
 
81690
81904
  // src/extension/crew-vibes/font-detect.ts
81691
- import { existsSync as existsSync75, readFileSync as readFileSync74 } from "node:fs";
81905
+ import { existsSync as existsSync75, readFileSync as readFileSync75 } from "node:fs";
81692
81906
  import { homedir as homedir11, platform } from "node:os";
81693
81907
  import { join as join75 } from "node:path";
81694
81908
  function fontPath() {
@@ -81715,10 +81929,10 @@ function isWebTerminal() {
81715
81929
  try {
81716
81930
  let pid = process.pid;
81717
81931
  for (let i2 = 0; i2 < 6 && pid > 1; i2++) {
81718
- const cgroup = readFileSync74(`/proc/${pid}/cgroup`, "utf8");
81932
+ const cgroup = readFileSync75(`/proc/${pid}/cgroup`, "utf8");
81719
81933
  if (cgroup.includes("gotty") || cgroup.includes("wetty")) return true;
81720
81934
  const match = cgroup.match(/\d+:.*:(.*)/);
81721
- const status = readFileSync74(`/proc/${pid}/status`, "utf8");
81935
+ const status = readFileSync75(`/proc/${pid}/status`, "utf8");
81722
81936
  const ppid = status.match(/^PPid:\s+(\d+)/m);
81723
81937
  pid = ppid ? Number.parseInt(ppid[1], 10) : 1;
81724
81938
  }
@@ -81761,7 +81975,7 @@ var DEFAULT_CONFIG2 = {
81761
81975
  labels: ["Orbit", "Cruise", "Warp", "Black Hole", "Supernova", "Big Bang"],
81762
81976
  icons: ["\uE710", "\uE711", "\uE712", "\uE713", "\uE714", "\uE715"],
81763
81977
  providerUsage: true,
81764
- providerRefreshMs: 3e5
81978
+ providerRefreshMs: 12e4
81765
81979
  }
81766
81980
  };
81767
81981
  var FALLBACK_CAPACITY_ICONS = [
@@ -81850,226 +82064,18 @@ function loadConfig2() {
81850
82064
  try {
81851
82065
  const path81 = configPath2();
81852
82066
  if (!existsSync76(path81)) return normalizeConfig(void 0);
81853
- return normalizeConfig(JSON.parse(readFileSync75(path81, "utf8")));
82067
+ return normalizeConfig(JSON.parse(readFileSync76(path81, "utf8")));
81854
82068
  } catch {
81855
82069
  return normalizeConfig(void 0);
81856
82070
  }
81857
82071
  }
81858
82072
  function saveConfig(config) {
81859
82073
  const path81 = configPath2();
81860
- mkdirSync44(dirname39(path81), { recursive: true });
82074
+ mkdirSync45(dirname39(path81), { recursive: true });
81861
82075
  writeFileSync33(path81, `${JSON.stringify(normalizeConfig(config), null, 2)}
81862
82076
  `);
81863
82077
  }
81864
82078
 
81865
- // src/extension/crew-vibes/provider-usage.ts
81866
- import { readFileSync as readFileSync76 } from "node:fs";
81867
- import { homedir as homedir12 } from "node:os";
81868
- import { join as join77 } from "node:path";
81869
- function withTimeout(ms, fn) {
81870
- const controller = new AbortController();
81871
- const timeoutId = setTimeout(() => controller.abort(), ms);
81872
- return fn(controller.signal).finally(() => clearTimeout(timeoutId));
81873
- }
81874
- function piAuthPath() {
81875
- return join77(homedir12(), ".pi", "agent", "auth.json");
81876
- }
81877
- function loadAnthropicToken() {
81878
- const envToken = process.env.ANTHROPIC_OAUTH_TOKEN?.trim();
81879
- if (envToken) return envToken;
81880
- try {
81881
- const data2 = JSON.parse(readFileSync76(piAuthPath(), "utf8"));
81882
- const token = data2.anthropic?.access;
81883
- return typeof token === "string" && token.length > 0 ? token : void 0;
81884
- } catch {
81885
- return void 0;
81886
- }
81887
- }
81888
- function loadZaiToken() {
81889
- const envKey = process.env.ZAI_API_KEY?.trim() || process.env.Z_AI_API_KEY?.trim();
81890
- if (envKey) return envKey;
81891
- try {
81892
- const data2 = JSON.parse(readFileSync76(piAuthPath(), "utf8"));
81893
- const key = data2["z-ai"]?.access || data2["z-ai"]?.key || data2.zai?.access || data2.zai?.key;
81894
- return typeof key === "string" && key.length > 0 ? key : void 0;
81895
- } catch {
81896
- return void 0;
81897
- }
81898
- }
81899
- var COPILOT_TOKEN_KEYS = ["oauth_token", "user_token", "github_token", "token"];
81900
- function tokenFromHostEntry(entry) {
81901
- if (!entry) return void 0;
81902
- for (const key of COPILOT_TOKEN_KEYS) {
81903
- const value = entry[key];
81904
- if (typeof value === "string" && value.length > 0) return value;
81905
- }
81906
- return void 0;
81907
- }
81908
- function loadLegacyCopilotToken() {
81909
- const configHome = process.env.XDG_CONFIG_HOME?.trim() || join77(homedir12(), ".config");
81910
- const candidates = [join77(configHome, "github-copilot", "hosts.json"), join77(homedir12(), ".github-copilot", "hosts.json")];
81911
- for (const hostsPath of candidates) {
81912
- try {
81913
- const data2 = JSON.parse(readFileSync76(hostsPath, "utf8"));
81914
- if (!data2 || typeof data2 !== "object") continue;
81915
- const normalized = {};
81916
- for (const [host, entry] of Object.entries(data2)) {
81917
- normalized[host.toLowerCase()] = entry;
81918
- }
81919
- const preferred = tokenFromHostEntry(normalized["github.com"]) ?? tokenFromHostEntry(normalized["api.github.com"]);
81920
- if (preferred) return preferred;
81921
- for (const entry of Object.values(normalized)) {
81922
- const token = tokenFromHostEntry(entry);
81923
- if (token) return token;
81924
- }
81925
- } catch {
81926
- }
81927
- }
81928
- return void 0;
81929
- }
81930
- function loadCopilotToken() {
81931
- const envToken = (process.env.COPILOT_GITHUB_TOKEN || process.env.GH_TOKEN || process.env.GITHUB_TOKEN || "").trim();
81932
- if (envToken) return envToken;
81933
- try {
81934
- const data2 = JSON.parse(readFileSync76(piAuthPath(), "utf8"));
81935
- const piToken = data2["github-copilot"]?.refresh || data2["github-copilot"]?.access;
81936
- if (typeof piToken === "string" && piToken.length > 0) return piToken;
81937
- } catch {
81938
- }
81939
- return loadLegacyCopilotToken();
81940
- }
81941
- async function fetchAnthropicUsage(token) {
81942
- const data2 = await withTimeout(1e4, async (signal) => {
81943
- const res = await fetch("https://api.anthropic.com/api/oauth/usage", {
81944
- headers: {
81945
- Authorization: `Bearer ${token}`,
81946
- "anthropic-beta": "oauth-2025-04-20"
81947
- },
81948
- signal
81949
- });
81950
- if (!res.ok) throw new Error(`anthropic usage HTTP ${res.status}`);
81951
- return await res.json();
81952
- });
81953
- return {
81954
- fiveHourPercent: data2.five_hour?.utilization ?? 0,
81955
- weeklyPercent: data2.seven_day?.utilization ?? 0,
81956
- fiveHourResetAt: data2.five_hour?.resets_at ?? null,
81957
- weeklyResetAt: data2.seven_day?.resets_at ?? null
81958
- };
81959
- }
81960
- async function fetchCopilotMonthlyPercent(token) {
81961
- const data2 = await withTimeout(1e4, async (signal) => {
81962
- const res = await fetch("https://api.github.com/copilot_internal/user", {
81963
- headers: {
81964
- Authorization: `token ${token}`,
81965
- "Editor-Version": "vscode/1.96.2",
81966
- "User-Agent": "GitHubCopilotChat/0.26.7",
81967
- "X-Github-Api-Version": "2025-04-01",
81968
- Accept: "application/json"
81969
- },
81970
- signal
81971
- });
81972
- if (!res.ok) throw new Error(`copilot user HTTP ${res.status}`);
81973
- return await res.json();
81974
- });
81975
- const percentRemaining = data2.quota_snapshots?.premium_interactions?.percent_remaining;
81976
- if (typeof percentRemaining !== "number") return void 0;
81977
- return Math.max(0, 100 - percentRemaining);
81978
- }
81979
- async function fetchZaiUsage(token) {
81980
- const data2 = await withTimeout(1e4, async (signal) => {
81981
- const res = await fetch("https://api.z.ai/api/monitor/usage/quota/limit", {
81982
- method: "GET",
81983
- headers: {
81984
- Authorization: `Bearer ${token}`,
81985
- Accept: "application/json"
81986
- },
81987
- signal
81988
- });
81989
- if (!res.ok) throw new Error(`z.ai usage HTTP ${res.status}`);
81990
- return await res.json();
81991
- });
81992
- if (!data2.success || data2.code !== 200) throw new Error(data2.msg || "z.ai API error");
81993
- const limits = data2.data?.limits ?? [];
81994
- let tokensPercent = 0;
81995
- let monthlyPercent = 0;
81996
- let tokensResetAt = null;
81997
- let monthlyResetAt = null;
81998
- for (const limit of limits) {
81999
- const pct = limit.percentage ?? 0;
82000
- const resetIso = typeof limit.nextResetTime === "number" ? new Date(limit.nextResetTime).toISOString() : typeof limit.nextResetTime === "string" ? limit.nextResetTime : null;
82001
- if (limit.type === "TOKENS_LIMIT") {
82002
- tokensPercent = pct;
82003
- tokensResetAt = resetIso;
82004
- } else if (limit.type === "TIME_LIMIT") {
82005
- monthlyPercent = pct;
82006
- monthlyResetAt = resetIso;
82007
- }
82008
- }
82009
- return {
82010
- providerName: "z.ai",
82011
- fiveHourPercent: tokensPercent,
82012
- fiveHourResetAt: tokensResetAt,
82013
- weeklyPercent: monthlyPercent,
82014
- weeklyResetAt: monthlyResetAt
82015
- };
82016
- }
82017
- var cachedUsage = null;
82018
- var cachedAt = 0;
82019
- function clearProviderUsageCache() {
82020
- cachedUsage = null;
82021
- cachedAt = 0;
82022
- }
82023
- async function fetchProviderUsage(maxAgeMs = 3e5) {
82024
- if (cachedUsage !== null && Date.now() - cachedAt < maxAgeMs) {
82025
- return cachedUsage;
82026
- }
82027
- try {
82028
- const anthropicToken = loadAnthropicToken();
82029
- if (anthropicToken) {
82030
- const base = await fetchAnthropicUsage(anthropicToken);
82031
- const usage = {
82032
- providerName: "Claude",
82033
- fiveHourPercent: base.fiveHourPercent,
82034
- fiveHourResetAt: base.fiveHourResetAt,
82035
- weeklyPercent: base.weeklyPercent,
82036
- weeklyResetAt: base.weeklyResetAt
82037
- };
82038
- cachedUsage = usage;
82039
- cachedAt = Date.now();
82040
- return usage;
82041
- }
82042
- const zaiToken = loadZaiToken();
82043
- if (zaiToken) {
82044
- const usage = await fetchZaiUsage(zaiToken);
82045
- usage.providerName = "z.ai";
82046
- cachedUsage = usage;
82047
- cachedAt = Date.now();
82048
- return usage;
82049
- }
82050
- const copilotToken = loadCopilotToken();
82051
- if (copilotToken) {
82052
- const monthlyPercent = await fetchCopilotMonthlyPercent(copilotToken);
82053
- if (monthlyPercent !== void 0) {
82054
- const usage = {
82055
- providerName: "Copilot",
82056
- fiveHourPercent: 0,
82057
- fiveHourResetAt: null,
82058
- weeklyPercent: monthlyPercent,
82059
- weeklyResetAt: null,
82060
- copilotMonthlyPercent: monthlyPercent
82061
- };
82062
- cachedUsage = usage;
82063
- cachedAt = Date.now();
82064
- return usage;
82065
- }
82066
- }
82067
- return null;
82068
- } catch {
82069
- return null;
82070
- }
82071
- }
82072
-
82073
82079
  // src/extension/crew-vibes/figures.ts
82074
82080
  var BRAILLE_FRAMES = [
82075
82081
  "\u280B ",
@@ -82127,6 +82133,12 @@ function isDangerStage(index, levels) {
82127
82133
  return index >= Math.max(0, levels - 2);
82128
82134
  }
82129
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
+
82130
82142
  // src/extension/crew-vibes/render.ts
82131
82143
  function formatCount(value) {
82132
82144
  if (value < 1e3) return value.toString();
@@ -82199,14 +82211,6 @@ function setSpeedStatus(ctx, config, text) {
82199
82211
  }
82200
82212
  ctx.ui.setStatus(SPEED_STATUS_ID, text);
82201
82213
  }
82202
- function setCapacityStatus(ctx, config, text) {
82203
- if (!ctx?.hasUI) return;
82204
- if (!config.enabled || !config.capacity.enabled) {
82205
- ctx.ui.setStatus(CAPACITY_STATUS_ID, void 0);
82206
- return;
82207
- }
82208
- ctx.ui.setStatus(CAPACITY_STATUS_ID, text);
82209
- }
82210
82214
  function clearVibesStatus(ctx) {
82211
82215
  if (!ctx?.hasUI) return;
82212
82216
  ctx.ui.setStatus(SPEED_STATUS_ID, void 0);
@@ -82261,6 +82265,442 @@ function renderProviderUsage(theme, usage) {
82261
82265
  return parts.join(" ");
82262
82266
  }
82263
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
+
82450
+ // src/extension/crew-vibes/provider-usage.ts
82451
+ import { readFileSync as readFileSync77 } from "node:fs";
82452
+ import { homedir as homedir12 } from "node:os";
82453
+ import { join as join77 } from "node:path";
82454
+ function withTimeout(ms, fn) {
82455
+ const controller = new AbortController();
82456
+ const timeoutId = setTimeout(() => controller.abort(), ms);
82457
+ return fn(controller.signal).finally(() => clearTimeout(timeoutId));
82458
+ }
82459
+ function piAuthPath() {
82460
+ return join77(homedir12(), ".pi", "agent", "auth.json");
82461
+ }
82462
+ function loadAnthropicToken() {
82463
+ const envToken = process.env.ANTHROPIC_OAUTH_TOKEN?.trim();
82464
+ if (envToken) return envToken;
82465
+ try {
82466
+ const data2 = JSON.parse(readFileSync77(piAuthPath(), "utf8"));
82467
+ const token = data2.anthropic?.access;
82468
+ return typeof token === "string" && token.length > 0 ? token : void 0;
82469
+ } catch {
82470
+ return void 0;
82471
+ }
82472
+ }
82473
+ function loadZaiToken() {
82474
+ const envKey = process.env.ZAI_API_KEY?.trim() || process.env.Z_AI_API_KEY?.trim();
82475
+ if (envKey) return envKey;
82476
+ try {
82477
+ const data2 = JSON.parse(readFileSync77(piAuthPath(), "utf8"));
82478
+ const key = data2["z-ai"]?.access || data2["z-ai"]?.key || data2.zai?.access || data2.zai?.key;
82479
+ return typeof key === "string" && key.length > 0 ? key : void 0;
82480
+ } catch {
82481
+ return void 0;
82482
+ }
82483
+ }
82484
+ function loadMinimaxToken() {
82485
+ const envKey = process.env.MINIMAX_API_KEY?.trim();
82486
+ if (envKey) return envKey;
82487
+ try {
82488
+ const data2 = JSON.parse(readFileSync77(piAuthPath(), "utf8"));
82489
+ const key = data2.minimax?.key;
82490
+ return typeof key === "string" && key.length > 0 ? key : void 0;
82491
+ } catch {
82492
+ return void 0;
82493
+ }
82494
+ }
82495
+ var COPILOT_TOKEN_KEYS = ["oauth_token", "user_token", "github_token", "token"];
82496
+ function tokenFromHostEntry(entry) {
82497
+ if (!entry) return void 0;
82498
+ for (const key of COPILOT_TOKEN_KEYS) {
82499
+ const value = entry[key];
82500
+ if (typeof value === "string" && value.length > 0) return value;
82501
+ }
82502
+ return void 0;
82503
+ }
82504
+ function loadLegacyCopilotToken() {
82505
+ const configHome = process.env.XDG_CONFIG_HOME?.trim() || join77(homedir12(), ".config");
82506
+ const candidates = [join77(configHome, "github-copilot", "hosts.json"), join77(homedir12(), ".github-copilot", "hosts.json")];
82507
+ for (const hostsPath of candidates) {
82508
+ try {
82509
+ const data2 = JSON.parse(readFileSync77(hostsPath, "utf8"));
82510
+ if (!data2 || typeof data2 !== "object") continue;
82511
+ const normalized = {};
82512
+ for (const [host, entry] of Object.entries(data2)) {
82513
+ normalized[host.toLowerCase()] = entry;
82514
+ }
82515
+ const preferred = tokenFromHostEntry(normalized["github.com"]) ?? tokenFromHostEntry(normalized["api.github.com"]);
82516
+ if (preferred) return preferred;
82517
+ for (const entry of Object.values(normalized)) {
82518
+ const token = tokenFromHostEntry(entry);
82519
+ if (token) return token;
82520
+ }
82521
+ } catch {
82522
+ }
82523
+ }
82524
+ return void 0;
82525
+ }
82526
+ function loadCopilotToken() {
82527
+ const envToken = (process.env.COPILOT_GITHUB_TOKEN || process.env.GH_TOKEN || process.env.GITHUB_TOKEN || "").trim();
82528
+ if (envToken) return envToken;
82529
+ try {
82530
+ const data2 = JSON.parse(readFileSync77(piAuthPath(), "utf8"));
82531
+ const piToken = data2["github-copilot"]?.refresh || data2["github-copilot"]?.access;
82532
+ if (typeof piToken === "string" && piToken.length > 0) return piToken;
82533
+ } catch {
82534
+ }
82535
+ return loadLegacyCopilotToken();
82536
+ }
82537
+ async function fetchAnthropicUsage(token) {
82538
+ const data2 = await withTimeout(1e4, async (signal) => {
82539
+ const res = await fetch("https://api.anthropic.com/api/oauth/usage", {
82540
+ headers: {
82541
+ Authorization: `Bearer ${token}`,
82542
+ "anthropic-beta": "oauth-2025-04-20"
82543
+ },
82544
+ signal
82545
+ });
82546
+ if (!res.ok) throw new Error(`anthropic usage HTTP ${res.status}`);
82547
+ return await res.json();
82548
+ });
82549
+ return {
82550
+ fiveHourPercent: data2.five_hour?.utilization ?? 0,
82551
+ weeklyPercent: data2.seven_day?.utilization ?? 0,
82552
+ fiveHourResetAt: data2.five_hour?.resets_at ?? null,
82553
+ weeklyResetAt: data2.seven_day?.resets_at ?? null
82554
+ };
82555
+ }
82556
+ async function fetchCopilotMonthlyPercent(token) {
82557
+ const data2 = await withTimeout(1e4, async (signal) => {
82558
+ const res = await fetch("https://api.github.com/copilot_internal/user", {
82559
+ headers: {
82560
+ Authorization: `token ${token}`,
82561
+ "Editor-Version": "vscode/1.96.2",
82562
+ "User-Agent": "GitHubCopilotChat/0.26.7",
82563
+ "X-Github-Api-Version": "2025-04-01",
82564
+ Accept: "application/json"
82565
+ },
82566
+ signal
82567
+ });
82568
+ if (!res.ok) throw new Error(`copilot user HTTP ${res.status}`);
82569
+ return await res.json();
82570
+ });
82571
+ const percentRemaining = data2.quota_snapshots?.premium_interactions?.percent_remaining;
82572
+ if (typeof percentRemaining !== "number") return void 0;
82573
+ return Math.max(0, 100 - percentRemaining);
82574
+ }
82575
+ async function fetchZaiUsage(token) {
82576
+ const data2 = await withTimeout(1e4, async (signal) => {
82577
+ const res = await fetch("https://api.z.ai/api/monitor/usage/quota/limit", {
82578
+ method: "GET",
82579
+ headers: {
82580
+ Authorization: `Bearer ${token}`,
82581
+ Accept: "application/json"
82582
+ },
82583
+ signal
82584
+ });
82585
+ if (!res.ok) throw new Error(`z.ai usage HTTP ${res.status}`);
82586
+ return await res.json();
82587
+ });
82588
+ if (!data2.success || data2.code !== 200) throw new Error(data2.msg || "z.ai API error");
82589
+ const limits = data2.data?.limits ?? [];
82590
+ let tokensPercent = 0;
82591
+ let monthlyPercent = 0;
82592
+ let tokensResetAt = null;
82593
+ let monthlyResetAt = null;
82594
+ for (const limit of limits) {
82595
+ const pct = limit.percentage ?? 0;
82596
+ const resetIso = typeof limit.nextResetTime === "number" ? new Date(limit.nextResetTime).toISOString() : typeof limit.nextResetTime === "string" ? limit.nextResetTime : null;
82597
+ if (limit.type === "TOKENS_LIMIT") {
82598
+ tokensPercent = pct;
82599
+ tokensResetAt = resetIso;
82600
+ } else if (limit.type === "TIME_LIMIT") {
82601
+ monthlyPercent = pct;
82602
+ monthlyResetAt = resetIso;
82603
+ }
82604
+ }
82605
+ return {
82606
+ providerName: "z.ai",
82607
+ fiveHourPercent: tokensPercent,
82608
+ fiveHourResetAt: tokensResetAt,
82609
+ weeklyPercent: monthlyPercent,
82610
+ weeklyResetAt: monthlyResetAt
82611
+ };
82612
+ }
82613
+ var cachedUsage = null;
82614
+ var cachedAt = 0;
82615
+ function clearProviderUsageCache() {
82616
+ cachedUsage = null;
82617
+ cachedAt = 0;
82618
+ }
82619
+ async function fetchMinimaxUsage(token) {
82620
+ const data2 = await withTimeout(1e4, async (signal) => {
82621
+ const res = await fetch("https://www.minimax.io/v1/token_plan/remains", {
82622
+ headers: { Authorization: `Bearer ${token}`, Accept: "application/json" },
82623
+ signal
82624
+ });
82625
+ if (!res.ok) throw new Error(`minimax usage HTTP ${res.status}`);
82626
+ return await res.json();
82627
+ });
82628
+ if (data2.base_resp?.status_code !== 0) throw new Error(data2.base_resp?.status_msg || "minimax API error");
82629
+ const models = data2.model_remains ?? [];
82630
+ const general = models.find((m) => m.model_name === "general") ?? models[0];
82631
+ if (!general) throw new Error("minimax: no model data");
82632
+ const intervalUsed = 100 - (general.current_interval_remaining_percent ?? 100);
82633
+ const weeklyUsed = 100 - (general.current_weekly_remaining_percent ?? 100);
82634
+ const intervalReset = typeof general.end_time === "number" ? new Date(general.end_time).toISOString() : null;
82635
+ const weeklyReset = typeof general.weekly_end_time === "number" ? new Date(general.weekly_end_time).toISOString() : null;
82636
+ return {
82637
+ providerName: "Minimax",
82638
+ fiveHourPercent: intervalUsed,
82639
+ fiveHourResetAt: intervalReset,
82640
+ weeklyPercent: weeklyUsed,
82641
+ weeklyResetAt: weeklyReset
82642
+ };
82643
+ }
82644
+ var QUOTA_PROVIDERS = /* @__PURE__ */ new Set(["anthropic", "minimax", "minimax-cn", "zai", "github-copilot"]);
82645
+ async function fetchForProvider(provider) {
82646
+ switch (provider) {
82647
+ case "anthropic": {
82648
+ const token = loadAnthropicToken();
82649
+ if (!token) return null;
82650
+ const base = await fetchAnthropicUsage(token);
82651
+ return { providerName: "Claude", ...base };
82652
+ }
82653
+ case "minimax":
82654
+ case "minimax-cn": {
82655
+ const token = loadMinimaxToken();
82656
+ if (!token) return null;
82657
+ return await fetchMinimaxUsage(token);
82658
+ }
82659
+ case "zai": {
82660
+ const token = loadZaiToken();
82661
+ if (!token) return null;
82662
+ const usage = await fetchZaiUsage(token);
82663
+ usage.providerName = "z.ai";
82664
+ return usage;
82665
+ }
82666
+ case "github-copilot": {
82667
+ const token = loadCopilotToken();
82668
+ if (!token) return null;
82669
+ const pct = await fetchCopilotMonthlyPercent(token);
82670
+ if (pct === void 0) return null;
82671
+ return {
82672
+ providerName: "Copilot",
82673
+ fiveHourPercent: 0,
82674
+ fiveHourResetAt: null,
82675
+ weeklyPercent: pct,
82676
+ weeklyResetAt: null,
82677
+ copilotMonthlyPercent: pct
82678
+ };
82679
+ }
82680
+ default:
82681
+ return null;
82682
+ }
82683
+ }
82684
+ async function fetchProviderUsage(maxAgeMs = 3e5, provider) {
82685
+ if (!provider || !QUOTA_PROVIDERS.has(provider)) {
82686
+ cachedUsage = null;
82687
+ return null;
82688
+ }
82689
+ if (cachedUsage !== null && Date.now() - cachedAt < maxAgeMs) {
82690
+ return cachedUsage;
82691
+ }
82692
+ try {
82693
+ const usage = await fetchForProvider(provider);
82694
+ if (usage) {
82695
+ cachedUsage = usage;
82696
+ cachedAt = Date.now();
82697
+ }
82698
+ return usage;
82699
+ } catch {
82700
+ return null;
82701
+ }
82702
+ }
82703
+
82264
82704
  // src/extension/crew-vibes/speed.ts
82265
82705
  var COMPACTION_THRESHOLD = 5e3;
82266
82706
  function estimateTokensFromDelta(text) {
@@ -82473,19 +82913,6 @@ var SpeedAnimator = class {
82473
82913
  }
82474
82914
  };
82475
82915
 
82476
- // src/extension/crew-vibes/cat-frames.ts
82477
- var CAT_FRAMES2 = [
82478
- // Frame 0 — standing
82479
- ["\u2584\u2584 \u2588\u2588\u2588\u2584 ", "\u2580\u2580\u2588\u2588\u2588\u2588\u2588\u2584", " \u2588\u2588\u2580\u2588\u2588 ", " \u2580\u2580 \u2580\u2580"],
82480
- // Frame 1 — step 1
82481
- [" \u2588\u2588\u2588\u2588 ", " \u2588\u2588\u2588\u2588\u2588\u2584", "\u2584\u2588\u2588\u2588\u2588\u2588\u2588\u2580", " \u2588\u2588 \u2580\u2588\u2584"],
82482
- // Frame 2 — step 2
82483
- [" \u2584\u2588\u2588\u2588\u2588\u2588\u2588", " \u2588\u2588\u2588\u2588\u2588\u2580 ", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588 ", "\u2580\u2588\u2588\u2588\u2588\u2588\u2588 "],
82484
- // Frame 3 — step 3
82485
- [" \u2588\u2588\u2588\u2588 ", "\u2584\u2588\u2588\u2588\u2588\u2588\u2584 ", "\u2580\u2588\u2588\u2588\u2588\u2588\u2588\u2584", "\u2584\u2588\u2580 \u2588\u2588 "]
82486
- ];
82487
- var CAT_FRAME_COUNT = CAT_FRAMES2.length;
82488
-
82489
82916
  // src/extension/crew-vibes/index.ts
82490
82917
  function isAssistantMessage(message) {
82491
82918
  return typeof message === "object" && message !== null && message.role === "assistant";
@@ -82512,28 +82939,31 @@ function registerCrewVibes(pi) {
82512
82939
  let footerTimer;
82513
82940
  let capacityTimer;
82514
82941
  let providerTimer;
82515
- let lastProviderText;
82516
- let catFrameIndex = 0;
82517
- function visibleLen(text) {
82518
- return text.replace(/\x1b\[[0-9;]*m/g, "").length;
82519
- }
82520
- function spreadLine(left, right) {
82521
- const cols = process.stdout.columns || 120;
82522
- const padding = Math.max(2, cols - visibleLen(left) - visibleLen(right));
82523
- return left + "\xA0".repeat(padding) + right;
82524
- }
82942
+ let lastProviderUsage = null;
82943
+ let currentProvider;
82944
+ let currentThinkingLevel;
82525
82945
  function themeOf(ctx) {
82526
82946
  return asCrewTheme2(ctx.hasUI ? ctx.ui.theme : void 0);
82527
82947
  }
82528
- 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) {
82529
82957
  if (!ctx?.hasUI) return;
82530
- if (!config.enabled || !config.capacity.enabled) {
82531
- setCapacityStatus(ctx, config, void 0);
82958
+ if (!metersActive()) {
82959
+ setFooter(ctx, void 0);
82532
82960
  return;
82533
82961
  }
82534
- const capText = renderCapacity(themeOf(ctx), config.capacity, getCapacityUsage(ctx));
82535
- const combined = lastProviderText ? spreadLine(capText, lastProviderText) : capText;
82536
- 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);
82537
82967
  }
82538
82968
  function publishSpeedFooter(ctx, speed = footerAnimator.value()) {
82539
82969
  if (!config.enabled || !config.speed.enabled || !config.speed.footer) {
@@ -82590,10 +83020,6 @@ function registerCrewVibes(pi) {
82590
83020
  const speed = speedTracker.liveTokS();
82591
83021
  applyIndicator(ctx, speed);
82592
83022
  renderWorking(ctx, speed);
82593
- if (isWebTerminal()) {
82594
- catFrameIndex = (catFrameIndex + 1) % CAT_FRAMES2.length;
82595
- ctx.ui.setWidget("crew-vibes-cat", [...CAT_FRAMES2[catFrameIndex]], { placement: "aboveEditor" });
82596
- }
82597
83023
  }, config.speed.renderIntervalMs);
82598
83024
  liveTimer.unref?.();
82599
83025
  }
@@ -82612,29 +83038,28 @@ function registerCrewVibes(pi) {
82612
83038
  function startCapacityTimer(ctx) {
82613
83039
  if (capacityTimer) return;
82614
83040
  const interval = Math.max(250, config.capacity.refreshIntervalMs);
82615
- capacityTimer = setInterval(() => publishCapacity(ctx), interval);
83041
+ capacityTimer = setInterval(() => refreshFooter(ctx), interval);
82616
83042
  capacityTimer.unref?.();
82617
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
+ }
82618
83057
  function startProviderTimer(ctx) {
82619
83058
  if (providerTimer) return;
82620
83059
  if (!config.capacity.providerUsage) return;
82621
83060
  const interval = Math.max(1e4, config.capacity.providerRefreshMs);
82622
- async function tick() {
82623
- if (!config.enabled || !config.capacity.providerUsage) {
82624
- stopProviderTimer();
82625
- return;
82626
- }
82627
- try {
82628
- const usage = await fetchProviderUsage(config.capacity.providerRefreshMs);
82629
- lastProviderText = renderProviderUsage(themeOf(ctx), usage);
82630
- publishCapacity(ctx);
82631
- } catch {
82632
- lastProviderText = void 0;
82633
- publishCapacity(ctx);
82634
- }
82635
- }
82636
- tick();
82637
- providerTimer = setInterval(tick, interval);
83061
+ fetchProviderAndRefresh(ctx);
83062
+ providerTimer = setInterval(() => fetchProviderAndRefresh(ctx), interval);
82638
83063
  providerTimer.unref?.();
82639
83064
  }
82640
83065
  function resetWorking(ctx) {
@@ -82650,11 +83075,13 @@ function registerCrewVibes(pi) {
82650
83075
  stopFooterTimer();
82651
83076
  stopCapacityTimer();
82652
83077
  stopProviderTimer();
83078
+ setFooter(ctx, void 0);
82653
83079
  clearVibesStatus(ctx);
82654
83080
  return;
82655
83081
  }
82656
- publishCapacity(ctx);
83082
+ installFooter(ctx);
82657
83083
  publishSpeedFooter(ctx);
83084
+ startCapacityTimer(ctx);
82658
83085
  if (config.capacity.providerUsage) startProviderTimer(ctx);
82659
83086
  }
82660
83087
  pi.on("session_start", (_event, ctx) => {
@@ -82668,11 +83095,14 @@ function registerCrewVibes(pi) {
82668
83095
  speedTracker.resetSession();
82669
83096
  footerAnimator.reset(null);
82670
83097
  clearProviderUsageCache();
83098
+ currentProvider = ctx.model?.provider;
83099
+ currentThinkingLevel = void 0;
82671
83100
  if (!config.enabled) {
83101
+ setFooter(ctx, void 0);
82672
83102
  clearVibesStatus(ctx);
82673
83103
  return;
82674
83104
  }
82675
- publishCapacity(ctx);
83105
+ installFooter(ctx);
82676
83106
  publishSpeedFooter(ctx);
82677
83107
  startCapacityTimer(ctx);
82678
83108
  startProviderTimer(ctx);
@@ -82692,9 +83122,6 @@ function registerCrewVibes(pi) {
82692
83122
  footerAnimator.reset(speedTracker.lastTokS);
82693
83123
  startLiveTimer(ctx);
82694
83124
  lastRenderedAt = 0;
82695
- if (isWebTerminal() && ctx.hasUI) {
82696
- ctx.ui.setWidget("crew-vibes-cat", [...CAT_FRAMES2[0]], { placement: "aboveEditor" });
82697
- }
82698
83125
  });
82699
83126
  pi.on("message_update", (event, ctx) => {
82700
83127
  if (!config.enabled || !config.speed.enabled || !isAssistantMessage(event.message) || !speedTracker.isStreaming) return;
@@ -82714,7 +83141,7 @@ function registerCrewVibes(pi) {
82714
83141
  });
82715
83142
  pi.on("message_end", (event, ctx) => {
82716
83143
  if (!isAssistantMessage(event.message)) return;
82717
- publishCapacity(ctx);
83144
+ refreshFooter(ctx);
82718
83145
  if (!config.enabled || !config.speed.enabled || !speedTracker.isStreaming) return;
82719
83146
  const completed = speedTracker.finishMessage(assistantUsageOutput(event.message) ?? 0, assistantStopReason(event.message));
82720
83147
  if (!completed) return;
@@ -82722,9 +83149,6 @@ function registerCrewVibes(pi) {
82722
83149
  publishSpeedFooter(ctx);
82723
83150
  startFooterTimer(ctx);
82724
83151
  applyIndicator(ctx, speedTracker.lastTokS);
82725
- if (isWebTerminal() && ctx.hasUI) {
82726
- ctx.ui.setWidget("crew-vibes-cat", void 0);
82727
- }
82728
83152
  });
82729
83153
  pi.on("turn_end", () => {
82730
83154
  speedTracker.stopMessage();
@@ -82736,19 +83160,26 @@ function registerCrewVibes(pi) {
82736
83160
  if (ctx && config.enabled && ctx.hasUI) {
82737
83161
  applyIndicator(ctx, speedTracker.lastTokS);
82738
83162
  ctx.ui.setWorkingMessage();
82739
- if (isWebTerminal()) ctx.ui.setWidget("crew-vibes-cat", void 0);
82740
83163
  }
82741
83164
  });
82742
- pi.on("model_select", (_event, ctx) => publishCapacity(ctx));
82743
- pi.on("session_compact", (_event, ctx) => publishCapacity(ctx));
82744
- pi.on("session_tree", (_event, ctx) => publishCapacity(ctx));
83165
+ pi.on("model_select", (event, ctx) => {
83166
+ currentProvider = event.model?.provider;
83167
+ clearProviderUsageCache();
83168
+ fetchProviderAndRefresh(ctx);
83169
+ });
83170
+ pi.on("thinking_level_select", (event, ctx) => {
83171
+ currentThinkingLevel = event.level;
83172
+ refreshFooter(ctx);
83173
+ });
83174
+ pi.on("session_compact", (_event, ctx) => refreshFooter(ctx));
83175
+ pi.on("session_tree", (_event, ctx) => refreshFooter(ctx));
82745
83176
  pi.on("session_shutdown", (_event, ctx) => {
82746
83177
  stopLiveTimer();
82747
83178
  stopFooterTimer();
82748
83179
  stopCapacityTimer();
82749
83180
  stopProviderTimer();
83181
+ setFooter(ctx, void 0);
82750
83182
  clearVibesStatus(ctx);
82751
- if (ctx.hasUI) ctx.ui.setWidget("crew-vibes-cat", void 0);
82752
83183
  });
82753
83184
  async function handleCommand(args, ctx) {
82754
83185
  const tokens = args.trim().split(/\s+/).filter(Boolean);
@@ -83642,7 +84073,7 @@ function registerSubagentTools(pi, subagentManager, options = {}) {
83642
84073
  savePersistedSubagentRecord(ctx.cwd, current2);
83643
84074
  break;
83644
84075
  }
83645
- await new Promise((resolve21) => setTimeout(resolve21, 1e3));
84076
+ await new Promise((resolve22) => setTimeout(resolve22, 1e3));
83646
84077
  current2 = refreshPersistedSubagentRecord(ctx, current2);
83647
84078
  if (!current2.runId) break;
83648
84079
  }
@@ -84284,7 +84715,7 @@ Subagent may need manual intervention.`
84284
84715
  if (!loaded) return true;
84285
84716
  return !loaded.tasks.some((t2) => t2.status === "running" || t2.status === "queued");
84286
84717
  };
84287
- while (!check()) await new Promise((resolve21) => setTimeout(resolve21, 500));
84718
+ while (!check()) await new Promise((resolve22) => setTimeout(resolve22, 500));
84288
84719
  };
84289
84720
  validatedRegistry.hasRunning = async (runId) => {
84290
84721
  const manifest = manifestCacheForRegistry.get(runId);