@leo-alvarenga/pi-mini-subagents 0.1.0 → 0.2.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/package.json +1 -1
- package/src/spawn.ts +30 -9
- package/src/state.ts +11 -2
- package/src/tool.ts +2 -2
- package/src/utils.ts +5 -2
- package/src/widget.ts +13 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leo-alvarenga/pi-mini-subagents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Transient subagents for Pi: mini_subagent tool (single + parallel), /subagents command, and a live TUI panel",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "github.com/leo-alvarenga/pi-mono",
|
package/src/spawn.ts
CHANGED
|
@@ -53,10 +53,17 @@ function getPiInvocation(args: string[]): { command: string; args: string[] } {
|
|
|
53
53
|
return { command: "pi", args };
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
async function writePromptTempFile(
|
|
57
|
-
|
|
56
|
+
async function writePromptTempFile(
|
|
57
|
+
prompt: string,
|
|
58
|
+
): Promise<{ dir: string; filePath: string }> {
|
|
59
|
+
const dir = await fs.promises.mkdtemp(
|
|
60
|
+
path.join(os.tmpdir(), "pi-mini-subagent-"),
|
|
61
|
+
);
|
|
58
62
|
const filePath = path.join(dir, "prompt.md");
|
|
59
|
-
await fs.promises.writeFile(filePath, prompt, {
|
|
63
|
+
await fs.promises.writeFile(filePath, prompt, {
|
|
64
|
+
encoding: "utf-8",
|
|
65
|
+
mode: 0o600,
|
|
66
|
+
});
|
|
60
67
|
return { dir, filePath };
|
|
61
68
|
}
|
|
62
69
|
|
|
@@ -65,7 +72,8 @@ function extractText(msg: any): string {
|
|
|
65
72
|
const content = Array.isArray(msg?.content) ? msg.content : [];
|
|
66
73
|
for (let i = content.length - 1; i >= 0; i--) {
|
|
67
74
|
const part = content[i];
|
|
68
|
-
if (part?.type === "text" && typeof part.text === "string")
|
|
75
|
+
if (part?.type === "text" && typeof part.text === "string")
|
|
76
|
+
return part.text;
|
|
69
77
|
}
|
|
70
78
|
return "";
|
|
71
79
|
}
|
|
@@ -82,7 +90,9 @@ export interface RunSubagentOptions {
|
|
|
82
90
|
* Spawn a transient headless pi subprocess, stream its JSON events, and
|
|
83
91
|
* resolve with the final output once it exits.
|
|
84
92
|
*/
|
|
85
|
-
export async function runSubagent(
|
|
93
|
+
export async function runSubagent(
|
|
94
|
+
opts: RunSubagentOptions,
|
|
95
|
+
): Promise<SubagentRunResult> {
|
|
86
96
|
const prompt = buildSystemPrompt(opts.allowWrite);
|
|
87
97
|
const { dir: tmpDir, filePath: tmpPath } = await writePromptTempFile(prompt);
|
|
88
98
|
|
|
@@ -91,17 +101,28 @@ export async function runSubagent(opts: RunSubagentOptions): Promise<SubagentRun
|
|
|
91
101
|
: `Task: ${opts.task}`;
|
|
92
102
|
|
|
93
103
|
const args = [
|
|
94
|
-
"--mode",
|
|
104
|
+
"--mode",
|
|
105
|
+
"json",
|
|
95
106
|
"-p",
|
|
96
107
|
"--no-session",
|
|
97
|
-
"--tools",
|
|
98
|
-
"
|
|
108
|
+
"--tools",
|
|
109
|
+
buildAllowlist(opts.allowWrite).join(","),
|
|
110
|
+
"--append-system-prompt",
|
|
111
|
+
tmpPath,
|
|
99
112
|
taskText,
|
|
100
113
|
];
|
|
101
114
|
|
|
102
115
|
const result: SubagentRunResult = {
|
|
103
116
|
output: "",
|
|
104
|
-
usage: {
|
|
117
|
+
usage: {
|
|
118
|
+
input: 0,
|
|
119
|
+
output: 0,
|
|
120
|
+
cacheRead: 0,
|
|
121
|
+
cacheWrite: 0,
|
|
122
|
+
cost: 0,
|
|
123
|
+
contextTokens: 0,
|
|
124
|
+
turns: 0,
|
|
125
|
+
},
|
|
105
126
|
exitCode: 0,
|
|
106
127
|
stderr: "",
|
|
107
128
|
aborted: false,
|
package/src/state.ts
CHANGED
|
@@ -38,7 +38,12 @@ export class SubagentStore {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/** Create a `running` record and commit it. */
|
|
41
|
-
start(
|
|
41
|
+
start(
|
|
42
|
+
ctx: ExtensionContext,
|
|
43
|
+
task: string,
|
|
44
|
+
allowWrite: boolean,
|
|
45
|
+
cwd?: string,
|
|
46
|
+
): SubagentRecord {
|
|
42
47
|
const s = this.getState(ctx);
|
|
43
48
|
const record: SubagentRecord = {
|
|
44
49
|
id: s.nextId,
|
|
@@ -53,7 +58,11 @@ export class SubagentStore {
|
|
|
53
58
|
}
|
|
54
59
|
|
|
55
60
|
/** Patch an existing record in place and commit. */
|
|
56
|
-
finish(
|
|
61
|
+
finish(
|
|
62
|
+
ctx: ExtensionContext,
|
|
63
|
+
id: number,
|
|
64
|
+
patch: Partial<SubagentRecord>,
|
|
65
|
+
): void {
|
|
57
66
|
const s = this.getState(ctx);
|
|
58
67
|
this.commit(ctx, {
|
|
59
68
|
records: s.records.map((r) => (r.id === id ? { ...r, ...patch } : r)),
|
package/src/tool.ts
CHANGED
|
@@ -135,7 +135,7 @@ export function registerMiniSubagentTool(
|
|
|
135
135
|
);
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
//
|
|
138
|
+
// single mode
|
|
139
139
|
if (hasSingle) {
|
|
140
140
|
const record = store.start(
|
|
141
141
|
ctx,
|
|
@@ -184,7 +184,7 @@ export function registerMiniSubagentTool(
|
|
|
184
184
|
};
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
//
|
|
187
|
+
// parallel mode
|
|
188
188
|
const tasks = params.tasks!;
|
|
189
189
|
if (tasks.length > MAX_PARALLEL_TASKS) {
|
|
190
190
|
return errorResult(
|
package/src/utils.ts
CHANGED
|
@@ -12,8 +12,10 @@ function formatTokens(n: number): string {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function getStyledSubagent(r: SubagentRecord, th: Theme): string {
|
|
15
|
+
const task = r.task.replace(/\s*[\r\n]+\s*/g, " ");
|
|
15
16
|
const style = STATUS_STYLES[r.status] ?? STATUS_STYLES.completed;
|
|
16
|
-
|
|
17
|
+
|
|
18
|
+
let line = `${th.fg(style.fg, style.icon)} ${th.fg("accent", `#${r.id}`)} ${th.fg(style.fg, task)}`;
|
|
17
19
|
|
|
18
20
|
if (r.status === "completed" && r.tokens) {
|
|
19
21
|
line += th.fg("dim", ` · ${formatTokens(r.tokens)}`);
|
|
@@ -38,7 +40,7 @@ export function getStyledSubagentHeader(
|
|
|
38
40
|
|
|
39
41
|
return th.fg(
|
|
40
42
|
"accent",
|
|
41
|
-
`${PANEL_STATE_ICON[collapseState]}
|
|
43
|
+
`${PANEL_STATE_ICON[collapseState]} Subagents | ${running} running / ${done} done`,
|
|
42
44
|
);
|
|
43
45
|
}
|
|
44
46
|
|
|
@@ -73,6 +75,7 @@ export function getStyledSubagentList(
|
|
|
73
75
|
} else {
|
|
74
76
|
const running = records.filter((r) => r.status === "running");
|
|
75
77
|
const finished = records.filter((r) => r.status !== "running");
|
|
78
|
+
|
|
76
79
|
const visible = [...running, ...finished].slice(0, MAX_PANEL_ROWS);
|
|
77
80
|
|
|
78
81
|
for (const r of visible) {
|
package/src/widget.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
ExtensionAPI,
|
|
3
|
+
ExtensionContext,
|
|
4
|
+
Theme,
|
|
5
|
+
} from "@earendil-works/pi-coding-agent";
|
|
2
6
|
|
|
3
7
|
import { PANEL_TOGGLE_CHORD, WIDGET_KEY } from "./constants";
|
|
4
8
|
import type { SubagentStore } from "./state";
|
|
@@ -42,7 +46,10 @@ class SubagentWidget {
|
|
|
42
46
|
}
|
|
43
47
|
}
|
|
44
48
|
|
|
45
|
-
export function refreshWidget(
|
|
49
|
+
export function refreshWidget(
|
|
50
|
+
ctx: ExtensionContext,
|
|
51
|
+
store: SubagentStore,
|
|
52
|
+
): void {
|
|
46
53
|
if (!ctx.hasUI) return;
|
|
47
54
|
|
|
48
55
|
ctx.ui.setWidget(
|
|
@@ -56,7 +63,10 @@ export function refreshWidget(ctx: ExtensionContext, store: SubagentStore): void
|
|
|
56
63
|
);
|
|
57
64
|
}
|
|
58
65
|
|
|
59
|
-
export function registerSubagentWidget(
|
|
66
|
+
export function registerSubagentWidget(
|
|
67
|
+
pi: ExtensionAPI,
|
|
68
|
+
store: SubagentStore,
|
|
69
|
+
): void {
|
|
60
70
|
pi.registerShortcut(PANEL_TOGGLE_CHORD, {
|
|
61
71
|
description: "Toggle subagents panel",
|
|
62
72
|
handler: (ctx) => {
|