@dreki-gg/pi-subagent 0.7.0 → 0.8.1

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.
@@ -12,16 +12,10 @@
12
12
  * Uses JSON mode to capture structured output from subagents.
13
13
  */
14
14
 
15
- import { spawn } from 'node:child_process';
16
- import { existsSync } from 'node:fs';
17
- import * as fs from 'node:fs';
18
- import { copyFile, mkdir, readdir, unlink } from 'node:fs/promises';
19
15
  import * as os from 'node:os';
20
- import { join } from 'node:path';
21
- import * as path from 'node:path';
22
- import type { AgentToolResult } from '@mariozechner/pi-agent-core';
23
- import type { Message } from '@mariozechner/pi-ai';
24
- import { StringEnum } from '@mariozechner/pi-ai';
16
+ import type { AgentToolResult } from '@earendil-works/pi-agent-core';
17
+ import type { Message } from '@earendil-works/pi-ai';
18
+ import { StringEnum } from '@earendil-works/pi-ai';
25
19
  import {
26
20
  type ExtensionAPI,
27
21
  type ExtensionCommandContext,
@@ -31,17 +25,15 @@ import {
31
25
  getMarkdownTheme,
32
26
  ModelRegistry,
33
27
  SettingsManager,
34
- withFileMutationQueue,
35
- } from '@mariozechner/pi-coding-agent';
36
- import type { ResolvedPaths } from '@mariozechner/pi-coding-agent';
37
- import { Box, Container, Markdown, Spacer, Text, type AutocompleteItem } from '@mariozechner/pi-tui';
28
+ } from '@earendil-works/pi-coding-agent';
29
+ import type { ResolvedPaths } from '@earendil-works/pi-coding-agent';
30
+ import { Box, Container, Markdown, Spacer, Text, type AutocompleteItem } from '@earendil-works/pi-tui';
38
31
  import { Type } from 'typebox';
39
32
  import {
40
33
  type AgentConfig,
41
34
  type AgentScope,
42
35
  type AgentSource,
43
- bundledAgentsDir,
44
- discoverAgentsWithPackages,
36
+ discoverAgents,
45
37
  } from './agents.js';
46
38
  import { formatRunAgentUsage, parseRunAgentArgs } from './run-agent-args.js';
47
39
  import { runAgent } from './agent-runner.js';
@@ -49,6 +41,7 @@ import { extractRecentConversation } from './synthesis.js';
49
41
  import type { AgentResult } from './agent-runner-types.js';
50
42
  import { getFinalText } from './agent-result-utils.js';
51
43
  import { buildHandoffFromResult, renderHandoffForPrompt } from './handoffs.js';
44
+ import { emptyUsage, spawnPiAgent } from './spawn-utils.js';
52
45
 
53
46
  const MAX_PARALLEL_TASKS = 8;
54
47
  const MAX_CONCURRENCY = 4;
@@ -200,7 +193,7 @@ interface SingleResult {
200
193
  interface SubagentDetails {
201
194
  mode: 'single' | 'parallel' | 'chain';
202
195
  agentScope: AgentScope;
203
- projectAgentsDir: string | null;
196
+ projectPromptsDir: string | null;
204
197
  results: SingleResult[];
205
198
  }
206
199
 
@@ -254,39 +247,6 @@ async function mapWithConcurrencyLimit<TIn, TOut>(
254
247
  return results;
255
248
  }
256
249
 
257
- async function writePromptToTempFile(
258
- agentName: string,
259
- prompt: string,
260
- ): Promise<{ dir: string; filePath: string }> {
261
- const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'pi-subagent-'));
262
- const safeName = agentName.replace(/[^\w.-]+/g, '_');
263
- const filePath = path.join(tmpDir, `prompt-${safeName}.md`);
264
- await withFileMutationQueue(filePath, async () => {
265
- await fs.promises.writeFile(filePath, prompt, { encoding: 'utf-8', mode: 0o600 });
266
- });
267
- return { dir: tmpDir, filePath };
268
- }
269
-
270
- function getPiInvocation(args: string[]): { command: string; args: string[] } {
271
- const currentScript = process.argv[1];
272
-
273
- // Bun standalone binaries set argv[1] to a virtual FS path (e.g. /$bunfs/root/pi)
274
- // that only resolves inside the running Bun process. Skip it — the binary IS the entry point.
275
- const isBunVirtualPath = currentScript?.startsWith('/$bunfs/');
276
-
277
- if (currentScript && !isBunVirtualPath && fs.existsSync(currentScript)) {
278
- return { command: process.execPath, args: [currentScript, ...args] };
279
- }
280
-
281
- const execName = path.basename(process.execPath).toLowerCase();
282
- const isGenericRuntime = /^(node|bun)(\.exe)?$/.test(execName);
283
- if (!isGenericRuntime) {
284
- // Standalone binary (e.g. pi compiled with Bun) — invoke directly
285
- return { command: process.execPath, args };
286
- }
287
-
288
- return { command: 'pi', args };
289
- }
290
250
 
