@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
@@ -31,7 +31,7 @@
31
31
  * per-call I/O" shape) and reuses the last verdict in between.
32
32
  */
33
33
  import * as fs from "node:fs";
34
- export const realStat = (targetPath) => {
34
+ const realStat = (targetPath) => {
35
35
  try {
36
36
  return fs.statSync(targetPath);
37
37
  }
@@ -46,7 +46,7 @@ function runningOnWindows() {
46
46
  return process.platform === "win32";
47
47
  }
48
48
  /** Default ceiling for one process listing. */
49
- export const DEFAULT_SNAPSHOT_TIMEOUT_MS = 4_000;
49
+ const DEFAULT_SNAPSHOT_TIMEOUT_MS = 4_000;
50
50
  /**
51
51
  * @typedef {"pid" | "ppid" | "ageMs" | "rssBytes" | "cpuKernel100ns" | "cpuUser100ns" | "startedAt" | "command"} ProcessField
52
52
  * @typedef {{ pid: number, ppid: number, command: string, ageMs?: number, rssBytes?: number, cpuKernel100ns?: number, cpuUser100ns?: number, startedAt?: string, cwd?: string }} ProcRow
@@ -52,7 +52,7 @@ import * as path from "node:path";
52
52
  * @param {import("node:fs").Dirent} entry
53
53
  * @returns {"file" | "dir" | "other"}
54
54
  */
55
- export function resolvedEntryKind(fullPath, entry) {
55
+ function resolvedEntryKind(fullPath, entry) {
56
56
  if (entry.isSymbolicLink()) {
57
57
  try {
58
58
  const stats = fs.statSync(fullPath);
@@ -1115,7 +1115,7 @@ function tallyLspPrimaryVsAuxiliary(results) {
1115
1115
  }
1116
1116
  return { primary, auxiliary };
1117
1117
  }
1118
- export function mergeDiagnosticsWithWidgetSummaries(widgetSummaries, lspResults, projectSnapshot, projectDelta,
1118
+ function mergeDiagnosticsWithWidgetSummaries(widgetSummaries, lspResults, projectSnapshot, projectDelta,
1119
1119
  /**
1120
1120
  * #1993: resolved paths covered by a CONFIRMED, fully-covered LSP result
1121
1121
  * in `lspResults`. The fresh sweep is AUTHORITATIVE for these files - any
@@ -245,6 +245,10 @@ onConfirmedNoBlockers) {
245
245
  // compact-render as a bare "N diagnostics" — that erases the fact some
246
246
  // files' clean status was never actually confirmed by the server.
247
247
  const unconfirmedFiles = details?.unconfirmedFiles ?? 0;
248
+ const navigationOnlyFiles = details?.navigationOnlyFiles ?? 0;
249
+ if (navigationOnlyFiles > 0) {
250
+ return `lsp_diagnostics${scope} — ${count} ${noun} · ${details?.cleanFiles ?? 0} clean · ${navigationOnlyFiles} navigation-only`;
251
+ }
248
252
  if (unconfirmedFiles > 0) {
249
253
  const cleanFiles = details?.cleanFiles ?? 0;
250
254
  const timedOutFiles = details?.timedOutFiles ?? 0;
@@ -470,6 +474,7 @@ async function collectDiagnosticsForFile(absPath, lspService, waitMs, serverScop
470
474
  // field across the socket. An older incumbent omits it → empty → the
471
475
  // pre-#1470 handling, unchanged.
472
476
  unconfirmedServerIds: attached.response.unconfirmedServerIds ?? [],
477
+ diagnosticsUnsupportedServerIds: [],
473
478
  content,
474
479
  };
475
480
  }
@@ -528,6 +533,7 @@ async function collectDiagnosticsForFile(absPath, lspService, waitMs, serverScop
528
533
  // the getDiagnostics fallback never reports one, which is honest — that
529
534
  // path claims no confirmation at all.
530
535
  unconfirmedServerIds: touchCoverageGap(touched),
536
+ diagnosticsUnsupportedServerIds: touched?.diagnosticsUnsupportedServerIds ?? [],
531
537
  content,
532
538
  binding,
533
539
  };
@@ -777,7 +783,7 @@ onConfirmedNoBlockers) {
777
783
  };
778
784
  }
779
785
  }
780
- const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, content: collectedContent, binding, skipReason, } = await collectDiagnosticsForFile(file, lspService, waitMs, serverScope);
786
+ const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, diagnosticsUnsupportedServerIds, content: collectedContent, binding, skipReason, } = await collectDiagnosticsForFile(file, lspService, waitMs, serverScope);
781
787
  const health = lspService.getDiagnosticsHealth?.(file);
782
788
  // #570: a timed-out priming check is never a confirmed "clean" — treat it
783
789
  // as unconfirmed without consulting the (unrelated) silent-tier
@@ -788,7 +794,12 @@ onConfirmedNoBlockers) {
788
794
  // be merged in rather than discarded.
789
795
  let effectiveRawDiags = rawDiags;
790
796
  let confirmation;
791
- if (skipReason !== undefined) {
797
+ if (diagnosticsUnsupportedServerIds.length > 0) {
798
+ // No diagnostic provider and no push evidence is a capability boundary,
799
+ // not a clean result and not a timeout.
800
+ confirmation = undefined;
801
+ }
802
+ else if (skipReason !== undefined) {
792
803
  confirmation = "unconfirmed";
793
804
  }
794
805
  else if (timedOut) {
@@ -851,6 +862,7 @@ onConfirmedNoBlockers) {
851
862
  if (cacheCtx &&
852
863
  scopeKey !== undefined &&
853
864
  confirmation !== "unconfirmed" &&
865
+ diagnosticsUnsupportedServerIds.length === 0 &&
854
866
  unconfirmedServerIds.length === 0) {
855
867
  cacheCtx.record(file, scopeKey, effectiveRawDiags, stat.mtimeMs, collectedContent !== undefined
856
868
  ? hashDiagnosticContent(collectedContent)
@@ -864,6 +876,7 @@ onConfirmedNoBlockers) {
864
876
  timedOut: confirmation === "unconfirmed" ? timedOut : undefined,
865
877
  ...(skipReason !== undefined && { skipReason }),
866
878
  primaryServerId: primaryServerId(file),
879
+ diagnosticsUnsupported: diagnosticsUnsupportedServerIds.length > 0,
867
880
  };
868
881
  }
869
882
  async function runFileDiagnostics(absPath, severity, lspService, waitMs, nextWriteIndex, serverScope = "all", cwd = process.cwd(),
@@ -873,7 +886,7 @@ onConfirmedNoBlockers) {
873
886
  // Reserve the token before awaiting this file's LSP result. The direct-file
874
887
  // path performs its own confirmation/reconciliation below (#1198).
875
888
  const writeIndex = nextWriteIndex?.();
876
- const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, content: collectedContent, binding, skipReason, } = await collectDiagnosticsForFile(absPath, lspService, waitMs, serverScope);
889
+ const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, diagnosticsUnsupportedServerIds, content: collectedContent, binding, skipReason, } = await collectDiagnosticsForFile(absPath, lspService, waitMs, serverScope);
877
890
  const lspHealth = lspService.getDiagnosticsHealth?.(absPath);
878
891
  const unavailable = lspUnavailableMessage(absPath, lspHealth);
879
892
  // #533: an empty result needs a confirmed/unconfirmed verdict — a push-only,
@@ -887,7 +900,10 @@ onConfirmedNoBlockers) {
887
900
  // diagnostics it surfaces are merged in, not discarded.
888
901
  let effectiveRawDiags = rawDiags;
889
902
  let confirmation;
890
- if (skipReason !== undefined) {
903
+ if (diagnosticsUnsupportedServerIds.length > 0) {
904
+ confirmation = undefined;
905
+ }
906
+ else if (skipReason !== undefined) {
891
907
  confirmation = "unconfirmed";
892
908
  }
893
909
  else if (timedOut) {
@@ -955,6 +971,9 @@ onConfirmedNoBlockers) {
955
971
  // auxiliary findings exist — a wall of ast-grep/opengrep noise must never
956
972
  // bury whether the actual language server confirmed the file clean.
957
973
  const primaryLine = (() => {
974
+ if (diagnosticsUnsupportedServerIds.length > 0) {
975
+ return `Primary LSP${primaryId ? ` (${primaryId})` : ""}: navigation-only — diagnostics unsupported (no pull provider or publish evidence).`;
976
+ }
958
977
  if (timedOut) {
959
978
  return ("Primary LSP: check timed out — NOT the same as 0 diagnostics; the " +
960
979
  "file may still have errors that just hadn't been reported yet. " +
@@ -1031,6 +1050,7 @@ onConfirmedNoBlockers) {
1031
1050
  truncated,
1032
1051
  unconfirmed,
1033
1052
  timedOut: unconfirmed ? timedOut : undefined,
1053
+ navigationOnlyFiles: diagnosticsUnsupportedServerIds.length > 0 ? 1 : undefined,
1034
1054
  ...(skipReason !== undefined && { skipReason }),
1035
1055
  // #1470: which servers this result does NOT speak for. Absent when it
1036
1056
  // speaks for all of them.
@@ -1054,7 +1074,12 @@ function tallyConfirmation(results) {
1054
1074
  let clean = 0;
1055
1075
  let unconfirmed = 0;
1056
1076
  let timedOut = 0;
1077
+ let navigationOnly = 0;
1057
1078
  for (const result of results) {
1079
+ if (result.diagnosticsUnsupported) {
1080
+ navigationOnly += 1;
1081
+ continue;
1082
+ }
1058
1083
  if (result.diagnostics.length > 0)
1059
1084
  continue;
1060
1085
  if (result.confirmation === "unconfirmed") {
@@ -1068,11 +1093,13 @@ function tallyConfirmation(results) {
1068
1093
  clean += 1;
1069
1094
  }
1070
1095
  }
1071
- return { clean, unconfirmed, timedOut };
1096
+ return { clean, unconfirmed, timedOut, navigationOnly };
1072
1097
  }
1073
1098
  function classifyBatchFileOutcome(result) {
1074
1099
  if (result.error)
1075
1100
  return "failed";
1101
+ if (result.diagnosticsUnsupported)
1102
+ return "unsupported";
1076
1103
  // A timed-out/unconfirmed answer may contain partial findings, but it cannot
1077
1104
  // honestly be called a complete findings result. Keep the raw findings for
1078
1105
  // investigation while making the aggregate incomplete.
@@ -1228,6 +1255,7 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options)
1228
1255
  };
1229
1256
  }
1230
1257
  const { results, fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut, outcomeCounts, incompleteFiles, } = await collectBatchDiagnostics(absPaths, severity, lspService, options);
1258
+ const navigationOnly = results.filter((result) => result.diagnosticsUnsupported).length;
1231
1259
  const lines = [
1232
1260
  `Files checked: ${results.length}`,
1233
1261
  `Total diagnostics: ${total}`,
@@ -1298,6 +1326,7 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options)
1298
1326
  truncated,
1299
1327
  cleanFiles: clean,
1300
1328
  unconfirmedFiles: unconfirmed,
1329
+ navigationOnlyFiles: navigationOnly > 0 ? navigationOnly : undefined,
1301
1330
  timedOutFiles: timedOut > 0 ? timedOut : undefined,
1302
1331
  outcomes: results.map((result) => ({
1303
1332
  file: result.file,
@@ -1366,7 +1395,8 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
1366
1395
  }
1367
1396
  const wasCapped = collectedFiles.length > MAX_FILES;
1368
1397
  const filesToProcess = collectedFiles.slice(0, MAX_FILES);
1369
- const { fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut, } = await collectBatchDiagnostics(filesToProcess, severity, lspService, options);
1398
+ const { results, fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut, } = await collectBatchDiagnostics(filesToProcess, severity, lspService, options);
1399
+ const navigationOnly = results.filter((result) => result.diagnosticsUnsupported).length;
1370
1400
  let text;
1371
1401
  if (total === 0) {
1372
1402
  // #533/#570: an unconfirmed-containing directory result must never
@@ -1450,6 +1480,7 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
1450
1480
  truncated,
1451
1481
  cleanFiles: clean,
1452
1482
  unconfirmedFiles: unconfirmed,
1483
+ navigationOnlyFiles: navigationOnly > 0 ? navigationOnly : undefined,
1453
1484
  timedOutFiles: timedOut > 0 ? timedOut : undefined,
1454
1485
  fileErrors: fileErrors.length > 0 ? fileErrors : undefined,
1455
1486
  lspHealthWarnings: lspHealthWarnings.length > 0 ? lspHealthWarnings : undefined,
@@ -9,7 +9,7 @@ function finiteNumber(value) {
9
9
  ? value
10
10
  : undefined;
11
11
  }
12
- export function lspStatusFromFailureKind(failureKind) {
12
+ function lspStatusFromFailureKind(failureKind) {
13
13
  if (failureKind === "success" || failureKind === "fallback_success") {
14
14
  return "success";
15
15
  }
@@ -145,7 +145,7 @@ function collectCallHierarchyLocationSummaries(result, operation) {
145
145
  }
146
146
  return out;
147
147
  }
148
- export function collectLspToolLocationsForOperation(operation, result, filePath) {
148
+ function collectLspToolLocationsForOperation(operation, result, filePath) {
149
149
  if ([
150
150
  "definition",
151
151
  "typeDefinition",
@@ -210,7 +210,7 @@ function parseBalancedJsonPrefix(text) {
210
210
  }
211
211
  return undefined;
212
212
  }
213
- export function parseLspNavigationTextPayload(text) {
213
+ function parseLspNavigationTextPayload(text) {
214
214
  const notes = [];
215
215
  const hints = [];
216
216
  const errors = [];
@@ -65,7 +65,7 @@ export function selectCompactText(result, args, expanded, summarize) {
65
65
  }
66
66
  /** Apply a CompactStyle to text. `brand` uses raw blue ANSI; the rest defer to
67
67
  * the theme so error-red and normal output stay consistent with the host. */
68
- export function paintCompact(style, text, theme) {
68
+ function paintCompact(style, text, theme) {
69
69
  if (style === "brand") {
70
70
  return `${PI_LENS_BLUE_FG}${text}${RESET_FG}`;
71
71
  }
@@ -8,7 +8,7 @@
8
8
  import * as path from "node:path";
9
9
  import { Type } from "../clients/deps/typebox.js";
10
10
  import { symbolSearch, } from "../clients/lens-engine.js";
11
- import { baseName, compactRenderResult } from "./render-compact.js";
11
+ import { compactRenderResult } from "./render-compact.js";
12
12
  /**
13
13
  * Machine-actionable follow-up hint (#771) — mirrors ast-grep-search.ts's
14
14
  * `suggestedDump` pattern. Cheap enough to attach per hit rather than only the
@@ -153,4 +153,3 @@ export function createSymbolSearchTool(getProjectRoot) {
153
153
  }
154
154
  // Re-exported so tests importing from this module can reach baseName without
155
155
  // a second import path.
156
- export { baseName };
@@ -27,6 +27,20 @@ Each runtime toggle is settable from the CLI *and* from `config.json`. The two a
27
27
  | `--lens-compact-tool-line` | `ui.compactToolLine` | `false` |
28
28
  | `--no-lazy-tools` | `tools.lazy` | `true` |
29
29
  | `--lens-turn-end-madge` | `turnEnd.madge.enabled` | `false` |
30
+ | `--no-knip` | `knip.enabled` | `true` |
31
+ | `--no-jscpd` | `jscpd.enabled` | `true` |
32
+ | `--no-madge` | `madge.enabled` | `true` |
33
+ | `--no-gitleaks` | `gitleaks.enabled` | `true` |
34
+ | `--no-govulncheck` | `govulncheck.enabled` | `true` |
35
+ | `--no-dead-code` | `deadCode.enabled` | `true` |
36
+ | `--no-complexity` | `complexity.enabled` | `true` |
37
+
38
+ ## Startup controls
39
+
40
+ | Configuration key | Default | Accepted values |
41
+ | --- | --- | --- |
42
+ | `startup.mode` | `full` | `quick`, `full`, or `minimal`; `PI_LENS_STARTUP_MODE` wins |
43
+ | `startup.scans.enabled` | `true` | `true` or `false` |
30
44
 
31
45
  By default pi-lens registers six situational tools (the `ast_grep_*` family,
32
46
  `lsp_navigation`, `lens_diagnostic_mark`) inactive and exposes a small loader,
@@ -249,6 +263,12 @@ Explicit override for the review graph's own file budget (#775), for monorepos t
249
263
 
250
264
  ### Schema rules
251
265
 
266
+ Session-start analyzer keys default to `true` and work in both the global
267
+ config and `.pi-lens.json`. Set one to `false` to skip that analyzer; pi-lens
268
+ records the disabled skip. `startup.mode` accepts `quick`, `full`, or
269
+ `minimal`, and `startup.scans.enabled: false` disables background startup scans
270
+ while leaving diagnostics and LSP active. `PI_LENS_STARTUP_MODE` still wins.
271
+
252
272
  - Unknown rule ids are ignored (forward-compat). Unrecognized **top-level** keys are logged once and then ignored — never fatal to the parse. The LSP namespaces a shared file legitimately carries (`servers`, `serverOverrides`, `disabledServers`, `warmFiles`) and `$schema` are tolerated silently; a user-level-only lens key placed here (e.g. `lsp`, `tests`, `delta`) is logged as "not honored at project scope"; anything else is logged as a likely typo. The raw parsed JSON is still exposed for forward-compat consumers regardless.
253
273
  - Every toggle key in the table above must be a boolean, and its containing section must be an object. A wrong type is logged once and the key is treated as absent, so the flag falls through to its default rather than the whole file being rejected.
254
274
  - A malformed JSON file is logged once and treated as "no config" — your diagnostics never get blocked by a syntax error in your own config.
@@ -35,11 +35,19 @@ they share a server id but not a verified clean-signal behavior.
35
35
  | **2 — push, publishes-versioned** | `publishDiagnostics([])` **with version** on every scan, incl. clean→clean | YES, currency-proven via version | ast-grep |
36
36
  | **2\* — push, publishes-unversioned** | re-publishes on a clean scan but **version-less** — the wait still early-returns (the client accepts a version-less publish as fresh: it can't be proven stale), but currency is only *temporally correlated*, not proven | YES at runtime, with a staleness-risk caveat (not a latency cost) | opengrep |
37
37
  | **3 — push, silent on clean** | server publishes nothing when nothing changed | **NO** — budget-wait floor (safe; a timeout is *not* a false clean). **This tier is #458's learned-deadline target set.** | typescript-language-server |
38
+ | **Navigation-only — custom, no evidence** | custom `lsp.servers.*` entry has no pull provider and has not published in this session | **NO** — diagnostics are unsupported; skip the wait and report navigation-only | Dexter |
38
39
 
39
40
  Detection is **cached** at `initialize` (`detectWorkspaceDiagnosticsSupport` →
40
41
  `state.workspaceDiagnosticsSupport.mode`, upgraded on `client/registerCapability`),
41
42
  so the tier is free at collection time — no per-edit probe.
42
43
 
44
+ Custom servers without a pull provider begin with one bounded first-contact
45
+ probe. The probe uses the smaller of the server's push-wait budget and the live
46
+ hook deadline. If the hook cuts it off first, the result remains unconfirmed and
47
+ the next touch probes again. Only silence through the full push budget latches
48
+ that server id as navigation-only for the service session. A publish upgrades it
49
+ to the ordinary push-wait policy.
50
+
43
51
  ## Matrix (dev box + CI nightly; mode last refreshed 2026-06-17 from run 27713958681, clean-behavior probed on the dev box 2026-07-08 — #460)
44
52
 
45
53
  `mode` from cached capabilities; `clean-behavior` from the phase-aware publish-trace
@@ -74,7 +82,7 @@ classification standing.
74
82
  | toml | taplo | push-only | publishes-unversioned | 2* | dev+ci |
75
83
  | terraform | terraform-ls | push-only | TBD | 2/3? | dev+ci |
76
84
  | prisma | @prisma/language-server | push-only | publishes-unversioned | 2* | dev+ci |
77
- | php | intelephense | push-only | TBD | 2/3? | dev |
85
+ | php | intelephense | push-only | TBD | 2/3? | dev+ci |
78
86
  | zig | zls | push-only | publishes-unversioned | 2* | dev+ci |
79
87
  | vue | @vue/language-server | push-only | TBD | 2/3? | dev+ci |
80
88
  | dart | dart language-server | push-only | publishes-unversioned | 2* | ci |
@@ -6,6 +6,12 @@ Assume the implementation's claims are incomplete. Read the issue, full diff,
6
6
  repository instructions, shared delegated worker contract, PR body, and merge
7
7
  state. Keep the review read-only.
8
8
 
9
+ Diff the change from its merge base (`git diff origin/master...HEAD`, or
10
+ `git diff $(git merge-base origin/master HEAD)..HEAD`), never a two-dot diff
11
+ against `origin/master`: a checkout cut before another lane merged shows that
12
+ merge in reverse as deletions and produces a false HIGH (2026-09-08, #2730
13
+ round 2 and #2747 round 1).
14
+
9
15
  Reproduce the build and targeted tests. Verify quoted red-first evidence by
10
16
  keeping the tests and removing the source fix. Mutate every new guard and demand
11
17
  a red test. Probe inversions, concurrency, input channels, trust boundaries,
@@ -30,7 +30,18 @@ real store, sink, coordinator, or registry, use it and assert the durable result
30
30
  Git authority is separate from the role. Commit, push, or open a PR only when
31
31
  the delegation explicitly grants that authority after worktree verification.
32
32
  Otherwise, edit and test with the assigned worktree as the command working
33
- directory, then return the patch and evidence to the orchestrator. Never merge.
33
+ directory, leave every change uncommitted, and write two handoff files at the
34
+ worktree root: `PR_BODY.md` (the full PR body, transcripts pasted) and
35
+ `COMMIT_MSG.txt` (subject, body, issue ref, trailers). Name any path inside the
36
+ worktree that must not be committed. The orchestrator commits from those files;
37
+ they are never committed themselves. Never merge.
38
+
39
+ A sandboxed worker may find the shared `.git` and the linked `node_modules`
40
+ read-only and the network absent (the codex `workspace-write` sandbox does
41
+ this; see plegma#311). Run Vitest as
42
+ `node_modules/.bin/vitest run <files> --configLoader runner`, and if the
43
+ tree-sitter grammar prefetch hangs offline, verify through direct probes of the
44
+ built code and say so; the orchestrator re-runs the files outside the sandbox.
34
45
 
35
46
  When Git authority is granted, use one logical commit with an imperative,
36
47
  conventional-prefix subject of at most 50 characters, a blank line, and a
@@ -11,10 +11,11 @@ Every child process the runner spawns — `pi`, the MCP server, `node`, `npm`
11
11
  runs under a pinned environment: `HOME`, `USERPROFILE`, `PI_LENS_HOME`,
12
12
  `PILENS_DATA_DIR`, `PI_LENS_INSTALL_LOG` and `npm_config_cache` all inside the
13
13
  scratch root. `PI_LENS_INSTALL_LOG` is on that list by hard experience: it is
14
- what `scripts/warm-loader-cache.mjs` keys its install log on, `PI_LENS_HOME`
15
- does NOT redirect it, and the runner's first six runs — which pinned the pi-lens
16
- home but passed no environment to `npm` at all — put 41 records into the
17
- maintainer's real `~/.pi-lens/install.log` (#2619 review F1). The pack runs in a
14
+ what `scripts/warm-loader-cache.mjs` keys its install log on, with
15
+ `PI_LENS_HOME/install.log` as the fallback when it is unset, and the runner's
16
+ first six runs — which pinned the pi-lens home but passed no environment to
17
+ `npm` at all — put 41 records into the maintainer's real
18
+ `~/.pi-lens/install.log` (#2619 review F1). The pack runs in a
18
19
  `git archive HEAD` export, never the live checkout, because `npm pack` fires our
19
20
  own `prepack` (rewrites `package.json` + `package-lock.json`) and `prepare`
20
21
  (rebuilds `dist/`, downloads grammars, reinstalls git hooks).
@@ -61,6 +62,13 @@ arithmetic.
61
62
  | `mcp-stdio` | an MCP client speaking JSON-RPC to `dist/mcp/server.js` |
62
63
  | `git-install` | `pi install git:...` against a pushed ref |
63
64
 
65
+ Static packaging shape under `npm-pack` is NOT a matrix row: CI's
66
+ `ci.yml` `prod-install-build` job already runs `publint` (gating) on every
67
+ PR's packed tarball (#2700, the check #2587 was missing). `attw`
68
+ (arethetypeswrong) is not run there or here — the package ships no
69
+ `.d.ts` at all (`tsconfig.dist.json` sets `"declaration": false`, no
70
+ `types`/`exports` in `package.json`), so there is nothing for it to grade.
71
+
64
72
  ## The matrix
65
73
 
66
74
  | row id | feature | modality | entry point | pass criterion | witness | reuse | umbrella |
@@ -6,7 +6,7 @@ server's `initialize` handshake. Generated by `node scripts/server-capabilities.
6
6
  absent on the generating host are listed under *Unavailable* — run in a
7
7
  provisioned environment (the nightly) to capture those rows.
8
8
 
9
- _Last generated: 2026-08-21 on linux; 30 servers captured, 14 unavailable._
9
+ _Last generated: 2026-09-08 on linux; 30 servers captured, 18 unavailable._
10
10
 
11
11
  ## Diagnostic mode + navigation/edit operations
12
12
 
@@ -60,7 +60,7 @@ Legend: ✓ advertised, · not. **mode** = document diagnostics (`pull` = `textD
60
60
  - **deno**: callHierarchyProvider, codeActionProvider, codeLensProvider, completionProvider, definitionProvider, diagnosticProvider, documentFormattingProvider, documentHighlightProvider, documentSymbolProvider, executeCommandProvider, experimental, foldingRangeProvider, hoverProvider, implementationProvider, inlayHintProvider, notebookDocumentSync, referencesProvider, renameProvider, selectionRangeProvider, semanticTokensProvider, signatureHelpProvider, textDocumentSync, typeDefinitionProvider, workspace, workspaceSymbolProvider
61
61
  - **docker**: codeActionProvider, completionProvider, definitionProvider, documentFormattingProvider, documentHighlightProvider, documentLinkProvider, documentOnTypeFormattingProvider, documentRangeFormattingProvider, documentSymbolProvider, foldingRangeProvider, hoverProvider, renameProvider, signatureHelpProvider, textDocumentSync
62
62
  - **expert**: codeActionProvider, codeLensProvider, completionProvider, definitionProvider, documentFormattingProvider, documentSymbolProvider, executeCommandProvider, experimental, hoverProvider, referencesProvider, textDocumentSync, workspace, workspaceSymbolProvider
63
- - **fsharp**: callHierarchyProvider, codeActionProvider, codeLensProvider, completionProvider, definitionProvider, documentFormattingProvider, documentHighlightProvider, documentRangeFormattingProvider, documentSymbolProvider, foldingRangeProvider, hoverProvider, implementationProvider, inlayHintProvider, inlineValueProvider, referencesProvider, renameProvider, selectionRangeProvider, semanticTokensProvider, signatureHelpProvider, textDocumentSync, typeDefinitionProvider, workspaceSymbolProvider
63
+ - **fsharp**: callHierarchyProvider, codeActionProvider, codeLensProvider, completionProvider, definitionProvider, documentFormattingProvider, documentHighlightProvider, documentRangeFormattingProvider, documentSymbolProvider, foldingRangeProvider, hoverProvider, implementationProvider, inlayHintProvider, referencesProvider, renameProvider, selectionRangeProvider, semanticTokensProvider, signatureHelpProvider, textDocumentSync, typeDefinitionProvider, workspaceSymbolProvider
64
64
  - **gleam**: codeActionProvider, completionProvider, definitionProvider, documentFormattingProvider, documentHighlightProvider, documentSymbolProvider, foldingRangeProvider, hoverProvider, referencesProvider, renameProvider, signatureHelpProvider, textDocumentSync, typeDefinitionProvider, workspace
65
65
  - **go**: callHierarchyProvider, codeActionProvider, codeLensProvider, completionProvider, definitionProvider, documentFormattingProvider, documentHighlightProvider, documentLinkProvider, documentSymbolProvider, executeCommandProvider, experimental, foldingRangeProvider, hoverProvider, implementationProvider, inlayHintProvider, referencesProvider, renameProvider, selectionRangeProvider, semanticTokensProvider, signatureHelpProvider, textDocumentSync, typeDefinitionProvider, typeHierarchyProvider, workspace, workspaceSymbolProvider
66
66
  - **html**: colorProvider, definitionProvider, diagnosticProvider, documentFormattingProvider, documentHighlightProvider, documentLinkProvider, documentRangeFormattingProvider, documentSymbolProvider, foldingRangeProvider, hoverProvider, linkedEditingRangeProvider, referencesProvider, renameProvider, selectionRangeProvider, signatureHelpProvider, textDocumentSync
@@ -109,12 +109,12 @@ Toolchain/binary not installed here, so capabilities weren't captured
109
109
  (many are the toolchain-gated family tracked in #241):
110
110
 
111
111
  - @prisma/language-server
112
+ - @vue/language-server
112
113
  - PowerShell Editor Services (pwsh Start-EditorServices.ps1 -Stdio)
113
114
  - clangd
114
115
  - cmake-language-server
115
116
  - fish-lsp
116
117
  - haskell-language-server
117
- - intelephense
118
118
  - jdtls
119
119
  - jdtls + lombok javaagent
120
120
  - kotlin-language-server
@@ -122,3 +122,7 @@ Toolchain/binary not installed here, so capabilities weren't captured
122
122
  - ocamllsp
123
123
  - rust-analyzer
124
124
  - sourcekit-lsp
125
+ - typescript native (clean file)
126
+ - typescript native (tsc --lsp --stdio, TS7+)
127
+ - typescript-language-server
128
+ - typescript-language-server (clean file)
@@ -156,7 +156,7 @@ the cwd, falling back to the stateless snapshot read otherwise.
156
156
 
157
157
  ## Caps and boundaries
158
158
 
159
- - **≤6000 files, ≤512KB per file** (`WORD_INDEX_MAX_FILES`,
159
+ - **≤6000 files, ≤512KB per file** (the file cap is passed into the walk;
160
160
  `WORD_INDEX_MAX_BYTES` in `clients/word-index.ts`) — shared by every
161
161
  build path and the per-edit update path alike.
162
162
  - **Deletions aren't plumbed at the per-edit seam** — the cascade only ever
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-lens",
3
- "version": "4.1.4",
3
+ "version": "4.1.5",
4
4
  "type": "module",
5
5
  "description": "Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo",
6
6
  "repository": {
@@ -21,10 +21,12 @@
21
21
  "prepack": "node scripts/strip-dev-deps-for-pack.mjs --strip",
22
22
  "postpack": "node scripts/strip-dev-deps-for-pack.mjs --restore",
23
23
  "lint": "tsc --project tsconfig.json && npm run lint:js",
24
- "lint:js": "oxlint --deny-warnings --ignore-pattern \"tests/**/*.ts\" --ignore-pattern \"tests/**/*.tsx\" --ignore-pattern \"**/*.d.mts\" --ignore-pattern \"tests/fixtures/autofix-smoke/**\" --ignore-pattern \"tests/fixtures/format-smoke/**\" --ignore-pattern \"tests/fixtures/tool-smoke/**\" --ignore-pattern \"tests/fixtures/call-graph/javascript/**\" --ignore-pattern \"tests/fixtures/function-facts/javascript-parameter.js\" .",
24
+ "lint:js": "oxlint --deny-warnings --import-plugin --promise-plugin -D block-scoped-var -D import/default -D import/namespace -D import/no-absolute-path -D import/no-empty-named-blocks -D import/no-named-as-default -D import/no-self-import -D no-extend-native -D no-extra-bind -D no-implied-eval -D no-new -D no-unexpected-multiline -D no-useless-constructor -D oxc/approx-constant -D oxc/misrefactored-assign-op -D oxc/no-accumulating-spread -D oxc/no-async-endpoint-handlers -D oxc/no-this-in-exported-function -D promise/no-callback-in-promise -D promise/no-multiple-resolved -D promise/no-new-statics -D promise/valid-params -D typescript/no-confusing-non-null-assertion -D typescript/no-extraneous-class -D typescript/no-unnecessary-type-constraint -D typescript/no-unsafe-enum-comparison -D unicorn/no-accessor-recursion -D unicorn/no-array-fill-with-reference-type -D unicorn/no-confusing-array-with -D unicorn/no-instanceof-builtins -D unicorn/prefer-array-flat-map -D unicorn/require-module-specifiers --ignore-pattern \"tests/**/*.ts\" --ignore-pattern \"tests/**/*.tsx\" --ignore-pattern \"**/*.d.mts\" --ignore-pattern \"tests/fixtures/autofix-smoke/**\" --ignore-pattern \"tests/fixtures/format-smoke/**\" --ignore-pattern \"tests/fixtures/tool-smoke/**\" --ignore-pattern \"tests/fixtures/call-graph/javascript/**\" --ignore-pattern \"tests/fixtures/function-facts/javascript-parameter.js\" .",
25
+ "lint:js:advisory": "node scripts/lint-js-advisory.mjs --deny-warnings --format default -D correctness -D suspicious -D perf --import-plugin --promise-plugin --type-aware -A no-underscore-dangle -A no-await-in-loop -A unicorn/no-array-sort -A unicorn/consistent-function-scoping -A promise/no-promise-in-callback -A no-useless-call -A import/no-unassigned-import -A no-unmodified-loop-condition --ignore-pattern \"**/*.d.mts\" --ignore-pattern \"tests/fixtures/autofix-smoke/**\" --ignore-pattern \"tests/fixtures/format-smoke/**\" --ignore-pattern \"tests/fixtures/tool-smoke/**\" --ignore-pattern \"tests/fixtures/call-graph/javascript/**\" --ignore-pattern \"tests/fixtures/function-facts/javascript-parameter.js\" .",
25
26
  "depcruise:check": "depcruise --validate --config .dependency-cruiser.cjs --ignore-known .dependency-cruiser-known-violations.json --output-type err index.ts \"clients/**/*.ts\"",
26
27
  "fmt": "oxfmt",
27
28
  "fmt:check": "oxfmt --check",
29
+ "taplo": "taplo check $(git ls-files '*.toml' ':!tests/fixtures/**') && taplo fmt --check $(git ls-files '*.toml' ':!tests/fixtures/**' ':!config/ruff/core.toml') # taplo 0.7.0 panics on this file's comments; lint it above",
28
30
  "watch": "tsc --watch",
29
31
  "test": "node scripts/with-test-lock.mjs -- vitest run",
30
32
  "test:unit": "node scripts/with-test-lock.mjs -- vitest run",
@@ -35,6 +37,7 @@
35
37
  "check": "node scripts/check-extensions.mjs",
36
38
  "selftest:install": "node scripts/install-selftest.mjs",
37
39
  "check:lockfile": "node scripts/check-lockfile-sync.mjs",
40
+ "knip": "node scripts/run-knip.mjs",
38
41
  "check:grammars": "node scripts/check-grammar-provenance.mjs",
39
42
  "check:grammar-load": "node scripts/check-grammar-load.mjs",
40
43
  "audit:tree-sitter": "node scripts/audit-tree-sitter-rules.mjs",
@@ -138,20 +141,8 @@
138
141
  "./skills"
139
142
  ]
140
143
  },
141
- "knip": {
142
- "entry": [
143
- "index.ts"
144
- ],
145
- "project": [
146
- "**/*.ts",
147
- "!**/*.test.ts"
148
- ],
149
- "ignore": [
150
- "**/*.test.ts"
151
- ]
152
- },
153
144
  "allowScripts": {
154
- "@ast-grep/cli@0.45.2": true,
145
+ "@ast-grep/cli@0.45.3": true,
155
146
  "@google/genai@1.52.0": true,
156
147
  "protobufjs@7.6.5": true
157
148
  }
@@ -13,5 +13,7 @@
13
13
  # dictionary here; every entry must have a provenance comment.
14
14
 
15
15
  [default.extend-identifiers]
16
- dito = "dito" # #967 — real model/vendor namespace (provider-failover normalization prefix), not a misspelling of "ditto"
17
- unparseable = "unparseable" # #967 — valid English spelling ("un-parseable"), not a typo of "unparsable"
16
+ # #967 — real model/vendor namespace (provider-failover normalization prefix), not a misspelling of "ditto"
17
+ dito = "dito"
18
+ # #967 — valid English spelling ("un-parseable"), not a typo of "unparsable"
19
+ unparseable = "unparseable"
@@ -54,7 +54,7 @@ import * as path from "node:path";
54
54
  * @param {import("node:fs").Dirent} entry
55
55
  * @returns {"file" | "dir" | "other"}
56
56
  */
57
- export function resolvedEntryKind(fullPath, entry) {
57
+ function resolvedEntryKind(fullPath, entry) {
58
58
  if (entry.isSymbolicLink()) {
59
59
  try {
60
60
  const stats = fs.statSync(fullPath);
@@ -281,5 +281,5 @@ export function appendBounded(
281
281
  return lines.slice(Math.max(0, lines.length - max));
282
282
  }
283
283
 
284
- /** Cap for `~/.pi-lens/install.log`. */
284
+ /** Cap for the install log under `PI_LENS_HOME` or `~/.pi-lens`. */
285
285
  export const INSTALL_LOG_MAX_LINES = 100;
@@ -44,7 +44,8 @@ function log(message) {
44
44
  }
45
45
 
46
46
  /**
47
- * Append one JSONL record to ~/.pi-lens/install.log.
47
+ * Append one JSONL record to PI_LENS_INSTALL_LOG, or install.log under
48
+ * PI_LENS_HOME (falling back to ~/.pi-lens when neither is set).
48
49
  *
49
50
  * pi's install output scrolls away, so without this there is no record that the
50
51
  * warm ran, was skipped, or failed. One bounded line per install keeps the
@@ -54,10 +55,11 @@ function log(message) {
54
55
  function record(entry) {
55
56
  try {
56
57
  const override = process.env.PI_LENS_INSTALL_LOG;
58
+ const home = process.env.PI_LENS_HOME?.trim();
57
59
  const file =
58
60
  typeof override === "string" && override.length > 0
59
61
  ? override
60
- : path.join(os.homedir(), ".pi-lens", "install.log");
62
+ : path.join(home || path.join(os.homedir(), ".pi-lens"), "install.log");
61
63
  fs.mkdirSync(path.dirname(file), { recursive: true });
62
64
  const existing = fs.existsSync(file)
63
65
  ? fs.readFileSync(file, "utf8").split("\n")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@herbertgao/pi-extensions",
3
- "version": "2026.9.1",
3
+ "version": "2026.9.2",
4
4
  "description": "Aggregate installer for HerbertGao-maintained Pi extensions.",
5
5
  "keywords": [
6
6
  "extensions",
@@ -56,7 +56,7 @@
56
56
  "@modelcontextprotocol/sdk": "^1.29.0",
57
57
  "@mozilla/readability": "^0.6.0",
58
58
  "@napi-rs/keyring": "^1.3.0",
59
- "@narumitw/pi-btw": "0.57.0",
59
+ "@narumitw/pi-btw": "0.58.0",
60
60
  "@narumitw/pi-caffeinate": "0.49.7",
61
61
  "@narumitw/pi-tui-kit": "^0.59.0",
62
62
  "@noble/ed25519": "^3.1.0",
@@ -90,7 +90,7 @@
90
90
  "open": "^10.2.0",
91
91
  "p-limit": "^6.1.0",
92
92
  "pi-footer": "0.5.1",
93
- "pi-lens": "4.1.4",
93
+ "pi-lens": "4.1.5",
94
94
  "pi-mcp-adapter": "2.32.1",
95
95
  "pi-web-access": "0.28.0",
96
96
  "pidusage": "^4.0.1",