refactor pi resource wiring by directory

This commit is contained in:
Rasyidan Akbar F. 2026-04-14 19:57:55 +07:00
commit b465e0aed0
19 changed files with 2087 additions and 0 deletions

View file

@ -0,0 +1,131 @@
# pi subagent scaffold
Foundation for a global pi subagent extension with parallel and chained delegation.
## What this includes
- `index.ts` — main `subagent` tool and `/subagents` command
- `agents.ts` — agent discovery and manifest parsing
- `agents/` — starter bundled agents:
- `scout``zai/glm-5.1`
- `planner``openai-codex/gpt-5.4:xhigh`
- `implementer``openai-codex/gpt-5.3-codex`
- `reviewer``openai-codex/gpt-5.4:xhigh`
- `librarian``zai/glm-5.1` + `exa_search` / `exa_code`
- `prompts/` — starter prompt templates:
- `/implement`
- `/scout-and-plan`
- `/implement-and-review`
- `/parallel-scout`
- `/research`
## Discovery model
This extension supports three agent sources:
- **bundled** — agents shipped in this directory (`./agents`)
- **user**`~/.pi/agent/agents`
- **project** — nearest `.pi/agents` from the current working directory upward
Scope values:
- `global` — bundled + user
- `project` — project only
- `both` — bundled + user + project
Precedence when names collide:
1. bundled
2. user
3. project
So project-local agents override user/global ones, and user/global agents override bundled defaults.
## Parallel safety
Parallel mode is intentionally conservative.
- agents with `parallelSafe: true` can run in parallel
- agents with `parallelSafe: false` are blocked in parallel mode
The bundled `implementer` is marked serial-only.
## Agent manifest format
Each agent is a markdown file with YAML frontmatter:
```md
---
name: planner
description: Creates implementation plans
model: anthropic/claude-sonnet-4-5
tools: read, grep, find, ls
parallelSafe: true
role: planner
tags: planning, design
---
System prompt goes here.
```
Notes:
- `model` accepts normal pi `--model` values, including `provider/id` and optional `:thinking`
- the current scaffold intentionally uses built-in pi providers (`openai-codex`, `zai`), so no custom provider extension is required yet
- `tools` should usually be explicit to avoid recursive delegation
- `parallelSafe` defaults to `false` if omitted
## Install globally from this repo
Recommended runtime target:
```bash
mkdir -p ~/.pi/agent/extensions
ln -sfn /Users/rasyidanakbar/Development/dotfiles/pi/extensions/subagent ~/.pi/agent/extensions/subagent
```
Then start pi and run:
```text
/reload
```
Because prompts and bundled agents live inside the extension directory, a single directory symlink is enough.
## Usage examples
Single agent:
```text
Use subagent planner to propose a plan for refactoring auth
```
Parallel:
```text
Use subagent with two tasks in parallel: scout auth flow, librarian find similar patterns
```
Chain:
```text
Use subagent chain: scout -> planner -> implementer
```
Prompt templates:
```text
/implement add request validation to the API
/scout-and-plan refactor auth to support oauth
/implement-and-review add pagination to the endpoint
/parallel-scout investigate session handling
/research evaluate the best pattern for background job retries in this codebase
```
## Good next steps
1. add `parallel_then_reduce`
2. add budget / timeout controls
3. add a custom `index_search` tool for librarian workflows
4. wire the whole `pi/extensions/` directory into Home Manager so `~/.pi/agent/extensions/` is managed automatically
5. pair librarian with the separate `pi/extensions/exa-tools` extension for web/docs/code retrieval

View file

