@cortexkit/aft-opencode 0.35.2 → 0.35.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"bg-notifications.d.ts","sourceRoot":"","sources":["../src/bg-notifications.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bg-notifications.d.ts","sourceRoot":"","sources":["../src/bg-notifications.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAWhD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAG3B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,IAAI,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,eAAe,GAAG,WAAW,CAAC;IACvC,qFAAqF;IACrF,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;CACjC;AAED,KAAK,cAAc,GAAG;IACpB,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAChC,kBAAkB,EAAE,YAAY,EAAE,CAAC;IACnC,kBAAkB,EAAE,qBAAqB,EAAE,CAAC;IAC5C,qBAAqB,EAAE,iBAAiB,EAAE,CAAC;IAC3C,oBAAoB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAClC,aAAa,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IACrC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,wBAAwB,EAAE,MAAM,CAAC;IACjC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,OAAO,CAAC;IACzB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,kBAAkB,EAAE,KAAK,CAAC;QAAE,UAAU,EAAE,YAAY,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5E;;;;;;OAMG;IACH,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC;;;;;;;;;;;OAWG;IACH,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAIF,eAAO,MAAM,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAa,CAAC;AAGtE,eAAO,MAAM,4BAA4B,QAAiB,CAAC;AAS3D,UAAU,YAAY;IACpB,GAAG,EAAE,aAAa,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AASD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAwBvF;AAED,wBAAsB,yBAAyB,CAC7C,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAIf;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAoBnF;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAOrF;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAiB/E;AAED,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,MAAM,EAAE,MAAM,EACd,gBAAgB,UAAO,GACtB,IAAI,CAiBN;AAED,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAEzF;AAsCD,wBAAsB,wBAAwB,CAC5C,YAAY,EAAE,YAAY,GAAG;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,EAChD,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,WAAW,EAAE,OAAO,GACnB,YAAY,EAAE,CAkChB;AAED,wBAAsB,wBAAwB,CAC5C,YAAY,EAAE,YAAY,GAAG;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,EAChD,UAAU,EAAE,OAAO,GAClB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,yBAAyB,CAC7C,YAAY,EAAE,YAAY,GAAG;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,EAChD,QAAQ,EAAE,qBAAqB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,yBAAyB,CAC7C,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,GACtC,OAAO,CAAC,IAAI,CAAC,CAmEf;AAED,wBAAsB,uBAAuB,CAC3C,YAAY,EAAE,YAAY,GAAG;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GAC/C,OAAO,CAAC,IAAI,CAAC,CAGf;AAyND,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CASjF;AAED,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,SAAS,qBAAqB,EAAE,GAAG,MAAM,CAQ7F;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,SAAS,iBAAiB,EAAE,GAAG,MAAM,CAWxF;AAcD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAcnE;AAED,wBAAgB,kCAAkC,IAAI,IAAI,CAKzD"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AA6MlD;;;;;;;;;;;;;;;;;GAiBG;AAQH,QAAA,MAAM,MAAM,EAAE,MAA6D,CAAC;AA80B5E,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AA4MlD;;;;;;;;;;;;;;;;;GAiBG;AAQH,QAAA,MAAM,MAAM,EAAE,MAA6D,CAAC;AA80B5E,eAAe,MAAM,CAAC"}
package/dist/index.js CHANGED
@@ -11867,6 +11867,9 @@ class BinaryBridge {
11867
11867
  hasPendingRequests() {
11868
11868
  return this.pending.size > 0;
11869
11869
  }
11870
+ getCwd() {
11871
+ return this.cwd;
11872
+ }
11870
11873
  getCachedStatus() {
11871
11874
  return this.cachedStatus;
11872
11875
  }
@@ -12064,7 +12067,6 @@ class BinaryBridge {
12064
12067
  if (!snapshot || typeof snapshot !== "object" || Array.isArray(snapshot))
12065
12068
  return;
12066
12069
  this.cachedStatus = snapshot;
12067
- this.logVia("Received status_changed push frame; cached AFT status snapshot");
12068
12070
  for (const listener of this.statusListeners) {
12069
12071
  this.deliverStatusSnapshot(listener, this.cachedStatus);
12070
12072
  }
@@ -12211,6 +12213,7 @@ class BinaryBridge {
12211
12213
  const remaining = stdoutDecoder.end();
12212
12214
  if (remaining)
12213
12215
  this.onStdoutData(remaining);
12216
+ this.flushStdoutBuffer();
12214
12217
  });
12215
12218
  const stderrDecoder = new StringDecoder("utf8");
12216
12219
  child.stderr?.on("data", (chunk) => {
@@ -12233,6 +12236,7 @@ class BinaryBridge {
12233
12236
  return;
12234
12237
  if (this._shuttingDown)
12235
12238
  return;
12239
+ this.flushStdoutBuffer();
12236
12240
  this.logVia(`Process exited: code=${code}, signal=${signal}`);
12237
12241
  if (signal === "SIGTERM" || signal === "SIGKILL" || signal === "SIGHUP" || signal === "SIGINT") {
12238
12242
  this.process = null;
@@ -12301,71 +12305,81 @@ class BinaryBridge {
12301
12305
  this.stdoutBuffer = this.stdoutBuffer.slice(newlineIdx + 1);
12302
12306
  if (!line)
12303
12307
  continue;
12304
- try {
12305
- const response = JSON.parse(line);
12306
- this.lastChildActivityAt = Date.now();
12307
- if (response.type === "progress") {
12308
- const requestId = response.request_id;
12309
- const entry = requestId ? this.pending.get(requestId) : undefined;
12310
- const kind = response.kind === "stderr" ? "stderr" : "stdout";
12311
- const text = typeof response.chunk === "string" ? response.chunk : "";
12312
- entry?.onProgress?.({ kind, text });
12313
- continue;
12314
- }
12315
- if (response.type === "permission_ask") {
12316
- const requestId = response.request_id;
12317
- const entry = requestId ? this.pending.get(requestId) : undefined;
12318
- if (requestId && entry) {
12319
- this.pending.delete(requestId);
12320
- clearTimeout(entry.timer);
12321
- entry.resolve({
12322
- success: false,
12323
- code: "permission_required",
12324
- message: "bash command requires permission",
12325
- asks: response.asks
12326
- });
12327
- }
12328
- continue;
12329
- }
12330
- if (response.type === "bash_completed") {
12331
- this.onBashCompletion?.(response, this);
12332
- continue;
12333
- }
12334
- if (response.type === "bash_long_running") {
12335
- this.onBashLongRunning?.(response, this);
12336
- continue;
12337
- }
12338
- if (response.type === "bash_pattern_match") {
12339
- this.onBashPatternMatch?.(response, this);
12340
- continue;
12341
- }
12342
- if (response.type === "configure_warnings") {
12343
- this.handleConfigureWarningsFrame(response).catch((err) => {
12344
- this.warnVia(`configure warning delivery failed: ${err instanceof Error ? err.message : String(err)}`);
12345
- });
12346
- continue;
12347
- }
12348
- if (response.type === "status_changed") {
12349
- this.handleStatusChangedFrame(response);
12350
- continue;
12351
- }
12352
- const id = response.id;
12353
- if (id && this.pending.has(id)) {
12354
- const entry = this.pending.get(id);
12355
- if (!entry)
12356
- continue;
12357
- this.pending.delete(id);
12308
+ this.processStdoutLine(line);
12309
+ }
12310
+ }
12311
+ flushStdoutBuffer() {
12312
+ const line = this.stdoutBuffer.trim();
12313
+ this.stdoutBuffer = "";
12314
+ if (!line)
12315
+ return;
12316
+ this.processStdoutLine(line);
12317
+ }
12318
+ processStdoutLine(line) {
12319
+ try {
12320
+ const response = JSON.parse(line);
12321
+ this.lastChildActivityAt = Date.now();
12322
+ if (response.type === "progress") {
12323
+ const requestId = response.request_id;
12324
+ const entry = requestId ? this.pending.get(requestId) : undefined;
12325
+ const kind = response.kind === "stderr" ? "stderr" : "stdout";
12326
+ const text = typeof response.chunk === "string" ? response.chunk : "";
12327
+ entry?.onProgress?.({ kind, text });
12328
+ return;
12329
+ }
12330
+ if (response.type === "permission_ask") {
12331
+ const requestId = response.request_id;
12332
+ const entry = requestId ? this.pending.get(requestId) : undefined;
12333
+ if (requestId && entry) {
12334
+ this.pending.delete(requestId);
12358
12335
  clearTimeout(entry.timer);
12359
- this.consecutiveRequestTimeouts = 0;
12360
- this.scheduleRestartCountReset();
12361
- this.captureStatusBar(response);
12362
- entry.resolve(response);
12363
- } else if (typeof response.type === "string") {
12364
- this.logVia(`Ignoring unknown stdout push frame type: ${response.type}`);
12336
+ entry.resolve({
12337
+ success: false,
12338
+ code: "permission_required",
12339
+ message: "bash command requires permission",
12340
+ asks: response.asks
12341
+ });
12365
12342
  }
12366
- } catch (_err) {
12367
- this.warnVia(`Failed to parse stdout line: ${line}`);
12343
+ return;
12344
+ }
12345
+ if (response.type === "bash_completed") {
12346
+ this.onBashCompletion?.(response, this);
12347
+ return;
12348
+ }
12349
+ if (response.type === "bash_long_running") {
12350
+ this.onBashLongRunning?.(response, this);
12351
+ return;
12352
+ }
12353
+ if (response.type === "bash_pattern_match") {
12354
+ this.onBashPatternMatch?.(response, this);
12355
+ return;
12356
+ }
12357
+ if (response.type === "configure_warnings") {
12358
+ this.handleConfigureWarningsFrame(response).catch((err) => {
12359
+ this.warnVia(`configure warning delivery failed: ${err instanceof Error ? err.message : String(err)}`);
12360
+ });
12361
+ return;
12362
+ }
12363
+ if (response.type === "status_changed") {
12364
+ this.handleStatusChangedFrame(response);
12365
+ return;
12366
+ }
12367
+ const id = response.id;
12368
+ if (id && this.pending.has(id)) {
12369
+ const entry = this.pending.get(id);
12370
+ if (!entry)
12371
+ return;
12372
+ this.pending.delete(id);
12373
+ clearTimeout(entry.timer);
12374
+ this.consecutiveRequestTimeouts = 0;
12375
+ this.scheduleRestartCountReset();
12376
+ this.captureStatusBar(response);
12377
+ entry.resolve(response);
12378
+ } else if (typeof response.type === "string") {
12379
+ this.logVia(`Ignoring unknown stdout push frame type: ${response.type}`);
12368
12380
  }
12381
+ } catch (_err) {
12382
+ this.warnVia(`Failed to parse stdout line: ${line}`);
12369
12383
  }
12370
12384
  }
12371
12385
  captureStatusBar(response) {
@@ -14493,13 +14507,7 @@ function consumeBgCompletion(sessionID, taskId) {
14493
14507
  state.consumedTaskIds.delete(evicted);
14494
14508
  }
14495
14509
  }
14496
- if (state.pendingCompletions.length === 0 && state.pendingLongRunning.length === 0 && state.pendingPatternMatches.length === 0 && state.debounceTimer) {
14497
- clearTimeout(state.debounceTimer);
14498
- state.debounceTimer = null;
14499
- state.firstCompletionAt = null;
14500
- state.scheduledFireAt = null;
14501
- state.scheduledCompletionCount = 0;
14502
- }
14510
+ clearWakeTimerIfNoPending(state);
14503
14511
  }
14504
14512
  async function markBgCompletionDelivered(drainContext, taskId) {
14505
14513
  await ackCompletions(drainContext, [
@@ -14670,16 +14678,11 @@ async function appendInTurnBgCompletions(drainContext, output) {
14670
14678
  }
14671
14679
  state.pendingLongRunning = [];
14672
14680
  state.pendingPatternMatches = [];
14681
+ state.retryDelayMs = null;
14673
14682
  state.wakeRetryAttempts = 0;
14674
14683
  state.wakeHardStopped = false;
14675
14684
  await ackCompletions(drainContext, completionAcks);
14676
- if (state.debounceTimer) {
14677
- clearTimeout(state.debounceTimer);
14678
- state.debounceTimer = null;
14679
- state.firstCompletionAt = null;
14680
- state.scheduledFireAt = null;
14681
- state.scheduledCompletionCount = 0;
14682
- }
14685
+ clearWakeTimerIfNoPending(state);
14683
14686
  }
14684
14687
  async function handleIdleBgCompletions(drainContext) {
14685
14688
  stateFor(drainContext.sessionID).wakeDeferredTaskIds.clear();
@@ -14921,13 +14924,18 @@ function wakeEligibleCompletions(state, includeDeferredCompletions) {
14921
14924
  return state.pendingCompletions.filter((completion) => !state.wakeDeferredTaskIds.has(completion.task_id));
14922
14925
  }
14923
14926
  function clearWakeTimerIfNoPending(state) {
14924
- if (state.pendingCompletions.length === 0 && state.pendingLongRunning.length === 0 && state.pendingPatternMatches.length === 0 && state.debounceTimer) {
14925
- clearTimeout(state.debounceTimer);
14926
- state.debounceTimer = null;
14927
- state.firstCompletionAt = null;
14928
- state.scheduledFireAt = null;
14929
- state.scheduledCompletionCount = 0;
14927
+ if (state.pendingCompletions.length > 0 || state.pendingLongRunning.length > 0 || state.pendingPatternMatches.length > 0) {
14928
+ return;
14930
14929
  }
14930
+ if (state.debounceTimer)
14931
+ clearTimeout(state.debounceTimer);
14932
+ state.debounceTimer = null;
14933
+ state.firstCompletionAt = null;
14934
+ state.scheduledFireAt = null;
14935
+ state.scheduledCompletionCount = 0;
14936
+ state.retryDelayMs = null;
14937
+ state.wakeRetryAttempts = 0;
14938
+ state.wakeHardStopped = false;
14931
14939
  }
14932
14940
  function scheduleWake(state, sendWake, onSendFailure, sessionID, includeDeferredCompletions = true) {
14933
14941
  if (state.wakeHardStopped)
@@ -30598,7 +30606,16 @@ function showToast(ctx, title, message, variant = "info", duration3 = 3000) {
30598
30606
  // src/lsp-auto-install.ts
30599
30607
  import { spawn as spawn3 } from "node:child_process";
30600
30608
  import { createHash as createHash4 } from "node:crypto";
30601
- import { createReadStream, mkdirSync as mkdirSync8, readFileSync as readFileSync11, renameSync as renameSync6, rmSync as rmSync5, statSync as statSync6 } from "node:fs";
30609
+ import {
30610
+ createReadStream,
30611
+ existsSync as existsSync12,
30612
+ mkdirSync as mkdirSync8,
30613
+ readFileSync as readFileSync11,
30614
+ renameSync as renameSync6,
30615
+ rmSync as rmSync5,
30616
+ statSync as statSync6,
30617
+ writeFileSync as writeFileSync8
30618
+ } from "node:fs";
30602
30619
  import { join as join16 } from "node:path";
30603
30620
 
30604
30621
  // src/lsp-cache.ts
@@ -31173,6 +31190,17 @@ async function resolveTargetVersion(spec, config2, fetchImpl = fetch) {
31173
31190
  writeVersionCheck(spec.npm, probe.version);
31174
31191
  return { version: probe.version, pinned: false, probe };
31175
31192
  }
31193
+ function ensureInstallAnchor(cwd) {
31194
+ try {
31195
+ const stub = join16(cwd, "package.json");
31196
+ if (!existsSync12(stub)) {
31197
+ writeFileSync8(stub, `${JSON.stringify({ name: "aft-lsp-cache", version: "0.0.0", private: true })}
31198
+ `);
31199
+ }
31200
+ } catch (err) {
31201
+ warn2(`[lsp] could not write package.json stub in ${cwd}: ${err}`);
31202
+ }
31203
+ }
31176
31204
  function runInstall(spec, version2, cwd, signal) {
31177
31205
  return new Promise((resolve3) => {
31178
31206
  const target = `${spec.npm}@${version2}`;
@@ -31188,6 +31216,7 @@ function runInstall(spec, version2, cwd, signal) {
31188
31216
  resolve3(false);
31189
31217
  return;
31190
31218
  }
31219
+ ensureInstallAnchor(cwd);
31191
31220
  const child = spawn3(npm.command, ["install", "--no-save", "--ignore-scripts", "--silent", target], {
31192
31221
  stdio: ["ignore", "pipe", "pipe"],
31193
31222
  cwd,
@@ -31441,7 +31470,7 @@ import {
31441
31470
  copyFileSync as copyFileSync4,
31442
31471
  createReadStream as createReadStream2,
31443
31472
  createWriteStream as createWriteStream3,
31444
- existsSync as existsSync12,
31473
+ existsSync as existsSync13,
31445
31474
  lstatSync as lstatSync2,
31446
31475
  mkdirSync as mkdirSync9,
31447
31476
  readdirSync as readdirSync4,
@@ -31452,7 +31481,7 @@ import {
31452
31481
  rmSync as rmSync6,
31453
31482
  statSync as statSync7,
31454
31483
  unlinkSync as unlinkSync6,
31455
- writeFileSync as writeFileSync8
31484
+ writeFileSync as writeFileSync9
31456
31485
  } from "node:fs";
31457
31486
  import { dirname as dirname8, join as join17, relative as relative2, resolve as resolve3 } from "node:path";
31458
31487
  import { Readable as Readable3 } from "node:stream";
@@ -31603,7 +31632,7 @@ function writeGithubInstalledMetaIn(installDir, version2, binarySha256, archiveS
31603
31632
  binarySha256,
31604
31633
  ...archiveSha256 ? { archiveSha256 } : {}
31605
31634
  };
31606
- writeFileSync8(join17(installDir, INSTALLED_META_FILE2), JSON.stringify(meta3), "utf8");
31635
+ writeFileSync9(join17(installDir, INSTALLED_META_FILE2), JSON.stringify(meta3), "utf8");
31607
31636
  } catch (err) {
31608
31637
  warn2(`[lsp] failed to write github installed metadata in ${installDir}: ${err}`);
31609
31638
  }
@@ -32072,7 +32101,7 @@ async function downloadAndInstall(spec, tag, assets, platform3, arch, fetchImpl,
32072
32101
  } catch {}
32073
32102
  }
32074
32103
  const innerBinaryPath = join17(extractDir, spec.binaryPathInArchive(platform3, arch, version2));
32075
- if (!existsSync12(innerBinaryPath)) {
32104
+ if (!existsSync13(innerBinaryPath)) {
32076
32105
  error2(`[lsp] ${spec.id}: extracted binary not found at ${innerBinaryPath}`);
32077
32106
  return null;
32078
32107
  }
@@ -32156,7 +32185,7 @@ function runGithubAutoInstall(relevantServers, config2, fetchImpl = fetch) {
32156
32185
  if (!host) {
32157
32186
  for (const spec of GITHUB_LSP_TABLE) {
32158
32187
  try {
32159
- if (existsSync12(ghBinDir(spec))) {
32188
+ if (existsSync13(ghBinDir(spec))) {
32160
32189
  cachedBinDirs.push(ghBinDir(spec));
32161
32190
  }
32162
32191
  } catch {}
@@ -32403,7 +32432,7 @@ function writeTerminal(terminal, data) {
32403
32432
 
32404
32433
  // src/shared/rpc-server.ts
32405
32434
  import { randomBytes as randomBytes2 } from "node:crypto";
32406
- import { mkdirSync as mkdirSync10, renameSync as renameSync8, unlinkSync as unlinkSync7, writeFileSync as writeFileSync9 } from "node:fs";
32435
+ import { mkdirSync as mkdirSync10, renameSync as renameSync8, unlinkSync as unlinkSync7, writeFileSync as writeFileSync10 } from "node:fs";
32407
32436
  import { createServer } from "node:http";
32408
32437
  import { dirname as dirname9, join as join19 } from "node:path";
32409
32438
 
@@ -32456,7 +32485,7 @@ class AftRpcServer {
32456
32485
  const dir = dirname9(this.portFilePath);
32457
32486
  mkdirSync10(dir, { recursive: true, mode: 448 });
32458
32487
  const tmpPath = `${this.portFilePath}.tmp`;
32459
- writeFileSync9(tmpPath, JSON.stringify({ port: this.port, token: this.token }), {
32488
+ writeFileSync10(tmpPath, JSON.stringify({ port: this.port, token: this.token }), {
32460
32489
  encoding: "utf-8",
32461
32490
  mode: 384
32462
32491
  });
@@ -32632,6 +32661,20 @@ function readCompression(value) {
32632
32661
  session: readCompressionAggregate(compression.session)
32633
32662
  };
32634
32663
  }
32664
+ function readStatusBar(value) {
32665
+ if (typeof value !== "object" || value === null)
32666
+ return;
32667
+ const bar = asRecord(value);
32668
+ return {
32669
+ errors: readNumber(bar.errors),
32670
+ warnings: readNumber(bar.warnings),
32671
+ dead_code: readNumber(bar.dead_code),
32672
+ unused_exports: readNumber(bar.unused_exports),
32673
+ duplicates: readNumber(bar.duplicates),
32674
+ todos: readNumber(bar.todos),
32675
+ tier2_stale: readBoolean(bar.tier2_stale)
32676
+ };
32677
+ }
32635
32678
  function formatFlag(enabled) {
32636
32679
  return enabled ? "enabled" : "disabled";
32637
32680
  }
@@ -32725,6 +32768,7 @@ function coerceAftStatus(response) {
32725
32768
  checkpoints: readNumber(session.checkpoints)
32726
32769
  },
32727
32770
  compression: readCompression(response.compression),
32771
+ status_bar: readStatusBar(response.status_bar),
32728
32772
  message: readString(response.message, "")
32729
32773
  };
32730
32774
  }
@@ -32780,6 +32824,10 @@ function formatStatusMarkdown(status) {
32780
32824
  if (status.storage_dir ?? status.disk.storage_dir) {
32781
32825
  lines.push(`- **Storage dir:** \`${status.storage_dir ?? status.disk.storage_dir}\``);
32782
32826
  }
32827
+ if (status.status_bar) {
32828
+ const sb = status.status_bar;
32829
+ lines.push("", `### Code Health${sb.tier2_stale ? " (~ stale)" : ""}`, `- **Errors:** ${formatCount(sb.errors)}`, `- **Warnings:** ${formatCount(sb.warnings)}`, `- **Duplicates:** ${formatCount(sb.duplicates)}`, `- **TODOs:** ${formatCount(sb.todos)}`);
32830
+ }
32783
32831
  lines.push("", "### Current session", `- **ID:** \`${status.session.id}\``, `- **Tracked files:** ${formatCount(status.session.tracked_files)}`, `- **Checkpoints:** ${formatCount(status.session.checkpoints)}`, `- **Project checkpoints (all sessions):** ${formatCount(status.checkpoints_total)}`);
32784
32832
  return lines.join(`
32785
32833
  `);
@@ -32787,7 +32835,7 @@ function formatStatusMarkdown(status) {
32787
32835
 
32788
32836
  // src/shared/tui-config.ts
32789
32837
  var import_comment_json4 = __toESM(require_src2(), 1);
32790
- import { existsSync as existsSync13, mkdirSync as mkdirSync11, readFileSync as readFileSync13, writeFileSync as writeFileSync10 } from "node:fs";
32838
+ import { existsSync as existsSync14, mkdirSync as mkdirSync11, readFileSync as readFileSync13, writeFileSync as writeFileSync11 } from "node:fs";
32791
32839
  import { dirname as dirname10, join as join21 } from "node:path";
32792
32840
 
32793
32841
  // src/shared/opencode-config-dir.ts
@@ -32824,9 +32872,9 @@ function resolveTuiConfigPath() {
32824
32872
  const configDir = getOpenCodeConfigPaths({ binary: "opencode" }).configDir;
32825
32873
  const jsoncPath = join21(configDir, "tui.jsonc");
32826
32874
  const jsonPath = join21(configDir, "tui.json");
32827
- if (existsSync13(jsoncPath))
32875
+ if (existsSync14(jsoncPath))
32828
32876
  return jsoncPath;
32829
- if (existsSync13(jsonPath))
32877
+ if (existsSync14(jsonPath))
32830
32878
  return jsonPath;
32831
32879
  return jsonPath;
32832
32880
  }
@@ -32834,7 +32882,7 @@ function ensureTuiPluginEntry() {
32834
32882
  try {
32835
32883
  const configPath = resolveTuiConfigPath();
32836
32884
  let config2 = {};
32837
- if (existsSync13(configPath)) {
32885
+ if (existsSync14(configPath)) {
32838
32886
  config2 = import_comment_json4.parse(readFileSync13(configPath, "utf-8")) ?? {};
32839
32887
  }
32840
32888
  const plugins = Array.isArray(config2.plugin) ? config2.plugin.filter((value) => typeof value === "string") : [];
@@ -32844,7 +32892,7 @@ function ensureTuiPluginEntry() {
32844
32892
  plugins.push(PLUGIN_ENTRY);
32845
32893
  config2.plugin = plugins;
32846
32894
  mkdirSync11(dirname10(configPath), { recursive: true });
32847
- writeFileSync10(configPath, `${import_comment_json4.stringify(config2, null, 2)}
32895
+ writeFileSync11(configPath, `${import_comment_json4.stringify(config2, null, 2)}
32848
32896
  `);
32849
32897
  log2(`[aft-plugin] added TUI plugin entry to ${configPath}`);
32850
32898
  return true;
@@ -32947,6 +32995,7 @@ function isEmptyParam(value) {
32947
32995
  return Object.keys(value).length === 0;
32948
32996
  return false;
32949
32997
  }
32998
+ var BASH_TRANSPORT_TIMEOUT_MS = 30000;
32950
32999
  var LONG_RUNNING_COMMAND_TIMEOUT_MS = {
32951
33000
  callers: 60000,
32952
33001
  trace_to: 60000,
@@ -33082,6 +33131,13 @@ async function callBridge(ctx, runtime, command, params = {}, options) {
33082
33131
  ingestBgCompletions(runtime.sessionID, response.bg_completions);
33083
33132
  return response;
33084
33133
  }
33134
+ async function callBashBridge(ctx, runtime, command, params = {}, options) {
33135
+ return await callBridge(ctx, runtime, command, params, {
33136
+ transportTimeoutMs: BASH_TRANSPORT_TIMEOUT_MS,
33137
+ ...options,
33138
+ keepBridgeOnTimeout: true
33139
+ });
33140
+ }
33085
33141
 
33086
33142
  // src/tools/permissions.ts
33087
33143
  import * as fs4 from "node:fs";
@@ -33907,7 +33963,6 @@ function setCache2(sessionId, isSubagent) {
33907
33963
  var z5 = tool5.schema;
33908
33964
  var METADATA_PREVIEW_LIMIT = 30 * 1024;
33909
33965
  var FOREGROUND_POLL_INTERVAL_MS = 100;
33910
- var BASH_TRANSPORT_TIMEOUT_MS = 30000;
33911
33966
  var DEFAULT_HARD_TIMEOUT_MS = 30 * 60 * 1000;
33912
33967
  var BASH_DESCRIPTION = `Hoisted bash tool with output compression, command rewriting to AFT tools, optional background execution, and PTY mode for interactive programs. By default, output is compressed; pass compressed: false for raw output. Pass background: true to spawn in the background and get a taskId for bash_status/bash_kill. Pass pty: true for interactive REPLs and drive them with bash_status({ outputMode: "screen" }) plus bash_write (pty implies background automatically). Use bash_watch to block on or register for pattern matches and exit events.`;
33913
33968
  async function withPermissionLoop(ctx, runtime, params, bridgeCall, options) {
@@ -33979,9 +34034,7 @@ function createBashTool(ctx) {
33979
34034
  pty_rows: args.ptyRows,
33980
34035
  pty_cols: args.ptyCols,
33981
34036
  permissions_requested: true
33982
- }, callBridge, {
33983
- transportTimeoutMs: BASH_TRANSPORT_TIMEOUT_MS,
33984
- keepBridgeOnTimeout: true,
34037
+ }, callBashBridge, {
33985
34038
  onProgress: ({ text }) => {
33986
34039
  accumulatedOutput = preview(accumulatedOutput + text);
33987
34040
  metadata?.({ output: accumulatedOutput, description });
@@ -34013,7 +34066,7 @@ function createBashTool(ctx) {
34013
34066
  const waitTimeoutMs = subagentForcedForeground ? argTimeout ?? DEFAULT_HARD_TIMEOUT_MS : argTimeout !== undefined ? Math.min(argTimeout, foregroundWaitMs) : foregroundWaitMs;
34014
34067
  const startedAt = Date.now();
34015
34068
  while (true) {
34016
- const status = await callBridge(ctx, context, "bash_status", { task_id: taskId });
34069
+ const status = await callBashBridge(ctx, context, "bash_status", { task_id: taskId });
34017
34070
  if (status.success === false) {
34018
34071
  throw new Error(status.message ?? "bash_status failed");
34019
34072
  }
@@ -34034,7 +34087,9 @@ function createBashTool(ctx) {
34034
34087
  await sleep(FOREGROUND_POLL_INTERVAL_MS);
34035
34088
  continue;
34036
34089
  }
34037
- const promoted = await callBridge(ctx, context, "bash_promote", { task_id: taskId });
34090
+ const promoted = await callBashBridge(ctx, context, "bash_promote", {
34091
+ task_id: taskId
34092
+ });
34038
34093
  if (promoted.success === false) {
34039
34094
  throw new Error(promoted.message ?? "bash_promote failed");
34040
34095
  }
@@ -34115,7 +34170,7 @@ function createBashKillTool(ctx) {
34115
34170
  taskId: z5.string().describe("Background task ID returned by bash({ background: true }), e.g. bash-6b454047.")
34116
34171
  },
34117
34172
  execute: async (args, context) => {
34118
- const data = await callBridge(ctx, context, "bash_kill", {
34173
+ const data = await callBashBridge(ctx, context, "bash_kill", {
34119
34174
  task_id: args.taskId
34120
34175
  });
34121
34176
  if (data.success === false) {
@@ -34130,7 +34185,7 @@ function createBashKillTool(ctx) {
34130
34185
  };
34131
34186
  }
34132
34187
  async function bashStatusSnapshot(ctx, runtime, taskId, outputMode, options) {
34133
- const data = await callBridge(ctx, runtime, "bash_status", { task_id: taskId, output_mode: outputMode }, options);
34188
+ const data = await callBashBridge(ctx, runtime, "bash_status", { task_id: taskId, output_mode: outputMode }, options);
34134
34189
  if (data.success === false) {
34135
34190
  throw new Error(data.message ?? "bash_status failed");
34136
34191
  }
@@ -34268,7 +34323,6 @@ var z6 = tool6.schema;
34268
34323
  var BASH_WAIT_POLL_INTERVAL_MS = 100;
34269
34324
  var DEFAULT_BASH_STATUS_WAIT_TIMEOUT_MS = 30000;
34270
34325
  var MAX_BASH_STATUS_WAIT_TIMEOUT_MS = 300000;
34271
- var BASH_TRANSPORT_TIMEOUT_MS2 = 30000;
34272
34326
  var REGEX_WAIT_SCAN_WINDOW_BYTES = 64 * 1024;
34273
34327
  function createBashWatchTool(ctx) {
34274
34328
  return {
@@ -34303,7 +34357,7 @@ function createBashWatchTool(ctx) {
34303
34357
  markExplicitControl(context.sessionID, taskId, false);
34304
34358
  let registered;
34305
34359
  try {
34306
- registered = await callBridge(ctx, context, "bash_notify", notifyParams);
34360
+ registered = await callBashBridge(ctx, context, "bash_notify", notifyParams);
34307
34361
  } catch (err) {
34308
34362
  unmarkExplicitControl(context.sessionID, taskId);
34309
34363
  throw err;
@@ -34316,7 +34370,6 @@ function createBashWatchTool(ctx) {
34316
34370
  throw new Error(`invalid_request: ${message}`);
34317
34371
  throw new Error(`${code}: ${message}`);
34318
34372
  }
34319
- markExplicitControl(context.sessionID, taskId);
34320
34373
  const metadata2 = context.metadata;
34321
34374
  metadata2?.({ taskId, registered: true, watchId: registered.watch_id });
34322
34375
  return `Watch registered: ${registered.watch_id} on task ${taskId}
@@ -34359,7 +34412,7 @@ ${preview2.slice(0, 2000)}`;
34359
34412
  return text;
34360
34413
  }
34361
34414
  async function bashStatusSnapshot2(ctx, runtime, taskId, outputMode, options) {
34362
- const data = await callBridge(ctx, runtime, "bash_status", { task_id: taskId, output_mode: outputMode }, options);
34415
+ const data = await callBashBridge(ctx, runtime, "bash_status", { task_id: taskId, output_mode: outputMode }, options);
34363
34416
  if (data.success === false)
34364
34417
  throw new Error(data.message ?? "bash_status failed");
34365
34418
  return data;
@@ -34370,10 +34423,7 @@ async function waitForBashStatus(ctx, runtime, taskId, outputMode, waitFor, effe
34370
34423
  let spillCursor = { output: 0, stderr: 0, combined: 0 };
34371
34424
  let scanText = "";
34372
34425
  let scanBaseOffset = 0;
34373
- const bridgeOptions = {
34374
- keepBridgeOnTimeout: true,
34375
- transportTimeoutMs: BASH_TRANSPORT_TIMEOUT_MS2
34376
- };
34426
+ const bridgeOptions = {};
34377
34427
  markTaskWaiting(runtime.sessionID, taskId);
34378
34428
  let sawTerminal = false;
34379
34429
  try {
@@ -34559,7 +34609,7 @@ function createBashWriteTool(ctx) {
34559
34609
  ]).describe("Either a string of verbatim bytes (e.g. 'print(1)\\n') OR an array mixing strings " + "and { key: '<name>' } objects for atomic text+key sequences. " + "Example: [ 'iHello', { key: 'esc' }, ':wq', { key: 'enter' } ].")
34560
34610
  },
34561
34611
  execute: async (args, context) => {
34562
- const data = await callBridge(ctx, context, "bash_write", {
34612
+ const data = await callBashBridge(ctx, context, "bash_write", {
34563
34613
  task_id: args.taskId,
34564
34614
  input: args.input
34565
34615
  });
@@ -37272,14 +37322,12 @@ var PLUGIN_VERSION = (() => {
37272
37322
  return "0.0.0";
37273
37323
  }
37274
37324
  })();
37275
- var ANNOUNCEMENT_VERSION = "0.35.0";
37325
+ var ANNOUNCEMENT_VERSION = "0.35.3";
37276
37326
  var ANNOUNCEMENT_FEATURES = [
37277
- "Leaner semantic indexing local embedding now uses about half the CPU and a fraction of the peak memory, with no change to results and no re-index needed.",
37278
- "New IDE-style status bar on tool results live LSP errors/warnings plus dead-code, unused-export, duplicate, and TODO counts at a glance.",
37279
- "`aft_inspect` is more accurate (dead-code reachability, duplicate collapse, ranked findings) and a background scheduler keeps its counts fresh during long sessions.",
37280
- "The TUI sidebar is now collapsible click the `AFT` header to toggle a condensed view.",
37281
- "`npx @cortexkit/aft --version` reports CLI, binary, and per-harness versions; `doctor --issue` can scope its report to a single session.",
37282
- "New SCSS support and `.inc` files parsed as PHP."
37327
+ "Code Health in the TUI sidebar and `/aft-status`: live LSP errors and warnings plus duplicate and TODO counts, shown as at-a-glance traffic lights when the sidebar is collapsed.",
37328
+ "The semantic index now recovers on its own from a transient embedding-backend blip (a restarted local server, or a model still loading) instead of getting stuck on `failed`.",
37329
+ "Fixed a background codebase-scan crash on very deep or minified files.",
37330
+ "More reliable LSP auto-install when a parent directory has its own `package.json`."
37283
37331
  ];
37284
37332
  var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/DSa65w8wuf";
37285
37333
  var plugin = async (input) => initializePluginForDirectory(input);
@@ -37428,8 +37476,8 @@ ${lines}
37428
37476
  delivery: projectConfig.configure_warnings_delivery ?? "toast"
37429
37477
  });
37430
37478
  },
37431
- onBashCompletion: (completion) => {
37432
- const sessionDir = getSessionDirectoryCached(completion.session_id) ?? input.directory;
37479
+ onBashCompletion: (completion, bridge) => {
37480
+ const sessionDir = bridge.getCwd();
37433
37481
  handlePushedBgCompletion({
37434
37482
  ctx,
37435
37483
  directory: sessionDir,
@@ -37438,8 +37486,8 @@ ${lines}
37438
37486
  serverUrl: input.serverUrl?.toString()
37439
37487
  }, completion);
37440
37488
  },
37441
- onBashLongRunning: (reminder) => {
37442
- const sessionDir = getSessionDirectoryCached(reminder.session_id) ?? input.directory;
37489
+ onBashLongRunning: (reminder, bridge) => {
37490
+ const sessionDir = bridge.getCwd();
37443
37491
  handlePushedBgLongRunning({
37444
37492
  ctx,
37445
37493
  directory: sessionDir,
@@ -37448,8 +37496,8 @@ ${lines}
37448
37496
  serverUrl: input.serverUrl?.toString()
37449
37497
  }, reminder);
37450
37498
  },
37451
- onBashPatternMatch: (frame) => {
37452
- const sessionDir = getSessionDirectoryCached(frame.session_id) ?? input.directory;
37499
+ onBashPatternMatch: (frame, bridge) => {
37500
+ const sessionDir = bridge.getCwd();
37453
37501
  handlePushedPatternMatch({
37454
37502
  ctx,
37455
37503
  directory: sessionDir,
@@ -71,6 +71,36 @@ export interface AutoInstallResult {
71
71
  installsComplete: Promise<void>;
72
72
  }
73
73
  export declare function abortInFlightAutoInstalls(): Promise<void>;
74
+ /**
75
+ * Spawn `npm install --no-save <pkg>@<version>` in the cache dir.
76
+ *
77
+ * Uses `--ignore-scripts` to neutralize lifecycle hooks (the v0.16 audit
78
+ * hardening). Output goes to plugin log.
79
+ *
80
+ * Previously this used `bun add`, but OpenCode runs under whichever runtime
81
+ * the user has installed (Node 22 is common), and bun is not guaranteed to
82
+ * be on PATH. Every install would fail silently with ENOENT, so users
83
+ * without bun saw recurring `lsp_binary_missing` warnings for newer servers
84
+ * like `@vue/language-server` even though `lsp.auto_install` was true.
85
+ * GitHub #46 reported this exact symptom against v0.27.0.
86
+ *
87
+ * npm is guaranteed to be present whenever the plugin runs through OpenCode's
88
+ * normal CLI distribution path, matches Pi's auto-install behavior, and is
89
+ * what OpenCode itself uses for its built-in LSP auto-install.
90
+ */
91
+ /**
92
+ * Anchor an `npm install --no-save` to `cwd` by writing a minimal package.json.
93
+ *
94
+ * Without a package.json in `cwd`, npm walks UP the directory tree; if any
95
+ * ancestor (e.g. ~/package.json) has one, npm installs into THAT package's
96
+ * node_modules instead, leaving our cache dir's node_modules/<server> missing
97
+ * while still exiting 0. The result is a silent install failure and a recurring
98
+ * lsp_binary_missing warning. The old `bun add` flow created this package.json
99
+ * implicitly; npm needs it written explicitly. GitHub #92.
100
+ *
101
+ * Idempotent: only writes when absent. Failures are non-fatal (logged).
102
+ */
103
+ export declare function ensureInstallAnchor(cwd: string): void;
74
104
  /**
75
105
  * Top-level entry point. Returns the list of bin directories that already
76
106
  * have an installed binary AND kicks off background installs for missing
@@ -1 +1 @@
1
- {"version":3,"file":"lsp-auto-install.d.ts","sourceRoot":"","sources":["../src/lsp-auto-install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AA6BH,+DAA+D;AAC/D,MAAM,WAAW,iBAAiB;IAChC,oCAAoC;IACpC,WAAW,EAAE,OAAO,CAAC;IACrB,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3C,kGAAkG;IAClG,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC/B;AAED,qCAAqC;AACrC,MAAM,WAAW,iBAAiB;IAChC,mFAAmF;IACnF,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gDAAgD;IAChD,eAAe,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B;;;;;;;OAOG;IACH,OAAO,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C;;;;;;;;;OASG;IACH,gBAAgB,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AA8DD,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC,CAM/D;AAsYD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,iBAAiB,EACzB,SAAS,GAAE,OAAO,KAAa,GAC9B,iBAAiB,CAwEnB"}
1
+ {"version":3,"file":"lsp-auto-install.d.ts","sourceRoot":"","sources":["../src/lsp-auto-install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAsCH,+DAA+D;AAC/D,MAAM,WAAW,iBAAiB;IAChC,oCAAoC;IACpC,WAAW,EAAE,OAAO,CAAC;IACrB,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3C,kGAAkG;IAClG,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC/B;AAED,qCAAqC;AACrC,MAAM,WAAW,iBAAiB;IAChC,mFAAmF;IACnF,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gDAAgD;IAChD,eAAe,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B;;;;;;;OAOG;IACH,OAAO,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C;;;;;;;;;OASG;IACH,gBAAgB,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AA8DD,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC,CAM/D;AA4DD;;;;;;;;;;;;;;;;GAgBG;AACH;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAYrD;AA8TD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,iBAAiB,EACzB,SAAS,GAAE,OAAO,KAAa,GAC9B,iBAAiB,CAwEnB"}
@@ -8,6 +8,23 @@ export interface StatusCompression {
8
8
  project: StatusCompressionAggregate;
9
9
  session: StatusCompressionAggregate;
10
10
  }
11
+ /**
12
+ * The agent status-bar glance — `[AFT E· W· | D· U· C· | T·]`. `errors`/
13
+ * `warnings` are live LSP diagnostics for files touched this session; the
14
+ * Tier-2 trio (`dead_code`/`unused_exports`/`duplicates`) plus `todos` come
15
+ * from the last completed background scan. `tier2_stale` marks those Tier-2
16
+ * counts as predating the latest edit. Null in the snapshot until the Tier-2
17
+ * cache is populated at least once (no fabricated zeros).
18
+ */
19
+ export interface StatusBar {
20
+ errors: number;
21
+ warnings: number;
22
+ dead_code: number;
23
+ unused_exports: number;
24
+ duplicates: number;
25
+ todos: number;
26
+ tier2_stale: boolean;
27
+ }
11
28
  export interface AftStatusSnapshot {
12
29
  version: string;
13
30
  project_root: string | null;
@@ -70,6 +87,12 @@ export interface AftStatusSnapshot {
70
87
  };
71
88
  /** Compression aggregate passthrough; rendering is added separately. */
72
89
  compression?: StatusCompression;
90
+ /**
91
+ * Agent status-bar counts (E/W/D/U/C/T). Undefined until the Tier-2 cache
92
+ * is populated at least once — the sidebar hides the Code Health section
93
+ * until then so it never shows fabricated zeros.
94
+ */
95
+ status_bar?: StatusBar;
73
96
  /**
74
97
  * Human-readable explanation for a synthetic snapshot (e.g.
75
98
  * `cache_role === "not_initialized"`). When the plugin returns a placeholder
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/shared/status.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/shared/status.ts"],"names":[],"mappings":"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"}
@@ -77,6 +77,7 @@ export declare function isEmptyParam(value: unknown): boolean;
77
77
  * file tree or wait on external I/O (embedding API, index build). The
78
78
  * goal is to absorb slow first-call spikes without masking real hangs.
79
79
  */
80
+ export declare const BASH_TRANSPORT_TIMEOUT_MS = 30000;
80
81
  export declare const LONG_RUNNING_COMMAND_TIMEOUT_MS: Record<string, number>;
81
82
  /** Returns the per-command timeout override, or undefined to use the bridge default. */
82
83
  export declare function timeoutForCommand(command: string): number | undefined;
@@ -160,6 +161,13 @@ export declare function bridgeFor(ctx: PluginContext, runtime: ToolRuntime): Bin
160
161
  * session identifier still work — they just share undo/checkpoint state.
161
162
  */
162
163
  export declare function callBridge(ctx: PluginContext, runtime: ToolRuntime, command: string, params?: Record<string, unknown>, options?: BridgeRequestOptions): Promise<Record<string, unknown>>;
164
+ /**
165
+ * Send a bash-family command without restarting the shared bridge on transport
166
+ * timeout. Bash has its own child-process timeout/watchdog handling; a late
167
+ * transport response must not sacrifice the warm shared bridge and reject
168
+ * unrelated sibling requests.
169
+ */
170
+ export declare function callBashBridge(ctx: PluginContext, runtime: ToolRuntime, command: string, params?: Record<string, unknown>, options?: BridgeRequestOptions): Promise<Record<string, unknown>>;
163
171
  /**
164
172
  * Eagerly warm the session-directory cache for a runtime. Safe to call from
165
173
  * synchronous code — the lookup runs in the background and failures are
@@ -1 +1 @@
1
- {"version":3,"file":"_shared.d.ts","sourceRoot":"","sources":["../../src/tools/_shared.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAQhF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,EAAE,KAAK,MAAM,KAAG,GACR,CAAC;AAEhD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,CAAC,EAAE,OAAO,EACV,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAWpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAMpD;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,+BAA+B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CASlE,CAAC;AAEF,wFAAwF;AACxF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAErE;AA2BD,8EAA8E;AAC9E,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,MAAM,CAsDR;AAsBD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAkBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAW3D;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,CAAC,CAKjB;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQjD;AAED;;2BAE2B;AAC3B,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAGtF;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,CAEjB;AAED;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,WAAW,GAAG,YAAY,CAEhF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACpC,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA0BlC;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAAC,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAE9F"}
1
+ {"version":3,"file":"_shared.d.ts","sourceRoot":"","sources":["../../src/tools/_shared.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAQhF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,EAAE,KAAK,MAAM,KAAG,GACR,CAAC;AAEhD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,CAAC,EAAE,OAAO,EACV,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAWpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAMpD;AAED;;;;;;;GAOG;AAKH,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAEhD,eAAO,MAAM,+BAA+B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CASlE,CAAC;AAEF,wFAAwF;AACxF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAErE;AA2BD,8EAA8E;AAC9E,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,MAAM,CAsDR;AAsBD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAkBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAW3D;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,CAAC,CAKjB;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQjD;AAED;;2BAE2B;AAC3B,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAGtF;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,CAEjB;AAED;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,WAAW,GAAG,YAAY,CAEhF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACpC,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA0BlC;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACpC,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAMlC;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAAC,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAE9F"}
@@ -1 +1 @@
1
- {"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../src/tools/bash.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAe,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAavE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AA2EjD,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CA4QjE;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CAyBvE;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CAuBrE"}
1
+ {"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../src/tools/bash.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAe,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAavE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAmEjD,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CAuQjE;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CAyBvE;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CAuBrE"}
@@ -1 +1 @@
1
- {"version":3,"file":"bash_watch.d.ts","sourceRoot":"","sources":["../../src/tools/bash_watch.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAavE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAUjD,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AACrD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AACF,KAAK,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,MAAM,CAAC,EAAE,gBAAgB,CAAA;CAAE,CAAC;AAGlF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CAyFtE;AAiDD,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,OAAO,EAAE,eAAe,GAAG,SAAS,EACpC,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,kBAAkB,CAAC,CA+D7B;AA6DD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,GAAG,SAAS,CAK5E;AA4DD,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,eAAe,GACvB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAEtC"}
1
+ {"version":3,"file":"bash_watch.d.ts","sourceRoot":"","sources":["../../src/tools/bash_watch.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAavE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AASjD,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AACrD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AACF,KAAK,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,MAAM,CAAC,EAAE,gBAAgB,CAAA;CAAE,CAAC;AAGlF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CAwFtE;AAiDD,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,OAAO,EAAE,eAAe,GAAG,SAAS,EACpC,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,kBAAkB,CAAC,CA4D7B;AA6DD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,GAAG,SAAS,CAK5E;AA4DD,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,eAAe,GACvB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAEtC"}
package/dist/tui.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/tui/index.tsx
2
2
  import { createMemo as createMemo2, createSignal as createSignal2, onCleanup as onCleanup2 } from "solid-js";
3
3
  // package.json
4
- var version = "0.35.2";
4
+ var version = "0.35.4";
5
5
 
6
6
  // src/shared/rpc-client.ts
7
7
  import { existsSync, readdirSync, readFileSync, unlinkSync } from "node:fs";
@@ -343,6 +343,20 @@ function readCompression(value) {
343
343
  session: readCompressionAggregate(compression.session)
344
344
  };
345
345
  }
346
+ function readStatusBar(value) {
347
+ if (typeof value !== "object" || value === null)
348
+ return;
349
+ const bar = asRecord(value);
350
+ return {
351
+ errors: readNumber(bar.errors),
352
+ warnings: readNumber(bar.warnings),
353
+ dead_code: readNumber(bar.dead_code),
354
+ unused_exports: readNumber(bar.unused_exports),
355
+ duplicates: readNumber(bar.duplicates),
356
+ todos: readNumber(bar.todos),
357
+ tier2_stale: readBoolean(bar.tier2_stale)
358
+ };
359
+ }
346
360
  function formatSemanticIndexStatus(status, stage) {
347
361
  if ((status === "loading" || status === "building") && stage === "fingerprint_change") {
348
362
  return "Rebuilding (model changed)";
@@ -430,6 +444,7 @@ function coerceAftStatus(response) {
430
444
  checkpoints: readNumber(session.checkpoints)
431
445
  },
432
446
  compression: readCompression(response.compression),
447
+ status_bar: readStatusBar(response.status_bar),
433
448
  message: readString(response.message, "")
434
449
  };
435
450
  }
@@ -591,6 +606,14 @@ function collapsedCompressionValue(compression) {
591
606
  const pct = original_tokens > 0 ? Math.round(savings_tokens / original_tokens * 100) : 0;
592
607
  return `${formatCount(savings_tokens)} / ${pct}%`;
593
608
  }
609
+ function collapsedHealthLights(statusBar) {
610
+ if (!statusBar)
611
+ return null;
612
+ const diagnostics = statusBar.errors > 0 ? "err" : statusBar.warnings > 0 ? "warn" : "ok";
613
+ const code = statusBar.duplicates > 0 ? "warn" : "ok";
614
+ const todos = statusBar.todos > 0 ? "warn" : "ok";
615
+ return { diagnostics, code, todos };
616
+ }
594
617
  function resolveTuiStorageDir() {
595
618
  return resolveCortexKitStorageRoot();
596
619
  }
@@ -755,6 +778,7 @@ var SidebarContent = (props) => {
755
778
  const trigramBytes = () => s()?.disk?.trigram_disk_bytes ?? 0;
756
779
  const semanticBytes = () => s()?.disk?.semantic_disk_bytes ?? 0;
757
780
  const compressionRows = () => formatCompressionSidebarRows(s()?.compression);
781
+ const statusBar = () => s()?.status_bar;
758
782
  const degradedReasonLabel = (reason) => {
759
783
  if (reason === "home_root") {
760
784
  return "project root is your home directory";
@@ -873,6 +897,28 @@ var SidebarContent = (props) => {
873
897
  children: "●"
874
898
  }, undefined, false, undefined, this)
875
899
  }, undefined, false, undefined, this),
900
+ collapsedHealthLights(statusBar()) && /* @__PURE__ */ jsxDEV(CollapsedRow, {
901
+ theme: props.theme,
902
+ label: "Code Health",
903
+ children: /* @__PURE__ */ jsxDEV("box", {
904
+ flexDirection: "row",
905
+ gap: 1,
906
+ children: [
907
+ /* @__PURE__ */ jsxDEV("text", {
908
+ fg: toneColor(props.theme, collapsedHealthLights(statusBar()).diagnostics),
909
+ children: "●"
910
+ }, undefined, false, undefined, this),
911
+ /* @__PURE__ */ jsxDEV("text", {
912
+ fg: toneColor(props.theme, collapsedHealthLights(statusBar()).code),
913
+ children: "●"
914
+ }, undefined, false, undefined, this),
915
+ /* @__PURE__ */ jsxDEV("text", {
916
+ fg: toneColor(props.theme, collapsedHealthLights(statusBar()).todos),
917
+ children: "●"
918
+ }, undefined, false, undefined, this)
919
+ ]
920
+ }, undefined, true, undefined, this)
921
+ }, undefined, false, undefined, this),
876
922
  collapsedCompressionValue(s()?.compression) && /* @__PURE__ */ jsxDEV(CollapsedRow, {
877
923
  theme: props.theme,
878
924
  label: "Compression",
@@ -944,6 +990,38 @@ var SidebarContent = (props) => {
944
990
  value: formatBytes2(semanticBytes()),
945
991
  tone: "muted"
946
992
  }, undefined, false, undefined, this),
993
+ statusBar() && /* @__PURE__ */ jsxDEV(Fragment, {
994
+ children: [
995
+ /* @__PURE__ */ jsxDEV(SectionHeader, {
996
+ theme: props.theme,
997
+ title: statusBar().tier2_stale ? "Code Health ~" : "Code Health"
998
+ }, undefined, false, undefined, this),
999
+ /* @__PURE__ */ jsxDEV(StatRow, {
1000
+ theme: props.theme,
1001
+ label: "Errors",
1002
+ value: formatCount(statusBar().errors),
1003
+ tone: statusBar().errors > 0 ? "err" : "muted"
1004
+ }, undefined, false, undefined, this),
1005
+ /* @__PURE__ */ jsxDEV(StatRow, {
1006
+ theme: props.theme,
1007
+ label: "Warnings",
1008
+ value: formatCount(statusBar().warnings),
1009
+ tone: statusBar().warnings > 0 ? "warn" : "muted"
1010
+ }, undefined, false, undefined, this),
1011
+ /* @__PURE__ */ jsxDEV(StatRow, {
1012
+ theme: props.theme,
1013
+ label: "Duplicates",
1014
+ value: formatCount(statusBar().duplicates),
1015
+ tone: "muted"
1016
+ }, undefined, false, undefined, this),
1017
+ /* @__PURE__ */ jsxDEV(StatRow, {
1018
+ theme: props.theme,
1019
+ label: "TODOs",
1020
+ value: formatCount(statusBar().todos),
1021
+ tone: "muted"
1022
+ }, undefined, false, undefined, this)
1023
+ ]
1024
+ }, undefined, true, undefined, this),
947
1025
  compressionRows().length > 0 && /* @__PURE__ */ jsxDEV(Fragment, {
948
1026
  children: [
949
1027
  /* @__PURE__ */ jsxDEV(SectionHeader, {
@@ -1433,6 +1511,43 @@ var StatusDialog = (props) => {
1433
1511
  }, undefined, false, undefined, this))
1434
1512
  ]
1435
1513
  }, undefined, true, undefined, this) : null,
1514
+ status()?.status_bar ? /* @__PURE__ */ jsxDEV2("box", {
1515
+ flexDirection: "column",
1516
+ width: "100%",
1517
+ marginTop: 1,
1518
+ children: [
1519
+ /* @__PURE__ */ jsxDEV2("text", {
1520
+ fg: t().text,
1521
+ children: /* @__PURE__ */ jsxDEV2("b", {
1522
+ children: status().status_bar.tier2_stale ? "Code Health ~" : "Code Health"
1523
+ }, undefined, false, undefined, this)
1524
+ }, undefined, false, undefined, this),
1525
+ /* @__PURE__ */ jsxDEV2(R, {
1526
+ theme: t(),
1527
+ label: "Errors",
1528
+ value: formatCountShort(status().status_bar.errors),
1529
+ tone: status().status_bar.errors > 0 ? "err" : "muted"
1530
+ }, undefined, false, undefined, this),
1531
+ /* @__PURE__ */ jsxDEV2(R, {
1532
+ theme: t(),
1533
+ label: "Warnings",
1534
+ value: formatCountShort(status().status_bar.warnings),
1535
+ tone: status().status_bar.warnings > 0 ? "warn" : "muted"
1536
+ }, undefined, false, undefined, this),
1537
+ /* @__PURE__ */ jsxDEV2(R, {
1538
+ theme: t(),
1539
+ label: "Duplicates",
1540
+ value: formatCountShort(status().status_bar.duplicates),
1541
+ tone: "muted"
1542
+ }, undefined, false, undefined, this),
1543
+ /* @__PURE__ */ jsxDEV2(R, {
1544
+ theme: t(),
1545
+ label: "TODOs",
1546
+ value: formatCountShort(status().status_bar.todos),
1547
+ tone: "muted"
1548
+ }, undefined, false, undefined, this)
1549
+ ]
1550
+ }, undefined, true, undefined, this) : null,
1436
1551
  /* @__PURE__ */ jsxDEV2("box", {
1437
1552
  marginTop: 1,
1438
1553
  justifyContent: "flex-end",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/aft-opencode",
3
- "version": "0.35.2",
3
+ "version": "0.35.4",
4
4
  "type": "module",
5
5
  "description": "OpenCode plugin for Agent File Tools (AFT) — tree-sitter and lsp powered code analysis",
6
6
  "main": "dist/index.js",
@@ -30,19 +30,19 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@clack/prompts": "^1.2.0",
33
- "@cortexkit/aft-bridge": "0.35.2",
33
+ "@cortexkit/aft-bridge": "0.35.4",
34
34
  "@xterm/headless": "^5.5.0",
35
35
  "comment-json": "^4.6.2",
36
36
  "undici": "^7.25.0",
37
37
  "zod": "^4.1.8"
38
38
  },
39
39
  "optionalDependencies": {
40
- "@cortexkit/aft-darwin-arm64": "0.35.2",
41
- "@cortexkit/aft-darwin-x64": "0.35.2",
42
- "@cortexkit/aft-linux-arm64": "0.35.2",
43
- "@cortexkit/aft-linux-x64": "0.35.2",
44
- "@cortexkit/aft-win32-arm64": "0.35.2",
45
- "@cortexkit/aft-win32-x64": "0.35.2"
40
+ "@cortexkit/aft-darwin-arm64": "0.35.4",
41
+ "@cortexkit/aft-darwin-x64": "0.35.4",
42
+ "@cortexkit/aft-linux-arm64": "0.35.4",
43
+ "@cortexkit/aft-linux-x64": "0.35.4",
44
+ "@cortexkit/aft-win32-arm64": "0.35.4",
45
+ "@cortexkit/aft-win32-x64": "0.35.4"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@opencode-ai/plugin": "^1.15.11",
@@ -10,6 +10,24 @@ export interface StatusCompression {
10
10
  session: StatusCompressionAggregate;
11
11
  }
12
12
 
13
+ /**
14
+ * The agent status-bar glance — `[AFT E· W· | D· U· C· | T·]`. `errors`/
15
+ * `warnings` are live LSP diagnostics for files touched this session; the
16
+ * Tier-2 trio (`dead_code`/`unused_exports`/`duplicates`) plus `todos` come
17
+ * from the last completed background scan. `tier2_stale` marks those Tier-2
18
+ * counts as predating the latest edit. Null in the snapshot until the Tier-2
19
+ * cache is populated at least once (no fabricated zeros).
20
+ */
21
+ export interface StatusBar {
22
+ errors: number;
23
+ warnings: number;
24
+ dead_code: number;
25
+ unused_exports: number;
26
+ duplicates: number;
27
+ todos: number;
28
+ tier2_stale: boolean;
29
+ }
30
+
13
31
  export interface AftStatusSnapshot {
14
32
  version: string;
15
33
  project_root: string | null;
@@ -72,6 +90,12 @@ export interface AftStatusSnapshot {
72
90
  };
73
91
  /** Compression aggregate passthrough; rendering is added separately. */
74
92
  compression?: StatusCompression;
93
+ /**
94
+ * Agent status-bar counts (E/W/D/U/C/T). Undefined until the Tier-2 cache
95
+ * is populated at least once — the sidebar hides the Code Health section
96
+ * until then so it never shows fabricated zeros.
97
+ */
98
+ status_bar?: StatusBar;
75
99
  /**
76
100
  * Human-readable explanation for a synthetic snapshot (e.g.
77
101
  * `cache_role === "not_initialized"`). When the plugin returns a placeholder
@@ -125,6 +149,23 @@ function readCompression(value: unknown): StatusCompression | undefined {
125
149
  };
126
150
  }
127
151
 
152
+ function readStatusBar(value: unknown): StatusBar | undefined {
153
+ // Null until Tier-2 is populated — the Rust snapshot emits JSON null, which
154
+ // is not an object, so this returns undefined and the sidebar hides the
155
+ // Code Health section (no fabricated zeros).
156
+ if (typeof value !== "object" || value === null) return undefined;
157
+ const bar = asRecord(value);
158
+ return {
159
+ errors: readNumber(bar.errors),
160
+ warnings: readNumber(bar.warnings),
161
+ dead_code: readNumber(bar.dead_code),
162
+ unused_exports: readNumber(bar.unused_exports),
163
+ duplicates: readNumber(bar.duplicates),
164
+ todos: readNumber(bar.todos),
165
+ tier2_stale: readBoolean(bar.tier2_stale),
166
+ };
167
+ }
168
+
128
169
  function formatFlag(enabled: boolean): string {
129
170
  return enabled ? "enabled" : "disabled";
130
171
  }
@@ -227,6 +268,7 @@ export function coerceAftStatus(response: Record<string, unknown>): AftStatusSna
227
268
  checkpoints: readNumber(session.checkpoints),
228
269
  },
229
270
  compression: readCompression(response.compression),
271
+ status_bar: readStatusBar(response.status_bar),
230
272
  message: readString(response.message, ""),
231
273
  };
232
274
  }
@@ -288,6 +330,21 @@ export function formatStatusDialogMessage(status: AftStatusSnapshot): string {
288
330
  lines.push(`- storage dir: ${status.storage_dir ?? status.disk.storage_dir}`);
289
331
  }
290
332
 
333
+ if (status.status_bar) {
334
+ const sb = status.status_bar;
335
+ lines.push(
336
+ "",
337
+ `Code Health${sb.tier2_stale ? " (~ stale)" : ""}`,
338
+ `- errors: ${formatCount(sb.errors)}`,
339
+ `- warnings: ${formatCount(sb.warnings)}`,
340
+ // dead code / unused exports hidden until oxc resolver lands (restore both)
341
+ // `- dead code: ${formatCount(sb.dead_code)}`,
342
+ // `- unused exports: ${formatCount(sb.unused_exports)}`,
343
+ `- duplicates: ${formatCount(sb.duplicates)}`,
344
+ `- todos: ${formatCount(sb.todos)}`,
345
+ );
346
+ }
347
+
291
348
  lines.push(
292
349
  "",
293
350
  "Current session",
@@ -383,6 +440,21 @@ export function formatStatusMarkdown(status: AftStatusSnapshot): string {
383
440
  lines.push(`- **Storage dir:** \`${status.storage_dir ?? status.disk.storage_dir}\``);
384
441
  }
385
442
 
443
+ if (status.status_bar) {
444
+ const sb = status.status_bar;
445
+ lines.push(
446
+ "",
447
+ `### Code Health${sb.tier2_stale ? " (~ stale)" : ""}`,
448
+ `- **Errors:** ${formatCount(sb.errors)}`,
449
+ `- **Warnings:** ${formatCount(sb.warnings)}`,
450
+ // dead code / unused exports hidden until oxc resolver lands (restore both)
451
+ // `- **Dead code:** ${formatCount(sb.dead_code)}`,
452
+ // `- **Unused exports:** ${formatCount(sb.unused_exports)}`,
453
+ `- **Duplicates:** ${formatCount(sb.duplicates)}`,
454
+ `- **TODOs:** ${formatCount(sb.todos)}`,
455
+ );
456
+ }
457
+
386
458
  lines.push(
387
459
  "",
388
460
  "### Current session",
package/src/tui/index.tsx CHANGED
@@ -466,6 +466,57 @@ const StatusDialog = (props: StatusDialogProps) => {
466
466
  </box>
467
467
  ) : null}
468
468
 
469
+ {/* Code Health — the agent status-bar glance (E/W/D/U/C/T), surfaced so
470
+ users see the same view agents get. Hidden until the Tier-2 cache is
471
+ populated (status_bar undefined) so it never shows fabricated zeros.
472
+ A `~` on the header flags the Tier-2 counts as predating the latest
473
+ edit. */}
474
+ {status()?.status_bar ? (
475
+ <box flexDirection="column" width="100%" marginTop={1}>
476
+ <text fg={t().text}>
477
+ <b>{status()!.status_bar!.tier2_stale ? "Code Health ~" : "Code Health"}</b>
478
+ </text>
479
+ <R
480
+ theme={t()}
481
+ label="Errors"
482
+ value={formatCountShort(status()!.status_bar!.errors)}
483
+ tone={status()!.status_bar!.errors > 0 ? "err" : "muted"}
484
+ />
485
+ <R
486
+ theme={t()}
487
+ label="Warnings"
488
+ value={formatCountShort(status()!.status_bar!.warnings)}
489
+ tone={status()!.status_bar!.warnings > 0 ? "warn" : "muted"}
490
+ />
491
+ {/* Dead Code / Unused Exports hidden until the oxc resolver lands
492
+ (current scanner over-reports on barrel re-exports). Restore both. */}
493
+ {/* <R
494
+ theme={t()}
495
+ label="Dead Code"
496
+ value={formatCountShort(status()!.status_bar!.dead_code)}
497
+ tone="muted"
498
+ />
499
+ <R
500
+ theme={t()}
501
+ label="Unused Exports"
502
+ value={formatCountShort(status()!.status_bar!.unused_exports)}
503
+ tone="muted"
504
+ /> */}
505
+ <R
506
+ theme={t()}
507
+ label="Duplicates"
508
+ value={formatCountShort(status()!.status_bar!.duplicates)}
509
+ tone="muted"
510
+ />
511
+ <R
512
+ theme={t()}
513
+ label="TODOs"
514
+ value={formatCountShort(status()!.status_bar!.todos)}
515
+ tone="muted"
516
+ />
517
+ </box>
518
+ ) : null}
519
+
469
520
  {/* Footer */}
470
521
  <box marginTop={1} justifyContent="flex-end" width="100%">
471
522
  <text fg={t().textMuted}>Enter or Esc to close</text>
@@ -16,6 +16,7 @@ import {
16
16
  coerceAftStatus,
17
17
  formatSemanticIndexStatus,
18
18
  formatSemanticRefreshing,
19
+ type StatusBar,
19
20
  type StatusCompression,
20
21
  } from "../shared/status";
21
22
  import { resolveCortexKitStorageRoot } from "../shared/storage-paths";
@@ -179,6 +180,34 @@ export function collapsedCompressionValue(
179
180
  return `${formatCount(savings_tokens)} / ${pct}%`;
180
181
  }
181
182
 
183
+ export type HealthLightTone = "ok" | "warn" | "err";
184
+
185
+ export interface HealthLights {
186
+ // Diagnostics: red if any errors, yellow if any warnings, else green.
187
+ diagnostics: HealthLightTone;
188
+ // Code cruft: yellow if there is any duplicate, green when zero. Never red —
189
+ // cruft is not a build failure. NOTE: dead_code / unused_exports are
190
+ // temporarily excluded from this signal (and the expanded rows below) until
191
+ // the oxc-based resolver lands and makes those counts trustworthy on real
192
+ // TS/JS codebases. Restore them here when that engine ships.
193
+ code: HealthLightTone;
194
+ // TODOs: yellow if any, else green.
195
+ todos: HealthLightTone;
196
+ }
197
+
198
+ // Three traffic lights for the collapsed view, replacing the cramped
199
+ // `E· W· | D· U· C· | T·` string. Returns null until populated.
200
+ export function collapsedHealthLights(statusBar: StatusBar | undefined): HealthLights | null {
201
+ if (!statusBar) return null;
202
+ const diagnostics: HealthLightTone =
203
+ statusBar.errors > 0 ? "err" : statusBar.warnings > 0 ? "warn" : "ok";
204
+ const code: HealthLightTone =
205
+ // statusBar.dead_code > 0 || statusBar.unused_exports > 0 || // restore with oxc engine
206
+ statusBar.duplicates > 0 ? "warn" : "ok";
207
+ const todos: HealthLightTone = statusBar.todos > 0 ? "warn" : "ok";
208
+ return { diagnostics, code, todos };
209
+ }
210
+
182
211
  // v0.27 moved AFT storage to the CortexKit root. TUI code must use a
183
212
  // lightweight local path helper rather than the shared bridge barrel, which
184
213
  // also exports URL-fetch helpers unsuitable for Bun's TUI runtime.
@@ -426,6 +455,7 @@ const SidebarContent = (props: {
426
455
  const trigramBytes = () => s()?.disk?.trigram_disk_bytes ?? 0;
427
456
  const semanticBytes = () => s()?.disk?.semantic_disk_bytes ?? 0;
428
457
  const compressionRows = () => formatCompressionSidebarRows(s()?.compression);
458
+ const statusBar = () => s()?.status_bar;
429
459
 
430
460
  // Degraded-mode reason → human-readable hint. Distinct strings per reason
431
461
  // because the UX direction is different: "home_root" tells the user to
@@ -537,6 +567,19 @@ const SidebarContent = (props: {
537
567
  <CollapsedRow theme={props.theme} label="Semantic Index">
538
568
  <text fg={toneColor(props.theme, semanticStatus().tone)}>●</text>
539
569
  </CollapsedRow>
570
+ {collapsedHealthLights(statusBar()) && (
571
+ <CollapsedRow theme={props.theme} label="Code Health">
572
+ <box flexDirection="row" gap={1}>
573
+ <text fg={toneColor(props.theme, collapsedHealthLights(statusBar())!.diagnostics)}>
574
+
575
+ </text>
576
+ <text fg={toneColor(props.theme, collapsedHealthLights(statusBar())!.code)}>●</text>
577
+ <text fg={toneColor(props.theme, collapsedHealthLights(statusBar())!.todos)}>
578
+
579
+ </text>
580
+ </box>
581
+ </CollapsedRow>
582
+ )}
540
583
  {collapsedCompressionValue(s()?.compression) && (
541
584
  <CollapsedRow theme={props.theme} label="Compression">
542
585
  <text fg={props.theme.textMuted}>
@@ -614,6 +657,61 @@ const SidebarContent = (props: {
614
657
  tone="muted"
615
658
  />
616
659
 
660
+ {/* Code Health — the agent status-bar glance, surfaced for users.
661
+ Hidden until the Tier-2 cache is populated (status_bar undefined),
662
+ so it never shows fabricated zeros. Errors/warnings are live LSP
663
+ diagnostics; D/U/C/T come from the last background scan. A `~` on
664
+ the section header flags the Tier-2 counts as predating the latest
665
+ edit. */}
666
+ {statusBar() && (
667
+ <>
668
+ <SectionHeader
669
+ theme={props.theme}
670
+ title={statusBar()!.tier2_stale ? "Code Health ~" : "Code Health"}
671
+ />
672
+ <StatRow
673
+ theme={props.theme}
674
+ label="Errors"
675
+ value={formatCount(statusBar()!.errors)}
676
+ tone={statusBar()!.errors > 0 ? "err" : "muted"}
677
+ />
678
+ <StatRow
679
+ theme={props.theme}
680
+ label="Warnings"
681
+ value={formatCount(statusBar()!.warnings)}
682
+ tone={statusBar()!.warnings > 0 ? "warn" : "muted"}
683
+ />
684
+ {/* Dead Code / Unused Exports temporarily hidden until the
685
+ oxc-based resolver lands and makes these trustworthy on real
686
+ TS/JS codebases (current tree-sitter scanner over-reports via
687
+ barrel re-export gaps). Restore both rows when that ships. */}
688
+ {/* <StatRow
689
+ theme={props.theme}
690
+ label="Dead Code"
691
+ value={formatCount(statusBar()!.dead_code)}
692
+ tone="muted"
693
+ />
694
+ <StatRow
695
+ theme={props.theme}
696
+ label="Unused Exports"
697
+ value={formatCount(statusBar()!.unused_exports)}
698
+ tone="muted"
699
+ /> */}
700
+ <StatRow
701
+ theme={props.theme}
702
+ label="Duplicates"
703
+ value={formatCount(statusBar()!.duplicates)}
704
+ tone="muted"
705
+ />
706
+ <StatRow
707
+ theme={props.theme}
708
+ label="TODOs"
709
+ value={formatCount(statusBar()!.todos)}
710
+ tone="muted"
711
+ />
712
+ </>
713
+ )}
714
+
617
715
  {/* Compression aggregates. Tabular layout matching Search/Semantic
618
716
  Index above: each scope ("Session", "Project") renders as a
619
717
  subheader followed by two StatRows (Tokens Saved, Compression