@trim21/personal-pi-extensions 0.1.611 → 0.1.613

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 (75) hide show
  1. package/package.json +2 -2
  2. package/src/aft/config.ts +9 -3
  3. package/src/aft/index.ts +6 -2
  4. package/src/aft/logger.ts +9 -3
  5. package/src/aft/tools.ts +5 -3
  6. package/src/bwrap/approval-rules.ts +51 -18
  7. package/src/bwrap/approval-suggest.ts +12 -4
  8. package/src/bwrap/core.ts +39 -13
  9. package/src/bwrap/dcg-scan.ts +16 -6
  10. package/src/bwrap/holder.ts +3 -1
  11. package/src/bwrap/mihomo-config.ts +18 -6
  12. package/src/bwrap/network-stack.ts +30 -10
  13. package/src/bwrap/runtime.ts +36 -14
  14. package/src/claude-code/common.ts +6 -2
  15. package/src/claude-code/edit-utils.ts +3 -1
  16. package/src/claude-code/files.ts +40 -14
  17. package/src/claude-code/glob.ts +12 -4
  18. package/src/claude-code/grep.ts +41 -14
  19. package/src/claude-code/session-tools.ts +18 -6
  20. package/src/claude-code/shell.ts +9 -3
  21. package/src/gh/base.ts +72 -24
  22. package/src/gh/tools/download-release-assets.ts +12 -4
  23. package/src/gh/tools/list-releases.ts +3 -1
  24. package/src/gh/tools/list-workflow-runs.ts +9 -3
  25. package/src/gh/tools/read-ci-logs.ts +21 -7
  26. package/src/gh/tools/read-repo.ts +3 -1
  27. package/src/lib/cli-args.ts +6 -2
  28. package/src/lib/cli.ts +55 -22
  29. package/src/lib/file-reads.ts +24 -8
  30. package/src/lib/github.ts +35 -12
  31. package/src/lib/lsp/adapter.ts +6 -2
  32. package/src/lib/lsp/bin.ts +12 -4
  33. package/src/lib/lsp/client.ts +205 -69
  34. package/src/lib/lsp/diagnostic.ts +6 -2
  35. package/src/lib/lsp/inspect-tool.ts +25 -9
  36. package/src/lib/lsp/lsp.ts +154 -58
  37. package/src/lib/lsp/rename-tool.ts +11 -4
  38. package/src/lib/lsp/rename.ts +25 -8
  39. package/src/lib/lsp/server-config.ts +41 -14
  40. package/src/lib/lsp/watcher.ts +51 -17
  41. package/src/lib/path.ts +18 -6
  42. package/src/lib/proc.ts +3 -1
  43. package/src/lib/proxy.ts +9 -3
  44. package/src/lib/request-policy.ts +3 -1
  45. package/src/lib/seq-state.ts +3 -1
  46. package/src/lib/ui.ts +23 -8
  47. package/src/lib/write-guard.ts +21 -7
  48. package/src/openai-cost/config.ts +3 -1
  49. package/src/openai-cost/cost.ts +54 -18
  50. package/src/openai-cost/index.ts +3 -1
  51. package/src/openai-cost/provider.ts +6 -2
  52. package/src/opencode/bash.ts +9 -3
  53. package/src/opencode/edit-engine.ts +63 -41
  54. package/src/opencode/files.ts +30 -10
  55. package/src/opencode/glob.ts +3 -1
  56. package/src/opencode/grep.ts +12 -4
  57. package/src/opencode/question.ts +3 -1
  58. package/src/opencode/ripgrep.ts +23 -9
  59. package/src/opencode/todo.ts +3 -1
  60. package/src/session-name.ts +40 -16
  61. package/src/spawn-agent-agents.ts +18 -6
  62. package/src/spawn-agent.ts +58 -21
  63. package/src/system-prompt/index.ts +9 -3
  64. package/src/talk/core.ts +143 -48
  65. package/src/talk/format.ts +12 -4
  66. package/src/talk/group.ts +9 -3
  67. package/src/talk/index.ts +30 -10
  68. package/src/talk/mailbox.ts +30 -10
  69. package/src/talk/policy.ts +10 -5
  70. package/src/talk/registry.ts +35 -12
  71. package/src/talk/storage.ts +3 -1
  72. package/src/vision-agent.ts +41 -14
  73. package/src/web/config.ts +3 -1
  74. package/src/web/fetch.ts +75 -25
  75. package/src/web/search.ts +30 -10
