@dreki-gg/pi-subagent 0.6.0 → 0.8.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.
Files changed (40) hide show
  1. package/.fallowrc.json +6 -0
  2. package/CHANGELOG.md +37 -0
  3. package/README.md +82 -70
  4. package/docs/orchestration-principles.md +132 -0
  5. package/docs/plans/01_structured-handoffs-and-synthesis.plan.md +376 -0
  6. package/docs/plans/02_clean-context-review-loop.plan.md +240 -0
  7. package/docs/plans/03_parallel-recon-single-writer-docs.plan.md +199 -0
  8. package/docs/plans/04_manager-workflow.plan.md +248 -0
  9. package/docs/plans/05_advisor-routing.plan.md +244 -0
  10. package/extensions/subagent/agent-result-utils.ts +12 -0
  11. package/extensions/subagent/agent-runner-types.ts +23 -0
  12. package/extensions/subagent/agent-runner.ts +90 -0
  13. package/extensions/subagent/agents.ts +31 -77
  14. package/extensions/subagent/handoffs.ts +273 -0
  15. package/extensions/subagent/index.ts +359 -581
  16. package/extensions/subagent/run-agent-args.ts +90 -0
  17. package/extensions/subagent/{delegate-executor.ts → spawn-utils.ts} +64 -87
  18. package/extensions/subagent/synthesis.ts +1 -51
  19. package/package.json +7 -10
  20. package/prompts/advisor.md +37 -0
  21. package/prompts/bug-prover.md +42 -0
  22. package/{agents → prompts}/docs-scout.md +2 -2
  23. package/prompts/manager.md +52 -0
  24. package/{agents → prompts}/planner.md +16 -1
  25. package/prompts/reviewer.md +93 -0
  26. package/{agents → prompts}/scout.md +5 -1
  27. package/prompts/validator.md +42 -0
  28. package/prompts/worker.md +61 -0
  29. package/skills/spawn-subagents/SKILL.md +80 -8
  30. package/skills/write-an-agent/SKILL.md +5 -5
  31. package/agents/reviewer.md +0 -37
  32. package/agents/worker.md +0 -26
  33. package/extensions/subagent/delegate-args.ts +0 -145
  34. package/extensions/subagent/delegate-types.ts +0 -62
  35. package/extensions/subagent/delegate-ui.ts +0 -132
  36. package/extensions/subagent/workflows.ts +0 -150
  37. package/prompts/implement-and-review.md +0 -10
  38. package/prompts/implement.md +0 -10
  39. package/prompts/scout-and-plan.md +0 -9
  40. /package/{agents → prompts}/ux-designer.md +0 -0