@ -0,0 +1,186 @@
import * as fs from "node:fs";
import * as path from "node:path";
import { fileURLToPath } from "node:url";
import { getAgentDir, parseFrontmatter } from "@mariozechner/pi-coding-agent";
export type AgentScope = "global" | "project" | "both";
export type AgentSource = "bundled" | "user" | "project" | "unknown";
export interface AgentConfig {
name: string;
description: string;
tools?: string[];
model?: string;
systemPrompt: string;
source: AgentSource;
filePath: string;
parallelSafe: boolean;
role?: string;
tags?: string[];
}
export interface AgentDiscoveryResult {
agents: AgentConfig[];
bundledAgentsDir: string;
userAgentsDir: string;
projectAgentsDir: string | null;
}
function readString(value: unknown): string | undefined {
return typeof value === "string" && value.trim() ? value.trim() : undefined;
}
function readBoolean(value: unknown): boolean | undefined {
if (typeof value === "boolean") return value;
if (typeof value === "number") return value !== 0;
if (typeof value !== "string") return undefined;
const normalized = value.trim().toLowerCase();
if (["true", "yes", "on", "1"].includes(normalized)) return true;
if (["false", "no", "off", "0"].includes(normalized)) return false;
return undefined;
}
function readStringList(value: unknown): string[] | undefined {
if (Array.isArray(value)) {
const items = value
.map((item) => (typeof item === "string" ? item.trim() : ""))
.filter(Boolean);
return items.length > 0 ? items : undefined;
}
if (typeof value === "string") {
const items = value
.split(",")
.map((item) => item.trim())
.filter(Boolean);
return items.length > 0 ? items : undefined;
}
return undefined;
}
function isDirectory(pathname: string): boolean {
try {
return fs.statSync(pathname).isDirectory();
} catch {
return false;
}
}
function getBundledAgentsDir(): string {
return fileURLToPath(new URL("./agents", import.meta.url));
}
function getUserAgentsDir(): string {
return path.join(getAgentDir(), "agents");
}
function findNearestProjectAgentsDir(cwd: string): string | null {
let currentDir = cwd;
while (true) {
const candidate = path.join(currentDir, ".pi", "agents");
if (isDirectory(candidate)) return candidate;
const parentDir = path.dirname(currentDir);
if (parentDir === currentDir) return null;
currentDir = parentDir;
}
}
function loadAgentsFromDir(dir: string, source: Exclude<AgentSource, "unknown">): AgentConfig[] {
const agents: AgentConfig[] = [];
if (!isDirectory(dir)) return agents;
let entries: fs.Dirent[];
try {
entries = fs.readdirSync(dir, { withFileTypes: true });
} catch {
return agents;
}
for (const entry of entries) {
if (!entry.name.endsWith(".md")) continue;
if (!entry.isFile() && !entry.isSymbolicLink()) continue;
const filePath = path.join(dir, entry.name);
let content: string;
try {
content = fs.readFileSync(filePath, "utf-8");
} catch {
continue;
}
const { frontmatter, body } = parseFrontmatter<Record<string, unknown>>(content);
const name = readString(frontmatter.name);
const description = readString(frontmatter.description);
if (!name || !description) continue;
agents.push({
name,
description,
tools: readStringList(frontmatter.tools),
model: readString(frontmatter.model),
systemPrompt: body.trim(),
source,
filePath,
parallelSafe: readBoolean(frontmatter.parallelSafe) ?? false,
role: readString(frontmatter.role),
tags: readStringList(frontmatter.tags),
});
}
return agents;
}
export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryResult {
const bundledAgentsDir = getBundledAgentsDir();
const userAgentsDir = getUserAgentsDir();
const projectAgentsDir = findNearestProjectAgentsDir(cwd);
const bundledAgents = loadAgentsFromDir(bundledAgentsDir, "bundled");
const userAgents = scope === "project" ? [] : loadAgentsFromDir(userAgentsDir, "user");
const projectAgents = scope === "global" || !projectAgentsDir ? [] : loadAgentsFromDir(projectAgentsDir, "project");
const agentMap = new Map<string, AgentConfig>();
if (scope === "global") {
for (const agent of bundledAgents) agentMap.set(agent.name, agent);
for (const agent of userAgents) agentMap.set(agent.name, agent);
}
if (scope === "project") {
for (const agent of projectAgents) agentMap.set(agent.name, agent);
}
if (scope === "both") {
for (const agent of bundledAgents) agentMap.set(agent.name, agent);
for (const agent of userAgents) agentMap.set(agent.name, agent);
for (const agent of projectAgents) agentMap.set(agent.name, agent);
}
return {
agents: Array.from(agentMap.values()),
bundledAgentsDir,
userAgentsDir,
projectAgentsDir,
};
}
export function formatAgentList(agents: AgentConfig[], maxItems: number): { text: string; remaining: number } {
if (agents.length === 0) return { text: "none", remaining: 0 };
const listed = agents.slice(0, maxItems);
const remaining = Math.max(0, agents.length - listed.length);
const text = listed
.map((agent) => {
const mode = agent.parallelSafe ? "parallel" : "serial";
const role = agent.role ? ` ${agent.role}` : "";
return `${agent.name} (${agent.source}, ${mode}${role ? `, ${role}` : ""})`;
})
.join(", ");
return { text, remaining };
}

