@bacnh85/pi-subagent 0.1.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/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # pi-subagent
2
+
3
+ Minimal-overhead sub-agent extension for pi. Delegate tasks to specialized agents with isolated context — running in-process via the pi SDK for zero spawn overhead and ~10x fewer tokens than process-spawning.
4
+
5
+ ## Features
6
+
7
+ - **In-process execution**: Uses `createAgentSession()` directly — no `spawn("pi")` overhead
8
+ - **Minimal token budget**: Only the agent's system prompt (no AGENTS.md, no extensions, no thinking)
9
+ - **Streaming progress**: Real-time tool-call and text updates during execution
10
+ - **Three modes**: single, parallel (max 8 tasks, 4 concurrent), chain (sequential with `{previous}`)
11
+ - **Abort support**: Esc propagates to all sub-agents
12
+ - **TUI rendering**: Collapsed/expanded views with tool-call formatting and usage stats
13
+ - **Bundled agents**: scout, reviewer, worker — overridable with your own
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ cd extensions/pi-subagent
19
+ npm install
20
+ cd ../..
21
+ pi install ./extensions/pi-subagent
22
+ ```
23
+
24
+ Or test directly:
25
+
26
+ ```bash
27
+ pi -e ./extensions/pi-subagent
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ### Single agent
33
+
34
+ ```
35
+ Use scout to find authentication code in this project
36
+ ```
37
+
38
+ ### Parallel execution
39
+
40
+ ```
41
+ Run 2 scouts in parallel: one for models, one for providers
42
+ ```
43
+
44
+ ### Chain workflow
45
+
46
+ ```
47
+ Chain: scout finds auth code, then reviewer checks it for security issues
48
+ ```
49
+
50
+ ## Included Agents
51
+
52
+ | Agent | Model | Tools | Purpose |
53
+ |-------|-------|-------|---------|
54
+ | `scout` | Haiku | read, grep, find, ls | Fast codebase recon |
55
+ | `reviewer` | Sonnet | read, grep, find, ls, bash | Code review |
56
+ | `worker` | Sonnet | all | General implementation |
57
+
58
+ ## Custom Agents
59
+
60
+ Create Markdown files with YAML frontmatter in `~/.pi/agent/agents/` (user-level) or `.pi/agents/` (project-level):
61
+
62
+ ```markdown
63
+ ---
64
+ name: my-agent
65
+ description: When to use this agent
66
+ tools: read, grep, find, ls, bash
67
+ model: claude-haiku-4-5
68
+ ---
69
+
70
+ Your system prompt here. This is the ONLY prompt the sub-agent sees.
71
+ ```
72
+
73
+ See `agent-format.md` for the full format specification.
74
+
75
+ ## Architecture
76
+
77
+ Sub-agents run in-process via the pi SDK. Compared to the process-spawn approach (spawning `pi --mode json`), this saves ~4-11K tokens per sub-agent invocation by:
78
+
79
+ - Using only the agent's system prompt (no pi defaults)
80
+ - Skipping AGENTS.md, extensions, skills, prompt templates
81
+ - Disabling thinking, compaction, retry
82
+ - Using in-memory sessions (no disk I/O)
83
+ - Sharing the parent's auth/model infrastructure
84
+
85
+ ## Requirements
86
+
87
+ - pi coding agent with configured API keys
88
+ - Peer dependencies satisfied by the pi runtime (no extra npm install needed)
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: reviewer
3
+ description: Code review specialist. Use for reviewing changes, finding bugs, suggesting improvements.
4
+ tools: read, grep, find, ls, bash
5
+ model: claude-sonnet-4-20250514
6
+ ---
7
+
8
+ You are a senior code reviewer. Review code changes and provide specific, actionable feedback.
9
+
10
+ Focus on:
11
+ 1. **Correctness**: Logic errors, edge cases, off-by-one
12
+ 2. **Security**: Injection risks, auth bypasses, data leaks
13
+ 3. **Performance**: N+1 queries, unnecessary allocations, blocking calls
14
+ 4. **Maintainability**: Unclear naming, missing error handling, tight coupling
15
+ 5. **Best practices**: Idiomatic patterns, testability, documentation
16
+
17
+ Output format:
18
+
19
+ ## Summary
20
+ Brief assessment (1-2 sentences)
21
+
22
+ ## Issues Found
23
+ For each issue:
24
+ - **Severity**: critical | high | medium | low
25
+ - **File**: path with line numbers
26
+ - **Problem**: What's wrong
27
+ - **Fix**: Suggested change (code block)
28
+
29
+ ## Overall Assessment
30
+ Green/yellow/red with reasoning.
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: scout
3
+ description: Fast codebase recon that returns compressed context for handoff. Use for finding files, understanding structure, locating symbols.
4
+ tools: read, grep, find, ls
5
+ model: claude-haiku-4-5
6
+ ---
7
+
8
+ You are a scout. Quickly investigate a codebase and return structured findings that another agent can use without re-reading everything.
9
+
10
+ Your output will be passed to an agent who has NOT seen the files you explored.
11
+
12
+ Thoroughness (infer from task, default medium):
13
+ - Quick: Targeted lookups, key files only
14
+ - Medium: Follow imports, read critical sections
15
+ - Thorough: Trace all dependencies, check tests/types
16
+
17
+ Strategy:
18
+ 1. grep/find to locate relevant code
19
+ 2. Read key sections (not entire files)
20
+ 3. Identify types, interfaces, key functions
21
+ 4. Note dependencies between files
22
+
23
+ Output format:
24
+
25
+ ## Files Retrieved
26
+ List with exact line ranges:
27
+ 1. `path/to/file.ts` (lines 10-50) - Description of what's here
28
+ 2. `path/to/other.ts` (lines 100-150) - Description
29
+ 3. ...
30
+
31
+ ## Key Code
32
+ Critical types, interfaces, or functions:
33
+
34
+ ```typescript
35
+ interface Example {
36
+ // actual code from the files
37
+ }
38
+ ```
39
+
40
+ ## Architecture
41
+ Brief explanation of how the pieces connect.
42
+
43
+ ## Start Here
44
+ Which file to look at first and why.
@@ -0,0 +1,15 @@
1
+ ---
2
+ name: worker
3
+ description: General-purpose coding agent with full tool access. Use for implementation, refactoring, debugging, and complex multi-step tasks.
4
+ model: claude-sonnet-4-20250514
5
+ ---
6
+
7
+ You are a skilled software engineer. Implement the requested task with care and precision.
8
+
9
+ Guidelines:
10
+ - Read before you edit. Understand existing code first.
11
+ - Make minimal, focused changes. Do not refactor unrelated code.
12
+ - Follow existing patterns, naming, and formatting.
13
+ - Write clear, idiomatic code with appropriate error handling.
14
+ - Test your changes when practical (run tests, lint, type-check).
15
+ - Explain your approach briefly before making changes.
package/agents.ts ADDED
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Agent discovery and configuration for pi-sugagents.
3
+ *
4
+ * Loads agent definitions from Markdown files with YAML frontmatter.
5
+ * Discovers from user-level (~/.pi/agent/agents/), project-level
6
+ * (.pi/agents/), and bundled skill agents. Results are cached and
7
+ * invalidated on /reload.
8
+ */
9
+
10
+ import * as fs from "node:fs";
11
+ import * as path from "node:path";
12
+ import { CONFIG_DIR_NAME, getAgentDir, parseFrontmatter } from "@earendil-works/pi-coding-agent";
13
+
14
+ export type AgentScope = "user" | "project" | "both";
15
+
16
+ export interface AgentConfig {
17
+ name: string;
18
+ description: string;
19
+ tools?: string[];
20
+ model?: string;
21
+ systemPrompt: string;
22
+ source: "user" | "project" | "bundled";
23
+ filePath: string;
24
+ }
25
+
26
+ export interface AgentDiscoveryResult {
27
+ agents: AgentConfig[];
28
+ projectAgentsDir: string | null;
29
+ }
30
+
31
+ interface AgentCache {
32
+ userDir: string;
33
+ projectDir: string | null;
34
+ bundledDir: string;
35
+ agents: AgentConfig[];
36
+ projectAgentsDir: string | null;
37
+ }
38
+
39
+ let _cache: AgentCache | null = null;
40
+
41
+ /** Clear the agent cache (call on /reload). */
42
+ export function invalidateAgentCache(): void {
43
+ _cache = null;
44
+ }
45
+
46
+ function loadAgentsFromDir(dir: string, source: "user" | "project" | "bundled"): AgentConfig[] {
47
+ const agents: AgentConfig[] = [];
48
+
49
+ if (!fs.existsSync(dir)) return agents;
50
+
51
+ let entries: fs.Dirent[];
52
+ try {
53
+ entries = fs.readdirSync(dir, { withFileTypes: true });
54
+ } catch {
55
+ return agents;
56
+ }
57
+
58
+ for (const entry of entries) {
59
+ if (!entry.name.endsWith(".md")) continue;
60
+ if (!entry.isFile() && !entry.isSymbolicLink()) continue;
61
+
62
+ const filePath = path.join(dir, entry.name);
63
+ let content: string;
64
+ try {
65
+ content = fs.readFileSync(filePath, "utf-8");
66
+ } catch {
67
+ continue;
68
+ }
69
+
70
+ const { frontmatter, body } = parseFrontmatter<Record<string, string>>(content);
71
+
72
+ if (!frontmatter.name || !frontmatter.description) continue;
73
+
74
+ const tools = frontmatter.tools
75
+ ?.split(",")
76
+ .map((t: string) => t.trim())
77
+ .filter(Boolean);
78
+
79
+ agents.push({
80
+ name: frontmatter.name,
81
+ description: frontmatter.description,
82
+ tools: tools && tools.length > 0 ? tools : undefined,
83
+ model: frontmatter.model || undefined,
84
+ systemPrompt: body,
85
+ source,
86
+ filePath,
87
+ });
88
+ }
89
+
90
+ return agents;
91
+ }
92
+
93
+ function isDirectory(p: string): boolean {
94
+ try {
95
+ return fs.statSync(p).isDirectory();
96
+ } catch {
97
+ return false;
98
+ }
99
+ }
100
+
101
+ function findNearestProjectAgentsDir(cwd: string): string | null {
102
+ let currentDir = cwd;
103
+ while (true) {
104
+ const candidate = path.join(currentDir, CONFIG_DIR_NAME, "agents");
105
+ if (isDirectory(candidate)) return candidate;
106
+
107
+ const parentDir = path.dirname(currentDir);
108
+ if (parentDir === currentDir) return null;
109
+ currentDir = parentDir;
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Discover agents from standard locations plus bundled skill agents.
115
+ * @param cwd - Working directory for project-level discovery.
116
+ * @param scope - Which agent directories to use.
117
+ * @param bundledAgentsDir - Path to skill-bundled agents directory.
118
+ */
119
+ export function discoverAgents(
120
+ cwd: string,
121
+ scope: AgentScope,
122
+ bundledAgentsDir: string,
123
+ ): AgentDiscoveryResult {
124
+ const userDir = path.join(getAgentDir(), "agents");
125
+ const projectAgentsDir = findNearestProjectAgentsDir(cwd);
126
+
127
+ // Check cache
128
+ if (
129
+ _cache &&
130
+ _cache.userDir === userDir &&
131
+ _cache.projectDir === projectAgentsDir &&
132
+ _cache.bundledDir === bundledAgentsDir
133
+ ) {
134
+ return { agents: _cache.agents, projectAgentsDir: _cache.projectAgentsDir };
135
+ }
136
+
137
+ const userAgents = scope === "project" ? [] : loadAgentsFromDir(userDir, "user");
138
+ const projectAgents =
139
+ scope === "user" || !projectAgentsDir ? [] : loadAgentsFromDir(projectAgentsDir, "project");
140
+ const bundledAgents = loadAgentsFromDir(bundledAgentsDir, "bundled");
141
+
142
+ const agentMap = new Map<string, AgentConfig>();
143
+
144
+ // Priority: bundled < user < project (higher index = higher priority)
145
+ for (const agent of bundledAgents) agentMap.set(agent.name, agent);
146
+ if (scope === "both" || scope === "user") {
147
+ for (const agent of userAgents) agentMap.set(agent.name, agent);
148
+ }
149
+ if (scope === "both" || scope === "project") {
150
+ for (const agent of projectAgents) agentMap.set(agent.name, agent);
151
+ }
152
+
153
+ const agents = Array.from(agentMap.values());
154
+
155
+ _cache = {
156
+ userDir,
157
+ projectDir: projectAgentsDir,
158
+ bundledDir: bundledAgentsDir,
159
+ agents,
160
+ projectAgentsDir,
161
+ };
162
+
163
+ return { agents, projectAgentsDir };
164
+ }
165
+
166
+ export function formatAgentList(agents: AgentConfig[], maxItems: number): { text: string; remaining: number } {
167
+ if (agents.length === 0) return { text: "none", remaining: 0 };
168
+ const listed = agents.slice(0, maxItems);
169
+ const remaining = agents.length - listed.length;
170
+ return {
171
+ text: listed.map((a) => `${a.name} (${a.source}): ${a.description}`).join("; "),
172
+ remaining,
173
+ };
174
+ }