291
251
  type OnUpdateCallback = (partial: AgentToolResult<SubagentDetails>) => void;
292
252
 
@@ -314,15 +274,7 @@ async function runSingleAgent(
314
274
  exitCode: 1,
315
275
  messages: [],
316
276
  stderr: `Unknown agent: "${agentName}". Available agents: ${available}.`,
317
- usage: {
318
- input: 0,
319
- output: 0,
320
- cacheRead: 0,
321
- cacheWrite: 0,
322
- cost: 0,
323
- contextTokens: 0,
324
- turns: 0,
325
- },
277
+ usage: emptyUsage(),
326
278
  step,
327
279
  };
328
280
  }
@@ -330,14 +282,6 @@ async function runSingleAgent(
330
282
  const selectedModel = modelOverride ?? agent.model;
331
283
  const selectedThinking = thinkingOverride ?? agent.thinking;
332
284
 
333
- const args: string[] = ['--mode', 'json', '-p', '--no-session'];
334
- if (selectedModel) args.push('--model', selectedModel);
335
- if (selectedThinking) args.push('--thinking', selectedThinking);
336
- if (agent.tools && agent.tools.length > 0) args.push('--tools', agent.tools.join(','));
337
-
338
- let tmpPromptDir: string | null = null;
339
- let tmpPromptPath: string | null = null;
340
-
341
285
  const currentResult: SingleResult = {
342
286
  agent: agentName,
343
287
  agentSource: agent.source,
@@ -345,15 +289,7 @@ async function runSingleAgent(
345
289
  exitCode: 0,
346
290
  messages: [],
347
291
  stderr: '',
348
- usage: {
349
- input: 0,
350
- output: 0,
351
- cacheRead: 0,
352
- cacheWrite: 0,
353
- cost: 0,
354
- contextTokens: 0,
355
- turns: 0,
356
- },
292
+ usage: emptyUsage(),
357
293
  model: selectedModel,
358
294
  step,
359
295
  };
@@ -367,113 +303,39 @@ async function runSingleAgent(
367
303
  }
368
304
  };
369
305
 
370
- try {
371
- if (agent.systemPrompt.trim()) {
372
- const tmp = await writePromptToTempFile(agent.name, agent.systemPrompt);
373
- tmpPromptDir = tmp.dir;
374
- tmpPromptPath = tmp.filePath;
375
- args.push('--append-system-prompt', tmpPromptPath);
376
- }
377
-
378
- args.push(`Task: ${task}`);
379
- let wasAborted = false;
380
-
381
- const exitCode = await new Promise<number>((resolve) => {
382
- const invocation = getPiInvocation(args);
383
- const proc = spawn(invocation.command, invocation.args, {
384
- cwd: cwd ?? defaultCwd,
385
- shell: false,
386
- stdio: ['ignore', 'pipe', 'pipe'],
387
- });
388
- let buffer = '';
389
-
390
- const processLine = (line: string) => {
391
- if (!line.trim()) return;
392
- let event: any;
393
- try {
394
- event = JSON.parse(line);
395
- } catch {
396
- return;
397
- }
398
-
399
- if (event.type === 'message_end' && event.message) {
400
- const msg = event.message as Message;
401
- currentResult.messages.push(msg);
402
-
403
- if (msg.role === 'assistant') {
404
- currentResult.usage.turns++;
405
- const usage = msg.usage;
406
- if (usage) {
407
- currentResult.usage.input += usage.input || 0;
408
- currentResult.usage.output += usage.output || 0;
409
- currentResult.usage.cacheRead += usage.cacheRead || 0;
410
- currentResult.usage.cacheWrite += usage.cacheWrite || 0;
411
- currentResult.usage.cost += usage.cost?.total || 0;
412
- currentResult.usage.contextTokens = usage.totalTokens || 0;
413
- }
414
- if (!currentResult.model && msg.model) currentResult.model = msg.model;
415
- if (msg.stopReason) currentResult.stopReason = msg.stopReason;
416
- if (msg.errorMessage) currentResult.errorMessage = msg.errorMessage;
417
- }
418
- emitUpdate();
419
- }
420
-
421
- if (event.type === 'tool_result_end' && event.message) {
422
- currentResult.messages.push(event.message as Message);
423
- emitUpdate();
424
- }
425
- };
426
-
427
- proc.stdout.on('data', (data) => {
428
- buffer += data.toString();
429
- const lines = buffer.split('\n');
430
- buffer = lines.pop() || '';
431
- for (const line of lines) processLine(line);
432
- });
433
-
434
- proc.stderr.on('data', (data) => {
435
- currentResult.stderr += data.toString();
436
- });
437
-
438
- proc.on('close', (code) => {
439
- if (buffer.trim()) processLine(buffer);
440
- resolve(code ?? 0);
441
- });
442
-
443
- proc.on('error', () => {
444
- resolve(1);
445
- });
306
+ const spawnResult = await spawnPiAgent({
307
+ cwd: cwd ?? defaultCwd,
308
+ agentName: agent.name,
309
+ task,
310
+ systemPrompt: agent.systemPrompt,
311
+ model: selectedModel,
312
+ thinking: selectedThinking,
313
+ tools: agent.tools,
314
+ signal,
315
+ onMessage: (msg) => {
316
+ currentResult.messages = spawnResult.messages;
317
+ currentResult.usage = spawnResult.usage;
318
+ currentResult.model = spawnResult.model ?? currentResult.model;
319
+ currentResult.stopReason = spawnResult.stopReason;
320
+ currentResult.errorMessage = spawnResult.errorMessage;
321
+ emitUpdate();
322
+ },
323
+ onToolResult: () => {
324
+ currentResult.messages = spawnResult.messages;
325
+ emitUpdate();
326
+ },
327
+ });
446
328
 
447
- if (signal) {
448
- const killProc = () => {
449
- wasAborted = true;
450
- proc.kill('SIGTERM');
451
- setTimeout(() => {
452
- if (!proc.killed) proc.kill('SIGKILL');
453
- }, 5000);
454
- };
455
- if (signal.aborted) killProc();
456
- else signal.addEventListener('abort', killProc, { once: true });
457
- }
458
- });
329
+ currentResult.exitCode = spawnResult.exitCode;
330
+ currentResult.messages = spawnResult.messages;
331
+ currentResult.stderr = spawnResult.stderr;
332
+ currentResult.usage = spawnResult.usage;
333
+ currentResult.model = spawnResult.model ?? currentResult.model;
334
+ currentResult.stopReason = spawnResult.stopReason;
335
+ currentResult.errorMessage = spawnResult.errorMessage;
459
336
 
460
- currentResult.exitCode = exitCode;
461
- if (wasAborted) throw new Error('Subagent was aborted');
462
- return currentResult;
463
- } finally {
464
- if (tmpPromptPath)
465
- try {
466
- fs.unlinkSync(tmpPromptPath);
467
- } catch {
468
- /* ignore */
469
- }
470
- if (tmpPromptDir)
471
- try {
472
- fs.rmdirSync(tmpPromptDir);
473
- } catch {
474
- /* ignore */
475
- }
476
- }
337
+ if (spawnResult.wasAborted) throw new Error('Subagent was aborted');
338
+ return currentResult;
477
339
  }
