pi-code 0.6.0 → 0.7.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 +4 -4
- package/extensions/internal/builtin-styles/explanatory.md +15 -0
- package/extensions/internal/builtin-styles/learning.md +15 -0
- package/extensions/internal/builtin-styles/proactive.md +9 -0
- package/extensions/internal/project-approval.ts +10 -0
- package/extensions/mcp.ts +52 -5
- package/extensions/output-styles.ts +43 -7
- package/extensions/status-line.ts +154 -12
- package/extensions/subagent/README.md +15 -34
- package/extensions/subagent/agents/explore.md +18 -0
- package/extensions/subagent/agents/general-purpose.md +10 -0
- package/extensions/subagent/agents/plan.md +16 -0
- package/extensions/subagent/agents.ts +9 -4
- package/extensions/subagent/index.ts +15 -2
- package/package.json +1 -1
- package/extensions/subagent/agents/planner.md +0 -37
- package/extensions/subagent/agents/reviewer.md +0 -35
- package/extensions/subagent/agents/scout.md +0 -50
- package/extensions/subagent/agents/worker.md +0 -24
- package/extensions/subagent/prompts/implement-and-review.md +0 -10
- package/extensions/subagent/prompts/implement.md +0 -10
- package/extensions/subagent/prompts/scout-and-plan.md +0 -9
package/README.md
CHANGED
|
@@ -39,18 +39,18 @@ One `pi install` and everything below loads on the next start. `pi list` shows w
|
|
|
39
39
|
| Custom slash commands | `.claude/commands/*.md` → pi prompt templates | `commands.ts` |
|
|
40
40
|
| Skills | `.claude/skills` → pi skill discovery (pi reads `name`, `description`, `disable-model-invocation`; `allowed-tools` is inert in pi's loader) | `skills.ts` |
|
|
41
41
|
| Hooks | `.claude/settings.json` hooks: PreToolUse (blocks, rewrites input via `updatedInput`), PostToolUse (feedback and `additionalContext` land next to the tool result), PostToolUseFailure, SessionStart (context injection), UserPromptSubmit (blocks and injects context), Stop (a block continues the conversation), SubagentStart/SubagentStop, PreCompact, PostCompact, SessionEnd; Claude matcher semantics incl. `mcp__server__tool` names; payloads carry session_id, transcript_path, cwd, permission_mode, effort | `hooks.ts` |
|
|
42
|
-
| Output styles | `.claude/output-styles` + active `outputStyle
|
|
42
|
+
| Output styles | `.claude/output-styles` + active `outputStyle`; Claude replace semantics with `keep-coding-instructions`; bundled Explanatory/Learning/Proactive; `/output-style [name]` | `output-styles.ts` |
|
|
43
43
|
| CLAUDE.md `@imports` | resolves `@path` imports pi's native loader skips; loads `CLAUDE.local.md` (approval-gated) | `context-imports.ts` |
|
|
44
|
-
| MCP servers | user `~/.claude.json` (incl. per-project `projects[cwd]` local scope), `~/.pi/agent/mcp.json`; project `.mcp.json`, `.pi/mcp.json` (once approved); stdio/HTTP/SSE by `type`; `${VAR:-default}` expansion; `MCP_TIMEOUT`/`MCP_TOOL_TIMEOUT` | `mcp.ts` |
|
|
44
|
+
| MCP servers | user `~/.claude.json` (incl. per-project `projects[cwd]` local scope), `~/.pi/agent/mcp.json`; project `.mcp.json`, `.pi/mcp.json` (once approved; `enabledMcpjsonServers`/`disabledMcpjsonServers`/`enableAllProjectMcpServers` honored, consent keys only from non-repo settings); stdio/HTTP/SSE by `type`; `${VAR:-default}` expansion; `MCP_TIMEOUT`/`MCP_TOOL_TIMEOUT` | `mcp.ts` |
|
|
45
45
|
| Project trust | prompts before loading project config (MCP servers, hooks, agents, rules, output styles) that pi would otherwise trust silently | `internal/project-approval.ts` |
|
|
46
|
-
| Subagents / Task | `~/.claude/agents` and `~/.pi/agent/agents`, plus project `.claude/agents` and `.pi/agents`; background runs | `subagent/` |
|
|
46
|
+
| Subagents / Task | builtin Explore/Plan/general-purpose agents, `~/.claude/agents` and `~/.pi/agent/agents`, plus project `.claude/agents` and `.pi/agents`; agent roster with descriptions in the system prompt; background runs | `subagent/` |
|
|
47
47
|
| Plan mode | `plan_mode_complete` tool, exact tool snapshot/restore | `plan-mode/` |
|
|
48
48
|
| Todo list | persistent overlay, status machine, compaction-safe | `todo.ts` |
|
|
49
49
|
| Checkpoints / rewind | shadow-repo snapshots; restore overwrites checkpointed files, keeps files created later | `git-checkpoint.ts` |
|
|
50
50
|
| Persistent memory | per-project memories, index injected each session | `memory.ts` |
|
|
51
51
|
| WebSearch / WebFetch | key-free DuckDuckGo search, SSRF-guarded fetch | `web.ts` |
|
|
52
52
|
| AskUserQuestion | one question with `header`, single- or `multiSelect` options, plus free-text; no multi-question batching | `question.ts` |
|
|
53
|
-
| Statusline | turn state + session cost | `status-line.ts` |
|
|
53
|
+
| Statusline | Claude `statusLine` command contract (stdin JSON, `padding`, `refreshInterval`); built-in turn state + session cost fallback | `status-line.ts` |
|
|
54
54
|
| Notifications | vendored example | `notify.ts` |
|
|
55
55
|
|
|
56
56
|
`CLAUDE.md` itself needs no extension: pi loads `CLAUDE.md` / `AGENTS.md` context files natively (global + walking cwd to root). `context-imports.ts` only adds the `@import` resolution pi's loader lacks, appending the imported files without re-injecting the base.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Explanatory
|
|
3
|
+
description: Educational insights while completing tasks
|
|
4
|
+
keep-coding-instructions: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Between completing software engineering tasks, provide brief educational "Insights" that help the user understand implementation choices and codebase patterns.
|
|
8
|
+
|
|
9
|
+
Mark each one clearly:
|
|
10
|
+
|
|
11
|
+
`✻ Insight ─────`
|
|
12
|
+
(2-3 sentences on why this approach, what pattern it follows, or what tradeoff it makes)
|
|
13
|
+
`─────`
|
|
14
|
+
|
|
15
|
+
Share an insight when there is a genuine decision or pattern worth understanding, not after every step. Keep the work itself unchanged: insights are commentary, never a substitute for doing the task.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Learning
|
|
3
|
+
description: Collaborative learn-by-doing with small human-written pieces
|
|
4
|
+
keep-coding-instructions: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Work collaboratively, learn-by-doing style: share brief "Insights" explaining implementation choices as you work, and regularly ask the user to contribute small, strategic pieces of code themselves.
|
|
8
|
+
|
|
9
|
+
When a piece is well-scoped for the user to write (a condition, a small function body, a test assertion), leave a marker instead of writing it:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
// TODO(human): <one sentence describing exactly what to implement here>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Then stop and ask the user to fill it in, explaining what the piece needs to do and why it matters. Choose pieces that teach something about the codebase or the problem, not busywork. Keep your own contributions moving the task forward between their turns.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Proactive
|
|
3
|
+
description: Execute immediately, prefer action over planning
|
|
4
|
+
keep-coding-instructions: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Execute immediately. Make reasonable assumptions instead of pausing for routine decisions, and prefer action over planning: when a step is reversible and follows from the task, do it rather than proposing it.
|
|
8
|
+
|
|
9
|
+
Ask only when a decision is genuinely the user's to make (destructive actions, real scope changes). Report what you did and why afterward, concisely.
|
|
@@ -69,6 +69,16 @@ const APPROVAL_BODY = 'It ships Claude Code configuration that pi-code loads. MC
|
|
|
69
69
|
* `defaultProjectTrust` at all, so there is no user preference to fall back on. A run
|
|
70
70
|
* that cannot ask has not been approved.
|
|
71
71
|
*/
|
|
72
|
+
/** The same decision as isProjectApproved, but never prompts: an undecided project
|
|
73
|
+
* reads as unapproved. For surfaces that only display project config, like the
|
|
74
|
+
* subagent roster, where a mid-turn dialog would be wrong. */
|
|
75
|
+
export function isProjectApprovedSilently(ctx: Pick<ApprovalContext, 'cwd' | 'isProjectTrusted'>, deps: ApprovalDeps = defaultDeps): boolean {
|
|
76
|
+
if (ctx.isProjectTrusted?.() !== true) return false
|
|
77
|
+
if (!deps.hasClaudeShaped(ctx.cwd)) return true
|
|
78
|
+
if (deps.piWouldAsk(ctx.cwd)) return true
|
|
79
|
+
return deps.savedDecision(ctx.cwd) === true
|
|
80
|
+
}
|
|
81
|
+
|
|
72
82
|
export async function isProjectApproved(ctx: ApprovalContext, deps: ApprovalDeps = defaultDeps): Promise<boolean> {
|
|
73
83
|
if (ctx.isProjectTrusted?.() !== true) return false // pi already declined, or never trusted
|
|
74
84
|
if (!deps.hasClaudeShaped(ctx.cwd)) return true // nothing here pi's own check would miss
|
package/extensions/mcp.ts
CHANGED
|
@@ -92,6 +92,37 @@ export function projectConfigPaths(cwd: string): string[] {
|
|
|
92
92
|
return [path.join(cwd, '.mcp.json'), path.join(cwd, '.pi', 'mcp.json')]
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
export interface ProjectServerPolicy {
|
|
96
|
+
disabled: Set<string>
|
|
97
|
+
consented: Set<string>
|
|
98
|
+
consentAll: boolean
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Claude's per-server approvals for project .mcp.json servers. Consent-granting keys
|
|
102
|
+
* (enabledMcpjsonServers, enableAllProjectMcpServers) count only from files the repo
|
|
103
|
+
* does not control (user settings and settings.local.json), so a checked-in
|
|
104
|
+
* settings.json cannot approve its own servers. disabledMcpjsonServers counts from
|
|
105
|
+
* every file and wins over consent. Lists union across files: for denies the union is
|
|
106
|
+
* the restrictive reading, and consent is the union of the user's own two files. */
|
|
107
|
+
export function projectServerPolicy(cwd: string, home: string): ProjectServerPolicy {
|
|
108
|
+
const read = (file: string): Record<string, unknown> => {
|
|
109
|
+
try {
|
|
110
|
+
return JSON.parse(fs.readFileSync(file, 'utf-8'))
|
|
111
|
+
} catch {
|
|
112
|
+
return {}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const names = (value: unknown): string[] => (Array.isArray(value) ? value.filter((entry): entry is string => typeof entry === 'string') : [])
|
|
116
|
+
const userSettings = read(path.join(home, '.claude', 'settings.json'))
|
|
117
|
+
const projectSettings = read(path.join(cwd, '.claude', 'settings.json'))
|
|
118
|
+
const localSettings = read(path.join(cwd, '.claude', 'settings.local.json'))
|
|
119
|
+
const disabled = new Set([...names(userSettings.disabledMcpjsonServers), ...names(projectSettings.disabledMcpjsonServers), ...names(localSettings.disabledMcpjsonServers)])
|
|
120
|
+
const consentSources = [userSettings, localSettings]
|
|
121
|
+
const consented = new Set(consentSources.flatMap((settings) => names(settings.enabledMcpjsonServers)))
|
|
122
|
+
const consentAll = consentSources.some((settings) => settings.enableAllProjectMcpServers === true)
|
|
123
|
+
return { disabled, consented, consentAll }
|
|
124
|
+
}
|
|
125
|
+
|
|
95
126
|
export function loadConfigFrom(files: string[]): Record<string, ServerConfig> {
|
|
96
127
|
const servers: Record<string, ServerConfig> = {}
|
|
97
128
|
for (const file of files) {
|
|
@@ -325,11 +356,27 @@ export default async function mcpExtension(pi: ExtensionAPI) {
|
|
|
325
356
|
userConnected = true
|
|
326
357
|
await connectServers(loadUserScope(os.homedir(), ctx.cwd))
|
|
327
358
|
}
|
|
328
|
-
// A project .mcp.json can run arbitrary commands on connect, so only honor it once
|
|
329
|
-
//
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
359
|
+
// A project .mcp.json can run arbitrary commands on connect, so only honor it once
|
|
360
|
+
// the project is trusted. Per-server settings refine that: disabled servers never
|
|
361
|
+
// connect, servers the user consented to individually connect without the
|
|
362
|
+
// whole-project confirm, and the rest stay behind it. Reconnect attempts after a
|
|
363
|
+
// refusal are safe: connectServers skips names that already connected.
|
|
364
|
+
if (!projectConnected) {
|
|
365
|
+
const policy = projectServerPolicy(ctx.cwd, os.homedir())
|
|
366
|
+
const candidates = loadConfigFrom(projectConfigPaths(ctx.cwd))
|
|
367
|
+
const consented: Record<string, ServerConfig> = {}
|
|
368
|
+
const gated: Record<string, ServerConfig> = {}
|
|
369
|
+
for (const [name, config] of Object.entries(candidates)) {
|
|
370
|
+
if (policy.disabled.has(name)) continue
|
|
371
|
+
if (policy.consentAll || policy.consented.has(name)) consented[name] = config
|
|
372
|
+
else gated[name] = config
|
|
373
|
+
}
|
|
374
|
+
if (Object.keys(consented).length > 0) await connectServers(consented)
|
|
375
|
+
if (Object.keys(gated).length === 0) projectConnected = true
|
|
376
|
+
else if (await isProjectApproved(ctx)) {
|
|
377
|
+
projectConnected = true
|
|
378
|
+
await connectServers(gated)
|
|
379
|
+
}
|
|
333
380
|
}
|
|
334
381
|
|
|
335
382
|
pi.events.emit(MCP_TOOLS_CHANNEL, [...aliases])
|
|
@@ -8,8 +8,11 @@
|
|
|
8
8
|
* tone and role. `/output-style` lists the styles and persists a choice to the
|
|
9
9
|
* project's settings.local.json.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* Claude semantics: a style replaces the built-in coding instructions unless its
|
|
12
|
+
* frontmatter sets `keep-coding-instructions: true`. The replacement excises pi's
|
|
13
|
+
* default coding prose up to a stable marker line and keeps everything after it
|
|
14
|
+
* (append text, project context, skills, other extensions' additions); when the
|
|
15
|
+
* marker is absent (custom SYSTEM.md), the style falls back to appending.
|
|
13
16
|
*
|
|
14
17
|
* Docs: https://code.claude.com/docs/en/output-styles.md
|
|
15
18
|
*/
|
|
@@ -25,6 +28,7 @@ export interface OutputStyle {
|
|
|
25
28
|
name: string
|
|
26
29
|
description: string
|
|
27
30
|
body: string
|
|
31
|
+
keepCodingInstructions: boolean
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
function field(frontmatter: string, key: string): string {
|
|
@@ -37,7 +41,27 @@ export function parseStyle(content: string, fallbackName: string): OutputStyle {
|
|
|
37
41
|
const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content)
|
|
38
42
|
const frontmatter = match ? match[1] : ''
|
|
39
43
|
const body = match ? content.slice(match[0].length) : content
|
|
40
|
-
return { name: field(frontmatter, 'name') || fallbackName, description: field(frontmatter, 'description'), body: body.trim() }
|
|
44
|
+
return { name: field(frontmatter, 'name') || fallbackName, description: field(frontmatter, 'description'), body: body.trim(), keepCodingInstructions: field(frontmatter, 'keep-coding-instructions') === 'true' }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Equivalents of Claude's built-in styles, shipped with pi-code as the
|
|
48
|
+
* lowest-precedence source: a user or project style of the same name wins. */
|
|
49
|
+
export const BUILTIN_STYLES_DIR = path.join(import.meta.dirname, 'internal', 'builtin-styles')
|
|
50
|
+
|
|
51
|
+
/** The last line of pi's default coding instructions. Everything after it (append
|
|
52
|
+
* text, project context, skills, cwd, other extensions' additions) survives a style
|
|
53
|
+
* replacement. Tracks pi's dist/core/system-prompt.js; a canary test pins it. */
|
|
54
|
+
export const CODING_BASE_MARKER = '- Always read pi .md files completely and follow links to related docs (e.g., tui.md for TUI API details)'
|
|
55
|
+
|
|
56
|
+
/** Apply a style per Claude semantics: replace the coding instructions unless the
|
|
57
|
+
* style keeps them; fall back to appending when the marker is absent. */
|
|
58
|
+
export function applyStyle(systemPrompt: string, style: OutputStyle): string {
|
|
59
|
+
const styleSection = `## Output Style: ${style.name}\n\n${style.body}`
|
|
60
|
+
if (!style.keepCodingInstructions) {
|
|
61
|
+
const idx = systemPrompt.indexOf(CODING_BASE_MARKER)
|
|
62
|
+
if (idx !== -1) return `${styleSection}${systemPrompt.slice(idx + CODING_BASE_MARKER.length)}`
|
|
63
|
+
}
|
|
64
|
+
return `${systemPrompt}\n\n${styleSection}`
|
|
41
65
|
}
|
|
42
66
|
|
|
43
67
|
function isDirectory(target: string): boolean {
|
|
@@ -132,7 +156,7 @@ export default function outputStylesExtension(pi: ExtensionAPI) {
|
|
|
132
156
|
// project styles / selection once the project is approved. isProjectTrusted alone
|
|
133
157
|
// is true for a repo pi never asked about; see project-approval.
|
|
134
158
|
const trusted = await isProjectApproved(ctx)
|
|
135
|
-
styles = loadStyles(styleDirs(ctx.cwd, home, trusted))
|
|
159
|
+
styles = loadStyles([BUILTIN_STYLES_DIR, ...styleDirs(ctx.cwd, home, trusted)])
|
|
136
160
|
localSettingsPath = path.join(ctx.cwd, '.claude', 'settings.local.json')
|
|
137
161
|
activeName = readActiveStyleName(settingsFiles(ctx.cwd, home, trusted))
|
|
138
162
|
const active = styleForName(styles, activeName)
|
|
@@ -142,12 +166,24 @@ export default function outputStylesExtension(pi: ExtensionAPI) {
|
|
|
142
166
|
pi.on('before_agent_start', async (event) => {
|
|
143
167
|
const active = styleForName(styles, activeName)
|
|
144
168
|
if (!active || active.body.length === 0) return
|
|
145
|
-
return { systemPrompt:
|
|
169
|
+
return { systemPrompt: applyStyle(event.systemPrompt, active) }
|
|
146
170
|
})
|
|
147
171
|
|
|
148
172
|
pi.registerCommand('output-style', {
|
|
149
|
-
description: 'Choose the active Claude output style',
|
|
150
|
-
handler: async (
|
|
173
|
+
description: 'Choose the active Claude output style (or /output-style <name>)',
|
|
174
|
+
handler: async (args, ctx) => {
|
|
175
|
+
const requested = args.trim()
|
|
176
|
+
if (requested) {
|
|
177
|
+
const picked = styles.find((style) => style.name.toLowerCase() === requested.toLowerCase())
|
|
178
|
+
if (!picked) {
|
|
179
|
+
ctx.ui.notify(`Unknown output style: ${requested}. Available: ${styles.map((style) => style.name).join(', ')}`, 'error')
|
|
180
|
+
return
|
|
181
|
+
}
|
|
182
|
+
activeName = picked.name
|
|
183
|
+
persistActiveStyle(localSettingsPath, picked.name)
|
|
184
|
+
ctx.ui.notify(`Output style set to ${picked.name} (applies next turn)`, 'info')
|
|
185
|
+
return
|
|
186
|
+
}
|
|
151
187
|
if (!ctx.hasUI) {
|
|
152
188
|
ctx.ui.notify('/output-style requires interactive mode', 'error')
|
|
153
189
|
return
|
|
@@ -1,16 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Status Line Extension
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Honors Claude Code's `statusLine` settings contract: a configured command runs
|
|
5
|
+
* with the session JSON on stdin (model, workspace, cost, context_window, effort,
|
|
6
|
+
* output_style, session ids) and its first stdout line becomes the footer segment,
|
|
7
|
+
* padded per `padding`. It re-runs, debounced 300ms as Claude does, at session
|
|
8
|
+
* start, after turns, after compaction, on plan-mode changes (the permission-mode
|
|
9
|
+
* analogue, off the shared bus), and on the optional `refreshInterval` timer
|
|
10
|
+
* (minimum 1s). A project-defined command is arbitrary shell, so project settings
|
|
11
|
+
* count only once the project is already approved, read without prompting.
|
|
7
12
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
13
|
+
* Without a configured statusLine, the built-in segment shows turn state plus
|
|
14
|
+
* running session cost, summed from per-message usage on the current branch so it
|
|
15
|
+
* stays correct across /tree navigation and forks. The built-in segment is also
|
|
16
|
+
* the fallback while a configured command produces no output. Multi-line output
|
|
17
|
+
* is truncated to its first line: the segment is one footer row in pi.
|
|
18
|
+
*
|
|
19
|
+
* Docs: https://code.claude.com/docs/en/statusline.md
|
|
10
20
|
*/
|
|
11
21
|
|
|
22
|
+
import * as fs from 'node:fs'
|
|
23
|
+
import * as os from 'node:os'
|
|
12
24
|
import type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent'
|
|
13
25
|
|
|
26
|
+
import { hookFiles, runHookCommand } from './hooks.js'
|
|
27
|
+
import { isPlanModeState, PLAN_MODE_CHANNEL } from './internal/plan-mode-state.js'
|
|
28
|
+
import { isProjectApprovedSilently } from './internal/project-approval.js'
|
|
29
|
+
import { readActiveStyleName, settingsFiles } from './output-styles.js'
|
|
30
|
+
|
|
31
|
+
const COMMAND_TIMEOUT_MS = 5_000
|
|
32
|
+
const DEBOUNCE_MS = 300
|
|
33
|
+
|
|
14
34
|
interface UsageEntry {
|
|
15
35
|
type: string
|
|
16
36
|
message?: { usage?: { cost?: { total?: number } } }
|
|
@@ -28,34 +48,156 @@ function formatCost(cost: number): string {
|
|
|
28
48
|
return cost >= 0.01 ? `$${cost.toFixed(2)}` : `$${cost.toFixed(4)}`
|
|
29
49
|
}
|
|
30
50
|
|
|
51
|
+
export interface StatusLineConfig {
|
|
52
|
+
command: string
|
|
53
|
+
padding: number
|
|
54
|
+
refreshInterval: number | undefined
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** The `statusLine` recorded in settings, last file winning. Claude's shape is
|
|
58
|
+
* `{type: "command", command, padding?, refreshInterval?}`; entries without a
|
|
59
|
+
* command string are ignored, and refreshInterval has a documented minimum of 1. */
|
|
60
|
+
export function readStatusLineConfig(files: string[]): StatusLineConfig | undefined {
|
|
61
|
+
let found: StatusLineConfig | undefined
|
|
62
|
+
for (const file of files) {
|
|
63
|
+
try {
|
|
64
|
+
const settings = JSON.parse(fs.readFileSync(file, 'utf-8'))
|
|
65
|
+
const entry = settings.statusLine
|
|
66
|
+
if (!entry || typeof entry.command !== 'string') continue
|
|
67
|
+
if (entry.type !== undefined && entry.type !== 'command') continue
|
|
68
|
+
found = {
|
|
69
|
+
command: entry.command,
|
|
70
|
+
padding: typeof entry.padding === 'number' && entry.padding > 0 ? entry.padding : 0,
|
|
71
|
+
refreshInterval: typeof entry.refreshInterval === 'number' && entry.refreshInterval >= 1 ? entry.refreshInterval : undefined,
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
// missing or invalid file: skip
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return found
|
|
78
|
+
}
|
|
79
|
+
|
|
31
80
|
export default function statusLine(pi: ExtensionAPI) {
|
|
32
81
|
let turnCount = 0
|
|
82
|
+
let config: StatusLineConfig | undefined
|
|
83
|
+
let sessionCtx: ExtensionContext | undefined
|
|
84
|
+
let commandLine: string | undefined
|
|
85
|
+
let permissionMode = 'default'
|
|
86
|
+
let refreshTimer: ReturnType<typeof setInterval> | undefined
|
|
87
|
+
let debounceTimer: ReturnType<typeof setTimeout> | undefined
|
|
88
|
+
let running = false
|
|
89
|
+
let rerunQueued = false
|
|
33
90
|
|
|
34
|
-
function
|
|
91
|
+
function segmentText(ctx: ExtensionContext, symbol: string): string {
|
|
35
92
|
const theme = ctx.ui.theme
|
|
36
93
|
const cost = sessionCost(ctx)
|
|
37
94
|
const costText = cost > 0 ? theme.fg('muted', ` ${formatCost(cost)}`) : ''
|
|
38
95
|
const turnText = turnCount > 0 ? theme.fg('dim', ` turn ${turnCount}`) : theme.fg('dim', ' ready')
|
|
39
|
-
|
|
96
|
+
return symbol + turnText + costText
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function show(ctx: ExtensionContext, builtIn: string): void {
|
|
100
|
+
ctx.ui.setStatus('pi-code-status', commandLine ?? builtIn)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** The stdin payload per Claude's documented statusline contract. */
|
|
104
|
+
function buildPayload(ctx: ExtensionContext): Record<string, unknown> {
|
|
105
|
+
const usage = ctx.getContextUsage() ?? { tokens: null, contextWindow: 0, percent: null }
|
|
106
|
+
const styleName = readActiveStyleName(settingsFiles(ctx.cwd, os.homedir(), true))
|
|
107
|
+
const payload: Record<string, unknown> = {
|
|
108
|
+
session_id: ctx.sessionManager.getSessionId(),
|
|
109
|
+
cwd: ctx.cwd,
|
|
110
|
+
workspace: { current_dir: ctx.cwd, project_dir: ctx.cwd },
|
|
111
|
+
model: { id: (ctx.model as { id?: string } | undefined)?.id ?? '' },
|
|
112
|
+
cost: { total_cost_usd: sessionCost(ctx) },
|
|
113
|
+
context_window: { context_window_size: usage.contextWindow, used_percentage: usage.percent, total_input_tokens: usage.tokens },
|
|
114
|
+
permission_mode: permissionMode,
|
|
115
|
+
}
|
|
116
|
+
const transcript = ctx.sessionManager.getSessionFile()
|
|
117
|
+
if (transcript) payload.transcript_path = transcript
|
|
118
|
+
if (ctx.thinkingLevel) payload.effort = { level: ctx.thinkingLevel }
|
|
119
|
+
if (styleName) payload.output_style = { name: styleName }
|
|
120
|
+
return payload
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function runCommand(ctx: ExtensionContext): Promise<void> {
|
|
124
|
+
if (!config) return
|
|
125
|
+
if (running) {
|
|
126
|
+
rerunQueued = true
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
running = true
|
|
130
|
+
try {
|
|
131
|
+
const result = await runHookCommand(config.command, buildPayload(ctx), COMMAND_TIMEOUT_MS)
|
|
132
|
+
const first = result.stdout.split('\n')[0].trimEnd()
|
|
133
|
+
const pad = ' '.repeat(config.padding)
|
|
134
|
+
commandLine = first ? `${pad}${first}${pad}` : undefined
|
|
135
|
+
show(ctx, segmentText(ctx, ctx.ui.theme.fg('dim', '○')))
|
|
136
|
+
} finally {
|
|
137
|
+
running = false
|
|
138
|
+
if (rerunQueued) {
|
|
139
|
+
rerunQueued = false
|
|
140
|
+
void runCommand(ctx)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
40
143
|
}
|
|
41
144
|
|
|
145
|
+
/** Claude debounces statusline updates at 300ms so rapid triggers batch. */
|
|
146
|
+
function scheduleRefresh(): void {
|
|
147
|
+
if (!config || !sessionCtx) return
|
|
148
|
+
const ctx = sessionCtx
|
|
149
|
+
clearTimeout(debounceTimer)
|
|
150
|
+
debounceTimer = setTimeout(() => {
|
|
151
|
+
void runCommand(ctx)
|
|
152
|
+
}, DEBOUNCE_MS)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
pi.events.on(PLAN_MODE_CHANNEL, (data) => {
|
|
156
|
+
if (!isPlanModeState(data)) return
|
|
157
|
+
permissionMode = data.active ? 'plan' : 'default'
|
|
158
|
+
scheduleRefresh()
|
|
159
|
+
})
|
|
160
|
+
|
|
42
161
|
pi.on('session_start', async (_event, ctx) => {
|
|
43
|
-
// One instance serves every session, so a fresh session must not inherit
|
|
162
|
+
// One instance serves every session, so a fresh session must not inherit state.
|
|
44
163
|
turnCount = 0
|
|
45
|
-
|
|
164
|
+
commandLine = undefined
|
|
165
|
+
sessionCtx = ctx
|
|
166
|
+
clearInterval(refreshTimer)
|
|
167
|
+
// Reading config must never open a trust dialog: several extensions resolve
|
|
168
|
+
// approval at session start, and a second prompt stacks over the first and eats
|
|
169
|
+
// the keys meant for it. An undecided project simply skips project settings.
|
|
170
|
+
const trusted = isProjectApprovedSilently(ctx)
|
|
171
|
+
config = readStatusLineConfig(hookFiles(ctx.cwd, os.homedir(), trusted))
|
|
172
|
+
if (config?.refreshInterval) {
|
|
173
|
+
refreshTimer = setInterval(() => scheduleRefresh(), config.refreshInterval * 1000)
|
|
174
|
+
}
|
|
175
|
+
show(ctx, segmentText(ctx, ctx.ui.theme.fg('dim', '○')))
|
|
176
|
+
scheduleRefresh()
|
|
46
177
|
})
|
|
47
178
|
|
|
48
179
|
pi.on('turn_start', async (_event, ctx) => {
|
|
49
180
|
turnCount++
|
|
50
181
|
const theme = ctx.ui.theme
|
|
51
|
-
ctx
|
|
182
|
+
show(ctx, theme.fg('accent', '●') + theme.fg('dim', ` turn ${turnCount}...`))
|
|
52
183
|
})
|
|
53
184
|
|
|
54
185
|
pi.on('turn_end', async (_event, ctx) => {
|
|
55
|
-
|
|
186
|
+
show(ctx, segmentText(ctx, ctx.ui.theme.fg('success', '✓')))
|
|
187
|
+
scheduleRefresh()
|
|
56
188
|
})
|
|
57
189
|
|
|
58
190
|
pi.on('agent_end', async (_event, ctx) => {
|
|
59
|
-
|
|
191
|
+
show(ctx, segmentText(ctx, ctx.ui.theme.fg('success', '✓')))
|
|
192
|
+
scheduleRefresh()
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
pi.on('session_compact', async (_event, _ctx) => {
|
|
196
|
+
scheduleRefresh()
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
pi.on('session_shutdown', async () => {
|
|
200
|
+
clearInterval(refreshTimer)
|
|
201
|
+
clearTimeout(debounceTimer)
|
|
60
202
|
})
|
|
61
203
|
}
|
|
@@ -21,15 +21,10 @@ subagent/
|
|
|
21
21
|
├── index.ts # The extension (entry point)
|
|
22
22
|
├── agents.ts # Agent discovery logic
|
|
23
23
|
├── background.ts # Background run registry and spawning
|
|
24
|
-
├── agents/ #
|
|
25
|
-
│ ├──
|
|
26
|
-
│ ├──
|
|
27
|
-
│
|
|
28
|
-
│ └── worker.md # General-purpose (full capabilities)
|
|
29
|
-
└── prompts/ # Workflow presets (prompt templates)
|
|
30
|
-
├── implement.md # scout -> planner -> worker
|
|
31
|
-
├── scout-and-plan.md # scout -> planner (no implementation)
|
|
32
|
-
└── implement-and-review.md # worker -> reviewer -> worker
|
|
24
|
+
├── agents/ # Bundled builtin agents, always available (lowest precedence)
|
|
25
|
+
│ ├── explore.md # Explore: fast read-only codebase exploration
|
|
26
|
+
│ ├── plan.md # Plan: read-only implementation planning
|
|
27
|
+
│ └── general-purpose.md # general-purpose: full capabilities
|
|
33
28
|
```
|
|
34
29
|
|
|
35
30
|
## Installation
|
|
@@ -42,7 +37,7 @@ This tool executes a separate `pi` subprocess with a delegated system prompt and
|
|
|
42
37
|
|
|
43
38
|
**Project-local agents** (`.pi/agents/*.md`) are repo-controlled prompts that can instruct the model to read files, run bash commands, etc.
|
|
44
39
|
|
|
45
|
-
**Default behavior:**
|
|
40
|
+
**Default behavior:** Loads the bundled builtin agents (Explore, Plan, general-purpose) plus **user-level agents** from `~/.claude/agents` and `~/.pi/agent/agents`. A user or project agent with the same name overrides a builtin. Discovered agents and their descriptions are listed in the system prompt each turn, so the model can pick one itself; project agent descriptions appear only once the project is approved.
|
|
46
41
|
|
|
47
42
|
To enable project-local agents (`.claude/agents`, `.pi/agents`), pass `agentScope: "both"` (or `"project"`). Only do this for repositories you trust.
|
|
48
43
|
|
|
@@ -52,24 +47,17 @@ When running interactively, the tool prompts for confirmation before running pro
|
|
|
52
47
|
|
|
53
48
|
### Single agent
|
|
54
49
|
```
|
|
55
|
-
Use
|
|
50
|
+
Use Explore to find all authentication code
|
|
56
51
|
```
|
|
57
52
|
|
|
58
53
|
### Parallel execution
|
|
59
54
|
```
|
|
60
|
-
Run 2
|
|
55
|
+
Run 2 Explore agents in parallel: one to find models, one to find providers
|
|
61
56
|
```
|
|
62
57
|
|
|
63
58
|
### Chained workflow
|
|
64
59
|
```
|
|
65
|
-
Use a chain: first have
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### Workflow prompts
|
|
69
|
-
```
|
|
70
|
-
/implement add Redis caching to the session store
|
|
71
|
-
/scout-and-plan refactor auth to support OAuth
|
|
72
|
-
/implement-and-review add input validation to API endpoints
|
|
60
|
+
Use a chain: first have Explore find the read tool, then have Plan suggest improvements
|
|
73
61
|
```
|
|
74
62
|
|
|
75
63
|
## Tool Modes
|
|
@@ -135,22 +123,15 @@ model. Fields with no pi equivalent are ignored: `skills`, `memory`,
|
|
|
135
123
|
|
|
136
124
|
Project agents override user agents with the same name when `agentScope: "both"`.
|
|
137
125
|
|
|
138
|
-
##
|
|
139
|
-
|
|
140
|
-
| Agent | Purpose | Model | Tools |
|
|
141
|
-
|-------|---------|-------|-------|
|
|
142
|
-
| `scout` | Fast codebase recon | Haiku | read, grep, find, ls, bash |
|
|
143
|
-
| `planner` | Implementation plans | Sonnet | read, grep, find, ls |
|
|
144
|
-
| `reviewer` | Code review | Sonnet | read, grep, find, ls, bash |
|
|
145
|
-
| `worker` | General-purpose | Sonnet | (all default) |
|
|
126
|
+
## Builtin Agents
|
|
146
127
|
|
|
147
|
-
|
|
128
|
+
| Agent | Purpose | Tools |
|
|
129
|
+
|-------|---------|-------|
|
|
130
|
+
| `Explore` | Fast read-only codebase exploration | read, grep, find, ls |
|
|
131
|
+
| `Plan` | Read-only implementation planning | read, grep, find, ls |
|
|
132
|
+
| `general-purpose` | Full capabilities, isolated context | (all default) |
|
|
148
133
|
|
|
149
|
-
|
|
150
|
-
|--------|------|
|
|
151
|
-
| `/implement <query>` | scout → planner → worker |
|
|
152
|
-
| `/scout-and-plan <query>` | scout → planner |
|
|
153
|
-
| `/implement-and-review <query>` | worker → reviewer → worker |
|
|
134
|
+
No model is pinned: each runs on the session's default model.
|
|
154
135
|
|
|
155
136
|
## Error Handling
|
|
156
137
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Explore
|
|
3
|
+
description: Fast read-only codebase exploration that returns compressed findings
|
|
4
|
+
tools: read, grep, find, ls
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are an exploration agent. Quickly investigate the codebase and return structured findings that another agent can use without re-reading everything.
|
|
8
|
+
|
|
9
|
+
You must NOT make any changes: only read, search, and summarize.
|
|
10
|
+
|
|
11
|
+
Your output goes to an agent who has NOT seen the files you explored. Report:
|
|
12
|
+
|
|
13
|
+
1. Relevant files with paths and one-line roles
|
|
14
|
+
2. Key functions/types with `file:line` references
|
|
15
|
+
3. How the pieces connect (data flow, call flow)
|
|
16
|
+
4. Anything surprising or risky
|
|
17
|
+
|
|
18
|
+
Be selective: compressed, load-bearing findings beat exhaustive dumps.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: general-purpose
|
|
3
|
+
description: General-purpose agent with full capabilities in an isolated context
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are a general-purpose agent with full capabilities, operating in an isolated context window to handle delegated tasks without polluting the main conversation.
|
|
7
|
+
|
|
8
|
+
Work autonomously to complete the assigned task, using the available tools as needed.
|
|
9
|
+
|
|
10
|
+
When finished, report: what was done, what was verified (commands run, tests passed), and anything the delegator must know (caveats, follow-ups, files changed).
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Plan
|
|
3
|
+
description: Designs implementation plans from context and requirements, read-only
|
|
4
|
+
tools: read, grep, find, ls
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are a planning specialist. You receive context and requirements, then produce a clear implementation plan.
|
|
8
|
+
|
|
9
|
+
You must NOT make any changes: only read, analyze, and plan.
|
|
10
|
+
|
|
11
|
+
Deliver:
|
|
12
|
+
|
|
13
|
+
1. Step-by-step plan, each step small and independently verifiable
|
|
14
|
+
2. Files to touch per step, with `file:line` anchors where known
|
|
15
|
+
3. Risks and open questions, each with a suggested resolution
|
|
16
|
+
4. What to test and how the tests would fail without the change
|
|
@@ -68,7 +68,7 @@ function parseEffortField(raw: unknown): string | undefined {
|
|
|
68
68
|
const READ_ONLY_TOOLS = ['read', 'grep', 'find', 'ls']
|
|
69
69
|
|
|
70
70
|
/** Parse one agent markdown file; null when it is not a usable agent definition. */
|
|
71
|
-
function parseAgentFile(content: string, source:
|
|
71
|
+
function parseAgentFile(content: string, source: AgentSource, filePath: string): AgentConfig | null {
|
|
72
72
|
let parsed: { frontmatter: Record<string, unknown>; body: string }
|
|
73
73
|
try {
|
|
74
74
|
parsed = parseFrontmatter<Record<string, unknown>>(content)
|
|
@@ -106,7 +106,7 @@ export interface AgentConfig {
|
|
|
106
106
|
model?: string
|
|
107
107
|
effort?: string
|
|
108
108
|
systemPrompt: string
|
|
109
|
-
source:
|
|
109
|
+
source: AgentSource
|
|
110
110
|
filePath: string
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -115,7 +115,7 @@ export interface AgentDiscoveryResult {
|
|
|
115
115
|
projectAgentsDir: string | null
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
function loadAgentsFromDir(dir: string, source:
|
|
118
|
+
function loadAgentsFromDir(dir: string, source: AgentSource): AgentConfig[] {
|
|
119
119
|
const agents: AgentConfig[] = []
|
|
120
120
|
|
|
121
121
|
if (!fs.existsSync(dir)) {
|
|
@@ -202,6 +202,11 @@ function buildAgentMap(userAgents: AgentConfig[], projectAgents: AgentConfig[],
|
|
|
202
202
|
return agentMap
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
export type AgentSource = 'user' | 'project' | 'builtin'
|
|
206
|
+
|
|
207
|
+
/** Bundled default agents (Explore, Plan, general-purpose), lowest precedence. */
|
|
208
|
+
export const BUILTIN_AGENTS_DIR = path.join(import.meta.dirname, 'agents')
|
|
209
|
+
|
|
205
210
|
export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryResult {
|
|
206
211
|
const userDir = path.join(getAgentDir(), 'agents')
|
|
207
212
|
const claudeUserDir = path.join(os.homedir(), '.claude', 'agents')
|
|
@@ -209,7 +214,7 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
|
|
|
209
214
|
const projectClaudeDir = findNearestDir(cwd, path.join('.claude', 'agents'))
|
|
210
215
|
|
|
211
216
|
// ~/.claude/agents loads first so ~/.pi/agent/agents wins on name conflicts
|
|
212
|
-
const userAgents = scope === 'project' ? [] : [...loadAgentsFromDir(claudeUserDir, 'user'), ...loadAgentsFromDir(userDir, 'user')]
|
|
217
|
+
const userAgents = scope === 'project' ? [] : [...loadAgentsFromDir(BUILTIN_AGENTS_DIR, 'builtin'), ...loadAgentsFromDir(claudeUserDir, 'user'), ...loadAgentsFromDir(userDir, 'user')]
|
|
213
218
|
// project .claude/agents loads first so project .pi/agents wins on name conflicts
|
|
214
219
|
const projectAgents = scope === 'user' ? [] : [...(projectClaudeDir ? loadAgentsFromDir(projectClaudeDir, 'project') : []), ...(projectPiDir ? loadAgentsFromDir(projectPiDir, 'project') : [])]
|
|
215
220
|
|
|
@@ -24,7 +24,7 @@ import { type ExtensionAPI, type ExtensionContext, getMarkdownTheme, type Theme,
|
|
|
24
24
|
import { Container, Markdown, Spacer, Text } from '@earendil-works/pi-tui'
|
|
25
25
|
import { type Static, Type } from 'typebox'
|
|
26
26
|
import { capForContext } from '../internal/output-guard.js'
|
|
27
|
-
import { isProjectApproved } from '../internal/project-approval.js'
|
|
27
|
+
import { isProjectApproved, isProjectApprovedSilently } from '../internal/project-approval.js'
|
|
28
28
|
import { SUBAGENT_CHANNEL } from '../internal/subagent-events.js'
|
|
29
29
|
import { type AgentConfig, type AgentScope, discoverAgents } from './agents.js'
|
|
30
30
|
import { activeBackgroundRuns, backgroundStatusText, MAX_BACKGROUND_RUNS, startBackgroundRun } from './background.js'
|
|
@@ -139,7 +139,7 @@ interface UsageStats {
|
|
|
139
139
|
|
|
140
140
|
interface SingleResult {
|
|
141
141
|
agent: string
|
|
142
|
-
agentSource: 'user' | 'project' | 'unknown'
|
|
142
|
+
agentSource: 'user' | 'project' | 'builtin' | 'unknown'
|
|
143
143
|
task: string
|
|
144
144
|
exitCode: number
|
|
145
145
|
messages: Message[]
|
|
@@ -1068,6 +1068,19 @@ function renderParallelResult(results: SingleResult[], expanded: boolean, theme:
|
|
|
1068
1068
|
}
|
|
1069
1069
|
|
|
1070
1070
|
export default function subagentExtension(pi: ExtensionAPI) {
|
|
1071
|
+
// Claude surfaces each agent's description so the model can pick one autonomously.
|
|
1072
|
+
// Rebuilt per turn (agents are rediscovered per invocation too); project agents are
|
|
1073
|
+
// included only when the project is already approved, read without prompting, since
|
|
1074
|
+
// a trust dialog must not appear mid-turn and their descriptions are project text.
|
|
1075
|
+
pi.on('before_agent_start', async (event, ctx) => {
|
|
1076
|
+
const scope = isProjectApprovedSilently(ctx) ? 'both' : 'user'
|
|
1077
|
+
const { agents } = discoverAgents(ctx.cwd, scope)
|
|
1078
|
+
if (agents.length === 0) return
|
|
1079
|
+
const line = (text: string): string => text.replace(/\s+/g, ' ').trim().slice(0, 200)
|
|
1080
|
+
const roster = agents.map((agent) => `- ${agent.name} (${agent.source}): ${line(agent.description)}`).join('\n')
|
|
1081
|
+
return { systemPrompt: `${event.systemPrompt}\n\n## Subagents\n\nDelegate isolated tasks with the subagent tool ({agent, task}). Available agents:\n${roster}` }
|
|
1082
|
+
})
|
|
1083
|
+
|
|
1071
1084
|
pi.registerTool({
|
|
1072
1085
|
name: 'subagent',
|
|
1073
1086
|
label: 'Subagent',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Claude Code experience for the pi coding agent: reads your .claude config (rules, commands, skills, hooks, output styles, MCP servers, agents) and adds todo, checkpoints, memory, web, and subagents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi",
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: planner
|
|
3
|
-
description: Creates implementation plans from context and requirements
|
|
4
|
-
tools: read, grep, find, ls
|
|
5
|
-
model: claude-sonnet-4-5
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
You are a planning specialist. You receive context (from a scout) and requirements, then produce a clear implementation plan.
|
|
9
|
-
|
|
10
|
-
You must NOT make any changes. Only read, analyze, and plan.
|
|
11
|
-
|
|
12
|
-
Input format you'll receive:
|
|
13
|
-
- Context/findings from a scout agent
|
|
14
|
-
- Original query or requirements
|
|
15
|
-
|
|
16
|
-
Output format:
|
|
17
|
-
|
|
18
|
-
## Goal
|
|
19
|
-
One sentence summary of what needs to be done.
|
|
20
|
-
|
|
21
|
-
## Plan
|
|
22
|
-
Numbered steps, each small and actionable:
|
|
23
|
-
1. Step one - specific file/function to modify
|
|
24
|
-
2. Step two - what to add/change
|
|
25
|
-
3. ...
|
|
26
|
-
|
|
27
|
-
## Files to Modify
|
|
28
|
-
- `path/to/file.ts` - what changes
|
|
29
|
-
- `path/to/other.ts` - what changes
|
|
30
|
-
|
|
31
|
-
## New Files (if any)
|
|
32
|
-
- `path/to/new.ts` - purpose
|
|
33
|
-
|
|
34
|
-
## Risks
|
|
35
|
-
Anything to watch out for.
|
|
36
|
-
|
|
37
|
-
Keep the plan concrete. The worker agent will execute it verbatim.
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: reviewer
|
|
3
|
-
description: Code review specialist for quality and security analysis
|
|
4
|
-
tools: read, grep, find, ls, bash
|
|
5
|
-
model: claude-sonnet-4-5
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
You are a senior code reviewer. Analyze code for quality, security, and maintainability.
|
|
9
|
-
|
|
10
|
-
Bash is for read-only commands only: `git diff`, `git log`, `git show`. Do NOT modify files or run builds.
|
|
11
|
-
Assume tool permissions are not perfectly enforceable; keep all bash usage strictly read-only.
|
|
12
|
-
|
|
13
|
-
Strategy:
|
|
14
|
-
1. Run `git diff` to see recent changes (if applicable)
|
|
15
|
-
2. Read the modified files
|
|
16
|
-
3. Check for bugs, security issues, code smells
|
|
17
|
-
|
|
18
|
-
Output format:
|
|
19
|
-
|
|
20
|
-
## Files Reviewed
|
|
21
|
-
- `path/to/file.ts` (lines X-Y)
|
|
22
|
-
|
|
23
|
-
## Critical (must fix)
|
|
24
|
-
- `file.ts:42` - Issue description
|
|
25
|
-
|
|
26
|
-
## Warnings (should fix)
|
|
27
|
-
- `file.ts:100` - Issue description
|
|
28
|
-
|
|
29
|
-
## Suggestions (consider)
|
|
30
|
-
- `file.ts:150` - Improvement idea
|
|
31
|
-
|
|
32
|
-
## Summary
|
|
33
|
-
Overall assessment in 2-3 sentences.
|
|
34
|
-
|
|
35
|
-
Be specific with file paths and line numbers.
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: scout
|
|
3
|
-
description: Fast codebase recon that returns compressed context for handoff to other agents
|
|
4
|
-
tools: read, grep, find, ls, bash
|
|
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
|
-
```typescript
|
|
41
|
-
function keyFunction() {
|
|
42
|
-
// actual implementation
|
|
43
|
-
}
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
## Architecture
|
|
47
|
-
Brief explanation of how the pieces connect.
|
|
48
|
-
|
|
49
|
-
## Start Here
|
|
50
|
-
Which file to look at first and why.
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: worker
|
|
3
|
-
description: General-purpose subagent with full capabilities, isolated context
|
|
4
|
-
model: claude-sonnet-4-5
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
You are a worker agent with full capabilities. You operate in an isolated context window to handle delegated tasks without polluting the main conversation.
|
|
8
|
-
|
|
9
|
-
Work autonomously to complete the assigned task. Use all available tools as needed.
|
|
10
|
-
|
|
11
|
-
Output format when finished:
|
|
12
|
-
|
|
13
|
-
## Completed
|
|
14
|
-
What was done.
|
|
15
|
-
|
|
16
|
-
## Files Changed
|
|
17
|
-
- `path/to/file.ts` - what changed
|
|
18
|
-
|
|
19
|
-
## Notes (if any)
|
|
20
|
-
Anything the main agent should know.
|
|
21
|
-
|
|
22
|
-
If handing off to another agent (e.g. reviewer), include:
|
|
23
|
-
- Exact file paths changed
|
|
24
|
-
- Key functions/types touched (short list)
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Worker implements, reviewer reviews, worker applies feedback
|
|
3
|
-
---
|
|
4
|
-
Use the subagent tool with the chain parameter to execute this workflow:
|
|
5
|
-
|
|
6
|
-
1. First, use the "worker" agent to implement: $@
|
|
7
|
-
2. Then, use the "reviewer" agent to review the implementation from the previous step (use {previous} placeholder)
|
|
8
|
-
3. Finally, use the "worker" agent to apply the feedback from the review (use {previous} placeholder)
|
|
9
|
-
|
|
10
|
-
Execute this as a chain, passing output between steps via {previous}.
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Full implementation workflow - scout gathers context, planner creates plan, worker implements
|
|
3
|
-
---
|
|
4
|
-
Use the subagent tool with the chain parameter to execute this workflow:
|
|
5
|
-
|
|
6
|
-
1. First, use the "scout" agent to find all code relevant to: $@
|
|
7
|
-
2. Then, use the "planner" agent to create an implementation plan for "$@" using the context from the previous step (use {previous} placeholder)
|
|
8
|
-
3. Finally, use the "worker" agent to implement the plan from the previous step (use {previous} placeholder)
|
|
9
|
-
|
|
10
|
-
Execute this as a chain, passing output between steps via {previous}.
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Scout gathers context, planner creates implementation plan (no implementation)
|
|
3
|
-
---
|
|
4
|
-
Use the subagent tool with the chain parameter to execute this workflow:
|
|
5
|
-
|
|
6
|
-
1. First, use the "scout" agent to find all code relevant to: $@
|
|
7
|
-
2. Then, use the "planner" agent to create an implementation plan for "$@" using the context from the previous step (use {previous} placeholder)
|
|
8
|
-
|
|
9
|
-
Execute this as a chain, passing output between steps via {previous}. Do NOT implement - just return the plan.
|