View file

@ -0,0 +1,34 @@
---
name: implementer
description: Executes approved plans and makes code changes carefully
tools: read, grep, find, ls, bash, edit, write
model: openai-codex/gpt-5.3-codex
parallelSafe: false
role: implementer
tags: implementation, coding, execution
---
You are an implementation specialist operating in an isolated subagent context.
Your job is to make the requested change safely and completely.
Rules:
- Follow the provided plan if one exists.
- Prefer minimal, targeted edits.
- Do not make unrelated refactors.
- When using bash, keep commands focused on verification and repository-aware workflows.
- If the task is ambiguous, make the smallest reasonable assumption and document it.
Output format:
## Completed
What you changed.
## Files Changed
- `path/to/file.ts` - summary of change
## Validation
Commands run, checks performed, or why validation was not run.
## Notes
Any assumptions, follow-up items, or risks for a reviewer.

View file

@ -0,0 +1,39 @@
---
name: librarian
description: Retrieves references, patterns, prior art, and supporting context
tools: read, grep, find, ls, exa_search, exa_code
model: zai/glm-5.1
parallelSafe: true
role: librarian
tags: search, references, indexing, docs
---
You are a librarian.
Your job is to gather supporting references from the repository so other agents can reason faster.
Focus on:
- similar implementations elsewhere in the repo
- naming conventions and file patterns
- related tests, docs, or configs
- existing abstractions worth reusing
- official docs, external references, and code examples when local context is not enough
Tool preference:
- use `exa_search` for official docs, web references, release notes, or broader external research
- use `exa_code` for library usage patterns, OSS examples, and coding-specific prior art
- use local repo tools first when the answer is already likely in the current codebase
Output format:
## Relevant References
- `path/to/file.ts` - why it is relevant
## Reusable Patterns
Summarize useful conventions or implementation patterns.
## Related Tests / Docs
Point to tests, fixtures, docs, or configs that should be consulted.
## Recommendation
What other agent should do with these references.

View file

@ -0,0 +1,36 @@
---
name: planner
description: Turns requirements and findings into a concrete execution plan
tools: read, grep, find, ls
model: openai-codex/gpt-5.4:xhigh
parallelSafe: true
role: planner
tags: planning, design, execution
---
You are a planning specialist.
You receive requirements, codebase findings, or both. Produce a concrete implementation plan that a separate implementation agent can follow.
Rules:
- Do not modify code.
- Do not invent files or architecture without stating that they are proposals.
- Keep steps small, ordered, and testable.
- Prefer minimal, low-risk changes over broad rewrites.
Output format:
## Goal
One concise sentence.
## Plan
Numbered, execution-ready steps.
## Files to Touch
List likely files and the intended change in each.
## Validation
List the checks, commands, or behavioral verification the implementer should run.
## Risks
Call out breakage risks, hidden dependencies, or edge cases.

View file

