@leo-alvarenga/pi-mini-subagents 0.1.1 → 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/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/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) => {
|