pi-subagents 0.23.1 → 0.24.0
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/CHANGELOG.md +13 -0
- package/README.md +13 -76
- package/package.json +1 -1
- package/prompts/parallel-cleanup.md +11 -1
- package/prompts/parallel-review.md +11 -1
- package/skills/pi-subagents/SKILL.md +11 -12
- package/src/agents/agent-serializer.ts +0 -42
- package/src/agents/agents.ts +1 -1
- package/src/extension/index.ts +2 -2
- package/src/runs/background/async-status.ts +16 -50
- package/src/runs/background/run-status.ts +8 -9
- package/src/runs/foreground/chain-clarify.ts +183 -218
- package/src/shared/status-format.ts +49 -0
- package/src/shared/types.ts +0 -5
- package/src/slash/slash-commands.ts +0 -74
- package/src/tui/render.ts +32 -58
- package/src/agents/agent-templates.ts +0 -60
- package/src/manager-ui/agent-manager-chain-detail.ts +0 -164
- package/src/manager-ui/agent-manager-detail.ts +0 -235
- package/src/manager-ui/agent-manager-edit.ts +0 -456
- package/src/manager-ui/agent-manager-list.ts +0 -283
- package/src/manager-ui/agent-manager-parallel.ts +0 -302
- package/src/manager-ui/agent-manager.ts +0 -732
- package/src/tui/subagents-status.ts +0 -621
- package/src/tui/text-editor.ts +0 -286
package/src/tui/text-editor.ts
DELETED
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
import { matchesKey, visibleWidth } from "@mariozechner/pi-tui";
|
|
2
|
-
|
|
3
|
-
export interface TextEditorState {
|
|
4
|
-
buffer: string;
|
|
5
|
-
cursor: number;
|
|
6
|
-
viewportOffset: number;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
interface TextEditorOptions {
|
|
10
|
-
multiLine?: boolean;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function createEditorState(initial = ""): TextEditorState {
|
|
14
|
-
return {
|
|
15
|
-
buffer: initial,
|
|
16
|
-
cursor: 0,
|
|
17
|
-
viewportOffset: 0,
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function wrapText(text: string, width: number): { lines: string[]; starts: number[] } {
|
|
22
|
-
const lines: string[] = [];
|
|
23
|
-
const starts: number[] = [];
|
|
24
|
-
|
|
25
|
-
if (width <= 0) {
|
|
26
|
-
return { lines: [text], starts: [0] };
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (text.length === 0) {
|
|
30
|
-
return { lines: [""], starts: [0] };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const segments = text.split("\n");
|
|
34
|
-
let offset = 0;
|
|
35
|
-
|
|
36
|
-
for (let i = 0; i < segments.length; i++) {
|
|
37
|
-
const segment = segments[i] ?? "";
|
|
38
|
-
if (segment.length === 0) {
|
|
39
|
-
starts.push(offset);
|
|
40
|
-
lines.push("");
|
|
41
|
-
} else {
|
|
42
|
-
let lineStart = 0;
|
|
43
|
-
let pos = 0;
|
|
44
|
-
let lineWidth = 0;
|
|
45
|
-
|
|
46
|
-
while (pos < segment.length) {
|
|
47
|
-
const char = String.fromCodePoint(segment.codePointAt(pos)!);
|
|
48
|
-
const charWidth = visibleWidth(char);
|
|
49
|
-
|
|
50
|
-
if (lineWidth > 0 && lineWidth + charWidth > width) {
|
|
51
|
-
starts.push(offset + lineStart);
|
|
52
|
-
lines.push(segment.slice(lineStart, pos));
|
|
53
|
-
lineStart = pos;
|
|
54
|
-
lineWidth = 0;
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
pos += char.length;
|
|
59
|
-
lineWidth += charWidth;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
starts.push(offset + lineStart);
|
|
63
|
-
lines.push(segment.slice(lineStart));
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
offset += segment.length;
|
|
67
|
-
if (i < segments.length - 1) {
|
|
68
|
-
offset += 1;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const endsWithNewline = text.endsWith("\n");
|
|
73
|
-
if (!endsWithNewline) {
|
|
74
|
-
const lastLine = lines[lines.length - 1] ?? "";
|
|
75
|
-
if (text.length > 0 && visibleWidth(lastLine) === width) {
|
|
76
|
-
starts.push(text.length);
|
|
77
|
-
lines.push("");
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return { lines, starts };
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export function getCursorDisplayPos(cursor: number, starts: number[]): { line: number; col: number } {
|
|
85
|
-
for (let i = starts.length - 1; i >= 0; i--) {
|
|
86
|
-
if (cursor >= starts[i]) {
|
|
87
|
-
return { line: i, col: cursor - starts[i] };
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return { line: 0, col: 0 };
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export function ensureCursorVisible(cursorLine: number, viewportHeight: number, currentOffset: number): number {
|
|
94
|
-
let offset = currentOffset;
|
|
95
|
-
|
|
96
|
-
if (cursorLine < offset) {
|
|
97
|
-
offset = cursorLine;
|
|
98
|
-
} else if (cursorLine >= offset + viewportHeight) {
|
|
99
|
-
offset = cursorLine - viewportHeight + 1;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return Math.max(0, offset);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function isWordChar(ch: string): boolean {
|
|
106
|
-
const code = ch.charCodeAt(0);
|
|
107
|
-
return (code >= 48 && code <= 57) || (code >= 65 && code <= 90) || (code >= 97 && code <= 122) || code === 95;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function wordBackward(buffer: string, cursor: number): number {
|
|
111
|
-
let pos = cursor;
|
|
112
|
-
while (pos > 0 && !isWordChar(buffer[pos - 1]!)) pos--;
|
|
113
|
-
while (pos > 0 && isWordChar(buffer[pos - 1]!)) pos--;
|
|
114
|
-
return pos;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function wordForward(buffer: string, cursor: number): number {
|
|
118
|
-
const len = buffer.length;
|
|
119
|
-
let pos = cursor;
|
|
120
|
-
while (pos < len && isWordChar(buffer[pos]!)) pos++;
|
|
121
|
-
while (pos < len && !isWordChar(buffer[pos]!)) pos++;
|
|
122
|
-
return pos;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function normalizeInsertText(data: string, multiLine: boolean): string | null {
|
|
126
|
-
let text = data;
|
|
127
|
-
|
|
128
|
-
text = text.split("\x1b[200~").join("");
|
|
129
|
-
text = text.split("\x1b[201~").join("");
|
|
130
|
-
text = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
131
|
-
|
|
132
|
-
if (!multiLine) {
|
|
133
|
-
const nl = text.indexOf("\n");
|
|
134
|
-
if (nl !== -1) text = text.slice(0, nl);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
text = text.replace(/\t/g, " ");
|
|
138
|
-
if (text.length === 0) return null;
|
|
139
|
-
|
|
140
|
-
for (let i = 0; i < text.length; i++) {
|
|
141
|
-
const code = text.charCodeAt(i);
|
|
142
|
-
if (code < 32) {
|
|
143
|
-
if (multiLine && text[i] === "\n") continue;
|
|
144
|
-
return null;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return text;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
export function handleEditorInput(
|
|
152
|
-
state: TextEditorState,
|
|
153
|
-
data: string,
|
|
154
|
-
textWidth: number,
|
|
155
|
-
options?: TextEditorOptions,
|
|
156
|
-
): TextEditorState | null {
|
|
157
|
-
const multiLine = options?.multiLine === true;
|
|
158
|
-
|
|
159
|
-
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
|
|
160
|
-
return null;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (matchesKey(data, "return")) {
|
|
164
|
-
if (!multiLine) return null;
|
|
165
|
-
const buffer = state.buffer.slice(0, state.cursor) + "\n" + state.buffer.slice(state.cursor);
|
|
166
|
-
return { ...state, buffer, cursor: state.cursor + 1 };
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const { lines: wrapped, starts } = wrapText(state.buffer, textWidth);
|
|
170
|
-
const cursorPos = getCursorDisplayPos(state.cursor, starts);
|
|
171
|
-
|
|
172
|
-
if (matchesKey(data, "alt+left") || matchesKey(data, "ctrl+left")) {
|
|
173
|
-
return { ...state, cursor: wordBackward(state.buffer, state.cursor) };
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (matchesKey(data, "alt+right") || matchesKey(data, "ctrl+right")) {
|
|
177
|
-
return { ...state, cursor: wordForward(state.buffer, state.cursor) };
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (matchesKey(data, "left")) {
|
|
181
|
-
if (state.cursor > 0) return { ...state, cursor: state.cursor - 1 };
|
|
182
|
-
return state;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (matchesKey(data, "right")) {
|
|
186
|
-
if (state.cursor < state.buffer.length) return { ...state, cursor: state.cursor + 1 };
|
|
187
|
-
return state;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (matchesKey(data, "up")) {
|
|
191
|
-
if (cursorPos.line > 0) {
|
|
192
|
-
const targetLine = cursorPos.line - 1;
|
|
193
|
-
const targetCol = Math.min(cursorPos.col, wrapped[targetLine]?.length ?? 0);
|
|
194
|
-
return { ...state, cursor: starts[targetLine] + targetCol };
|
|
195
|
-
}
|
|
196
|
-
return state;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (matchesKey(data, "down")) {
|
|
200
|
-
if (cursorPos.line < wrapped.length - 1) {
|
|
201
|
-
const targetLine = cursorPos.line + 1;
|
|
202
|
-
const targetCol = Math.min(cursorPos.col, wrapped[targetLine]?.length ?? 0);
|
|
203
|
-
return { ...state, cursor: starts[targetLine] + targetCol };
|
|
204
|
-
}
|
|
205
|
-
return state;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (matchesKey(data, "home")) {
|
|
209
|
-
return { ...state, cursor: starts[cursorPos.line] };
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (matchesKey(data, "end")) {
|
|
213
|
-
return { ...state, cursor: starts[cursorPos.line] + (wrapped[cursorPos.line]?.length ?? 0) };
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (matchesKey(data, "ctrl+home")) {
|
|
217
|
-
return { ...state, cursor: 0 };
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (matchesKey(data, "ctrl+end")) {
|
|
221
|
-
return { ...state, cursor: state.buffer.length };
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
if (matchesKey(data, "alt+backspace")) {
|
|
225
|
-
const target = wordBackward(state.buffer, state.cursor);
|
|
226
|
-
if (target === state.cursor) return state;
|
|
227
|
-
const buffer = state.buffer.slice(0, target) + state.buffer.slice(state.cursor);
|
|
228
|
-
return { ...state, buffer, cursor: target };
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (matchesKey(data, "backspace")) {
|
|
232
|
-
if (state.cursor > 0) {
|
|
233
|
-
const buffer = state.buffer.slice(0, state.cursor - 1) + state.buffer.slice(state.cursor);
|
|
234
|
-
return { ...state, buffer, cursor: state.cursor - 1 };
|
|
235
|
-
}
|
|
236
|
-
return state;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
if (matchesKey(data, "delete")) {
|
|
240
|
-
if (state.cursor < state.buffer.length) {
|
|
241
|
-
const buffer = state.buffer.slice(0, state.cursor) + state.buffer.slice(state.cursor + 1);
|
|
242
|
-
return { ...state, buffer };
|
|
243
|
-
}
|
|
244
|
-
return state;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const insert = normalizeInsertText(data, multiLine);
|
|
248
|
-
if (insert) {
|
|
249
|
-
const buffer = state.buffer.slice(0, state.cursor) + insert + state.buffer.slice(state.cursor);
|
|
250
|
-
return { ...state, buffer, cursor: state.cursor + insert.length };
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
return null;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
function renderWithCursor(text: string, cursorPos: number): string {
|
|
257
|
-
const before = text.slice(0, cursorPos);
|
|
258
|
-
const cursorChar = text[cursorPos] ?? " ";
|
|
259
|
-
const after = text.slice(cursorPos + 1);
|
|
260
|
-
return `${before}\x1b[7m${cursorChar}\x1b[27m${after}`;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
export function renderEditor(
|
|
264
|
-
state: TextEditorState,
|
|
265
|
-
width: number,
|
|
266
|
-
viewportHeight: number,
|
|
267
|
-
): string[] {
|
|
268
|
-
const { lines: wrapped, starts } = wrapText(state.buffer, width);
|
|
269
|
-
const cursorPos = getCursorDisplayPos(state.cursor, starts);
|
|
270
|
-
const lines: string[] = [];
|
|
271
|
-
|
|
272
|
-
for (let i = 0; i < viewportHeight; i++) {
|
|
273
|
-
const lineIdx = state.viewportOffset + i;
|
|
274
|
-
if (lineIdx < wrapped.length) {
|
|
275
|
-
let content = wrapped[lineIdx] ?? "";
|
|
276
|
-
if (lineIdx === cursorPos.line) {
|
|
277
|
-
content = renderWithCursor(content, cursorPos.col);
|
|
278
|
-
}
|
|
279
|
-
lines.push(content);
|
|
280
|
-
} else {
|
|
281
|
-
lines.push("");
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return lines;
|
|
286
|
-
}
|