@herbertgao/pi-extensions 2026.9.1 → 2026.9.2

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 (166) hide show
  1. package/node_modules/@narumitw/pi-btw/README.md +57 -141
  2. package/node_modules/@narumitw/pi-btw/dist/index.ts +466 -41
  3. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +4 -4
  4. package/node_modules/@narumitw/pi-btw/docs/workflows.md +74 -0
  5. package/node_modules/@narumitw/pi-btw/package.json +2 -1
  6. package/node_modules/@narumitw/pi-btw/src/btw.ts +15 -1
  7. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +32 -2
  8. package/node_modules/@narumitw/pi-btw/src/keybindings.ts +330 -0
  9. package/node_modules/@narumitw/pi-btw/src/menu.ts +136 -19
  10. package/node_modules/@narumitw/pi-btw/src/settings.ts +32 -0
  11. package/node_modules/@narumitw/pi-btw/src/side-thread.ts +57 -5
  12. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +52 -30
  13. package/node_modules/pi-lens/CHANGELOG.md +84 -0
  14. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +0 -7
  15. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +1 -1
  16. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +1 -1
  17. package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +0 -7
  18. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +2 -0
  19. package/node_modules/pi-lens/dist/clients/bash-file-access.js +133 -1
  20. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +0 -6
  21. package/node_modules/pi-lens/dist/clients/bootstrap.js +4 -4
  22. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +1 -1
  23. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +0 -5
  24. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +1 -9
  25. package/node_modules/pi-lens/dist/clients/cache-observability.js +1 -6
  26. package/node_modules/pi-lens/dist/clients/cargo-manifest.js +2 -2
  27. package/node_modules/pi-lens/dist/clients/cascade-logger.js +0 -7
  28. package/node_modules/pi-lens/dist/clients/child-unref.js +1 -19
  29. package/node_modules/pi-lens/dist/clients/config-core/index.js +11 -8
  30. package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +1 -1
  31. package/node_modules/pi-lens/dist/clients/config-core/provenance.js +2 -9
  32. package/node_modules/pi-lens/dist/clients/config-core/records.js +1 -1
  33. package/node_modules/pi-lens/dist/clients/config-core/schema.js +3 -5
  34. package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +1 -1
  35. package/node_modules/pi-lens/dist/clients/config-resolve.js +4 -4
  36. package/node_modules/pi-lens/dist/clients/config-schema.js +39 -2
  37. package/node_modules/pi-lens/dist/clients/dead-code-logger.js +0 -4
  38. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +1 -1
  39. package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +1 -1
  40. package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +0 -4
  41. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +0 -26
  42. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +18 -1
  43. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +0 -6
  44. package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +1 -1
  45. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +0 -3
  46. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-complexity.js +2 -2
  47. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-fan-out.js +1 -1
  48. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +3 -1
  49. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +4 -0
  50. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +1 -1
  51. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +1 -0
  52. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +6 -2
  53. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +1 -1
  54. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +1 -1
  55. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +4 -2
  56. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +1 -64
  57. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils.js +0 -21
  58. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +2 -20
  59. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +1 -0
  60. package/node_modules/pi-lens/dist/clients/dispatch/utils/format-utils.js +2 -2
  61. package/node_modules/pi-lens/dist/clients/disposition-logger.js +0 -7
  62. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +2 -2
  63. package/node_modules/pi-lens/dist/clients/file-kinds.js +1 -38
  64. package/node_modules/pi-lens/dist/clients/file-time.js +1 -1
  65. package/node_modules/pi-lens/dist/clients/file-utils.js +5 -1
  66. package/node_modules/pi-lens/dist/clients/format-service.js +0 -1
  67. package/node_modules/pi-lens/dist/clients/formatters.js +110 -3
  68. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +3 -4
  69. package/node_modules/pi-lens/dist/clients/git-guard.js +5 -0
  70. package/node_modules/pi-lens/dist/clients/grammar-source.js +3 -3
  71. package/node_modules/pi-lens/dist/clients/installer/index.js +220 -9
  72. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +84 -18
  73. package/node_modules/pi-lens/dist/clients/instance-reaper.js +6 -6
  74. package/node_modules/pi-lens/dist/clients/language-profile.js +0 -9
  75. package/node_modules/pi-lens/dist/clients/language-registry.js +5 -1
  76. package/node_modules/pi-lens/dist/clients/latency-logger.js +5 -7
  77. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -1
  78. package/node_modules/pi-lens/dist/clients/lens-config.js +19 -0
  79. package/node_modules/pi-lens/dist/clients/lens-engine.js +3 -4
  80. package/node_modules/pi-lens/dist/clients/lens-events.js +1 -1
  81. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +18 -0
  82. package/node_modules/pi-lens/dist/clients/lens-map.js +1 -1
  83. package/node_modules/pi-lens/dist/clients/log-cleanup.js +0 -36
  84. package/node_modules/pi-lens/dist/clients/lsp/client.js +4 -3
  85. package/node_modules/pi-lens/dist/clients/lsp/config.js +1 -0
  86. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +1 -1
  87. package/node_modules/pi-lens/dist/clients/lsp/edits.js +3 -3
  88. package/node_modules/pi-lens/dist/clients/lsp/index.js +216 -25
  89. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +1 -1
  90. package/node_modules/pi-lens/dist/clients/lsp/language.js +0 -8
  91. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
  92. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +0 -2
  93. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +0 -1
  94. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +5 -5
  95. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +6 -0
  96. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +2 -1
  97. package/node_modules/pi-lens/dist/clients/lsp-document-symbols.js +1 -1
  98. package/node_modules/pi-lens/dist/clients/metrics-history.js +28 -107
  99. package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +1 -1
  100. package/node_modules/pi-lens/dist/clients/module-report.js +1 -1
  101. package/node_modules/pi-lens/dist/clients/mutating-tool.js +3 -3
  102. package/node_modules/pi-lens/dist/clients/mutation-attribution.js +3 -3
  103. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +0 -4
  104. package/node_modules/pi-lens/dist/clients/observed-mutation.js +9 -9
  105. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +2 -6
  106. package/node_modules/pi-lens/dist/clients/opengrep-config.js +1 -1
  107. package/node_modules/pi-lens/dist/clients/path-utils.js +23 -2
  108. package/node_modules/pi-lens/dist/clients/performance-report.js +2 -2
  109. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +1 -1
  110. package/node_modules/pi-lens/dist/clients/pipeline.js +2 -2
  111. package/node_modules/pi-lens/dist/clients/project-changes.js +1 -1
  112. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +1 -1
  113. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +1 -1
  114. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +1 -1
  115. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +1 -1
  116. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +1 -1
  117. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +1 -1
  118. package/node_modules/pi-lens/dist/clients/project-lens-config.js +46 -1
  119. package/node_modules/pi-lens/dist/clients/project-scan-policy.js +3 -51
  120. package/node_modules/pi-lens/dist/clients/python-provenance.js +1 -1
  121. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +1 -1
  122. package/node_modules/pi-lens/dist/clients/read-guard.js +5 -2
  123. package/node_modules/pi-lens/dist/clients/recent-touches.js +2 -2
  124. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +3 -5
  125. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +1 -1
  126. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +1 -1
  127. package/node_modules/pi-lens/dist/clients/runtime-session.js +45 -10
  128. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +11 -1
  129. package/node_modules/pi-lens/dist/clients/sanitize.js +0 -11
  130. package/node_modules/pi-lens/dist/clients/scan-utils.js +1 -56
  131. package/node_modules/pi-lens/dist/clients/session-summary.js +0 -24
  132. package/node_modules/pi-lens/dist/clients/sgconfig.js +1 -5
  133. package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +1 -1
  134. package/node_modules/pi-lens/dist/clients/smells-rollup.js +2 -2
  135. package/node_modules/pi-lens/dist/clients/startup-scan.js +2 -10
  136. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +77 -72
  137. package/node_modules/pi-lens/dist/clients/tool-policy.js +1 -1
  138. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +1 -52
  139. package/node_modules/pi-lens/dist/clients/tui-fit.js +0 -4
  140. package/node_modules/pi-lens/dist/clients/user-notify.js +0 -4
  141. package/node_modules/pi-lens/dist/clients/word-index-logger.js +0 -7
  142. package/node_modules/pi-lens/dist/clients/word-index-store.js +3 -7
  143. package/node_modules/pi-lens/dist/clients/word-index.js +9 -16
  144. package/node_modules/pi-lens/dist/clients/zizmor-config.js +1 -1
  145. package/node_modules/pi-lens/dist/index.js +1062 -538
  146. package/node_modules/pi-lens/dist/mcp/build-staleness.js +1 -1
  147. package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +1 -1
  148. package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +1 -1
  149. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +1 -1
  150. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +37 -6
  151. package/node_modules/pi-lens/dist/tools/lsp-structured-output.js +3 -3
  152. package/node_modules/pi-lens/dist/tools/render-compact.js +1 -1
  153. package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -2
  154. package/node_modules/pi-lens/docs/globalconfig.md +20 -0
  155. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +9 -1
  156. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +6 -0
  157. package/node_modules/pi-lens/docs/pi-lens-subagent.md +12 -1
  158. package/node_modules/pi-lens/docs/release-qa-baseline.md +12 -4
  159. package/node_modules/pi-lens/docs/servercapabilities.md +7 -3
  160. package/node_modules/pi-lens/docs/word-index.md +1 -1
  161. package/node_modules/pi-lens/package.json +6 -15
  162. package/node_modules/pi-lens/rules/typos/_typos.toml +4 -2
  163. package/node_modules/pi-lens/scripts/lib/skills-predicate.mjs +1 -1
  164. package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +1 -1
  165. package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +4 -2
  166. package/package.json +3 -3
@@ -1994,7 +1994,7 @@ var init_minimatch = __esm({
1994
1994
  });
1995
1995
 
1996
1996
  // dist/clients/path-utils.js
1997
- import { existsSync, realpathSync } from "node:fs";
1997
+ import { existsSync, readFileSync, realpathSync, statSync } from "node:fs";
1998
1998
  import * as os from "node:os";
1999
1999
  import * as path2 from "node:path";
2000
2000
  import { win32 } from "node:path";
@@ -2127,14 +2127,27 @@ function findLocalToolConfig(startDir, names, options = {}) {
2127
2127
  }
2128
2128
  return void 0;
2129
2129
  }
