@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
@@ -12,7 +12,7 @@
12
12
  *
13
13
  * builtin -> global -> project root -> nested-project -> env -> cli -> host
14
14
  *
15
- * It is `config-core`'s own `TIER_PRECEDENCE`, not a second ordering: this
15
+ * It is `config-core`'s own `SOURCE_TIERS`, not a second ordering: this
16
16
  * module places files into tiers and the core sorts them. `docs/configuration.md`
17
17
  * documents the same order once, and `tests/clients/config-resolve.test.ts`
18
18
  * walks it.
@@ -151,7 +151,7 @@ function collectDocuments(dir, locations, tier, onReadError) {
151
151
  * is `nested-project`, which is what makes "nearest wins, per field" fall out
152
152
  * of the core's tier precedence instead of being re-implemented here.
153
153
  */
154
- export function collectPiLensConfigDocuments(options) {
154
+ function collectPiLensConfigDocuments(options) {
155
155
  const documents = [];
156
156
  const { globalDir, onReadError } = options;
157
157
  const globalConfigPath = canonicalGlobalFile(options);
@@ -408,7 +408,7 @@ function assignRaw(target, key, value) {
408
408
  * half-migrated the file it was written in is the defect this whole change
409
409
  * exists to remove.
410
410
  */
411
- export function configSources(documents) {
411
+ function configSources(documents) {
412
412
  const sources = [];
413
413
  for (const document of documents) {
414
414
  const value = document.value;
@@ -576,7 +576,7 @@ function record(input) {
576
576
  * duplicate notice about a config that failed to load is noise, while silence
577
577
  * about it is the failure mode this module exists to prevent.
578
578
  */
579
- export function configRecordOwner(record) {
579
+ function configRecordOwner(record) {
580
580
  const key = record.canonicalKey ?? record.key;
581
581
  if (key.length === 0)
582
582
  return undefined;
@@ -43,7 +43,7 @@ import { LEGACY_ROOT_LSP_KEYS, LSP_NAMESPACE_KEY } from "./config-locations.js";
43
43
  import { DENY_KEY, } from "./config-core/schema.js";
44
44
  import { flagConfigSectionKeys, GLOBAL_NON_FLAG_CONFIG_SECTIONS, LENS_FLAGS, PROJECT_FOREIGN_CONFIG_NAMESPACES, PROJECT_NON_FLAG_CONFIG_SECTIONS, } from "./lens-flag-registry.js";
45
45
  /** The JSON Schema dialect the published artifact declares. */
46
- export const CONFIG_SCHEMA_DIALECT = "https://json-schema.org/draft/2020-12/schema";
46
+ const CONFIG_SCHEMA_DIALECT = "https://json-schema.org/draft/2020-12/schema";
47
47
  /**
48
48
  * A node the schema says nothing else about. `config-core` treats a node with
49
49
  * no `type` as opaque and walks its children OPEN, so an undeclared section
@@ -109,7 +109,6 @@ export { LSP_KEY_TYPES };
109
109
  const LSP_KEY_DENY = {
110
110
  disabledServers: "array-union",
111
111
  };
112
- export { LSP_KEY_DENY };
113
112
  /** The `x-deny` annotation for a key, or nothing when it carries no denial. */
114
113
  function denyAnnotation(key) {
115
114
  const policy = LSP_KEY_DENY[key];
@@ -161,6 +160,44 @@ function buildConfigSchema() {
161
160
  for (const key of PROJECT_FOREIGN_CONFIG_NAMESPACES) {
162
161
  properties[key] ??= opaque("experimental");
163
162
  }
163
+ for (const key of [
164
+ "knip",
165
+ "jscpd",
166
+ "madge",
167
+ "gitleaks",
168
+ "govulncheck",
169
+ "deadCode",
170
+ "complexity",
171
+ ]) {
172
+ properties[key] = {
173
+ type: "object",
174
+ additionalProperties: true,
175
+ properties: {
176
+ enabled: { type: "boolean", [STABILITY_TIER_KEY]: "experimental" },
177
+ },
178
+ [STABILITY_TIER_KEY]: "stable",
179
+ };
180
+ }
181
+ properties.startup = {
182
+ type: "object",
183
+ additionalProperties: true,
184
+ properties: {
185
+ mode: {
186
+ type: "string",
187
+ enum: ["quick", "full", "minimal"],
188
+ [STABILITY_TIER_KEY]: "experimental",
189
+ },
190
+ scans: {
191
+ type: "object",
192
+ additionalProperties: true,
193
+ properties: {
194
+ enabled: { type: "boolean", [STABILITY_TIER_KEY]: "experimental" },
195
+ },
196
+ [STABILITY_TIER_KEY]: "experimental",
197
+ },
198
+ },
199
+ [STABILITY_TIER_KEY]: "stable",
200
+ };
164
201
  // The four legacy ROOT LSP keys. Still accepted for their deprecation window
165
202
  // (#2418 registry), and `experimental` because they are scheduled for
166
203
  // removal — a `stable` tier on a key with a `removeNotBefore` would be two
@@ -28,7 +28,3 @@ export function logDeadCodeScan(event) {
28
28
  return;
29
29
  writer.log({ ts: new Date().toISOString(), ...event });
30
30
  }
31
- /** Resolve once all enqueued dead-code writes are on disk (tests/shutdown). */
32
- export function flushDeadCodeLog() {
33
- return writer.flush();
34
- }
@@ -128,7 +128,7 @@ export function computeStrictAnchor(args) {
128
128
  });
129
129
  }
130
130
  /** Intent-level anchor — see module doc. Used for defer/flagged/suppress. */
131
- export function computeWeakAnchor(args) {
131
+ function computeWeakAnchor(args) {
132
132
  return stableFindingId("ddw:", {
133
133
  cwd: args.cwd,
134
134
  filePath: args.filePath,
@@ -23,7 +23,7 @@ export function getDiagnosticLogger() {
23
23
  }
24
24
  return _logger;
25
25
  }
26
- export function createDiagnosticLogger() {
26
+ function createDiagnosticLogger() {
27
27
  // Lazy filePath: the log file is keyed on the current date, resolved per
28
28
  // drain so a long-lived logger rolls over at midnight. A daily rollover
29
29
  // is NOT a size bound (#2505): one very busy day, or a long-lived
@@ -33,7 +33,3 @@ export function observeRunnerLatency(options) {
33
33
  export function resetObservedRunnerLatency() {
34
34
  slowRunners.clear();
35
35
  }
36
- /** Test-only inspection without exposing mutable state. */
37
- export function observedRunnerTierForTests(projectRoot, runnerId) {
38
- return classifyObservedRunner(projectRoot, runnerId);
39
- }
@@ -1237,29 +1237,3 @@ async function runRunner(ctx, runner, defaultSemantic) {
1237
1237
  }
1238
1238
  }
1239
1239
  // --- Simple Integration Helper ---
1240
- /**
1241
- * @internal
1242
- * Low-level dispatch entry point. Use `dispatchLint` from `./integration.js` instead —
1243
- * that version provides session-persistent baselines and FactStore.
1244
- * This function creates an ephemeral FactStore per call; facts do not persist across calls.
1245
- */
1246
- export async function dispatchLint(filePath, cwd, pi, facts, registry) {
1247
- // By default, only run BLOCKING rules for fast feedback on file write
1248
- const ctx = createDispatchContext(filePath, cwd, pi, facts, true);
1249
- // Get runners for this file kind
1250
- if (!ctx.kind)
1251
- return "";
1252
- const runners = registry.getForKind(ctx.kind, ctx.filePath);
1253
- if (runners.length === 0) {
1254
- return "";
1255
- }
1256
- // Create groups from registered runners (all in fallback mode)
1257
- const groups = [
1258
- {
1259
- mode: "fallback",
1260
- runnerIds: runners.map((r) => r.id),
1261
- },
1262
- ];
1263
- const result = await dispatchForFile(ctx, groups, registry);
1264
- return result.output;
1265
- }
@@ -1489,7 +1489,9 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
1489
1489
  try {
1490
1490
  const snapshots = (await lspService.getCapabilitySnapshots?.(neighborPath)) ?? [];
1491
1491
  const tier = classifyCascadeWaitTier(lspService, neighborPath, snapshots);
1492
- if (tier === "tier3-silent" || tier === "collect-later") {
1492
+ if (tier === "tier3-silent" ||
1493
+ tier === "collect-later" ||
1494
+ tier === "diagnostics-unsupported") {
1493
1495
  const spawnedForTouch = await lspService.getClientForFile(neighborPath);
1494
1496
  if (spawnedForTouch) {
1495
1497
  // Sampled BEFORE the touchFile notify: a publish landing
@@ -1505,6 +1507,21 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
1505
1507
  source: "cascade",
1506
1508
  clientScope: "primary",
1507
1509
  });
1510
+ if (tier === "diagnostics-unsupported") {
1511
+ logCascade({
1512
+ phase: "cascade_skip",
1513
+ filePath,
1514
+ neighborFile: neighborPath,
1515
+ durationMs: Date.now() - neighborStart,
1516
+ lspServerCount: configuredServerCount,
1517
+ coldSnapshot: isColdSnapshot,
1518
+ metadata: {
1519
+ serverId: spawnedForTouch.client.serverId,
1520
+ waitTier: tier,
1521
+ },
1522
+ });
1523
+ return undefined;
1524
+ }
1508
1525
  recordOutstandingCascadeTouch({
1509
1526
  filePath: neighborPath,
1510
1527
  serverId: spawnedForTouch.client.serverId,
@@ -9,9 +9,3 @@ export function warmDispatchIntegration() {
9
9
  export function loadDispatchIntegration() {
10
10
  return warmDispatchIntegration();
11
11
  }
12
- /** Test-only reset; production sessions intentionally retain the promise
13
- * across successful loads (a rejection already evicts itself — see
14
- * `createLazyImport`). */
15
- export function resetDispatchIntegrationForTests() {
16
- lazyIntegration.resetForTests();
17
- }
@@ -1,7 +1,7 @@
1
1
  /** Turn-end handoff for runners moved off the post-write critical path. */
2
2
  import { incrementDegradationCount } from "../degradation-ledger.js";
3
3
  const pending = [];
4
- export const MAX_PENDING_RUNNER_FINDINGS = 50;
4
+ const MAX_PENDING_RUNNER_FINDINGS = 50;
5
5
  export function deferRunnerFindings(entry) {
6
6
  const tracked = { ...entry, settled: false };
7
7
  // Attach exactly once at ownership time. Re-attaching at every turn end
@@ -276,6 +276,3 @@ export const TOOL_PLANS = Object.fromEntries(Object.entries(LANGUAGE_CAPABILITY_
276
276
  export function getToolPlan(kind) {
277
277
  return TOOL_PLANS[kind];
278
278
  }
279
- export function getAllToolPlans() {
280
- return TOOL_PLANS;
281
- }
@@ -3,8 +3,8 @@ import { isTestSuiteOrganizer } from "./framework-call-noise.js";
3
3
  // projects without a config. Project-specific overrides are read from the
4
4
  // per-dispatch context; these mutable fallbacks exist for tests/legacy direct
5
5
  // rule evaluation only.
6
- export const DEFAULT_HIGH_COMPLEXITY_THRESHOLD = 15;
7
- export const DEFAULT_HIGH_COMPLEXITY_DEPTH_THRESHOLD = 6;
6
+ const DEFAULT_HIGH_COMPLEXITY_THRESHOLD = 15;
7
+ const DEFAULT_HIGH_COMPLEXITY_DEPTH_THRESHOLD = 6;
8
8
  let ccThreshold = DEFAULT_HIGH_COMPLEXITY_THRESHOLD;
9
9
  let depthThreshold = DEFAULT_HIGH_COMPLEXITY_DEPTH_THRESHOLD;
10
10
  function isPositiveFiniteThreshold(value) {
@@ -3,7 +3,7 @@ import { isTestFrameworkNoiseCall, isTestSuiteOrganizer, } from "./framework-cal
3
3
  // projects without a config. Project-specific overrides are read from the
4
4
  // per-dispatch context; this mutable fallback exists for tests/legacy direct
5
5
  // rule evaluation only.
6
- export const DEFAULT_HIGH_FAN_OUT_THRESHOLD = 20;
6
+ const DEFAULT_HIGH_FAN_OUT_THRESHOLD = 20;
7
7
  let fanOutThreshold = DEFAULT_HIGH_FAN_OUT_THRESHOLD;
8
8
  /** Override fallback threshold for tests/legacy direct rule evaluation. */
9
9
  export function setHighFanOutThreshold(n) {
@@ -184,7 +184,9 @@ export async function loadSg() {
184
184
  }
185
185
  })();
186
186
  sgLoadPromise = task;
187
- task.finally(() => {
187
+ // `task` never rejects (the IIFE catches), so the derived promise is inert;
188
+ // `void` records that no consumer awaits it (oxlint no-floating-promises).
189
+ void task.finally(() => {
188
190
  if (sgLoadPromise === task)
189
191
  sgLoadPromise = undefined;
190
192
  });
@@ -97,6 +97,10 @@ async function resolveCompiler(absPath, cwd) {
97
97
  flavor: "msvc",
98
98
  };
99
99
  }
100
+ // Mirrors the deliberate global-PATH probes in utils/runner-helpers.ts:
101
+ // this never resolves a config file or a target path, so there is no cwd
102
+ // for it to get wrong.
103
+ // cwd-exempt: presence probe only -- `cl` with no args and no target file, used solely to detect whether MSVC is on PATH
100
104
  const clProbe = await safeSpawnAsync("cl", [], { timeout: 5000 });
101
105
  if (!clProbe.error && clProbe.status !== null) {
102
106
  return {
@@ -489,7 +489,7 @@ const OUR_RENDER_FLAGS = ["--output-dir"];
489
489
  *
490
490
  * Returns the matched flag (for the message) or null.
491
491
  */
492
- export function rejectedOurInvocation(output) {
492
+ function rejectedOurInvocation(output) {
493
493
  if (!output)
494
494
  return null;
495
495
  const complaint = /unknown flag|unknown shorthand flag|flag provided but not defined|unknown command|unrecognized (?:flag|option)/i.test(output);
@@ -86,6 +86,7 @@ const oxlintRunner = {
86
86
  }
87
87
  // Run oxlint (or Vite+'s vp lint wrapper) on the file.
88
88
  const result = await safeSpawnAsync(cmd, args, {
89
+ cwd,
89
90
  timeout: 30000,
90
91
  maxOutputBytes: MAX_OXLINT_OUTPUT_BYTES,
91
92
  });
@@ -26,8 +26,10 @@ const PS_TIMEOUT_MS = 30000;
26
26
  * `spawnFailure.kind` and the errno on `error` — which is what lets the
27
27
  * availability policy tell a stall from an absence.
28
28
  */
29
- function spawnPs(cmd, args, timeoutMs = PS_TIMEOUT_MS) {
29
+ function spawnPs(cmd, args, options = {}) {
30
+ const { timeoutMs = PS_TIMEOUT_MS, cwd } = options;
30
31
  return safeSpawnAsync(cmd, args, {
32
+ cwd,
31
33
  timeout: timeoutMs,
32
34
  resourceLabel: "psscriptanalyzer",
33
35
  });
@@ -124,6 +126,7 @@ async function resolvePowerShellCmd() {
124
126
  for (const candidate of ["pwsh", "powershell"]) {
125
127
  const sampler = startHostStallSampler();
126
128
  const startedAt = Date.now();
129
+ // cwd-exempt: global interpreter-presence probe ("is pwsh/powershell on PATH at all"), not tied to any project
127
130
  const result = await spawnPs(candidate, [
128
131
  "-NoProfile",
129
132
  "-NonInteractive",
@@ -175,6 +178,7 @@ async function checkModuleAvailable(cmd) {
175
178
  return memo;
176
179
  const sampler = startHostStallSampler();
177
180
  const startedAt = Date.now();
181
+ // cwd-exempt: global module-presence probe -- PSScriptAnalyzer resolves from PowerShell's module search path, not a per-project install
178
182
  const result = await spawnPs(cmd, [
179
183
  "-NoProfile",
180
184
  "-NonInteractive",
@@ -303,7 +307,7 @@ const psScriptAnalyzerRunner = {
303
307
  tmpScript,
304
308
  "-FilePath",
305
309
  absPath,
306
- ]);
310
+ ], { timeoutMs: PS_TIMEOUT_MS, cwd });
307
311
  const hostStallMs = sampler.stop();
308
312
  const elapsedMs = Date.now() - startedAt;
309
313
  // The two probes above use `-Command`, which PowerShell's execution
@@ -63,7 +63,7 @@ const ruffRunner = {
63
63
  // builder, so the package-owned fallback config can never drift.
64
64
  const configArgs = ruffConfigArgs(cwd);
65
65
  // Step 1: Capture diagnostics (before fixing) — teaching signal for the agent
66
- const checkResult = await safeSpawnAsync(cmd, ["check", "--output-format", "json", ...configArgs, ctx.filePath], { timeout: 30000 });
66
+ const checkResult = await safeSpawnAsync(cmd, ["check", "--output-format", "json", ...configArgs, ctx.filePath], { cwd, timeout: 30000 });
67
67
  const raw = stripAnsi(checkResult.stdout + checkResult.stderr);
68
68
  const diagnostics = parseRuffJson(checkResult.stdout || "", ctx.filePath);
69
69
  const parsedDiagnostics = diagnostics.length > 0 ? diagnostics : parseRuffOutput(raw, ctx.filePath);
@@ -149,7 +149,7 @@ const shellcheckRunner = {
149
149
  args.push("--severity", "info");
150
150
  }
151
151
  args.push(ctx.filePath);
152
- const result = await safeSpawnAsync(cmd, args, { timeout: 15000 });
152
+ const result = await safeSpawnAsync(cmd, args, { cwd, timeout: 15000 });
153
153
  // shellcheck exits with code 1 if issues found, 0 if clean
154
154
  if (result.status === 0 && !result.stdout?.trim()) {
155
155
  return { status: "succeeded", diagnostics: [], semantic: "none" };
@@ -82,15 +82,17 @@ const spellcheckRunner = {
82
82
  priority: PRIORITY.DOC_QUALITY,
83
83
  skipTestFiles: false, // Check docs in test files too
84
84
  async run(ctx) {
85
+ const cwd = ctx.cwd || process.cwd();
85
86
  // Skip if typos-cli is not installed
86
- if (!(await typos.isAvailableAsync(ctx.cwd || process.cwd()))) {
87
+ if (!(await typos.isAvailableAsync(cwd))) {
87
88
  return { status: "skipped", diagnostics: [], semantic: "none" };
88
89
  }
89
90
  // Run typos-cli with JSON output
90
91
  // --format json: Output JSON Lines
91
92
  // --exclude <pattern>: Could be used to exclude code blocks if needed
92
93
  const args = ["--format", "json", ctx.filePath];
93
- const result = await safeSpawnAsync(typos.getCommand(ctx.cwd || process.cwd()), args, {
94
+ const result = await safeSpawnAsync(typos.getCommand(cwd), args, {
95
+ cwd,
94
96
  timeout: 15000,
95
97
  });
96
98
  // #1816: typos-cli exits 0 clean, 2 with typos found, and 1 on an ERROR
@@ -5,13 +5,12 @@
5
5
  * Supports the common `file:line:col: message` format used by most linters.
6
6
  */
7
7
  import { stripAnsi } from "../../../sanitize.js";
8
- import { escapeRegExp } from "../../../string-utils.js";
9
8
  import { getAutofixCapability } from "../../../tool-policy.js";
10
9
  /**
11
10
  * Create a parser for line-based tool output.
12
11
  * Common format: file:line:col: message (with variations)
13
12
  */
14
- export function createLineParser(config) {
13
+ function createLineParser(config) {
15
14
  return (raw, filePath) => {
16
15
  const diagnostics = [];
17
16
  // Optionally strip ANSI codes (for tools that output colored text)
@@ -81,68 +80,6 @@ export const parseGoVetOutput = createLineParser({
81
80
  generateId: (m) => `go-vet-${m[2]}`,
82
81
  defectClass: "correctness",
83
82
  });
84
- /**
85
- * Parse Biome output: file:line:col message (category)
86
- * With autofix support for fix suggestions
87
- */
88
- export function createBiomeParser(_autofix = false) {
89
- const biomeAutofix = getAutofixCapability("biome");
90
- return createLineParser({
91
- tool: "biome",
92
- regex: /^(.+?):(\d+):(\d+)\s+(.+?)\s*\((.+?)\)/,
93
- extractMessage: (m) => `${m[5]}: ${m[4]}`, // category: message
94
- extractRule: (m) => m[5],
95
- generateId: (m) => `biome-${m[2]}-${m[5]}`,
96
- getSeverity: (line) => (line.includes("error") ? "error" : "warning"),
97
- fixable: true,
98
- autoFixAvailable: biomeAutofix?.safePipelineAutofix ?? false,
99
- fixKind: biomeAutofix?.fixKind === "none" ? undefined : biomeAutofix?.fixKind,
100
- });
101
- }
102
- // Backward-compatible default biome parser
103
- export const parseBiomeOutput = createBiomeParser(false);
104
83
  // =============================================================================
105
84
  // GENERIC PARSER FACTORY
106
85
  // =============================================================================
107
- /**
108
- * Create a simple parser for tools using standard file:line:col format.
109
- * Format variations: :line:col:, line:col, (line,col), etc.
110
- */
111
- export function createSimpleParser(tool, options = {}) {
112
- const sep = options.separator ?? ":";
113
- const severity = options.severity ?? "warning";
114
- const fixable = options.fixable ?? false;
115
- // Build regex based on separator type
116
- const escapedSep = sep === " " ? "\\s+" : escapeRegExp(sep);
117
- const regex = options.includesFileName
118
- ? new RegExp(`^(.+?)${escapedSep}(\\d+)${escapedSep}(\\d+)${escapedSep}(.+)`)
119
- : new RegExp(`^(\\d+)${escapedSep}(\\d+)${escapedSep}(.+)`);
120
- return (raw, filePath) => {
121
- const diagnostics = [];
122
- const lines = raw.split("\n").filter((l) => l.trim());
123
- for (const line of lines) {
124
- const match = line.match(regex);
125
- if (!match)
126
- continue;
127
- const lineNum = options.includesFileName
128
- ? parseInt(match[2], 10)
129
- : parseInt(match[1], 10);
130
- const colNum = options.includesFileName
131
- ? parseInt(match[3], 10)
132
- : parseInt(match[2], 10);
133
- const message = options.includesFileName ? match[4] : match[3];
134
- diagnostics.push({
135
- id: `${tool}-${lineNum}`,
136
- message: message.trim(),
137
- filePath,
138
- line: lineNum,
139
- column: colNum,
140
- severity,
141
- semantic: severity === "error" ? "blocking" : "warning",
142
- tool,
143
- fixable,
144
- });
145
- }
146
- return diagnostics;
147
- };
148
- }
@@ -13,24 +13,3 @@ export function readFileContent(filePath) {
13
13
  return undefined;
14
14
  }
15
15
  }
16
- /**
17
- * Parse common tool output line into a diagnostic
18
- * Format: file:line:col message (category)
19
- */
20
- export function parseToolLine(line, toolName, filePath) {
21
- const match = line.match(/^(.+?):(\d+):(\d+)\s+(.+?)\s*\((.+?)\)/);
22
- if (!match)
23
- return null;
24
- return {
25
- id: `${toolName}-${match[2]}-${match[5]}`,
26
- message: `${match[5]}: ${match[4]}`,
27
- filePath,
28
- line: parseInt(match[2], 10),
29
- column: parseInt(match[3], 10),
30
- severity: line.includes("error") ? "error" : "warning",
31
- semantic: "warning",
32
- tool: toolName,
33
- rule: match[5],
34
- fixable: false,
35
- };
36
- }
@@ -19,14 +19,7 @@ import { BoundedLruCache } from "../../bounded-cache.js";
19
19
  import { compareOrdinal } from "../../string-utils.js";
20
20
  // --- Constants ---
21
21
  /** Overly broad patterns that match everything (cause false positive explosions) */
22
- export const OVERLY_BROAD_PATTERNS = [
23
- "$NAME",
24
- "$FIELD",
25
- "$_",
26
- "$X",
27
- "$VAR",
28
- "$EXPR",
29
- ];
22
+ const OVERLY_BROAD_PATTERNS = ["$NAME", "$FIELD", "$_", "$X", "$VAR", "$EXPR"];
30
23
  /** Maximum complexity score for rules in blockingOnly mode */
31
24
  export const MAX_BLOCKING_RULE_COMPLEXITY = 8;
32
25
  /**
@@ -126,7 +119,7 @@ export function loadYamlRulesFresh(ruleDir, severityFilter) {
126
119
  cache.set(ruleDir, { rules, signature });
127
120
  return rules;
128
121
  }
129
- export function getCachedRules(ruleDir, severityFilter) {
122
+ function getCachedRules(ruleDir, severityFilter) {
130
123
  if (!fs.existsSync(ruleDir)) {
131
124
  return [];
132
125
  }
@@ -179,17 +172,6 @@ export function isOverlyBroadPattern(pattern) {
179
172
  return true;
180
173
  return /^\$[A-Z_]+$/i.test(pattern.trim());
181
174
  }
182
- export function isValidCondition(condition) {
183
- if (!condition)
184
- return false;
185
- if (condition.all !== undefined && condition.all.length === 0)
186
- return false;
187
- if (condition.any !== undefined && condition.any.length === 0)
188
- return false;
189
- if (isOverlyBroadPattern(condition.pattern))
190
- return false;
191
- return true;
192
- }
193
175
  export function isStructuredRule(rule) {
194
176
  if (!rule.rule)
195
177
  return false;
@@ -54,6 +54,7 @@ const yamllintRunner = {
54
54
  if (!cmd)
55
55
  return { status: "skipped", diagnostics: [], semantic: "none" };
56
56
  const result = await safeSpawnAsync(cmd, ["-f", "parsable", ctx.filePath], {
57
+ cwd,
57
58
  timeout: 15000,
58
59
  });
59
60
  // #1816: this runner read `result.status` zero times, so a yamllint
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Shared formatting utilities for the dispatch system.
3
3
  */
4
- export const EMOJI = {
4
+ const EMOJI = {
5
5
  blocking: "🔴",
6
6
  warning: "🟡",
7
7
  fixed: "✅",
@@ -12,7 +12,7 @@ export const EMOJI = {
12
12
  /**
13
13
  * Format a single diagnostic for display
14
14
  */
15
- export function formatDiagnostic(d) {
15
+ function formatDiagnostic(d) {
16
16
  const line = d.line ? `L${d.line}: ` : "";
17
17
  const indented = d.message.split("\n").join("\n ");
18
18
  const fix = d.fixSuggestion ? `\n 💡 Fix: ${d.fixSuggestion}` : "";
@@ -30,10 +30,3 @@ export function logDispositionEvent(entry) {
30
30
  }
31
31
  writer.log({ ts: new Date().toISOString(), ...entry });
32
32
  }
33
- export function getDispositionLogPath() {
34
- return DISPOSITION_LOG_FILE;
35
- }
36
- /** Resolve once all enqueued disposition writes are on disk. */
37
- export function flushDispositionLog() {
38
- return writer.flush();
39
- }
@@ -88,14 +88,14 @@ export function startEventLoopMonitor(resolutionMs = 20) {
88
88
  * every block that tier cares about while keeping sub-second jitter — where
89
89
  * measurement slop rivals the signal — out of the verdict.
90
90
  */
91
- export const STALL_CLASSIFY_FLOOR_MS = 1000;
91
+ const STALL_CLASSIFY_FLOOR_MS = 1000;
92
92
  /**
93
93
  * Slack allowed to the CPU budget before a block counts as unaccounted.
94
94
  * `process.cpuUsage()` and the histogram are sampled at slightly different
95
95
  * instants and the histogram quantizes, so a block whose CPU coverage is
96
96
  * within this much of exact is called `cpu-accounted`, not a stall.
97
97
  */
98
- export const STALL_CLASSIFY_SLOP_MS = 250;
98
+ const STALL_CLASSIFY_SLOP_MS = 250;
99
99
  /**
100
100
  * Split a block into compute-vs-stall from the CPU it could possibly have
101
101
  * burned (#1980). Pure, so the discrimination is testable without the native
@@ -92,7 +92,7 @@ export const KIND_EXTENSIONS = {
92
92
  zig: [".zig", ".zon"],
93
93
  };
94
94
  /** Return whether a path has an extension registered for the given file kind. */
95
- export function hasKindExtension(filePath, kind) {
95
+ function hasKindExtension(filePath, kind) {
96
96
  const extension = extname(filePath).toLowerCase();
97
97
  return KIND_EXTENSIONS[kind].some((candidate) => candidate === extension);
98
98
  }
@@ -284,18 +284,6 @@ export const NON_CODE_KINDS = new Set([
284
284
  "toml",
285
285
  "yaml",
286
286
  ]);
287
- /**
288
- * Check if a file kind represents a code file (not config/markdown).
289
- */
290
- export function isCodeKind(kind) {
291
- return CODE_KINDS.has(kind);
292
- }
293
- /**
294
- * Check if a file kind represents a text/config/doc/markup file.
295
- */
296
- export function isConfigKind(kind) {
297
- return NON_CODE_KINDS.has(kind);
298
- }
299
287
  /**
300
288
  * Check if a file path resolves to a {@link CODE_KINDS} kind. Undetectable
301
289
  * files (unknown extension, e.g. `.coffee` from SOURCE_PRECEDENCE) are
@@ -351,31 +339,6 @@ export function getFileKindLabel(kind) {
351
339
  };
352
340
  return labels[kind] ?? kind;
353
341
  }
354
- /**
355
- * Get file extensions for a file kind.
356
- */
357
- export function getExtensionsForKind(kind) {
358
- return [...(KIND_EXTENSIONS[kind] ?? [])];
359
- }
360
- /**
361
- * Check if a file should be scanned for linting/formatting.
362
- * Excludes test files, generated files, etc.
363
- */
364
- export function isScannableFile(filePath) {
365
- const kind = detectFileKind(filePath);
366
- if (!kind)
367
- return false;
368
- // Exclude test files for most kinds
369
- const base = basename(filePath);
370
- if (base.includes(".test.") ||
371
- base.includes(".spec.") ||
372
- base.startsWith("test-") ||
373
- base.startsWith("spec-")) {
374
- return false;
375
- }
376
- // Only scan code and config files
377
- return isCodeKind(kind) || isConfigKind(kind);
378
- }
379
342
  /**
380
343
  * Get the language identifier for LSP/tools that use language IDs.
381
344
  */
@@ -119,7 +119,7 @@ export class FileTime {
119
119
  }
120
120
  }
121
121
  // --- Error Type ---
122
- export class FileTimeError extends Error {
122
+ class FileTimeError extends Error {
123
123
  filePath;
124
124
  reason;
125
125
  constructor(message, filePath, reason) {