@ -0,0 +1,37 @@
---
name: reviewer
description: Reviews code and plans for correctness, regressions, and maintainability
tools: read, grep, find, ls
model: openai-codex/gpt-5.4:xhigh
parallelSafe: true
role: reviewer
tags: review, quality, safety
---
You are a reviewer.
Your job is to evaluate the delegated work for correctness, regression risk, maintainability, and missing validation.
Focus on:
- logic bugs
- edge cases
- mismatches between plan and implementation
- missing tests or validation
- unnecessary complexity
Output format:
## Summary
Two or three sentences on overall quality.
## Must Fix
Critical issues that block merging.
## Should Fix
Important but non-blocking issues.
## Nice to Improve
Optional cleanups or simplifications.
## Validation Gaps
What should still be checked.

View file

@ -0,0 +1,40 @@
---
name: scout
description: Fast codebase recon for locating relevant files, symbols, and flows
tools: read, grep, find, ls
model: zai/glm-5.1
parallelSafe: true
role: scout
tags: search, recon, context
---
You are a scout.
Your job is to quickly investigate a codebase and return compressed, high-signal findings that another agent can use without re-reading everything from scratch.
You are optimized for:
- locating the right files
- tracing imports and call paths
- identifying key types, interfaces, and entrypoints
- narrowing the search space for planner/reviewer/implementer agents
Do not propose broad speculative rewrites. Stay concrete.
Output format:
## Goal
Restate the delegated task in one or two lines.
## Key Files
List exact files and why they matter.
- `path/to/file.ts` - purpose
- `path/to/other.ts` - purpose
## Important Findings
Bullet the most relevant facts, APIs, constraints, and relationships.
## Suggested Next Read
Name the 1-3 files another agent should inspect first, and why.
## Open Questions
Any ambiguity or missing context that another agent should verify.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,10 @@
---
description: Implementer makes the change, reviewer critiques it, implementer applies the review
---
Use the `subagent` tool with `chain` mode for this workflow:
1. Run `implementer` to implement: $@
2. Run `reviewer` to review the implementation output using `{previous}`
3. Run `implementer` again to apply the review feedback using `{previous}`
Use a chain so each step receives the prior step's output via `{previous}`.

View file

@ -0,0 +1,10 @@
---
description: Scout gathers context, planner creates a plan, implementer applies it
---
Use the `subagent` tool with `chain` mode for this workflow:
1. Run `scout` to gather the most relevant code paths for: $@
2. Run `planner` to turn the findings into an implementation plan for: $@
3. Run `implementer` to execute the plan from the previous step using `{previous}`
Use a chain so each step receives the prior step's output via `{previous}`.

View file

@ -0,0 +1,9 @@
---
description: Run scout and librarian in parallel, then summarize their findings
---
Use the `subagent` tool with `tasks` mode to run these in parallel for: $@
- `scout`: identify the most relevant files, flows, and entrypoints
- `librarian`: find related patterns, prior art, tests, or docs
After both results return, synthesize them into one concise summary with recommended next steps.

View file

@ -0,0 +1,28 @@
---
description: Run local scout and external librarian research in parallel, then synthesize grounded guidance
---
Use the `subagent` tool with `tasks` mode to run these in parallel for: $@
- `scout`: inspect the current repository and identify the most relevant local files, entrypoints, constraints, and likely change surface
- `librarian`: gather external references and coding prior art, using `exa_search` for docs/web research and `exa_code` for implementation examples when useful
After both results return:
1. Synthesize them into one grounded response.
2. Clearly separate:
- local repository findings
- external references and docs
- recommended implementation patterns
3. Prefer official docs and high-signal sources over generic summaries.
4. Call out where external advice may not fit this repository's existing architecture.
5. Do **not** implement changes unless the user explicitly asks.
Return the final answer in this structure:
## Goal
## Local Findings
## External References
## Recommended Pattern
## Risks / Caveats
## Next Steps
## Sources

View file

@ -0,0 +1,9 @@
---
description: Scout gathers context, planner turns it into a concrete plan without implementation
---
Use the `subagent` tool with `chain` mode for this workflow:
1. Run `scout` to gather the most relevant code paths for: $@
2. Run `planner` to create a concrete implementation plan for: $@ using `{previous}` as context
Return the plan only. Do not implement changes.