478
340
 
479
341
  const TaskItem = Type.Object({
@@ -531,24 +393,15 @@ const SubagentParams = Type.Object({
531
393
  ),
532
394
  });
533
395
 
534
- /**
535
- * Try to resolve package paths including the agents resource.
536
- * Returns null if the pi version does not support ResolvedPaths.agents.
537
- */
538
- async function tryResolvePackagePaths(cwd: string): Promise<ResolvedPaths | undefined> {
396
+ async function resolvePackagePaths(cwd: string): Promise<ResolvedPaths | undefined> {
539
397
  try {
540
398
  const agentDir = getAgentDir();
541
399
  const settingsManager = SettingsManager.create(cwd, agentDir);
542
400
  const packageManager = new DefaultPackageManager({ cwd, agentDir, settingsManager });
543
- const resolved = await packageManager.resolve();
544
- // Check if this pi version has agents support
545
- if ('agents' in resolved && Array.isArray((resolved as Record<string, unknown>).agents)) {
546
- return resolved;
547
- }
401
+ return await packageManager.resolve();
548
402
  } catch {
549
- // Incompatible pi version or missing API
403
+ return undefined;
550
404
  }
551
- return undefined;
552
405
  }
553
406
 
554
407
  export default function (pi: ExtensionAPI) {
@@ -599,8 +452,8 @@ export default function (pi: ExtensionAPI) {
599
452
  }
600
453
 
601
454
  async function getAutocompleteAgents(scope: AgentScope): Promise<AgentConfig[]> {
602
- const resolvedPaths = await tryResolvePackagePaths(autocompleteCwd);
603
- return discoverAgentsWithPackages(autocompleteCwd, scope, resolvedPaths).agents;
455
+ const resolvedPaths = await resolvePackagePaths(autocompleteCwd);
456
+ return discoverAgents(autocompleteCwd, scope, resolvedPaths).agents;
604
457
  }
605
458
 
606
459
  async function getAutocompleteModels(scope: AgentScope): Promise<
@@ -645,15 +498,15 @@ export default function (pi: ExtensionAPI) {
645
498
  'Delegate tasks to specialized subagents with isolated context.',
646
499
  'Modes: single (agent + task), parallel (tasks array), chain (sequential with {previous} placeholder).',
647
500
  'Optional per-run or per-step model and reasoning-level overrides are supported via model/thinking.',
648
- 'Default agent scope is "user" (from ~/.pi/agent/agents).',
649
- 'To enable project-local agents in .pi/agents, set agentScope: "both" (or "project").',
501
+ 'Default agent scope is "user" (from ~/.pi/agent/prompts).',
502
+ 'To enable project-local agents in .pi/prompts, set agentScope: "both" (or "project").',
650
503
  ].join(' '),
651
504
  parameters: SubagentParams,
652
505
 
653
506
  async execute(_toolCallId, params, signal, onUpdate, ctx) {
654
507
  const agentScope: AgentScope = params.agentScope ?? 'user';
655
- const resolvedPaths = await tryResolvePackagePaths(ctx.cwd);
656
- const discovery = discoverAgentsWithPackages(ctx.cwd, agentScope, resolvedPaths);
508
+ const resolvedPaths = await resolvePackagePaths(ctx.cwd);
509
+ const discovery = discoverAgents(ctx.cwd, agentScope, resolvedPaths);
657
510
  const agents = discovery.agents;
658
511
  const confirmProjectAgents = params.confirmProjectAgents ?? true;
659
512
 
@@ -667,7 +520,7 @@ export default function (pi: ExtensionAPI) {
667
520
  (results: SingleResult[]): SubagentDetails => ({
668
521
  mode,
669
522
  agentScope,
670
- projectAgentsDir: discovery.projectAgentsDir,
523
+ projectPromptsDir: discovery.projectPromptsDir,
671
524
  results,
672
525
  });
673
526
 
@@ -700,7 +553,7 @@ export default function (pi: ExtensionAPI) {
700
553
 
701
554
  if (projectAgentsRequested.length > 0) {
702
555
  const names = projectAgentsRequested.map((a) => a.name).join(', ');
703
- const dir = discovery.projectAgentsDir ?? '(unknown)';
556
+ const dir = discovery.projectPromptsDir ?? '(unknown)';
704
557
  const ok = await ctx.ui.confirm(
705
558
  'Run project-local agents?',
706
559
  `Agents: ${names}\nSource: ${dir}\n\nProject agents are repo-controlled. Only continue for trusted repositories.`,
@@ -736,6 +589,10 @@ export default function (pi: ExtensionAPI) {
736
589
  }
737
590
  : undefined;
738
591
 
592
+ if (ctx.hasUI) {
593
+ ctx.ui.setWorkingMessage(`Chain step ${i + 1}/${params.chain.length}: ${step.agent}`);
594
+ }
595
+
739
596
  const result = await runSingleAgent(
740
597
  ctx.cwd,
741
598
  agents,
@@ -782,6 +639,9 @@ export default function (pi: ExtensionAPI) {
782
639
  }),
783
640
  );
784
641
  }
642
+ if (ctx.hasUI) {
643
+ ctx.ui.setWorkingMessage();
644
+ }
785
645
  return {
786
646
  content: [
787
647
  {
@@ -845,6 +705,10 @@ export default function (pi: ExtensionAPI) {
845
705
  }
846
706
  };
847
707
 
708
+ if (ctx.hasUI) {
709
+ ctx.ui.setWorkingMessage(`Running ${params.tasks.length} agents in parallel`);
710
+ }
711
+
848
712
  const results = await mapWithConcurrencyLimit(
849
713
  params.tasks,
850
714
  MAX_CONCURRENCY,
@@ -874,6 +738,10 @@ export default function (pi: ExtensionAPI) {
874
738
  },
875
739
  );
876
740
 
741
+ if (ctx.hasUI) {
742
+ ctx.ui.setWorkingMessage();
743
+ }
744
+
877
745
  const successCount = results.filter((r) => r.exitCode === 0).length;
878
746
  const summaries = results.map((r) => {
879
747
  const output = getFinalOutput(r.messages);
@@ -891,6 +759,10 @@ export default function (pi: ExtensionAPI) {
891
759
  }
892
760
 
893
761
  if (params.agent && params.task) {
762
+ if (ctx.hasUI) {
763
+ ctx.ui.setWorkingMessage(`Running agent: ${params.agent}`);
764
+ }
765
+
894
766
  const result = await runSingleAgent(
895
767
  ctx.cwd,
896
768
  agents,
@@ -904,6 +776,10 @@ export default function (pi: ExtensionAPI) {
904
776
  onUpdate,
905
777
  makeDetails('single'),
906
778
  );
779
+ if (ctx.hasUI) {
780
+ ctx.ui.setWorkingMessage();
781
+ }
782
+
907
783
  const isError =
908
784
  result.exitCode !== 0 || result.stopReason === 'error' || result.stopReason === 'aborted';
909
785
  if (isError) {
@@ -1271,13 +1147,13 @@ export default function (pi: ExtensionAPI) {
1271
1147
  async function confirmProjectAgentsIfNeeded(
1272
1148
  ctx: ExtensionCommandContext,
1273
1149
  agents: AgentConfig[],
1274
- projectAgentsDir: string | null,
1150
+ projectPromptsDir: string | null,
1275
1151
  confirmProjectAgents: boolean,
1276
1152
  ): Promise<boolean> {
1277
1153
  if (!ctx.hasUI || !confirmProjectAgents || agents.length === 0) return true;
1278
1154
 
1279
1155
  const names = Array.from(new Set(agents.map((agent) => agent.name))).join(', ');
1280
- const dir = projectAgentsDir ?? '(unknown)';
1156
+ const dir = projectPromptsDir ?? '(unknown)';
1281
1157
  return ctx.ui.confirm(
1282
1158
  'Run project-local agents?',
1283
1159
  `Agents: ${names}\nSource: ${dir}\n\nProject agents are repo-controlled. Only continue for trusted repositories.`,
@@ -1392,157 +1268,10 @@ export default function (pi: ExtensionAPI) {
1392
1268
  return box;
1393
1269
  });
1394
1270
 
1395
- async function listMdFiles(dir: string): Promise<Set<string>> {
1396
- if (!existsSync(dir)) return new Set();
1397
- try {
1398
- const entries = await readdir(dir);
1399
- return new Set(entries.filter((f) => f.endsWith('.md')).map((f) => f.replace(/\.md$/, '')));
1400
- } catch {
1401
- return new Set();
1402
- }
1403
- }
1404
-
1405
1271
  pi.on('session_start', async (_event, ctx) => {
1406
1272
  autocompleteCwd = ctx.cwd;
1407
1273
  });
1408
1274
 
1409
- pi.registerCommand('delegate-agents', {
1410
- description: 'List, customize, or reset delegate agents',
1411
- getArgumentCompletions: async (argumentText) => {
1412
- const userDir = join(getAgentDir(), 'agents');
1413
- const { completedTokens } = parseArgumentText(argumentText);
1414
- const action = completedTokens[0];
1415
-
1416
- if (!action) {
1417
- return buildArgumentCompletions(argumentText, [
1418
- { value: 'list', description: 'Show bundled and overridden agents' },
1419
- { value: 'reset', description: 'Restore bundled agents by removing user overrides' },
1420
- { value: 'edit', description: 'Copy a bundled agent into your user agents directory' },
1421
- ]);
1422
- }
1423
-
1424
- if (action === 'reset') {
1425
- if (completedTokens.length > 1) return null;
1426
- const bundled = await listMdFiles(bundledAgentsDir);
1427
- const user = await listMdFiles(userDir);
1428
- const resettable = [...user].filter((name) => bundled.has(name)).sort();
1429
- return buildArgumentCompletions(argumentText, [
1430
- { value: '--all', description: 'Reset all user overrides that have bundled versions' },
1431
- ...resettable.map((name) => ({
1432
- value: name,
1433
- description: 'Reset this user override to the bundled version',
1434
- })),
1435
- ]);
1436
- }
1437
-
1438
- if (action === 'edit') {
1439
- if (completedTokens.length > 1) return null;
1440
- const bundled = [...(await listMdFiles(bundledAgentsDir))].sort();
1441
- return buildArgumentCompletions(
1442
- argumentText,
1443
- bundled.map((name) => ({
1444
- value: name,
1445
- description: 'Copy this bundled agent into your user agents directory',
1446
- })),
1447
- );
1448
- }
1449
-
1450
- return null;
1451
- },
1452
- handler: async (args, ctx) => {
1453
- autocompleteCwd = ctx.cwd;
1454
- const userDir = join(getAgentDir(), 'agents');
1455
- const parts = args?.trim().split(/\s+/) || [];
1456
- const action = parts[0] || 'list';
1457
-
1458
- if (action === 'list') {
1459
- const bundled = await listMdFiles(bundledAgentsDir);
1460
- const user = await listMdFiles(userDir);
1461
- const allNames = new Set([...bundled, ...user]);
1462
-
1463
- const lines: string[] = ['## Delegate Agents\n'];
1464
- for (const name of [...allNames].sort()) {
1465
- const isBundled = bundled.has(name);
1466
- const isUser = user.has(name);
1467
- if (isUser && isBundled) {
1468
- lines.push(`- **${name}** — user override (bundled version available, \`/delegate-agents reset ${name}\` to restore)`);
1469
- } else if (isUser) {
1470
- lines.push(`- **${name}** — user-only`);
1471
- } else {
1472
- lines.push(`- **${name}** — bundled`);
1473
- }
1474
- }
1475
- pi.sendMessage({ customType: 'delegate-agents-list', content: lines.join('\n'), display: true });
1476
- return;
1477
- }
1478
-
1479
- if (action === 'reset') {
1480
- const name = parts[1];
1481
- if (!name) {
1482
- ctx.ui.notify('Usage: /delegate-agents reset <name|--all>', 'warning');
1483
- return;
1484
- }
1485
-
1486
- if (name === '--all') {
1487
- const user = await listMdFiles(userDir);
1488
- const bundled = await listMdFiles(bundledAgentsDir);
1489
- let count = 0;
1490
- for (const n of user) {
1491
- if (bundled.has(n)) {
1492
- await unlink(join(userDir, `${n}.md`));
1493
- count++;
1494
- }
1495
- }
1496
- ctx.ui.notify(count > 0 ? `Reset ${count} agent(s) to bundled versions.` : 'No user overrides to reset.', 'info');
1497
- return;
1498
- }
1499
-
1500
- const userFile = join(userDir, `${name}.md`);
1501
- const bundledFile = join(bundledAgentsDir, `${name}.md`);
1502
-
1503
- if (!existsSync(userFile)) {
1504
- ctx.ui.notify(`No user override for "${name}" — already using bundled version.`, 'info');
1505
- return;
1506
- }
1507
- if (!existsSync(bundledFile)) {
1508
- ctx.ui.notify(`"${name}" is user-only (no bundled version). Delete manually if needed.`, 'warning');
1509
- return;
1510
- }
1511
-
1512
- await unlink(userFile);
1513
- ctx.ui.notify(`Reset "${name}" — now using bundled version.`, 'info');
1514
- return;
1515
- }
1516
-
1517
- if (action === 'edit') {
1518
- const name = parts[1];
1519
- if (!name) {
1520
- ctx.ui.notify('Usage: /delegate-agents edit <name>', 'warning');
1521
- return;
1522
- }
1523
-
1524
- const bundledFile = join(bundledAgentsDir, `${name}.md`);
1525
- const userFile = join(userDir, `${name}.md`);
1526
-
1527
- if (existsSync(userFile)) {
1528
- ctx.ui.notify(`User override already exists: ${userFile}`, 'info');
1529
- return;
1530
- }
1531
- if (!existsSync(bundledFile)) {
1532
- ctx.ui.notify(`No bundled agent named "${name}".`, 'warning');
1533
- return;
1534
- }
1535
-
1536
- await mkdir(userDir, { recursive: true });
1537
- await copyFile(bundledFile, userFile);
1538
- ctx.ui.notify(`Copied "${name}" to ${userFile} — edit it there to customize.`, 'info');
1539
- return;
1540
- }
1541
-
1542
- ctx.ui.notify('Unknown action. Usage: /delegate-agents [list|reset <name|--all>|edit <name>]', 'warning');
1543
- },
1544
- });
1545
-
1546
1275
  pi.registerCommand('run-agent', {
1547
1276
  description:
1548
1277
  'Run a single agent directly — honors agent sessionStrategy metadata such as fork-at',
@@ -1589,8 +1318,8 @@ export default function (pi: ExtensionAPI) {
1589
1318
 
1590
1319
  if (expectsScopeValue) {
1591
1320
  return buildArgumentCompletions(argumentText, [
1592
- { value: 'user', description: 'Only user/global agents from ~/.pi/agent/agents' },
1593
- { value: 'project', description: 'Only project-local agents from .pi/agents' },
1321
+ { value: 'user', description: 'Only user/global agents from ~/.pi/agent/prompts' },
1322
+ { value: 'project', description: 'Only project-local agents from .pi/prompts' },
1594
1323
  { value: 'both', description: 'User/global agents plus project-local agents' },
1595
1324
  ]);
1596
1325
  }
@@ -1640,8 +1369,8 @@ export default function (pi: ExtensionAPI) {
1640
1369
  return;
1641
1370
  }
1642
1371
 
1643
- const resolvedPaths = await tryResolvePackagePaths(ctx.cwd);
1644
- const discovery = discoverAgentsWithPackages(ctx.cwd, agentScope, resolvedPaths);
1372
+ const resolvedPaths = await resolvePackagePaths(ctx.cwd);
1373
+ const discovery = discoverAgents(ctx.cwd, agentScope, resolvedPaths);
1645
1374
  const agent = discovery.agents.find((candidate) => candidate.name === agentName);
1646
1375
 
1647
1376
  if (!agent) {
@@ -1654,7 +1383,7 @@ export default function (pi: ExtensionAPI) {
1654
1383
  const approved = await confirmProjectAgentsIfNeeded(
1655
1384
  ctx,
1656
1385
  requestedProjectAgents,
1657
- discovery.projectAgentsDir,
1386
+ discovery.projectPromptsDir,
1658
1387
  confirmProjectAgents,
1659
1388
  );
1660
1389
  if (!approved) {