@trim21/personal-pi-extensions 0.1.695 → 0.1.696

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.695",
3
+ "version": "0.1.696",
4
4
  "type": "module",
5
5
  "description": "Custom pi coding-agent extensions: bwrap sandbox, workspace guard, opencode edit, and more",
6
6
  "keywords": [
@@ -235,17 +235,27 @@ async function truncateOutput(
235
235
  };
236
236
  }
237
237
 
238
+ /** 代码块围栏比内容里最长的反引号串更长,免得内容里的 ``` 把面板截断。 */
239
+ function codeBlock(content: string, language: string): string {
240
+ const longest = (content.match(/`+/g) ?? []).reduce((max, run) => Math.max(max, run.length), 0);
241
+ const fence = "`".repeat(Math.max(3, longest + 1));
242
+ return `${fence}${language}\n${content.trimEnd()}\n${fence}`;
243
+ }
244
+
238
245
  /**
239
- * TUI 面板:脚本原文放在 js 代码块里,用比脚本里最长反引号串更长的围栏包住,
240
- * 免得脚本里的 ``` 把面板截断。
246
+ * TUI 面板:`## Input` 是脚本原文,`## Output` 是脚本经 `text()` / `console.log`
247
+ * 产生的文本(没有输出就没有这一段;返回值与图片只进工具结果正文)。
248
+ * 副标题在 `detail` 后带上输出字符数,不展开面板也能看出这次产出了多少文本。
241
249
  */
242
- function scriptPendant(code: string, subtitle: string): ToolPendant {
243
- const longest = (code.match(/`+/g) ?? []).reduce((max, run) => Math.max(max, run.length), 0);
244
- const fence = "`".repeat(Math.max(3, longest + 1));
250
+ function scriptPendant(code: string, detail: string, output: string): ToolPendant {
251
+ const sections = [`## Input\n${codeBlock(code, "js")}`];
252
+ if (output.trim() !== "") {
253
+ sections.push(`## Output\n${codeBlock(output, "")}`);
254
+ }
245
255
  return {
246
256
  title: CODEMODE_TOOL_NAME,
247
- subtitle,
248
- markdown: `${fence}js\n${code.trimEnd()}\n${fence}`,
257
+ subtitle: `${detail} · ${output.length} chars output`,
258
+ markdown: sections.join("\n\n"),
249
259
  };
250
260
  }
251
261
 
@@ -302,13 +312,15 @@ export function createCodemodeTools(pi: ExtensionAPI): CodemodeTools {
302
312
  content: [{ type: "text", text: message }],
303
313
  details: {
304
314
  error: message,
305
- pendant: scriptPendant(params.code, "invalid @options"),
315
+ pendant: scriptPendant(params.code, "invalid @options", ""),
306
316
  },
307
317
  };
308
318
  }
309
319
 
310
320
  // 脚本的 fs 原语读到的文件也进同一份记账,随结果持久化(与 details.store 同一处)
311
321
  const recordedReads: Record<string, FileSnapshot> = {};
322
+ // 脚本流式输出的文本累计:进度面板与结果面板的 Output 段是同一段文本
323
+ let streamedText = "";
312
324
 
313
325
  const outcome = await sandbox.run({
314
326
  code,
@@ -317,12 +329,14 @@ export function createCodemodeTools(pi: ExtensionAPI): CodemodeTools {
317
329
  signal,
318
330
  onOutput: (items) => {
319
331
  const text = textOf(items);
320
- if (text) {
321
- onUpdate?.({
322
- content: [{ type: "text", text }],
323
- details: { pendant: scriptPendant(code, text.slice(0, 80)) },
324
- });
332
+ if (text === "") {
333
+ return;
325
334
  }
335
+ streamedText = streamedText === "" ? text : `${streamedText}\n${text}`;
336
+ onUpdate?.({
337
+ content: [{ type: "text", text }],
338
+ details: { pendant: scriptPendant(code, text.slice(0, 80), streamedText) },
339
+ });
326
340
  },
327
341
  onCallProgress: ({ phase, name, args }) => {
328
342
  const detail =
@@ -332,7 +346,7 @@ export function createCodemodeTools(pi: ExtensionAPI): CodemodeTools {
332
346
  const line = `${phase === "start" ? "→" : "←"} ${name}${detail}`;
333
347
  onUpdate?.({
334
348
  content: [{ type: "text", text: line }],
335
- details: { pendant: scriptPendant(code, line.slice(0, 120)) },
349
+ details: { pendant: scriptPendant(code, line.slice(0, 120), streamedText) },
336
350
  });
337
351
  },
338
352
  onCall: async ({ name, args }) => {
@@ -371,13 +385,10 @@ export function createCodemodeTools(pi: ExtensionAPI): CodemodeTools {
371
385
  if (outcome.ok) {
372
386
  const writes = outcome.writes;
373
387
  const hasWrites = Object.keys(writes.set).length > 0 || writes.delete.length > 0;
388
+ const outputText = textOf(outcome.output);
374
389
  const value =
375
390
  outcome.value === undefined ? "" : `\n\n${JSON.stringify(outcome.value, null, 2)}`;
376
- const truncated = await truncateOutput(
377
- textOf(outcome.output) + value,
378
- images,
379
- maxOutputTokens,
380
- );
391
+ const truncated = await truncateOutput(outputText + value, images, maxOutputTokens);
381
392
  return {
382
393
  content: [
383
394
  { type: "text" as const, text: truncated.text },
@@ -392,7 +403,7 @@ export function createCodemodeTools(pi: ExtensionAPI): CodemodeTools {
392
403
  // store 的写入随工具结果持久化,下一次调用从这里重放恢复
393
404
  ...(hasWrites && { store: writes }),
394
405
  ...(hasReads(recordedReads) && { reads: recordedReads }),
395
- pendant: scriptPendant(code, `${outcome.calls.length} tool call(s)`),
406
+ pendant: scriptPendant(code, `${outcome.calls.length} tool call(s)`, outputText),
396
407
  ...(truncated.fullOutputPath && { fullOutputPath: truncated.fullOutputPath }),
397
408
  },
398
409
  };
@@ -413,7 +424,7 @@ export function createCodemodeTools(pi: ExtensionAPI): CodemodeTools {
413
424
  calls: outcome.calls,
414
425
  error: outcome.error.kind,
415
426
  ...(hasReads(recordedReads) && { reads: recordedReads }),
416
- pendant: scriptPendant(code, `failed (${outcome.error.kind})`),
427
+ pendant: scriptPendant(code, `failed (${outcome.error.kind})`, body),
417
428
  },
418
429
  };
419
430
  },