@chankov/agent-skills 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/.pi/extensions/agent-skills-update-check/README.md +58 -0
- package/.pi/extensions/agent-skills-update-check/index.ts +161 -0
- package/.pi/extensions/agent-skills-update-check/package.json +6 -0
- package/.versions/0.2.0/.claude/commands/build.md +18 -0
- package/.versions/0.2.0/.claude/commands/code-simplify.md +22 -0
- package/.versions/0.2.0/.claude/commands/design-agent.md +14 -0
- package/.versions/0.2.0/.claude/commands/doctor.md +13 -0
- package/.versions/0.2.0/.claude/commands/plan.md +16 -0
- package/.versions/0.2.0/.claude/commands/prime.md +22 -0
- package/.versions/0.2.0/.claude/commands/review.md +16 -0
- package/.versions/0.2.0/.claude/commands/setup.md +19 -0
- package/.versions/0.2.0/.claude/commands/ship.md +17 -0
- package/.versions/0.2.0/.claude/commands/spec.md +15 -0
- package/.versions/0.2.0/.claude/commands/test.md +19 -0
- package/.versions/0.2.0/.opencode/commands/as-build.md +17 -0
- package/.versions/0.2.0/.opencode/commands/as-code-simplify.md +16 -0
- package/.versions/0.2.0/.opencode/commands/as-design-agent.md +15 -0
- package/.versions/0.2.0/.opencode/commands/as-doctor.md +11 -0
- package/.versions/0.2.0/.opencode/commands/as-plan.md +16 -0
- package/.versions/0.2.0/.opencode/commands/as-prime.md +22 -0
- package/.versions/0.2.0/.opencode/commands/as-review.md +15 -0
- package/.versions/0.2.0/.opencode/commands/as-setup.md +11 -0
- package/.versions/0.2.0/.opencode/commands/as-ship.md +16 -0
- package/.versions/0.2.0/.opencode/commands/as-spec.md +16 -0
- package/.versions/0.2.0/.opencode/commands/as-test.md +21 -0
- package/.versions/0.2.0/.pi/agents/agent-chain.yaml +49 -0
- package/.versions/0.2.0/.pi/agents/bowser.md +19 -0
- package/.versions/0.2.0/.pi/agents/pi-pi/agent-expert.md +98 -0
- package/.versions/0.2.0/.pi/agents/pi-pi/cli-expert.md +41 -0
- package/.versions/0.2.0/.pi/agents/pi-pi/config-expert.md +63 -0
- package/.versions/0.2.0/.pi/agents/pi-pi/ext-expert.md +43 -0
- package/.versions/0.2.0/.pi/agents/pi-pi/keybinding-expert.md +134 -0
- package/.versions/0.2.0/.pi/agents/pi-pi/pi-orchestrator.md +57 -0
- package/.versions/0.2.0/.pi/agents/pi-pi/prompt-expert.md +70 -0
- package/.versions/0.2.0/.pi/agents/pi-pi/skill-expert.md +42 -0
- package/.versions/0.2.0/.pi/agents/pi-pi/theme-expert.md +40 -0
- package/.versions/0.2.0/.pi/agents/pi-pi/tui-expert.md +85 -0
- package/.versions/0.2.0/.pi/agents/teams.yaml +31 -0
- package/.versions/0.2.0/.pi/damage-control-rules.yaml +278 -0
- package/.versions/0.2.0/.pi/extensions/agent-skills-update-check/README.md +58 -0
- package/.versions/0.2.0/.pi/extensions/agent-skills-update-check/index.ts +161 -0
- package/.versions/0.2.0/.pi/extensions/agent-skills-update-check/package.json +6 -0
- package/.versions/0.2.0/.pi/extensions/chrome-devtools-mcp/README.md +39 -0
- package/.versions/0.2.0/.pi/extensions/chrome-devtools-mcp/index.ts +61 -0
- package/.versions/0.2.0/.pi/extensions/chrome-devtools-mcp/package.json +6 -0
- package/.versions/0.2.0/.pi/extensions/compact-and-continue/README.md +42 -0
- package/.versions/0.2.0/.pi/extensions/compact-and-continue/index.ts +120 -0
- package/.versions/0.2.0/.pi/extensions/compact-and-continue/package.json +6 -0
- package/.versions/0.2.0/.pi/extensions/mcp-bridge/README.md +46 -0
- package/.versions/0.2.0/.pi/extensions/mcp-bridge/index.ts +206 -0
- package/.versions/0.2.0/.pi/extensions/mcp-bridge/package.json +6 -0
- package/.versions/0.2.0/.pi/extensions/package-lock.json +1143 -0
- package/.versions/0.2.0/.pi/extensions/package.json +9 -0
- package/.versions/0.2.0/.pi/harnesses/agent-chain/README.md +37 -0
- package/.versions/0.2.0/.pi/harnesses/agent-chain/index.ts +795 -0
- package/.versions/0.2.0/.pi/harnesses/agent-chain/package.json +6 -0
- package/.versions/0.2.0/.pi/harnesses/agent-team/README.md +38 -0
- package/.versions/0.2.0/.pi/harnesses/agent-team/index.ts +732 -0
- package/.versions/0.2.0/.pi/harnesses/agent-team/package.json +6 -0
- package/.versions/0.2.0/.pi/harnesses/coms/README.md +36 -0
- package/.versions/0.2.0/.pi/harnesses/coms/index.ts +1595 -0
- package/.versions/0.2.0/.pi/harnesses/coms/package.json +6 -0
- package/.versions/0.2.0/.pi/harnesses/coms-net/README.md +46 -0
- package/.versions/0.2.0/.pi/harnesses/coms-net/index.ts +1637 -0
- package/.versions/0.2.0/.pi/harnesses/coms-net/package.json +6 -0
- package/.versions/0.2.0/.pi/harnesses/damage-control/README.md +38 -0
- package/.versions/0.2.0/.pi/harnesses/damage-control/index.ts +207 -0
- package/.versions/0.2.0/.pi/harnesses/damage-control/package.json +6 -0
- package/.versions/0.2.0/.pi/harnesses/damage-control-continue/README.md +37 -0
- package/.versions/0.2.0/.pi/harnesses/damage-control-continue/index.ts +234 -0
- package/.versions/0.2.0/.pi/harnesses/damage-control-continue/package.json +6 -0
- package/.versions/0.2.0/.pi/harnesses/minimal/README.md +27 -0
- package/.versions/0.2.0/.pi/harnesses/minimal/index.ts +32 -0
- package/.versions/0.2.0/.pi/harnesses/minimal/package.json +6 -0
- package/.versions/0.2.0/.pi/harnesses/package-lock.json +35 -0
- package/.versions/0.2.0/.pi/harnesses/package.json +9 -0
- package/.versions/0.2.0/.pi/harnesses/pi-pi/README.md +39 -0
- package/.versions/0.2.0/.pi/harnesses/pi-pi/index.ts +631 -0
- package/.versions/0.2.0/.pi/harnesses/pi-pi/package.json +6 -0
- package/.versions/0.2.0/.pi/harnesses/purpose-gate/README.md +27 -0
- package/.versions/0.2.0/.pi/harnesses/purpose-gate/index.ts +82 -0
- package/.versions/0.2.0/.pi/harnesses/purpose-gate/package.json +6 -0
- package/.versions/0.2.0/.pi/harnesses/session-replay/README.md +28 -0
- package/.versions/0.2.0/.pi/harnesses/session-replay/index.ts +214 -0
- package/.versions/0.2.0/.pi/harnesses/session-replay/package.json +6 -0
- package/.versions/0.2.0/.pi/harnesses/subagent-widget/README.md +36 -0
- package/.versions/0.2.0/.pi/harnesses/subagent-widget/index.ts +479 -0
- package/.versions/0.2.0/.pi/harnesses/subagent-widget/package.json +6 -0
- package/.versions/0.2.0/.pi/harnesses/system-select/README.md +39 -0
- package/.versions/0.2.0/.pi/harnesses/system-select/index.ts +165 -0
- package/.versions/0.2.0/.pi/harnesses/system-select/package.json +6 -0
- package/.versions/0.2.0/.pi/harnesses/tilldone/README.md +35 -0
- package/.versions/0.2.0/.pi/harnesses/tilldone/index.ts +724 -0
- package/.versions/0.2.0/.pi/harnesses/tilldone/package.json +6 -0
- package/.versions/0.2.0/.pi/harnesses/tool-counter/README.md +31 -0
- package/.versions/0.2.0/.pi/harnesses/tool-counter/index.ts +100 -0
- package/.versions/0.2.0/.pi/harnesses/tool-counter/package.json +6 -0
- package/.versions/0.2.0/.pi/harnesses/tool-counter-widget/README.md +27 -0
- package/.versions/0.2.0/.pi/harnesses/tool-counter-widget/index.ts +66 -0
- package/.versions/0.2.0/.pi/harnesses/tool-counter-widget/package.json +6 -0
- package/.versions/0.2.0/.pi/prompts/build.md +24 -0
- package/.versions/0.2.0/.pi/prompts/code-simplify.md +22 -0
- package/.versions/0.2.0/.pi/prompts/doctor.md +13 -0
- package/.versions/0.2.0/.pi/prompts/plan.md +16 -0
- package/.versions/0.2.0/.pi/prompts/review.md +16 -0
- package/.versions/0.2.0/.pi/prompts/setup.md +19 -0
- package/.versions/0.2.0/.pi/prompts/ship.md +17 -0
- package/.versions/0.2.0/.pi/prompts/spec.md +15 -0
- package/.versions/0.2.0/.pi/prompts/test.md +19 -0
- package/.versions/0.2.0/.pi/skills/bowser/SKILL.md +114 -0
- package/.versions/0.2.0/.version +1 -0
- package/.versions/0.2.0/agents/builder.md +6 -0
- package/.versions/0.2.0/agents/code-reviewer.md +93 -0
- package/.versions/0.2.0/agents/documenter.md +6 -0
- package/.versions/0.2.0/agents/plan-reviewer.md +22 -0
- package/.versions/0.2.0/agents/planner.md +6 -0
- package/.versions/0.2.0/agents/scout.md +6 -0
- package/.versions/0.2.0/agents/security-auditor.md +97 -0
- package/.versions/0.2.0/agents/test-engineer.md +89 -0
- package/.versions/0.2.0/hooks/SIMPLIFY-IGNORE.md +90 -0
- package/.versions/0.2.0/hooks/hooks.json +14 -0
- package/.versions/0.2.0/hooks/session-start.sh +74 -0
- package/.versions/0.2.0/hooks/simplify-ignore-test.sh +247 -0
- package/.versions/0.2.0/hooks/simplify-ignore.sh +302 -0
- package/.versions/0.2.0/references/accessibility-checklist.md +159 -0
- package/.versions/0.2.0/references/performance-checklist.md +121 -0
- package/.versions/0.2.0/references/prompting-patterns.md +380 -0
- package/.versions/0.2.0/references/security-checklist.md +134 -0
- package/.versions/0.2.0/references/testing-patterns.md +236 -0
- package/.versions/0.2.0/skills/api-and-interface-design/SKILL.md +294 -0
- package/.versions/0.2.0/skills/browser-testing-with-devtools/SKILL.md +335 -0
- package/.versions/0.2.0/skills/ci-cd-and-automation/SKILL.md +390 -0
- package/.versions/0.2.0/skills/code-review-and-quality/SKILL.md +347 -0
- package/.versions/0.2.0/skills/code-simplification/SKILL.md +331 -0
- package/.versions/0.2.0/skills/context-engineering/SKILL.md +291 -0
- package/.versions/0.2.0/skills/debugging-and-error-recovery/SKILL.md +300 -0
- package/.versions/0.2.0/skills/deprecation-and-migration/SKILL.md +206 -0
- package/.versions/0.2.0/skills/designing-agents/SKILL.md +394 -0
- package/.versions/0.2.0/skills/designing-agents/pi-harness-authoring.md +213 -0
- package/.versions/0.2.0/skills/documentation-and-adrs/SKILL.md +278 -0
- package/.versions/0.2.0/skills/frontend-ui-engineering/SKILL.md +322 -0
- package/.versions/0.2.0/skills/git-workflow-and-versioning/SKILL.md +316 -0
- package/.versions/0.2.0/skills/guided-workspace-setup/SKILL.md +293 -0
- package/.versions/0.2.0/skills/idea-refine/SKILL.md +178 -0
- package/.versions/0.2.0/skills/idea-refine/examples.md +238 -0
- package/.versions/0.2.0/skills/idea-refine/frameworks.md +99 -0
- package/.versions/0.2.0/skills/idea-refine/refinement-criteria.md +113 -0
- package/.versions/0.2.0/skills/idea-refine/scripts/idea-refine.sh +15 -0
- package/.versions/0.2.0/skills/incremental-implementation/SKILL.md +279 -0
- package/.versions/0.2.0/skills/performance-optimization/SKILL.md +350 -0
- package/.versions/0.2.0/skills/planning-and-task-breakdown/SKILL.md +237 -0
- package/.versions/0.2.0/skills/security-and-hardening/SKILL.md +349 -0
- package/.versions/0.2.0/skills/shipping-and-launch/SKILL.md +309 -0
- package/.versions/0.2.0/skills/source-driven-development/SKILL.md +194 -0
- package/.versions/0.2.0/skills/spec-driven-development/SKILL.md +237 -0
- package/.versions/0.2.0/skills/test-driven-development/SKILL.md +379 -0
- package/.versions/0.2.0/skills/using-agent-skills/SKILL.md +176 -0
- package/CHANGELOG.md +36 -0
- package/bin/cli.js +42 -7
- package/bin/lib/update-notifier.js +195 -0
- package/docs/npm-install.md +60 -0
- package/hooks/session-start.sh +66 -12
- package/package.json +1 -1
- package/skills/guided-workspace-setup/SKILL.md +1 -1
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subagent Widget — /sub, /subclear, /subrm, /subcont commands with stacking live widgets
|
|
3
|
+
*
|
|
4
|
+
* Each /sub spawns a background Pi subagent with its own persistent session,
|
|
5
|
+
* enabling conversation continuations via /subcont.
|
|
6
|
+
*
|
|
7
|
+
* Usage: pi -e extensions/subagent-widget.ts
|
|
8
|
+
* Then:
|
|
9
|
+
* /sub list files and summarize — spawn a new subagent
|
|
10
|
+
* /subcont 1 now write tests for it — continue subagent #1's conversation
|
|
11
|
+
* /subrm 2 — remove subagent #2 widget
|
|
12
|
+
* /subclear — clear all subagent widgets
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
16
|
+
import { DynamicBorder } from "@mariozechner/pi-coding-agent";
|
|
17
|
+
import { Container, Text } from "@mariozechner/pi-tui";
|
|
18
|
+
import { Type } from "@sinclair/typebox";
|
|
19
|
+
const { spawn } = require("child_process") as any;
|
|
20
|
+
import * as fs from "fs";
|
|
21
|
+
import * as os from "os";
|
|
22
|
+
import * as path from "path";
|
|
23
|
+
|
|
24
|
+
interface SubState {
|
|
25
|
+
id: number;
|
|
26
|
+
status: "running" | "done" | "error";
|
|
27
|
+
task: string;
|
|
28
|
+
textChunks: string[];
|
|
29
|
+
toolCount: number;
|
|
30
|
+
elapsed: number;
|
|
31
|
+
sessionFile: string; // persistent JSONL session path — used by /subcont to resume
|
|
32
|
+
turnCount: number; // increments each time /subcont continues this agent
|
|
33
|
+
proc?: any; // active ChildProcess ref (for kill on /subrm)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default function (pi: ExtensionAPI) {
|
|
37
|
+
const agents: Map<number, SubState> = new Map();
|
|
38
|
+
let nextId = 1;
|
|
39
|
+
let widgetCtx: any;
|
|
40
|
+
|
|
41
|
+
// ── Session file helpers ──────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
function makeSessionFile(id: number): string {
|
|
44
|
+
const dir = path.join(os.homedir(), ".pi", "agent", "sessions", "subagents");
|
|
45
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
46
|
+
return path.join(dir, `subagent-${id}-${Date.now()}.jsonl`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ── Widget rendering ──────────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
function updateWidgets() {
|
|
52
|
+
if (!widgetCtx) return;
|
|
53
|
+
|
|
54
|
+
for (const [id, state] of Array.from(agents.entries())) {
|
|
55
|
+
const key = `sub-${id}`;
|
|
56
|
+
widgetCtx.ui.setWidget(key, (_tui: any, theme: any) => {
|
|
57
|
+
const container = new Container();
|
|
58
|
+
const borderFn = (s: string) => theme.fg("dim", s);
|
|
59
|
+
|
|
60
|
+
container.addChild(new Text("", 0, 0)); // top margin
|
|
61
|
+
container.addChild(new DynamicBorder(borderFn));
|
|
62
|
+
const content = new Text("", 1, 0);
|
|
63
|
+
container.addChild(content);
|
|
64
|
+
container.addChild(new DynamicBorder(borderFn));
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
render(width: number): string[] {
|
|
68
|
+
const lines: string[] = [];
|
|
69
|
+
const statusColor = state.status === "running" ? "accent"
|
|
70
|
+
: state.status === "done" ? "success" : "error";
|
|
71
|
+
const statusIcon = state.status === "running" ? "●"
|
|
72
|
+
: state.status === "done" ? "✓" : "✗";
|
|
73
|
+
|
|
74
|
+
const taskPreview = state.task.length > 40
|
|
75
|
+
? state.task.slice(0, 37) + "..."
|
|
76
|
+
: state.task;
|
|
77
|
+
|
|
78
|
+
const turnLabel = state.turnCount > 1
|
|
79
|
+
? theme.fg("dim", ` · Turn ${state.turnCount}`)
|
|
80
|
+
: "";
|
|
81
|
+
|
|
82
|
+
lines.push(
|
|
83
|
+
theme.fg(statusColor, `${statusIcon} Subagent #${state.id}`) +
|
|
84
|
+
turnLabel +
|
|
85
|
+
theme.fg("dim", ` ${taskPreview}`) +
|
|
86
|
+
theme.fg("dim", ` (${Math.round(state.elapsed / 1000)}s)`) +
|
|
87
|
+
theme.fg("dim", ` | Tools: ${state.toolCount}`)
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const fullText = state.textChunks.join("");
|
|
91
|
+
const lastLine = fullText.split("\n").filter((l: string) => l.trim()).pop() || "";
|
|
92
|
+
if (lastLine) {
|
|
93
|
+
const trimmed = lastLine.length > width - 10
|
|
94
|
+
? lastLine.slice(0, width - 13) + "..."
|
|
95
|
+
: lastLine;
|
|
96
|
+
lines.push(theme.fg("muted", ` ${trimmed}`));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
content.setText(lines.join("\n"));
|
|
100
|
+
return container.render(width);
|
|
101
|
+
},
|
|
102
|
+
invalidate() {
|
|
103
|
+
container.invalidate();
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ── Streaming helpers ─────────────────────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
function processLine(state: SubState, line: string) {
|
|
113
|
+
if (!line.trim()) return;
|
|
114
|
+
try {
|
|
115
|
+
const event = JSON.parse(line);
|
|
116
|
+
const type = event.type;
|
|
117
|
+
|
|
118
|
+
if (type === "message_update") {
|
|
119
|
+
const delta = event.assistantMessageEvent;
|
|
120
|
+
if (delta?.type === "text_delta") {
|
|
121
|
+
state.textChunks.push(delta.delta || "");
|
|
122
|
+
updateWidgets();
|
|
123
|
+
}
|
|
124
|
+
} else if (type === "tool_execution_start") {
|
|
125
|
+
state.toolCount++;
|
|
126
|
+
updateWidgets();
|
|
127
|
+
}
|
|
128
|
+
} catch {}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function spawnAgent(
|
|
132
|
+
state: SubState,
|
|
133
|
+
prompt: string,
|
|
134
|
+
ctx: any,
|
|
135
|
+
): Promise<void> {
|
|
136
|
+
const model = ctx.model
|
|
137
|
+
? `${ctx.model.provider}/${ctx.model.id}`
|
|
138
|
+
: "openrouter/google/gemini-3-flash-preview";
|
|
139
|
+
|
|
140
|
+
return new Promise<void>((resolve) => {
|
|
141
|
+
const proc = spawn("pi", [
|
|
142
|
+
"--mode", "json",
|
|
143
|
+
"-p",
|
|
144
|
+
"--session", state.sessionFile, // persistent session for /subcont resumption
|
|
145
|
+
"--no-extensions",
|
|
146
|
+
"--model", model,
|
|
147
|
+
"--tools", "read,bash,grep,find,ls",
|
|
148
|
+
"--thinking", "off",
|
|
149
|
+
prompt,
|
|
150
|
+
], {
|
|
151
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
152
|
+
env: { ...process.env },
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
state.proc = proc;
|
|
156
|
+
|
|
157
|
+
const startTime = Date.now();
|
|
158
|
+
const timer = setInterval(() => {
|
|
159
|
+
state.elapsed = Date.now() - startTime;
|
|
160
|
+
updateWidgets();
|
|
161
|
+
}, 1000);
|
|
162
|
+
|
|
163
|
+
let buffer = "";
|
|
164
|
+
|
|
165
|
+
proc.stdout!.setEncoding("utf-8");
|
|
166
|
+
proc.stdout!.on("data", (chunk: string) => {
|
|
167
|
+
buffer += chunk;
|
|
168
|
+
const lines = buffer.split("\n");
|
|
169
|
+
buffer = lines.pop() || "";
|
|
170
|
+
for (const line of lines) processLine(state, line);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
proc.stderr!.setEncoding("utf-8");
|
|
174
|
+
proc.stderr!.on("data", (chunk: string) => {
|
|
175
|
+
if (chunk.trim()) {
|
|
176
|
+
state.textChunks.push(chunk);
|
|
177
|
+
updateWidgets();
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
proc.on("close", (code) => {
|
|
182
|
+
if (buffer.trim()) processLine(state, buffer);
|
|
183
|
+
clearInterval(timer);
|
|
184
|
+
state.elapsed = Date.now() - startTime;
|
|
185
|
+
state.status = code === 0 ? "done" : "error";
|
|
186
|
+
state.proc = undefined;
|
|
187
|
+
updateWidgets();
|
|
188
|
+
|
|
189
|
+
const result = state.textChunks.join("");
|
|
190
|
+
ctx.ui.notify(
|
|
191
|
+
`Subagent #${state.id} ${state.status} in ${Math.round(state.elapsed / 1000)}s`,
|
|
192
|
+
state.status === "done" ? "success" : "error"
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
pi.sendMessage({
|
|
196
|
+
customType: "subagent-result",
|
|
197
|
+
content: `Subagent #${state.id}${state.turnCount > 1 ? ` (Turn ${state.turnCount})` : ""} finished "${prompt}" in ${Math.round(state.elapsed / 1000)}s.\n\nResult:\n${result.slice(0, 8000)}${result.length > 8000 ? "\n\n... [truncated]" : ""}`,
|
|
198
|
+
display: true,
|
|
199
|
+
}, { deliverAs: "followUp", triggerTurn: true });
|
|
200
|
+
|
|
201
|
+
resolve();
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
proc.on("error", (err) => {
|
|
205
|
+
clearInterval(timer);
|
|
206
|
+
state.status = "error";
|
|
207
|
+
state.proc = undefined;
|
|
208
|
+
state.textChunks.push(`Error: ${err.message}`);
|
|
209
|
+
updateWidgets();
|
|
210
|
+
resolve();
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ── Tools for the Main Agent ──────────────────────────────────────────────
|
|
216
|
+
|
|
217
|
+
pi.registerTool({
|
|
218
|
+
name: "subagent_create",
|
|
219
|
+
description: "Spawn a background subagent to perform a task. Returns the subagent ID immediately while it runs in the background. Results will be delivered as a follow-up message when finished.",
|
|
220
|
+
parameters: Type.Object({
|
|
221
|
+
task: Type.String({ description: "The complete task description for the subagent to perform" }),
|
|
222
|
+
}),
|
|
223
|
+
execute: async (callId, args, _signal, _onUpdate, ctx) => {
|
|
224
|
+
widgetCtx = ctx;
|
|
225
|
+
const id = nextId++;
|
|
226
|
+
const state: SubState = {
|
|
227
|
+
id,
|
|
228
|
+
status: "running",
|
|
229
|
+
task: args.task,
|
|
230
|
+
textChunks: [],
|
|
231
|
+
toolCount: 0,
|
|
232
|
+
elapsed: 0,
|
|
233
|
+
sessionFile: makeSessionFile(id),
|
|
234
|
+
turnCount: 1,
|
|
235
|
+
};
|
|
236
|
+
agents.set(id, state);
|
|
237
|
+
updateWidgets();
|
|
238
|
+
|
|
239
|
+
// Fire-and-forget
|
|
240
|
+
spawnAgent(state, args.task, ctx);
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
content: [{ type: "text", text: `Subagent #${id} spawned and running in background.` }],
|
|
244
|
+
};
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
pi.registerTool({
|
|
249
|
+
name: "subagent_continue",
|
|
250
|
+
description: "Continue an existing subagent's conversation. Use this to give further instructions to a finished subagent. Returns immediately while it runs in the background.",
|
|
251
|
+
parameters: Type.Object({
|
|
252
|
+
id: Type.Number({ description: "The ID of the subagent to continue" }),
|
|
253
|
+
prompt: Type.String({ description: "The follow-up prompt or new instructions" }),
|
|
254
|
+
}),
|
|
255
|
+
execute: async (callId, args, _signal, _onUpdate, ctx) => {
|
|
256
|
+
widgetCtx = ctx;
|
|
257
|
+
const state = agents.get(args.id);
|
|
258
|
+
if (!state) {
|
|
259
|
+
return { content: [{ type: "text", text: `Error: No subagent #${args.id} found.` }] };
|
|
260
|
+
}
|
|
261
|
+
if (state.status === "running") {
|
|
262
|
+
return { content: [{ type: "text", text: `Error: Subagent #${args.id} is still running.` }] };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
state.status = "running";
|
|
266
|
+
state.task = args.prompt;
|
|
267
|
+
state.textChunks = [];
|
|
268
|
+
state.elapsed = 0;
|
|
269
|
+
state.turnCount++;
|
|
270
|
+
updateWidgets();
|
|
271
|
+
|
|
272
|
+
ctx.ui.notify(`Continuing Subagent #${args.id} (Turn ${state.turnCount})…`, "info");
|
|
273
|
+
spawnAgent(state, args.prompt, ctx);
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
content: [{ type: "text", text: `Subagent #${args.id} continuing conversation in background.` }],
|
|
277
|
+
};
|
|
278
|
+
},
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
pi.registerTool({
|
|
282
|
+
name: "subagent_remove",
|
|
283
|
+
description: "Remove a specific subagent. Kills it if it's currently running.",
|
|
284
|
+
parameters: Type.Object({
|
|
285
|
+
id: Type.Number({ description: "The ID of the subagent to remove" }),
|
|
286
|
+
}),
|
|
287
|
+
execute: async (callId, args, _signal, _onUpdate, ctx) => {
|
|
288
|
+
widgetCtx = ctx;
|
|
289
|
+
const state = agents.get(args.id);
|
|
290
|
+
if (!state) {
|
|
291
|
+
return { content: [{ type: "text", text: `Error: No subagent #${args.id} found.` }] };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (state.proc && state.status === "running") {
|
|
295
|
+
state.proc.kill("SIGTERM");
|
|
296
|
+
}
|
|
297
|
+
ctx.ui.setWidget(`sub-${args.id}`, undefined);
|
|
298
|
+
agents.delete(args.id);
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
content: [{ type: "text", text: `Subagent #${args.id} removed successfully.` }],
|
|
302
|
+
};
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
pi.registerTool({
|
|
307
|
+
name: "subagent_list",
|
|
308
|
+
description: "List all active and finished subagents, showing their IDs, tasks, and status.",
|
|
309
|
+
parameters: Type.Object({}),
|
|
310
|
+
execute: async () => {
|
|
311
|
+
if (agents.size === 0) {
|
|
312
|
+
return { content: [{ type: "text", text: "No active subagents." }] };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const list = Array.from(agents.values()).map(s =>
|
|
316
|
+
`#${s.id} [${s.status.toUpperCase()}] (Turn ${s.turnCount}) - ${s.task}`
|
|
317
|
+
).join("\n");
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
content: [{ type: "text", text: `Subagents:\n${list}` }],
|
|
321
|
+
};
|
|
322
|
+
},
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
// ── /sub <task> ───────────────────────────────────────────────────────────
|
|
328
|
+
|
|
329
|
+
pi.registerCommand("sub", {
|
|
330
|
+
description: "Spawn a subagent with live widget: /sub <task>",
|
|
331
|
+
handler: async (args, ctx) => {
|
|
332
|
+
widgetCtx = ctx;
|
|
333
|
+
|
|
334
|
+
const task = args?.trim();
|
|
335
|
+
if (!task) {
|
|
336
|
+
ctx.ui.notify("Usage: /sub <task>", "error");
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const id = nextId++;
|
|
341
|
+
const state: SubState = {
|
|
342
|
+
id,
|
|
343
|
+
status: "running",
|
|
344
|
+
task,
|
|
345
|
+
textChunks: [],
|
|
346
|
+
toolCount: 0,
|
|
347
|
+
elapsed: 0,
|
|
348
|
+
sessionFile: makeSessionFile(id),
|
|
349
|
+
turnCount: 1,
|
|
350
|
+
};
|
|
351
|
+
agents.set(id, state);
|
|
352
|
+
updateWidgets();
|
|
353
|
+
|
|
354
|
+
// Fire-and-forget
|
|
355
|
+
spawnAgent(state, task, ctx);
|
|
356
|
+
},
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
// ── /subcont <number> <prompt> ────────────────────────────────────────────
|
|
360
|
+
|
|
361
|
+
pi.registerCommand("subcont", {
|
|
362
|
+
description: "Continue an existing subagent's conversation: /subcont <number> <prompt>",
|
|
363
|
+
handler: async (args, ctx) => {
|
|
364
|
+
widgetCtx = ctx;
|
|
365
|
+
|
|
366
|
+
const trimmed = args?.trim() ?? "";
|
|
367
|
+
const spaceIdx = trimmed.indexOf(" ");
|
|
368
|
+
if (spaceIdx === -1) {
|
|
369
|
+
ctx.ui.notify("Usage: /subcont <number> <prompt>", "error");
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const num = parseInt(trimmed.slice(0, spaceIdx), 10);
|
|
374
|
+
const prompt = trimmed.slice(spaceIdx + 1).trim();
|
|
375
|
+
|
|
376
|
+
if (isNaN(num) || !prompt) {
|
|
377
|
+
ctx.ui.notify("Usage: /subcont <number> <prompt>", "error");
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const state = agents.get(num);
|
|
382
|
+
if (!state) {
|
|
383
|
+
ctx.ui.notify(`No subagent #${num} found. Use /sub to create one.`, "error");
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (state.status === "running") {
|
|
388
|
+
ctx.ui.notify(`Subagent #${num} is still running — wait for it to finish first.`, "warning");
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Resume: update state for a new turn
|
|
393
|
+
state.status = "running";
|
|
394
|
+
state.task = prompt;
|
|
395
|
+
state.textChunks = [];
|
|
396
|
+
state.elapsed = 0;
|
|
397
|
+
state.turnCount++;
|
|
398
|
+
updateWidgets();
|
|
399
|
+
|
|
400
|
+
ctx.ui.notify(`Continuing Subagent #${num} (Turn ${state.turnCount})…`, "info");
|
|
401
|
+
|
|
402
|
+
// Fire-and-forget — reuses the same sessionFile for conversation history
|
|
403
|
+
spawnAgent(state, prompt, ctx);
|
|
404
|
+
},
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
// ── /subrm <number> ───────────────────────────────────────────────────────
|
|
408
|
+
|
|
409
|
+
pi.registerCommand("subrm", {
|
|
410
|
+
description: "Remove a specific subagent widget: /subrm <number>",
|
|
411
|
+
handler: async (args, ctx) => {
|
|
412
|
+
widgetCtx = ctx;
|
|
413
|
+
|
|
414
|
+
const num = parseInt(args?.trim() ?? "", 10);
|
|
415
|
+
if (isNaN(num)) {
|
|
416
|
+
ctx.ui.notify("Usage: /subrm <number>", "error");
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const state = agents.get(num);
|
|
421
|
+
if (!state) {
|
|
422
|
+
ctx.ui.notify(`No subagent #${num} found.`, "error");
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Kill the process if still running
|
|
427
|
+
if (state.proc && state.status === "running") {
|
|
428
|
+
state.proc.kill("SIGTERM");
|
|
429
|
+
ctx.ui.notify(`Subagent #${num} killed and removed.`, "warning");
|
|
430
|
+
} else {
|
|
431
|
+
ctx.ui.notify(`Subagent #${num} removed.`, "info");
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
ctx.ui.setWidget(`sub-${num}`, undefined);
|
|
435
|
+
agents.delete(num);
|
|
436
|
+
},
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
// ── /subclear ─────────────────────────────────────────────────────────────
|
|
440
|
+
|
|
441
|
+
pi.registerCommand("subclear", {
|
|
442
|
+
description: "Clear all subagent widgets",
|
|
443
|
+
handler: async (_args, ctx) => {
|
|
444
|
+
widgetCtx = ctx;
|
|
445
|
+
|
|
446
|
+
let killed = 0;
|
|
447
|
+
for (const [id, state] of Array.from(agents.entries())) {
|
|
448
|
+
if (state.proc && state.status === "running") {
|
|
449
|
+
state.proc.kill("SIGTERM");
|
|
450
|
+
killed++;
|
|
451
|
+
}
|
|
452
|
+
ctx.ui.setWidget(`sub-${id}`, undefined);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const total = agents.size;
|
|
456
|
+
agents.clear();
|
|
457
|
+
nextId = 1;
|
|
458
|
+
|
|
459
|
+
const msg = total === 0
|
|
460
|
+
? "No subagents to clear."
|
|
461
|
+
: `Cleared ${total} subagent${total !== 1 ? "s" : ""}${killed > 0 ? ` (${killed} killed)` : ""}.`;
|
|
462
|
+
ctx.ui.notify(msg, total === 0 ? "info" : "success");
|
|
463
|
+
},
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
// ── Session lifecycle ─────────────────────────────────────────────────────
|
|
467
|
+
|
|
468
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
469
|
+
for (const [id, state] of Array.from(agents.entries())) {
|
|
470
|
+
if (state.proc && state.status === "running") {
|
|
471
|
+
state.proc.kill("SIGTERM");
|
|
472
|
+
}
|
|
473
|
+
ctx.ui.setWidget(`sub-${id}`, undefined);
|
|
474
|
+
}
|
|
475
|
+
agents.clear();
|
|
476
|
+
nextId = 1;
|
|
477
|
+
widgetCtx = ctx;
|
|
478
|
+
});
|
|
479
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# system-select
|
|
2
|
+
|
|
3
|
+
Switch the system prompt via `/system`.
|
|
4
|
+
|
|
5
|
+
> Ported from [`pi-vs-claude-code`](https://github.com/disler/pi-vs-claude-code) by [disler](https://github.com/disler) (MIT). See the [extension catalog](../../../docs/pi-extensions.md).
|
|
6
|
+
|
|
7
|
+
## What it does
|
|
8
|
+
|
|
9
|
+
Scans `.pi/agents/`, `.claude/agents/`, `.gemini/agents/`, and `.codex/agents/` — both
|
|
10
|
+
project-local and global — for agent definition `.md` files. `/system` opens a select
|
|
11
|
+
dialog to pick one; the chosen agent's body is prepended to pi's default instructions so
|
|
12
|
+
tool usage still works, and tools are restricted to the agent's declared tool set when
|
|
13
|
+
one is specified.
|
|
14
|
+
|
|
15
|
+
## Commands & tools
|
|
16
|
+
|
|
17
|
+
- `/system` — open the agent-persona picker
|
|
18
|
+
|
|
19
|
+
## Requires
|
|
20
|
+
|
|
21
|
+
- Agent definition `.md` files in any scanned directory. This repo ships a set under
|
|
22
|
+
`.pi/agents/` (`scout`, `planner`, `builder`, `reviewer`, `documenter`, `red-team`, …).
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pi -e .pi/harnesses/system-select/index.ts
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Pairs well with `minimal` as a lightweight footer:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pi -e .pi/harnesses/system-select/index.ts -e .pi/harnesses/minimal/index.ts
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Upstream changes
|
|
37
|
+
|
|
38
|
+
- Theme integration removed — the `themeMap.ts` import and the `applyExtensionDefaults()`
|
|
39
|
+
call were stripped (this repo does not ship pi themes).
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System Select — Switch the system prompt via /system
|
|
3
|
+
*
|
|
4
|
+
* Scans .pi/agents/, .claude/agents/, .gemini/agents/, .codex/agents/
|
|
5
|
+
* (project-local and global) for agent definition .md files.
|
|
6
|
+
*
|
|
7
|
+
* /system opens a select dialog to pick a system prompt. The selected
|
|
8
|
+
* agent's body is prepended to Pi's default instructions so tool usage
|
|
9
|
+
* still works. Tools are restricted to the agent's declared tool set
|
|
10
|
+
* if specified.
|
|
11
|
+
*
|
|
12
|
+
* Usage: pi -e extensions/system-select.ts -e extensions/minimal.ts
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
16
|
+
import { readdirSync, readFileSync, existsSync } from "node:fs";
|
|
17
|
+
import { join, basename } from "node:path";
|
|
18
|
+
import { homedir } from "node:os";
|
|
19
|
+
|
|
20
|
+
interface AgentDef {
|
|
21
|
+
name: string;
|
|
22
|
+
description: string;
|
|
23
|
+
tools: string[];
|
|
24
|
+
body: string;
|
|
25
|
+
source: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function parseFrontmatter(raw: string): { fields: Record<string, string>; body: string } {
|
|
29
|
+
const match = raw.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/);
|
|
30
|
+
if (!match) return { fields: {}, body: raw };
|
|
31
|
+
const fields: Record<string, string> = {};
|
|
32
|
+
for (const line of match[1].split("\n")) {
|
|
33
|
+
const idx = line.indexOf(":");
|
|
34
|
+
if (idx > 0) fields[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
|
|
35
|
+
}
|
|
36
|
+
return { fields, body: match[2] };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function scanAgents(dir: string, source: string): AgentDef[] {
|
|
40
|
+
if (!existsSync(dir)) return [];
|
|
41
|
+
const agents: AgentDef[] = [];
|
|
42
|
+
try {
|
|
43
|
+
for (const file of readdirSync(dir)) {
|
|
44
|
+
if (!file.endsWith(".md")) continue;
|
|
45
|
+
const raw = readFileSync(join(dir, file), "utf-8");
|
|
46
|
+
const { fields, body } = parseFrontmatter(raw);
|
|
47
|
+
agents.push({
|
|
48
|
+
name: fields.name || basename(file, ".md"),
|
|
49
|
+
description: fields.description || "",
|
|
50
|
+
tools: fields.tools ? fields.tools.split(",").map((t) => t.trim()) : [],
|
|
51
|
+
body: body.trim(),
|
|
52
|
+
source,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
} catch {}
|
|
56
|
+
return agents;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function displayName(name: string): string {
|
|
60
|
+
return name.split("-").map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export default function (pi: ExtensionAPI) {
|
|
64
|
+
let activeAgent: AgentDef | null = null;
|
|
65
|
+
let allAgents: AgentDef[] = [];
|
|
66
|
+
let defaultTools: string[] = [];
|
|
67
|
+
|
|
68
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
69
|
+
activeAgent = null;
|
|
70
|
+
allAgents = [];
|
|
71
|
+
|
|
72
|
+
const home = homedir();
|
|
73
|
+
const cwd = ctx.cwd;
|
|
74
|
+
|
|
75
|
+
const dirs: [string, string][] = [
|
|
76
|
+
[join(cwd, ".pi", "agents"), ".pi"],
|
|
77
|
+
[join(cwd, ".claude", "agents"), ".claude"],
|
|
78
|
+
[join(cwd, ".gemini", "agents"), ".gemini"],
|
|
79
|
+
[join(cwd, ".codex", "agents"), ".codex"],
|
|
80
|
+
[join(home, ".pi", "agent", "agents"), "~/.pi"],
|
|
81
|
+
[join(home, ".claude", "agents"), "~/.claude"],
|
|
82
|
+
[join(home, ".gemini", "agents"), "~/.gemini"],
|
|
83
|
+
[join(home, ".codex", "agents"), "~/.codex"],
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
const seen = new Set<string>();
|
|
87
|
+
const sourceCounts: Record<string, number> = {};
|
|
88
|
+
|
|
89
|
+
for (const [dir, source] of dirs) {
|
|
90
|
+
const agents = scanAgents(dir, source);
|
|
91
|
+
for (const agent of agents) {
|
|
92
|
+
const key = agent.name.toLowerCase();
|
|
93
|
+
if (seen.has(key)) continue;
|
|
94
|
+
seen.add(key);
|
|
95
|
+
allAgents.push(agent);
|
|
96
|
+
sourceCounts[source] = (sourceCounts[source] || 0) + 1;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
defaultTools = pi.getActiveTools();
|
|
101
|
+
ctx.ui.setStatus("system-prompt", "System Prompt: Default");
|
|
102
|
+
|
|
103
|
+
const defaultPrompt = ctx.getSystemPrompt();
|
|
104
|
+
const lines = defaultPrompt.split("\n").length;
|
|
105
|
+
const chars = defaultPrompt.length;
|
|
106
|
+
|
|
107
|
+
const loadedSources = Object.entries(sourceCounts)
|
|
108
|
+
.map(([src, count]) => `${count} from ${src}`)
|
|
109
|
+
.join(", ");
|
|
110
|
+
|
|
111
|
+
const notifyLines = [];
|
|
112
|
+
if (allAgents.length > 0) {
|
|
113
|
+
notifyLines.push(`Loaded ${allAgents.length} agents (${loadedSources})`);
|
|
114
|
+
}
|
|
115
|
+
notifyLines.push(`System Prompt: Default (${lines} lines, ${chars} chars)`);
|
|
116
|
+
|
|
117
|
+
ctx.ui.notify(notifyLines.join("\n"), "info");
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
pi.registerCommand("system", {
|
|
121
|
+
description: "Select a system prompt from discovered agents",
|
|
122
|
+
handler: async (_args, ctx) => {
|
|
123
|
+
if (allAgents.length === 0) {
|
|
124
|
+
ctx.ui.notify("No agents found in .*/agents/*.md", "warning");
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const options = [
|
|
129
|
+
"Reset to Default",
|
|
130
|
+
...allAgents.map((a) => `${a.name} — ${a.description} [${a.source}]`),
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
const choice = await ctx.ui.select("Select System Prompt", options);
|
|
134
|
+
if (choice === undefined) return;
|
|
135
|
+
|
|
136
|
+
if (choice === options[0]) {
|
|
137
|
+
activeAgent = null;
|
|
138
|
+
pi.setActiveTools(defaultTools);
|
|
139
|
+
ctx.ui.setStatus("system-prompt", "System Prompt: Default");
|
|
140
|
+
ctx.ui.notify("System Prompt reset to Default", "success");
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const idx = options.indexOf(choice) - 1;
|
|
145
|
+
const agent = allAgents[idx];
|
|
146
|
+
activeAgent = agent;
|
|
147
|
+
|
|
148
|
+
if (agent.tools.length > 0) {
|
|
149
|
+
pi.setActiveTools(agent.tools);
|
|
150
|
+
} else {
|
|
151
|
+
pi.setActiveTools(defaultTools);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
ctx.ui.setStatus("system-prompt", `System Prompt: ${displayName(agent.name)}`);
|
|
155
|
+
ctx.ui.notify(`System Prompt switched to: ${displayName(agent.name)}`, "success");
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
pi.on("before_agent_start", async (event, _ctx) => {
|
|
160
|
+
if (!activeAgent) return;
|
|
161
|
+
return {
|
|
162
|
+
systemPrompt: activeAgent.body + "\n\n" + event.systemPrompt,
|
|
163
|
+
};
|
|
164
|
+
});
|
|
165
|
+
}
|