@dreki-gg/pi-subagent 0.5.0 → 0.6.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/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @dreki-gg/pi-subagent
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`5e853af`](https://github.com/dreki-gg/pi-extensions/commit/5e853af054a31c4bf87d80f944513e537a39201d) Thanks [@jalbarrang](https://github.com/jalbarrang)! - Sync the extensions repo with Pi 0.68.0 and improve direct agent runs.
8
+
9
+ - `@dreki-gg/pi-context7`: remove stale alias docs and align compatibility tests with the canonical tool names actually exported.
10
+ - `@dreki-gg/pi-modes`: use `before_agent_start.systemPromptOptions.selectedTools` when available so mode prompt text reflects the active prompt tool set.
11
+ - `@dreki-gg/pi-subagent`: add `/run-agent`, support `sessionStrategy: fork-at` in agent frontmatter, default bundled `worker` and `reviewer` to forked direct runs, and add a custom renderer for run summaries.
12
+
3
13
  ## 0.5.0
4
14
 
5
15
  ### Minor Changes
package/README.md CHANGED
@@ -28,6 +28,12 @@ Examples:
28
28
  - "have planner make a plan, then worker implement it"
29
29
  - "send this to reviewer"
30
30
 
31
+ For direct user-invoked single-agent runs, use:
32
+
33
+ ```text
34
+ /run-agent worker implement the auth flow we discussed
35
+ ```
36
+
31
37
  `/delegate` is optional. It exists for rigid gated workflows, plan approval, and explicit workflow control.
32
38
 
33
39
  ## Optional Delegate Command
@@ -65,6 +71,24 @@ Pin a workflow and skip the project-agent confirmation prompt:
65
71
  5. **Plan gate** — shows planner output for approval before worker runs
66
72
  6. **Summary** — full phase-by-phase report with usage totals
67
73
 
74
+ ## Direct Agent Runs
75
+
76
+ Run one agent directly from the current session:
77
+
78
+ ```text
79
+ /run-agent [--scope user|project|both] [--yes-project-agents] <agent> [task]
80
+ ```
81
+
82
+ Examples:
83
+
84
+ ```text
85
+ /run-agent scout trace how auth state is loaded
86
+ /run-agent worker implement the refactor we just planned
87
+ /run-agent --scope project reviewer review the latest changes
88
+ ```
89
+
90
+ If the chosen agent frontmatter sets `sessionStrategy: fork-at`, the command clones the current active path into a new session before running the agent. That keeps long implementation runs isolated in their own branch while preserving the original conversation.
91
+
68
92
  ### Delegate Flags
69
93
 
70
94
  | Flag | Meaning |
@@ -94,6 +118,7 @@ name: my-agent
94
118
  description: What this agent does
95
119
  tools: read, grep, find, ls
96
120
  model: anthropic/claude-haiku-4-5
121
+ sessionStrategy: fork-at
97
122
  ---
98
123
 
99
124
  System prompt for the agent.
@@ -106,13 +131,17 @@ The package ships with these agents out of the box:
106
131
  - `scout` — fast codebase recon
107
132
  - `docs-scout` — Context7-first documentation lookup
108
133
  - `planner` — implementation planning
109
- - `worker` — general-purpose implementation
110
- - `reviewer` — code review
134
+ - `worker` — general-purpose implementation (`sessionStrategy: fork-at` by default)
135
+ - `reviewer` — code review (`sessionStrategy: fork-at` by default)
111
136
  - `ux-designer` — frontend UI design
112
137
 
113
138
  Resolution order is: bundled → user (`~/.pi/agent/agents/`) → project (`.pi/agents/`). Project agents override user and bundled agents by name.
114
139
 
115
- > Note: pi packages do not currently expose a first-class `agents` manifest. If you mirror package agents into `~/.pi/agent/agents/`, treat those files as managed package artifacts rather than hand-authored overrides.
140
+ Optional frontmatter:
141
+ - `thinking` — reasoning effort for the spawned pi process
142
+ - `sessionStrategy: fork-at` — when used with `/run-agent`, clone the current active branch into a new session before running
143
+
144
+ > Note: pi now supports package-shipped agents via `pi.agents` (or conventional `agents/` directories). This package publishes its bundled agents that way, while user agents in `~/.pi/agent/agents/` and project agents in `.pi/agents/` still override them by name.
116
145
 
117
146
  ### Managing Agents
118
147
 
@@ -4,6 +4,7 @@ description: Code review specialist for quality and security analysis
4
4
  tools: read, grep, find, ls, bash
5
5
  model: openai/gpt-5.4
6
6
  thinking: medium
7
+ sessionStrategy: fork-at
7
8
  ---
8
9
 
9
10
  You are a senior code reviewer. Analyze code for quality, security, and maintainability.
package/agents/worker.md CHANGED
@@ -3,6 +3,7 @@ name: worker
3
3
  description: General-purpose subagent with full capabilities, isolated context
4
4
  model: openai/gpt-5.4
5
5
  thinking: medium
6
+ sessionStrategy: fork-at
6
7
  ---
7
8
 
8
9
  You are a worker agent with full capabilities. You operate in an isolated context window to handle delegated tasks without polluting the main conversation.
@@ -16,6 +16,7 @@ import type { ResolvedPaths } from '@mariozechner/pi-coding-agent';
16
16
 
17
17
  export type AgentScope = 'user' | 'project' | 'both';
18
18
  export type AgentSource = 'bundled' | 'user' | 'project' | 'package';
19
+ export type AgentSessionStrategy = 'inline' | 'fork-at';
19
20
 
20
21
  export interface AgentConfig {
21
22
  name: string;
@@ -23,6 +24,7 @@ export interface AgentConfig {
23
24
  tools?: string[];
24
25
  model?: string;
25
26
  thinking?: string;
27
+ sessionStrategy?: AgentSessionStrategy;
26
28
  systemPrompt: string;
27
29
  source: AgentSource;
28
30
  filePath: string;
@@ -36,6 +38,13 @@ export interface AgentDiscoveryResult {
36
38
  /** Bundled agents ship with the package (../../agents relative to extensions/subagent/) */
37
39
  const bundledAgentsDir = path.resolve(import.meta.dirname, '..', '..', 'agents');
38
40
 
41
+ function parseSessionStrategy(value?: string): AgentSessionStrategy | undefined {
42
+ if (!value) return undefined;
43
+ const normalized = value.trim().toLowerCase();
44
+ if (normalized === 'inline' || normalized === 'fork-at') return normalized;
45
+ return undefined;
46
+ }
47
+
39
48
  function loadAgentsFromDir(dir: string, source: AgentSource): AgentConfig[] {
40
49
  const agents: AgentConfig[] = [];
41
50
 
@@ -79,6 +88,7 @@ function loadAgentsFromDir(dir: string, source: AgentSource): AgentConfig[] {
79
88
  tools: tools && tools.length > 0 ? tools : undefined,
80
89
  model: frontmatter.model,
81
90
  thinking: frontmatter.thinking,
91
+ sessionStrategy: parseSessionStrategy(frontmatter.sessionStrategy),
82
92
  systemPrompt: body,
83
93
  source,
84
94
  filePath,
@@ -152,6 +162,7 @@ function loadAgentFromFile(filePath: string, source: AgentSource): AgentConfig |
152
162
  tools: tools && tools.length > 0 ? tools : undefined,
153
163
  model: frontmatter.model,
154
164
  thinking: frontmatter.thinking,
165
+ sessionStrategy: parseSessionStrategy(frontmatter.sessionStrategy),
155
166
  systemPrompt: body,
156
167
  source,
157
168
  filePath,
@@ -8,6 +8,13 @@ export interface DelegateCommandOptions {
8
8
  explicitTask?: string;
9
9
  }
10
10
 
11
+ export interface RunAgentCommandOptions {
12
+ agentScope: AgentScope;
13
+ confirmProjectAgents: boolean;
14
+ agentName?: string;
15
+ explicitTask?: string;
16
+ }
17
+
11
18
  const WORKFLOW_IDS = new Set<WorkflowId>([
12
19
  'scout-only',
13
20
  'scout-and-plan',
@@ -82,3 +89,57 @@ export function parseDelegateArgs(rawArgs?: string):
82
89
  },
83
90
  };
84
91
  }
92
+
93
+ export function formatRunAgentUsage(): string {
94
+ return 'Usage: /run-agent [--scope user|project|both] [--yes-project-agents] <agent> [task]';
95
+ }
96
+
97
+ export function parseRunAgentArgs(rawArgs?: string):
98
+ | { ok: true; options: RunAgentCommandOptions }
99
+ | { ok: false; error: string } {
100
+ const tokens = tokenize(rawArgs?.trim() ?? '');
101
+ const taskTokens: string[] = [];
102
+
103
+ let agentScope: AgentScope = 'user';
104
+ let confirmProjectAgents = true;
105
+ let agentName: string | undefined;
106
+
107
+ for (let i = 0; i < tokens.length; i++) {
108
+ const token = tokens[i];
109
+
110
+ if (token === '--scope') {
111
+ const value = tokens[++i];
112
+ if (!value || !['user', 'project', 'both'].includes(value)) {
113
+ return { ok: false, error: `Invalid --scope value.\n\n${formatRunAgentUsage()}` };
114
+ }
115
+ agentScope = value as AgentScope;
116
+ continue;
117
+ }
118
+
119
+ if (token === '--yes-project-agents') {
120
+ confirmProjectAgents = false;
121
+ continue;
122
+ }
123
+
124
+ if (token.startsWith('--')) {
125
+ return { ok: false, error: `Unknown option: ${token}\n\n${formatRunAgentUsage()}` };
126
+ }
127
+
128
+ if (!agentName) {
129
+ agentName = token;
130
+ continue;
131
+ }
132
+
133
+ taskTokens.push(token);
134
+ }
135
+
136
+ return {
137
+ ok: true,
138
+ options: {
139
+ agentScope,
140
+ confirmProjectAgents,
141
+ agentName,
142
+ explicitTask: taskTokens.join(' ').trim() || undefined,
143
+ },
144
+ };
145
+ }
@@ -32,7 +32,7 @@ import {
32
32
  withFileMutationQueue,
33
33
  } from '@mariozechner/pi-coding-agent';
34
34
  import type { ResolvedPaths } from '@mariozechner/pi-coding-agent';
35
- import { Container, Markdown, Spacer, Text } from '@mariozechner/pi-tui';
35
+ import { Box, Container, Markdown, Spacer, Text } from '@mariozechner/pi-tui';
36
36
  import { Type } from '@sinclair/typebox';
37
37
  import {
38
38
  type AgentConfig,
@@ -42,7 +42,12 @@ import {
42
42
  discoverAgents,
43
43
  discoverAgentsWithPackages,
44
44
  } from './agents.js';
45
- import { formatDelegateUsage, parseDelegateArgs } from './delegate-args.js';
45
+ import {
46
+ formatDelegateUsage,
47
+ formatRunAgentUsage,
48
+ parseDelegateArgs,
49
+ parseRunAgentArgs,
50
+ } from './delegate-args.js';
46
51
  import { runAgent, runParallel } from './delegate-executor.js';
47
52
  import { buildSynthesisPrompt, extractRecentConversation } from './synthesis.js';
48
53
  import type {
@@ -182,6 +187,20 @@ interface UsageStats {
182
187
  turns: number;
183
188
  }
184
189
 
190
+ interface RunAgentSummaryDetails {
191
+ agent: string;
192
+ agentSource: AgentSource;
193
+ agentScope: AgentScope;
194
+ sessionStrategy: string;
195
+ forked: boolean;
196
+ exitCode: number;
197
+ stopReason?: string;
198
+ errorMessage?: string;
199
+ usage: UsageStats;
200
+ task: string;
201
+ output: string;
202
+ }
203
+
185
204
  interface SingleResult {
186
205
  agent: string;
187
206
  agentSource: AgentSource | 'unknown';
@@ -1160,6 +1179,130 @@ export default function (pi: ExtensionAPI) {
1160
1179
  .join('\n\n');
1161
1180
  }
1162
1181
 
1182
+ async function confirmProjectAgentsIfNeeded(
1183
+ ctx: ExtensionCommandContext,
1184
+ agents: AgentConfig[],
1185
+ projectAgentsDir: string | null,
1186
+ confirmProjectAgents: boolean,
1187
+ ): Promise<boolean> {
1188
+ if (!ctx.hasUI || !confirmProjectAgents || agents.length === 0) return true;
1189
+
1190
+ const names = Array.from(new Set(agents.map((agent) => agent.name))).join(', ');
1191
+ const dir = projectAgentsDir ?? '(unknown)';
1192
+ return ctx.ui.confirm(
1193
+ 'Run project-local agents?',
1194
+ `Agents: ${names}\nSource: ${dir}\n\nProject agents are repo-controlled. Only continue for trusted repositories.`,
1195
+ );
1196
+ }
1197
+
1198
+ function buildRunAgentTask(conversation: string, explicitTask?: string): string | undefined {
1199
+ const trimmedConversation = conversation.trim();
1200
+ const trimmedTask = explicitTask?.trim();
1201
+
1202
+ if (trimmedTask && trimmedConversation) {
1203
+ return `Assigned task:\n${trimmedTask}\n\nRelevant main-session context:\n${trimmedConversation}`;
1204
+ }
1205
+
1206
+ if (trimmedTask) return trimmedTask;
1207
+ if (trimmedConversation) {
1208
+ return `Use the following main-session context to determine and complete the task:\n\n${trimmedConversation}`;
1209
+ }
1210
+
1211
+ return undefined;
1212
+ }
1213
+
1214
+ function formatRunAgentSummary(options: {
1215
+ agent: AgentConfig;
1216
+ agentScope: AgentScope;
1217
+ task: string;
1218
+ result: AgentResult;
1219
+ forked: boolean;
1220
+ }): string {
1221
+ const { agent, agentScope, task, result, forked } = options;
1222
+ const output = getFinalText(result).trim();
1223
+ const usage = formatUsageStats(result.usage, result.model);
1224
+ const failed = result.exitCode !== 0 || result.stopReason === 'error';
1225
+ const status = failed ? 'failed' : 'completed';
1226
+ const lines = [
1227
+ '## Agent Run',
1228
+ `- Agent: \`${agent.name}\``,
1229
+ `- Source: ${agent.source}`,
1230
+ `- Scope: ${agentScope}`,
1231
+ `- Session strategy: ${agent.sessionStrategy ?? 'inline'}`,
1232
+ `- Session handling: ${forked ? 'forked current branch at active entry' : 'inline in current session'}`,
1233
+ `- Status: ${status}`,
1234
+ '',
1235
+ '## Task',
1236
+ task,
1237
+ '',
1238
+ '## Output',
1239
+ output || '_No output returned._',
1240
+ ];
1241
+
1242
+ if (failed) {
1243
+ const errorText = result.errorMessage?.trim() || result.stderr.trim();
1244
+ if (errorText) {
1245
+ lines.push('', '## Error', errorText);
1246
+ }
1247
+ }
1248
+
1249
+ if (usage) {
1250
+ lines.push('', '## Usage', usage);
1251
+ }
1252
+
1253
+ return lines.join('\n');
1254
+ }
1255
+
1256
+ pi.registerMessageRenderer('run-agent-summary', (message, { expanded }, theme) => {
1257
+ const details = message.details as RunAgentSummaryDetails | undefined;
1258
+ if (!details) return new Text(typeof message.content === 'string' ? message.content : '', 0, 0);
1259
+
1260
+ const failed = details.exitCode !== 0 || details.stopReason === 'error';
1261
+ const icon = failed ? theme.fg('error', '✗') : theme.fg('success', '✓');
1262
+ const title = `${icon} ${theme.fg('toolTitle', theme.bold(details.agent))}${theme.fg('muted', ` (${details.agentSource})`)}`;
1263
+ const modeLine = theme.fg(
1264
+ 'dim',
1265
+ `${details.forked ? 'forked branch' : 'inline'} • ${details.agentScope} • ${details.sessionStrategy}`,
1266
+ );
1267
+ const usageLine = formatUsageStats(details.usage);
1268
+ const outputPreview = details.output.trim() || '(no output)';
1269
+
1270
+ if (!expanded) {
1271
+ const previewLines = outputPreview.split('\n').slice(0, 3).join('\n');
1272
+ const text = [
1273
+ title,
1274
+ modeLine,
1275
+ theme.fg('muted', `Task: ${details.task}`),
1276
+ failed && details.errorMessage ? theme.fg('error', `Error: ${details.errorMessage}`) : previewLines,
1277
+ usageLine ? theme.fg('dim', usageLine) : undefined,
1278
+ theme.fg('muted', '(Ctrl+O to expand)'),
1279
+ ]
1280
+ .filter((line): line is string => Boolean(line))
1281
+ .join('\n');
1282
+ const box = new Box(1, 1, (text) => theme.bg('customMessageBg', text));
1283
+ box.addChild(new Text(text, 0, 0));
1284
+ return box;
1285
+ }
1286
+
1287
+ const mdTheme = getMarkdownTheme();
1288
+ const box = new Box(1, 1, (text) => theme.bg('customMessageBg', text));
1289
+ box.addChild(new Text(title, 0, 0));
1290
+ box.addChild(new Text(modeLine, 0, 0));
1291
+ if (usageLine) box.addChild(new Text(theme.fg('dim', usageLine), 0, 0));
1292
+ box.addChild(new Spacer(1));
1293
+ box.addChild(new Text(theme.fg('muted', 'Task'), 0, 0));
1294
+ box.addChild(new Text(theme.fg('text', details.task), 0, 0));
1295
+ if (failed && details.errorMessage) {
1296
+ box.addChild(new Spacer(1));
1297
+ box.addChild(new Text(theme.fg('error', 'Error'), 0, 0));
1298
+ box.addChild(new Text(theme.fg('error', details.errorMessage), 0, 0));
1299
+ }
1300
+ box.addChild(new Spacer(1));
1301
+ box.addChild(new Text(theme.fg('muted', 'Output'), 0, 0));
1302
+ box.addChild(new Markdown(outputPreview, 0, 0, mdTheme));
1303
+ return box;
1304
+ });
1305
+
1163
1306
  async function executePhase(
1164
1307
  cwd: string,
1165
1308
  phase: PhaseDefinition,
@@ -1328,6 +1471,114 @@ export default function (pi: ExtensionAPI) {
1328
1471
  },
1329
1472
  });
1330
1473
 
1474
+ pi.registerCommand('run-agent', {
1475
+ description:
1476
+ 'Run a single agent directly — honors agent sessionStrategy metadata such as fork-at',
1477
+ handler: async (args, ctx) => {
1478
+ const parsed = parseRunAgentArgs(args);
1479
+ if (!parsed.ok) {
1480
+ ctx.ui.notify(parsed.error, 'warning');
1481
+ return;
1482
+ }
1483
+
1484
+ const { agentName, explicitTask, agentScope, confirmProjectAgents } = parsed.options;
1485
+ if (!agentName) {
1486
+ ctx.ui.notify(formatRunAgentUsage(), 'warning');
1487
+ return;
1488
+ }
1489
+
1490
+ const resolvedPaths = await tryResolvePackagePaths(ctx.cwd);
1491
+ const discovery = discoverAgentsWithPackages(ctx.cwd, agentScope, resolvedPaths);
1492
+ const agent = discovery.agents.find((candidate) => candidate.name === agentName);
1493
+
1494
+ if (!agent) {
1495
+ const available = discovery.agents.map((candidate) => candidate.name).join(', ') || 'none';
1496
+ ctx.ui.notify(`Unknown agent: "${agentName}". Available: ${available}`, 'warning');
1497
+ return;
1498
+ }
1499
+
1500
+ const requestedProjectAgents = agent.source === 'project' ? [agent] : [];
1501
+ const approved = await confirmProjectAgentsIfNeeded(
1502
+ ctx,
1503
+ requestedProjectAgents,
1504
+ discovery.projectAgentsDir,
1505
+ confirmProjectAgents,
1506
+ );
1507
+ if (!approved) {
1508
+ ctx.ui.notify('Run cancelled — project-local agents not approved.', 'info');
1509
+ return;
1510
+ }
1511
+
1512
+ const conversation = extractRecentConversation(ctx);
1513
+ const task = buildRunAgentTask(conversation, explicitTask);
1514
+ if (!task) {
1515
+ ctx.ui.notify(`No conversation context and no task specified.\n\n${formatRunAgentUsage()}`, 'warning');
1516
+ return;
1517
+ }
1518
+
1519
+ let forked = false;
1520
+ if (agent.sessionStrategy === 'fork-at') {
1521
+ const leafId = ctx.sessionManager.getLeafId?.();
1522
+ if (!leafId) {
1523
+ ctx.ui.notify(
1524
+ `Agent "${agent.name}" prefers fork-at, but the current session cannot be forked. Running inline instead.`,
1525
+ 'warning',
1526
+ );
1527
+ } else {
1528
+ const forkCompat = ctx.fork as unknown as (
1529
+ entryId: string,
1530
+ options?: { position: 'at' },
1531
+ ) => Promise<{ cancelled: boolean }>;
1532
+ const forkResult = await forkCompat(leafId, { position: 'at' });
1533
+ if (forkResult.cancelled) {
1534
+ ctx.ui.notify(`Run cancelled — unable to fork session for "${agent.name}".`, 'info');
1535
+ return;
1536
+ }
1537
+ forked = true;
1538
+ }
1539
+ }
1540
+
1541
+ ctx.ui.notify(
1542
+ `Running agent: ${agent.name} [${agent.source}${forked ? ', forked' : ', inline'}]`,
1543
+ 'info',
1544
+ );
1545
+
1546
+ const result = await runAgent(ctx.cwd, agent.name, task, { agentScope, resolvedPaths });
1547
+ const failed = result.exitCode !== 0 || result.stopReason === 'error';
1548
+ const output = getFinalText(result).trim();
1549
+
1550
+ ctx.ui.notify(
1551
+ failed ? `Agent "${agent.name}" failed.` : `Agent "${agent.name}" complete.`,
1552
+ failed ? 'error' : 'info',
1553
+ );
1554
+
1555
+ pi.sendMessage({
1556
+ customType: 'run-agent-summary',
1557
+ content: formatRunAgentSummary({
1558
+ agent,
1559
+ agentScope,
1560
+ task,
1561
+ result,
1562
+ forked,
1563
+ }),
1564
+ display: true,
1565
+ details: {
1566
+ agent: agent.name,
1567
+ agentSource: agent.source,
1568
+ agentScope,
1569
+ sessionStrategy: agent.sessionStrategy ?? 'inline',
1570
+ forked,
1571
+ exitCode: result.exitCode,
1572
+ stopReason: result.stopReason,
1573
+ errorMessage: result.errorMessage,
1574
+ usage: result.usage,
1575
+ task,
1576
+ output,
1577
+ } satisfies RunAgentSummaryDetails,
1578
+ });
1579
+ },
1580
+ });
1581
+
1331
1582
  pi.registerCommand('delegate', {
1332
1583
  description:
1333
1584
  'Orchestrate subagent workflows — supports --scope, --workflow, and project-local agents',
@@ -1379,24 +1630,22 @@ export default function (pi: ExtensionAPI) {
1379
1630
  return;
1380
1631
  }
1381
1632
 
1382
- if ((agentScope === 'project' || agentScope === 'both') && confirmProjectAgents && ctx.hasUI) {
1633
+ if (agentScope === 'project' || agentScope === 'both') {
1383
1634
  const discovery = discoverAgentsWithPackages(ctx.cwd, agentScope, resolvedPaths);
1384
1635
  const requestedProjectAgents = workflow.phases
1385
1636
  .flatMap((phase) => phase.agents)
1386
1637
  .map((name) => discovery.agents.find((agent) => agent.name === name))
1387
1638
  .filter((agent): agent is AgentConfig => agent?.source === 'project');
1388
1639
 
1389
- if (requestedProjectAgents.length > 0) {
1390
- const names = Array.from(new Set(requestedProjectAgents.map((agent) => agent.name))).join(', ');
1391
- const dir = discovery.projectAgentsDir ?? '(unknown)';
1392
- const ok = await ctx.ui.confirm(
1393
- 'Run project-local agents?',
1394
- `Agents: ${names}\nSource: ${dir}\n\nProject agents are repo-controlled. Only continue for trusted repositories.`,
1395
- );
1396
- if (!ok) {
1397
- ctx.ui.notify('Delegate cancelled — project-local agents not approved.', 'info');
1398
- return;
1399
- }
1640
+ const ok = await confirmProjectAgentsIfNeeded(
1641
+ ctx,
1642
+ requestedProjectAgents,
1643
+ discovery.projectAgentsDir,
1644
+ confirmProjectAgents,
1645
+ );
1646
+ if (!ok) {
1647
+ ctx.ui.notify('Delegate cancelled — project-local agents not approved.', 'info');
1648
+ return;
1400
1649
  }
1401
1650
  }
1402
1651
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dreki-gg/pi-subagent",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Subagent tool and delegate orchestration for pi — isolated agents, parallel scouts, planning gates, and workflow presets",
5
5
  "keywords": [
6
6
  "pi-package"