@trim21/personal-pi-extensions 0.1.688 → 0.1.690

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.688",
3
+ "version": "0.1.690",
4
4
  "type": "module",
5
5
  "description": "Custom pi coding-agent extensions: bwrap sandbox, workspace guard, opencode edit, and more",
6
6
  "keywords": [
@@ -53,7 +53,7 @@
53
53
  "eslint-plugin-import-x": "^4.17.1",
54
54
  "eslint-plugin-promise": "7.3.0",
55
55
  "eslint-plugin-simple-import-sort": "14.0.0",
56
- "eslint-plugin-unicorn": "76.0.0",
56
+ "eslint-plugin-unicorn": "77.0.0",
57
57
  "eslint-plugin-unused-imports": "4.4.1",
58
58
  "husky": "^9.1.7",
59
59
  "jiti": "^2.7.0",
@@ -85,7 +85,8 @@ function killHolder(pid: number | undefined): void {
85
85
  }
86
86
 
87
87
  /** 轮询等待进程退出(进程消失即返回)。 */
88
- async function waitForExit(pid: number, timeoutMs = 2000): Promise<void> {
88
+ async function waitForExit(pid: number): Promise<void> {
89
+ const timeoutMs = 2000;
89
90
  const deadline = Date.now() + timeoutMs;
90
91
  while (Date.now() < deadline) {
91
92
  try {
@@ -36,7 +36,8 @@ const VCS_DIRECTORIES_TO_EXCLUDE = [".git", ".svn", ".hg", ".bzr", ".jj", ".sl"]
36
36
  */
37
37
  const DEFAULT_HEAD_LIMIT = 250;
38
38
 
39
- function truncateOutput(output: string, maxCharacters = 30_000): string {
39
+ function truncateOutput(output: string): string {
40
+ const maxCharacters = 30_000;
40
41
  if (output.length <= maxCharacters) {
41
42
  return output;
42
43
  }
@@ -108,6 +108,7 @@ function formatBashSuccess(result: Awaited<ReturnType<BwrapRuntime["execute"]>>)
108
108
  content: { type: "text"; text: string }[];
109
109
  details: BashToolDetails | undefined;
110
110
  } {
111
+ // eslint-disable-next-line unicorn/prefer-default-parameters -- output 是 string,空输出需显示 "(no output)",默认参数只覆盖 undefined
111
112
  const { output, truncation, fullOutputPath } = result;
112
113
  const text = appendTruncationNotice(output || "(no output)", truncation, fullOutputPath);
113
114
  return {
@@ -8903,15 +8903,20 @@ function parseWrites(json) {
8903
8903
  return emptyWrites();
8904
8904
  }
8905
8905
  }
8906
- function scriptError(payload, kind) {
8906
+ function scriptError(payload) {
8907
8907
  if (payload === void 0) {
8908
- return { kind, message: "the script failed without an error message" };
8908
+ return { kind: "script", message: "the script failed without an error message" };
8909
8909
  }
8910
8910
  try {
8911
8911
  const parsed = JSON.parse(payload);
8912
- return { kind, name: parsed.name, message: parsed.message ?? payload, stack: parsed.stack };
8912
+ return {
8913
+ kind: "script",
8914
+ name: parsed.name,
8915
+ message: parsed.message ?? payload,
8916
+ stack: parsed.stack
8917
+ };
8913
8918
  } catch {
8914
- return { kind, message: payload };
8919
+ return { kind: "script", message: payload };
8915
8920
  }
8916
8921
  }
8917
8922
  async function runScript(wasm, start) {
@@ -8966,7 +8971,7 @@ async function runScript(wasm, start) {
8966
8971
  post({
8967
8972
  t: "done",
8968
8973
  ok: false,
8969
- error: scriptError(b === void 0 ? void 0 : b.toString(), "script"),
8974
+ error: scriptError(b === void 0 ? void 0 : b.toString()),
8970
8975
  writes
8971
8976
  });
8972
8977
  }
@@ -9034,8 +9039,7 @@ async function runScript(wasm, start) {
9034
9039
  t: "done",
9035
9040
  ok: false,
9036
9041
  error: scriptError(
9037
- JSON.stringify({ name: error.name, message: error.message, stack: error.stack }),
9038
- "script"
9042
+ JSON.stringify({ name: error.name, message: error.message, stack: error.stack })
9039
9043
  ),
9040
9044
  writes: emptyWrites()
9041
9045
  });
@@ -69,15 +69,20 @@ function parseWrites(json: string): StoreWrites {
69
69
  }
70
70
 
71
71
  /** 把 prelude 的 error 描述 JSON 转成可上报的错误。 */
72
- function scriptError(payload: string | undefined, kind: ScriptError["kind"]): ScriptError {
72
+ function scriptError(payload: string | undefined): ScriptError {
73
73
  if (payload === undefined) {
74
- return { kind, message: "the script failed without an error message" };
74
+ return { kind: "script", message: "the script failed without an error message" };
75
75
  }
76
76
  try {
77
77
  const parsed = JSON.parse(payload) as { name?: string; message?: string; stack?: string };
78
- return { kind, name: parsed.name, message: parsed.message ?? payload, stack: parsed.stack };
78
+ return {
79
+ kind: "script",
80
+ name: parsed.name,
81
+ message: parsed.message ?? payload,
82
+ stack: parsed.stack,
83
+ };
79
84
  } catch {
80
- return { kind, message: payload };
85
+ return { kind: "script", message: payload };
81
86
  }
82
87
  }
83
88
 
@@ -150,7 +155,7 @@ async function runScript(wasm: object, start: Extract<HostMessage, { t: "start"
150
155
  post({
151
156
  t: "done",
152
157
  ok: false,
153
- error: scriptError(b === undefined ? undefined : b.toString(), "script"),
158
+ error: scriptError(b === undefined ? undefined : b.toString()),
154
159
  writes,
155
160
  });
156
161
  }
@@ -229,7 +234,6 @@ async function runScript(wasm: object, start: Extract<HostMessage, { t: "start"
229
234
  ok: false,
230
235
  error: scriptError(
231
236
  JSON.stringify({ name: error.name, message: error.message, stack: error.stack }),
232
- "script",
233
237
  ),
234
238
  writes: emptyWrites(),
235
239
  });
package/src/gh/schemas.ts CHANGED
@@ -16,14 +16,16 @@ import { Type } from "typebox";
16
16
  /** GitHub 的 actor(author / assignee / mergedBy / review 作者)。 */
17
17
  export const ghActorSchema = Type.Object({
18
18
  login: Type.Optional(Type.String()),
19
- name: Type.Optional(Type.String()),
19
+ // `user.name` 是 `string | null`:没有显示名的账号返回 null
20
+ name: Type.Optional(Type.Union([Type.String(), Type.Null()])),
20
21
  is_bot: Type.Optional(Type.Boolean()),
21
22
  });
22
23
 
23
24
  export const ghLabelSchema = Type.Object({
24
25
  name: Type.String(),
25
26
  color: Type.Optional(Type.String()),
26
- description: Type.Optional(Type.String()),
27
+ // 没有描述的 label 是 `"description": null`(不是缺字段),所以既要 optional 也要 nullable
28
+ description: Type.Optional(Type.Union([Type.String(), Type.Null()])),
27
29
  });
28
30
 
29
31
  export const ghMilestoneSchema = Type.Object({
@@ -1466,7 +1466,7 @@ export async function create(input: CreateInput): Promise<LspClient> {
1466
1466
  const toPaths = (locations: { uri: string }[] | null): Set<string> =>
1467
1467
  new Set(
1468
1468
  (locations ?? []).flatMap((location) =>
1469
- location.uri.startsWith("file:") ? [normalize(fileURLToPath(location.uri))] : [],
1469
+ location.uri.startsWith("file:") ? normalize(fileURLToPath(location.uri)) : [],
1470
1470
  ),
1471
1471
  );
1472
1472
 
@@ -112,6 +112,7 @@ function resolveTemplate(template: string, root: string, cwd: string): string {
112
112
  const envVarPattern = /\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}/g;
113
113
 
114
114
  function interpolateEnvVars(value: string, env: NodeJS.ProcessEnv): string {
115
+ // eslint-disable-next-line unicorn/prefer-default-parameters -- replaceAll 回调参数被上下文的 ...args: any[] 定成 any,默认参数无法保留 string 注解
115
116
  return value.replaceAll(envVarPattern, (_match, name: string, fallback?: string) => {
116
117
  const resolved = env[name];
117
118
  return resolved !== undefined && resolved !== "" ? resolved : (fallback ?? "");
@@ -446,6 +446,7 @@ function registerReadTool(bus: ToolBus, getService: () => LspService, state: Rea
446
446
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
447
447
  signal?.throwIfAborted();
448
448
 
449
+ // eslint-disable-next-line unicorn/prefer-default-parameters -- params.offset || 1:0 视为 1,默认参数只覆盖 undefined
449
450
  const { filePath: rawPath, offset, limit } = params;
450
451
 
451
452
  const absolutePath = isAbsolute(rawPath) ? rawPath : resolvePath(ctx.cwd, rawPath);
@@ -30,7 +30,7 @@ export class OutboundPolicy {
30
30
  * definition worked through what it was handed). `target` scopes the
31
31
  * identical-body dedupe to a single peer (loop-breaking).
32
32
  */
33
- check(body: string, unreadBacklog: number, target?: string): OutboundVerdict {
33
+ check(body: string, unreadBacklog: number, target = ""): OutboundVerdict {
34
34
  if (body.length > MAX_BODY_CHARS) {
35
35
  return {
36
36
  ok: false,
@@ -44,7 +44,7 @@ export class OutboundPolicy {
44
44
  };
45
45
  }
46
46
  const now = this.now();
47
- const dedupeKey = `${body}\u0000${target ?? ""}`;
47
+ const dedupeKey = `${body}\u0000${target}`;
48
48
  const lastSame = this.recentBodies.get(dedupeKey);
49
49
  if (lastSame !== undefined && now - lastSame < DEDUPE_WINDOW_MS) {
50
50
  return {
@@ -66,9 +66,9 @@ export class OutboundPolicy {
66
66
  }
67
67
 
68
68
  /** Record a successful send so dedupe/rate state stays current. */
69
- recordSend(body: string, target?: string): void {
69
+ recordSend(body: string, target = ""): void {
70
70
  this.sentAt.push(this.now());
71
- this.recentBodies.set(`${body}\u0000${target ?? ""}`, this.now());
71
+ this.recentBodies.set(`${body}\u0000${target}`, this.now());
72
72
  // bound the dedupe map
73
73
  if (this.recentBodies.size <= 200) {
74
74
  return;