@@ -0,0 +1,244 @@
1
+ ---
2
+ name: "Advisor / smart-friend routing"
3
+ overview: "Add a bundled `advisor` role and lightweight escalation guidance so strong primary agents in `@dreki-gg/pi-subagent` can ask for focused second opinions on tricky cases without turning every task into a swarm. This slice is about capability routing, not replacing the main worker."
4
+ todo:
5
+ - id: "advisor-routing-1"
6
+ task: "Add a bundled `advisor` agent with a focused consultative contract: diagnose, suggest next investigation, and surface risks without taking ownership of implementation"
7
+ status: done
8
+ - id: "advisor-routing-2"
9
+ task: "Teach bundled worker/planner/reviewer prompts when to call the advisor and what context to send"
10
+ status: done
11
+ - id: "advisor-routing-3"
12
+ task: "Add a reusable prompt entry point and docs examples for advisor-assisted workflows"
13
+ status: done
14
+ ---
15
+
16
+ # Goal
17
+
18
+ Introduce a lightweight advisor pattern so primary agents can consult a strong second opinion for hard cases while keeping a single primary owner of the work.
19
+
20
+ # Context
21
+
22
+ - Parent rationale: we want capability routing and targeted escalation, not a default “everyone talks to everyone” swarm.
23
+ - Module root: `packages/subagent`
24
+ - This slice should work with the current package architecture and should not depend on a new runtime mode.
25
+ - Keep the advisor narrowly scoped: diagnose, redirect, highlight risk, or suggest the next question. The advisor should not silently become another worker.
26
+
27
+ ## What exists
28
+
29
+ Current relevant file tree on disk:
30
+
31
+ - `packages/subagent/prompts/planner.md`
32
+ - `packages/subagent/prompts/reviewer.md`
33
+ - `packages/subagent/prompts/worker.md`
34
+ - `packages/subagent/prompts/docs-scout.md`
35
+ - `packages/subagent/prompts/implement.md`
36
+ - `packages/subagent/prompts/implement-and-review.md`
37
+ - `packages/subagent/README.md`
38
+ - `packages/subagent/extensions/subagent/index.ts`
39
+ - `packages/subagent/extensions/subagent/agent-runner.ts`
40
+ - `packages/subagent/skills/spawn-subagents/SKILL.md`
41
+ - `packages/subagent/skills/write-an-agent/SKILL.md`
42
+
43
+ Actual current state on disk:
44
+
45
+ - There is no bundled `advisor` agent in `packages/subagent/prompts/`.
46
+ - There is no advisor-oriented prompt template in `packages/subagent/prompts/`.
47
+ - The current bundled roles are:
48
+ - `planner` for planning (`packages/subagent/prompts/planner.md`)
49
+ - `worker` for implementation (`packages/subagent/prompts/worker.md`)
50
+ - `reviewer` for verification (`packages/subagent/prompts/reviewer.md`)
51
+ - plus `scout`, `docs-scout`, and `ux-designer`
52
+ - The package runner already supports spawned agents consulting tools as needed:
53
+ - in `packages/subagent/extensions/subagent/index.ts:325-328`, `runSingleAgent()` passes `--tools` only when frontmatter declares them
54
+ - in `packages/subagent/extensions/subagent/agent-runner.ts:73-76`, `runAgent()` behaves the same way
55
+ - therefore bundled agents with omitted `tools:` can keep access to the broader tool surface, including `subagent`
56
+ - The current prompt contracts do **not** teach any role when to escalate:
57
+ - `planner.md` only says to produce a concrete plan
58
+ - `worker.md` only says to complete the task autonomously
59
+ - `reviewer.md` only says to review the diff / files
60
+ - The `spawn-subagents` skill recommends specialists and review loops, but does not mention an advisor or “consult when tricky” routing rule.
61
+
62
+ # API inventory
63
+
64
+ ## Existing agent prompt contracts that may invoke advisor
65
+
66
+ From `packages/subagent/prompts/planner.md`:
67
+
68
+ ```md
69
+ You are a planning specialist.
70
+ You must NOT make any changes. Only read, analyze, and plan.
71
+ ```
72
+
73
+ From `packages/subagent/prompts/worker.md`:
74
+
75
+ ```md
76
+ You are a worker agent with full capabilities.
77
+ Work autonomously to complete the assigned task. Use all available tools as needed.
78
+ ```
79
+
80
+ From `packages/subagent/prompts/reviewer.md`:
81
+
82
+ ```md
83
+ You are a senior code reviewer.
84
+ Analyze code for quality, security, and maintainability.
85
+ ```
86
+
87
+ ## Existing subagent entry shapes available to an advisor pattern
88
+
89
+ From the `subagent` tool surface:
90
+
91
+ ```ts
92
+ // single consult
93
+ { agent: 'advisor', task: '...' }
94
+
95
+ // consult inside a chain
96
+ { chain: [{ agent: 'worker', task: '...' }, { agent: 'advisor', task: '... {previous} ...' }] }
97
+
98
+ // focused parallel consults
99
+ { tasks: [{ agent: 'advisor', task: '...' }, { agent: 'docs-scout', task: '...' }] }
100
+ ```
101
+
102
+ ## Proposed advisor contract
103
+
104
+ The advisor should return guidance the caller can act on immediately:
105
+
106
+ ```md
107
+ ## Assessment
108
+ - what looks tricky / risky / ambiguous
109
+
110
+ ## What to inspect next
111
+ - concrete files, commands, or questions
112
+
113
+ ## Recommendation
114
+ - best next action for the caller
115
+
116
+ ## Risks
117
+ - what could still go wrong
118
+ ```
119
+
120
+ The advisor should not claim to have implemented or verified anything it did not actually inspect.
121
+
122
+ # Tasks
123
+
124
+ ## 1. Add a bundled `advisor` agent with a focused consultative contract
125
+
126
+ ### Files
127
+ - Create `packages/subagent/prompts/advisor.md`
128
+
129
+ ### What to add
130
+ Create a concise bundled agent whose sole job is to provide focused second-opinion guidance.
131
+
132
+ ### Required behavior
133
+ - Work from the caller’s question plus any provided handoff.
134
+ - Ask the caller to investigate specific files / commands when the answer depends on context the advisor has not yet inspected.
135
+ - Surface hidden risks or better approaches.
136
+ - Avoid taking over implementation.
137
+ - Prefer decisive recommendations over brainstorming lists.
138
+
139
+ ### Frontmatter guidance
140
+ - Use an existing strong model already in package conventions (default to `openai/gpt-5.4` unless there is a strong reason to diversify).
141
+ - Keep the prompt under ~100 lines per `packages/subagent/skills/write-an-agent/SKILL.md`.
142
+ - Omit `tools:` only if you want the advisor to have broad access; otherwise keep the tool list minimal and read-oriented.
143
+
144
+ ### Suggested prompt rules
145
+ 1. Diagnose the hard part, not the whole task from scratch.
146
+ 2. If more repo context is needed, say exactly what to inspect next.
147
+ 3. Return a recommendation the caller can act on immediately.
148
+ 4. Do not implement code or rewrite plans unless explicitly asked.
149
+
150
+ ## 2. Teach bundled prompts when to call the advisor and what context to send
151
+
152
+ ### Files
153
+ - Modify `packages/subagent/prompts/worker.md`
154
+ - Modify `packages/subagent/prompts/planner.md`
155
+ - Modify `packages/subagent/prompts/reviewer.md`
156
+
157
+ ### What to change
158
+ Add small, explicit escalation rules to the existing bundled roles.
159
+
160
+ ### Recommended escalation triggers
161
+ - `worker`
162
+ - ambiguous architecture tradeoff
163
+ - persistent failing tests or unexplained errors
164
+ - merge conflicts / tangled diffs
165
+ - security-sensitive or migration-heavy changes
166
+ - `planner`
167
+ - multiple viable designs with materially different blast radius
168
+ - unclear ownership boundaries across packages
169
+ - need for sharper decomposition before implementation
170
+ - `reviewer`
171
+ - uncertainty whether an issue is real vs. intended
172
+ - suspected deeper architectural problem beyond the current diff
173
+ - need for a second opinion on severity
174
+
175
+ ### Required context to send when consulting advisor
176
+ Keep the consult lightweight:
177
+ - current role (`worker` / `planner` / `reviewer`)
178
+ - the exact question
179
+ - touched files / symbols if known
180
+ - the smallest relevant task summary
181
+ - what has already been tried or observed
182
+
183
+ ### Notes
184
+ - Do not tell every role to always call the advisor. This is an escalation path, not a mandatory step.
185
+ - Keep the primary role in charge; the advisor should inform decisions, not replace ownership.
186
+
187
+ ## 3. Add a reusable prompt entry point and docs examples for advisor-assisted workflows
188
+
189
+ ### Files
190
+ - Create `packages/subagent/prompts/consult-advisor.md`
191
+ - Modify `packages/subagent/README.md`
192
+ - Modify `packages/subagent/skills/spawn-subagents/SKILL.md`
193
+
194
+ ### What to add
195
+ Create a simple prompt template that encourages focused advisor consultation without changing core workflow semantics.
196
+
197
+ ### Suggested prompt shape
198
+
199
+ ```md
200
+ Use the subagent tool to consult the `advisor` agent about: $@
201
+
202
+ The advisor should:
203
+ 1. identify the hard part
204
+ 2. suggest the next investigation or decision
205
+ 3. surface concrete risks
206
+ 4. return a recommendation, not a full implementation
207
+ ```
208
+
209
+ ### README / skill updates
210
+ - Add `advisor` to the bundled agent list once shipped.
211
+ - Add one example such as:
212
+ - “ask advisor whether this migration should be split before sending it to worker”
213
+ - “have worker consult advisor on the failing test loop”
214
+ - In `spawn-subagents`, add guidance that advisor use is optional and should be reserved for tricky or high-risk cases.
215
+
216
+ # Files to create
217
+
218
+ - `packages/subagent/prompts/advisor.md`
219
+ - `packages/subagent/prompts/consult-advisor.md`
220
+
221
+ # Files to modify
222
+
223
+ - `packages/subagent/prompts/worker.md` — add narrow escalation guidance
224
+ - `packages/subagent/prompts/planner.md` — add narrow escalation guidance
225
+ - `packages/subagent/prompts/reviewer.md` — add narrow escalation guidance
226
+ - `packages/subagent/README.md` — document the new advisor role and one or two examples
227
+ - `packages/subagent/skills/spawn-subagents/SKILL.md` — explain when to use advisor vs. planner/reviewer
228
+
229
+ # Testing notes
230
+
231
+ - If this remains markdown-only, there is no TypeScript validation requirement.
232
+ - Manually test one focused consult, such as a planner or worker asking advisor a hard question, and verify:
233
+ - the advisor returns a recommendation rather than trying to own the full task
234
+ - the caller stays in charge of implementation or review
235
+ - the exchange is shorter and more targeted than a full additional workflow
236
+ - If you introduce any helper TS module later to render consult packets, run `bun run --filter '@dreki-gg/pi-subagent' typecheck`.
237
+
238
+ # Patterns to follow
239
+
240
+ - `packages/subagent/skills/write-an-agent/SKILL.md:15-20` and `:46-67` — keep the new agent narrow, concise, and output-contract-driven
241
+ - `packages/subagent/prompts/planner.md:9-38` — example of a specialist planner role that the advisor should complement, not replace
242
+ - `packages/subagent/prompts/reviewer.md:10-37` — example of a verifier role that may occasionally need escalation
243
+ - `packages/subagent/prompts/worker.md:9-26` — example of a primary owner role that should remain in charge after consulting advisor
244
+ - `packages/subagent/skills/spawn-subagents/SKILL.md:23-58` — existing specialist-selection guidance to extend with optional escalation
@@ -0,0 +1,12 @@
1
+ import type { AgentResult } from './agent-runner-types.js';
2
+
3
+ export function getFinalText(result: AgentResult): string {
4
+ for (let i = result.messages.length - 1; i >= 0; i--) {
5
+ const msg = result.messages[i];
6
+ if (msg.role !== 'assistant') continue;
7
+ for (const part of msg.content) {
8
+ if (part.type === 'text') return part.text;
9
+ }
10
+ }
11
+ return '(no output)';
12
+ }
@@ -0,0 +1,23 @@
1
+ import type { Message } from '@earendil-works/pi-ai';
2
+
3
+ export interface UsageStats {
4
+ input: number;
5
+ output: number;
6
+ cacheRead: number;
7
+ cacheWrite: number;
8
+ cost: number;
9
+ contextTokens: number;
10
+ turns: number;
11
+ }
12
+
13
+ export interface AgentResult {
14
+ agent: string;
15
+ task: string;
16
+ exitCode: number;
17
+ messages: Message[];
18
+ stderr: string;
19
+ usage: UsageStats;
20
+ model?: string;
21
+ stopReason?: string;
22
+ errorMessage?: string;
23
+ }
@@ -0,0 +1,90 @@
1
+ import type { ResolvedPaths } from '@earendil-works/pi-coding-agent';
2
+ import { discoverAgents, type AgentScope } from './agents.js';
3
+ import type { AgentResult } from './agent-runner-types.js';
4
+ import { emptyUsage, spawnPiAgent } from './spawn-utils.js';
5
+
6
+ export type OnPhaseUpdate = (phaseName: string, agentName: string, result: AgentResult) => void;
7
+
8
+ export interface RunAgentOptions {
9
+ agentScope?: AgentScope;
10
+ cwd?: string;
11
+ model?: string;
12
+ thinking?: string;
13
+ onUpdate?: OnPhaseUpdate;
14
+ phaseName?: string;
15
+ signal?: AbortSignal;
16
+ resolvedPaths?: ResolvedPaths;
17
+ }
18
+
19
+ export async function runAgent(
20
+ cwd: string,
21
+ agentName: string,
22
+ task: string,
23
+ options: RunAgentOptions = {},
24
+ ): Promise<AgentResult> {
25
+ const { agents } = discoverAgents(cwd, options.agentScope ?? 'user', options.resolvedPaths);
26
+ const agent = agents.find((a) => a.name === agentName);
27
+
28
+ if (!agent) {
29
+ const available = agents.map((a) => a.name).join(', ') || 'none';
30
+ return {
31
+ agent: agentName,
32
+ task,
33
+ exitCode: 1,
34
+ messages: [],
35
+ stderr: `Unknown agent: "${agentName}". Available: ${available}.`,
36
+ usage: emptyUsage(),
37
+ };
38
+ }
39
+
40
+ const selectedModel = options.model ?? agent.model;
41
+ const selectedThinking = options.thinking ?? agent.thinking;
42
+
43
+ const result: AgentResult = {
44
+ agent: agentName,
45
+ task,
46
+ exitCode: 0,
47
+ messages: [],
48
+ stderr: '',
49
+ usage: emptyUsage(),
50
+ model: selectedModel,
51
+ };
52
+
53
+ const spawnResult = await spawnPiAgent({
54
+ cwd: options.cwd ?? cwd,
55
+ agentName: agent.name,
56
+ task,
57
+ systemPrompt: agent.systemPrompt,
58
+ model: selectedModel,
59
+ thinking: selectedThinking,
60
+ tools: agent.tools,
61
+ signal: options.signal,
62
+ onMessage: () => {
63
+ // Copy accumulated state for the phase update callback
64
+ if (options.onUpdate) {
65
+ result.messages = spawnResult.messages;
66
+ result.usage = spawnResult.usage;
67
+ result.model = spawnResult.model ?? result.model;
68
+ result.stopReason = spawnResult.stopReason;
69
+ result.errorMessage = spawnResult.errorMessage;
70
+ options.onUpdate(options.phaseName ?? 'unknown', agentName, { ...result });
71
+ }
72
+ },
73
+ onToolResult: () => {
74
+ if (options.onUpdate) {
75
+ result.messages = spawnResult.messages;
76
+ options.onUpdate(options.phaseName ?? 'unknown', agentName, { ...result });
77
+ }
78
+ },
79
+ });
80
+
81
+ result.exitCode = spawnResult.exitCode;
82
+ result.messages = spawnResult.messages;
83
+ result.stderr = spawnResult.stderr;
84
+ result.usage = spawnResult.usage;
85
+ result.model = spawnResult.model ?? result.model;
86
+ result.stopReason = spawnResult.stopReason;
87
+ result.errorMessage = spawnResult.errorMessage;
88
+
89
+ return result;
90
+ }
@@ -1,21 +1,18 @@
1
1
  /**
2
2
  * Agent discovery and configuration
3
3
  *
4
- * Supports two discovery strategies:
5
- * 1. Package-resolved: Uses pi's ResolvedPaths.agents from the package manager
6
- * when available (pi forks with first-class agents resource support).
7
- * Package agents are resolved, filtered, and toggleable via pi config.
8
- * 2. Legacy: Manual filesystem discovery from bundled, user, and project dirs.
9
- * Used as fallback when ResolvedPaths.agents is not available.
4
+ * Uses pi's ResolvedPaths.prompts from the package manager to discover
5
+ * agent prompts from installed packages. User and project prompts are
6
+ * discovered from the filesystem and override package prompts by name.
10
7
  */
