@trim21/personal-pi-extensions 0.1.611 → 0.1.612

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trim21/personal-pi-extensions",
3
- "version": "0.1.611",
3
+ "version": "0.1.612",
4
4
  "type": "module",
5
5
  "description": "Custom pi coding-agent extensions: bwrap sandbox, workspace guard, opencode edit, and more",
6
6
  "keywords": [
@@ -52,7 +52,7 @@
52
52
  "eslint-plugin-import-x": "^4.17.1",
53
53
  "eslint-plugin-promise": "7.3.0",
54
54
  "eslint-plugin-simple-import-sort": "14.0.0",
55
- "eslint-plugin-unicorn": "74.0.0",
55
+ "eslint-plugin-unicorn": "76.0.0",
56
56
  "eslint-plugin-unused-imports": "4.4.1",
57
57
  "husky": "^9.1.7",
58
58
  "jiti": "^2.7.0",
package/src/aft/tools.ts CHANGED
@@ -152,7 +152,7 @@ export function registerOutlineTool(pi: ExtensionAPI, ctx: AftToolContext): void
152
152
  }
153
153
  const rawArgs = compactArgs({
154
154
  target: filesMode ? target : resolved,
155
- files: filesMode ? true : undefined,
155
+ files: filesMode || undefined,
156
156
  includeTests: params.includeTests,
157
157
  });
158
158
 
@@ -232,7 +232,7 @@ export function registerZoomTool(pi: ExtensionAPI, ctx: AftToolContext): void {
232
232
  1,
233
233
  Number.MAX_SAFE_INTEGER,
234
234
  ),
235
- callgraph: coerceBoolean(params.callgraph) ? true : undefined,
235
+ callgraph: coerceBoolean(params.callgraph) || undefined,
236
236
  });
237
237
 
238
238
  const subtitle = buildZoomSubtitle(extCtx.cwd, params);
@@ -140,10 +140,15 @@ function collectCommand(node: Node, all: Node[]): BashCommand | undefined {
140
140
  // 自身且每次返回新 wrapper,`===` 比较失效会无限递归。
141
141
  const nested: BashCommand[] = [];
142
142
  for (const descendant of all) {
143
- if (descendant === node) continue;
144
- if (descendant.startIndex >= node.startIndex && descendant.endIndex <= node.endIndex) {
145
- const inner = collectCommand(descendant, all);
146
- if (inner) nested.push(inner);
143
+ if (descendant === node) {
144
+ continue;
145
+ }
146
+ if (descendant.startIndex < node.startIndex || descendant.endIndex > node.endIndex) {
147
+ continue;
148
+ }
149
+ const inner = collectCommand(descendant, all);
150
+ if (inner) {
151
+ nested.push(inner);
147
152
  }
148
153
  }
149
154
  return { name, args, raw: node.text, nested };
@@ -209,11 +209,13 @@ class BashOutput {
209
209
  this.tailBytes -= this.tail[0].length;
210
210
  this.tail.shift();
211
211
  }
212
- if (this.tailBytes > BASH_TAIL_LIMIT_BYTES && this.tail.length === 1) {
213
- // 单个 chunk 超过上限:截掉头部,只保留尾部
214
- this.tail[0] = this.tail[0].subarray(this.tailBytes - BASH_TAIL_LIMIT_BYTES);
215
- this.tailBytes = BASH_TAIL_LIMIT_BYTES;
212
+ if (this.tailBytes <= BASH_TAIL_LIMIT_BYTES || this.tail.length !== 1) {
213
+ return;
216
214
  }
215
+
216
+ // 单个 chunk 超过上限:截掉头部,只保留尾部
217
+ this.tail[0] = this.tail[0].subarray(this.tailBytes - BASH_TAIL_LIMIT_BYTES);
218
+ this.tailBytes = BASH_TAIL_LIMIT_BYTES;
217
219
  }
218
220
 
219
221
  close(): Promise<void> {
package/src/lib/cli.ts CHANGED
@@ -304,13 +304,14 @@ export function parseCommand<TFlags extends TObject>(
304
304
  );
305
305
  }
306
306
  }
307
- if (typeof raw === "string" && f.kind === "number") {
308
- const n = Number(raw);
309
- if (Number.isNaN(n)) {
310
- return errorResult(spec, `Invalid value for '--${f.key}': '${raw}'`);
311
- }
312
- rawFlags[f.key] = n;
307
+ if (typeof raw !== "string" || f.kind !== "number") {
308
+ continue;
309
+ }
310
+ const n = Number(raw);
311
+ if (Number.isNaN(n)) {
312
+ return errorResult(spec, `Invalid value for '--${f.key}': '${raw}'`);
313
313
  }
314
+ rawFlags[f.key] = n;
314
315
  }
315
316
 
316
317
  const { min, max } = spec.arity ?? {};
@@ -274,12 +274,13 @@ function validateConfig(config: ResolvedLspConfig, adapters?: LspServerAdapter[]
274
274
  adapters ? adapters.map((adapter) => adapter.id) : Object.keys(config.servers),
275
275
  );
276
276
  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
- );
277
+ if (unknown.length === 0) {
278
+ return;
282
279
  }
