pi-lens 3.8.63 → 3.8.65

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 (49) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +12 -9
  3. package/dist/clients/deadline-utils.js +23 -0
  4. package/dist/clients/installer/index.js +18 -27
  5. package/dist/clients/log-cleanup.js +36 -13
  6. package/dist/clients/lsp/client.js +44 -0
  7. package/dist/clients/lsp/index.js +208 -33
  8. package/dist/clients/lsp/interactive-install.js +14 -4
  9. package/dist/clients/lsp/launch.js +29 -100
  10. package/dist/clients/mcp/review.js +7 -4
  11. package/dist/clients/package-manager.js +223 -0
  12. package/dist/clients/pipeline.js +55 -2
  13. package/dist/clients/project-metadata.js +3 -23
  14. package/dist/clients/safe-spawn.js +9 -0
  15. package/dist/clients/tree-sitter-client.js +72 -14
  16. package/dist/index.js +16 -0
  17. package/dist/mcp/server.js +4 -2
  18. package/dist/tools/ast-grep-search.js +10 -6
  19. package/dist/tools/lens-diagnostics.js +49 -10
  20. package/dist/tools/lsp-diagnostics.js +27 -4
  21. package/dist/tools/scan-progress.js +53 -0
  22. package/grammars/tree-sitter-bash.wasm +0 -0
  23. package/grammars/tree-sitter-bash.wasm.json +5 -0
  24. package/grammars/tree-sitter-css.wasm +0 -0
  25. package/grammars/tree-sitter-css.wasm.json +5 -0
  26. package/grammars/tree-sitter-go.wasm +0 -0
  27. package/grammars/tree-sitter-go.wasm.json +5 -0
  28. package/grammars/tree-sitter-html.wasm +0 -0
  29. package/grammars/tree-sitter-html.wasm.json +5 -0
  30. package/grammars/tree-sitter-java.wasm +0 -0
  31. package/grammars/tree-sitter-java.wasm.json +5 -0
  32. package/grammars/tree-sitter-javascript.wasm +0 -0
  33. package/grammars/tree-sitter-javascript.wasm.json +5 -0
  34. package/grammars/tree-sitter-json.wasm +0 -0
  35. package/grammars/tree-sitter-json.wasm.json +5 -0
  36. package/grammars/tree-sitter-python.wasm +0 -0
  37. package/grammars/tree-sitter-python.wasm.json +5 -0
  38. package/grammars/tree-sitter-rust.wasm +0 -0
  39. package/grammars/tree-sitter-rust.wasm.json +5 -0
  40. package/grammars/tree-sitter-tsx.wasm +0 -0
  41. package/grammars/tree-sitter-tsx.wasm.json +5 -0
  42. package/grammars/tree-sitter-typescript.wasm +0 -0
  43. package/grammars/tree-sitter-typescript.wasm.json +5 -0
  44. package/grammars/tree-sitter-yaml.wasm +0 -0
  45. package/grammars/tree-sitter-yaml.wasm.json +5 -0
  46. package/package.json +9 -7
  47. package/scripts/analyze-pi-lens-logs.mjs +101 -3
  48. package/scripts/download-grammars.js +171 -30
  49. package/scripts/grammars.lock.json +32 -0