11
8
 
12
9
  import * as fs from 'node:fs';
13
10
  import * as path from 'node:path';
14
- import { getAgentDir, parseFrontmatter } from '@mariozechner/pi-coding-agent';
15
- import type { ResolvedPaths } from '@mariozechner/pi-coding-agent';
11
+ import { getAgentDir, parseFrontmatter } from '@earendil-works/pi-coding-agent';
12
+ import type { ResolvedPaths } from '@earendil-works/pi-coding-agent';
16
13
 
17
14
  export type AgentScope = 'user' | 'project' | 'both';
18
- export type AgentSource = 'bundled' | 'user' | 'project' | 'package';
15
+ export type AgentSource = 'user' | 'project' | 'package';
19
16
  export type AgentSessionStrategy = 'inline' | 'fork-at';
20
17
 
21
18
  export interface AgentConfig {
@@ -32,12 +29,9 @@ export interface AgentConfig {
32
29
 
33
30
  export interface AgentDiscoveryResult {
34
31
  agents: AgentConfig[];
35
- projectAgentsDir: string | null;
32
+ projectPromptsDir: string | null;
36
33
  }
37
34
 
38
- /** Bundled agents ship with the package (../../agents relative to extensions/subagent/) */
39
- const bundledAgentsDir = path.resolve(import.meta.dirname, '..', '..', 'agents');
40
-
41
35
  function parseSessionStrategy(value?: string): AgentSessionStrategy | undefined {
42
36
  if (!value) return undefined;
43
37
  const normalized = value.trim().toLowerCase();
@@ -106,10 +100,10 @@ function isDirectory(p: string): boolean {
106
100
  }
107
101
  }
108
102
 
109
- function findNearestProjectAgentsDir(cwd: string): string | null {
103
+ function findNearestProjectPromptsDir(cwd: string): string | null {
110
104
  let currentDir = cwd;
111
105
  while (true) {
112
- const candidate = path.join(currentDir, '.pi', 'agents');
106
+ const candidate = path.join(currentDir, '.pi', 'prompts');
113
107
  if (isDirectory(candidate)) return candidate;
114
108
 
115
109
  const parentDir = path.dirname(currentDir);
@@ -119,18 +113,14 @@ function findNearestProjectAgentsDir(cwd: string): string | null {
119
113
  }
120
114
 
121
115
  /**
122
- * Load agents from package-manager resolved paths (enabled only).
123
- * This is called with pre-resolved paths from an async context.
116
+ * Load agents from package-manager resolved prompt paths (enabled only).
124
117
  */
125
- export function loadAgentsFromResolvedPaths(resolvedPaths: ResolvedPaths): AgentConfig[] {
126
- // Check if the agents field exists (compatibility with upstream pi)
127
- const agentResources = (resolvedPaths as unknown as Record<string, unknown>).agents;
128
- if (!agentResources || !Array.isArray(agentResources)) {
129
- return [];
130
- }
118
+ function loadAgentsFromResolvedPaths(resolvedPaths: ResolvedPaths): AgentConfig[] {
119
+ const promptResources = (resolvedPaths as unknown as Record<string, unknown>).prompts;
120
+ if (!Array.isArray(promptResources)) return [];
131
121
 
132
122
  const agents: AgentConfig[] = [];
133
- for (const resource of agentResources) {
123
+ for (const resource of promptResources) {
134
124
  if (!resource.enabled) continue;
135
125
  const agent = loadAgentFromFile(resource.path, 'package');
136
126
  if (agent) agents.push(agent);
@@ -169,21 +159,28 @@ function loadAgentFromFile(filePath: string, source: AgentSource): AgentConfig |
169
159
  };
170
160
  }
171
161
 
172
- export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryResult {
173
- const userDir = path.join(getAgentDir(), 'agents');
174
- const projectAgentsDir = findNearestProjectAgentsDir(cwd);
175
-
176
- // Bundled agents from the package itself (lowest priority)
177
- const bundledAgents = loadAgentsFromDir(bundledAgentsDir, 'bundled');
162
+ /**
163
+ * Discover agents from package-resolved prompts, user, and project directories.
164
+ *
165
+ * Priority: package (lowest) → user → project (highest, overrides by name).
166
+ */
167
+ export function discoverAgents(
168
+ cwd: string,
169
+ scope: AgentScope,
170
+ resolvedPaths?: ResolvedPaths,
171
+ ): AgentDiscoveryResult {
172
+ const userDir = path.join(getAgentDir(), 'prompts');
173
+ const projectPromptsDir = findNearestProjectPromptsDir(cwd);
178
174
 
175
+ const packageAgents = resolvedPaths ? loadAgentsFromResolvedPaths(resolvedPaths) : [];
179
176
  const userAgents = scope === 'project' ? [] : loadAgentsFromDir(userDir, 'user');
180
177
  const projectAgents =
181
- scope === 'user' || !projectAgentsDir ? [] : loadAgentsFromDir(projectAgentsDir, 'project');
178
+ scope === 'user' || !projectPromptsDir ? [] : loadAgentsFromDir(projectPromptsDir, 'project');
182
179
 
183
- // Priority: bundled (lowest) → user → project (highest, overrides by name)
184
180
  const agentMap = new Map<string, AgentConfig>();
185
181
 
186
- for (const agent of bundledAgents) agentMap.set(agent.name, agent);
182
+ // Package agents are lowest priority
183
+ for (const agent of packageAgents) agentMap.set(agent.name, agent);
187
184
 
188
185
  if (scope === 'both') {
189
186
  for (const agent of userAgents) agentMap.set(agent.name, agent);
@@ -194,48 +191,5 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
194
191
  for (const agent of projectAgents) agentMap.set(agent.name, agent);
195
192
  }
196
193
 
197
- return { agents: Array.from(agentMap.values()), projectAgentsDir };
198
- }
199
-
200
- /**
201
- * Discover agents with package-resolved paths merged in.
202
- * Package-resolved agents (from pi.agents in installed packages) are loaded
203
- * at lowest priority, then overridden by bundled, user, and project agents.
204
- *
205
- * Falls back to discoverAgents() when resolvedPaths is not provided or
206
- * does not contain the agents field (upstream pi compatibility).
207
- */
208
- export function discoverAgentsWithPackages(
209
- cwd: string,
210
- scope: AgentScope,
211
- resolvedPaths?: ResolvedPaths,
212
- ): AgentDiscoveryResult {
213
- const base = discoverAgents(cwd, scope);
214
-
215
- if (!resolvedPaths) return base;
216
-
217
- const packageAgents = loadAgentsFromResolvedPaths(resolvedPaths);
218
- if (packageAgents.length === 0) return base;
219
-
220
- // Package agents are lowest priority: existing agents override by name
221
- const agentMap = new Map<string, AgentConfig>();
222
- for (const agent of packageAgents) agentMap.set(agent.name, agent);
223
- for (const agent of base.agents) agentMap.set(agent.name, agent);
224
-
225
- return { agents: Array.from(agentMap.values()), projectAgentsDir: base.projectAgentsDir };
226
- }
227
-
228
- export { bundledAgentsDir };
229
-
230
- export function formatAgentList(
231
- agents: AgentConfig[],
232
- maxItems: number,
233
- ): { text: string; remaining: number } {
234
- if (agents.length === 0) return { text: 'none', remaining: 0 };
235
- const listed = agents.slice(0, maxItems);
236
- const remaining = agents.length - listed.length;
237
- return {
238
- text: listed.map((a) => `${a.name} (${a.source}): ${a.description}`).join('; '),
239
- remaining,
240
- };
194
+ return { agents: Array.from(agentMap.values()), projectPromptsDir };
241
195
  }