@@ -138,14 +138,19 @@ async function formatReferenceLocations(
138
138
  const byPath = new Map<string, InspectLocation[]>();
139
139
  for (const location of locations) {
140
140
  const existing = byPath.get(location.path);
141
- if (existing) existing.push(location);
142
- else byPath.set(location.path, [location]);
141
+ if (existing) {
142
+ existing.push(location);
143
+ } else {
144
+ byPath.set(location.path, [location]);
145
+ }
143
146
  }
144
147
  const paths = [...byPath.keys()];
145
148
  const sections = [`Found ${locations.length} reference(s) in ${paths.length} file(s):`];
146
149
  for (const path of paths.slice(0, MAX_FILES_LISTED)) {
147
150
  const entries = byPath.get(path);
148
- if (!entries) continue;
151
+ if (!entries) {
152
+ continue;
153
+ }
149
154
  const shown = entries.slice(0, MAX_ENTRIES_PER_FILE);
150
155
  sections.push(`### ${path} (${entries.length})`);
151
156
  for (const entry of shown) {
@@ -177,11 +182,15 @@ function formatMarkedString(marked: MarkedString): string {
177
182
 
178
183
  /** hover contents 原样透传:仅做结构格式化(MarkedString → code fence),不改写内容。 */
179
184
  function formatHoverContents(contents: Hover["contents"]): string {
180
- if (typeof contents === "string") return contents;
185
+ if (typeof contents === "string") {
186
+ return contents;
187
+ }
181
188
  if (Array.isArray(contents)) {
182
189
  return contents.map((marked) => formatMarkedString(marked)).join("\n\n");
183
190
  }
184
- if ("language" in contents) return formatMarkedString(contents);
191
+ if ("language" in contents) {
192
+ return formatMarkedString(contents);
193
+ }
185
194
  return contents.value;
186
195
  }
187
196
 
@@ -217,10 +226,15 @@ async function probeSymbolCandidates(options: {
217
226
  const groups = new Map<string, { output: InspectOutput; candidates: LspPosition[] }>();
218
227
  for (const [index, candidate] of candidates.entries()) {
219
228
  const output = outputs.at(index);
220
- if (!output) continue;
229
+ if (!output) {
230
+ continue;
231
+ }
221
232
  const group = groups.get(output.text);
222
- if (group) group.candidates.push(candidate);
223
- else groups.set(output.text, { output, candidates: [candidate] });
233
+ if (group) {
234
+ group.candidates.push(candidate);
235
+ } else {
236
+ groups.set(output.text, { output, candidates: [candidate] });
237
+ }
224
238
  }
225
239
  if (groups.size > 1) {
226
240
  const listing = [...groups.values()]
@@ -235,7 +249,9 @@ async function probeSymbolCandidates(options: {
235
249
  );
236
250
  }
237
251
  const first = [...groups.values()].at(0);
238
- if (!first) throw new Error("LSP inspect returned no result");
252
+ if (!first) {
253
+ throw new Error("LSP inspect returned no result");
254
+ }
239
255
  return first.output;
240
256
  }
241
257
 
@@ -152,7 +152,9 @@ export interface ResolvedLspConfig {
152
152
  function parseTimeoutString(value: string): number {
153
153
  // 单位组永远参与匹配(缺省为空串),避免"可选捕获组在类型上不可空"的歧义
154
154
  const match = /^(\d+(?:\.\d+)?)(ms|s|m|h|)\s*$/.exec(value.trim());
155
- if (!match) return NaN;
155
+ if (!match) {
156
+ return NaN;
157
+ }
156
158
  const amount = Number(match[1]);
157
159
  const factors: Record<string, number | undefined> = {
158
160
  "": 1,
@@ -167,7 +169,9 @@ function parseTimeoutString(value: string): number {
167
169
 
168
170
  /** 时长字段换算为 ms:number 原样、字符串按 parseTimeoutString;无效值返回 undefined。 */
169
171
  function toMs(value: number | string | undefined): number | undefined {
170
- if (value === undefined) return undefined;
172
+ if (value === undefined) {
173
+ return undefined;
174
+ }
171
175
  const ms = typeof value === "number" ? value : parseTimeoutString(value);
172
176
  return Number.isFinite(ms) && ms > 0 ? ms : undefined;
173
177
  }
@@ -224,11 +228,15 @@ function unknownFieldWarnings(config: LspConfig, filePath: string): string[] {
224
228
  const warnings: string[] = [];
225
229
  const check = (record: object, known: Record<string, unknown>, scope: string): void => {
226
230
  for (const key of Object.keys(record)) {
227
- if (!(key in known)) warnings.push(`${filePath}${scope}: unknown field "${key}" ignored`);
231
+ if (!(key in known)) {
232
+ warnings.push(`${filePath}${scope}: unknown field "${key}" ignored`);
233
+ }
228
234
  }
229
235
  };
230
236
  check(config, lspConfigSchema.properties, "");
231
- if (config.watch) check(config.watch, watchConfigSchema.properties, " watch");
237
+ if (config.watch) {
238
+ check(config.watch, watchConfigSchema.properties, " watch");
239
+ }
232
240
  for (const [id, server] of Object.entries(config.servers ?? {})) {
233
241
  check(server, serverConfigSchema.properties, ` (server "${id}")`);
234
242
  }
@@ -243,10 +251,14 @@ async function readConfigFile(
243
251
  try {
244
252
  const raw = await readFile(filePath, "utf8");
245
253
  const config = Value.Parse(lspConfigSchema, JSON.parse(raw) as unknown);
246
- for (const message of unknownFieldWarnings(config, filePath)) onWarning?.(message);
254
+ for (const message of unknownFieldWarnings(config, filePath)) {
255
+ onWarning?.(message);
256
+ }
247
257
  return config;
248
258
  } catch (error) {
249
- if (isMissingFile(error)) return {};
259
+ if (isMissingFile(error)) {
260
+ return {};
261
+ }
250
262
  throw error;
251
263
  }
252
264
  }
@@ -256,10 +268,14 @@ function readConfigFileSync(filePath: string, onWarning?: (message: string) => v
256
268
  try {
257
269
  const raw = readFileSync(filePath, "utf8");
258
270
  const config = Value.Parse(lspConfigSchema, JSON.parse(raw) as unknown);
259
- for (const message of unknownFieldWarnings(config, filePath)) onWarning?.(message);
271
+ for (const message of unknownFieldWarnings(config, filePath)) {
272
+ onWarning?.(message);
273
+ }
260
274
  return config;
261
275
  } catch (error) {
262
- if (isMissingFile(error)) return {};
276
+ if (isMissingFile(error)) {
277
+ return {};
278
+ }
263
279
  throw error;
264
280
  }
265
281
  }
@@ -274,12 +290,13 @@ function validateConfig(config: ResolvedLspConfig, adapters?: LspServerAdapter[]
274
290
  adapters ? adapters.map((adapter) => adapter.id) : Object.keys(config.servers),
275
291
  );
276
292
  const unknown = config.enabled === undefined ? [] : [...config.enabled.difference(available)];
277
- if (unknown.length > 0) {
278
- const list = [...available].toSorted().join(", ") || "none";
279
- throw new Error(
280
- `lsp.json: unknown server id in enabled: ${unknown.join(", ")} (available: ${list})`,
281
- );
293
+ if (unknown.length === 0) {
294
+ return;
282
295
  }
296
+ const list = [...available].toSorted().join(", ") || "none";
297
+ throw new Error(
298
+ `lsp.json: unknown server id in enabled: ${unknown.join(", ")} (available: ${list})`,
299
+ );
283
300
  }
284
301
 
285
302
  /** 合并 watch 段:全局为基底、本地逐字段覆盖;ignore 取并集去重(全局在前)。两边都未配置时返回 undefined,调用方据此省略 watch 键。 */
@@ -287,7 +304,9 @@ function mergeWatch(
287
304
  globalWatch: LspConfig["watch"],
288
305
  localWatch: LspConfig["watch"],
289
306
  ): LspConfig["watch"] {
290
- if (!globalWatch && !localWatch) return undefined;
307
+ if (!globalWatch && !localWatch) {
308
+ return undefined;
309
+ }
291
310
  return {
292
311
  ...globalWatch,
293
312
  ...localWatch,
@@ -336,8 +355,12 @@ export function filterAdapters(
336
355
  ): LspServerAdapter[] {
337
356
  validateConfig(config, adapters);
338
357
  return adapters.filter((adapter) => {
339
- if (config.enabled && !config.enabled.has(adapter.id)) return false;
340
- if (config.disabled?.has(adapter.id)) return false;
358
+ if (config.enabled && !config.enabled.has(adapter.id)) {
359
+ return false;
360
+ }
361
+ if (config.disabled?.has(adapter.id)) {
362
+ return false;
363
+ }
341
364
  return true;
342
365
  });
343
366
  }
@@ -531,7 +554,9 @@ export function createLspService(
531
554
 
532
555
  /** 配置缓存:同一 cwd 内复用;cwd 变化或 reload 清缓存后重读。 */
533
556
  async function currentConfig(cwd: string): Promise<ResolvedLspConfig> {
534
- if (state.config && state.configCwd === cwd) return state.config;
557
+ if (state.config && state.configCwd === cwd) {
558
+ return state.config;
559
+ }
535
560
  const config = await loadLspConfig(cwd, globalConfigPath, (message) =>
536
561
  sessionNotify?.(message, "warning"),
537
562
  );
@@ -552,8 +577,12 @@ export function createLspService(
552
577
  * 退化为 cwd(client 只服务 cwd 内的文件);与 cwd 无交集时返回 undefined。
553
578
  */
554
579
  function watchDirFor(root: string, cwd: string): string | undefined {
555
- if (containsPath(root, cwd)) return root;
556
- if (containsPath(cwd, root)) return cwd;
580
+ if (containsPath(root, cwd)) {
581
+ return root;
582
+ }
583
+ if (containsPath(cwd, root)) {
584
+ return cwd;
585
+ }
557
586
  return undefined;
558
587
  }
559
588
 
@@ -562,7 +591,9 @@ export function createLspService(
562
591
  const dirs = new Set<string>();
563
592
  for (const client of state.clients) {
564
593
  const dir = watchDirFor(client.root, cwd);
565
- if (dir && !watcherFailed.has(dir)) dirs.add(dir);
594
+ if (dir && !watcherFailed.has(dir)) {
595
+ dirs.add(dir);
596
+ }
566
597
  }
567
598
  return [...dirs]
568
599
  .filter((dir) => [...dirs].every((other) => other === dir || !containsPath(dir, other)))
@@ -645,7 +676,9 @@ export function createLspService(
645
676
  cwd: string,
646
677
  notify?: ExtensionUIContext["notify"],
647
678
  ): Promise<void> {
648
- if (state.closing || state.disabled) return;
679
+ if (state.closing || state.disabled) {
680
+ return;
681
+ }
649
682
  let config: ResolvedLspConfig;
650
683
  try {
651
684
  config = await currentConfig(cwd);
@@ -662,23 +695,31 @@ export function createLspService(
662
695
  const desired = config.watch.enabled ? desiredWatchRoots(cwd) : [];
663
696
  // Map 迭代期间删除当前项是安全的
664
697
  for (const [root, watcher] of watchers) {
665
- if (desired.includes(root)) continue;
698
+ if (desired.includes(root)) {
699
+ continue;
700
+ }
666
701
  watchers.delete(root);
667
702
  void watcher.stop().catch(() => {
668
703
  // 停止失败不影响流程
669
704
  });
670
705
  }
671
706
  for (const root of desired) {
672
- if (watchers.has(root)) continue;
707
+ if (watchers.has(root)) {
708
+ continue;
709
+ }
673
710
  const watcher = await startWatcher(root, config.watch, notify);
674
- if (watcher) watchers.set(root, watcher);
711
+ if (watcher) {
712
+ watchers.set(root, watcher);
713
+ }
675
714
  }
676
715
  }
677
716
 
678
717
  /** 事件按各 client 的 root 前缀 / 注册 pattern / 扩展名过滤后投递。 */
679
718
  async function fanOut(changes: FileChange[]): Promise<void> {
680
719
  const cwd = state.cwd;
681
- if (!cwd) return;
720
+ if (!cwd) {
721
+ return;
722
+ }
682
723
  await Promise.all(
683
724
  state.clients.map(async (client) => {
684
725
  const filtered = changes.filter((change) => {
@@ -704,7 +745,9 @@ export function createLspService(
704
745
  (minimatch(relativeCandidate, watcher.pattern) ||
705
746
  minimatch(absoluteCandidate, watcher.pattern)),
706
747
  );
707
- if (!matched) return false;
748
+ if (!matched) {
749
+ return false;
750
+ }
708
751
  }
709
752
  const extensions = state.clientExtensions.get(client);
710
753
  return (
@@ -713,7 +756,9 @@ export function createLspService(
713
756
  extensions.includes(extname(change.path))
714
757
  );
715
758
  });
716
- if (filtered.length === 0) return;
759
+ if (filtered.length === 0) {
760
+ return;
761
+ }
717
762
  try {
718
763
  await client.notify.watchedFiles(filtered);
719
764
  } catch {
@@ -725,7 +770,9 @@ export function createLspService(
725
770
 
726
771
  /** 汇总当前所有 LSP server 状态并渲染到 footer status。 */
727
772
  function updateStatusText(): void {
728
- if (!renderStatus) return;
773
+ if (!renderStatus) {
774
+ return;
775
+ }
729
776
  if (state.disabled) {
730
777
  renderStatus("lsp: disabled");
731
778
  return;
@@ -764,14 +811,17 @@ export function createLspService(
764
811
  updateStatusText();
765
812
  const reporter = sessionNotify ?? notify;
766
813
  const lastNotified = state.brokenNotifiedAt.get(key);
767
- if (reporter && (lastNotified === undefined || now - lastNotified >= notifyIntervalMs)) {
768
- state.brokenNotifiedAt.set(key, now);
769
- reporter(
770
- `LSP server "${serverID}" failed to start for ${root}: ${cause}. ` +
771
- `Fix the issue or run /lsp-reload ${serverID} to retry now.`,
772
- "error",
773
- );
814
+ const due = lastNotified === undefined || now - lastNotified >= notifyIntervalMs;
815
+ if (!reporter || !due) {
816
+ return;
774
817
  }
818
+
819
+ state.brokenNotifiedAt.set(key, now);
820
+ reporter(
821
+ `LSP server "${serverID}" failed to start for ${root}: ${cause}. ` +
822
+ `Fix the issue or run /lsp-reload ${serverID} to retry now.`,
823
+ "error",
824
+ );
775
825
  }
776
826
 
777
827
  /**
@@ -788,7 +838,9 @@ export function createLspService(
788
838
  ): Promise<LspClient | undefined> {
789
839
  const key = root + adapter.id;
790
840
  const inflight = state.spawning.get(key);
791
- if (inflight) return inflight;
841
+ if (inflight) {
842
+ return inflight;
843
+ }
792
844
  const task = (async () => {
793
845
  try {
794
846
  const handle = await adapter.spawn(root, cwd);
@@ -841,7 +893,9 @@ export function createLspService(
841
893
  })();
842
894
  state.spawning.set(key, task);
843
895
  void task.finally(() => {
844
- if (state.spawning.get(key) === task) state.spawning.delete(key);
896
+ if (state.spawning.get(key) === task) {
897
+ state.spawning.delete(key);
898
+ }
845
899
  });
846
900
  return task;
847
901
  }
@@ -852,8 +906,12 @@ export function createLspService(
852
906
  notify?: ExtensionUIContext["notify"],
853
907
  adapterFilter?: (adapter: LspServerAdapter) => boolean,
854
908
  ): Promise<LspClient[]> {
855
- if (state.closing || state.disabled) return [];
856
- if (!containsPath(file, cwd)) return [];
909
+ if (state.closing || state.disabled) {
910
+ return [];
911
+ }
912
+ if (!containsPath(file, cwd)) {
913
+ return [];
914
+ }
857
915
  const config = await currentConfig(cwd);
858
916
  const active = filterAdapters(adapters ?? createAdapters(config.servers), config);
859
917
  const extension = extname(file) || file;
@@ -866,15 +924,25 @@ export function createLspService(
866
924
  }
867
925
 
868
926
  for (const adapter of active) {
869
- if (adapterFilter && !adapterFilter(adapter)) continue;
870
- if (adapter.extensions.length > 0 && !adapter.extensions.includes(extension)) continue;
927
+ if (adapterFilter && !adapterFilter(adapter)) {
928
+ continue;
929
+ }
930
+ if (adapter.extensions.length > 0 && !adapter.extensions.includes(extension)) {
931
+ continue;
932
+ }
871
933
  const root = serverRoot(adapter, file, cwd);
872
- if (!containsPath(file, root)) continue;
873
- if (!matchesInclude(adapter.include ?? [], file, root, cwd)) continue;
934
+ if (!containsPath(file, root)) {
935
+ continue;
936
+ }
937
+ if (!matchesInclude(adapter.include ?? [], file, root, cwd)) {
938
+ continue;
939
+ }
874
940
  const key = root + adapter.id;
875
941
  const failedAt = state.brokenFailAt.get(key);
876
942
  if (failedAt !== undefined) {
877
- if (Date.now() - failedAt < retryCooldownMs) continue;
943
+ if (Date.now() - failedAt < retryCooldownMs) {
944
+ continue;
945
+ }
878
946
  // 冷却已过:允许重试;重试成功后下面会清除 broken 记录
879
947
  state.brokenFailAt.delete(key);
880
948
  }
@@ -886,7 +954,9 @@ export function createLspService(
886
954
  }
887
955
 
888
956
  const client = await startClient(adapter, root, cwd, config, notify);
889
- if (client) result.push(client);
957
+ if (client) {
958
+ result.push(client);
959
+ }
890
960
  }
891
961
 
892
962
  return result;
@@ -907,7 +977,9 @@ export function createLspService(
907
977
  clients.map(async (client) => {
908
978
  const after = Date.now();
909
979
  const version = await client.notify.open({ path: file });
910
- if (!diagnostics) return;
980
+ if (!diagnostics) {
981
+ return;
982
+ }
911
983
  await abortable(
912
984
  client.waitForDiagnostics({
913
985
  path: file,
@@ -944,7 +1016,9 @@ export function createLspService(
944
1016
  cwd: string,
945
1017
  options?: LspRequestOptions,
946
1018
  ): Promise<DiagnosticReport> {
947
- if (options?.signal?.aborted) return EMPTY_DIAGNOSTIC_REPORT;
1019
+ if (options?.signal?.aborted) {
1020
+ return EMPTY_DIAGNOSTIC_REPORT;
1021
+ }
948
1022
  await touchFile(file, cwd, "document", options);
949
1023
  const all = await diagnostics();
950
1024
  const normalized = normalize(file);
@@ -1103,7 +1177,9 @@ export function createLspService(
1103
1177
  running: readonly { serverID: string; root: string }[],
1104
1178
  ): Promise<string[]> {
1105
1179
  const cwd = state.cwd;
1106
- if (!cwd) return [];
1180
+ if (!cwd) {
1181
+ return [];
1182
+ }
1107
1183
  let config: ResolvedLspConfig;
1108
1184
  try {
1109
1185
  config = await currentConfig(cwd);
@@ -1121,7 +1197,9 @@ export function createLspService(
1121
1197
  const restarted = await Promise.all(
1122
1198
  [...unique.values()].map(async ({ serverID, root }): Promise<string | undefined> => {
1123
1199
  const adapter = active.find((candidate) => candidate.id === serverID);
1124
- if (!adapter) return;
1200
+ if (!adapter) {
1201
+ return;
1202
+ }
1125
1203
  const client = await startClient(adapter, root, cwd, config);
1126
1204
  return client ? serverID : undefined;
1127
1205
  }),
@@ -1140,9 +1218,13 @@ export function createLspService(
1140
1218
  // 个别进程退出失败不阻止清理流程
1141
1219
  });
1142
1220
  state.clients = state.clients.filter((client) => client.serverID !== serverID);
1143
- for (const client of targets) state.clientExtensions.delete(client);
1221
+ for (const client of targets) {
1222
+ state.clientExtensions.delete(client);
1223
+ }
1144
1224
  for (const [key, server] of state.servers) {
1145
- if (server.serverID !== serverID) continue;
1225
+ if (server.serverID !== serverID) {
1226
+ continue;
1227
+ }
1146
1228
  state.brokenFailAt.delete(key);
1147
1229
  state.brokenNotifiedAt.delete(key);
1148
1230
  state.servers.delete(key);
@@ -1151,9 +1233,13 @@ export function createLspService(
1151
1233
  state.disabled = false;
1152
1234
  // reload 是资源耗尽后的重试入口:清掉停用记录并按新配置重算监听范围
1153
1235
  watcherFailed.clear();
1154
- if (state.cwd) await reconcileWatchers(state.cwd);
1236
+ if (state.cwd) {
1237
+ await reconcileWatchers(state.cwd);
1238
+ }
1155
1239
  updateStatusText();
1156
- if (running.length === 0) return [];
1240
+ if (running.length === 0) {
1241
+ return [];
1242
+ }
1157
1243
  return respawnRunning(running);
1158
1244
  }
1159
1245
 
@@ -1176,7 +1262,9 @@ export function createLspService(
1176
1262
  state.closing = false;
1177
1263
  state.disabled = false;
1178
1264
  watcherFailed.clear();
1179
- if (state.cwd) await reconcileWatchers(state.cwd);
1265
+ if (state.cwd) {
1266
+ await reconcileWatchers(state.cwd);
1267
+ }
1180
1268
  updateStatusText();
1181
1269
  return respawnRunning(running);
1182
1270
  }
@@ -1243,8 +1331,12 @@ function getNoopService(): LspService {
1243
1331
  function enabledServerCount(config: ResolvedLspConfig, adapters?: LspServerAdapter[]): number {
1244
1332
  const ids = adapters ? adapters.map((adapter) => adapter.id) : Object.keys(config.servers);
1245
1333
  return ids.filter((id) => {
1246
- if (config.enabled && !config.enabled.has(id)) return false;
1247
- if (config.disabled?.has(id)) return false;
1334
+ if (config.enabled && !config.enabled.has(id)) {
1335
+ return false;
1336
+ }
1337
+ if (config.disabled?.has(id)) {
1338
+ return false;
1339
+ }
1248
1340
  return true;
1249
1341
  }).length;
1250
1342
  }
@@ -1282,7 +1374,9 @@ export function createLspManager(
1282
1374
  }
1283
1375
  });
1284
1376
  validateConfig(config, options?.adapters);
1285
- if (enabledServerCount(config, options?.adapters) === 0) return;
1377
+ if (enabledServerCount(config, options?.adapters) === 0) {
1378
+ return;
1379
+ }
1286
1380
  // 下面两个闭包被 service 长期持有,可能在会话被替换或 reload 后仍触发
1287
1381
  // (session_shutdown 清理、后台 watcher 错误、in-flight spawn 失败),
1288
1382
  // 而旧 ctx 已被 pi 标记 stale,ctx.ui getter 会直接抛错。UI 写入是尽力
@@ -1313,7 +1407,9 @@ export function createLspManager(
1313
1407
  hooks.onEnabled(pi, next);
1314
1408
  } catch (error) {
1315
1409
  service = undefined;
1316
- if (error instanceof Error) ctx.ui.notify(error.message, "error");
1410
+ if (error instanceof Error) {
1411
+ ctx.ui.notify(error.message, "error");
1412
+ }
1317
1413
  }
1318
1414
  });
1319
1415
 
@@ -161,8 +161,11 @@ export function registerLspRenameTool(
161
161
  for (const { candidate, result: probe } of successes) {
162
162
  const key = canonicalizeEdit(probe.edit);
163
163
  const group = groups.get(key);
164
- if (group) group.candidates.push(candidate);
165
- else groups.set(key, { result: probe, candidates: [candidate] });
164
+ if (group) {
165
+ group.candidates.push(candidate);
166
+ } else {
167
+ groups.set(key, { result: probe, candidates: [candidate] });
168
+ }
166
169
  }
167
170
  if (groups.size > 1) {
168
171
  const listing = [...groups.values()]
@@ -180,7 +183,9 @@ export function registerLspRenameTool(
180
183
  );
181
184
  }
182
185
  const firstGroup = [...groups.values()].at(0);
183
- if (!firstGroup) throw new Error("LSP rename returned no target");
186
+ if (!firstGroup) {
187
+ throw new Error("LSP rename returned no target");
188
+ }
184
189
  const { result } = firstGroup;
185
190
 
186
191
  // ── 内存展开 → 审批 → 写盘 → 诊断 ────────────────────────────────────
@@ -218,7 +223,9 @@ export function registerLspRenameTool(
218
223
  notify,
219
224
  signal,
220
225
  });
221
- if (diagnostics.text !== "") diagnosticText += `${diagnostics.text}\n`;
226
+ if (diagnostics.text !== "") {
227
+ diagnosticText += `${diagnostics.text}\n`;
228
+ }
222
229
  }
223
230
 
224
231
  const renamed =
@@ -55,7 +55,9 @@ function toPath(uri: string): string {
55
55
  function lineStartOffsets(text: string): number[] {
56
56
  const starts = [0];
57
57
  for (let i = 0; i < text.length; i++) {
58
- if (text.codePointAt(i) === 10) starts.push(i + 1);
58
+ if (text.codePointAt(i) === 10) {
59
+ starts.push(i + 1);
60
+ }
59
61
  }
60
62
  return starts;
61
63
  }
@@ -64,7 +66,9 @@ function lineStartOffsets(text: string): number[] {
64
66
  function lineContentLength(text: string, starts: readonly number[], line: number): number {
65
67
  const base = starts[line];
66
68
  let end = line + 1 < starts.length ? starts[line + 1] - 1 : text.length;
67
- if (end > base && text.codePointAt(end - 1) === 13) end--;
69
+ if (end > base && text.codePointAt(end - 1) === 13) {
70
+ end--;
71
+ }
68
72
  return end - base;
69
73
  }
70
74
 
@@ -117,8 +121,11 @@ function collectTextEdits(edit: WorkspaceEdit): Map<string, RangeLike[]> {
117
121
  const byPath = new Map<string, RangeLike[]>();
118
122
  const push = (path: string, edits: readonly RangeLike[]): void => {
119
123
  const existing = byPath.get(path);
120
- if (existing) existing.push(...edits);
121
- else byPath.set(path, [...edits]);
124
+ if (existing) {
125
+ existing.push(...edits);
126
+ } else {
127
+ byPath.set(path, [...edits]);
128
+ }
122
129
  };
123
130
  for (const [uri, edits] of Object.entries(edit.changes ?? {})) {
124
131
  push(toPath(uri), edits.filter(isRangeLike));
@@ -190,8 +197,14 @@ const WORD_PATTERN = /[\p{L}\p{N}_$]+/gu;
190
197
 
191
198
  /** 两个路径集合是否一致(用于判断 references 结果是否收敛)。 */
192
199
  export function samePathSet(a: ReadonlySet<string>, b: ReadonlySet<string>): boolean {
193
- if (a.size !== b.size) return false;
194
- for (const path of a) if (!b.has(path)) return false;
200
+ if (a.size !== b.size) {
201
+ return false;
202
+ }
203
+ for (const path of a) {
204
+ if (!b.has(path)) {
205
+ return false;
206
+ }
207
+ }
195
208
  return true;
196
209
  }
197
210
 
@@ -244,7 +257,9 @@ export function symbolCandidates(
244
257
  character?: number,
245
258
  ): LspPosition[] {
246
259
  const starts = lineStartOffsets(text);
247
- if (line < 0 || line >= starts.length) return [];
260
+ if (line < 0 || line >= starts.length) {
261
+ return [];
262
+ }
248
263
  const base = starts[line];
249
264
  const length = lineContentLength(text, starts, line);
250
265
  const matches = [...text.slice(base, base + length).matchAll(WORD_PATTERN)];
@@ -264,7 +279,9 @@ export function symbolCandidates(
264
279
  }
265
280
  const candidates: LspPosition[] = [];
266
281
  for (const match of matches) {
267
- if (match[0] === symbol) candidates.push({ line, character: match.index });
282
+ if (match[0] === symbol) {
283
+ candidates.push({ line, character: match.index });
284
+ }
268
285
  }
269
286
  return candidates;
270
287
  }