2130
+ function isRealGitMarker(markerPath) {
2131
+ try {
2132
+ const marker = statSync(markerPath);
2133
+ if (marker.isDirectory())
2134
+ return existsSync(path2.join(markerPath, "HEAD"));
2135
+ if (!marker.isFile())
2136
+ return false;
2137
+ return readFileSync(markerPath, "utf8").split(/\r?\n/, 1)[0].startsWith("gitdir:");
2138
+ } catch {
2139
+ return false;
2140
+ }
2141
+ }
2130
2142
  function findNearestMarkerRoot(startDir, markers, options = {}) {
2131
2143
  const boundaries = options.boundaries ?? [];
2132
2144
  const homeDir = path2.resolve(options.homeDir ?? os.homedir());
2145
+ const markerPredicate = options.markerPredicate ?? (() => true);
2133
2146
  let current = path2.resolve(startDir);
2134
2147
  for (let depth = 0; depth < 64; depth++) {
2135
2148
  if (isAtOrAboveHomeDir(current, homeDir))
2136
2149
  return null;
2137
- if (markers.some((m) => existsSync(path2.join(current, m))))
2150
+ if (markers.some((m) => existsSync(path2.join(current, m)) && markerPredicate(path2.join(current, m))))
2138
2151
  return current;
2139
2152
  if (boundaries.some((m) => existsSync(path2.join(current, m))))
2140
2153
  return null;
@@ -3942,7 +3955,19 @@ var init_user_notify = __esm({
3942
3955
 
3943
3956
  // dist/clients/ledger-bounds.js
3944
3957
  function normalizeForLedger(value) {
3945
- return String(value ?? "unknown");
3958
+ if (value === null || value === void 0)
3959
+ return "unknown";
3960
+ if (typeof value === "object" && !Object.hasOwn(value, "toString")) {
3961
+ const proto = Object.getPrototypeOf(value);
3962
+ if (proto === Object.prototype || proto === null || Array.isArray(value)) {
3963
+ try {
3964
+ return JSON.stringify(value) ?? "unknown";
3965
+ } catch {
3966
+ return "[unserializable object]";
3967
+ }
3968
+ }
3969
+ }
3970
+ return String(value);
3946
3971
  }
3947
3972
  function truncateForLedger(value) {
3948
3973
  const text = normalizeForLedger(value);
@@ -3980,7 +4005,7 @@ function phaseFinished(token) {
3980
4005
  closedAt: (/* @__PURE__ */ new Date()).toISOString()
3981
4006
  },
3982
4007
  ...closedBrackets
3983
- ].slice(0, CLOSED_BRACKET_CAP);
4008
+ ].slice(0, RECENT_PHASE_CAP);
3984
4009
  }
3985
4010
  function getPhaseForWindow(windowStartMs, windowEndMs) {
3986
4011
  const nowMs = Date.now();
@@ -4076,7 +4101,7 @@ function getLatencyLogPath() {
4076
4101
  function flushLatencyLog() {
4077
4102
  return writer2.flush();
4078
4103
  }
4079
- var LATENCY_LOG_DIR, LATENCY_LOG_FILE, writer2, RECENT_PHASE_CAP, recentPhases, LAST_PHASE_EXCLUDED, CLOSED_BRACKET_CAP, liveBrackets, closedBrackets, MIN_PLAUSIBLE_ELAPSED_FRACTION, FRACTION_TIE_EPSILON, oncePerSessionPhases, sessionRecordSeq, sessionRecordId;
4104
+ var LATENCY_LOG_DIR, LATENCY_LOG_FILE, writer2, RECENT_PHASE_CAP, recentPhases, LAST_PHASE_EXCLUDED, liveBrackets, closedBrackets, MIN_PLAUSIBLE_ELAPSED_FRACTION, FRACTION_TIE_EPSILON, oncePerSessionPhases, sessionRecordSeq, sessionRecordId;
4080
4105
  var init_latency_logger = __esm({
4081
4106
  "dist/clients/latency-logger.js"() {
4082
4107
  "use strict";
@@ -4117,7 +4142,6 @@ var init_latency_logger = __esm({
4117
4142
  "concurrent_session_bind_rollup",
4118
4143
  "auxiliary_readiness"
4119
4144
  ]);
4120
- CLOSED_BRACKET_CAP = RECENT_PHASE_CAP;
4121
4145
  liveBrackets = /* @__PURE__ */ new Map();
4122
4146
  closedBrackets = [];
4123
4147
  MIN_PLAUSIBLE_ELAPSED_FRACTION = 0.05;
@@ -5151,10 +5175,10 @@ var require_history = __commonJS({
5151
5175
  var require_ps = __commonJS({
5152
5176
  "node_modules/pidusage/lib/ps.js"(exports, module) {
5153
5177
  "use strict";
5154
- var os34 = __require("os");
5178
+ var os35 = __require("os");
5155
5179
  var bin = require_bin();
5156
5180
  var history = require_history();
5157
- var PLATFORM = os34.platform();
5181
+ var PLATFORM = os35.platform();
5158
5182
  function parseTime(timestr, centisec) {
5159
5183
  let time = 0;
5160
5184
  const tpart = timestr.split(/-|:|\./);
@@ -5199,7 +5223,7 @@ var require_ps = __commonJS({
5199
5223
  return done(new Error("pidusage ps command exited with code " + code));
5200
5224
  }
5201
5225
  const date = Date.now();
5202
- stdout = stdout.split(os34.EOL);
5226
+ stdout = stdout.split(os35.EOL);
5203
5227
  const statistics = {};
5204
5228
  for (let i = 1; i < stdout.length; i++) {
5205
5229
  const line = stdout[i].trim().split(/\s+/);
@@ -5280,7 +5304,7 @@ var require_parallel = __commonJS({
5280
5304
  // node_modules/pidusage/lib/helpers/cpu.js
5281
5305
  var require_cpu = __commonJS({
5282
5306
  "node_modules/pidusage/lib/helpers/cpu.js"(exports, module) {
5283
- var os34 = __require("os");
5307
+ var os35 = __require("os");
5284
5308
  var fs132 = __require("fs");
5285
5309
  var exec = __require("child_process").exec;
5286
5310
  var parallel = require_parallel();
@@ -5313,7 +5337,7 @@ var require_cpu = __commonJS({
5313
5337
  if (!process.env.PIDUSAGE_SILENT) {
5314
5338
  console.warn("[pidusage] We couldn't find uptime from /proc/uptime, using os.uptime() value");
5315
5339
  }
5316
- return next(null, os34.uptime() || /* @__PURE__ */ new Date() / 1e3);
5340
+ return next(null, os35.uptime() || /* @__PURE__ */ new Date() / 1e3);
5317
5341
  }
5318
5342
  return next(null, parseFloat(uptime.split(" ")[0]));
5319
5343
  });
@@ -5533,7 +5557,7 @@ var require_procfile = __commonJS({
5533
5557
  var require_wmic = __commonJS({
5534
5558
  "node_modules/pidusage/lib/wmic.js"(exports, module) {
5535
5559
  "use strict";
5536
- var os34 = __require("os");
5560
+ var os35 = __require("os");
5537
5561
  var bin = require_bin();
5538
5562
  var history = require_history();
5539
5563
  function parseDate(datestr) {
@@ -5577,8 +5601,8 @@ var require_wmic = __commonJS({
5577
5601
  return done(new Error("pidusage wmic command exited with code " + code));
5578
5602
  }
5579
5603
  const date = Date.now();
5580
- const uptime = Math.floor(os34.uptime() || date / 1e3);
5581
- stdout = stdout.split(os34.EOL);
5604
+ const uptime = Math.floor(os35.uptime() || date / 1e3);
5605
+ stdout = stdout.split(os35.EOL);
5582
5606
  let again = false;
5583
5607
  const statistics = {};
5584
5608
  for (let i = 1; i < stdout.length; i++) {
@@ -5628,7 +5652,7 @@ var require_wmic = __commonJS({
5628
5652
  var require_gwmi = __commonJS({
5629
5653
  "node_modules/pidusage/lib/gwmi.js"(exports, module) {
5630
5654
  "use strict";
5631
- var os34 = __require("os");
5655
+ var os35 = __require("os");
5632
5656
  var bin = require_bin();
5633
5657
  var history = require_history();
5634
5658
  function parseDate(datestr) {
@@ -5667,8 +5691,8 @@ var require_gwmi = __commonJS({
5667
5691
  return done(new Error("pidusage gwmi command exited with code " + code));
5668
5692
  }
5669
5693
  const date = Date.now();
5670
- const uptime = Math.floor(os34.uptime() || date / 1e3);
5671
- stdout = stdout.split(os34.EOL).slice(1);
5694
+ const uptime = Math.floor(os35.uptime() || date / 1e3);
5695
+ stdout = stdout.split(os35.EOL).slice(1);
5672
5696
  const index = stdout.findIndex((v) => !!v);
5673
5697
  stdout = stdout.slice(index + 2);
5674
5698
  if (!stdout.length) {
@@ -5726,7 +5750,7 @@ var require_stats = __commonJS({
5726
5750
  "node_modules/pidusage/lib/stats.js"(exports, module) {
5727
5751
  "use strict";
5728
5752
  var fs132 = __require("fs");
5729
- var os34 = __require("os");
5753
+ var os35 = __require("os");
5730
5754
  var spawn3 = __require("child_process").spawn;
5731
5755
  var requireMap = {
5732
5756
  ps: () => require_ps(),
@@ -5748,7 +5772,7 @@ var require_stats = __commonJS({
5748
5772
  sunos: "ps",
5749
5773
  win: "wmic"
5750
5774
  };
5751
- var platform = os34.platform();
5775
+ var platform = os35.platform();
5752
5776
  if (fs132.existsSync("/etc/alpine-release")) {
5753
5777
  platform = "alpine";
5754
5778
  }
@@ -5780,7 +5804,7 @@ var require_stats = __commonJS({
5780
5804
  }
5781
5805
  }
5782
5806
  if (fn === void 0) {
5783
- return callback(new Error(os34.platform() + " is not supported yet, please open an issue (https://github.com/soyuka/pidusage)"));
5807
+ return callback(new Error(os35.platform() + " is not supported yet, please open an issue (https://github.com/soyuka/pidusage)"));
5784
5808
  }
5785
5809
  let single = false;
5786
5810
  if (!Array.isArray(pids)) {
@@ -6014,7 +6038,7 @@ function acquireBoundedPidFileLock(lockPath2, options) {
6014
6038
  options.logContention();
6015
6039
  return null;
6016
6040
  }
6017
- throw new Error(options.timeoutMessage);
6041
+ throw new Error(options.timeoutMessage, { cause: error });
6018
6042
  }
6019
6043
  Atomics.wait(waitArray, 0, 0, options.retryMs);
6020
6044
  }
@@ -9458,7 +9482,23 @@ var init_lens_flag_registry = __esm({
9458
9482
  negated: false,
9459
9483
  default: false,
9460
9484
  scope: "global"
9461
- }
9485
+ },
9486
+ ...[
9487
+ "knip",
9488
+ "jscpd",
9489
+ "madge",
9490
+ "gitleaks",
9491
+ "govulncheck",
9492
+ "deadCode",
9493
+ "complexity"
9494
+ ].map((analyzer) => ({
9495
+ name: `no-${analyzer.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}`,
9496
+ description: `Disable the ${analyzer} session-start analyzer. Also via ${analyzer}.enabled=false in config.`,
9497
+ configKey: `${analyzer}.enabled`,
9498
+ negated: true,
9499
+ default: false,
9500
+ scope: "project"
9501
+ }))
9462
9502
  ];
9463
9503
  byName = new Map(LENS_FLAGS.map((spec) => [spec.name, spec]));
9464
9504
  PROJECT_SCOPED_LENS_FLAGS = LENS_FLAGS.filter((spec) => spec.scope === "project");
@@ -9466,6 +9506,7 @@ var init_lens_flag_registry = __esm({
9466
9506
  "ignore",
9467
9507
  "dispatch",
9468
9508
  "widget",
9509
+ "startup",
9469
9510
  "$schema"
9470
9511
  ];
9471
9512
  PROJECT_NON_FLAG_CONFIG_SECTIONS = [
@@ -9474,7 +9515,8 @@ var init_lens_flag_registry = __esm({
9474
9515
  "maxProjectFiles",
9475
9516
  "reviewGraph",
9476
9517
  "trivy",
9477
- "helm"
9518
+ "helm",
9519
+ "startup"
9478
9520
  ];
9479
9521
  LSP_PROJECT_HONORED_KEYS = DEPRECATED_CONFIG_SURFACES.filter((row) => row.kind === "key").map((row) => row.surface);
9480
9522
  PROJECT_FOREIGN_CONFIG_NAMESPACES = [
@@ -9561,7 +9603,7 @@ var init_config_locations = __esm({
9561
9603
 
9562
9604
  // dist/clients/config-core/provenance.js
9563
9605
  function tierPrecedence(tier) {
9564
- return TIER_PRECEDENCE.indexOf(tier);
9606
+ return SOURCE_TIERS.indexOf(tier);
9565
9607
  }
9566
9608
  function isOperatorTier(tier) {
9567
9609
  return TIER_CLASS[tier] === "operator";
@@ -9592,7 +9634,7 @@ function compareKeys(left, right) {
9592
9634
  return -1;
9593
9635
  return left > right ? 1 : 0;
9594
9636
  }
9595
- var SOURCE_TIERS, TIER_PRECEDENCE, TIER_CLASS;
9637
+ var SOURCE_TIERS, TIER_CLASS;
9596
9638
  var init_provenance = __esm({
9597
9639
  "dist/clients/config-core/provenance.js"() {
9598
9640
  "use strict";
@@ -9606,7 +9648,6 @@ var init_provenance = __esm({
9606
9648
  "cli",
9607
9649
  "host"
9608
9650
  ];
9609
- TIER_PRECEDENCE = SOURCE_TIERS;
9610
9651
  TIER_CLASS = {
9611
9652
  builtin: "default",
9612
9653
  global: "operator",
@@ -9972,7 +10013,6 @@ var MERGE_STRATEGY_KEY, DEFAULT_MERGE_STRATEGY, KEYED_PREFIX, DENY_KEY, DENY_POL
9972
10013
  var init_schema = __esm({
9973
10014
  "dist/clients/config-core/schema.js"() {
9974
10015
  "use strict";
9975
- init_config_diagnostic_codes();
9976
10016
  MERGE_STRATEGY_KEY = "x-merge-strategy";
9977
10017
  DEFAULT_MERGE_STRATEGY = "replace";
9978
10018
  KEYED_PREFIX = "keyed:";
@@ -10553,6 +10593,44 @@ function buildConfigSchema() {
10553
10593
  for (const key4 of PROJECT_FOREIGN_CONFIG_NAMESPACES) {
10554
10594
  properties[key4] ??= opaque("experimental");
10555
10595
  }
10596
+ for (const key4 of [
10597
+ "knip",
10598
+ "jscpd",
10599
+ "madge",
10600
+ "gitleaks",
10601
+ "govulncheck",
10602
+ "deadCode",
10603
+ "complexity"
10604
+ ]) {
10605
+ properties[key4] = {
10606
+ type: "object",
10607
+ additionalProperties: true,
10608
+ properties: {
10609
+ enabled: { type: "boolean", [STABILITY_TIER_KEY]: "experimental" }
10610
+ },
10611
+ [STABILITY_TIER_KEY]: "stable"
10612
+ };
10613
+ }
10614
+ properties.startup = {
10615
+ type: "object",
10616
+ additionalProperties: true,
10617
+ properties: {
10618
+ mode: {
10619
+ type: "string",
10620
+ enum: ["quick", "full", "minimal"],
10621
+ [STABILITY_TIER_KEY]: "experimental"
10622
+ },
10623
+ scans: {
10624
+ type: "object",
10625
+ additionalProperties: true,
10626
+ properties: {
10627
+ enabled: { type: "boolean", [STABILITY_TIER_KEY]: "experimental" }
10628
+ },
10629
+ [STABILITY_TIER_KEY]: "experimental"
10630
+ }
10631
+ },
10632
+ [STABILITY_TIER_KEY]: "stable"
10633
+ };
10556
10634
  for (const key4 of LEGACY_ROOT_LSP_KEYS) {
10557
10635
  properties[key4] = opaque("experimental");
10558
10636
  }
@@ -11262,8 +11340,9 @@ function replayConfigNotices(entry) {
11262
11340
  }
11263
11341
  function loadPiLensProjectConfig(startDir, preloadedInfo) {
11264
11342
  const entry = discoveryEntryFor(startDir);
11265
- reportPiLensConfigRecords(entry.legacyRecords);
11266
11343
  const configInfo = preloadedInfo ?? freshInfoFor(entry);
11344
+ const selectedLegacyPath = configInfo?.path;
11345
+ reportPiLensConfigRecords(entry.legacyRecords.filter((record6) => record6.file !== selectedLegacyPath));
11267
11346
  if (!configInfo)
11268
11347
  return EMPTY_PROJECT_CONFIG;
11269
11348
  return loadCachedConfigFile(configInfo);
@@ -11542,6 +11621,31 @@ function parseConfigFile(configPath) {
11542
11621
  }
11543
11622
  }
11544
11623
  }
11624
+ let startup;
11625
+ if (obj.startup !== void 0) {
11626
+ if (!obj.startup || typeof obj.startup !== "object" || Array.isArray(obj.startup)) {
11627
+ note("startup must be an object");
11628
+ } else {
11629
+ const section = obj.startup;
11630
+ if (section.mode === "quick" || section.mode === "full" || section.mode === "minimal") {
11631
+ startup = { mode: section.mode };
11632
+ } else if ("mode" in section) {
11633
+ note('startup.mode must be "quick", "full", or "minimal"');
11634
+ }
11635
+ if (section.scans !== void 0) {
11636
+ if (!section.scans || typeof section.scans !== "object" || Array.isArray(section.scans)) {
11637
+ note("startup.scans must be an object");
11638
+ } else if (typeof section.scans.enabled === "boolean") {
11639
+ startup ??= {};
11640
+ startup.scans = {
11641
+ enabled: section.scans.enabled
11642
+ };
11643
+ } else if ("enabled" in section.scans) {
11644
+ note("startup.scans.enabled must be a boolean");
11645
+ }
11646
+ }
11647
+ }
11648
+ }
11545
11649
  const knownProjectKeys = /* @__PURE__ */ new Set([
11546
11650
  ...PROJECT_NON_FLAG_CONFIG_SECTIONS,
11547
11651
  ...flagConfigSectionKeys(PROJECT_SCOPED_LENS_FLAGS),
@@ -11576,6 +11680,7 @@ function parseConfigFile(configPath) {
11576
11680
  }
11577
11681
  return {
11578
11682
  config: {
11683
+ ...startup === void 0 ? {} : { startup },
11579
11684
  ignore,
11580
11685
  rules: rules2,
11581
11686
  ...mutations,
@@ -11680,6 +11785,24 @@ function loadPiLensGlobalConfig(configPath = getPiLensGlobalConfigPath()) {
11680
11785
  const ignore = Array.isArray(raw.ignore) ? raw.ignore.filter((p) => typeof p === "string") : void 0;
11681
11786
  if (ignore && ignore.length > 0)
11682
11787
  config.ignore = ignore;
11788
+ const startup = asConfigObject2(raw.startup);
11789
+ if (startup) {
11790
+ const startupConfig = {};
11791
+ const mode = startup.mode;
11792
+ const scans = asConfigObject2(startup.scans);
11793
+ if (mode === "quick" || mode === "full" || mode === "minimal")
11794
+ startupConfig.mode = mode;
11795
+ else if ("mode" in startup)
11796
+ warnInvalid('startup.mode must be "quick", "full", or "minimal"');
11797
+ if (scans) {
11798
+ if (typeof scans.enabled === "boolean") {
11799
+ startupConfig.scans = { enabled: scans.enabled };
11800
+ } else if ("enabled" in scans)
11801
+ warnInvalid("startup.scans.enabled must be a boolean");
11802
+ }
11803
+ if (startupConfig.mode !== void 0 || startupConfig.scans !== void 0)
11804
+ config.startup = startupConfig;
11805
+ }
11683
11806
  const dispatch = asConfigObject2(raw.dispatch);
11684
11807
  if (dispatch) {
11685
11808
  const floor = dispatch.runnerTimeoutFloorMs;
@@ -12201,7 +12324,7 @@ function getProjectIgnoreGlobs(rootDir) {
12201
12324
  return globs;
12202
12325
  }
12203
12326
  function globToRegExp(glob) {
12204
- const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
12327
+ const escaped = glob.replace(/\*+/g, "*").replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
12205
12328
  return new RegExp(`^${escaped}$`, "i");
12206
12329
  }
12207
12330
  function isExcludedDirName(dirName, extraPatterns = []) {
@@ -13148,135 +13271,12 @@ function resetBoundedTelemetry() {
13148
13271
  latestCountedTurnIndex = void 0;
13149
13272
  retiredThroughTurnIndex = Number.NEGATIVE_INFINITY;
13150
13273
  }
13151
- var BOUNDED_TELEMETRY_PHASES, REGISTERED_PHASES, MAX_TRACKED_TURN_CAPS, turnCounts, latestCountedTurnIndex, retiredThroughTurnIndex;
13274
+ var MAX_TRACKED_TURN_CAPS, turnCounts, latestCountedTurnIndex, retiredThroughTurnIndex;
13152
13275
  var init_bounded_telemetry = __esm({
13153
13276
  "dist/clients/bounded-telemetry.js"() {
13154
13277
  "use strict";
13155
13278
  init_degradation_ledger();
13156
13279
  init_latency_logger();
13157
- BOUNDED_TELEMETRY_PHASES = [
13158
- /**
13159
- * #2467: a demand for the analyzer bootstrap clients was not served and
13160
- * the caller proceeded without them. Rising-edge per demand reason, with
13161
- * the ledger holding the exact count — a wedged or missing analyzer graph
13162
- * must not turn every tool call into a log line.
13163
- */
13164
- "bootstrap_clients_unavailable",
13165
- /** #1705: a deferred-format record whose origin worktree never claimed it. */
13166
- "agent_end_deferred_format_orphan_origin_mismatch",
13167
- /** #1713: a `textDocument`/`workspace` diagnostic pull abandoned at budget. */
13168
- "lsp_pull_diagnostic_timeout",
13169
- /** #1713: that abandoned pull answered anyway, too late to serve anyone. */
13170
- "lsp_pull_late_answer_discarded",
13171
- /** #1773: a pull skipped outright because the caller's budget was already
13172
- * exhausted — never dispatched, so it must not be confused with a genuine
13173
- * timeout. */
13174
- "lsp_pull_skipped_budget_exhausted",
13175
- /** #1774: an abandoned pull's request eventually REJECTED (e.g. a server
13176
- * `ContentModified`) rather than answering or staying silent forever —
13177
- * the third outcome late-answer telemetry previously could not see. */
13178
- "lsp_pull_late_rejection",
13179
- /** #1716: a hover/definition/references/etc. request abandoned at budget. */
13180
- "lsp_nav_request_timeout",
13181
- /** #1716: that abandoned nav request answered anyway, after the caller left. */
13182
- "lsp_nav_late_answer_discarded",
13183
- /**
13184
- * #1743 review: a per-file touch skipped a server in breaker cooldown or
13185
- * latched permanently broken. Fires once per file per touch during an
13186
- * outage, so it is rising-edge per (server, file).
13187
- */
13188
- "lsp_client_skipped_broken",
13189
- /** #1743 review: the same skip, for a temporarily unavailable command. */
13190
- "lsp_client_skipped_unavailable_command",
13191
- /**
13192
- * #1934: `getWarmClientForFile` found no live client for a file that has at
13193
- * least one language server with a resolvable root. Its callers run per
13194
- * file (the cascade quiet window, read expansion, `hasWarmLSP`), so it is
13195
- * rising-edge per candidate (server, root) set, with the ledger holding the
13196
- * exact count.
13197
- */
13198
- "lsp_warm_client_missing",
13199
- /** #2052: a file's nearest LSP root is outside the session ceiling. */
13200
- "lsp_capability_skip",
13201
- /**
13202
- * #2044: failed-first test state was retired after a confirmed missing path,
13203
- * retained because the filesystem verdict was indeterminate, or evicted at
13204
- * the state cap. Selection checks and detailed rows are both capped per turn.
13205
- */
13206
- "test_runner_failed_target_state",
13207
- /** #2366: bounded lifecycle records for automatic test-result delivery. */
13208
- "test_runner_delivery",
13209
- /**
13210
- * #1723: an event-loop block at or above the floor. Not a degradation, so
13211
- * no ledger kind; bounded by call cadence (one `turn_end` runs it once per
13212
- * turn) rather than by an option here.
13213
- */
13214
- "loop_block",
13215
- /**
13216
- * #1925: a session event skipped because its ctx was invalidated by a
13217
- * session replacement or reload. A replaced session can drain a whole
13218
- * queue of them, so it is rising-edge per event name.
13219
- */
13220
- "session_event_stale_ctx_skip",
13221
- /**
13222
- * #2007: a worktree-mutating git command declined because another live
13223
- * session shares this dirty checkout. Rising edge per checkout root; the
13224
- * ledger keeps the exact repeat count.
13225
- */
13226
- "shared_checkout_switch_blocked",
13227
- /**
13228
- * #2007: `git status` could not report the working-tree state, so the same
13229
- * command was declined on an UNKNOWN rather than assumed clean.
13230
- */
13231
- "shared_checkout_probe_failed",
13232
- /** #2356: a notify-stall auxiliary remained uncovered after its bounded
13233
- * replacement window. Detailed rows are capped per turn; the ledger and
13234
- * aggregate turn-end row retain the complete count and identity. */
13235
- "lsp_scanner_coverage_gap",
13236
- /** #2358: a busy CPU discriminator deferred notify-stall teardown. */
13237
- "lsp_notify_stall_cpu_busy",
13238
- /**
13239
- * #2430: an observational pre/post snapshot was declined or cut short — the
13240
- * per-turn budget was already spent, the capture timed out, or the turn was
13241
- * aborted. Every unclassified tool call in a busy turn can reach this, so
13242
- * it is capped per turn with the ledger holding the exact count.
13243
- */
13244
- "observed_mutation_budget_exhausted",
13245
- /**
13246
- * #2430: the `agent_settled` tracked-file sweep could not complete within
13247
- * its bound. Once per settle at most, capped for the same reason.
13248
- */
13249
- "observed_sweep_skipped_budget",
13250
- /**
13251
- * #2430: a tracked file's size/mtime moved but no hashed baseline could
13252
- * confirm the bytes actually changed — too large for the sweep's read
13253
- * budget, or never hashed. Named rather than replayed, because a `touch`
13254
- * moves mtime without moving a byte (#2449 review round 2, F7). Bounded
13255
- * per turn: one settle can find many such files at once.
13256
- */
13257
- "observed_sweep_unverifiable",
13258
- /**
13259
- * #2430: an armed observation's universe was TRUNCATED because the tool
13260
- * named a directory with more entries than the net may watch. Bounded per
13261
- * turn with the ledger holding the exact count, and the identity is the
13262
- * tool name so the record answers WHICH tool is being watched partially
13263
- * (#2449 review round 3).
13264
- */
13265
- "observed_target_dir_capped",
13266
- /**
13267
- * #2430: the "pi-lens wrote these bytes itself" set hit
13268
- * `OBSERVED_HANDLED_MAX` and dropped its oldest entry. The dropped file is
13269
- * NAMED, because the eviction silently reintroduces the exact defect
13270
- * #2449 round 3 (S5) fixed: the ledger still holds the pre-drain bytes
13271
- * while the only record that those bytes were ours is gone, so the next
13272
- * settled sweep replays pi-lens's own formatter output as third-party
13273
- * drift. A cap that drops a mark has to say which one (catalog shape 10);
13274
- * capped per turn because a turn that overflows the set overflows it many
13275
- * times (#2449 review round 4, S2).
13276
- */
13277
- "observed_handled_evicted"
13278
- ];
13279
- REGISTERED_PHASES = new Set(BOUNDED_TELEMETRY_PHASES);
13280
13280
  MAX_TRACKED_TURN_CAPS = 64;
13281
13281
  turnCounts = /* @__PURE__ */ new Map();
13282
13282
  retiredThroughTurnIndex = Number.NEGATIVE_INFINITY;
@@ -13525,18 +13525,13 @@ __export(file_kinds_exports, {
13525
13525
  SPECIAL_FILENAMES: () => SPECIAL_FILENAMES,
13526
13526
  TERRAGRUNT_FILENAMES: () => TERRAGRUNT_FILENAMES,
13527
13527
  detectFileKind: () => detectFileKind,
13528
- getExtensionsForKind: () => getExtensionsForKind,
13529
13528
  getFileKindLabel: () => getFileKindLabel,
13530
13529
  getFileKindsForExtension: () => getFileKindsForExtension,
13531
13530
  getLanguageId: () => getLanguageId,
13532
- hasKindExtension: () => hasKindExtension,
13533
- isCodeKind: () => isCodeKind,
13534
13531
  isCodeKindFile: () => isCodeKindFile,
13535
- isConfigKind: () => isConfigKind,
13536
13532
  isFileKind: () => isFileKind,
13537
13533
  isJstsFactFile: () => isJstsFactFile,
13538
- isReadableSourceFile: () => isReadableSourceFile,
13539
- isScannableFile: () => isScannableFile
13534
+ isReadableSourceFile: () => isReadableSourceFile
13540
13535
  });
13541
13536
  import { basename as basename7, extname as extname2 } from "node:path";
13542
13537
  function hasKindExtension(filePath, kind) {
@@ -13577,12 +13572,6 @@ function getFileKindsForExtension(ext2) {
13577
13572
  const kind = EXT_TO_KIND.get(normalizedExt.toLowerCase());
13578
13573
  return kind ? [kind] : [];
13579
13574
  }
13580
- function isCodeKind(kind) {
13581
- return CODE_KINDS.has(kind);
13582
- }
13583
- function isConfigKind(kind) {
13584
- return NON_CODE_KINDS.has(kind);
13585
- }
13586
13575
  function isCodeKindFile(filePath) {
13587
13576
  const kind = detectFileKind(filePath);
13588
13577
  return kind !== void 0 && CODE_KINDS.has(kind);
@@ -13630,19 +13619,6 @@ function getFileKindLabel(kind) {
13630
13619
  };
13631
13620
  return labels[kind] ?? kind;
13632
13621
  }
13633
- function getExtensionsForKind(kind) {
13634
- return [...KIND_EXTENSIONS[kind] ?? []];
13635
- }
13636
- function isScannableFile(filePath) {
13637
- const kind = detectFileKind(filePath);
13638
- if (!kind)
13639
- return false;
13640
- const base = basename7(filePath);
13641
- if (base.includes(".test.") || base.includes(".spec.") || base.startsWith("test-") || base.startsWith("spec-")) {
13642
- return false;
13643
- }
13644
- return isCodeKind(kind) || isConfigKind(kind);
13645
- }
13646
13622
  function getLanguageId(kind) {
13647
13623
  const languageIds = {
13648
13624
  jsts: "typescript",
@@ -16374,7 +16350,8 @@ Re-read the relevant section, then retry the edit using the current line range/c
16374
16350
  return filePath.endsWith(suffix);
16375
16351
  }
16376
16352
  if (pattern.includes("*")) {
16377
- const regex = new RegExp(`^${pattern.replace(/\\/g, "\\\\").replace(/\./g, "\\.").replace(/\*/g, ".*")}$`);
16353
+ const collapsedPattern = pattern.replace(/\*+/g, "*");
16354
+ const regex = new RegExp(`^${collapsedPattern.replace(/\\/g, "\\\\").replace(/\./g, "\\.").replace(/\*/g, ".*")}$`);
16378
16355
  return regex.test(filePath);
16379
16356
  }
16380
16357
  return filePath === pattern;
@@ -18177,6 +18154,37 @@ var init_package_manager = __esm({
18177
18154
  }
18178
18155
  });
18179
18156
 
18157
+ // dist/clients/hook-budgets.js
18158
+ var HOOK_WALL_BUDGET_MS, HOOK_BUDGET_KEYS;
18159
+ var init_hook_budgets = __esm({
18160
+ "dist/clients/hook-budgets.js"() {
18161
+ "use strict";
18162
+ HOOK_WALL_BUDGET_MS = Object.freeze({
18163
+ /** Visible startup work; the user is watching a progress line. */
18164
+ session_start: 5e3,
18165
+ /** Pure bookkeeping — nothing here may await (#2523 contract). */
18166
+ turn_start: 0,
18167
+ /** Measured p50 3687 ms / p90 14246 ms today. The gap IS the issue. */
18168
+ turn_end: 3e3,
18169
+ /** Measured p90 10043 ms today, from one unbounded pipeline re-entry. */
18170
+ agent_end: 1e3,
18171
+ /** The designated place for settled-time sweeps; the widest non-edit budget. */
18172
+ agent_settled: 1e4,
18173
+ /** Read/Grep/Glob/Bash: must never await analyzer bootstrap (AC5). */
18174
+ tool_result_read_only: 500,
18175
+ /** The ONLY path the contract lets block the host, and only this long. */
18176
+ tool_result_edit: 1e4,
18177
+ /** Teardown: spawning or awaiting here aborts libuv (#234). */
18178
+ session_shutdown: 0,
18179
+ /** Synchronous message contribution; the host is blocked on the answer. */
18180
+ context: 0,
18181
+ /** Fork bookkeeping; the session is being replaced under it. */
18182
+ session_before_fork: 0
18183
+ });
18184
+ HOOK_BUDGET_KEYS = Object.keys(HOOK_WALL_BUDGET_MS);
18185
+ }
18186
+ });
18187
+
18180
18188
  // dist/clients/installer/managed-tool-refresh-session.js
18181
18189
  function reserveManagedToolRefreshSlot(maxPerSession2) {
18182
18190
  if (refreshesThisSession >= maxPerSession2)
@@ -18352,24 +18360,45 @@ async function installedRefreshCandidates() {
18352
18360
  checkArgs: tool.checkArgs,
18353
18361
  verificationTimeoutMs: tool.verificationTimeoutMs ?? 1e4,
18354
18362
  ...tool.packageName !== void 0 && { packageName: tool.packageName },
18355
- ...tool.binaryName !== void 0 && { binaryName: tool.binaryName }
18363
+ ...tool.binaryName !== void 0 && { binaryName: tool.binaryName },
18364
+ ...tool.packageEntryOf !== void 0 && {
18365
+ packageEntryOf: tool.packageEntryOf
18366
+ }
18356
18367
  });
18357
18368
  }
18358
- return candidates;
18369
+ const byPackage = /* @__PURE__ */ new Map();
18370
+ const distinct = [];
18371
+ for (const candidate of candidates) {
18372
+ if (candidate.strategy !== "npm" || !candidate.packageName) {
18373
+ distinct.push(candidate);
18374
+ continue;
18375
+ }
18376
+ const existing = byPackage.get(candidate.packageName);
18377
+ if (existing) {
18378
+ existing.coveredToolIds?.push(candidate.toolId);
18379
+ continue;
18380
+ }
18381
+ const representative = { ...candidate, coveredToolIds: [candidate.toolId] };
18382
+ byPackage.set(candidate.packageName, representative);
18383
+ distinct.push(representative);
18384
+ }
18385
+ return distinct;
18359
18386
  }
18360
18387
  function selectStaleTools(candidates, state7, now) {
18361
18388
  const interval = refreshIntervalMs();
18362
18389
  const retry = retryIntervalMs();
18363
18390
  const due = candidates.filter((candidate) => {
18364
- const entry = state7.tools[candidate.toolId];
18365
- if (!entry)
18366
- return true;
18367
- const cooldown = entry.failed ? retry : interval;
18368
- return now - entry.checkedAt >= cooldown;
18391
+ return (candidate.coveredToolIds ?? [candidate.toolId]).some((toolId) => {
18392
+ const entry = state7.tools[toolId];
18393
+ if (!entry)
18394
+ return true;
18395
+ const cooldown = entry.failed ? retry : interval;
18396
+ return now - entry.checkedAt >= cooldown;
18397
+ });
18369
18398
  });
18370
18399
  return due.sort((a, b) => {
18371
- const aAt = state7.tools[a.toolId]?.checkedAt ?? 0;
18372
- const bAt = state7.tools[b.toolId]?.checkedAt ?? 0;
18400
+ const aAt = Math.min(...(a.coveredToolIds ?? [a.toolId]).map((toolId) => state7.tools[toolId]?.checkedAt ?? 0));
18401
+ const bAt = Math.min(...(b.coveredToolIds ?? [b.toolId]).map((toolId) => state7.tools[toolId]?.checkedAt ?? 0));
18373
18402
  return aAt !== bAt ? aAt - bAt : a.toolId.localeCompare(b.toolId);
18374
18403
  });
18375
18404
  }
@@ -18410,7 +18439,8 @@ async function refreshNonNpmOne(candidate, entry, now) {
18410
18439
  currentVersion: previousVersion,
18411
18440
  changed: false,
18412
18441
  verified: false,
18413
- ok: false
18442
+ ok: false,
18443
+ attempted: false
18414
18444
  };
18415
18445
  }
18416
18446
  invalidateManagedToolResolution(candidate.toolId);
@@ -18443,7 +18473,8 @@ async function refreshNonNpmOne(candidate, entry, now) {
18443
18473
  // No spawn to verify for the non-npm strategies — a failed refresh never
18444
18474
  // touched the installed copy, so there is nothing new to check.
18445
18475
  verified: false,
18446
- ok: false
18476
+ ok: false,
18477
+ attempted: true
18447
18478
  };
18448
18479
  }
18449
18480
  const currentVersion = attempt.version;
@@ -18470,7 +18501,8 @@ async function refreshNonNpmOne(candidate, entry, now) {
18470
18501
  // (`verifyRefreshedArtifact` for github/archive/maven, the `--version`
18471
18502
  // probe for pip/gem) — `ok: true` here already means "and it runs".
18472
18503
  verified: true,
18473
- ok: true
18504
+ ok: true,
18505
+ attempted: true
18474
18506
  };
18475
18507
  }
18476
18508
  async function refreshNpmOne(candidate, now) {
@@ -18485,7 +18517,8 @@ async function refreshNpmOne(candidate, now) {
18485
18517
  packageName,
18486
18518
  changed: false,
18487
18519
  verified: false,
18488
- ok: false
18520
+ ok: false,
18521
+ attempted: false
18489
18522
  };
18490
18523
  }
18491
18524
  try {
@@ -18516,14 +18549,15 @@ async function performNpmRefresh(candidate, packageName, now) {
18516
18549
  const elapsedMs = Date.now() - startedAt;
18517
18550
  if (spawned.status !== 0 || spawned.error) {
18518
18551
  const reason = (spawned.error?.message ?? spawned.stderr ?? "").slice(0, 200);
18552
+ const coveredIds = candidate.coveredToolIds?.join(",") ?? candidate.toolId;
18519
18553
  const onDisk = await readInstalledVersion(packageName);
18520
18554
  invalidateManagedToolResolution(candidate.toolId);
18521
18555
  recordDegradationOnce({
18522
18556
  kind: "managed-tool-refresh",
18523
18557
  subject: candidate.toolId,
18524
- reason: `${pm} update failed: ${reason || "non-zero exit"}`
18558
+ reason: `package ${packageName}, covered ids ${coveredIds}; ${pm} update failed: ${reason || "non-zero exit"}`
18525
18559
  });
18526
- logSessionStart(`managed-tool-refresh ${candidate.toolId}: ${pm} update failed after ${elapsedMs}ms \u2014 on disk now ${onDisk ?? "unreadable"} (was ${previousVersion ?? "unknown"}); resolution cache cleared for re-probe (${reason || "non-zero exit"})`);
18560
+ logSessionStart(`managed-tool-refresh ${candidate.toolId}: ${pm} update failed after ${elapsedMs}ms \u2014 package ${packageName}, covered ids ${coveredIds}; on disk now ${onDisk ?? "unreadable"} (was ${previousVersion ?? "unknown"}); resolution cache cleared for re-probe (${reason || "non-zero exit"})`);
18527
18561
  await writeRefreshStamp(candidate.toolId, {
18528
18562
  checkedAt: now,
18529
18563
  failed: true,
@@ -18537,7 +18571,8 @@ async function performNpmRefresh(candidate, packageName, now) {
18537
18571
  currentVersion: onDisk,
18538
18572
  changed: onDisk !== void 0 && onDisk !== previousVersion,
18539
18573
  verified: false,
18540
- ok: false
18574
+ ok: false,
18575
+ attempted: true
18541
18576
  };
18542
18577
  }
18543
18578
  const currentVersion = await readInstalledVersion(packageName);
@@ -18545,14 +18580,25 @@ async function performNpmRefresh(candidate, packageName, now) {
18545
18580
  invalidateManagedToolResolution(candidate.toolId);
18546
18581
  const binaryName = candidate.binaryName;
18547
18582
  const binPath = resolveManagedNpmBinPath(binaryName);
18548
- const verified = await verifyToolBinary(binPath, void 0, void 0, candidate.verificationTimeoutMs, candidate.checkArgs);
18583
+ const verified = await verifyToolBinary(
18584
+ binPath,
18585
+ void 0,
18586
+ void 0,
18587
+ candidate.verificationTimeoutMs,
18588
+ candidate.checkArgs,
18589
+ // #2722: the same verification the install used. Without this the daily
18590
+ // refresh would demand a `--version` verdict from a server that can never
18591
+ // give one — a ~4 MB doomed spawn that stamps the refresh failed forever.
18592
+ candidate.packageEntryOf
18593
+ );
18549
18594
  if (!verified) {
18595
+ const coveredIds = candidate.coveredToolIds?.join(",") ?? candidate.toolId;
18550
18596
  recordDegradationOnce({
18551
18597
  kind: "managed-tool-refresh",
18552
18598
  subject: candidate.toolId,
18553
- reason: `binary failed verification after ${pm} update (${previousVersion ?? "unknown"} \u2192 ${currentVersion ?? "unknown"})`
18599
+ reason: `package ${packageName}, covered ids ${coveredIds}; binary failed verification after ${pm} update (${previousVersion ?? "unknown"} \u2192 ${currentVersion ?? "unknown"})`
18554
18600
  });
18555
- logSessionStart(`managed-tool-refresh ${candidate.toolId}: ${pm} update ran but ${binPath} failed verification \u2014 resolution cache cleared for re-probe (${elapsedMs}ms)`);
18601
+ logSessionStart(`managed-tool-refresh ${candidate.toolId}: ${pm} update ran but ${binPath} failed verification \u2014 package ${packageName}, covered ids ${coveredIds}; resolution cache cleared for re-probe (${elapsedMs}ms)`);
18556
18602
  await writeRefreshStamp(candidate.toolId, {
18557
18603
  checkedAt: now,
18558
18604
  failed: true,
@@ -18566,14 +18612,15 @@ async function performNpmRefresh(candidate, packageName, now) {
18566
18612
  currentVersion,
18567
18613
  changed,
18568
18614
  verified: false,
18569
- ok: false
18615
+ ok: false,
18616
+ attempted: true
18570
18617
  };
18571
18618
  }
18572
18619
  await writeRefreshStamp(candidate.toolId, {
18573
18620
  checkedAt: now,
18574
18621
  ...currentVersion !== void 0 && { version: currentVersion }
18575
18622
  });
18576
- logSessionStart(changed ? `managed-tool-refresh ${candidate.toolId}: ${previousVersion ?? "unknown"} \u2192 ${currentVersion} via ${pm} update, verified (${elapsedMs}ms)` : `managed-tool-refresh ${candidate.toolId}: unchanged at ${currentVersion ?? previousVersion ?? "unknown"} (${elapsedMs}ms)`);
18623
+ logSessionStart(changed ? `managed-tool-refresh ${candidate.toolId}: ${previousVersion ?? "unknown"} \u2192 ${currentVersion} via ${pm} update, verified; package ${packageName}, covered ids ${candidate.coveredToolIds?.join(",") ?? candidate.toolId} (${elapsedMs}ms)` : `managed-tool-refresh ${candidate.toolId}: unchanged at ${currentVersion ?? previousVersion ?? "unknown"}; package ${packageName}, covered ids ${candidate.coveredToolIds?.join(",") ?? candidate.toolId} (${elapsedMs}ms)`);
18577
18624
  return {
18578
18625
  toolId: candidate.toolId,
18579
18626
  strategy: "npm",
@@ -18582,7 +18629,8 @@ async function performNpmRefresh(candidate, packageName, now) {
18582
18629
  currentVersion,
18583
18630
  changed,
18584
18631
  verified: true,
18585
- ok: true
18632
+ ok: true,
18633
+ attempted: true
18586
18634
  };
18587
18635
  }
18588
18636
  function runManagedToolRefresh(now = Date.now()) {
@@ -18638,19 +18686,49 @@ async function executeManagedToolRefresh(now, tap = noopTap) {
18638
18686
  releaseReservation();
18639
18687
  return { skipped: "nothing-due", refreshed: [] };
18640
18688
  }
18641
- const want = Math.min(maxPerSession(), stale.length);
18689
+ const stalePackages = /* @__PURE__ */ new Set();
18690
+ const distinctStale = stale.filter((candidate) => {
18691
+ if (candidate.strategy !== "npm" || !candidate.packageName)
18692
+ return true;
18693
+ if (stalePackages.has(candidate.packageName))
18694
+ return false;
18695
+ stalePackages.add(candidate.packageName);
18696
+ return true;
18697
+ });
18698
+ const want = Math.min(maxPerSession(), distinctStale.length);
18642
18699
  while (held < want && reserveManagedToolRefreshSlot(maxPerSession())) {
18643
18700
  held += 1;
18644
18701
  }
18645
18702
  let allowance = held;
18646
18703
  const refreshed = [];
18647
- for (const candidate of stale) {
18704
+ for (const candidate of distinctStale) {
18648
18705
  if (allowance <= 0)
18649
18706
  break;
18650
18707
  allowance -= 1;
18651
18708
  held -= 1;
18652
18709
  try {
18653
- refreshed.push(await refreshOne(candidate, read.state.tools[candidate.toolId], now));
18710
+ const result = await refreshOne(candidate, read.state.tools[candidate.toolId], now);
18711
+ refreshed.push(result);
18712
+ if (result.attempted && candidate.coveredToolIds) {
18713
+ for (const toolId of candidate.coveredToolIds) {
18714
+ if (toolId === candidate.toolId)
18715
+ continue;
18716
+ await bounded(writeRefreshStamp(toolId, {
18717
+ checkedAt: now,
18718
+ ...result.currentVersion !== void 0 && {
18719
+ version: result.currentVersion
18720
+ },
18721
+ ...result.ok ? {} : { failed: true }
18722
+ }), {
18723
+ ms: HOOK_WALL_BUDGET_MS.session_start,
18724
+ // The unref'd timer runs after session_start returns, so no
18725
+ // live turn signal belongs to this background refresh.
18726
+ signal: void 0,
18727
+ hook: "session_start",
18728
+ label: "writeRefreshStamp:covered-tool"
18729
+ });
18730
+ }
18731
+ }
18654
18732
  await tap();
18655
18733
  } catch (err) {
18656
18734
  recordDegradationOnce({
@@ -18669,6 +18747,8 @@ var STATE_FILENAME, STATE_VERSION, DEFAULT_INTERVAL_MS, DEFAULT_RETRY_INTERVAL_M
18669
18747
  var init_managed_tool_refresh = __esm({
18670
18748
  "dist/clients/installer/managed-tool-refresh.js"() {
18671
18749
  "use strict";
18750
+ init_hook_budgets();
18751
+ init_deadline_utils();
18672
18752
  init_degradation_ledger();
18673
18753
  init_durable_store();
18674
18754
  init_package_manager();
@@ -19459,6 +19539,7 @@ __export(installer_exports, {
19459
19539
  isSpawnableCommand: () => isSpawnableCommand,
19460
19540
  isToolInstalled: () => isToolInstalled,
19461
19541
  npmToolNeedsPostinstall: () => npmToolNeedsPostinstall,
19542
+ packageEntryVerification: () => packageEntryVerification,
19462
19543
  parsePinnedVersion: () => parsePinnedVersion,
19463
19544
  pickReleaseAsset: () => pickReleaseAsset,
19464
19545
  pipCommandCandidates: () => pipCommandCandidates,
@@ -19475,11 +19556,12 @@ __export(installer_exports, {
19475
19556
  resolvePlatformPackageBinary: () => resolvePlatformPackageBinary,
19476
19557
  swapExtractedDir: () => swapExtractedDir,
19477
19558
  updateProbeCache: () => updateProbeCache,
19559
+ verifyNpmPackageEntry: () => verifyNpmPackageEntry,
19478
19560
  verifyToolBinary: () => verifyToolBinary,
19479
19561
  wasLastResolveTransient: () => wasLastResolveTransient
19480
19562
  });
19481
19563
  import { spawn as spawn2 } from "node:child_process";
19482
- import { existsSync as existsSync12, statSync as statSync15, unlinkSync as unlinkSync4 } from "node:fs";
19564
+ import { existsSync as existsSync12, statSync as statSync16, unlinkSync as unlinkSync4 } from "node:fs";
19483
19565
  import fs33 from "node:fs/promises";
19484
19566
  import https from "node:https";
19485
19567
  import { createRequire } from "node:module";
@@ -19879,7 +19961,7 @@ async function isCommandAvailable2(command, _args) {
19879
19961
  for (const name of names) {
19880
19962
  const candidate = path38.join(dir, name);
19881
19963
  try {
19882
- const stat4 = statSync15(candidate);
19964
+ const stat4 = statSync16(candidate);
19883
19965
  if (stat4.isFile() && stat4.size > 0) {
19884
19966
  return true;
19885
19967
  }
@@ -19892,7 +19974,7 @@ async function isCommandAvailable2(command, _args) {
19892
19974
  async function isSpawnableCommand(command) {
19893
19975
  if (/[\\/]/.test(command)) {
19894
19976
  try {
19895
- const stat4 = statSync15(command);
19977
+ const stat4 = statSync16(command);
19896
19978
  return stat4.isFile() && stat4.size > 0;
19897
19979
  } catch {
19898
19980
  return false;
@@ -19917,7 +19999,59 @@ function parsePinnedVersion(packageName) {
19917
19999
  function extractVersionToken(output) {
19918
20000
  return output.match(/\d+\.\d+\.\d+(?:[-+][\w.]+)?/)?.[0];
19919
20001
  }
19920
- async function verifyToolBinary(binPath, onVersionOutput, onTransient, timeoutMs = 1e4, verificationArgs = ["--version"]) {
20002
+ function packageEntryVerification(tool) {
20003
+ return tool.verification === "package-entry" ? tool.packageName : void 0;
20004
+ }
20005
+ async function verifyNpmPackageEntry(binPath, packageName) {
20006
+ const nodeModulesDir = path38.dirname(path38.dirname(binPath));
20007
+ const pinned = parsePinnedVersion(packageName);
20008
+ const bareName = pinned ? packageName.slice(0, -(pinned.length + 1)) : packageName;
20009
+ const packageDir = path38.join(nodeModulesDir, ...bareName.split("/"));
20010
+ const shimName = path38.basename(binPath).replace(/\.(cmd|exe|ps1|bat)$/i, "").toLowerCase();
20011
+ const fail = (reason) => {
20012
+ logSessionStart(`auto-install verify: failed for ${binPath} (check=package-entry, kind=${reason})`);
20013
+ return false;
20014
+ };
20015
+ try {
20016
+ const shim = statSync16(binPath);
20017
+ if (!shim.isFile() || shim.size === 0)
20018
+ return fail("shim-not-a-file");
20019
+ } catch {
20020
+ return fail("shim-missing");
20021
+ }
20022
+ let manifest;
20023
+ try {
20024
+ manifest = JSON.parse(await fs33.readFile(path38.join(packageDir, "package.json"), "utf8"));
20025
+ } catch {
20026
+ return fail("package-json-unreadable");
20027
+ }
20028
+ if (typeof manifest.version !== "string" || !manifest.version) {
20029
+ return fail("package-json-no-version");
20030
+ }
20031
+ const bin = manifest.bin;
20032
+ const entry = typeof bin === "string" ? bin : bin && typeof bin === "object" ? Object.entries(bin).find(
20033
+ // Case-folded on purpose: on a case-insensitive filesystem the
20034
+ // shim on disk can differ in case from the manifest key npm
20035
+ // wrote it from, and `path.basename` reads the disk name.
20036
+ ([name]) => name.toLowerCase() === shimName
20037
+ )?.[1] : void 0;
20038
+ if (typeof entry !== "string" || !entry) {
20039
+ return fail("package-json-no-entry");
20040
+ }
20041
+ try {
20042
+ const stat4 = statSync16(path38.join(packageDir, entry));
20043
+ if (!stat4.isFile() || stat4.size === 0)
20044
+ return fail("entry-not-a-file");
20045
+ } catch {
20046
+ return fail("entry-missing");
20047
+ }
20048
+ logSessionStart(`auto-install verify: succeeded for ${binPath} (check=package-entry, version=${manifest.version}, entry=${entry})`);
20049
+ return true;
20050
+ }
20051
+ async function verifyToolBinary(binPath, onVersionOutput, onTransient, timeoutMs = 1e4, verificationArgs = ["--version"], packageEntryOf, onInconclusive) {
20052
+ if (packageEntryOf !== void 0) {
20053
+ return verifyNpmPackageEntry(binPath, packageEntryOf);
20054
+ }
19921
20055
  const isWindows3 = installerPlatform() === "win32";
19922
20056
  const hasKnownWindowsExt = /\.(cmd|exe|ps1)$/i.test(binPath);
19923
20057
  let execPath = isWindows3 && !hasKnownWindowsExt ? `${binPath}.cmd` : binPath;
@@ -19959,6 +20093,14 @@ ${result.stderr}`;
19959
20093
  }
19960
20094
  if (result.signal !== void 0 || result.spawnFailure)
19961
20095
  onTransient?.();
20096
+ if (result.outputTruncated && result.signal === void 0 && !result.spawnFailure) {
20097
+ onInconclusive?.();
20098
+ recordDegradationOnce({
20099
+ kind: "installer-verification-inconclusive",
20100
+ subject: binPath,
20101
+ reason: `transport-required marker unresolved in truncated output (${verificationArgs.join(" ")})`
20102
+ });
20103
+ }
19962
20104
  const kind = result.spawnFailure?.kind ?? (result.signal ? `killed-signal=${result.signal}` : result.error ? "spawn-error" : "exit-nonzero");
19963
20105
  logSessionStart(`auto-install verify: failed for ${binPath} (check=${verificationArgs.join(" ")}, kind=${kind}${result.status !== null ? `, exit=${result.status}` : ""})`);
19964
20106
  return false;
@@ -20048,7 +20190,7 @@ async function getAllToolStatuses() {
20048
20190
  const localPath = installerPlatform() === "win32" ? `${localBase}.cmd` : localBase;
20049
20191
  try {
20050
20192
  await fs33.access(localPath);
20051
- if (await verifyToolBinary(localPath, void 0, void 0, getToolVerificationTimeout(tool), tool.checkArgs)) {
20193
+ if (await verifyToolBinary(localPath, void 0, void 0, getToolVerificationTimeout(tool), tool.checkArgs, packageEntryVerification(tool))) {
20052
20194
  status.installed = true;
20053
20195
  status.source = "pi-lens-auto";
20054
20196
  status.path = localPath;
@@ -20141,7 +20283,7 @@ async function getToolPathResolved(toolId, onTransient) {
20141
20283
  const cmdPath = `${localBase}.cmd`;
20142
20284
  try {
20143
20285
  await fs33.access(cmdPath);
20144
- if (await verifyToolBinary(cmdPath, recordVersion, onTransient, getToolVerificationTimeout(tool), tool.checkArgs)) {
20286
+ if (await verifyToolBinary(cmdPath, recordVersion, onTransient, getToolVerificationTimeout(tool), tool.checkArgs, packageEntryVerification(tool))) {
20145
20287
  return cmdPath;
20146
20288
  }
20147
20289
  logSessionStart(`auto-install verify: ${cmdPath} exists but is broken, will reinstall`);
@@ -20150,7 +20292,7 @@ async function getToolPathResolved(toolId, onTransient) {
20150
20292
  const exePath = `${localBase}.exe`;
20151
20293
  try {
20152
20294
  await fs33.access(exePath);
20153
- if (await verifyToolBinary(exePath, recordVersion, onTransient, getToolVerificationTimeout(tool), tool.checkArgs)) {
20295
+ if (await verifyToolBinary(exePath, recordVersion, onTransient, getToolVerificationTimeout(tool), tool.checkArgs, packageEntryVerification(tool))) {
20154
20296
  return exePath;
20155
20297
  }
20156
20298
  logSessionStart(`auto-install verify: ${exePath} exists but is broken, will reinstall`);
@@ -20160,7 +20302,7 @@ async function getToolPathResolved(toolId, onTransient) {
20160
20302
  if (installerPlatform() !== "win32") {
20161
20303
  try {
20162
20304
  await fs33.access(localBase);
20163
- if (await verifyToolBinary(localBase, recordVersion, onTransient, getToolVerificationTimeout(tool), tool.checkArgs)) {
20305
+ if (await verifyToolBinary(localBase, recordVersion, onTransient, getToolVerificationTimeout(tool), tool.checkArgs, packageEntryVerification(tool))) {
20164
20306
  return localBase;
20165
20307
  }
20166
20308
  logSessionStart(`auto-install verify: ${localBase} exists but is broken, will reinstall`);
@@ -20613,13 +20755,19 @@ function npmToolNeedsPostinstall(packageName) {
20613
20755
  return NEEDS_POSTINSTALL.has(packageName);
20614
20756
  }
20615
20757
  function getRefreshableManagedNpmTools() {
20758
+ const seenPackages = /* @__PURE__ */ new Set();
20616
20759
  return getRefreshableManagedTools().filter((candidate) => candidate.strategy === "npm").map((candidate) => ({
20617
20760
  toolId: candidate.toolId,
20618
20761
  // The npm branch of getRefreshableManagedTools only admits entries that
20619
20762
  // have both, so these are total.
20620
20763
  packageName: candidate.packageName,
20621
20764
  binaryName: candidate.binaryName
20622
- }));
20765
+ })).filter((candidate) => {
20766
+ if (seenPackages.has(candidate.packageName))
20767
+ return false;
20768
+ seenPackages.add(candidate.packageName);
20769
+ return true;
20770
+ });
20623
20771
  }
20624
20772
  function mavenCoordinate(spec) {
20625
20773
  return [
@@ -20647,7 +20795,8 @@ function getRefreshableManagedTools() {
20647
20795
  checkArgs: tool.checkArgs,
20648
20796
  packageName: tool.packageName,
20649
20797
  binaryName: tool.binaryName,
20650
- verificationTimeoutMs: tool.verificationTimeoutMs
20798
+ verificationTimeoutMs: tool.verificationTimeoutMs,
20799
+ packageEntryOf: packageEntryVerification(tool)
20651
20800
  });
20652
20801
  break;
20653
20802
  }
@@ -21200,7 +21349,7 @@ function recordPackageManagerInstallException(toolId, strategyLabel, packageName
21200
21349
  installFailureReasons.set(toolId, message);
21201
21350
  return void 0;
21202
21351
  }
21203
- async function installNpmTool(toolId, packageName, binaryName, verificationArgs = ["--version"], verificationTimeoutMs = 1e4) {
21352
+ async function installNpmTool(toolId, packageName, binaryName, verificationArgs = ["--version"], verificationTimeoutMs = 1e4, packageEntryOf) {
21204
21353
  try {
21205
21354
  await fs33.mkdir(TOOLS_DIR, { recursive: true });
21206
21355
  const packageJsonPath = path38.join(TOOLS_DIR, "package.json");
@@ -21261,11 +21410,15 @@ async function installNpmTool(toolId, packageName, binaryName, verificationArgs
21261
21410
  debugLog(`Verifying ${binaryName}...`);
21262
21411
  let isValid = false;
21263
21412
  let lastAttemptTransient = false;
21413
+ let lastAttemptInconclusive = false;
21264
21414
  for (let attempt = 1; attempt <= 3; attempt++) {
21265
21415
  lastAttemptTransient = false;
21416
+ lastAttemptInconclusive = false;
21266
21417
  isValid = await verifyToolBinary(binPath, void 0, () => {
21267
21418
  lastAttemptTransient = true;
21268
- }, verificationTimeoutMs, verificationArgs);
21419
+ }, verificationTimeoutMs, verificationArgs, packageEntryOf, () => {
21420
+ lastAttemptInconclusive = true;
21421
+ });
21269
21422
  if (isValid)
21270
21423
  break;
21271
21424
  if (attempt < 3) {
@@ -21273,6 +21426,13 @@ async function installNpmTool(toolId, packageName, binaryName, verificationArgs
21273
21426
  await new Promise((r) => setTimeout(r, 1e3 * attempt));
21274
21427
  }
21275
21428
  }
21429
+ if (!isValid && lastAttemptInconclusive) {
21430
+ if (await verifyNpmPackageEntry(binPath, packageName)) {
21431
+ logSessionStart(`auto-install ${packageName}: verification inconclusive (output truncated before the transport-required marker) but the installed tree is intact; keeping installation for re-probe`);
21432
+ return void 0;
21433
+ }
21434
+ logSessionStart(`auto-install ${packageName}: verification inconclusive AND the installed tree is incomplete; cleaning up so the next install can repair it`);
21435
+ }
21276
21436
  if (!isValid && lastAttemptTransient) {
21277
21437
  logSessionStart(`auto-install ${packageName}: verification inconclusive (transient); keeping installation for re-probe`);
21278
21438
  return void 0;
@@ -21455,7 +21615,7 @@ async function installTool(toolId) {
21455
21615
  case "npm": {
21456
21616
  if (!tool.packageName || !tool.binaryName)
21457
21617
  return false;
21458
- const npmPath = await installNpmTool(tool.id, tool.packageName, tool.binaryName, tool.checkArgs, getToolVerificationTimeout(tool));
21618
+ const npmPath = await installNpmTool(tool.id, tool.packageName, tool.binaryName, tool.checkArgs, getToolVerificationTimeout(tool), packageEntryVerification(tool));
21459
21619
  if (npmPath !== void 0) {
21460
21620
  await Promise.resolve().then(() => (init_managed_tool_refresh(), managed_tool_refresh_exports)).then((m) => m.stampManagedToolInstalled(tool.id, tool.packageName)).catch(() => {
21461
21621
  });
@@ -22106,10 +22266,27 @@ var init_installer = __esm({
22106
22266
  binaryName: "docker-langserver"
22107
22267
  },
22108
22268
  {
22269
+ // #2722: intelephense has no CLI — its entry calls createConnection()
22270
+ // unconditionally — and Node prints the offending source line before the
22271
+ // error. The bundle is one ~4 MB minified line, so the transport-required
22272
+ // marker #208 rescues on lands at byte 4,154,741 of a 4,423,356-byte
22273
+ // stderr while Node truncates piped stderr at 1 MiB on exit. Measured on
22274
+ // intelephense@1.18.5, linux, Node v22.22.1:
22275
+ // $ intelephense --version 2>&1 | wc -c -> 1048576
22276
+ // $ intelephense --version 2>&1 | grep -c "Connection input stream is not set" -> 0
22277
+ // $ intelephense --version 2>err.txt; wc -c < err.txt -> 4423356
22278
+ // Same run over the alternatives, each with stdin closed the way
22279
+ // verifyToolBinary closes it (`input: ""`):
22280
+ // --help, -v, --socket=0 -> identical dump (exit 1, 4423356 bytes,
22281
+ // marker at 4154741, 1048576 through a pipe, marker absent)
22282
+ // --stdio -> exit 1 with ZERO bytes on either stream,
22283
+ // so it carries no marker to rescue on either.
22284
+ // No checkArgs value produces a verdict. Verified spawn-free instead.
22109
22285
  id: "intelephense",
22110
22286
  name: "Intelephense",
22111
22287
  checkCommand: "intelephense",
22112
22288
  checkArgs: ["--version"],
22289
+ verification: "package-entry",
22113
22290
  installStrategy: "npm",
22114
22291
  packageName: "intelephense",
22115
22292
  binaryName: "intelephense"
@@ -27763,6 +27940,9 @@ function classifyServerWaitTier(serverId, snapshot) {
27763
27940
  const mode = snapshot.workspaceDiagnosticsSupport?.mode;
27764
27941
  if (mode === "pull")
27765
27942
  return "pull-capable";
27943
+ if (snapshot.customServer === true && mode === "push-only" && snapshot.diagnosticsUnsupported === true && snapshot.diagnosticsPublished !== true) {
27944
+ return "diagnostics-unsupported";
27945
+ }
27766
27946
  if (mode !== "push-only")
27767
27947
  return "waits";
27768
27948
  const strategy = getStrategy(serverId, snapshot.launchVariant);
@@ -28156,7 +28336,7 @@ var init_path_utils2 = __esm({
28156
28336
  });
28157
28337
 
28158
28338
  // dist/clients/lsp/server.js
28159
- import { existsSync as existsSync15, mkdirSync as mkdirSync6, statSync as statSync18 } from "node:fs";
28339
+ import { existsSync as existsSync15, mkdirSync as mkdirSync6, statSync as statSync19 } from "node:fs";
28160
28340
  import { access as access2, readFile as readFile4, readdir, stat } from "node:fs/promises";
28161
28341
  import os16 from "node:os";
28162
28342
  import path47 from "node:path";
@@ -28174,8 +28354,8 @@ function posixFilesystemIsCaseInsensitive(root) {
28174
28354
  const alternatePath = path47.join(path47.dirname(resolved), alternate);
28175
28355
  if (alternate !== name && existsSync15(alternatePath)) {
28176
28356
  try {
28177
- const actual = statSync18(resolved);
28178
- const alternateStat = statSync18(alternatePath);
28357
+ const actual = statSync19(resolved);
28358
+ const alternateStat = statSync19(alternatePath);
28179
28359
  insensitive = actual.dev === alternateStat.dev && actual.ino === alternateStat.ino;
28180
28360
  } catch {
28181
28361
  insensitive = false;
@@ -30725,6 +30905,7 @@ function createCustomServer(config, id) {
30725
30905
  return {
30726
30906
  id,
30727
30907
  name: config.name,
30908
+ custom: true,
30728
30909
  extensions: config.extensions,
30729
30910
  root: config.rootMarkers ? createRootDetector(config.rootMarkers) : async () => process.cwd(),
30730
30911
  async spawn(root) {
@@ -31559,9 +31740,6 @@ var word_index_exports = {};
31559
31740
  __export(word_index_exports, {
31560
31741
  WORD_INDEX_FORMAT_VERSION: () => WORD_INDEX_FORMAT_VERSION,
31561
31742
  WORD_INDEX_MAX_BYTES: () => WORD_INDEX_MAX_BYTES,
31562
- WORD_INDEX_MAX_FILES: () => WORD_INDEX_MAX_FILES,
31563
- WORD_INDEX_QUERY_FILTER_KEYS: () => WORD_INDEX_QUERY_FILTER_KEYS,
31564
- WORD_INDEX_STAT_CONCURRENCY: () => WORD_INDEX_STAT_CONCURRENCY,
31565
31743
  WordIndexQueryError: () => WordIndexQueryError,
31566
31744
  _resetWordIndexBuildGuardForTests: () => _resetWordIndexBuildGuardForTests,
31567
31745
  buildWordIndex: () => buildWordIndex,
@@ -32925,7 +33103,7 @@ function scheduleWordIndexPersist(cwd, index, dbg2) {
32925
33103
  function flushWordIndexPersistsForTests() {
32926
33104
  getWordIndexPersistScheduler().flushAll();
32927
33105
  }
32928
- var wordIndexKey, STOPWORDS, TEST_VENDOR_RE, DOC_FILE_RE, TEST_VENDOR_PENALTY, DOC_FILE_PENALTY, BM25_K1, BM25_B, WORD_INDEX_BUILD_YIELD_BUDGET_MS, WORD_INDEX_LONG_LINE_YIELD_CHARS, WORD_INDEX_RECOMPACT_STORE_FLOOR, WORD_INDEX_RECOMPACT_STORE_FRACTION, asyncWordIndexOperations, WORD_INDEX_MAX_FILES, WORD_INDEX_MAX_BYTES, WORD_INDEX_STAT_CONCURRENCY, WORD_INDEX_INCREMENTAL_CHURN_THRESHOLD, WORD_INDEX_DENSE_REFRESH_MIN_DOCUMENTS, WORD_INDEX_FILE_READ_TOKEN_COST, WORD_INDEX_POSTING_SCAN_TOKEN_COST, WORD_INDEX_QUERY_FILTER_KEYS, WordIndexQueryError, WORD_INDEX_FILTER_TOKEN_RE, WORD_INDEX_FORMAT_VERSION, WORD_INDEX_INT32_MAX, WORD_INDEX_PACKED_FILE_ID_MAX, serializedWordIndexCaches, _lastSerializeWork, buildStatuses, WORD_INDEX_PERSIST_DEBOUNCE_MS_DEFAULT, wordIndexPersistScheduler;
33106
+ var wordIndexKey, STOPWORDS, TEST_VENDOR_RE, DOC_FILE_RE, TEST_VENDOR_PENALTY, DOC_FILE_PENALTY, BM25_K1, BM25_B, WORD_INDEX_BUILD_YIELD_BUDGET_MS, WORD_INDEX_LONG_LINE_YIELD_CHARS, WORD_INDEX_RECOMPACT_STORE_FLOOR, WORD_INDEX_RECOMPACT_STORE_FRACTION, asyncWordIndexOperations, WORD_INDEX_MAX_BYTES, WORD_INDEX_STAT_CONCURRENCY, WORD_INDEX_INCREMENTAL_CHURN_THRESHOLD, WORD_INDEX_DENSE_REFRESH_MIN_DOCUMENTS, WORD_INDEX_FILE_READ_TOKEN_COST, WORD_INDEX_POSTING_SCAN_TOKEN_COST, WORD_INDEX_QUERY_FILTER_KEYS, WordIndexQueryError, WORD_INDEX_FILTER_TOKEN_RE, WORD_INDEX_FORMAT_VERSION, WORD_INDEX_INT32_MAX, WORD_INDEX_PACKED_FILE_ID_MAX, serializedWordIndexCaches, _lastSerializeWork, buildStatuses, WORD_INDEX_PERSIST_DEBOUNCE_MS_DEFAULT, wordIndexPersistScheduler;
32929
33107
  var init_word_index = __esm({
32930
33108
  "dist/clients/word-index.js"() {
32931
33109
  "use strict";
@@ -32997,14 +33175,17 @@ var init_word_index = __esm({
32997
33175
  WORD_INDEX_RECOMPACT_STORE_FLOOR = 64;
32998
33176
  WORD_INDEX_RECOMPACT_STORE_FRACTION = 0.1;
32999
33177
  asyncWordIndexOperations = /* @__PURE__ */ new WeakMap();
33000
- WORD_INDEX_MAX_FILES = 6e3;
33001
33178
  WORD_INDEX_MAX_BYTES = 512 * 1024;
33002
33179
  WORD_INDEX_STAT_CONCURRENCY = 8;
33003
33180
  WORD_INDEX_INCREMENTAL_CHURN_THRESHOLD = 0.3;
33004
33181
  WORD_INDEX_DENSE_REFRESH_MIN_DOCUMENTS = 32;
33005
33182
  WORD_INDEX_FILE_READ_TOKEN_COST = 300;
33006
33183
  WORD_INDEX_POSTING_SCAN_TOKEN_COST = 0.2;
33007
- WORD_INDEX_QUERY_FILTER_KEYS = ["lang", "file", "ext"];
33184
+ WORD_INDEX_QUERY_FILTER_KEYS = [
33185
+ "lang",
33186
+ "file",
33187
+ "ext"
33188
+ ];
33008
33189
  WordIndexQueryError = class extends Error {
33009
33190
  constructor(message) {
33010
33191
  super(message);
@@ -54616,7 +54797,7 @@ var require_main = __commonJS({
54616
54797
  var ril_1 = __importDefault(require_ril());
54617
54798
  ril_1.default.install();
54618
54799
  var path208 = __importStar(__require("path"));
54619
- var os34 = __importStar(__require("os"));
54800
+ var os35 = __importStar(__require("os"));
54620
54801
  var fs132 = __importStar(__require("fs"));
54621
54802
  var crypto_1 = __require("crypto");
54622
54803
  var net_1 = __require("net");
@@ -54758,7 +54939,7 @@ var require_main = __commonJS({
54758
54939
  }
54759
54940
  let randomLength = 32;
54760
54941
  const fixedLength = "/lsp-.sock".length;
54761
- const tmpDir = fs132.realpathSync(XDG_RUNTIME_DIR ?? os34.tmpdir());
54942
+ const tmpDir = fs132.realpathSync(XDG_RUNTIME_DIR ?? os35.tmpdir());
54762
54943
  const limit = safeIpcPathLengths.get(process.platform);
54763
54944
  if (limit !== void 0) {
54764
54945
  randomLength = Math.min(limit - tmpDir.length - fixedLength, randomLength);
@@ -55585,7 +55766,7 @@ async function createWorkspaceUriConfiner(cwd) {
55585
55766
  throw new Error("URI must be a local file URI");
55586
55767
  filePath = uriToPath(uri);
55587
55768
  } catch (err) {
55588
- throw new Error(`invalid workspace edit URI ${uri}: ${err instanceof Error ? err.message : String(err)}`);
55769
+ throw new Error(`invalid workspace edit URI ${uri}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
55589
55770
  }
55590
55771
  if (!path88.isAbsolute(filePath))
55591
55772
  throw new Error(`workspace edit path escapes workspace: ${uri}`);
@@ -57220,13 +57401,14 @@ function resolveConfigurationSection(initialization, section) {
57220
57401
  }
57221
57402
  return cur;
57222
57403
  }
57223
- function setupIncomingHandlers(state7, initialization) {
57404
+ function setupIncomingHandlers(state7, initialization, onDiagnosticsPublished) {
57224
57405
  state7.connection.onNotification("textDocument/publishDiagnostics", (params) => {
57225
57406
  const publishReceivedAt = Date.now();
57226
57407
  const filePath = uriToPath(params.uri);
57227
57408
  const normalizedPath = normalizeMapKey(filePath);
57228
57409
  if (state7.closedDocuments?.has(normalizedPath))
57229
57410
  return;
57411
+ onDiagnosticsPublished?.(state7.serverId);
57230
57412
  const newDiags = normalizeLspDiagnostics(params.diagnostics || []);
57231
57413
  const docVersion = params.version;
57232
57414
  if (PUB_DEBUG) {
@@ -58529,7 +58711,7 @@ async function resolveCodeActionBestEffort(state7, action) {
58529
58711
  }
58530
58712
  async function createLSPClient(options) {
58531
58713
  installCrashGuard();
58532
- const { serverId, process: lspProcess, root, sessionCwd, initialization, initializeTimeoutMs = INITIALIZE_TIMEOUT_MS, launchVariant } = options;
58714
+ const { serverId, process: lspProcess, root, sessionCwd, initialization, initializeTimeoutMs = INITIALIZE_TIMEOUT_MS, launchVariant, onDiagnosticsPublished } = options;
58533
58715
  void recordLspChild({
58534
58716
  pid: lspProcess.pid,
58535
58717
  serverId,
@@ -58666,7 +58848,7 @@ async function createLSPClient(options) {
58666
58848
  return;
58667
58849
  void safeSendNotification(state7.connection, "workspace/didChangeWatchedFiles", { changes });
58668
58850
  });
58669
- setupIncomingHandlers(state7, initialization);
58851
+ setupIncomingHandlers(state7, initialization, onDiagnosticsPublished);
58670
58852
  connection.listen();
58671
58853
  setupConnectionLifecycle(state7, recentStderr);
58672
58854
  let initResult;
@@ -60471,6 +60653,7 @@ var init_lsp = __esm({
60471
60653
  init_lsp_budget();
60472
60654
  init_resource_sampler();
60473
60655
  init_deadline_utils();
60656
+ init_hook_budgets();
60474
60657
  init_safe_spawn();
60475
60658
  init_deferred_lsp_work();
60476
60659
  init_workspace_sweep_hold();
@@ -60540,6 +60723,12 @@ var init_lsp = __esm({
60540
60723
  /** Server/root pairs that already emitted unavailable for the current occurrence. */
60541
60724
  unavailableLogged = /* @__PURE__ */ new Set();
60542
60725
  optionalDisabled = /* @__PURE__ */ new Set();
60726
+ /**
60727
+ * One first-contact diagnostic wait per custom server in this service
60728
+ * generation. Concurrent touches share the probe; a hook cutoff never arms
60729
+ * the navigation-only latch, so the next touch can try again.
60730
+ */
60731
+ firstContactWaits = /* @__PURE__ */ new Map();
60543
60732
  /**
60544
60733
  * #1934 review F1: what the last COMPLETED `spawnClient` call for a
60545
60734
  * (server, root) key decided, written by that call at every point it
@@ -60727,6 +60916,8 @@ var init_lsp = __esm({
60727
60916
  clientSpawnGate = Promise.resolve();
60728
60917
  /** True after shutdown() has been called; blocks new operations */
60729
60918
  isDestroyed = false;
60919
+ /** Aborts bounded in-flight service work when this generation is retired. */
60920
+ shutdownController = new AbortController();
60730
60921
  /**
60731
60922
  * #850: teardown completion for every singleton generation retired before
60732
60923
  * this service was published. Only replacement services receive one; direct
@@ -60745,7 +60936,9 @@ var init_lsp = __esm({
60745
60936
  inFlight: /* @__PURE__ */ new Map(),
60746
60937
  clientSpawnedAt: /* @__PURE__ */ new Map(),
60747
60938
  demonstratedReady: /* @__PURE__ */ new Set(),
60748
- demonstratedCold: /* @__PURE__ */ new Set()
60939
+ demonstratedCold: /* @__PURE__ */ new Set(),
60940
+ diagnosticsPublished: /* @__PURE__ */ new Set(),
60941
+ diagnosticsUnsupported: /* @__PURE__ */ new Set()
60749
60942
  };
60750
60943
  }
60751
60944
  /**
@@ -62496,6 +62689,9 @@ var init_lsp = __esm({
62496
62689
  }
62497
62690
  const override = getServerInitOverride(server.id, filePath);
62498
62691
  const mergedInit = mergeInitializationOptions(spawned.initialization, override?.initializationOptions);
62692
+ this.state.diagnosticsPublished.delete(server.id);
62693
+ this.state.diagnosticsUnsupported.delete(server.id);
62694
+ this.firstContactWaits.delete(server.id);
62499
62695
  const client = await createLSPClient({
62500
62696
  serverId: server.id,
62501
62697
  process: spawned.process,
@@ -62503,7 +62699,10 @@ var init_lsp = __esm({
62503
62699
  sessionCwd: this.sessionCwd,
62504
62700
  initialization: mergedInit,
62505
62701
  initializeTimeoutMs: server.initializeTimeoutMs,
62506
- launchVariant: spawned.launchVariant
62702
+ launchVariant: spawned.launchVariant,
62703
+ onDiagnosticsPublished: (serverId) => {
62704
+ this.state.diagnosticsPublished.add(serverId);
62705
+ }
62507
62706
  });
62508
62707
  if (this.isDestroyed) {
62509
62708
  client.shutdown({ fast: true }).catch(() => {
@@ -62619,6 +62818,7 @@ var init_lsp = __esm({
62619
62818
  return;
62620
62819
  }
62621
62820
  const startedAt = Date.now();
62821
+ const hookDeadlineAt = options.hook !== void 0 && Object.hasOwn(HOOK_WALL_BUDGET_MS, options.hook) ? startedAt + HOOK_WALL_BUDGET_MS[options.hook] : void 0;
62622
62822
  const normalizedPath = normalizeMapKey(filePath);
62623
62823
  const outsideRoot = await this.findOutsideProjectRoot(filePath);
62624
62824
  if (outsideRoot) {
@@ -62696,6 +62896,17 @@ var init_lsp = __esm({
62696
62896
  }
62697
62897
  const leaseKeys = await this.acquireClientLeases(spawned, filePath);
62698
62898
  if (!leaseKeys) {
62899
+ if (this.checkDestroyed()) {
62900
+ const primaryServerIds = spawned.filter((entry) => entry.info.role !== "auxiliary").map((entry) => entry.info.id);
62901
+ return {
62902
+ diags: [],
62903
+ inconclusive: true,
62904
+ ...primaryServerIds.length > 0 && {
62905
+ inconclusiveServerIds: primaryServerIds
62906
+ },
62907
+ inconclusiveReason: "diagnostics-wait"
62908
+ };
62909
+ }
62699
62910
  return this.touchFile(filePath, content, options);
62700
62911
  }
62701
62912
  try {
@@ -62855,6 +63066,7 @@ var init_lsp = __esm({
62855
63066
  let auxUnconfirmedServerIds;
62856
63067
  let tsserverSyncEligible = false;
62857
63068
  let tsserverSyncConfirmed;
63069
+ let diagnosticsUnsupportedServerIds = [];
62858
63070
  if (diagnosticsMode !== "none") {
62859
63071
  const envWait = readEnvDiagnosticsWaitMs();
62860
63072
  const callerCap = options.maxDiagnosticsWaitMs ?? options.maxClientWaitMs;
@@ -62880,6 +63092,9 @@ var init_lsp = __esm({
62880
63092
  root: entry.client.root,
62881
63093
  operationSupport: entry.client.getOperationSupport(),
62882
63094
  workspaceDiagnosticsSupport: entry.client.getWorkspaceDiagnosticsSupport(),
63095
+ customServer: entry.info.custom,
63096
+ diagnosticsPublished: this.state.diagnosticsPublished.has(entry.info.id),
63097
+ diagnosticsUnsupported: this.state.diagnosticsUnsupported.has(entry.info.id),
62883
63098
  advertisedCommands: entry.client.getAdvertisedCommands(),
62884
63099
  rawCapabilityKeys: entry.client.getRawCapabilityKeys?.() ?? [],
62885
63100
  launchVariant: entry.client.getLaunchVariant?.()
@@ -62931,6 +63146,23 @@ var init_lsp = __esm({
62931
63146
  }
62932
63147
  const waitStartedAt = Date.now();
62933
63148
  const hasTouchAuxiliaries = clientScope === "with-auxiliary" && options.collectDiagnostics === true && spawned.some((e) => e.info.role === "auxiliary");
63149
+ if (spawned.some((entry) => entry.info.custom === true)) {
63150
+ try {
63151
+ const snapshotHook = options.hook ?? "tool_result_edit";
63152
+ const snapshotBudget = hookDeadlineAt === void 0 ? HOOK_WALL_BUDGET_MS.tool_result_edit : Math.max(0, hookDeadlineAt - Date.now());
63153
+ const snapshots = await bounded(this.getCapabilitySnapshots(filePath), {
63154
+ ms: snapshotBudget,
63155
+ signal: getAmbientAbortSignal(),
63156
+ shutdownSignal: this.shutdownController.signal,
63157
+ hook: snapshotHook,
63158
+ label: `getCapabilitySnapshots:${filePath}`
63159
+ });
63160
+ if (snapshots !== void 0) {
63161
+ diagnosticsUnsupportedServerIds = spawned.filter((entry) => classifyServerWaitTier(entry.info.id, snapshots.find((s) => s.serverId === entry.info.id)) === "diagnostics-unsupported").map((entry) => entry.info.id);
63162
+ }
63163
+ } catch {
63164
+ }
63165
+ }
62934
63166
  let pressureSnapshots = [];
62935
63167
  if (shouldPreferPullOnlyDiagnostics()) {
62936
63168
  try {
@@ -62945,10 +63177,27 @@ var init_lsp = __esm({
62945
63177
  const observedSpawnMs = getSuccessfulLspSpawnDurationMs(entry.client.serverId);
62946
63178
  return hasTouchAuxiliaries && entry.info.role === "auxiliary" && coldAuxiliaryServerIds.has(entry.client.serverId) && (configuredAuxCeilingMs !== void 0 || observedSpawnMs !== void 0 && observedSpawnMs > 0) ? auxWaitBudgetMs(entry.client.serverId, true, configuredAuxCeilingMs, declaredServerTimeout) : perServerDeclaredTimeouts[entryIndex];
62947
63179
  });
63180
+ const firstContactCutOffServerIds = /* @__PURE__ */ new Set();
63181
+ const firstContactErroredServerIds = /* @__PURE__ */ new Set();
63182
+ const answeredForThisTouch = (entry) => {
63183
+ const baseline = diagnosticBaselines.get(entry.client);
63184
+ const currentPathVersion = readPathVersion(entry.client);
63185
+ if (Number.isFinite(baseline) && currentPathVersion !== void 0 && currentPathVersion > baseline) {
63186
+ return true;
63187
+ }
63188
+ try {
63189
+ return entry.client.getAllDiagnostics?.().has(normalizedPath) === true;
63190
+ } catch {
63191
+ return false;
63192
+ }
63193
+ };
62948
63194
  const perServerRaceBudgets = spawned.map((entry, entryIndex) => {
62949
63195
  return hasTouchAuxiliaries && entry.info.role === "auxiliary" ? auxWaitBudgetMs(entry.client.serverId, coldAuxiliaryServerIds.has(entry.client.serverId), configuredAuxCeilingMs, perServerDeclaredTimeouts[entryIndex]) : perServerDeclaredTimeouts[entryIndex];
62950
63196
  });
62951
63197
  const perServerWaits = spawned.map((entry, entryIndex) => {
63198
+ if (this.state.diagnosticsUnsupported.has(entry.info.id) || diagnosticsUnsupportedServerIds.includes(entry.info.id)) {
63199
+ return Promise.resolve(void 0);
63200
+ }
62952
63201
  if (deferredResyncServerIds.has(entry.info.id)) {
62953
63202
  return Promise.resolve(void 0);
62954
63203
  }
@@ -62956,7 +63205,7 @@ var init_lsp = __esm({
62956
63205
  const baseline = diagnosticBaselines.get(entry.client);
62957
63206
  const pullOnly = classifyServerWaitTier(entry.client.serverId, pressureSnapshots.find((snapshot) => snapshot.serverId === entry.client.serverId)) === "pull-capable";
62958
63207
  const isWarmupTouch = source === "lsp_sweep_warmup";
62959
- const wait = !notifySkippedServerIds.has(entry.info.id) && Number.isFinite(baseline) ? entry.client.waitForDiagnostics(filePath, serverTimeout, {
63208
+ const waitForDiagnostics = () => !notifySkippedServerIds.has(entry.info.id) && Number.isFinite(baseline) ? entry.client.waitForDiagnostics(filePath, serverTimeout, {
62960
63209
  minVersion: baseline,
62961
63210
  ...pullOnly && { pullOnly: true },
62962
63211
  ...isWarmupTouch && { pullSettleSource: "pull-warmup" }
@@ -62966,7 +63215,64 @@ var init_lsp = __esm({
62966
63215
  }) : isWarmupTouch ? entry.client.waitForDiagnostics(filePath, serverTimeout, {
62967
63216
  pullSettleSource: "pull-warmup"
62968
63217
  }) : entry.client.waitForDiagnostics(filePath, serverTimeout);
62969
- return wait.catch(() => void 0);
63218
+ const firstContact = entry.info.custom === true && !this.state.diagnosticsPublished.has(entry.info.id) && !this.state.diagnosticsUnsupported.has(entry.info.id) && entry.client.getWorkspaceDiagnosticsSupport().mode !== "pull";
63219
+ if (!firstContact)
63220
+ return waitForDiagnostics().catch(() => void 0);
63221
+ let shared = this.firstContactWaits.get(entry.info.id);
63222
+ if (!shared) {
63223
+ let removeShutdownListener;
63224
+ const shutdown = new Promise((resolve135) => {
63225
+ const onShutdown = () => resolve135("shutdown");
63226
+ if (this.shutdownController.signal.aborted) {
63227
+ onShutdown();
63228
+ return;
63229
+ }
63230
+ this.shutdownController.signal.addEventListener("abort", onShutdown, {
63231
+ once: true
63232
+ });
63233
+ removeShutdownListener = () => this.shutdownController.signal.removeEventListener("abort", onShutdown);
63234
+ });
63235
+ const probe = Promise.race([
63236
+ waitForDiagnostics().then(() => "completed", () => "errored"),
63237
+ shutdown
63238
+ ]);
63239
+ const completed = withDeadline(probe, {
63240
+ ms: serverTimeout,
63241
+ onTimeout: "undefined"
63242
+ });
63243
+ shared = completed.then((outcome) => {
63244
+ if (outcome === "errored" || outcome === "shutdown") {
63245
+ return "errored";
63246
+ }
63247
+ if (answeredForThisTouch(entry))
63248
+ return "answered";
63249
+ this.state.diagnosticsUnsupported.add(entry.info.id);
63250
+ recordDegradationOnce({
63251
+ kind: "lsp-diagnostics-unsupported",
63252
+ subject: entry.info.id,
63253
+ reason: "custom server has no pull provider and no publish evidence after first contact wait"
63254
+ });
63255
+ return "silent";
63256
+ }).catch(() => "errored").finally(() => {
63257
+ removeShutdownListener?.();
63258
+ if (this.firstContactWaits.get(entry.info.id) === shared) {
63259
+ this.firstContactWaits.delete(entry.info.id);
63260
+ }
63261
+ });
63262
+ this.firstContactWaits.set(entry.info.id, shared);
63263
+ }
63264
+ const callerWait = hookDeadlineAt === void 0 ? shared : withDeadline(shared, {
63265
+ ms: Math.max(0, hookDeadlineAt - Date.now()),
63266
+ onTimeout: "undefined"
63267
+ });
63268
+ return callerWait.then((outcome) => {
63269
+ if (outcome === void 0) {
63270
+ firstContactCutOffServerIds.add(entry.info.id);
63271
+ } else if (outcome === "errored") {
63272
+ firstContactErroredServerIds.add(entry.info.id);
63273
+ }
63274
+ return void 0;
63275
+ });
62970
63276
  });
62971
63277
  let pushWaitSettled = false;
62972
63278
  const pushWait = hasTouchAuxiliaries ? (() => {
@@ -63060,9 +63366,10 @@ var init_lsp = __esm({
63060
63366
  });
63061
63367
  })() : Promise.all(perServerWaits).then(() => {
63062
63368
  });
63063
- pushWait.then(() => {
63369
+ const markPushWaitSettled = () => {
63064
63370
  pushWaitSettled = true;
63065
- });
63371
+ };
63372
+ void pushWait.then(markPushWaitSettled, markPushWaitSettled);
63066
63373
  if (tsserverSyncEligible) {
63067
63374
  const graceMs = readTsserverSyncGraceMs();
63068
63375
  const primaryClient = spawned[0].client;
@@ -63099,6 +63406,27 @@ var init_lsp = __esm({
63099
63406
  await pushWait;
63100
63407
  }
63101
63408
  const waitedMs = Date.now() - waitStartedAt;
63409
+ const firstContactCustomServerIds = spawned.filter((entry) => entry.info.custom === true && !this.state.diagnosticsPublished.has(entry.info.id) && !this.state.diagnosticsUnsupported.has(entry.info.id) && !firstContactCutOffServerIds.has(entry.info.id) && !firstContactErroredServerIds.has(entry.info.id) && entry.client.getWorkspaceDiagnosticsSupport().mode !== "pull").map((entry) => entry.info.id);
63410
+ for (const serverId of firstContactCustomServerIds) {
63411
+ const entry = spawned.find((candidate) => candidate.info.id === serverId);
63412
+ if (!entry || answeredForThisTouch(entry))
63413
+ continue;
63414
+ this.state.diagnosticsUnsupported.add(serverId);
63415
+ diagnosticsUnsupportedServerIds.push(serverId);
63416
+ recordDegradationOnce({
63417
+ kind: "lsp-diagnostics-unsupported",
63418
+ subject: serverId,
63419
+ reason: "custom server has no pull provider and no publish evidence after first contact wait"
63420
+ });
63421
+ }
63422
+ for (const entry of spawned) {
63423
+ if (entry.info.custom === true && this.state.diagnosticsUnsupported.has(entry.info.id)) {
63424
+ diagnosticsUnsupportedServerIds.push(entry.info.id);
63425
+ }
63426
+ }
63427
+ diagnosticsUnsupportedServerIds = [
63428
+ ...new Set(diagnosticsUnsupportedServerIds)
63429
+ ];
63102
63430
  if (!hasTouchAuxiliaries && options.collectDiagnostics === true) {
63103
63431
  const auxEntries = spawned.filter((entry) => entry.info.role === "auxiliary");
63104
63432
  if (auxEntries.length > 0) {
@@ -63144,25 +63472,14 @@ var init_lsp = __esm({
63144
63472
  savedVsBudgetMs: Math.max(0, timeoutMs - waitedMs)
63145
63473
  }
63146
63474
  });
63147
- } else if (waitedMs + 20 >= timeoutMs) {
63148
- const answeredForThisTouch = (entry) => {
63149
- const baseline = diagnosticBaselines.get(entry.client);
63150
- const currentPathVersion = readPathVersion(entry.client);
63151
- if (Number.isFinite(baseline) && currentPathVersion !== void 0 && currentPathVersion > baseline) {
63152
- return true;
63153
- }
63154
- try {
63155
- return entry.client.getAllDiagnostics?.().has(normalizedPath) === true;
63156
- } catch {
63157
- return false;
63158
- }
63159
- };
63160
- const waited = spawned.filter((entry) => !deferredResyncServerIds.has(entry.info.id));
63475
+ } else if (waitedMs + 20 >= timeoutMs || firstContactErroredServerIds.size > 0) {
63476
+ const waited = spawned.filter((entry) => !deferredResyncServerIds.has(entry.info.id) && !diagnosticsUnsupportedServerIds.includes(entry.info.id));
63161
63477
  const unanswered = waited.filter((entry) => !answeredForThisTouch(entry));
63162
63478
  diagnosticsUnansweredServerIds = unanswered.map((e) => e.info.id);
63163
63479
  diagnosticsUnansweredPrimaryServerIds = unanswered.filter((entry) => entry.info.role !== "auxiliary").map((e) => e.info.id);
63164
63480
  const hasWaitedPrimary = waited.some((entry) => entry.info.role !== "auxiliary");
63165
- diagnosticsTimedOut = !hasWaitedPrimary || diagnosticsUnansweredPrimaryServerIds.length > 0;
63481
+ const hasUnsupportedPrimary = diagnosticsUnsupportedServerIds.some((serverId) => spawned.some((entry) => entry.info.id === serverId && entry.info.role !== "auxiliary"));
63482
+ diagnosticsTimedOut = !hasWaitedPrimary && !hasUnsupportedPrimary || diagnosticsUnansweredPrimaryServerIds.length > 0;
63166
63483
  for (const entry of unanswered) {
63167
63484
  incrementDegradationCount({
63168
63485
  kind: "lsp-diagnostics-timeout",
@@ -63384,16 +63701,21 @@ var init_lsp = __esm({
63384
63701
  }
63385
63702
  }
63386
63703
  const result = { diags: collected ?? [] };
63704
+ const primaryDiagnosticsUnsupported = diagnosticsUnsupportedServerIds.some((serverId) => spawned.some((entry) => entry.info.id === serverId && entry.info.role !== "auxiliary"));
63705
+ if (diagnosticsUnsupportedServerIds.length > 0) {
63706
+ result.diagnosticsUnsupportedServerIds = diagnosticsUnsupportedServerIds;
63707
+ }
63387
63708
  if (collected !== void 0 && inconclusive) {
63388
63709
  result.inconclusive = true;
63389
63710
  if (verdict.inconclusiveServerIds) {
63390
63711
  result.inconclusiveServerIds = verdict.inconclusiveServerIds;
63391
63712
  }
63392
63713
  result.inconclusiveReason = verdict.inconclusiveReason;
63714
+ } else if (collected !== void 0 && primaryDiagnosticsUnsupported) {
63393
63715
  } else if (collected !== void 0 && coverageGap2) {
63394
63716
  result.confirmation = "partial";
63395
63717
  result.unconfirmedServerIds = [...unconfirmedServerIds];
63396
- } else if (collected !== void 0) {
63718
+ } else if (collected !== void 0 && !primaryDiagnosticsUnsupported) {
63397
63719
  result.confirmation = "confirmed";
63398
63720
  }
63399
63721
  let binding;
@@ -64103,6 +64425,9 @@ var init_lsp = __esm({
64103
64425
  root,
64104
64426
  operationSupport: client.getOperationSupport(),
64105
64427
  workspaceDiagnosticsSupport: client.getWorkspaceDiagnosticsSupport(),
64428
+ customServer: server.custom,
64429
+ diagnosticsPublished: this.state.diagnosticsPublished.has(server.id),
64430
+ diagnosticsUnsupported: this.state.diagnosticsUnsupported.has(server.id),
64106
64431
  advertisedCommands: client.getAdvertisedCommands(),
64107
64432
  rawCapabilityKeys: client.getRawCapabilityKeys?.() ?? [],
64108
64433
  launchVariant: client.getLaunchVariant?.()
@@ -64120,6 +64445,9 @@ var init_lsp = __esm({
64120
64445
  root: client.root,
64121
64446
  operationSupport: client.getOperationSupport(),
64122
64447
  workspaceDiagnosticsSupport: client.getWorkspaceDiagnosticsSupport(),
64448
+ customServer: this.state.servers.get(serverId)?.custom,
64449
+ diagnosticsPublished: this.state.diagnosticsPublished.has(serverId),
64450
+ diagnosticsUnsupported: this.state.diagnosticsUnsupported.has(serverId),
64123
64451
  advertisedCommands: client.getAdvertisedCommands(),
64124
64452
  rawCapabilityKeys: client.getRawCapabilityKeys?.() ?? [],
64125
64453
  launchVariant: client.getLaunchVariant?.()
@@ -65237,6 +65565,7 @@ var init_lsp = __esm({
65237
65565
  if (this.checkDestroyed())
65238
65566
  return;
65239
65567
  this.isDestroyed = true;
65568
+ this.shutdownController.abort();
65240
65569
  for (const [key4, token] of this.outstandingAuxNotifyWrites) {
65241
65570
  this.releaseOutstandingAuxNotifyWrite(key4, token);
65242
65571
  }
@@ -65273,6 +65602,7 @@ var init_lsp = __esm({
65273
65602
  });
65274
65603
  this.state.clients.clear();
65275
65604
  this.state.broken.clear();
65605
+ this.firstContactWaits.clear();
65276
65606
  this.lastSpawnVerdict.clear();
65277
65607
  for (const [key4, token] of this.outstandingAuxNotifyWrites) {
65278
65608
  this.releaseOutstandingAuxNotifyWrite(key4, token);
@@ -68728,45 +69058,8 @@ var init_widget_state = __esm({
68728
69058
  var scan_utils_exports = {};
68729
69059
  __export(scan_utils_exports, {
68730
69060
  getSourceFiles: () => getSourceFiles,
68731
- getSourceFilesAsync: () => getSourceFilesAsync,
68732
- parseAstGrepJson: () => parseAstGrepJson,
68733
- shouldIgnoreFile: () => shouldIgnoreFile
69061
+ getSourceFilesAsync: () => getSourceFilesAsync
68734
69062
  });
68735
- function parseAstGrepJson(raw) {
68736
- if (!raw)
68737
- return [];
68738
- const trimmed = raw.trim();
68739
- if (trimmed.startsWith("[")) {
68740
- try {
68741
- return JSON.parse(trimmed);
68742
- } catch {
68743
- return [];
68744
- }
68745
- }
68746
- return trimmed.split("\n").flatMap((l) => {
68747
- try {
68748
- return [JSON.parse(l)];
68749
- } catch {
68750
- return [];
68751
- }
68752
- });
68753
- }
68754
- function shouldIgnoreFile(filePath, isTsProject) {
68755
- const relPath = filePath.replace(/\\/g, "/");
68756
- if (isTsProject && isBuildArtifact(filePath))
68757
- return true;
68758
- const isJs = relPath.endsWith(".js") || relPath.endsWith(".mjs") || relPath.endsWith(".cjs");
68759
- if (isTsProject && isJs)
68760
- return true;
68761
- if (isTestFile(filePath))
68762
- return true;
68763
- const pathParts = relPath.split("/").filter(Boolean);
68764
- for (const segment of pathParts.slice(0, -1)) {
68765
- if (isExcludedDirName(segment))
68766
- return true;
68767
- }
68768
- return false;
68769
- }
68770
69063
  function getSourceFiles(dir, _isTsProject) {
68771
69064
  return collectSourceFiles(dir);
68772
69065
  }
@@ -68776,7 +69069,6 @@ function getSourceFilesAsync(dir, _isTsProject) {
68776
69069
  var init_scan_utils = __esm({
68777
69070
  "dist/clients/scan-utils.js"() {
68778
69071
  "use strict";
68779
- init_file_utils();
68780
69072
  init_source_filter();
68781
69073
  }
68782
69074
  });
@@ -68790,9 +69082,6 @@ __export(language_profile_exports, {
68790
69082
  detectProjectLanguageProfile: () => detectProjectLanguageProfile,
68791
69083
  detectProjectLanguageProfileAsync: () => detectProjectLanguageProfileAsync,
68792
69084
  getDefaultStartupTools: () => getDefaultStartupTools,
68793
- hasAnyLanguage: () => hasAnyLanguage,
68794
- hasLanguage: () => hasLanguage,
68795
- isLanguageConfigured: () => isLanguageConfigured,
68796
69085
  resolveLanguageRootForFile: () => resolveLanguageRootForFile
68797
69086
  });
68798
69087
  import * as fs81 from "node:fs";
@@ -68856,15 +69145,6 @@ function computeProjectLanguageProfile(projectRoot, sourceFiles) {
68856
69145
  detectedKinds
68857
69146
  };
68858
69147
  }
68859
- function hasLanguage(profile, kind) {
68860
- return !!profile.present[kind];
68861
- }
68862
- function hasAnyLanguage(profile, kinds) {
68863
- return kinds.some((kind) => hasLanguage(profile, kind));
68864
- }
68865
- function isLanguageConfigured(profile, kind) {
68866
- return !!profile.configured[kind];
68867
- }
68868
69148
  function getDefaultStartupTools(profile) {
68869
69149
  const tools = /* @__PURE__ */ new Set();
68870
69150
  for (const kind of Object.keys(LANGUAGE_POLICY)) {
@@ -73031,14 +73311,7 @@ var init_yaml_rule_parser = __esm({
73031
73311
  init_js_yaml2();
73032
73312
  init_bounded_cache();
73033
73313
  init_string_utils();
73034
- OVERLY_BROAD_PATTERNS = [
73035
- "$NAME",
73036
- "$FIELD",
73037
- "$_",
73038
- "$X",
73039
- "$VAR",
73040
- "$EXPR"
73041
- ];
73314
+ OVERLY_BROAD_PATTERNS = ["$NAME", "$FIELD", "$_", "$X", "$VAR", "$EXPR"];
73042
73315
  MAX_BLOCKING_RULE_COMPLEXITY = 8;
73043
73316
  RULES_CACHE_FRESHNESS_CADENCE_MS = 2e3;
73044
73317
  rulesCache = new BoundedLruCache(64);
@@ -73112,7 +73385,7 @@ async function loadSg() {
73112
73385
  }
73113
73386
  })();
73114
73387
  sgLoadPromise = task;
73115
- task.finally(() => {
73388
+ void task.finally(() => {
73116
73389
  if (sgLoadPromise === task)
73117
73390
  sgLoadPromise = void 0;
73118
73391
  });
@@ -74029,8 +74302,6 @@ function collectProjectSourceFilesWithBudgetAsync(rootDir, options) {
74029
74302
  var init_project_scan_policy = __esm({
74030
74303
  "dist/clients/project-scan-policy.js"() {
74031
74304
  "use strict";
74032
- init_file_utils();
74033
- init_generated_artifacts();
74034
74305
  init_source_filter();
74035
74306
  }
74036
74307
  });
@@ -74836,9 +75107,7 @@ var init_function_facts = __esm({
74836
75107
  // dist/clients/review-graph/builder.js
74837
75108
  var builder_exports = {};
74838
75109
  __export(builder_exports, {
74839
- GRAPH_MAX_IN_MEMORY_BYTES_DEFAULT: () => GRAPH_MAX_IN_MEMORY_BYTES_DEFAULT,
74840
75110
  GRAPH_PERSIST_MAX_ELEMENTS_DEFAULT: () => GRAPH_PERSIST_MAX_ELEMENTS_DEFAULT,
74841
- REVIEW_GRAPH_SOURCE_EXTENSIONS: () => REVIEW_GRAPH_SOURCE_EXTENSIONS,
74842
75111
  REVIEW_GRAPH_VERSION: () => REVIEW_GRAPH_VERSION,
74843
75112
  _getReviewGraphCacheStateForTests: () => _getReviewGraphCacheStateForTests,
74844
75113
  _getReviewGraphRebuildCountersForTests: () => _getReviewGraphRebuildCountersForTests,
@@ -74859,7 +75128,6 @@ __export(builder_exports, {
74859
75128
  _setReviewGraphEntryCounterForTests: () => _setReviewGraphEntryCounterForTests,
74860
75129
  _setReviewGraphWorkspaceEntryForTests: () => _setReviewGraphWorkspaceEntryForTests,
74861
75130
  _setSessionReviewGraphFactForTests: () => _setSessionReviewGraphFactForTests,
74862
- addLspFallbackSymbols: () => addLspFallbackSymbols,
74863
75131
  buildOrUpdateGraph: () => buildOrUpdateGraph,
74864
75132
  captureReviewGraphStructuralIr: () => captureReviewGraphStructuralIr,
74865
75133
  clearGraphCache: () => clearGraphCache,
@@ -74878,7 +75146,6 @@ __export(builder_exports, {
74878
75146
  getLastGraphBuildInfo: () => getLastGraphBuildInfo,
74879
75147
  getLastReviewGraphBuildAttempt: () => getLastReviewGraphBuildAttempt,
74880
75148
  getReviewGraphCacheIdentity: () => getReviewGraphCacheIdentity,
74881
- getReviewGraphMaxFiles: () => getReviewGraphMaxFiles,
74882
75149
  getReviewGraphRevisionDrift: () => getReviewGraphRevisionDrift,
74883
75150
  getReviewGraphSizeSkipVerdict: () => getReviewGraphSizeSkipVerdict,
74884
75151
  getReviewGraphWorkerFallbackReasonForTests: () => getReviewGraphWorkerFallbackReasonForTests,
@@ -78435,7 +78702,7 @@ function extractSymbolsAndRefsFromGraph(graph) {
78435
78702
  coverage.complete = false;
78436
78703
  return { allSymbols, allRefs, coverage };
78437
78704
  }
78438
- var REVIEW_GRAPH_VERSION, MAIN_KINDS, MAIN_KIND_EXTENSIONS, REVIEW_GRAPH_SOURCE_EXTENSIONS, CHANGED_SYMBOLS_PREFIX, extractorCache2, REVIEW_GRAPH_MAX_WARM_WORKSPACES, REVIEW_GRAPH_SOURCE_PATH_MEMO_ENTRIES, _sourcePathMemos, _buildCache, _workspaceGraphCache, REVIEW_GRAPH_IDLE_EVICT_MS_DEFAULT, _workspaceCacheEpoch, _workspaceCacheEpochs, _graphGenerationCounter, _buildIdCounter, SEQ_FASTPATH_MAX_CHANGES, SEQ_FASTPATH_REVERIFY_MS, SEQ_FASTPATH_REVERIFY_EVERY, _lastGraphBuildInfo, _graphBuildInfoByGraph, _anyGraphStamped, _reviewGraphBuildGateForTests, REVIEW_GRAPH_NODE_RESIDENT_BYTES, REVIEW_GRAPH_EDGE_RESIDENT_BYTES, GRAPH_MAX_IN_MEMORY_BYTES_DEFAULT, _retainedGraphSites, _cwdWorktreeCheckedCwds, STAT_YIELD_EVERY, yieldToLoop, DEFAULT_REVIEW_GRAPH_SIZE_SKIP_TTL_MS, _sizeSkipTtl, _resetReviewGraphSizeSkipTtlForTests, _buildAttempts, _sizeSkipVerdicts, REVIEW_GRAPH_SIZE_NEAR_MISS_RATIO, _reviewGraphEntryBudgetForTests, _reviewGraphEntryCounterForTests, GRAPH_CACHE_FILENAME, LEGACY_GRAPH_CACHE_FILENAME, GRAPH_CHECKPOINT_FILENAME, GRAPH_PERSIST_DEBOUNCE_MS_DEFAULT, GRAPH_PERSIST_MAX_ELEMENTS_DEFAULT, _pendingPersist, _persistTimers, _persistGenerations, _workerRequests, _persistWorker, _persistWorkerRequestId, _persistAttemptId, _workerDisabled, _persistWorkerUnavailableReason, _lastWorkerFallbackReasonForTests, _checkpointGenerations, _checkpointWorkerRequests, _checkpointOffloadCountForTests, _persistExitHookInstalled, _sweptStageDirs, REVIEW_GRAPH_ARTIFACT_PREFIX, STAGE_PID_PATTERN, GRAPH_CHECKPOINT_EVERY_FILES_DEFAULT, GRAPH_CHECKPOINT_MIN_INTERVAL_MS_DEFAULT, SYMBOL_KIND_SPECIFICITY, _rebuildCounters, _graphImportChanges;
78705
+ var REVIEW_GRAPH_VERSION, MAIN_KINDS, MAIN_KIND_EXTENSIONS, CHANGED_SYMBOLS_PREFIX, extractorCache2, REVIEW_GRAPH_MAX_WARM_WORKSPACES, REVIEW_GRAPH_SOURCE_PATH_MEMO_ENTRIES, _sourcePathMemos, _buildCache, _workspaceGraphCache, REVIEW_GRAPH_IDLE_EVICT_MS_DEFAULT, _workspaceCacheEpoch, _workspaceCacheEpochs, _graphGenerationCounter, _buildIdCounter, SEQ_FASTPATH_MAX_CHANGES, SEQ_FASTPATH_REVERIFY_MS, SEQ_FASTPATH_REVERIFY_EVERY, _lastGraphBuildInfo, _graphBuildInfoByGraph, _anyGraphStamped, _reviewGraphBuildGateForTests, REVIEW_GRAPH_NODE_RESIDENT_BYTES, REVIEW_GRAPH_EDGE_RESIDENT_BYTES, GRAPH_MAX_IN_MEMORY_BYTES_DEFAULT, _retainedGraphSites, _cwdWorktreeCheckedCwds, STAT_YIELD_EVERY, yieldToLoop, DEFAULT_REVIEW_GRAPH_SIZE_SKIP_TTL_MS, _sizeSkipTtl, _resetReviewGraphSizeSkipTtlForTests, _buildAttempts, _sizeSkipVerdicts, REVIEW_GRAPH_SIZE_NEAR_MISS_RATIO, _reviewGraphEntryBudgetForTests, _reviewGraphEntryCounterForTests, GRAPH_CACHE_FILENAME, LEGACY_GRAPH_CACHE_FILENAME, GRAPH_CHECKPOINT_FILENAME, GRAPH_PERSIST_DEBOUNCE_MS_DEFAULT, GRAPH_PERSIST_MAX_ELEMENTS_DEFAULT, _pendingPersist, _persistTimers, _persistGenerations, _workerRequests, _persistWorker, _persistWorkerRequestId, _persistAttemptId, _workerDisabled, _persistWorkerUnavailableReason, _lastWorkerFallbackReasonForTests, _checkpointGenerations, _checkpointWorkerRequests, _checkpointOffloadCountForTests, _persistExitHookInstalled, _sweptStageDirs, REVIEW_GRAPH_ARTIFACT_PREFIX, STAGE_PID_PATTERN, GRAPH_CHECKPOINT_EVERY_FILES_DEFAULT, GRAPH_CHECKPOINT_MIN_INTERVAL_MS_DEFAULT, SYMBOL_KIND_SPECIFICITY, _rebuildCounters, _graphImportChanges;
78439
78706
  var init_builder = __esm({
78440
78707
  "dist/clients/review-graph/builder.js"() {
78441
78708
  "use strict";
@@ -78491,7 +78758,6 @@ var init_builder = __esm({
78491
78758
  "shell"
78492
78759
  ]);
78493
78760
  MAIN_KIND_EXTENSIONS = Array.from(MAIN_KINDS).flatMap((kind) => KIND_EXTENSIONS[kind] ?? []);
78494
- REVIEW_GRAPH_SOURCE_EXTENSIONS = MAIN_KIND_EXTENSIONS;
78495
78761
  CHANGED_SYMBOLS_PREFIX = "session.reviewGraph.changedSymbols:";
78496
78762
  extractorCache2 = /* @__PURE__ */ new Map();
78497
78763
  REVIEW_GRAPH_MAX_WARM_WORKSPACES = 8;
@@ -80850,27 +81116,11 @@ function createLineParser(config) {
80850
81116
  return diagnostics;
80851
81117
  };
80852
81118
  }
80853
- function createBiomeParser(_autofix = false) {
80854
- const biomeAutofix = getAutofixCapability("biome");
80855
- return createLineParser({
80856
- tool: "biome",
80857
- regex: /^(.+?):(\d+):(\d+)\s+(.+?)\s*\((.+?)\)/,
80858
- extractMessage: (m) => `${m[5]}: ${m[4]}`,
80859
- // category: message
80860
- extractRule: (m) => m[5],
80861
- generateId: (m) => `biome-${m[2]}-${m[5]}`,
80862
- getSeverity: (line) => line.includes("error") ? "error" : "warning",
80863
- fixable: true,
80864
- autoFixAvailable: biomeAutofix?.safePipelineAutofix ?? false,
80865
- fixKind: biomeAutofix?.fixKind === "none" ? void 0 : biomeAutofix?.fixKind
80866
- });
80867
- }
80868
- var ruffAutofix, parseRuffOutput, parseGoVetOutput, parseBiomeOutput;
81119
+ var ruffAutofix, parseRuffOutput, parseGoVetOutput;
80869
81120
  var init_diagnostic_parsers = __esm({
80870
81121
  "dist/clients/dispatch/runners/utils/diagnostic-parsers.js"() {
80871
81122
  "use strict";
80872
81123
  init_sanitize();
80873
- init_string_utils();
80874
81124
  init_tool_policy();
80875
81125
  ruffAutofix = getAutofixCapability("ruff");
80876
81126
  parseRuffOutput = createLineParser({
@@ -80892,7 +81142,6 @@ var init_diagnostic_parsers = __esm({
80892
81142
  generateId: (m) => `go-vet-${m[2]}`,
80893
81143
  defectClass: "correctness"
80894
81144
  });
80895
- parseBiomeOutput = createBiomeParser(false);
80896
81145
  }
80897
81146
  });
80898
81147
 
@@ -83008,6 +83257,7 @@ var init_oxlint = __esm({
83008
83257
  return { status: "skipped", diagnostics: [], semantic: "none" };
83009
83258
  }
83010
83259
  const result = await safeSpawnAsync(cmd, args, {
83260
+ cwd,
83011
83261
  timeout: 3e4,
83012
83262
  maxOutputBytes: MAX_OXLINT_OUTPUT_BYTES
83013
83263
  });
@@ -83311,8 +83561,10 @@ ${result.stderr ?? ""}`, ctx.filePath);
83311
83561
  import * as fs101 from "node:fs/promises";
83312
83562
  import * as os29 from "node:os";
83313
83563
  import * as path139 from "node:path";
83314
- function spawnPs(cmd, args, timeoutMs = PS_TIMEOUT_MS) {
83564
+ function spawnPs(cmd, args, options = {}) {
83565
+ const { timeoutMs = PS_TIMEOUT_MS, cwd } = options;
83315
83566
  return safeSpawnAsync(cmd, args, {
83567
+ cwd,
83316
83568
  timeout: timeoutMs,
83317
83569
  resourceLabel: "psscriptanalyzer"
83318
83570
  });
@@ -83526,7 +83778,7 @@ $results | ConvertTo-Json -Depth 3 -Compress
83526
83778
  tmpScript,
83527
83779
  "-FilePath",
83528
83780
  absPath
83529
- ]);
83781
+ ], { timeoutMs: PS_TIMEOUT_MS, cwd });
83530
83782
  const hostStallMs = sampler.stop();
83531
83783
  const elapsedMs = Date.now() - startedAt;
83532
83784
  if (result.status === null) {
@@ -83896,7 +84148,7 @@ var init_ruff = __esm({
83896
84148
  return { status: "skipped", diagnostics: [], semantic: "none" };
83897
84149
  }
83898
84150
  const configArgs = ruffConfigArgs(cwd);
83899
- const checkResult = await safeSpawnAsync(cmd, ["check", "--output-format", "json", ...configArgs, ctx.filePath], { timeout: 3e4 });
84151
+ const checkResult = await safeSpawnAsync(cmd, ["check", "--output-format", "json", ...configArgs, ctx.filePath], { cwd, timeout: 3e4 });
83900
84152
  const raw = stripAnsi(checkResult.stdout + checkResult.stderr);
83901
84153
  const diagnostics = parseRuffJson(checkResult.stdout || "", ctx.filePath);
83902
84154
  const parsedDiagnostics = diagnostics.length > 0 ? diagnostics : parseRuffOutput(raw, ctx.filePath);
@@ -84532,7 +84784,7 @@ var init_shellcheck = __esm({
84532
84784
  args.push("--severity", "info");
84533
84785
  }
84534
84786
  args.push(ctx.filePath);
84535
- const result = await safeSpawnAsync(cmd, args, { timeout: 15e3 });
84787
+ const result = await safeSpawnAsync(cmd, args, { cwd, timeout: 15e3 });
84536
84788
  if (result.status === 0 && !result.stdout?.trim()) {
84537
84789
  return { status: "succeeded", diagnostics: [], semantic: "none" };
84538
84790
  }
@@ -84771,11 +85023,13 @@ var init_spellcheck = __esm({
84771
85023
  skipTestFiles: false,
84772
85024
  // Check docs in test files too
84773
85025
  async run(ctx) {
84774
- if (!await typos.isAvailableAsync(ctx.cwd || process.cwd())) {
85026
+ const cwd = ctx.cwd || process.cwd();
85027
+ if (!await typos.isAvailableAsync(cwd)) {
84775
85028
  return { status: "skipped", diagnostics: [], semantic: "none" };
84776
85029
  }
84777
85030
  const args = ["--format", "json", ctx.filePath];
84778
- const result = await safeSpawnAsync(typos.getCommand(ctx.cwd || process.cwd()), args, {
85031
+ const result = await safeSpawnAsync(typos.getCommand(cwd), args, {
85032
+ cwd,
84779
85033
  timeout: 15e3
84780
85034
  });
84781
85035
  const run = parseToolRun("spellcheck", { result, exitCodes: TYPOS_EXIT_CODES }, (out) => parseTyposOutput(out, ctx.filePath), { parseOutput: `${result.stdout ?? ""}${result.stderr ?? ""}` });
@@ -86510,6 +86764,7 @@ var init_yamllint = __esm({
86510
86764
  if (!cmd)
86511
86765
  return { status: "skipped", diagnostics: [], semantic: "none" };
86512
86766
  const result = await safeSpawnAsync(cmd, ["-f", "parsable", ctx.filePath], {
86767
+ cwd,
86513
86768
  timeout: 15e3
86514
86769
  });
86515
86770
  const raw = `${result.stdout ?? ""}${result.stderr ?? ""}`;
@@ -88240,7 +88495,7 @@ async function computeCascadeForFile(filePath, cwd, options = {}) {
88240
88495
  try {
88241
88496
  const snapshots = await lspService.getCapabilitySnapshots?.(neighborPath) ?? [];
88242
88497
  const tier = classifyCascadeWaitTier2(lspService, neighborPath, snapshots);
88243
- if (tier === "tier3-silent" || tier === "collect-later") {
88498
+ if (tier === "tier3-silent" || tier === "collect-later" || tier === "diagnostics-unsupported") {
88244
88499
  const spawnedForTouch = await lspService.getClientForFile(neighborPath);
88245
88500
  if (spawnedForTouch) {
88246
88501
  const touchedAt = Date.now();
@@ -88251,6 +88506,21 @@ async function computeCascadeForFile(filePath, cwd, options = {}) {
88251
88506
  source: "cascade",
88252
88507
  clientScope: "primary"
88253
88508
  });
88509
+ if (tier === "diagnostics-unsupported") {
88510
+ logCascade({
88511
+ phase: "cascade_skip",
88512
+ filePath,
88513
+ neighborFile: neighborPath,
88514
+ durationMs: Date.now() - neighborStart,
88515
+ lspServerCount: configuredServerCount,
88516
+ coldSnapshot: isColdSnapshot,
88517
+ metadata: {
88518
+ serverId: spawnedForTouch.client.serverId,
88519
+ waitTier: tier
88520
+ }
88521
+ });
88522
+ return void 0;
88523
+ }
88254
88524
  recordOutstandingCascadeTouch({
88255
88525
  filePath: neighborPath,
88256
88526
  serverId: spawnedForTouch.client.serverId,
@@ -89102,6 +89372,7 @@ __export(formatters_exports, {
89102
89372
  phpCsFixerFormatter: () => phpCsFixerFormatter,
89103
89373
  prettierFormatter: () => prettierFormatter,
89104
89374
  psscriptanalyzerFormatFormatter: () => psscriptanalyzerFormatFormatter,
89375
+ resolveFormatterCwd: () => resolveFormatterCwd,
89105
89376
  rubocopFormatter: () => rubocopFormatter,
89106
89377
  ruffFormatter: () => ruffFormatter,
89107
89378
  rustfmtFormatter: () => rustfmtFormatter,
@@ -89118,6 +89389,7 @@ __export(formatters_exports, {
89118
89389
  });
89119
89390
  import { AsyncLocalStorage as AsyncLocalStorage3 } from "node:async_hooks";
89120
89391
  import * as fs108 from "node:fs/promises";
89392
+ import * as os32 from "node:os";
89121
89393
  import * as path155 from "node:path";
89122
89394
  async function tryLazyInstallFormatterTool(tool, cwd) {
89123
89395
  const ok = await tryLazyInstallForFormatter(tool, cwd);
@@ -89395,6 +89667,21 @@ async function indentationArgs(filePath, tool, cwd) {
89395
89667
  String(indentation.width)
89396
89668
  ];
89397
89669
  }
89670
+ function resolveFormatterCwd(absolutePath, formatterName, homeDir = os32.homedir()) {
89671
+ const fileDir = path155.dirname(path155.resolve(absolutePath));
89672
+ const effectiveHome = homeDir || void 0;
89673
+ const markers = formatterName ? [...FORMATTER_MARKERS_BY_NAME.get(formatterName) ?? [], ".gitignore"] : [".gitignore"];
89674
+ const root = findNearestMarkerRoot(fileDir, markers, {
89675
+ homeDir: effectiveHome
89676
+ });
89677
+ if (root)
89678
+ return root;
89679
+ const gitRoot = findNearestMarkerRoot(fileDir, [".git"], {
89680
+ homeDir: effectiveHome,
89681
+ markerPredicate: isRealGitMarker
89682
+ });
89683
+ return gitRoot ?? fileDir;
89684
+ }
89398
89685
  function psScriptAnalyzerCommand(filePath, settingsPath) {
89399
89686
  const quoted = filePath.replace(/'/g, "''");
89400
89687
  const settingsArg = settingsPath ? ` -Settings '${settingsPath.replace(/'/g, "''")}'` : "";
@@ -89577,7 +89864,8 @@ function _getFormatterResetStateForTests() {
89577
89864
  whichLatchByCommand,
89578
89865
  whichTransientCommands,
89579
89866
  cooldownRecordedForRetryAtMs,
89580
- detectionCache
89867
+ detectionCache,
89868
+ formatterSignatureFlights
89581
89869
  };
89582
89870
  }
89583
89871
  function clearFormatterRuntimeState() {
@@ -89613,6 +89901,7 @@ async function formatFile(filePath, formatter) {
89613
89901
  try {
89614
89902
  const absolutePath = path155.resolve(filePath);
89615
89903
  const cwd = path155.dirname(absolutePath);
89904
+ const formatterCwd = resolveFormatterCwd(absolutePath, formatter.name);
89616
89905
  const contentBefore = await fs108.readFile(absolutePath, "utf-8");
89617
89906
  const cmd = await resolveFormatterCommand(formatter, absolutePath, cwd);
89618
89907
  if (cmd === SKIP_FORMATTING) {
@@ -89628,7 +89917,7 @@ async function formatFile(filePath, formatter) {
89628
89917
  }
89629
89918
  const result = await safeSpawnAsync(cmd[0], cmd.slice(1), {
89630
89919
  timeout: 15e3,
89631
- cwd
89920
+ cwd: formatterCwd
89632
89921
  });
89633
89922
  if (result.spawnFailure?.kind === "tool-not-found") {
89634
89923
  return {
@@ -89669,7 +89958,7 @@ async function formatFile(filePath, formatter) {
89669
89958
  function listAllFormatters() {
89670
89959
  return ALL_FORMATTERS.map((f) => f.name);
89671
89960
  }
89672
- var SKIP_FORMATTING, FORMATTER_UNAVAILABLE, WHICH_BUDGET_MS, whichLatchByCommand, whichTransientCommands, probedCommandsStorage, cooldownRecordedForRetryAtMs, EXPLICIT_FORMATTER_CONFIG_CHECKS, FORMATTERS_WITH_EXPLICIT_CONFIG_CHECK, BIOME_UNMATCHED_FLAG, biomeFormatter, prettierFormatter, OXFMT_NO_ERROR_ON_UNMATCHED, oxfmtFormatter, ruffFormatter, blackFormatter, sqlfluffFormatter, gofmtFormatter, rustfmtFormatter, zigFormatter, dartFormatter, shfmtFormatter, nixfmtFormatter, mixFormatter, ocamlformatFormatter, clangFormatFormatter, ktlintFormatter, ktfmtFormatter, rubocopFormatter, standardrbFormatter, gleamFormatter, terraformFormatter, terragruntHclFormatter, phpCsFixerFormatter, csharpierFormatter, fantomasFormatter, swiftformatFormatter, styluaFormatter, ormoluFormatter, taploFormatter, googleJavaFormatFormatter, cljfmtFormatter, cmakeFormatFormatter, cueFormatter, psscriptanalyzerFormatFormatter, ALL_FORMATTERS, FILENAME_FORMATTER_BASENAMES, detectionCache, formatterSignatureFlights, formatterCacheGeneration, FORMATTER_CONFIG_FILES, formatterConfigBasenames, BOX_DRAWING_GLOBAL, HAS_BOX_DRAWING;
89961
+ var SKIP_FORMATTING, FORMATTER_UNAVAILABLE, WHICH_BUDGET_MS, whichLatchByCommand, whichTransientCommands, probedCommandsStorage, cooldownRecordedForRetryAtMs, EXPLICIT_FORMATTER_CONFIG_CHECKS, FORMATTERS_WITH_EXPLICIT_CONFIG_CHECK, FORMATTER_MARKERS_BY_NAME, BIOME_UNMATCHED_FLAG, biomeFormatter, prettierFormatter, OXFMT_NO_ERROR_ON_UNMATCHED, oxfmtFormatter, ruffFormatter, blackFormatter, sqlfluffFormatter, gofmtFormatter, rustfmtFormatter, zigFormatter, dartFormatter, shfmtFormatter, nixfmtFormatter, mixFormatter, ocamlformatFormatter, clangFormatFormatter, ktlintFormatter, ktfmtFormatter, rubocopFormatter, standardrbFormatter, gleamFormatter, terraformFormatter, terragruntHclFormatter, phpCsFixerFormatter, csharpierFormatter, fantomasFormatter, swiftformatFormatter, styluaFormatter, ormoluFormatter, taploFormatter, googleJavaFormatFormatter, cljfmtFormatter, cmakeFormatFormatter, cueFormatter, psscriptanalyzerFormatFormatter, ALL_FORMATTERS, FILENAME_FORMATTER_BASENAMES, detectionCache, formatterSignatureFlights, formatterCacheGeneration, FORMATTER_CONFIG_FILES, formatterConfigBasenames, BOX_DRAWING_GLOBAL, HAS_BOX_DRAWING;
89673
89962
  var init_formatters = __esm({
89674
89963
  "dist/clients/formatters.js"() {
89675
89964
  "use strict";
@@ -89744,6 +90033,88 @@ var init_formatters = __esm({
89744
90033
  ["mix", (cwd) => hasMixFormatConfig(cwd)]
89745
90034
  ]);
89746
90035
  FORMATTERS_WITH_EXPLICIT_CONFIG_CHECK = new Set(EXPLICIT_FORMATTER_CONFIG_CHECKS.keys());
90036
+ FORMATTER_MARKERS_BY_NAME = /* @__PURE__ */ new Map([
90037
+ ["biome", ["biome.json", "biome.jsonc", "package.json"]],
90038
+ [
90039
+ "prettier",
90040
+ [
90041
+ ".prettierrc",
90042
+ ".prettierrc.json",
90043
+ ".prettierrc.yaml",
90044
+ ".prettierrc.yml",
90045
+ ".prettierrc.js",
90046
+ ".prettierrc.cjs",
90047
+ ".prettierrc.mjs",
90048
+ "prettier.config.js",
90049
+ "prettier.config.cjs",
90050
+ "prettier.config.mjs",
90051
+ ".prettierignore",
90052
+ "package.json"
90053
+ ]
90054
+ ],
90055
+ [
90056
+ "oxfmt",
90057
+ ["oxfmt.toml", ".oxfmtrc.json", "vite-plus.json", "package.json"]
90058
+ ],
90059
+ ["ruff", ["pyproject.toml", "ruff.toml", ".ruff.toml"]],
90060
+ ["black", ["pyproject.toml", "black.toml", ".black"]],
90061
+ ["sqlfluff", [".sqlfluff", "pyproject.toml", "setup.cfg"]],
90062
+ ["rustfmt", ["rustfmt.toml", ".rustfmt.toml", "Cargo.toml"]],
90063
+ ["rubocop", [".rubocop.yml", ".rubocop.yaml"]],
90064
+ ["standardrb", [".standard.yml", ".standard.yaml"]],
90065
+ ["clang-format", [".clang-format", "_clang-format"]],
90066
+ ["php-cs-fixer", [".php-cs-fixer.php", ".php-cs-fixer.dist.php"]],
90067
+ ["stylua", ["stylua.toml", ".stylua.toml"]],
90068
+ ["ocamlformat", [".ocamlformat"]],
90069
+ ["google-java-format", [".google-java-format", ".editorconfig"]],
90070
+ ["cljfmt", [".cljfmt.edn", "cljfmt.edn", ".cljfmt"]],
90071
+ [
90072
+ "cmake-format",
90073
+ [
90074
+ ".cmake-format",
90075
+ ".cmake-format.yaml",
90076
+ ".cmake-format.yml",
90077
+ ".cmake-format.json",
90078
+ ".cmake-format.py",
90079
+ "cmake-format.yaml",
90080
+ "cmake-format.yml",
90081
+ ".editorconfig"
90082
+ ]
90083
+ ],
90084
+ [
90085
+ "psscriptanalyzer-format",
90086
+ ["PSScriptAnalyzerSettings.psd1", "ScriptAnalyzerSettings.psd1"]
90087
+ ],
90088
+ [
90089
+ "csharpier",
90090
+ [
90091
+ ".csharpierrc",
90092
+ ".csharpierrc.json",
90093
+ ".csharpierrc.yaml",
90094
+ ".csharpierrc.yml"
90095
+ ]
90096
+ ],
90097
+ ["ormolu", [".ormolu"]],
90098
+ ["taplo", ["taplo.toml", ".taplo.toml"]],
90099
+ ["terraform", [".terraform.lock.hcl"]],
90100
+ ["swiftformat", [".swiftformat"]],
90101
+ ["fantomas", [".fantomasignore", ".editorconfig"]],
90102
+ ["mix", [".formatter.exs"]],
90103
+ ["shfmt", [".editorconfig"]],
90104
+ ["ktlint", [".editorconfig"]],
90105
+ [
90106
+ "ktfmt",
90107
+ [
90108
+ ".editorconfig",
90109
+ ".ktfmt",
90110
+ ".ktfmt.kts",
90111
+ "build.gradle",
90112
+ "build.gradle.kts",
90113
+ "settings.gradle",
90114
+ "settings.gradle.kts"
90115
+ ]
90116
+ ]
90117
+ ]);
89747
90118
  BIOME_UNMATCHED_FLAG = "--no-errors-on-unmatched";
89748
90119
  biomeFormatter = {
89749
90120
  name: "biome",
@@ -90422,7 +90793,7 @@ var init_formatters = __esm({
90422
90793
  ];
90423
90794
  FILENAME_FORMATTER_BASENAMES = new Set(ALL_FORMATTERS.flatMap((f) => f.filenames ?? []));
90424
90795
  detectionCache = new BoundedLruCache(32);
90425
- formatterSignatureFlights = /* @__PURE__ */ new Map();
90796
+ formatterSignatureFlights = new BoundedLruCache(32);
90426
90797
  formatterCacheGeneration = createGenerationSource("formatter-cache");
90427
90798
  FORMATTER_CONFIG_FILES = [
90428
90799
  "package.json",
@@ -90512,8 +90883,6 @@ var init_formatters = __esm({
90512
90883
  var startup_scan_exports = {};
90513
90884
  __export(startup_scan_exports, {
90514
90885
  MAX_STARTUP_SCAN_ENTRIES: () => MAX_STARTUP_SCAN_ENTRIES,
90515
- MAX_STARTUP_SOURCE_FILES: () => MAX_STARTUP_SOURCE_FILES,
90516
- PROJECT_ROOT_MARKERS: () => PROJECT_ROOT_MARKERS,
90517
90886
  __testing: () => __testing,
90518
90887
  _resetStartupScanMaxEntriesForTests: () => _resetStartupScanMaxEntriesForTests,
90519
90888
  _resetStartupScanVerdictTtlForTests: () => _resetStartupScanVerdictTtlForTests,
@@ -90526,7 +90895,7 @@ __export(startup_scan_exports, {
90526
90895
  resolveStartupScanContext: () => resolveStartupScanContext,
90527
90896
  resolveStartupScanContextAsync: () => resolveStartupScanContextAsync
90528
90897
  });
90529
- import * as os32 from "node:os";
90898
+ import * as os33 from "node:os";
90530
90899
  import * as path169 from "node:path";
90531
90900
  function isStartupScanVerdictFresh(verdict, now = Date.now()) {
90532
90901
  if (verdict.reason !== "too-many-source-files" && verdict.reason !== "too-many-entries")
@@ -90610,7 +90979,7 @@ function resolveStartupScanContext(cwd, options = {}) {
90610
90979
  }
90611
90980
  function computeStartupScanContext(cwd, options = {}) {
90612
90981
  const resolvedCwd = path169.resolve(cwd);
90613
- const homeDir = path169.resolve(options.homeDir ?? os32.homedir());
90982
+ const homeDir = path169.resolve(options.homeDir ?? os33.homedir());
90614
90983
  const maxSourceFiles = options.maxSourceFiles ?? getStartupScanMaxSourceFilesDerived(resolvedCwd);
90615
90984
  const maxScanEntries = options.maxScanEntries ?? getStartupScanMaxEntries();
90616
90985
  const projectRoot = findNearestProjectRoot(resolvedCwd);
@@ -90683,7 +91052,7 @@ async function resolveStartupScanContextAsync(cwd, options = {}) {
90683
91052
  if (cached)
90684
91053
  return cached;
90685
91054
  const resolvedCwd = path169.resolve(cwd);
90686
- const homeDir = path169.resolve(options.homeDir ?? os32.homedir());
91055
+ const homeDir = path169.resolve(options.homeDir ?? os33.homedir());
90687
91056
  const maxSourceFiles = options.maxSourceFiles ?? getStartupScanMaxSourceFilesDerived(resolvedCwd);
90688
91057
  const maxScanEntries = options.maxScanEntries ?? getStartupScanMaxEntries();
90689
91058
  const projectRoot = findNearestProjectRoot(resolvedCwd);
@@ -90729,7 +91098,7 @@ async function resolveStartupScanContextAsync(cwd, options = {}) {
90729
91098
  startupScanContextCache.set(cacheKey, result);
90730
91099
  return result;
90731
91100
  }
90732
- var PROJECT_ROOT_MARKERS, MAX_STARTUP_SOURCE_FILES, MAX_STARTUP_SCAN_ENTRIES, SOURCE_FILE_PATTERN, DEFAULT_STARTUP_SCAN_VERDICT_TTL_MS, _ttl, getStartupScanVerdictTtlMs, _resetStartupScanVerdictTtlForTests, _maxEntries, getStartupScanMaxEntries, _resetStartupScanMaxEntriesForTests, startupScanContextCache, __testing;
91101
+ var PROJECT_ROOT_MARKERS, MAX_STARTUP_SCAN_ENTRIES, SOURCE_FILE_PATTERN, DEFAULT_STARTUP_SCAN_VERDICT_TTL_MS, _ttl, getStartupScanVerdictTtlMs, _resetStartupScanVerdictTtlForTests, _maxEntries, getStartupScanMaxEntries, _resetStartupScanMaxEntriesForTests, startupScanContextCache, __testing;
90733
91102
  var init_startup_scan = __esm({
90734
91103
  "dist/clients/startup-scan.js"() {
90735
91104
  "use strict";
@@ -90749,7 +91118,6 @@ var init_startup_scan = __esm({
90749
91118
  "go.mod",
90750
91119
  "composer.json"
90751
91120
  ];
90752
- MAX_STARTUP_SOURCE_FILES = 2e3;
90753
91121
  MAX_STARTUP_SCAN_ENTRIES = 5e4;
90754
91122
  SOURCE_FILE_PATTERN = /\.(ts|tsx|js|jsx|py|go|rs|rb)$/;
90755
91123
  DEFAULT_STARTUP_SCAN_VERDICT_TTL_MS = 24 * 60 * 60 * 1e3;
@@ -91553,13 +91921,7 @@ __export(metrics_history_exports, {
91553
91921
  captureSnapshot: () => captureSnapshot,
91554
91922
  captureSnapshots: () => captureSnapshots,
91555
91923
  computeTDI: () => computeTDI,
91556
- computeTrend: () => computeTrend,
91557
- formatTrendCell: () => formatTrendCell,
91558
- getDelta: () => getDelta,
91559
- getTrendEmoji: () => getTrendEmoji,
91560
- getTrendSummary: () => getTrendSummary,
91561
- loadHistory: () => loadHistory,
91562
- saveHistory: () => saveHistory
91924
+ loadHistory: () => loadHistory
91563
91925
  });
91564
91926
  import { execSync } from "node:child_process";
91565
91927
  import * as fs119 from "node:fs";
@@ -91674,6 +92036,24 @@ function captureSnapshot(filePath, metrics) {
91674
92036
  }, SAVE_DEBOUNCE_MS);
91675
92037
  saveTimer.unref?.();
91676
92038
  }
92039
+ function computeTrend(history) {
92040
+ if (history.length < 2)
92041
+ return "stable";
92042
+ const recent = history.slice(-3);
92043
+ const first = recent[0];
92044
+ const last = recent[recent.length - 1];
92045
+ const miDelta = last.mi - first.mi;
92046
+ const cogDelta = last.cognitive - first.cognitive;
92047
+ if (miDelta > 2)
92048
+ return "improving";
92049
+ if (miDelta < -2)
92050
+ return "regressing";
92051
+ if (cogDelta < -10)
92052
+ return "improving";
92053
+ if (cogDelta > 10)
92054
+ return "regressing";
92055
+ return "stable";
92056
+ }
91677
92057
  function captureSnapshots(files2) {
91678
92058
  const history = loadHistory();
91679
92059
  for (const file of files2) {
@@ -91708,89 +92088,6 @@ function captureSnapshots(files2) {
91708
92088
  saveHistory(history);
91709
92089
  return history;
91710
92090
  }
91711
- function computeTrend(history) {
91712
- if (history.length < 2)
91713
- return "stable";
91714
- const recent = history.slice(-3);
91715
- const first = recent[0];
91716
- const last = recent[recent.length - 1];
91717
- const miDelta = last.mi - first.mi;
91718
- const cogDelta = last.cognitive - first.cognitive;
91719
- if (miDelta > 2)
91720
- return "improving";
91721
- if (miDelta < -2)
91722
- return "regressing";
91723
- if (cogDelta < -10)
91724
- return "improving";
91725
- if (cogDelta > 10)
91726
- return "regressing";
91727
- return "stable";
91728
- }
91729
- function getDelta(history) {
91730
- if (!history || history.history.length < 2)
91731
- return null;
91732
- const current = history.history[history.history.length - 1];
91733
- const previous = history.history[history.history.length - 2];
91734
- return {
91735
- mi: Math.round((current.mi - previous.mi) * 10) / 10,
91736
- cognitive: current.cognitive - previous.cognitive,
91737
- trend: history.trend
91738
- };
91739
- }
91740
- function getTrendEmoji(trend) {
91741
- switch (trend) {
91742
- case "improving":
91743
- return "\u{1F4C8}";
91744
- case "regressing":
91745
- return "\u{1F4C9}";
91746
- default:
91747
- return "\u27A1\uFE0F";
91748
- }
91749
- }
91750
- function getTrendSummary(history) {
91751
- let improving = 0;
91752
- let regressing = 0;
91753
- let stable = 0;
91754
- const regressions = [];
91755
- for (const [file, fileHistory] of Object.entries(history.files)) {
91756
- switch (fileHistory.trend) {
91757
- case "improving":
91758
- improving++;
91759
- break;
91760
- case "regressing": {
91761
- regressing++;
91762
- const delta = getDelta(fileHistory);
91763
- if (delta) {
91764
- regressions.push({ file, miDelta: delta.mi });
91765
- }
91766
- break;
91767
- }
91768
- default:
91769
- stable++;
91770
- }
91771
- }
91772
- regressions.sort((a, b) => a.miDelta - b.miDelta);
91773
- return {
91774
- improving,
91775
- regressing,
91776
- stable,
91777
- worstRegressions: regressions.slice(0, 5)
91778
- };
91779
- }
91780
- function formatTrendCell(filePath, history) {
91781
- const relativePath = path175.relative(process.cwd(), filePath);
91782
- const fileHistory = history.files[relativePath];
91783
- if (!fileHistory || fileHistory.history.length < 2) {
91784
- return "\u2014";
91785
- }
91786
- const delta = getDelta(fileHistory);
91787
- if (!delta)
91788
- return "\u2014";
91789
- const emoji = getTrendEmoji(delta.trend);
91790
- const miSign = delta.mi > 0 ? "+" : "";
91791
- const miColor = delta.mi > 0 ? "\u{1F7E2}" : delta.mi < 0 ? "\u{1F534}" : "\u26AA";
91792
- return `${emoji} ${miColor}${miSign}${delta.mi}`;
91793
- }
91794
92091
  function computeTDI(history) {
91795
92092
  const files2 = Object.values(history.files);
91796
92093
  if (files2.length === 0) {
@@ -91967,7 +92264,6 @@ var init_call_graph_impact = __esm({
91967
92264
  // dist/clients/lens-map.js
91968
92265
  var lens_map_exports = {};
91969
92266
  __export(lens_map_exports, {
91970
- DEFAULT_MAX_MAP_NODES: () => DEFAULT_MAX_MAP_NODES,
91971
92267
  aggregateGraphToFiles: () => aggregateGraphToFiles,
91972
92268
  computeLayout: () => computeLayout,
91973
92269
  generateLensMap: () => generateLensMap,
@@ -92964,9 +93260,7 @@ var init_lens_map = __esm({
92964
93260
  // dist/clients/performance-report.js
92965
93261
  var performance_report_exports = {};
92966
93262
  __export(performance_report_exports, {
92967
- DEFAULT_PERF_TOP_N: () => DEFAULT_PERF_TOP_N,
92968
93263
  MAX_PERF_PHASE_SAMPLES: () => MAX_PERF_PHASE_SAMPLES,
92969
- MAX_PERF_TOP_N: () => MAX_PERF_TOP_N,
92970
93264
  collectLatencyPerformance: () => collectLatencyPerformance,
92971
93265
  renderLatencyPerformanceReport: () => renderLatencyPerformanceReport,
92972
93266
  resolveLogByteBudget: () => resolveLogByteBudget,
@@ -96807,8 +97101,118 @@ function stripQuotes(token) {
96807
97101
  }
96808
97102
  return token;
96809
97103
  }
97104
+ function parseHeredocDelimiter(command, start) {
97105
+ let i = start;
97106
+ const stripTabs = command[i] === "-";
97107
+ if (stripTabs)
97108
+ i += 1;
97109
+ while (/\s/.test(command[i] ?? ""))
97110
+ i += 1;
97111
+ let delimiter2 = "";
97112
+ let quoted = false;
97113
+ while (i < command.length && !/[\s;&|<>]/.test(command[i] ?? "")) {
97114
+ const ch = command[i];
97115
+ if (ch === "'" || ch === '"') {
97116
+ quoted = true;
97117
+ const quote = ch;
97118
+ i += 1;
97119
+ while (i < command.length && command[i] !== quote) {
97120
+ delimiter2 += command[i++];
97121
+ }
97122
+ if (command[i] !== quote)
97123
+ return void 0;
97124
+ i += 1;
97125
+ continue;
97126
+ }
97127
+ if (ch === "\\") {
97128
+ quoted = true;
97129
+ i += 1;
97130
+ if (i >= command.length)
97131
+ return void 0;
97132
+ delimiter2 += command[i++];
97133
+ continue;
97134
+ }
97135
+ delimiter2 += ch;
97136
+ i += 1;
97137
+ }
97138
+ if (!delimiter2)
97139
+ return void 0;
97140
+ return { heredoc: { delimiter: delimiter2, stripTabs, quoted }, end: i - 1 };
97141
+ }
97142
+ function heredocSubstitutionBodies(body) {
97143
+ const substitutions = [];
97144
+ for (let i = 0; i < body.length; i += 1) {
97145
+ if (body.startsWith("$(", i)) {
97146
+ let depth = 1;
97147
+ let quote;
97148
+ for (let end = i + 2; end < body.length; end += 1) {
97149
+ const ch = body[end];
97150
+ if (quote === "single") {
97151
+ if (ch === "'")
97152
+ quote = void 0;
97153
+ continue;
97154
+ }
97155
+ if (quote === "double") {
97156
+ if (ch === '"' && body[end - 1] !== "\\")
97157
+ quote = void 0;
97158
+ continue;
97159
+ }
97160
+ if (ch === "'" || ch === '"') {
97161
+ quote = ch === "'" ? "single" : "double";
97162
+ } else if (body.startsWith("$(", end)) {
97163
+ depth += 1;
97164
+ end += 1;
97165
+ } else if (ch === ")") {
97166
+ depth -= 1;
97167
+ if (depth === 0) {
97168
+ substitutions.push(body.slice(i + 2, end));
97169
+ i = end;
97170
+ break;
97171
+ }
97172
+ }
97173
+ }
97174
+ } else if (body[i] === "`" && body[i - 1] !== "\\") {
97175
+ const end = body.indexOf("`", i + 1);
97176
+ if (end >= 0) {
97177
+ substitutions.push(body.slice(i + 1, end));
97178
+ i = end;
97179
+ }
97180
+ }
97181
+ }
97182
+ return substitutions;
97183
+ }
97184
+ function consumeHeredocBodies(command, start, heredocs) {
97185
+ let cursor = start;
97186
+ const substitutions = [];
97187
+ for (const heredoc of heredocs) {
97188
+ const bodyStart = cursor;
97189
+ let found = false;
97190
+ while (cursor < command.length) {
97191
+ const lineEnd = command.indexOf("\n", cursor);
97192
+ const end = lineEnd < 0 ? command.length : lineEnd;
97193
+ let line = command.slice(cursor, end);
97194
+ if (heredoc.stripTabs)
97195
+ line = line.replace(/^\t+/, "");
97196
+ if (line.replace(/\r$/, "") === heredoc.delimiter) {
97197
+ if (!heredoc.quoted)
97198
+ substitutions.push(...heredocSubstitutionBodies(command.slice(bodyStart, cursor)));
97199
+ cursor = lineEnd < 0 ? end : end + 1;
97200
+ found = true;
97201
+ break;
97202
+ }
97203
+ cursor = lineEnd < 0 ? end : end + 1;
97204
+ }
97205
+ if (!found) {
97206
+ if (!heredoc.quoted)
97207
+ substitutions.push(...heredocSubstitutionBodies(command.slice(bodyStart)));
97208
+ return { end: command.length - 1, substitutions };
97209
+ }
97210
+ }
97211
+ return { end: cursor - 1, substitutions };
97212
+ }
96810
97213
  function tokenizeShellCommand(command) {
96811
97214
  const segments = [];
97215
+ const pendingHeredocs = [];
96812
97216
  let tokens = [];
96813
97217
  let word = "";
96814
97218
  let quote;
@@ -96883,12 +97287,21 @@ function tokenizeShellCommand(command) {
96883
97287
  i++;
96884
97288
  continue;
96885
97289
  }
96886
- if (/\s/.test(ch)) {
97290
+ if (/\s/.test(ch) && ch !== "\n") {
96887
97291
  flushWord();
96888
97292
  continue;
96889
97293
  }
96890
97294
  if (ch === ";" || ch === "\n" || ch === "|" || ch === "&") {
96891
97295
  flushWord();
97296
+ if (ch === "\n" && pendingHeredocs.length > 0) {
97297
+ const heredocs = pendingHeredocs.splice(0);
97298
+ flushSegment();
97299
+ const consumed = consumeHeredocBodies(command, i + 1, heredocs);
97300
+ for (const substitution of consumed.substitutions)
97301
+ segments.push(...tokenizeShellCommand(substitution));
97302
+ i = consumed.end;
97303
+ continue;
97304
+ }
96892
97305
  let terminator;
96893
97306
  if (ch === "|" && next === "|")
96894
97307
  i++;
@@ -96904,6 +97317,13 @@ function tokenizeShellCommand(command) {
96904
97317
  }
96905
97318
  if (ch === "<" || ch === ">") {
96906
97319
  flushWord();
97320
+ if (ch === "<" && next === "<" && command[i - 1] !== "<" && command[i + 2] !== "<") {
97321
+ const parsed = parseHeredocDelimiter(command, i + 2);
97322
+ if (parsed) {
97323
+ pendingHeredocs.push(parsed.heredoc);
97324
+ i = parsed.end;
97325
+ }
97326
+ }
96907
97327
  unsupported = true;
96908
97328
  continue;
96909
97329
  }
@@ -97905,6 +98325,11 @@ function canonicalizeGuardCommand(command) {
97905
98325
  let pendingSpace = false;
97906
98326
  quote = void 0;
97907
98327
  for (const ch of result) {
98328
+ if (!quote && ch === "\n") {
98329
+ collapsed += ch;
98330
+ pendingSpace = false;
98331
+ continue;
98332
+ }
97908
98333
  if (!quote && /\s/.test(ch)) {
97909
98334
  pendingSpace = collapsed.length > 0;
97910
98335
  continue;
@@ -98830,7 +99255,6 @@ function loadFormatters() {
98830
99255
  }
98831
99256
 
98832
99257
  // dist/clients/format-service.js
98833
- init_file_time();
98834
99258
  var DEFAULT_FORMATTER_CONCURRENCY = 1;
98835
99259
  var FormatService = class {
98836
99260
  fileTime;
@@ -103104,6 +103528,25 @@ ${findings.data.content}` : `${AUTOMATION_FRAMING}${historicalTestContent(findin
103104
103528
  function testFindingsCurrentPrefix(runnerErrorOnly) {
103105
103529
  return runnerErrorOnly ? "Test runner could not complete last turn (advisory \u2014 not a failure introduced this turn):" : "Test failures detected last turn \u2014 fix before continuing:";
103106
103530
  }
103531
+ function retireTestFindings(cacheManager2, cwd) {
103532
+ const prior = cacheManager2.readCache("test-runner-findings", cwd)?.data;
103533
+ cacheManager2.writeCache("test-runner-findings", {
103534
+ content: "",
103535
+ testRunGeneration: prior?.testRunGeneration,
103536
+ deferredTargets: prior?.deferredTargets,
103537
+ retiredTargets: prior?.retiredTargets
103538
+ }, cwd);
103539
+ }
103540
+ function consumeTestFindings(cacheManager2, cwd, runtime2) {
103541
+ const record6 = cacheManager2.readCache("test-runner-findings", cwd);
103542
+ if (!record6?.data?.content)
103543
+ return;
103544
+ const findings = peekTestFindings(cacheManager2, cwd, runtime2, true);
103545
+ if (!findings)
103546
+ return;
103547
+ retireTestFindings(cacheManager2, cwd);
103548
+ return findings;
103549
+ }
103107
103550
  function consumeSessionStartGuidance(cacheManager2, cwd) {
103108
103551
  const guidance = cacheManager2.readCache("session-start-guidance", cwd);
103109
103552
  if (!guidance?.data?.content)
@@ -103123,23 +103566,11 @@ ${guidance.data.content}`
103123
103566
 
103124
103567
  // dist/clients/test-runner-delivery.js
103125
103568
  init_bounded_telemetry();
103126
- init_tui_fit();
103127
- var TEST_RUNNER_ENTRY_TYPE = "pilens:test-runner-findings";
103128
103569
  var MAX_PENDING_DELIVERIES = 32;
103129
- var MAX_ENTRY_CONTENT = 12e3;
103130
103570
  var pending3 = /* @__PURE__ */ new Map();
103131
103571
  function key3(cwd, sessionId, ownerId) {
103132
103572
  return `${ownerId ?? "direct"}\0${cwd}\0${sessionId}`;
103133
103573
  }
103134
- function boundedContent(content) {
103135
- if (content.length <= MAX_ENTRY_CONTENT)
103136
- return { content, droppedDetailCount: 0 };
103137
- return {
103138
- content: `${content.slice(0, MAX_ENTRY_CONTENT)}
103139
- [\u2026details truncated]`,
103140
- droppedDetailCount: content.length - MAX_ENTRY_CONTENT
103141
- };
103142
- }
103143
103574
  function record5(identity, outcome, delivery, metadata = {}) {
103144
103575
  emitBounded("test_runner_delivery", identity, {
103145
103576
  filePath: delivery.cwd,
@@ -103156,7 +103587,7 @@ function record5(identity, outcome, delivery, metadata = {}) {
103156
103587
  }
103157
103588
  }, {
103158
103589
  capPerTurn: { limit: 8, turnIndex: delivery.generation },
103159
- ledgerKind: outcome === "delivery-failed" || outcome === "capability-unavailable" ? "test-runner-delivery" : void 0,
103590
+ ledgerKind: outcome === "delivery-failed" ? "test-runner-delivery" : void 0,
103160
103591
  reason: `test runner delivery ${outcome}`
103161
103592
  });
103162
103593
  }
@@ -103179,6 +103610,7 @@ function stageTestRunnerDelivery(args) {
103179
103610
  generation: args.generation,
103180
103611
  targetCount: args.targetCount,
103181
103612
  createdAt: Date.now(),
103613
+ eligible: false,
103182
103614
  owner: args.owner
103183
103615
  };
103184
103616
  record5(deliveryKey, "superseded", superseded, {
@@ -103204,6 +103636,7 @@ function stageTestRunnerDelivery(args) {
103204
103636
  generation: args.generation,
103205
103637
  targetCount: args.targetCount,
103206
103638
  createdAt: Date.now(),
103639
+ eligible: false,
103207
103640
  owner: args.owner
103208
103641
  };
103209
103642
  pending3.set(deliveryKey, delivery);
@@ -103219,16 +103652,11 @@ function deliverTestRunnerFindings(args) {
103219
103652
  const currentGeneration = args.cacheManager.readCache("test-runner-findings", cwd)?.data?.testRunGeneration;
103220
103653
  if (currentGeneration !== void 0 && currentGeneration > delivery.generation) {
103221
103654
  pending3.delete(deliveryKey);
103222
- record5(deliveryKey, "superseded", delivery, {
103223
- currentGeneration
103224
- });
103655
+ record5(deliveryKey, "superseded", delivery, { currentGeneration });
103225
103656
  return;
103226
103657
  }
103227
103658
  if (typeof args.ctx.isIdle !== "function") {
103228
103659
  pending3.delete(deliveryKey);
103229
- record5(deliveryKey, "capability-unavailable", delivery, {
103230
- reason: "host does not expose ctx.isIdle"
103231
- });
103232
103660
  return;
103233
103661
  }
103234
103662
  try {
@@ -103242,8 +103670,7 @@ function deliverTestRunnerFindings(args) {
103242
103670
  });
103243
103671
  return;
103244
103672
  }
103245
- const findings = peekTestFindings(args.cacheManager, cwd, args.runtime, true);
103246
- if (!findings) {
103673
+ if (!peekTestFindings(args.cacheManager, cwd, args.runtime, true)) {
103247
103674
  pending3.delete(deliveryKey);
103248
103675
  record5(deliveryKey, "superseded", delivery);
103249
103676
  return;
@@ -103259,30 +103686,23 @@ function deliverTestRunnerFindings(args) {
103259
103686
  });
103260
103687
  return;
103261
103688
  }
103262
- const appendEntry = args.pi.appendEntry;
103263
- if (typeof appendEntry !== "function") {
103689
+ delivery.eligible = true;
103690
+ const current = args.cacheManager.readCache("test-runner-findings", cwd)?.data;
103691
+ if (!current?.content || current.testRunGeneration !== delivery.generation) {
103264
103692
  pending3.delete(deliveryKey);
103265
- record5(deliveryKey, "capability-unavailable", delivery);
103266
103693
  return;
103267
103694
  }
103268
- const bounded2 = boundedContent(findings.messages[0]?.content ?? "");
103269
- try {
103270
- appendEntry.call(args.pi, TEST_RUNNER_ENTRY_TYPE, {
103271
- ...bounded2,
103695
+ args.cacheManager.writeCache("test-runner-findings", {
103696
+ ...current,
103697
+ deliveryEligible: {
103272
103698
  sessionId: delivery.sessionId,
103273
103699
  generation: delivery.generation,
103274
- targetCount: delivery.targetCount
103275
- });
103276
- pending3.delete(deliveryKey);
103277
- record5(deliveryKey, "delivered", delivery, {
103278
- droppedDetailCount: bounded2.droppedDetailCount,
103279
- ageMs: Math.max(0, Date.now() - delivery.createdAt)
103280
- });
103281
- } catch (error) {
103282
- record5(deliveryKey, "delivery-failed", delivery, {
103283
- error: String(error).slice(0, 500)
103284
- });
103285
- }
103700
+ eligibleAt: Date.now()
103701
+ }
103702
+ }, cwd);
103703
+ record5(deliveryKey, "eligible", delivery, {
103704
+ ageMs: Math.max(0, Date.now() - delivery.createdAt)
103705
+ });
103286
103706
  }
103287
103707
  function deliverStagedTestRunnerFindings(args) {
103288
103708
  if (!args?.cwd || !args.ownerId)
@@ -103291,7 +103711,6 @@ function deliverStagedTestRunnerFindings(args) {
103291
103711
  if (!delivery?.owner)
103292
103712
  return;
103293
103713
  deliverTestRunnerFindings({
103294
- pi: delivery.owner.pi,
103295
103714
  ctx: delivery.owner.getCtx(),
103296
103715
  cacheManager: delivery.owner.cacheManager,
103297
103716
  runtime: delivery.owner.runtime,
@@ -103299,30 +103718,62 @@ function deliverStagedTestRunnerFindings(args) {
103299
103718
  ownerId: delivery.ownerId
103300
103719
  });
103301
103720
  }
103302
- function registerTestRunnerEntryRenderer(pi) {
103303
- const register = pi.registerEntryRenderer;
103304
- if (typeof register !== "function")
103305
- return false;
103306
- try {
103307
- register.call(pi, TEST_RUNNER_ENTRY_TYPE, renderTestRunnerEntry);
103308
- return true;
103309
- } catch {
103310
- return false;
103721
+ function consumeStagedTestRunnerFindings(args) {
103722
+ const deliveryKey = key3(args.cwd, args.sessionId, args.ownerId);
103723
+ let delivery = pending3.get(deliveryKey);
103724
+ const persisted = args.cacheManager.readCache("test-runner-findings", args.cwd)?.data;
103725
+ const eligible = persisted?.deliveryEligible;
103726
+ if (!delivery && persisted?.content && eligible) {
103727
+ const sameSession = eligible.sessionId === args.sessionId;
103728
+ if (!sameSession) {
103729
+ const foreignDelivery = {
103730
+ cwd: args.cwd,
103731
+ sessionId: eligible.sessionId,
103732
+ generation: eligible.generation,
103733
+ targetCount: 0,
103734
+ createdAt: eligible.eligibleAt,
103735
+ eligible: true,
103736
+ rehydrated: true
103737
+ };
103738
+ record5(deliveryKey, "foreign-session", foreignDelivery, {
103739
+ currentSessionId: args.sessionId,
103740
+ currentOwnerId: args.ownerId,
103741
+ reason: "session-mismatch"
103742
+ });
103743
+ return void 0;
103744
+ }
103745
+ delivery = {
103746
+ cwd: args.cwd,
103747
+ sessionId: args.sessionId,
103748
+ ownerId: args.ownerId,
103749
+ generation: eligible.generation,
103750
+ targetCount: 0,
103751
+ createdAt: eligible.eligibleAt,
103752
+ eligible: true,
103753
+ rehydrated: true
103754
+ };
103755
+ pending3.set(deliveryKey, delivery);
103311
103756
  }
103312
- }
103313
- function renderTestRunnerEntry(entry, _options, theme) {
103314
- const content = entry.data?.content;
103315
- if (typeof content !== "string")
103757
+ if (!delivery?.eligible)
103316
103758
  return void 0;
103317
- const lines = [
103318
- theme.fg("error", "pi-lens test failures"),
103319
- ...content.split("\n")
103320
- ];
103321
- return {
103322
- render: (width) => fitLines(lines, width),
103323
- invalidate: () => {
103324
- }
103325
- };
103759
+ const currentGeneration = persisted?.testRunGeneration;
103760
+ if (currentGeneration !== delivery.generation) {
103761
+ pending3.delete(deliveryKey);
103762
+ record5(deliveryKey, "superseded", delivery, { currentGeneration });
103763
+ return void 0;
103764
+ }
103765
+ const findings = consumeTestFindings(args.cacheManager, args.cwd, args.runtime);
103766
+ if (!findings) {
103767
+ pending3.delete(deliveryKey);
103768
+ record5(deliveryKey, "superseded", delivery);
103769
+ return void 0;
103770
+ }
103771
+ pending3.delete(deliveryKey);
103772
+ record5(deliveryKey, "delivered", delivery, {
103773
+ ageMs: Math.max(0, Date.now() - delivery.createdAt),
103774
+ rehydrated: delivery.rehydrated === true
103775
+ });
103776
+ return findings;
103326
103777
  }
103327
103778
  function resetTestRunnerDelivery() {
103328
103779
  pending3.clear();
@@ -104893,11 +105344,14 @@ function logProjectSnapshotProbe(args) {
104893
105344
  args.dbg(`project_snapshot: miss reason=${args.missReason}`);
104894
105345
  }
104895
105346
  }
104896
- function resolveStartupMode() {
105347
+ function resolveStartupMode(projectConfig, globalConfig) {
104897
105348
  const envMode = (process.env.PI_LENS_STARTUP_MODE ?? "").trim().toLowerCase();
104898
105349
  if (envMode === "full" || envMode === "minimal" || envMode === "quick") {
104899
105350
  return envMode;
104900
105351
  }
105352
+ const configMode = projectConfig?.startup?.mode ?? globalConfig?.startup?.mode;
105353
+ if (configMode)
105354
+ return configMode;
104901
105355
  if (isPrintMode()) {
104902
105356
  return "quick";
104903
105357
  }
@@ -105265,6 +105719,15 @@ function scheduleStartupScans(deps, runtime2, sessionGeneration, analysisRoot, s
105265
105719
  }
105266
105720
  function scheduleStartupScansWithClients(deps, runtime2, sessionGeneration, analysisRoot, snapshotRoot, languageProfile, dbg2) {
105267
105721
  const { todoScanner, cacheManager: cacheManager2, knipClient, jscpdClient, deadCodeClients, govulncheckClient, gitleaksClient, trivyClient, opengrepClient, astGrepClient, depChecker } = deps;
105722
+ const analyzerEnabled = (flag) => !deps.getFlag(flag);
105723
+ if (!analyzerEnabled("no-complexity")) {
105724
+ dbg2("session_start complexity: skipped (disabled by config)");
105725
+ recordDegradationOnce({
105726
+ kind: "startup-analyzer-disabled",
105727
+ subject: "complexity",
105728
+ reason: "skipped (disabled by config)"
105729
+ });
105730
+ }
105268
105731
  const taskDeferMsByName = {
105269
105732
  "call-graph": 5e3,
105270
105733
  "codebase-model": 5200,
@@ -105305,7 +105768,7 @@ function scheduleStartupScansWithClients(deps, runtime2, sessionGeneration, anal
105305
105768
  scanNames.push("knip", "jscpd", "ast-grep exports", "project index");
105306
105769
  }
105307
105770
  dbg2(`session_start: launching background scans (${scanNames.join(", ")})`);
105308
- runTask("todo", async () => {
105771
+ void runTask("todo", async () => {
105309
105772
  if (!runtime2.isCurrentSession(sessionGeneration))
105310
105773
  return;
105311
105774
  const items = await collectTodoBaselineItems(todoScanner, analysisRoot, () => runtime2.isCurrentSession(sessionGeneration));
@@ -105321,9 +105784,19 @@ function scheduleStartupScansWithClients(deps, runtime2, sessionGeneration, anal
105321
105784
  const isSubagent = isSubagentSession();
105322
105785
  const skipHeavyweightScans = isSlowFs(analysisRoot) || isSubagent;
105323
105786
  const runHeavyweightTask = (name, task) => {
105787
+ const flag = `no-${name}`;
105788
+ if (name !== "opengrep" && name !== "trivy" && !analyzerEnabled(flag)) {
105789
+ dbg2(`session_start ${name}: skipped (disabled by config)`);
105790
+ recordDegradationOnce({
105791
+ kind: "startup-analyzer-disabled",
105792
+ subject: name,
105793
+ reason: "skipped (disabled by config)"
105794
+ });
105795
+ return;
105796
+ }
105324
105797
  if (skipHeavyweightScans)
105325
105798
  return;
105326
- runTask(name, task);
105799
+ void runTask(name, task);
105327
105800
  };
105328
105801
  if (skipHeavyweightScans) {
105329
105802
  dbg2(`session_start: skipping knip/jscpd/madge/dead-code/govulncheck/gitleaks/trivy/opengrep (${isSubagent ? "subagent" : "slow-fs"})`);
@@ -105597,7 +106070,7 @@ function scheduleStartupScansWithClients(deps, runtime2, sessionGeneration, anal
105597
106070
  });
105598
106071
  dbg2(`session_start call-graph: built ${callGraph.edges.length} edges, ${callGraph.callers.size} callee entries (${Date.now() - startMs}ms)`);
105599
106072
  });
105600
- runTask("codebase-model", async () => {
106073
+ void runTask("codebase-model", async () => {
105601
106074
  await callGraphTask;
105602
106075
  if (!runtime2.isCurrentSession(sessionGeneration))
105603
106076
  return;
@@ -105611,7 +106084,7 @@ function scheduleStartupScansWithClients(deps, runtime2, sessionGeneration, anal
105611
106084
  const top3 = model.entries.slice(0, 3).map((e) => e.name).join(", ");
105612
106085
  dbg2(`session_start codebase-model: ${model.entries.length} entries, ${model.totalTokens} tokens, top symbols: ${top3 || "(none)"}`);
105613
106086
  });
105614
- runTask("ast-grep-exports", async () => {
106087
+ void runTask("ast-grep-exports", async () => {
105615
106088
  if (await astGrepClient.ensureAvailable()) {
105616
106089
  if (!runtime2.isCurrentSession(sessionGeneration))
105617
106090
  return;
@@ -105625,7 +106098,7 @@ function scheduleStartupScansWithClients(deps, runtime2, sessionGeneration, anal
105625
106098
  saveRuntimeProjectSnapshot({ cwd: snapshotRoot, runtime: runtime2, dbg: dbg2 });
105626
106099
  }
105627
106100
  });
105628
- runTask("word-index", async () => {
106101
+ void runTask("word-index", async () => {
105629
106102
  await buildOrRefreshWordIndex({
105630
106103
  runtime: runtime2,
105631
106104
  sessionGeneration,
@@ -105699,10 +106172,10 @@ async function handleSessionStart(deps) {
105699
106172
  metadata: { reason: deps.sessionReason }
105700
106173
  });
105701
106174
  }
105702
- let startupMode = resolveStartupMode();
106175
+ let startupMode = resolveStartupMode(deps.projectConfig, deps.globalConfig);
105703
106176
  const processGlobals = globalThis;
105704
106177
  const isFirstSessionOfProcess = !processGlobals.__piLensFirstSessionDone;
105705
- if (isFirstSessionOfProcess && process.env.PI_LENS_COLD_START_QUICK !== "0" && !process.env.PI_LENS_STARTUP_MODE) {
106178
+ if (isFirstSessionOfProcess && process.env.PI_LENS_COLD_START_QUICK !== "0" && !process.env.PI_LENS_STARTUP_MODE && !deps.projectConfig?.startup?.mode && !deps.globalConfig?.startup?.mode) {
105706
106179
  startupMode = deps.startupModeOverride ?? "quick";
105707
106180
  }
105708
106181
  processGlobals.__piLensFirstSessionDone = true;
@@ -106233,7 +106706,8 @@ async function handleSessionStart(deps) {
106233
106706
  } else {
106234
106707
  dbg2("session_start: no language defaults selected for pre-install");
106235
106708
  }
106236
- const startupScansWillRun = allowBootstrapTasks && startupScan.canWarmCaches;
106709
+ const startupScansEnabled = deps.projectConfig?.startup?.scans?.enabled ?? deps.globalConfig?.startup?.scans?.enabled ?? true;
106710
+ const startupScansWillRun = allowBootstrapTasks && startupScan.canWarmCaches && startupScansEnabled;
106237
106711
  const jstsHeavyScansWillRun = startupScansWillRun && canRunStartupHeavyScans(languageProfile, "jsts");
106238
106712
  if (allowBootstrapTasks) {
106239
106713
  scheduleDeferredToolProbes(deps, languageProfile, startupDefaults, jstsHeavyScansWillRun, dbg2);
@@ -106277,6 +106751,13 @@ async function handleSessionStart(deps) {
106277
106751
  cacheManager2.writeCache("session-start-guidance", { content: agentStartupGuidance.join("\n") }, analysisRoot);
106278
106752
  if (!allowBootstrapTasks) {
106279
106753
  dbg2("session_start: skipping startup background scans (startup mode)");
106754
+ } else if (!startupScansEnabled) {
106755
+ dbg2("session_start: skipping startup background scans (disabled by config)");
106756
+ recordDegradationOnce({
106757
+ kind: "startup-analyzer-disabled",
106758
+ subject: "startup-scans",
106759
+ reason: "skipped (disabled by config)"
106760
+ });
106280
106761
  } else if (!startupScan.canWarmCaches) {
106281
106762
  dbg2(`session_start: skipping heavy scans (${startupScan.reason ?? "unknown"})`);
106282
106763
  dbg2(`session_start: skipping TODO scan (${startupScan.reason ?? "unknown"})`);
@@ -108623,7 +109104,7 @@ async function handleToolCallImpl(deps) {
108623
109104
  timestamp: Date.now()
108624
109105
  });
108625
109106
  }
108626
- if (!isExternalOrVendor && filePath && !runtime2.complexityBaselines.has(filePath) && isComplexitySupportedFile(filePath)) {
109107
+ if (!getFlag("no-complexity") && !isExternalOrVendor && filePath && !runtime2.complexityBaselines.has(filePath) && isComplexitySupportedFile(filePath)) {
108627
109108
  const complexityClient = (await requestBootstrapClients({
108628
109109
  reason: "tool-call-complexity-baseline",
108629
109110
  // The pre-dispatch hook. It carries no wall budget in #2523's
@@ -108646,6 +109127,12 @@ async function handleToolCallImpl(deps) {
108646
109127
  entropy: baseline.codeEntropy
108647
109128
  });
108648
109129
  }
109130
+ } else if (getFlag("no-complexity") && filePath && isComplexitySupportedFile(filePath)) {
109131
+ recordDegradationOnce({
109132
+ kind: "startup-analyzer-disabled",
109133
+ subject: "complexity",
109134
+ reason: "skipped (disabled by config)"
109135
+ });
108649
109136
  }
108650
109137
  const isEditOnly = mutation?.kind === "edit";
108651
109138
  const isWriteOrEdit = mutation !== void 0;
@@ -115533,7 +116020,7 @@ init_config();
115533
116020
  init_server();
115534
116021
  init_path_utils();
115535
116022
  init_string_utils();
115536
- import * as os33 from "node:os";
116023
+ import * as os34 from "node:os";
115537
116024
  import * as path194 from "node:path";
115538
116025
  function isEffectiveFileViewError(file) {
115539
116026
  return "error" in file;
@@ -115589,7 +116076,7 @@ function decidedByOrNothing(entry) {
115589
116076
  }
115590
116077
  async function effectiveConfig(options = {}) {
115591
116078
  const cwd = options.cwd ?? process.cwd();
115592
- const homeDir = options.homeDir ?? os33.homedir();
116079
+ const homeDir = options.homeDir ?? os34.homedir();
115593
116080
  const resolveAt = (dir) => resolvePiLensConfig({
115594
116081
  cwd: dir,
115595
116082
  globalDir: getGlobalPiLensDir(),
@@ -118617,6 +119104,10 @@ function createLspDiagnosticsTool(nextWriteIndex, onConfirmedNoBlockers) {
118617
119104
  const scope = typeof files2 === "number" && files2 > 1 ? ` across ${files2} files` : target ? ` ${target}` : "";
118618
119105
  const noun = count === 1 ? "diagnostic" : "diagnostics";
118619
119106
  const unconfirmedFiles = details?.unconfirmedFiles ?? 0;
119107
+ const navigationOnlyFiles = details?.navigationOnlyFiles ?? 0;
119108
+ if (navigationOnlyFiles > 0) {
119109
+ return `lsp_diagnostics${scope} \u2014 ${count} ${noun} \xB7 ${details?.cleanFiles ?? 0} clean \xB7 ${navigationOnlyFiles} navigation-only`;
119110
+ }
118620
119111
  if (unconfirmedFiles > 0) {
118621
119112
  const cleanFiles = details?.cleanFiles ?? 0;
118622
119113
  const timedOutFiles = details?.timedOutFiles ?? 0;
@@ -118791,6 +119282,7 @@ async function collectDiagnosticsForFile(absPath, lspService, waitMs, serverScop
118791
119282
  // field across the socket. An older incumbent omits it → empty → the
118792
119283
  // pre-#1470 handling, unchanged.
118793
119284
  unconfirmedServerIds: attached.response.unconfirmedServerIds ?? [],
119285
+ diagnosticsUnsupportedServerIds: [],
118794
119286
  content
118795
119287
  };
118796
119288
  }
@@ -118820,6 +119312,7 @@ async function collectDiagnosticsForFile(absPath, lspService, waitMs, serverScop
118820
119312
  // the getDiagnostics fallback never reports one, which is honest — that
118821
119313
  // path claims no confirmation at all.
118822
119314
  unconfirmedServerIds: touchCoverageGap(touched),
119315
+ diagnosticsUnsupportedServerIds: touched?.diagnosticsUnsupportedServerIds ?? [],
118823
119316
  content,
118824
119317
  binding
118825
119318
  };
@@ -118942,11 +119435,13 @@ async function collectFileDiagnosticResult(file, severity, lspService, waitMs, n
118942
119435
  };
118943
119436
  }
118944
119437
  }
118945
- const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, content: collectedContent, binding, skipReason } = await collectDiagnosticsForFile(file, lspService, waitMs, serverScope);
119438
+ const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, diagnosticsUnsupportedServerIds, content: collectedContent, binding, skipReason } = await collectDiagnosticsForFile(file, lspService, waitMs, serverScope);
118946
119439
  const health = lspService.getDiagnosticsHealth?.(file);
118947
119440
  let effectiveRawDiags = rawDiags;
118948
119441
  let confirmation;
118949
- if (skipReason !== void 0) {
119442
+ if (diagnosticsUnsupportedServerIds.length > 0) {
119443
+ confirmation = void 0;
119444
+ } else if (skipReason !== void 0) {
118950
119445
  confirmation = "unconfirmed";
118951
119446
  } else if (timedOut) {
118952
119447
  if (applySeverityFilter(rawDiags, severity).length === 0) {
@@ -118980,7 +119475,7 @@ async function collectFileDiagnosticResult(file, severity, lspService, waitMs, n
118980
119475
  cwd
118981
119476
  });
118982
119477
  }
118983
- if (cacheCtx && scopeKey !== void 0 && confirmation !== "unconfirmed" && unconfirmedServerIds.length === 0) {
119478
+ if (cacheCtx && scopeKey !== void 0 && confirmation !== "unconfirmed" && diagnosticsUnsupportedServerIds.length === 0 && unconfirmedServerIds.length === 0) {
118984
119479
  cacheCtx.record(file, scopeKey, effectiveRawDiags, stat4.mtimeMs, collectedContent !== void 0 ? hashDiagnosticContent(collectedContent) : void 0, stat4.size);
118985
119480
  }
118986
119481
  return {
@@ -118990,17 +119485,20 @@ async function collectFileDiagnosticResult(file, severity, lspService, waitMs, n
118990
119485
  confirmation,
118991
119486
  timedOut: confirmation === "unconfirmed" ? timedOut : void 0,
118992
119487
  ...skipReason !== void 0 && { skipReason },
118993
- primaryServerId: primaryServerId(file)
119488
+ primaryServerId: primaryServerId(file),
119489
+ diagnosticsUnsupported: diagnosticsUnsupportedServerIds.length > 0
118994
119490
  };
118995
119491
  }
118996
119492
  async function runFileDiagnostics(absPath, severity, lspService, waitMs, nextWriteIndex, serverScope = "all", cwd = process.cwd(), onConfirmedNoBlockers) {
118997
119493
  const writeIndex = nextWriteIndex?.();
118998
- const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, content: collectedContent, binding, skipReason } = await collectDiagnosticsForFile(absPath, lspService, waitMs, serverScope);
119494
+ const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, diagnosticsUnsupportedServerIds, content: collectedContent, binding, skipReason } = await collectDiagnosticsForFile(absPath, lspService, waitMs, serverScope);
118999
119495
  const lspHealth = lspService.getDiagnosticsHealth?.(absPath);
119000
119496
  const unavailable = lspUnavailableMessage(absPath, lspHealth);
119001
119497
  let effectiveRawDiags = rawDiags;
119002
119498
  let confirmation;
119003
- if (skipReason !== void 0) {
119499
+ if (diagnosticsUnsupportedServerIds.length > 0) {
119500
+ confirmation = void 0;
119501
+ } else if (skipReason !== void 0) {
119004
119502
  confirmation = "unconfirmed";
119005
119503
  } else if (timedOut) {
119006
119504
  if (applySeverityFilter(rawDiags, severity).length === 0) {
@@ -119042,6 +119540,9 @@ async function runFileDiagnostics(absPath, severity, lspService, waitMs, nextWri
119042
119540
  const primaryDiags = limited.filter((d) => d.source === primaryId);
119043
119541
  const auxiliaryDiags = limited.filter((d) => d.source !== primaryId);
119044
119542
  const primaryLine = (() => {
119543
+ if (diagnosticsUnsupportedServerIds.length > 0) {
119544
+ return `Primary LSP${primaryId ? ` (${primaryId})` : ""}: navigation-only \u2014 diagnostics unsupported (no pull provider or publish evidence).`;
119545
+ }
119045
119546
  if (timedOut) {
119046
119547
  return "Primary LSP: check timed out \u2014 NOT the same as 0 diagnostics; the file may still have errors that just hadn't been reported yet. Re-check after the server settles, or increase waitMs.";
119047
119548
  }
@@ -119102,6 +119603,7 @@ async function runFileDiagnostics(absPath, severity, lspService, waitMs, nextWri
119102
119603
  truncated,
119103
119604
  unconfirmed,
119104
119605
  timedOut: unconfirmed ? timedOut : void 0,
119606
+ navigationOnlyFiles: diagnosticsUnsupportedServerIds.length > 0 ? 1 : void 0,
119105
119607
  ...skipReason !== void 0 && { skipReason },
119106
119608
  // #1470: which servers this result does NOT speak for. Absent when it
119107
119609
  // speaks for all of them.
@@ -119117,7 +119619,12 @@ function tallyConfirmation(results) {
119117
119619
  let clean = 0;
119118
119620
  let unconfirmed = 0;
119119
119621
  let timedOut = 0;
119622
+ let navigationOnly = 0;
119120
119623
  for (const result of results) {
119624
+ if (result.diagnosticsUnsupported) {
119625
+ navigationOnly += 1;
119626
+ continue;
119627
+ }
119121
119628
  if (result.diagnostics.length > 0)
119122
119629
  continue;
119123
119630
  if (result.confirmation === "unconfirmed") {
@@ -119128,11 +119635,13 @@ function tallyConfirmation(results) {
119128
119635
  clean += 1;
119129
119636
  }
119130
119637
  }
119131
- return { clean, unconfirmed, timedOut };
119638
+ return { clean, unconfirmed, timedOut, navigationOnly };
119132
119639
  }
119133
119640
  function classifyBatchFileOutcome(result) {
119134
119641
  if (result.error)
119135
119642
  return "failed";
119643
+ if (result.diagnosticsUnsupported)
119644
+ return "unsupported";
119136
119645
  if (result.timedOut || result.confirmation === "unconfirmed") {
119137
119646
  return "inconclusive";
119138
119647
  }
@@ -119240,6 +119749,7 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options)
119240
119749
  };
119241
119750
  }
119242
119751
  const { results, fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut, outcomeCounts, incompleteFiles } = await collectBatchDiagnostics(absPaths, severity, lspService, options);
119752
+ const navigationOnly = results.filter((result) => result.diagnosticsUnsupported).length;
119243
119753
  const lines = [
119244
119754
  `Files checked: ${results.length}`,
119245
119755
  `Total diagnostics: ${total}`,
@@ -119296,6 +119806,7 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options)
119296
119806
  truncated,
119297
119807
  cleanFiles: clean,
119298
119808
  unconfirmedFiles: unconfirmed,
119809
+ navigationOnlyFiles: navigationOnly > 0 ? navigationOnly : void 0,
119299
119810
  timedOutFiles: timedOut > 0 ? timedOut : void 0,
119300
119811
  outcomes: results.map((result) => ({
119301
119812
  file: result.file,
@@ -119345,7 +119856,8 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
119345
119856
  }
119346
119857
  const wasCapped = collectedFiles.length > MAX_FILES3;
119347
119858
  const filesToProcess = collectedFiles.slice(0, MAX_FILES3);
119348
- const { fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut } = await collectBatchDiagnostics(filesToProcess, severity, lspService, options);
119859
+ const { results, fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut } = await collectBatchDiagnostics(filesToProcess, severity, lspService, options);
119860
+ const navigationOnly = results.filter((result) => result.diagnosticsUnsupported).length;
119349
119861
  let text;
119350
119862
  if (total === 0) {
119351
119863
  const cleanLine = unconfirmed > 0 ? `${clean} clean \xB7 ${unconfirmed} unconfirmed: ${unconfirmedReasonClause(unconfirmed, timedOut)} NOT the same as 0 diagnostics.` : "No diagnostics found.";
@@ -119415,6 +119927,7 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
119415
119927
  truncated,
119416
119928
  cleanFiles: clean,
119417
119929
  unconfirmedFiles: unconfirmed,
119930
+ navigationOnlyFiles: navigationOnly > 0 ? navigationOnly : void 0,
119418
119931
  timedOutFiles: timedOut > 0 ? timedOut : void 0,
119419
119932
  fileErrors: fileErrors.length > 0 ? fileErrors : void 0,
119420
119933
  lspHealthWarnings: lspHealthWarnings.length > 0 ? lspHealthWarnings : void 0,
@@ -123156,7 +123669,6 @@ function activateExtension(hostPi) {
123156
123669
  dbg(`turn-summary renderer registration failed: ${registerRendererErr}`);
123157
123670
  }
123158
123671
  }
123159
- registerTestRunnerEntryRenderer(pi);
123160
123672
  pi.registerCommand("lens-toggle", {
123161
123673
  description: "Toggle pi-lens on/off for the current session. Usage: /lens-toggle",
123162
123674
  handler: async (_args, ctx) => {
@@ -123716,6 +124228,8 @@ ${degradationLines.join("\n")}` : "";
123716
124228
  emitHostReadyDelay,
123717
124229
  sessionReason,
123718
124230
  handlerEnteredAt,
124231
+ globalConfig,
124232
+ projectConfig: loadPiLensProjectConfig(runtime.projectRoot),
123719
124233
  // #2129: this call site is only reached for "primary"/
123720
124234
  // "sequential-replacement" — a declined start returned above.
123721
124235
  sessionStartClassification: sessionStartDecision.classification,
@@ -124072,7 +124586,6 @@ ${degradationLines.join("\n")}` : "";
124072
124586
  ...delivery,
124073
124587
  owner: {
124074
124588
  ownerId: testRunnerDeliveryOwnerId,
124075
- pi,
124076
124589
  cacheManager,
124077
124590
  runtime,
124078
124591
  getCtx: () => ownEventCtx ?? {}
@@ -124375,6 +124888,13 @@ ${degradationLines.join("\n")}` : "";
124375
124888
  }
124376
124889
  const turnEndFindings = consumeTurnEndFindings(cacheManager, cwd, runtime);
124377
124890
  const sessionGuidance = consumeSessionStartGuidance(cacheManager, cwd);
124891
+ const testFindings = consumeStagedTestRunnerFindings({
124892
+ cwd,
124893
+ sessionId: runtime.telemetrySessionId,
124894
+ ownerId: testRunnerDeliveryOwnerId,
124895
+ cacheManager,
124896
+ runtime
124897
+ });
124378
124898
  const agentNudge = consumeAgentNudge(dbg);
124379
124899
  const sourceMessages = [
124380
124900
  {
@@ -124385,6 +124905,10 @@ ${degradationLines.join("\n")}` : "";
124385
124905
  source: "turn-findings",
124386
124906
  messages: turnEndFindings?.messages ?? []
124387
124907
  },
124908
+ {
124909
+ source: "test-findings",
124910
+ messages: testFindings?.messages ?? []
124911
+ },
124388
124912
  {
124389
124913
  source: "agent-nudge",
124390
124914
  messages: agentNudge?.messages ?? []