@epoch-agent/plugin-lsp 3.2.0 → 4.0.0

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 (2) hide show
  1. package/dist/index.js +43 -36
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -34,7 +34,7 @@ var MAX_OPEN_DOCUMENTS = 64;
34
34
  var POLL_STEP_MS = 25;
35
35
  var LspServerDownError = class extends Error {
36
36
  constructor(detail) {
37
- super(`language server \u5DF2\u9000\u51FA\uFF1A${detail}`);
37
+ super(`language server exited: ${detail}`);
38
38
  this.detail = detail;
39
39
  this.name = "LspServerDownError";
40
40
  }
@@ -137,8 +137,8 @@ var LspClient = class _LspClient {
137
137
  this.connection.onRequest(RegistrationRequest.type, () => void 0);
138
138
  this.connection.onRequest(UnregistrationRequest.type, () => void 0);
139
139
  this.connection.onRequest(WorkDoneProgressCreateRequest.type, () => void 0);
140
- this.connection.onError(() => this.markDead("JSON-RPC \u8FDE\u63A5\u9519\u8BEF"));
141
- this.connection.onClose(() => this.markDead("JSON-RPC \u8FDE\u63A5\u5173\u95ED"));
140
+ this.connection.onError(() => this.markDead("JSON-RPC connection error"));
141
+ this.connection.onClose(() => this.markDead("JSON-RPC connection closed"));
142
142
  }
143
143
  appendStderr(chunk) {
144
144
  this.stderr = (this.stderr + chunk).slice(-STDERR_KEEP);
@@ -146,7 +146,7 @@ var LspClient = class _LspClient {
146
146
  markDead(detail, onCrash) {
147
147
  if (this.dead) return;
148
148
  const tail = this.stderr.trim();
149
- this.dead = tail ? `${detail}\uFF1Bstderr: ${tail.slice(-500)}` : detail;
149
+ this.dead = tail ? `${detail}; stderr: ${tail.slice(-500)}` : detail;
150
150
  if (!this.disposing) onCrash?.(this.dead);
151
151
  }
152
152
  async initialize(projectRoot) {
@@ -258,7 +258,7 @@ var LspClient = class _LspClient {
258
258
  this.connection.dispose();
259
259
  } catch {
260
260
  }
261
- this.dead ??= "\u5DF2\u4E3B\u52A8\u5173\u95ED";
261
+ this.dead ??= "disposed";
262
262
  await killTrackedProcess(this.pidValue);
263
263
  if (tree.length > 0) await killStampedPids(tree, { escalate: true });
264
264
  }
@@ -322,7 +322,7 @@ var TYPESCRIPT = {
322
322
  pkg: "typescript-language-server",
323
323
  bin: "typescript-language-server",
324
324
  args: ["--stdio"],
325
- install: "npm i -D typescript-language-server typescript\uFF08\u6216\u5168\u5C40 npm i -g\uFF09",
325
+ install: "npm i -D typescript-language-server typescript (or install globally with npm i -g)",
326
326
  initTimeoutMs: 3e4,
327
327
  initializationOptions: (projectRoot) => {
328
328
  const path = tsserverPath(projectRoot);
@@ -394,7 +394,7 @@ async function classify(paths, workDir, outcome) {
394
394
  const absPath = isAbsolute(raw) ? resolve2(raw) : resolve2(root, raw);
395
395
  const rel = relative(root, absPath);
396
396
  if (rel !== "" && (rel.startsWith("..") || isAbsolute(rel))) {
397
- outcome.rejected.push({ path: raw, reason: `\u4E0D\u5728\u5DE5\u4F5C\u533A ${root} \u5185` });
397
+ outcome.rejected.push({ path: raw, reason: `outside the workspace ${root}` });
398
398
  continue;
399
399
  }
400
400
  const key = uriKey(`file://${absPath.split(sep).join("/")}`);
@@ -404,11 +404,11 @@ async function classify(paths, workDir, outcome) {
404
404
  try {
405
405
  const info = await stat(absPath);
406
406
  if (!info.isFile()) {
407
- outcome.rejected.push({ path: display, reason: "\u4E0D\u662F\u6587\u4EF6" });
407
+ outcome.rejected.push({ path: display, reason: "not a file" });
408
408
  continue;
409
409
  }
410
410
  } catch {
411
- outcome.rejected.push({ path: display, reason: "\u6587\u4EF6\u4E0D\u5B58\u5728" });
411
+ outcome.rejected.push({ path: display, reason: "file does not exist" });
412
412
  continue;
413
413
  }
414
414
  const match = serverForExtension(extname(absPath));
@@ -452,7 +452,7 @@ async function runGroup(spec, files, input, outcome) {
452
452
  } catch (err) {
453
453
  outcome.rejected.push({
454
454
  path: file.display,
455
- reason: `\u8BFB\u4E0D\u51FA\u6765\uFF08${err instanceof Error ? err.message : String(err)}\uFF09`
455
+ reason: `cannot read it (${err instanceof Error ? err.message : String(err)})`
456
456
  });
457
457
  continue;
458
458
  }
@@ -464,7 +464,7 @@ async function runGroup(spec, files, input, outcome) {
464
464
  try {
465
465
  batch = await acquired.client.diagnose(documents, input.wait ?? DEFAULT_WAIT);
466
466
  } catch (err) {
467
- const reason = err instanceof LspServerDownError ? err.message : `\u8BCA\u65AD\u5931\u8D25\uFF08${String(err)}\uFF09`;
467
+ const reason = err instanceof LspServerDownError ? err.message : `diagnostics failed (${String(err)})`;
468
468
  outcome.skipped.push(skip(spec, reason, files));
469
469
  return;
470
470
  }
@@ -477,7 +477,11 @@ async function runGroup(spec, files, input, outcome) {
477
477
  if (batch.pending.length > 0) {
478
478
  const stuck = batch.pending.map((key) => byKey.get(key)).filter((f) => !!f);
479
479
  outcome.skipped.push(
480
- skip(spec, `server \u672A\u5728 ${(input.wait ?? DEFAULT_WAIT).timeoutMs}ms \u5185\u7ED9\u51FA\u7ED3\u679C`, stuck)
480
+ skip(
481
+ spec,
482
+ `the server gave no result within ${(input.wait ?? DEFAULT_WAIT).timeoutMs}ms`,
483
+ stuck
484
+ )
481
485
  );
482
486
  }
483
487
  }
@@ -491,12 +495,12 @@ async function diagnosePaths(input) {
491
495
  };
492
496
  const paths = input.paths.slice(0, MAX_FILES_PER_CALL);
493
497
  for (const extra of input.paths.slice(MAX_FILES_PER_CALL)) {
494
- outcome.rejected.push({ path: extra, reason: `\u4E00\u6B21\u6700\u591A ${MAX_FILES_PER_CALL} \u4E2A\u6587\u4EF6` });
498
+ outcome.rejected.push({ path: extra, reason: `at most ${MAX_FILES_PER_CALL} files per call` });
495
499
  }
496
500
  const candidates = await classify(paths, resolve2(input.workDir), outcome);
497
501
  for (const group of groupBySpec(candidates).values()) {
498
502
  if (input.signal?.aborted) {
499
- outcome.skipped.push(skip(group.spec, "\u5DF2\u53D6\u6D88", group.files));
503
+ outcome.skipped.push(skip(group.spec, "cancelled", group.files));
500
504
  continue;
501
505
  }
502
506
  await runGroup(group.spec, group.files, input, outcome);
@@ -533,7 +537,7 @@ function renderCounts(items) {
533
537
  }
534
538
  function renderFiles(files) {
535
539
  if (files.length <= MAX_FILE_NAMES) return files.join(", ");
536
- return `${files.slice(0, MAX_FILE_NAMES).join(", ")} \u7B49 ${files.length} \u4E2A`;
540
+ return `${files.slice(0, MAX_FILE_NAMES).join(", ")} \u2026 (${files.length} total)`;
537
541
  }
538
542
  function pickItems(items) {
539
543
  if (items.length <= MAX_ITEMS) return { shown: items, dropped: 0 };
@@ -551,29 +555,29 @@ function formatOutcome(outcome) {
551
555
  const { checked, diagnostics, skipped, unsupported, rejected } = outcome;
552
556
  if (diagnostics.length === 0) {
553
557
  lines.push(
554
- checked.length > 0 ? `\u672A\u53D1\u73B0\u95EE\u9898 \xB7 \u5DF2\u68C0\u67E5 ${checked.length} \u4E2A\u6587\u4EF6\uFF1A${renderFiles(checked)}` : "\u672A\u68C0\u67E5\u4EFB\u4F55\u6587\u4EF6"
558
+ checked.length > 0 ? `No problems found \xB7 checked ${checked.length} file(s): ${renderFiles(checked)}` : "No files were checked"
555
559
  );
556
560
  } else {
557
561
  const { shown, dropped } = pickItems(diagnostics);
558
562
  lines.push(
559
- `${diagnostics.length} \u6761\u8BCA\u65AD\uFF08${renderCounts(diagnostics)}\uFF09 \xB7 \u5DF2\u68C0\u67E5 ${checked.length} \u4E2A\u6587\u4EF6`,
563
+ `${diagnostics.length} diagnostic(s) (${renderCounts(diagnostics)}) \xB7 checked ${checked.length} file(s)`,
560
564
  "",
561
565
  ...shown.map(renderItem)
562
566
  );
563
567
  if (dropped > 0) {
564
- lines.push("", `\uFF08\u8FD8\u6709 ${dropped} \u6761\u672A\u5217\u51FA\uFF0C\u5DF2\u4F18\u5148\u4FDD\u7559 error\uFF09`);
568
+ lines.push("", `(${dropped} more not listed; errors were kept first)`);
565
569
  }
566
570
  }
567
571
  for (const skip2 of skipped) {
568
- lines.push("", `\u8DF3\u8FC7 ${skip2.label}\uFF1A${skip2.reason}`);
569
- if (skip2.install) lines.push(` \u5B89\u88C5\uFF1A${skip2.install}`);
570
- if (skip2.files.length > 0) lines.push(` \u672A\u68C0\u67E5\uFF1A${renderFiles(skip2.files)}`);
572
+ lines.push("", `Skipped ${skip2.label}: ${skip2.reason}`);
573
+ if (skip2.install) lines.push(` install: ${skip2.install}`);
574
+ if (skip2.files.length > 0) lines.push(` not checked: ${renderFiles(skip2.files)}`);
571
575
  }
572
576
  if (unsupported.length > 0) {
573
- lines.push("", `\u65E0\u5BF9\u5E94 language server\uFF0C\u672A\u68C0\u67E5\uFF1A${renderFiles(unsupported)}`);
577
+ lines.push("", `No matching language server; not checked: ${renderFiles(unsupported)}`);
574
578
  }
575
579
  for (const bad of rejected) {
576
- lines.push("", `\u65E0\u6CD5\u68C0\u67E5 ${bad.path}\uFF1A${bad.reason}`);
580
+ lines.push("", `Cannot check ${bad.path}: ${bad.reason}`);
577
581
  }
578
582
  return lines.join("\n");
579
583
  }
@@ -664,7 +668,7 @@ function resolveServer(spec, projectRoot, options = {}) {
664
668
  if (launch) return { available: true, launch };
665
669
  return {
666
670
  available: false,
667
- reason: `\u6CA1\u627E\u5230 ${spec.bin}\uFF08\u9879\u76EE node_modules \u548C PATH \u91CC\u90FD\u6CA1\u6709\uFF09`,
671
+ reason: `could not find ${spec.bin} (not in the project's node_modules, nor on PATH)`,
668
672
  install: spec.install
669
673
  };
670
674
  }
@@ -702,13 +706,13 @@ var LspServerPool = class {
702
706
  return { ok: true, client: existing.client };
703
707
  }
704
708
  this.entries.delete(key);
705
- this.noteFailure(key, "language server \u8FDB\u7A0B\u5DF2\u9000\u51FA");
709
+ this.noteFailure(key, "language server process has exited");
706
710
  }
707
711
  const failed = this.failures.get(key);
708
712
  if (failed && failed.count >= MAX_FAILURES) {
709
713
  return {
710
714
  ok: false,
711
- reason: `${failed.detail}\uFF08\u5DF2\u8FDE\u7EED\u5931\u8D25 ${failed.count} \u6B21\uFF0C\u672C\u6B21\u4F1A\u8BDD\u4E0D\u518D\u91CD\u542F\uFF09`
715
+ reason: `${failed.detail} (failed ${failed.count} times in a row; not restarting in this session)`
712
716
  };
713
717
  }
714
718
  const inflight = this.starting.get(key);
@@ -732,15 +736,18 @@ var LspServerPool = class {
732
736
  projectRoot,
733
737
  onCrash: (detail) => {
734
738
  this.entries.delete(key);
735
- this.noteFailure(key, `language server \u5D29\u4E86\uFF08${detail}\uFF09`);
739
+ this.noteFailure(key, `language server crashed (${detail})`);
736
740
  }
737
741
  });
738
742
  if (!client) {
739
- this.noteFailure(key, `language server \u8D77\u4E0D\u6765\u6216\u521D\u59CB\u5316\u8D85\u65F6\uFF08>${spec.initTimeoutMs}ms\uFF09`);
743
+ this.noteFailure(
744
+ key,
745
+ `language server failed to start or initialize timed out (>${spec.initTimeoutMs}ms)`
746
+ );
740
747
  const now = this.failures.get(key);
741
748
  return {
742
749
  ok: false,
743
- reason: now && now.count >= MAX_FAILURES ? `${now.detail}\uFF08\u672C\u6B21\u4F1A\u8BDD\u4E0D\u518D\u91CD\u542F\uFF09` : now?.detail ?? "\u542F\u52A8\u5931\u8D25"
750
+ reason: now && now.count >= MAX_FAILURES ? `${now.detail} (not restarting in this session)` : now?.detail ?? "failed to start"
744
751
  };
745
752
  }
746
753
  this.entries.set(key, { client, lastUsedAt: this.now() });
@@ -814,18 +821,18 @@ function fail(code, message, suggestion, started) {
814
821
  var lspPlugin = {
815
822
  name: "@epoch-agent/plugin-lsp",
816
823
  version: "0.0.0",
817
- description: "LSP \u8BCA\u65AD\u63D2\u4EF6 \u2014 \u7528\u9879\u76EE\u81EA\u5DF1\u7684 language server \u53D6\u7C7B\u578B / \u8BED\u6CD5\u9519\u8BEF",
824
+ description: "Language-server diagnostics \u2014 type and syntax errors from the project's own language server",
818
825
  tools: [
819
826
  {
820
827
  name: "lsp_diagnostics",
821
- description: `\u7528\u9879\u76EE\u81EA\u5DF1\u7684 language server \u68C0\u67E5\u6587\u4EF6\uFF0C\u8FD4\u56DE\u7C7B\u578B\u9519\u8BEF\u3001\u8BED\u6CD5\u9519\u8BEF\u548C\u672A\u4F7F\u7528\u53D8\u91CF\u7B49\u8BCA\u65AD\u3002\u53C2\u6570: paths(\u5FC5\u586B, \u6587\u4EF6\u8DEF\u5F84\u6570\u7EC4, \u76F8\u5BF9\u5DE5\u4F5C\u76EE\u5F55)\u3002\u5F53\u524D\u652F\u6301 ${supportedExtensions().join(" ")}\u3002**\u6539\u5B8C\u4EE3\u7801\u4E4B\u540E\u3001\u5BA3\u5E03\u5B8C\u6210\u4E4B\u524D\u8C03\u4E00\u6B21**\uFF0C\u628A\u672C\u8F6E\u65B0\u5EFA\u6216\u4FEE\u6539\u8FC7\u7684\u6E90\u6587\u4EF6\u90FD\u5217\u8FDB\u53BB \u2014\u2014 \u5B83\u6BD4\u8BA9 shell \u8DD1\u4E00\u904D tsc \u5FEB\u5F97\u591A\uFF0C\u56E0\u4E3A language server \u662F\u5E38\u9A7B\u7684\u3002\u6CE8\u610F\u5B83\u53EA\u505A\u5355\u6587\u4EF6\u7EA7\u8BCA\u65AD\uFF0C\u4E0D\u7B49\u4E8E\u4E00\u6B21\u5B8C\u6574\u6784\u5EFA\uFF1A\u6784\u5EFA\u811A\u672C\u3001\u6253\u5305\u914D\u7F6E\u3001\u8DE8\u5305\u7C7B\u578B\u89E3\u6790\u51FA\u7684\u95EE\u9898\u5B83\u770B\u4E0D\u5230\u3002\u6CA1\u88C5\u5BF9\u5E94 language server \u65F6\u4F1A\u5982\u5B9E\u62A5\u300C\u8DF3\u8FC7\u300D\u5E76\u7ED9\u51FA\u5B89\u88C5\u547D\u4EE4\uFF0C\u4E0D\u4F1A\u8BA9\u8FD9\u6B21\u8C03\u7528\u5931\u8D25\u3002`,
828
+ description: `Check files with the project's own language server and return type errors, syntax errors and unused-variable warnings. Supported extensions: ${supportedExtensions().join(" ")}. Call it once after changing code and before saying the work is finished, listing the source files you created or modified. It is much faster than running tsc through the shell because the language server is already resident. It only does single-file diagnostics, which is not the same as a full build: problems from build scripts, bundler config or cross-package type resolution are invisible to it. When no language server is installed it reports "skipped" and prints the install command instead of failing.`,
822
829
  parameters: {
823
830
  type: "object",
824
831
  properties: {
825
832
  paths: {
826
833
  type: "array",
827
834
  items: { type: "string" },
828
- description: '\u8981\u68C0\u67E5\u7684\u6587\u4EF6\u8DEF\u5F84\uFF08\u76F8\u5BF9\u5DE5\u4F5C\u76EE\u5F55\u6216\u7EDD\u5BF9\u8DEF\u5F84\uFF09\uFF0C\u4F8B\u5982 ["src/a.ts", "src/b.tsx"]'
835
+ description: 'File paths to check (relative to the working directory, or absolute), e.g. ["src/a.ts", "src/b.tsx"].'
829
836
  }
830
837
  },
831
838
  required: ["paths"]
@@ -839,8 +846,8 @@ var lspPlugin = {
839
846
  if (!paths) {
840
847
  return fail(
841
848
  ERR_BAD_PATHS,
842
- "paths \u5FC5\u987B\u662F\u975E\u7A7A\u7684\u6587\u4EF6\u8DEF\u5F84\u6570\u7EC4",
843
- '\u628A\u672C\u8F6E\u6539\u52A8\u8FC7\u7684\u6E90\u6587\u4EF6\u5217\u8FDB\u53BB\uFF0C\u4F8B\u5982 {"paths": ["src/a.ts"]}',
849
+ "paths must be a non-empty array of file paths",
850
+ 'List the source files you changed in this turn, e.g. {"paths": ["src/a.ts"]}',
844
851
  started
845
852
  );
846
853
  }
@@ -855,8 +862,8 @@ var lspPlugin = {
855
862
  } catch (err) {
856
863
  return fail(
857
864
  ERR_UNEXPECTED,
858
- `\u8BCA\u65AD\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`,
859
- "\u53EF\u4EE5\u5148\u7EE7\u7EED\uFF0C\u6216\u6539\u7528 terminal \u8DD1\u9879\u76EE\u81EA\u5DF1\u7684\u7C7B\u578B\u68C0\u67E5\u547D\u4EE4",
865
+ `diagnostics failed: ${err instanceof Error ? err.message : String(err)}`,
866
+ "You can keep going, or run the project's own type-check command through terminal instead",
860
867
  started
861
868
  );
862
869
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epoch-agent/plugin-lsp",
3
- "version": "3.2.0",
3
+ "version": "4.0.0",
4
4
  "private": false,
5
5
  "description": "LSP 诊断插件 — 探测项目自带的 language server,按文件取语义诊断",
6
6
  "repository": {
@@ -24,8 +24,8 @@
24
24
  "dependencies": {
25
25
  "vscode-jsonrpc": "^9.0.1",
26
26
  "vscode-languageserver-protocol": "^3.17.5",
27
- "@epoch-agent/infra": "3.2.0",
28
- "@epoch-agent/protocol": "3.2.0"
27
+ "@epoch-agent/infra": "4.0.0",
28
+ "@epoch-agent/protocol": "4.0.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "^26.1.2",