@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
@@ -2,17 +2,14 @@
2
2
  * Automatic test-runner delivery for the post-agent idle window (#2366).
3
3
  *
4
4
  * Test results remain in `test-runner-findings` for pull diagnostics and the
5
- * commit guard. This module only stages an owner-qualified pointer and
6
- * appends a non-context custom entry after the host proves that the agent is
5
+ * commit guard. This module stages an owner-qualified pointer and marks it
6
+ * eligible for the next context build after the host proves that the agent is
7
7
  * idle. Provenance validation stays in `peekTestFindings`, the one shared
8
8
  * delivery gate for this cache.
9
9
  */
10
10
  import { emitBounded } from "./bounded-telemetry.js";
11
- import { peekTestFindings } from "./runtime-context.js";
12
- import { fitLines } from "./tui-fit.js";
13
- export const TEST_RUNNER_ENTRY_TYPE = "pilens:test-runner-findings";
11
+ import { consumeTestFindings, peekTestFindings } from "./runtime-context.js";
14
12
  const MAX_PENDING_DELIVERIES = 32;
15
- const MAX_ENTRY_CONTENT = 12_000;
16
13
  const pending = new Map();
17
14
  function key(cwd, sessionId, ownerId) {
18
15
  // `cwd` comes from the same host session field at turn_end and settle. Keep
@@ -20,14 +17,6 @@ function key(cwd, sessionId, ownerId) {
20
17
  // identity; dispatch contexts already normalize their own path fields.
21
18
  return `${ownerId ?? "direct"}\u0000${cwd}\u0000${sessionId}`;
22
19
  }
23
- function boundedContent(content) {
24
- if (content.length <= MAX_ENTRY_CONTENT)
25
- return { content, droppedDetailCount: 0 };
26
- return {
27
- content: `${content.slice(0, MAX_ENTRY_CONTENT)}\n[…details truncated]`,
28
- droppedDetailCount: content.length - MAX_ENTRY_CONTENT,
29
- };
30
- }
31
20
  function record(identity, outcome, delivery, metadata = {}) {
32
21
  emitBounded("test_runner_delivery", identity, {
33
22
  filePath: delivery.cwd,
@@ -44,9 +33,7 @@ function record(identity, outcome, delivery, metadata = {}) {
44
33
  },
45
34
  }, {
46
35
  capPerTurn: { limit: 8, turnIndex: delivery.generation },
47
- ledgerKind: outcome === "delivery-failed" || outcome === "capability-unavailable"
48
- ? "test-runner-delivery"
49
- : undefined,
36
+ ledgerKind: outcome === "delivery-failed" ? "test-runner-delivery" : undefined,
50
37
  reason: `test runner delivery ${outcome}`,
51
38
  });
52
39
  }
@@ -70,6 +57,7 @@ export function stageTestRunnerDelivery(args) {
70
57
  generation: args.generation,
71
58
  targetCount: args.targetCount,
72
59
  createdAt: Date.now(),
60
+ eligible: false,
73
61
  owner: args.owner,
74
62
  };
75
63
  record(deliveryKey, "superseded", superseded, {
@@ -95,13 +83,14 @@ export function stageTestRunnerDelivery(args) {
95
83
  generation: args.generation,
96
84
  targetCount: args.targetCount,
97
85
  createdAt: Date.now(),
86
+ eligible: false,
98
87
  owner: args.owner,
99
88
  };
100
89
  pending.set(deliveryKey, delivery);
101
90
  prunePending();
102
91
  record(deliveryKey, "staged", delivery);
103
92
  }
104
- /** Deliver the latest staged result during a host-confirmed idle window. */
93
+ /** Mark the latest staged result eligible during a host-confirmed idle window. */
105
94
  export function deliverTestRunnerFindings(args) {
106
95
  const cwd = args.ctx.cwd ?? process.cwd();
107
96
  const deliveryKey = key(cwd, args.sessionId, args.ownerId);
@@ -112,16 +101,11 @@ export function deliverTestRunnerFindings(args) {
112
101
  if (currentGeneration !== undefined &&
113
102
  currentGeneration > delivery.generation) {
114
103
  pending.delete(deliveryKey);
115
- record(deliveryKey, "superseded", delivery, {
116
- currentGeneration,
117
- });
104
+ record(deliveryKey, "superseded", delivery, { currentGeneration });
118
105
  return;
119
106
  }
120
107
  if (typeof args.ctx.isIdle !== "function") {
121
108
  pending.delete(deliveryKey);
122
- record(deliveryKey, "capability-unavailable", delivery, {
123
- reason: "host does not expose ctx.isIdle",
124
- });
125
109
  return;
126
110
  }
127
111
  try {
@@ -136,14 +120,13 @@ export function deliverTestRunnerFindings(args) {
136
120
  });
137
121
  return;
138
122
  }
139
- const findings = peekTestFindings(args.cacheManager, cwd, args.runtime, true);
140
- if (!findings) {
123
+ if (!peekTestFindings(args.cacheManager, cwd, args.runtime, true)) {
141
124
  pending.delete(deliveryKey);
142
125
  record(deliveryKey, "superseded", delivery);
143
126
  return;
144
127
  }
145
- // Recheck immediately before append. The host may accept a prompt between
146
- // the first check and this synchronous call.
128
+ // Recheck immediately before marking eligible. The host may accept a prompt
129
+ // between the first check and this synchronous call.
147
130
  try {
148
131
  if (!args.ctx.isIdle()) {
149
132
  record(deliveryKey, "carried", delivery);
@@ -156,32 +139,23 @@ export function deliverTestRunnerFindings(args) {
156
139
  });
157
140
  return;
158
141
  }
159
- // SAFETY: appendEntry is an optional host capability absent from older Pi SDKs.
160
- const appendEntry = args.pi.appendEntry;
161
- if (typeof appendEntry !== "function") {
142
+ delivery.eligible = true;
143
+ const current = args.cacheManager.readCache("test-runner-findings", cwd)?.data;
144
+ if (!current?.content || current.testRunGeneration !== delivery.generation) {
162
145
  pending.delete(deliveryKey);
163
- record(deliveryKey, "capability-unavailable", delivery);
164
146
  return;
165
147
  }
166
- const bounded = boundedContent(findings.messages[0]?.content ?? "");
167
- try {
168
- appendEntry.call(args.pi, TEST_RUNNER_ENTRY_TYPE, {
169
- ...bounded,
148
+ args.cacheManager.writeCache("test-runner-findings", {
149
+ ...current,
150
+ deliveryEligible: {
170
151
  sessionId: delivery.sessionId,
171
152
  generation: delivery.generation,
172
- targetCount: delivery.targetCount,
173
- });
174
- pending.delete(deliveryKey);
175
- record(deliveryKey, "delivered", delivery, {
176
- droppedDetailCount: bounded.droppedDetailCount,
177
- ageMs: Math.max(0, Date.now() - delivery.createdAt),
178
- });
179
- }
180
- catch (error) {
181
- record(deliveryKey, "delivery-failed", delivery, {
182
- error: String(error).slice(0, 500),
183
- });
184
- }
153
+ eligibleAt: Date.now(),
154
+ },
155
+ }, cwd);
156
+ record(deliveryKey, "eligible", delivery, {
157
+ ageMs: Math.max(0, Date.now() - delivery.createdAt),
158
+ });
185
159
  }
186
160
  /** Deliver only the result staged by this activation and settled session. */
187
161
  export function deliverStagedTestRunnerFindings(args) {
@@ -195,7 +169,6 @@ export function deliverStagedTestRunnerFindings(args) {
195
169
  if (!delivery?.owner)
196
170
  return;
197
171
  deliverTestRunnerFindings({
198
- pi: delivery.owner.pi,
199
172
  ctx: delivery.owner.getCtx(),
200
173
  cacheManager: delivery.owner.cacheManager,
201
174
  runtime: delivery.owner.runtime,
@@ -203,31 +176,63 @@ export function deliverStagedTestRunnerFindings(args) {
203
176
  ownerId: delivery.ownerId,
204
177
  });
205
178
  }
206
- export function registerTestRunnerEntryRenderer(pi) {
207
- // SAFETY: registerEntryRenderer is an optional host capability absent from older Pi SDKs.
208
- const register = pi.registerEntryRenderer;
209
- if (typeof register !== "function")
210
- return false;
211
- try {
212
- register.call(pi, TEST_RUNNER_ENTRY_TYPE, renderTestRunnerEntry);
213
- return true;
179
+ /** Consume the result made eligible by the settled idle boundary. */
180
+ export function consumeStagedTestRunnerFindings(args) {
181
+ const deliveryKey = key(args.cwd, args.sessionId, args.ownerId);
182
+ let delivery = pending.get(deliveryKey);
183
+ const persisted = args.cacheManager.readCache("test-runner-findings", args.cwd)?.data;
184
+ const eligible = persisted?.deliveryEligible;
185
+ if (!delivery && persisted?.content && eligible) {
186
+ const sameSession = eligible.sessionId === args.sessionId;
187
+ if (!sameSession) {
188
+ const foreignDelivery = {
189
+ cwd: args.cwd,
190
+ sessionId: eligible.sessionId,
191
+ generation: eligible.generation,
192
+ targetCount: 0,
193
+ createdAt: eligible.eligibleAt,
194
+ eligible: true,
195
+ rehydrated: true,
196
+ };
197
+ record(deliveryKey, "foreign-session", foreignDelivery, {
198
+ currentSessionId: args.sessionId,
199
+ currentOwnerId: args.ownerId,
200
+ reason: "session-mismatch",
201
+ });
202
+ return undefined;
203
+ }
204
+ delivery = {
205
+ cwd: args.cwd,
206
+ sessionId: args.sessionId,
207
+ ownerId: args.ownerId,
208
+ generation: eligible.generation,
209
+ targetCount: 0,
210
+ createdAt: eligible.eligibleAt,
211
+ eligible: true,
212
+ rehydrated: true,
213
+ };
214
+ pending.set(deliveryKey, delivery);
214
215
  }
215
- catch {
216
- return false;
216
+ if (!delivery?.eligible)
217
+ return undefined;
218
+ const currentGeneration = persisted?.testRunGeneration;
219
+ if (currentGeneration !== delivery.generation) {
220
+ pending.delete(deliveryKey);
221
+ record(deliveryKey, "superseded", delivery, { currentGeneration });
222
+ return undefined;
217
223
  }
218
- }
219
- function renderTestRunnerEntry(entry, _options, theme) {
220
- const content = entry.data?.content;
221
- if (typeof content !== "string")
224
+ const findings = consumeTestFindings(args.cacheManager, args.cwd, args.runtime);
225
+ if (!findings) {
226
+ pending.delete(deliveryKey);
227
+ record(deliveryKey, "superseded", delivery);
222
228
  return undefined;
223
- const lines = [
224
- theme.fg("error", "pi-lens test failures"),
225
- ...content.split("\n"),
226
- ];
227
- return {
228
- render: (width) => fitLines(lines, width),
229
- invalidate: () => { },
230
- };
229
+ }
230
+ pending.delete(deliveryKey);
231
+ record(deliveryKey, "delivered", delivery, {
232
+ ageMs: Math.max(0, Date.now() - delivery.createdAt),
233
+ rehydrated: delivery.rehydrated === true,
234
+ });
235
+ return findings;
231
236
  }
232
237
  /** Test-only reset; session ownership prevents cross-session consumption. */
233
238
  export function _resetTestRunnerDeliveryForTests() {
@@ -756,7 +756,7 @@ const FORMATTER_POLICY_BY_FILENAME = new Map(TERRAGRUNT_FILENAMES.map((name) =>
756
756
  // lines don't fall at the head of the pre-existing lookup-table duplication and
757
757
  // get counted as new duplicated code. Bindings are live, so position is safe.
758
758
  export { AUTO_INSTALLABLE_DEFAULT_FORMATTERS, FORMATTER_POLICY_BY_EXTENSION, FORMATTER_POLICY_BY_FILENAME, };
759
- export function getFormatterPolicyForExtension(ext) {
759
+ function getFormatterPolicyForExtension(ext) {
760
760
  return FORMATTER_POLICY_BY_EXTENSION.get(ext.toLowerCase());
761
761
  }
762
762
  export function getFormatterPolicyForFile(filePath) {
@@ -80,7 +80,7 @@ function grammarFileStamp(filePath) {
80
80
  return undefined;
81
81
  }
82
82
  }
83
- export function isTreeSitterWasmAbortError(error) {
83
+ function isTreeSitterWasmAbortError(error) {
84
84
  const message = error instanceof Error ? error.message : String(error);
85
85
  return message.includes("Aborted") || message.includes("abort()");
86
86
  }
@@ -4012,54 +4012,3 @@ export class TreeSitterClient {
4012
4012
  }
4013
4013
  }
4014
4014
  // --- Simplified Pattern Search (regex fallback) ---
4015
- /**
4016
- * Fallback structural search using regex when tree-sitter unavailable
4017
- * Less accurate but works without WASM dependencies
4018
- */
4019
- export function regexStructuralSearch(pattern, files, options = {}) {
4020
- const matches = [];
4021
- const maxResults = options.maxResults ?? 50;
4022
- // Extract pattern structure for regex
4023
- // "console.log($MSG)" -> /console\.log\(([^)]+)\)/
4024
- const regexPattern = pattern
4025
- .replace(/\\/g, "\\\\")
4026
- .replace(/\./g, "\\.")
4027
- .replace(/\$\$\$[A-Z_][A-Z0-9_]*/g, "(.*?)") // variadic - non-greedy
4028
- .replace(/\$[A-Z_][A-Z0-9_]*/g, "([^,)]+)"); // single - capture group
4029
- try {
4030
- const regex = new RegExp(regexPattern, "g");
4031
- for (const file of files) {
4032
- if (matches.length >= maxResults)
4033
- break;
4034
- try {
4035
- const content = fs.readFileSync(file, "utf-8");
4036
- const lines = content.split("\n");
4037
- for (let i = 0; i < lines.length; i++) {
4038
- regex.lastIndex = 0;
4039
- const match = regex.exec(lines[i]);
4040
- if (match) {
4041
- const captures = {};
4042
- // Extract captures
4043
- for (let j = 1; j < match.length; j++) {
4044
- captures[`$${j}`] = match[j];
4045
- }
4046
- matches.push({
4047
- file,
4048
- line: i + 1,
4049
- column: match.index + 1,
4050
- matchedText: match[0],
4051
- captures,
4052
- });
4053
- if (matches.length >= maxResults)
4054
- break;
4055
- }
4056
- }
4057
- }
4058
- catch { }
4059
- }
4060
- }
4061
- catch {
4062
- // Invalid regex
4063
- }
4064
- return matches;
4065
- }
@@ -18,10 +18,6 @@
18
18
  import { truncateToWidth } from "./deps/pi-tui.js";
19
19
  const ELLIPSIS_KIND = { "…": 0, "...": 1, "": 2 };
20
20
  let truncateIsNative = null;
21
- /** Test-only: reset the signature probe between test cases. */
22
- export function _resetTruncateProbeForTests() {
23
- truncateIsNative = null;
24
- }
25
21
  export function fitLine(s, maxWidth, ellipsis = "...") {
26
22
  const w = Math.max(0, maxWidth);
27
23
  const fn = truncateToWidth;
@@ -32,10 +32,6 @@ export function wireUserNotifier(portsOrGetter) {
32
32
  export function resetUserNotifier() {
33
33
  notifierGetter = undefined;
34
34
  }
35
- /** True when a host render path is available (for callers that want to branch). */
36
- export function hasUserNotifier() {
37
- return notifierGetter !== undefined;
38
- }
39
35
  /**
40
36
  * Best-effort delivery of a user-facing degradation. Never throws — a stale
41
37
  * `ctx` after a session replacement must degrade to a no-op, not break the
@@ -47,10 +47,3 @@ export function logWordIndex(entry) {
47
47
  cwd: normalizeFilePath(entry.cwd),
48
48
  });
49
49
  }
50
- export function getWordIndexLogPath() {
51
- return WORD_INDEX_LOG_FILE;
52
- }
53
- /** Resolve once all enqueued word-index writes are on disk (tests/shutdown). */
54
- export function flushWordIndexLog() {
55
- return writer.flush();
56
- }
@@ -30,8 +30,8 @@ import { createDeadline, yieldIfOverBudget, } from "./cooperative-budget.js";
30
30
  /**
31
31
  * Bytes of backing store per posting entry: one `fileId` lane and one `line`
32
32
  * lane, both `Int32`. This is the constant #2069's acceptance criterion is
33
- * written against, and {@link estimatePostingListBytes} is the arithmetic the
34
- * memory-sample record and the regression test both read.
33
+ * written against, and the arithmetic the memory-sample record and the
34
+ * regression test both read.
35
35
  */
36
36
  export const WORD_POSTING_ENTRY_BYTES = 8;
37
37
  /**
@@ -189,10 +189,6 @@ export class WordPostingList {
189
189
  return list;
190
190
  }
191
191
  }
192
- /** Estimated bytes one posting list holds, backing store plus fixed headers. */
193
- export function estimatePostingListBytes(list) {
194
- return list.byteLength + WORD_POSTING_LIST_OVERHEAD_BYTES;
195
- }
196
192
  /**
197
193
  * Re-home every posting list into ONE shared `ArrayBuffer`, exactly sized.
198
194
  *
@@ -405,7 +401,7 @@ export class WordForwardEntry {
405
401
  * Bytes per forward entry: one pointer-compressed slot for the shared token
406
402
  * string and one `Int32` count lane.
407
403
  */
408
- export const WORD_FORWARD_ENTRY_BYTES = 8;
404
+ const WORD_FORWARD_ENTRY_BYTES = 8;
409
405
  /**
410
406
  * The word index's dense file-id space: canonical path key → integer id →
411
407
  * display path.
@@ -725,17 +725,6 @@ async function updateWordIndexDocumentAsyncUnsafe(index, doc, shouldContinue) {
725
725
  commitWordIndexDocumentReplacement(index, doc, perTokenHits, docLength, startedAt);
726
726
  return true;
727
727
  }
728
- /** Bounds shared by every word-index build path — keep the walk off the
729
- * critical path on large repos: cap the file count, and skip files too large
730
- * to be hand-written source (generated/bundled output the source filter
731
- * didn't already exclude).
732
- *
733
- * Deprecated (#776): `collectWordIndexDocs` below no longer reads this
734
- * constant directly — it derives its cap from `getWordIndexMaxFilesDerived`
735
- * (project-scale.ts's `maxProjectFiles` knob), which reproduces this same
736
- * 6,000 default at the default base. Kept exported for tests/callers that
737
- * still reference the literal. */
738
- export const WORD_INDEX_MAX_FILES = 6000;
739
728
  export const WORD_INDEX_MAX_BYTES = 512 * 1024;
740
729
  /**
741
730
  * Collect the bounded `{path, content}` doc set `buildWordIndex` consumes —
@@ -748,9 +737,9 @@ export const WORD_INDEX_MAX_BYTES = 512 * 1024;
748
737
  export async function collectWordIndexDocs(root, shouldContinue = () => true, preflightFiles) {
749
738
  const { collectSourceFilesAsync } = await import("./source-filter.js");
750
739
  // #747 hardening: pass the cap INTO the walk — without it,
751
- // `collectSourceFilesAsync` defaults to an unbounded traversal and the
752
- // `WORD_INDEX_MAX_FILES` slice below only trims the result AFTER the whole
753
- // tree (all of $HOME, on a misrooted cwd) has already been enumerated.
740
+ // `collectSourceFilesAsync` defaults to an unbounded traversal and any
741
+ // slice below would only trim the result AFTER the whole tree (all of
742
+ // $HOME, on a misrooted cwd) has already been enumerated.
754
743
  // #760: the walk is additionally bounded by source-filter's default
755
744
  // visited-entry budget (DEFAULT_MAX_SCAN_ENTRIES), so a mixed tree with few
756
745
  // source files among a huge pile of non-source files can't force a
@@ -811,7 +800,7 @@ export async function collectWordIndexDocs(root, shouldContinue = () => true, pr
811
800
  // Node's fs.promises.stat runs on libuv's threadpool (default 4 slots), so
812
801
  // real parallelism tops out there; the surplus workers are queue depth that
813
802
  // keeps the pool saturated without starving other threadpool consumers.
814
- export const WORD_INDEX_STAT_CONCURRENCY = 8;
803
+ const WORD_INDEX_STAT_CONCURRENCY = 8;
815
804
  const WORD_INDEX_INCREMENTAL_CHURN_THRESHOLD = 0.3;
816
805
  // A ratio alone misclassifies one stale file in a three-file project as dense.
817
806
  // Below this absolute floor, per-document replacement is bounded and cheaper
@@ -1092,7 +1081,11 @@ export async function refreshWordIndexIncrementally(index, root, shouldContinue
1092
1081
  timings,
1093
1082
  };
1094
1083
  }
1095
- export const WORD_INDEX_QUERY_FILTER_KEYS = ["lang", "file", "ext"];
1084
+ const WORD_INDEX_QUERY_FILTER_KEYS = [
1085
+ "lang",
1086
+ "file",
1087
+ "ext",
1088
+ ];
1096
1089
  /** Thrown by {@link parseWordIndexQuery}/{@link buildWordIndexQueryFilter} for
1097
1090
  * an unrecognized `key:` prefix or an unrecognized `lang:` value — a query
1098
1091
  * typo fails loudly with the supported list instead of silently degrading
@@ -15,7 +15,7 @@ import { classifyProbeFailure, createAvailabilityLatch, logAvailabilityDecision,
15
15
  // presence of one is the repo's deliberate opt-in: it carries the author's
16
16
  // chosen severities/ignores, so we let zizmor findings BLOCK in that workspace
17
17
  // (advisory-only otherwise, like Opengrep's local-rules gate).
18
- export const LOCAL_ZIZMOR_CONFIG_NAMES = [
18
+ const LOCAL_ZIZMOR_CONFIG_NAMES = [
19
19
  path.join(".github", "zizmor.yml"),
20
20
  path.join(".github", "zizmor.yaml"),
21
21
  "zizmor.yml",