pi-lens 3.8.30 → 3.8.31

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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,17 @@ All notable changes to pi-lens will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [3.8.31] - 2026-04-23
8
+
9
+ ### Fixed
10
+ - **Duplicate inline feedback on edit arrays** — `tool_result` calls for the same file are now deduplicated within a turn using a `reportedThisTurn` set on `RuntimeCoordinator`, cleared on each `turn_start`; previously pi's sequential per-hunk `tool_result` firing caused the pipeline to re-run and feedback to repeat N times per edit array
11
+ - **Double latency logging on pipeline completion** — removed redundant `logLatency` call in `pipeline.ts`; `runtime-tool-result.ts` already logs the outer `tool_result completed` with full duration including format, autofix, and cascade phases
12
+ - **Modified range tracking broken for 3-digit+ line numbers** — `parseDiffRanges` regex changed from `\s+` to `\s*` to handle unpadded line numbers; the diff format right-pads to the file's max digit width so e.g. line 613 in a <1000-line file has no leading space and was silently dropped
13
+ - **Stale gleam grammar entries** — removed dead `LANGUAGE_TO_GRAMMAR` and `getExtensionsForLanguage` entries for gleam; `tree-sitter-gleam.wasm` was never published in `tree-sitter-wasms@0.1.13`
14
+
15
+ ### Changed
16
+ - **TypeBox 0.34.x → 1.x migration** — updated `package.json` dependency from `@sinclair/typebox` to `typebox ^1.0.0` and updated imports in `tools/lsp-navigation.ts`, `tools/ast-grep-search.ts`, and `tools/ast-grep-replace.ts` to match pi-mono 0.69.0
17
+
7
18
  ## [3.8.30] - 2026-04-22
8
19
 
9
20
  ### Fixed
@@ -1014,13 +1014,6 @@ export async function runPipeline(
1014
1014
  }
1015
1015
 
1016
1016
  phase.end("total", { hasOutput: !!output });
1017
- logLatency({
1018
- type: "tool_result",
1019
- toolName,
1020
- filePath,
1021
- durationMs: elapsed,
1022
- result: output ? "completed" : "no_output",
1023
- });
1024
1017
 
