pi-cockpit 0.24.2 → 0.24.3

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A responsive **agent cockpit** for [Pi](https://pi.dev): wide terminals get a docked Maestro operations sidebar, narrow terminals automatically fall back to the existing Todo and Agent widgets, and every layout keeps the Starship-style footer.
4
4
 
5
- It is the third plugin of the `pi-maestro-flow` project (alongside `pi-maestro-flow` and `pi-maestro-teammate`). It is installed and registered with `pi-maestro-flow`, but it also runs standalone. Current version: **0.24.2**.
5
+ It is the third plugin of the `pi-maestro-flow` project (alongside `pi-maestro-flow` and `pi-maestro-teammate`). It is installed and registered with `pi-maestro-flow`, but it also runs standalone. Current version: **0.24.3**, with optional Teammate peer `^2.8.1` and Node.js minimum 22.19.0; Settings-Core 0.2.3 is unchanged. See [RELEASE.md](../../RELEASE.md) for suite dependency changes and verification scope.
6
6
 
7
7
  The dock is a non-capturing top-right overlay. Cockpit reserves its columns by wrapping the active TUI renderer at runtime, so the Pi workspace reflows instead of rendering underneath it. No Pi source files are modified.
8
8
 
@@ -25,7 +25,7 @@ The dock is a non-capturing top-right overlay. Cockpit reserves its columns by w
25
25
  `pi-cockpit` comes automatically with the orchestration layer — installing `pi-maestro-flow` pulls `pi-cockpit` and registers it into `settings.packages` on postinstall, no manual setup required. To use it on its own:
26
26
 
27
27
  ```bash
28
- pi install npm:pi-cockpit@0.24.2 # standalone from npm
28
+ pi install npm:pi-cockpit@0.24.3 # standalone from npm
29
29
  # or, for one run:
30
30
  pi -e ./packages/pi-cockpit
31
31
  ```
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "pi-cockpit",
3
- "version": "0.24.2",
3
+ "version": "0.24.3",
4
4
  "description": "Responsive Maestro operations sidebar, fallback status widgets, and footer for Pi.",
5
5
  "type": "module",
6
+ "engines": {
7
+ "node": ">=22.19.0"
8
+ },
6
9
  "keywords": [
7
10
  "pi-package",
8
11
  "pi",
@@ -43,7 +46,7 @@
43
46
  "@earendil-works/pi-ai": "*",
44
47
  "@earendil-works/pi-coding-agent": "*",
45
48
  "@earendil-works/pi-tui": "*",
46
- "pi-maestro-teammate": "^2.5.0",
49
+ "pi-maestro-teammate": "^2.8.1",
47
50
  "typebox": "*"
48
51
  },
49
52
  "peerDependenciesMeta": {
@@ -72,7 +75,7 @@
72
75
  "@earendil-works/pi-coding-agent": "0.99.0",
73
76
  "@earendil-works/pi-tui": "0.99.0",
74
77
  "@types/node": "^22.0.0",
75
- "pi-maestro-teammate": "2.8.0",
78
+ "pi-maestro-teammate": "2.8.1",
76
79
  "typebox": "1.3.27",
77
80
  "typescript": "^5.5.0"
78
81
  },
@@ -483,6 +483,8 @@ export function toolResultCard(
483
483
  o: {
484
484
  name: string;
485
485
  ok?: boolean;
486
+ /** Pre-colored glyph overriding the ✓/✗ status mark (e.g. "←" for incoming messages). */
487
+ mark?: string;
486
488
  arg?: string;
487
489
  summary?: string;
488
490
  rows?: string[];
@@ -493,9 +495,9 @@ export function toolResultCard(
493
495
  const bold = theme.bold ?? ((text: string) => text);
494
496
  return {
495
497
  render(width: number): string[] {
496
- const mark = o.ok === false
498
+ const mark = o.mark ?? (o.ok === false
497
499
  ? theme.fg("error", quietStatusMark("failure"))
498
- : theme.fg("success", quietStatusMark("success"));
500
+ : theme.fg("success", quietStatusMark("success")));
499
501
  const safeWidth = Math.max(1, width);
500
502
  if (safeWidth <= 1) return [];
501
503
  const label = `${mark} ${theme.fg("toolTitle", bold(o.name))}${o.arg ? ` ${theme.fg("accent", o.arg)}` : ""}${o.summary ? ` ${theme.fg("dim", `· ${o.summary}`)}` : ""}`;
@@ -1,7 +1,8 @@
1
1
  import { stripVTControlCharacters } from "node:util";
2
2
  import type { ExtensionAPI, Theme } from "@earendil-works/pi-coding-agent";
3
- import { Box, Text, truncateToWidth, visibleWidth, wrapTextWithAnsi, type Component } from "@earendil-works/pi-tui";
3
+ import type { Component } from "@earendil-works/pi-tui";
4
4
  import { tuiT } from "./tui-i18n.ts";
5
+ import { toolResultCard } from "./quiet-tools.ts";
5
6
 
6
7
  export const TEAMMATE_MESSAGE_CUSTOM_TYPE = "teammate-message";
7
8
 
@@ -93,27 +94,6 @@ function kindLabel(kind: string | undefined): string | undefined {
93
94
  return key ? tuiT(key) : kind;
94
95
  }
95
96
 
96
- const COLLAPSED_BODY_LINES = 2;
97
-
98
- function collapsedBody(value: string, width: number): string {
99
- const text = value.replace(/\s+/g, " ").trim();
100
- if (!text) return "";
101
- const lineWidth = Math.max(1, width);
102
- const lines = wrapTextWithAnsi(text, lineWidth);
103
- if (lines.length <= COLLAPSED_BODY_LINES) return lines.join("\n");
104
-
105
- const preview = lines.slice(0, COLLAPSED_BODY_LINES);
106
- const last = preview.length - 1;
107
- const ellipsisWidth = Math.max(1, visibleWidth("…"));
108
- const prefix = truncateToWidth(
109
- preview[last] ?? "",
110
- Math.max(0, lineWidth - ellipsisWidth),
111
- "",
112
- );
113
- preview[last] = `${prefix}…`;
114
- return preview.join("\n");
115
- }
116
-
117
97
  export function renderIncomingTeammateMessage(
118
98
  message: { content: MessageContent; details?: TeammateMessageDetails },
119
99
  options: { expanded: boolean; outputPad: number },
@@ -121,26 +101,19 @@ export function renderIncomingTeammateMessage(
121
101
  ): Component {
122
102
  const envelope = parseTeammateMessageEnvelope(message.content, message.details);
123
103
  const kind = kindLabel(envelope.kind);
124
- const header = [
125
- `${theme.fg("accent", "←")} ${theme.bold(tuiT("message.receivedFrom", { sender: envelope.sender }))}`,
126
- kind ? theme.fg("muted", `· ${kind}`) : "",
127
- ].filter(Boolean).join(" ");
128
- const box = new Box(options.outputPad, 1, (text) => theme.bg("customMessageBg", text));
129
- box.addChild({
130
- render(width: number): string[] {
131
- const sections = [header];
132
- if (options.expanded && envelope.guidance) sections.push(theme.fg("dim", envelope.guidance));
133
- if (envelope.body) {
134
- const body = options.expanded ? envelope.body : collapsedBody(envelope.body, width);
135
- sections.push(theme.fg("text", body));
136
- } else if (!options.expanded && envelope.guidance) {
137
- sections.push(theme.fg("text", collapsedBody(envelope.guidance, width)));
138
- }
139
- return new Text(sections.join("\n"), 0, 0).render(width);
140
- },
141
- invalidate(): void {},
104
+ const groups: string[][] = [];
105
+ if (options.expanded && envelope.guidance) {
106
+ groups.push(envelope.guidance.split("\n").map((line) => theme.fg("dim", line)));
107
+ }
108
+ const bodySource = envelope.body || (!options.expanded ? envelope.guidance : "");
109
+ if (bodySource) groups.push(bodySource.split("\n"));
110
+ return toolResultCard(theme, {
111
+ name: tuiT("message.receivedFrom", { sender: envelope.sender }),
112
+ mark: theme.fg("accent", "←"),
113
+ ...(kind ? { summary: kind } : {}),
114
+ groups,
115
+ maxBodyRows: options.expanded ? undefined : 8,
142
116
  });
143
- return box;
144
117
  }
145
118
 
146
119
  export function registerTeammateMessageRenderer(