280
+ const list = [...available].toSorted().join(", ") || "none";
281
+ throw new Error(
282
+ `lsp.json: unknown server id in enabled: ${unknown.join(", ")} (available: ${list})`,
283
+ );
283
284
  }
284
285
 
285
286
  /** 合并 watch 段:全局为基底、本地逐字段覆盖;ignore 取并集去重(全局在前)。两边都未配置时返回 undefined,调用方据此省略 watch 键。 */
@@ -764,14 +765,17 @@ export function createLspService(
764
765
  updateStatusText();
765
766
  const reporter = sessionNotify ?? notify;
766
767
  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
- );
768
+ const due = lastNotified === undefined || now - lastNotified >= notifyIntervalMs;
769
+ if (!reporter || !due) {
770
+ return;
774
771
  }
772
+
773
+ state.brokenNotifiedAt.set(key, now);
774
+ reporter(
775
+ `LSP server "${serverID}" failed to start for ${root}: ${cause}. ` +
776
+ `Fix the issue or run /lsp-reload ${serverID} to retry now.`,
777
+ "error",
778
+ );
775
779
  }
776
780
 
777
781
  /**
@@ -119,20 +119,21 @@ const LineTrimmedReplacer: Replacer = function* (content, find) {
119
119
  break;
120
120
  }
121
121
  }
122
- if (matches) {
123
- let matchStartIndex = 0;
124
- for (let k = 0; k < i; k++) {
125
- matchStartIndex += originalLines[k].length + 1;
126
- }
127
- let matchEndIndex = matchStartIndex;
128
- for (let k = 0; k < searchLines.length; k++) {
129
- matchEndIndex += originalLines[i + k].length;
130
- if (k < searchLines.length - 1) {
131
- matchEndIndex += 1;
132
- }
122
+ if (!matches) {
123
+ continue;
124
+ }
125
+ let matchStartIndex = 0;
126
+ for (let k = 0; k < i; k++) {
127
+ matchStartIndex += originalLines[k].length + 1;
128
+ }
129
+ let matchEndIndex = matchStartIndex;
130
+ for (let k = 0; k < searchLines.length; k++) {
131
+ matchEndIndex += originalLines[i + k].length;
132
+ if (k < searchLines.length - 1) {
133
+ matchEndIndex += 1;
133
134
  }
134
- yield content.slice(matchStartIndex, matchEndIndex);
135
135
  }
136
+ yield content.slice(matchStartIndex, matchEndIndex);
136
137
  }
137
138
  };
138
139
 
@@ -230,26 +231,29 @@ const BlockAnchorReplacer: Replacer = function* (content, find) {
230
231
  } else {
231
232
  similarity = 1;
232
233
  }
233
- if (similarity > maxSimilarity) {
234
- maxSimilarity = similarity;
235
- bestMatch = candidate;
234
+ if (similarity <= maxSimilarity) {
235
+ continue;
236
236
  }
237
+
238
+ maxSimilarity = similarity;
239
+ bestMatch = candidate;
237
240
  }
238
- if (bestMatch && maxSimilarity >= MULTIPLE_CANDIDATES_SIMILARITY_THRESHOLD) {
239
- const { startLine, endLine } = bestMatch;
240
- let matchStartIndex = 0;
241
- for (let k = 0; k < startLine; k++) {
242
- matchStartIndex += originalLines[k].length + 1;
243
- }
244
- let matchEndIndex = matchStartIndex;
245
- for (let k = startLine; k <= endLine; k++) {
246
- matchEndIndex += originalLines[k].length;
247
- if (k < endLine) {
248
- matchEndIndex += 1;
249
- }
241
+ if (!bestMatch || maxSimilarity < MULTIPLE_CANDIDATES_SIMILARITY_THRESHOLD) {
242
+ return;
243
+ }
244
+ const { startLine, endLine } = bestMatch;
245
+ let matchStartIndex = 0;
246
+ for (let k = 0; k < startLine; k++) {
247
+ matchStartIndex += originalLines[k].length + 1;
248
+ }
249
+ let matchEndIndex = matchStartIndex;
250
+ for (let k = startLine; k <= endLine; k++) {
251
+ matchEndIndex += originalLines[k].length;
252
+ if (k < endLine) {
253
+ matchEndIndex += 1;
250
254
  }
251
- yield content.slice(matchStartIndex, matchEndIndex);
252
255
  }
256
+ yield content.slice(matchStartIndex, matchEndIndex);
253
257
  };
254
258
 
255
259
  function normalizeWhitespace(text: string): string {
@@ -370,11 +374,13 @@ const ContextAwareReplacer: Replacer = function* (content, find) {
370
374
  for (let k = 1; k < blockLines.length - 1; k++) {
371
375
  const blockLine = blockLines[k].trim();
372
376
  const findLine = findLines[k].trim();
373
- if (blockLine.length > 0 || findLine.length > 0) {
374
- totalNonEmptyLines++;
375
- if (blockLine === findLine) {
376
- matchingLines++;
377
- }
377
+ if (blockLine.length === 0 && findLine.length === 0) {
378
+ continue;
379
+ }
380
+
381
+ totalNonEmptyLines++;
382
+ if (blockLine === findLine) {
383
+ matchingLines++;
378
384
  }
379
385
  }
380
386
  if (totalNonEmptyLines === 0 || matchingLines / totalNonEmptyLines >= 0.5) {
@@ -88,10 +88,12 @@ export function runRipgrep<R>(
88
88
  }
89
89
  if (item === undefined) return;
90
90
  items.push(item);
91
- if (items.length > options.limit) {
92
- state.truncated = true;
93
- stop();
91
+ if (items.length <= options.limit) {
92
+ return;
94
93
  }
94
+
95
+ state.truncated = true;
96
+ stop();
95
97
  };
96
98
 
97
99
  child.stderr.setEncoding("utf8");
@@ -171,12 +171,10 @@ export function extractFirstUserPrompt(
171
171
  currentPrompt: string,
172
172
  ): string | undefined {
173
173
  const trimmed = currentPrompt.trim();
174
- if (branch.length > 0) {
175
- for (const entry of branch) {
176
- if (entry.type !== "message") continue;
177
- const text = messageText(entry.message?.content);
178
- if (text) return text;
179
- }
174
+ for (const entry of branch) {
175
+ if (entry.type !== "message") continue;
176
+ const text = messageText(entry.message?.content);
177
+ if (text) return text;
180
178
  }
181
179
  return trimmed || undefined;
182
180
  }
@@ -286,10 +286,12 @@ export function overrideExtensionPaths(tools: string[]): string[] {
286
286
  const paths: string[] = [];
287
287
  for (const tool of tools) {
288
288
  const ext = TOOL_EXTENSION_OVERRIDES[tool];
289
- if (ext && !loaded.has(ext)) {
290
- loaded.add(ext);
291
- paths.push(extensionPath(ext));
289
+ if (!ext || loaded.has(ext)) {
290
+ continue;
292
291
  }
292
+
293
+ loaded.add(ext);
294
+ paths.push(extensionPath(ext));
293
295
  }
294
296
  return paths;
295
297
  }
@@ -68,11 +68,12 @@ export class OutboundPolicy {
68
68
  this.sentAt.push(this.now());
69
69
  this.recentBodies.set(`${body}\u0000${target ?? ""}`, this.now());
70
70
  // bound the dedupe map
71
- if (this.recentBodies.size > 200) {
72
- const cutoff = this.now() - DEDUPE_WINDOW_MS;
73
- for (const [key, ts] of this.recentBodies) {
74
- if (ts < cutoff) this.recentBodies.delete(key);
75
- }
71
+ if (this.recentBodies.size <= 200) {
72
+ return;
73
+ }
74
+ const cutoff = this.now() - DEDUPE_WINDOW_MS;
75
+ for (const [key, ts] of this.recentBodies) {
76
+ if (ts < cutoff) this.recentBodies.delete(key);
76
77
  }
77
78
  }
78
79
  }
@@ -169,7 +169,8 @@ function pidAlive(pid: number, pidStart?: number): boolean {
169
169
  }
170
170
  if (pidStart === undefined) return true;
171
171
  const start = readStartTime(pid);
172
- return start === undefined ? true : start === pidStart;
172
+ // 读不到启动时间时保守认为「pid 还活着」,只有两边都读到且不同才判定被复用
173
+ return start === undefined || start === pidStart;
173
174
  }
174
175
 
175
176
  export function presenceOf(record: AgentRecord): Presence {