@danypops/pi-papyrus 0.60.0 → 0.60.1

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.
@@ -1,3 +1,4 @@
1
+ import { stripVTControlCharacters } from "node:util";
1
2
  import { TOOL_DETAILS_BODY_MAX_CHARACTERS } from "@danypops/papyrus";
2
3
  import { boundedText, PAPYRUS_TOOL_DETAILS_SCHEMA, type ResultCompleteness, type ToolDetailsBase } from "./shared.ts";
3
4
 
@@ -8,8 +9,15 @@ export interface SemanticTextToolDetails extends ToolDetailsBase {
8
9
  completeness: ResultCompleteness;
9
10
  }
10
11
 
12
+ /** Preserves diagnostic text while removing terminal commands and normalizing line breaks and tabs. */
13
+ export function plainTerminalText(text: string): string {
14
+ return stripVTControlCharacters(text)
15
+ .replace(/\r\n?/g, "\n")
16
+ .replace(/\p{Cc}/gu, (character) => (character === "\n" ? "\n" : character === "\t" ? " " : ""));
17
+ }
18
+
11
19
  export function createSemanticTextDetails(operation: string, text: string): SemanticTextToolDetails {
12
- const bounded = boundedText(text, TOOL_DETAILS_BODY_MAX_CHARACTERS);
20
+ const bounded = boundedText(plainTerminalText(text), TOOL_DETAILS_BODY_MAX_CHARACTERS);
13
21
  return {
14
22
  schemaVersion: PAPYRUS_TOOL_DETAILS_SCHEMA,
15
23
  kind: "semantic-text",
@@ -0,0 +1,38 @@
1
+ import { TOOL_COLLAPSED_ROW_LIMIT } from "@danypops/papyrus";
2
+ import { expandHint } from "@danypops/vehicle-client-pi/expand-hint";
3
+ import type { Theme } from "@earendil-works/pi-coding-agent";
4
+ import { type Component, Text, truncateToWidth } from "@earendil-works/pi-tui";
5
+ import { createSemanticTextDetails, type SemanticTextToolDetails } from "./render-model/semantic-text.ts";
6
+
7
+ const MAX_EXPANDED_TEXT_ROWS = 200;
8
+
9
+ /** Renders diagnostic text with host-owned styles and bounded collapsed or expanded rows. */
10
+ export class SemanticTextCard implements Component {
11
+ private readonly details: SemanticTextToolDetails;
12
+
13
+ constructor(
14
+ details: SemanticTextToolDetails,
15
+ private readonly theme: Theme,
16
+ private readonly expanded: boolean,
17
+ ) {
18
+ const normalized = createSemanticTextDetails(details.operation, details.text);
19
+ this.details = { ...normalized, completeness: details.completeness.truncated ? details.completeness : normalized.completeness };
20
+ }
21
+
22
+ render(width: number): string[] {
23
+ if (width < 1) return [];
24
+ const lines = new Text(this.details.text, 0, 0).render(width);
25
+ const limit = this.expanded ? MAX_EXPANDED_TEXT_ROWS : TOOL_COLLAPSED_ROW_LIMIT;
26
+ const visible = lines.slice(0, limit).map((line) => truncateToWidth(this.theme.fg("toolOutput", line), width));
27
+ const omitted = lines.length - visible.length;
28
+ if (omitted > 0) {
29
+ const hint = this.expanded ? `${omitted} more lines omitted · display limit` : `${omitted} more lines · ${expandHint()}`;
30
+ visible.push(truncateToWidth(this.theme.fg("dim", hint), width));
31
+ } else if (this.details.completeness.truncated) {
32
+ visible.push(truncateToWidth(this.theme.fg("dim", "Output truncated"), width));
33
+ }
34
+ return visible;
35
+ }
36
+
37
+ invalidate(): void {}
38
+ }
@@ -40,6 +40,7 @@ import {
40
40
  type PapyrusToolDetails,
41
41
  parsePapyrusToolDetails,
42
42
  } from "../../tool-rendering/render-model.ts";
43
+ import { SemanticTextCard } from "../../tool-rendering/semantic-text.ts";
43
44
  import { recordRenderDiagnostic, shapeFingerprint } from "../render-diagnostics.ts";
44
45
  import { batchOutcomeSummary } from "./batch.ts";
45
46
  import {
@@ -134,6 +135,9 @@ export function papyrusVehicleRenderers(descriptor: VehicleOperationDescriptor):
134
135
  if (isTaskCompletion(output)) {
135
136
  return renderTaskCompletion(output, theme, options.expanded);
136
137
  }
138
+ if (isSemanticTextOutput(output)) {
139
+ return new SemanticTextCard(createSemanticTextDetails(descriptor.name, semanticText(output)), theme, options.expanded);
140
+ }
137
141
  recordRenderDiagnostic({ event: "render-result-fell-through-to-generic", operation: descriptor.name });
138
142
  }
139
143
  return renderVehicleResult(descriptor, result, options, theme, context);
@@ -217,7 +221,7 @@ function renderFromPapyrusPresentation(
217
221
  case "preview":
218
222
  return new Text(theme.fg("toolOutput", presentation.content), 0, 0);
219
223
  case "semantic-text":
220
- return new Text(theme.fg("toolOutput", presentation.text), 0, 0);
224
+ return new SemanticTextCard(presentation, theme, expanded);
221
225
  case "transition":
222
226
  case "graph":
223
227
  case "gate-run":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danypops/pi-papyrus",
3
- "version": "0.60.0",
3
+ "version": "0.60.1",
4
4
  "description": "Pi host extension for Papyrus: native tools, TUI panels, and context injection over the daemon-backed graph store",
5
5
  "license": "MIT",
6
6
  "type": "module",