1025
1018
  return {
1026
1019
  output,
@@ -22,6 +22,7 @@ export class RuntimeCoordinator {
22
22
  private _lastImpactCascadeOutput = "";
23
23
  private _complexityBaselines = new Map<string, FileComplexity>();
24
24
  private _fixedThisTurn = new Set<string>();
25
+ private _reportedThisTurn = new Set<string>();
25
26
  private _projectRulesScan: RuleScanResult = {
26
27
  rules: [],
27
28
  hasCustomRules: false,
@@ -43,6 +44,7 @@ export class RuntimeCoordinator {
43
44
  this._lastCascadeOutput = "";
44
45
  this._lastImpactCascadeOutput = "";
45
46
  this._fixedThisTurn.clear();
47
+ this._reportedThisTurn.clear();
46
48
  this._telemetrySessionId =
47
49
  `lens-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
48
50
  this._telemetryModel = "unknown";
@@ -81,6 +83,11 @@ export class RuntimeCoordinator {
81
83
  this._lastImpactCascadeOutput = "";
82
84
  this._turnIndex += 1;
83
85
  this._writeIndex = 0;
86
+ this._reportedThisTurn.clear();
87
+ }
88
+
89
+ get reportedThisTurn(): Set<string> {
90
+ return this._reportedThisTurn;
84
91
  }
85
92
 
86
93
  nextWriteIndex(): number {
@@ -41,7 +41,7 @@ interface ToolResultDeps {
41
41
  function parseDiffRanges(diff: string): { start: number; end: number }[] {
42
42
  const changedLines: number[] = [];
43
43
  for (const line of diff.split("\n")) {
44
- const match = line.match(/^[+-]\s+(\d+)\s/);
44
+ const match = line.match(/^[+-]\s*(\d+)\s/);
45
45
  if (match) {
46
46
  changedLines.push(Number.parseInt(match[1], 10));
47
47
  }
@@ -121,6 +121,14 @@ export async function handleToolResult(
121
121
  return;
122
122
  }
123
123
 
124
+ // Deduplicate sequential calls for the same file within the same turn.
125
+ // When pi processes an edit array serially, each hunk fires tool_result after
126
+ // the previous completes, bypassing the in-flight map above.
127
+ if (runtime.reportedThisTurn.has(filePath)) {
128
+ dbg(`tool_result: skipping already-reported file this turn for ${filePath}`);
129
+ return;
130
+ }
131
+
124
132
  const sessionFileTime = createFileTime("default");
125
133
  // tool_result is emitted after write/edit has already been applied.
126
134
  // Asserting pre-write stamps here produces false positives on rapid edits.
@@ -285,6 +293,8 @@ export async function handleToolResult(
285
293
  result: output ? "completed" : "no_output",
286
294
  });
287
295
 
296
+ runtime.reportedThisTurn.add(filePath);
297
+
288
298
  if (!output) return;
289
299
 
290
300
  return {
@@ -68,7 +68,6 @@ const LANGUAGE_TO_GRAMMAR: Record<string, string> = {
68
68
  c: "tree-sitter-c.wasm",
69
69
  cpp: "tree-sitter-cpp.wasm",
70
70
  elixir: "tree-sitter-elixir.wasm",
71
- gleam: "tree-sitter-gleam.wasm",
72
71
  ruby: "tree-sitter-ruby.wasm",
73
72
  };
74
73
 
@@ -1112,7 +1111,6 @@ export class TreeSitterClient {
1112
1111
  c: [".c", ".h"],
1113
1112
  cpp: [".cpp", ".hpp", ".cc", ".hh"],
1114
1113
  elixir: [".ex", ".exs"],
1115
- gleam: [".gleam"],
1116
1114
  ruby: [".rb"],
1117
1115
  };
1118
1116
  return mapping[languageId] || [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-lens",
3
- "version": "3.8.30",
3
+ "version": "3.8.31",
4
4
  "type": "module",
5
5
  "description": "Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo",
6
6
  "repository": {
@@ -74,7 +74,7 @@
74
74
  },
75
75
  "dependencies": {
76
76
  "@ast-grep/napi": "^0.42.1",
77
- "@sinclair/typebox": "^0.34.0",
77
+ "typebox": "^1.0.0",
78
78
  "minimatch": "^10.2.5",
79
79
  "typescript": "^5.0.0",
80
80
  "vscode-jsonrpc": "^8.2.1"
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Extracted from index.ts for maintainability.
5
5
  */
6
- import { Type } from "@sinclair/typebox";
6
+ import { Type } from "typebox";
7
7
  import { LANGUAGES } from "./shared.js";
8
8
  export function createAstGrepReplaceTool(astGrepClient) {
9
9
  return {
@@ -4,7 +4,7 @@
4
4
  * Extracted from index.ts for maintainability.
5
5
  */
6
6
 
7
- import { Type } from "@sinclair/typebox";
7
+ import { Type } from "typebox";
8
8
  import type { AstGrepClient } from "../clients/ast-grep-client.js";
9
9
  import { LANGUAGES } from "./shared.js";
10
10
 
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Extracted from index.ts for maintainability.
5
5
  */
6
- import { Type } from "@sinclair/typebox";
6
+ import { Type } from "typebox";
7
7
  import { LANGUAGES } from "./shared.js";
8
8
  function looksLikeRuleYamlOrPlainText(pattern) {
9
9
  const text = pattern.trim();
@@ -4,7 +4,7 @@
4
4
  * Extracted from index.ts for maintainability.
5
5
  */
6
6
 
7
- import { Type } from "@sinclair/typebox";
7
+ import { Type } from "typebox";
8
8
  import type { AstGrepClient } from "../clients/ast-grep-client.js";
9
9
  import { LANGUAGES } from "./shared.js";
10
10
 
@@ -6,7 +6,7 @@
6
6
  import * as nodeFs from "node:fs";
7
7
  import * as path from "node:path";
8
8
  import { pathToFileURL } from "node:url";
9
- import { Type } from "@sinclair/typebox";
9
+ import { Type } from "typebox";
10
10
  import { getLSPService } from "../clients/lsp/index.js";
11
11
  import { logLatency } from "../clients/latency-logger.js";
12
12
  function operationSupportStatus(operation, support) {
@@ -7,7 +7,7 @@
7
7
  import * as nodeFs from "node:fs";
8
8
  import * as path from "node:path";
9
9
  import { pathToFileURL } from "node:url";
10
- import { Type } from "@sinclair/typebox";
10
+ import { Type } from "typebox";
11
11
  import type { LSPCallHierarchyItem } from "../clients/lsp/client.js";
12
12
  import { getLSPService } from "../clients/lsp/index.js";
13
13
  import { logLatency } from "../clients/latency-logger.js";