@@ -1,16 +1,33 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Downloads tree-sitter WASM grammar files into node_modules/web-tree-sitter/grammars/.
4
- * Run automatically via postinstall. Skips gracefully if grammars already exist.
3
+ * Downloads tree-sitter WASM grammars, each verified against the committed
4
+ * provenance manifest (`scripts/grammars.lock.json`: package, version, per-grammar
5
+ * sha256) and stamped with a `<grammar>.wasm.json` sidecar. On a version bump or a
6
+ * hash mismatch the stale file is re-downloaded instead of skipped — closing the
7
+ * old skip-if-exists hole where an ABI-mismatched grammar could silently persist
8
+ * against the pinned `web-tree-sitter` (#177).
5
9
  *
6
- * Source: tree-sitter-wasms package on unpkg (mirrors npm registry artifacts).
10
+ * Modes:
11
+ * node download-grammars.js # all grammars → web-tree-sitter/grammars
12
+ * node download-grammars.js --core --dest grammars # core set → ./grammars (used by `prepare`)
13
+ * node download-grammars.js --write-manifest # regenerate grammars.lock.json from the CDN
14
+ *
15
+ * Source: tree-sitter-wasms on unpkg (mirrors the npm registry artifacts).
16
+ *
17
+ * Progress goes to stderr — stdout must stay clean so `npm pack --silent` (which
18
+ * runs this via `prepare`) captures only the tarball name (#376/#380).
7
19
  */
8
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
20
+ import { createHash } from "node:crypto";
21
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
9
22
  import { dirname, join } from "node:path";
10
- import { fileURLToPath } from "node:url";
23
+ import { fileURLToPath, pathToFileURL } from "node:url";
24
+ // The pinned tree-sitter-wasms release. Bump this, run `--write-manifest`, and
25
+ // commit the regenerated grammars.lock.json to move to a new grammar set.
11
26
  const TREE_SITTER_WASMS_VERSION = "0.1.13";
12
- const BASE_URL = `https://unpkg.com/tree-sitter-wasms@${TREE_SITTER_WASMS_VERSION}/out`;
13
- const GRAMMARS = [
27
+ const PACKAGE = "tree-sitter-wasms";
28
+ const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
29
+ const MANIFEST_PATH = join(SCRIPT_DIR, "grammars.lock.json");
30
+ export const GRAMMARS = [
14
31
  // Core typed languages
15
32
  "tree-sitter-typescript.wasm",
16
33
  "tree-sitter-tsx.wasm",
@@ -40,33 +57,153 @@ const GRAMMARS = [
40
57
  "tree-sitter-yaml.wasm",
41
58
  "tree-sitter-zig.wasm",
42
59
  ];
60
+ // The core set bundled into the tarball (via `prepare` → `grammars/`, shipped in
61
+ // `files[]`) so the common languages parse offline on every package manager. The
62
+ // long tail stays lazy-fetched at runtime. ~8.6MB uncompressed; ts/tsx dominate.
63
+ export const CORE = [
64
+ "tree-sitter-typescript.wasm",
65
+ "tree-sitter-tsx.wasm",
66
+ "tree-sitter-javascript.wasm",
67
+ "tree-sitter-python.wasm",
68
+ "tree-sitter-go.wasm",
69
+ "tree-sitter-rust.wasm",
70
+ "tree-sitter-json.wasm",
71
+ "tree-sitter-yaml.wasm",
72
+ "tree-sitter-bash.wasm",
73
+ "tree-sitter-html.wasm",
74
+ "tree-sitter-css.wasm",
75
+ "tree-sitter-java.wasm",
76
+ ];
77
+ /** `sha256:<hex>` digest of a buffer, matching the manifest/sidecar format. */
78
+ export function sha256(buf) {
79
+ return `sha256:${createHash("sha256").update(buf).digest("hex")}`;
80
+ }
81
+ /** Read the committed provenance manifest (`scripts/grammars.lock.json`). */
82
+ export function loadManifest() {
83
+ return JSON.parse(readFileSync(MANIFEST_PATH, "utf-8"));
84
+ }
85
+ /** Sidecar path for a grammar wasm file: `<grammar>.wasm.json`. */
86
+ export function sidecarPathFor(wasmPath) {
87
+ return `${wasmPath}.json`;
88
+ }
89
+ /**
90
+ * Whether `filename` must be (re)downloaded into `destDir`: true if the grammar
91
+ * or its sidecar is missing, the sidecar's version differs from the manifest, or
92
+ * the sidecar's recorded hash differs from the expected one. Pure and cheap (it
93
+ * trusts the sidecar's recorded hash — the CI provenance guard re-hashes the
94
+ * bytes for the full integrity check).
95
+ */
96
+ export function needsDownload(destDir, filename, manifest) {
97
+ const wasm = join(destDir, filename);
98
+ const sidecar = sidecarPathFor(wasm);
99
+ if (!existsSync(wasm) || !existsSync(sidecar))
100
+ return true;
101
+ let meta;
102
+ try {
103
+ meta = JSON.parse(readFileSync(sidecar, "utf-8"));
104
+ }
105
+ catch {
106
+ return true;
107
+ }
108
+ if (meta.version !== manifest.version)
109
+ return true;
110
+ const expected = manifest.grammars[filename];
111
+ if (expected && meta.sha256 !== expected)
112
+ return true;
113
+ return false;
114
+ }
43
115
  function findGrammarsDir() {
44
- const scriptDir = dirname(fileURLToPath(import.meta.url));
45
- const pkgRoot = dirname(scriptDir);
46
- // Prefer local node_modules next to this package
116
+ const pkgRoot = dirname(SCRIPT_DIR);
117
+ // Prefer local node_modules next to this package.
47
118
  return join(pkgRoot, "node_modules", "web-tree-sitter", "grammars");
48
119
  }
49
- async function downloadGrammar(destDir, filename) {
50
- const dest = join(destDir, filename);
51
- if (existsSync(dest)) {
52
- console.log(` skip ${filename} (already exists)`);
120
+ function baseUrl(version) {
121
+ return `https://unpkg.com/${PACKAGE}@${version}/out`;
122
+ }
123
+ async function fetchGrammar(version, filename) {
124
+ const res = await fetch(`${baseUrl(version)}/${filename}`);
125
+ if (!res.ok)
126
+ throw new Error(`HTTP ${res.status} fetching ${filename}`);
127
+ return Buffer.from(await res.arrayBuffer());
128
+ }
129
+ /**
130
+ * Download `filename` into `destDir` (if it isn't already verified), checking its
131
+ * bytes against the manifest and writing a provenance sidecar. Throws on an
132
+ * integrity mismatch — a grammar whose bytes don't match the pinned manifest is
133
+ * never written (guards CDN corruption / tampering / ABI drift).
134
+ */
135
+ async function downloadGrammar(destDir, filename, manifest) {
136
+ if (!needsDownload(destDir, filename, manifest)) {
137
+ console.error(` skip ${filename} (verified)`);
53
138
  return;
54
139
  }
55
- const url = `${BASE_URL}/${filename}`;
56
- const res = await fetch(url);
57
- if (!res.ok)
58
- throw new Error(`HTTP ${res.status} fetching ${url}`);
59
- const buf = await res.arrayBuffer();
60
- writeFileSync(dest, Buffer.from(buf));
61
- console.log(` ok ${filename}`);
140
+ const buf = await fetchGrammar(manifest.version, filename);
141
+ const actual = sha256(buf);
142
+ const expected = manifest.grammars[filename];
143
+ if (expected && actual !== expected) {
144
+ throw new Error(`integrity mismatch for ${filename}: expected ${expected}, got ${actual}`);
145
+ }
146
+ const wasm = join(destDir, filename);
147
+ writeFileSync(wasm, buf);
148
+ const sidecar = {
149
+ npmPackage: manifest.package,
150
+ version: manifest.version,
151
+ sha256: actual,
152
+ };
153
+ writeFileSync(sidecarPathFor(wasm), `${JSON.stringify(sidecar, null, 2)}\n`);
154
+ console.error(` ok ${filename}`);
155
+ }
156
+ function parseArgs(argv) {
157
+ const out = {
158
+ core: false,
159
+ dest: undefined,
160
+ writeManifest: false,
161
+ };
162
+ for (let i = 0; i < argv.length; i++) {
163
+ if (argv[i] === "--core")
164
+ out.core = true;
165
+ else if (argv[i] === "--write-manifest")
166
+ out.writeManifest = true;
167
+ else if (argv[i] === "--dest")
168
+ out.dest = argv[++i];
169
+ }
170
+ return out;
171
+ }
172
+ /** Regenerate grammars.lock.json by fetching every grammar and hashing it. */
173
+ async function regenerateManifest() {
174
+ console.error(`Regenerating manifest from ${PACKAGE}@${TREE_SITTER_WASMS_VERSION} …`);
175
+ const grammars = {};
176
+ for (const g of GRAMMARS) {
177
+ grammars[g] = sha256(await fetchGrammar(TREE_SITTER_WASMS_VERSION, g));
178
+ console.error(` hashed ${g}`);
179
+ }
180
+ const sorted = Object.fromEntries(Object.keys(grammars)
181
+ .sort()
182
+ .map((k) => [k, grammars[k]]));
183
+ const manifest = {
184
+ package: PACKAGE,
185
+ version: TREE_SITTER_WASMS_VERSION,
186
+ grammars: sorted,
187
+ };
188
+ writeFileSync(MANIFEST_PATH, `${JSON.stringify(manifest, null, 2)}\n`);
189
+ console.error(`Wrote ${MANIFEST_PATH} (${GRAMMARS.length} grammars).`);
62
190
  }
63
191
  async function main() {
64
- const grammarsDir = findGrammarsDir();
192
+ const args = parseArgs(process.argv.slice(2));
193
+ if (args.writeManifest)
194
+ return regenerateManifest();
195
+ const manifest = loadManifest();
196
+ // `--dest` is relative to cwd (used by `prepare` to bundle into `./grammars/`);
197
+ // default is the installed web-tree-sitter/grammars dir.
198
+ const grammarsDir = args.dest
199
+ ? join(process.cwd(), args.dest)
200
+ : findGrammarsDir();
201
+ const list = args.core ? CORE : GRAMMARS;
65
202
  if (!existsSync(grammarsDir)) {
66
203
  mkdirSync(grammarsDir, { recursive: true });
67
204
  }
68
- console.log(`Downloading tree-sitter grammars → ${grammarsDir}`);
69
- const results = await Promise.allSettled(GRAMMARS.map((g) => downloadGrammar(grammarsDir, g)));
205
+ console.error(`Downloading ${args.core ? "core" : "all"} tree-sitter grammars (${list.length}) → ${grammarsDir}`);
206
+ const results = await Promise.allSettled(list.map((g) => downloadGrammar(grammarsDir, g, manifest)));
70
207
  const failed = results.filter((r) => r.status === "rejected");
71
208
  if (failed.length > 0) {
72
209
  for (const f of failed) {
@@ -75,11 +212,15 @@ async function main() {
75
212
  console.warn(`${failed.length} grammar(s) failed — tree-sitter analysis may be unavailable.`);
76
213
  }
77
214
  else {
78
- console.log("All grammars downloaded successfully.");
215
+ console.error("All grammars downloaded successfully.");
79
216
  }
80
217
  }
81
- main().catch((err) => {
82
- // Never fail the install tree-sitter is optional
83
- console.warn("Warning: grammar download failed:", err.message);
84
- process.exit(0);
85
- });
218
+ // Only run when invoked directly (not when imported by the provenance check or
219
+ // tests) the import guard keeps the exported helpers side-effect-free.
220
+ if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
221
+ main().catch((err) => {
222
+ // Never fail the install — tree-sitter is optional.
223
+ console.warn("Warning: grammar download failed:", err.message);
224
+ process.exit(0);
225
+ });
226
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "package": "tree-sitter-wasms",
3
+ "version": "0.1.13",
4
+ "grammars": {
5
+ "tree-sitter-bash.wasm": "sha256:807dcdb1380a59befb112ed8fbd3d3872c7fadaf5903a769282b50973b30696d",
6
+ "tree-sitter-c.wasm": "sha256:056b25072382f72deee2c64ec238ffc4bb8cf42844ef21502c0e70f03a8a0d66",
7
+ "tree-sitter-c_sharp.wasm": "sha256:6266a7e32d68a3459104d994dc848df15d5672b0ea8e86d327274b694f8e6991",
8
+ "tree-sitter-cpp.wasm": "sha256:f6afdf53bfd6de76557bb7edb624a3a3869e14d9a83b78433f93617ecee42527",
9
+ "tree-sitter-css.wasm": "sha256:5fc615467b1b98420ed7517e5bf9e1f88468132dd903d842dfb13714f6a1cb0c",
10
+ "tree-sitter-dart.wasm": "sha256:7f5364e4256cf7e55efd01dd52421ef2663caa8061b82659b7e4bf61064545ec",
11
+ "tree-sitter-elixir.wasm": "sha256:82e91b9759ddca30d8978ebbfa8e347b4451b64c931f9ae62112e6db9b8fac20",
12
+ "tree-sitter-go.wasm": "sha256:9963ca89b616eaf04b08a43bc1fb0f07b85395bec313330851f1f1ead2f755b6",
13
+ "tree-sitter-html.wasm": "sha256:11b3405c1543fb012f5ed7f8ee73125076dce8b168301e1e787e4c717da6b456",
14
+ "tree-sitter-java.wasm": "sha256:637aac4415fb39a211a4f4292d63c66b5ce9c32fa2cd35464af4f681d91b9a1f",
15
+ "tree-sitter-javascript.wasm": "sha256:63812b9e275d26851264734868d27a1656bd44a2ef6eb3e85e6b03728c595ab5",
16
+ "tree-sitter-json.wasm": "sha256:fdb5219abe058369e16897aaa11eecf47ef4f546752c3ddbac339cdd89e1e667",
17
+ "tree-sitter-kotlin.wasm": "sha256:b5cb00c8d06ed0f10f1dbe497205b437809d7e87db1f638721a8cfb30e044449",
18
+ "tree-sitter-lua.wasm": "sha256:75ef809136d610068c5b2135741d89f5df62690a3d55169203351cb7cc85727d",
19
+ "tree-sitter-ocaml.wasm": "sha256:60849b6320ee956233d77b017c65c45660e507d03ae70aa1bd5783458e2e9e18",
20
+ "tree-sitter-php.wasm": "sha256:55bb617b6f01e14bab997861f0b20a2420cf6ba3199ffeb295b9ec398966d8a3",
21
+ "tree-sitter-python.wasm": "sha256:9056d0fb0c337810d019fae350e8167786119da98f0f282aceae7ab89ee8253b",
22
+ "tree-sitter-ruby.wasm": "sha256:93a5022855314cdb45458c7bb026a24a0ebc3a5ff6439e542e881f14dfa13a39",
23
+ "tree-sitter-rust.wasm": "sha256:4409921a70d0aa5bec7d1d7ce809a557a8ee1cf6ace901e3ac6a76e62cfea903",
24
+ "tree-sitter-swift.wasm": "sha256:41c4fdb2249a3aa6d87eed0d383081ff09725c2248b4977043a43825980ffcc7",
25
+ "tree-sitter-toml.wasm": "sha256:7849ac8ce9d10a4684ca189ea8ad3654c20c38acb2d674a014a164398cbd37a2",
26
+ "tree-sitter-tsx.wasm": "sha256:6aa3b2c70e76f5d48eafef1093e9c4de383e13f2fdde2f4e9b98a378f6a8f1b6",
27
+ "tree-sitter-typescript.wasm": "sha256:8515404dceed38e1ed86aa34b09fcf3379fff1b4ff9dd3967bcd6d1eb5ac3d8f",
28
+ "tree-sitter-vue.wasm": "sha256:6244521bb3fb60f34ce5f677f2af81facb2c38691193985ca5fa85e1b6f29250",
29
+ "tree-sitter-yaml.wasm": "sha256:5dea7cfff83d41d8f87fb8e434e1a5b292c0d670bfcdc42cb2af420ef490dde5",
30
+ "tree-sitter-zig.wasm": "sha256:59cc4531aa661e2de4c5bc04e4045b6bdd5d2bfa75045cbda5f673102d140eef"
31
+ }
32
+ }