pi-agent-squad 0.8.1 → 0.8.4

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.
Files changed (3) hide show
  1. package/index.ts +33 -6
  2. package/message.ts +6 -18
  3. package/package.json +1 -1
package/index.ts CHANGED
@@ -101,6 +101,8 @@ function formatEventDuration(elapsedMs: number): string {
101
101
  }
102
102
 
103
103
  class SubagentTranscriptEventComponent implements Component {
104
+ private static readonly COLLAPSED_MAX_BODY_LINES = 5;
105
+
104
106
  private readonly markdown: Markdown | undefined;
105
107
 
106
108
  constructor(
@@ -110,6 +112,7 @@ class SubagentTranscriptEventComponent implements Component {
110
112
  private readonly theme: any,
111
113
  private readonly replyRequested = false,
112
114
  private readonly elapsedMs?: number,
115
+ private readonly expanded = false,
113
116
  ) {
114
117
  const content = body.trim();
115
118
  if (content) {
@@ -153,6 +156,25 @@ class SubagentTranscriptEventComponent implements Component {
153
156
  return truncateToWidth(header, Math.max(1, width), "…");
154
157
  }
155
158
 
159
+ /**
160
+ * When collapsed, keep at most COLLAPSED_MAX_BODY_LINES body lines: the first
161
+ * two, a dim "hidden" marker, and the last two. ctrl+o (tool output expand)
162
+ * toggles the full body via the renderer's expanded option.
163
+ */
164
+ private clampBodyLines(bodyLines: string[]): string[] {
165
+ const shown = Math.max(2, SubagentTranscriptEventComponent.COLLAPSED_MAX_BODY_LINES);
166
+ if (this.expanded || bodyLines.length <= shown) return bodyLines;
167
+ const kept = Math.max(1, Math.floor((shown - 1) / 2));
168
+ const head = bodyLines.slice(0, kept);
169
+ const tail = bodyLines.slice(-kept);
170
+ const hidden = bodyLines.length - head.length - tail.length;
171
+ const marker = this.theme?.fg?.(
172
+ "dim",
173
+ `⋯ ${hidden} hidden line${hidden === 1 ? "" : "s"} · ctrl+o expand`,
174
+ ) ?? `⋯ ${hidden} hidden lines · ctrl+o expand`;
175
+ return [...head, marker, ...tail];
176
+ }
177
+
156
178
  render(width: number): string[] {
157
179
  const padding = " ".repeat(Math.min(1, Math.max(0, width - 1)));
158
180
  const contentWidth = Math.max(1, width - padding.length);
@@ -161,10 +183,12 @@ class SubagentTranscriptEventComponent implements Component {
161
183
  const railWidth = contentWidth >= 2 ? 1 : 0;
162
184
  const railGap = contentWidth >= 3 ? 1 : 0;
163
185
  const bodyWidth = Math.max(1, contentWidth - railWidth - railGap);
164
- const bodyLines = normalizeCompactCodeBlockLines(
165
- this.markdown.render(bodyWidth),
166
- bodyWidth,
167
- 0,
186
+ const bodyLines = this.clampBodyLines(
187
+ normalizeCompactCodeBlockLines(
188
+ this.markdown.render(bodyWidth),
189
+ bodyWidth,
190
+ 0,
191
+ ),
168
192
  );
169
193
  for (let index = 0; index < bodyLines.length; index++) {
170
194
  const connector = index === bodyLines.length - 1 ? "└" : "│";
@@ -567,7 +591,7 @@ export default function (pi: ExtensionAPI) {
567
591
  // ===== main-agent mode =====
568
592
  pi.registerMessageRenderer<IncomingMessageDetails>(
569
593
  INCOMING_MESSAGE_TYPE,
570
- (message, _options, theme) => {
594
+ (message, options, theme) => {
571
595
  const details = message.details ?? {};
572
596
  const agent = String(details.from || "subagent");
573
597
  const body =
@@ -580,12 +604,14 @@ export default function (pi: ExtensionAPI) {
580
604
  body,
581
605
  theme,
582
606
  Boolean(details.expectsReply),
607
+ undefined,
608
+ options.expanded,
583
609
  );
584
610
  },
585
611
  );
586
612
  pi.registerMessageRenderer<BackgroundEventDetails>(
587
613
  BACKGROUND_EVENT_TYPE,
588
- (message, _options, theme) => {
614
+ (message, options, theme) => {
589
615
  const details = message.details ?? {};
590
616
  const kind: TranscriptEventKind = details.status === "error" ? "error" : "done";
591
617
  const agent = String(details.agent || "subagent");
@@ -604,6 +630,7 @@ export default function (pi: ExtensionAPI) {
604
630
  theme,
605
631
  false,
606
632
  elapsedMs,
633
+ options.expanded,
607
634
  );
608
635
  },
609
636
  );
package/message.ts CHANGED
@@ -117,24 +117,12 @@ function replyPath(dir: string, id: string): string {
117
117
  }
118
118
 
119
119
  export function ensureDir(dir: string): void {
120
- const absolute = path.resolve(dir);
121
- const parsed = path.parse(absolute);
122
- let current = parsed.root;
123
- for (const segment of absolute.slice(parsed.root.length).split(path.sep).filter(Boolean)) {
124
- current = path.join(current, segment);
125
- try {
126
- const st = fs.lstatSync(current);
127
- if (st.isSymbolicLink() || !st.isDirectory()) {
128
- throw new Error(`Message channel path is not a real directory: ${current}`);
129
- }
130
- } catch (error) {
131
- if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
132
- fs.mkdirSync(current, { mode: 0o700 });
133
- const created = fs.lstatSync(current);
134
- if (created.isSymbolicLink() || !created.isDirectory()) {
135
- throw new Error(`Message channel path is not a real directory: ${current}`);
136
- }
137
- }
120
+ // macOS commonly exposes /var as a system symlink (/private/var), so
121
+ // validating every ancestor would reject otherwise safe channel paths.
122
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
123
+ const st = fs.lstatSync(dir);
124
+ if (st.isSymbolicLink() || !st.isDirectory()) {
125
+ throw new Error(`Message channel path is not a real directory: ${dir}`);
138
126
  }
139
127
  }
140
128
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-agent-squad",
3
- "version": "0.8.1",
3
+ "version": "0.8.4",
4
4
  "description": "Interactive multi-agent orchestration, messaging, and live sessions for the Pi Coding Agent",
5
5
  "type": "module",
6
6
  "keywords": [