@dreki-gg/pi-subagent 0.8.4 → 0.9.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,11 @@
|
|
|
1
1
|
# @dreki-gg/pi-subagent
|
|
2
2
|
|
|
3
|
+
## 0.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`99bc4e7`](https://github.com/dreki-gg/pi-extensions/commit/99bc4e7abb9f468ebd6705a56ed1c2a801dd466e) Thanks [@jalbarrang](https://github.com/jalbarrang)! - Add `list_agents` tool and `/create-agent` command to help the LLM discover available agents before spawning and scaffold new project-local agent prompts.
|
|
8
|
+
|
|
3
9
|
## 0.8.4
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* /create-agent command — scaffolds a new project-local agent prompt in .pi/prompts/.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as fs from 'node:fs';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
8
|
+
|
|
9
|
+
const TEMPLATE = `---
|
|
10
|
+
name: {{name}}
|
|
11
|
+
description: {{description}}
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
You are the **{{name}}** agent.
|
|
15
|
+
|
|
16
|
+
## Role
|
|
17
|
+
|
|
18
|
+
{{description}}
|
|
19
|
+
|
|
20
|
+
## Guidelines
|
|
21
|
+
|
|
22
|
+
- Be concise and focused on your assigned task.
|
|
23
|
+
- Use available tools to gather context before acting.
|
|
24
|
+
- Report your findings or output clearly.
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
function slugify(name: string): string {
|
|
28
|
+
return name
|
|
29
|
+
.toLowerCase()
|
|
30
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
31
|
+
.replace(/^-|-$/g, '');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function registerCreateAgentCommand(pi: ExtensionAPI) {
|
|
35
|
+
pi.registerCommand('create-agent', {
|
|
36
|
+
description:
|
|
37
|
+
'Create a new project-local agent prompt in .pi/prompts/. Usage: /create-agent <name> [description]',
|
|
38
|
+
|
|
39
|
+
handler: async (args, ctx) => {
|
|
40
|
+
if (!args?.trim()) {
|
|
41
|
+
ctx.ui.notify(
|
|
42
|
+
'Usage: /create-agent <name> [description]\n\nCreates a new agent prompt file in .pi/prompts/',
|
|
43
|
+
'warning',
|
|
44
|
+
);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const tokens = args.trim().split(/\s+/);
|
|
49
|
+
const name = slugify(tokens[0]);
|
|
50
|
+
const description = tokens.slice(1).join(' ') || `A project-local ${name} agent.`;
|
|
51
|
+
|
|
52
|
+
if (!name) {
|
|
53
|
+
ctx.ui.notify('Invalid agent name.', 'error');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const promptsDir = path.join(ctx.cwd, '.pi', 'prompts');
|
|
58
|
+
const filePath = path.join(promptsDir, `${name}.md`);
|
|
59
|
+
|
|
60
|
+
if (fs.existsSync(filePath)) {
|
|
61
|
+
ctx.ui.notify(
|
|
62
|
+
`Agent "${name}" already exists at ${filePath}`,
|
|
63
|
+
'warning',
|
|
64
|
+
);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Create directory if needed
|
|
69
|
+
fs.mkdirSync(promptsDir, { recursive: true });
|
|
70
|
+
|
|
71
|
+
// Write the template
|
|
72
|
+
const content = TEMPLATE
|
|
73
|
+
.replace(/\{\{name\}\}/g, name)
|
|
74
|
+
.replace(/\{\{description\}\}/g, description);
|
|
75
|
+
|
|
76
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
77
|
+
|
|
78
|
+
ctx.ui.notify(
|
|
79
|
+
`Created agent "${name}" at ${path.relative(ctx.cwd, filePath)}`,
|
|
80
|
+
'info',
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// Send a follow-up so the LLM can help refine the prompt
|
|
84
|
+
pi.sendUserMessage(
|
|
85
|
+
[
|
|
86
|
+
`I just created a new project-local agent prompt at \`${path.relative(ctx.cwd, filePath)}\`.`,
|
|
87
|
+
'',
|
|
88
|
+
`Agent name: **${name}**`,
|
|
89
|
+
`Description: ${description}`,
|
|
90
|
+
'',
|
|
91
|
+
'The file has a basic template. You can now read and refine the agent prompt to fit your needs.',
|
|
92
|
+
'Remember to use `agentScope: "both"` or `agentScope: "project"` when spawning this agent.',
|
|
93
|
+
].join('\n'),
|
|
94
|
+
{ deliverAs: 'followUp' },
|
|
95
|
+
);
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
}
|
|
@@ -44,6 +44,8 @@ import type { AgentResult } from './agent-runner-types.js';
|
|
|
44
44
|
import { getFinalText } from './agent-result-utils.js';
|
|
45
45
|
import { buildHandoffFromResult, renderHandoffForPrompt } from './handoffs.js';
|
|
46
46
|
import { emptyUsage, spawnPiAgent, type ToolExecutionStartEvent } from './spawn-utils.js';
|
|
47
|
+
import { registerListAgentsTool } from './list-agents.js';
|
|
48
|
+
import { registerCreateAgentCommand } from './create-agent.js';
|
|
47
49
|
|
|
48
50
|
const MAX_PARALLEL_TASKS = 8;
|
|
49
51
|
const MAX_CONCURRENCY = 4;
|
|
@@ -554,6 +556,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
554
556
|
return [...items.values()];
|
|
555
557
|
}
|
|
556
558
|
|
|
559
|
+
registerListAgentsTool(pi, resolvePackagePaths);
|
|
560
|
+
registerCreateAgentCommand(pi);
|
|
561
|
+
|
|
557
562
|
pi.registerTool({
|
|
558
563
|
name: 'subagent',
|
|
559
564
|
label: 'Subagent',
|
|
@@ -564,6 +569,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
564
569
|
'Default agent scope is "user" (from ~/.pi/agent/prompts).',
|
|
565
570
|
'To enable project-local agents in .pi/prompts, set agentScope: "both" (or "project").',
|
|
566
571
|
].join(' '),
|
|
572
|
+
promptGuidelines: [
|
|
573
|
+
'If you are unsure which agents are available, call list_agents first before using the subagent tool.',
|
|
574
|
+
'Use /create-agent to scaffold new project-local agent prompts when the user needs a custom agent.',
|
|
575
|
+
],
|
|
567
576
|
parameters: SubagentParams,
|
|
568
577
|
|
|
569
578
|
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* list_agents tool — lets the LLM discover available agents before spawning.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { ExtensionAPI, ResolvedPaths } from '@earendil-works/pi-coding-agent';
|
|
6
|
+
import { Type } from 'typebox';
|
|
7
|
+
import { type AgentScope, discoverAgents } from './agents.js';
|
|
8
|
+
import { StringEnum } from '@earendil-works/pi-ai';
|
|
9
|
+
|
|
10
|
+
export function registerListAgentsTool(
|
|
11
|
+
pi: ExtensionAPI,
|
|
12
|
+
resolvePackagePaths: (cwd: string) => Promise<ResolvedPaths | undefined>,
|
|
13
|
+
) {
|
|
14
|
+
const AgentScopeSchema = StringEnum(['user', 'project', 'both'] as const, {
|
|
15
|
+
description:
|
|
16
|
+
'Which agent directories to search. Default: "user". Use "both" to include project-local agents.',
|
|
17
|
+
default: 'user',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
pi.registerTool({
|
|
21
|
+
name: 'list_agents',
|
|
22
|
+
label: 'List Agents',
|
|
23
|
+
description:
|
|
24
|
+
'List available subagent prompts. Call this before spawning agents if you are unsure which agents exist.',
|
|
25
|
+
promptSnippet: 'List available subagent prompts to discover what agents can be spawned',
|
|
26
|
+
promptGuidelines: [
|
|
27
|
+
'Call list_agents before using the subagent tool when you are not certain which agents are available.',
|
|
28
|
+
'list_agents returns agent names, descriptions, sources, and capabilities.',
|
|
29
|
+
],
|
|
30
|
+
parameters: Type.Object({
|
|
31
|
+
agentScope: Type.Optional(AgentScopeSchema),
|
|
32
|
+
}),
|
|
33
|
+
|
|
34
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
35
|
+
const agentScope: AgentScope = params.agentScope ?? 'user';
|
|
36
|
+
const resolvedPaths = await resolvePackagePaths(ctx.cwd);
|
|
37
|
+
const discovery = discoverAgents(ctx.cwd, agentScope, resolvedPaths);
|
|
38
|
+
|
|
39
|
+
if (discovery.agents.length === 0) {
|
|
40
|
+
const dirs = agentScope === 'user'
|
|
41
|
+
? '~/.pi/agent/prompts'
|
|
42
|
+
: agentScope === 'project'
|
|
43
|
+
? '.pi/prompts'
|
|
44
|
+
: '~/.pi/agent/prompts and .pi/prompts';
|
|
45
|
+
return {
|
|
46
|
+
content: [
|
|
47
|
+
{
|
|
48
|
+
type: 'text',
|
|
49
|
+
text: `No agents found in scope "${agentScope}". Check ${dirs} for agent prompt files.`,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
details: { agents: [], scope: agentScope },
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const lines: string[] = [
|
|
57
|
+
`Available agents (scope: ${agentScope}):`,
|
|
58
|
+
'',
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
for (const agent of discovery.agents) {
|
|
62
|
+
lines.push(`### ${agent.name}`);
|
|
63
|
+
lines.push(`- **Source**: ${agent.source}`);
|
|
64
|
+
lines.push(`- **Description**: ${agent.description}`);
|
|
65
|
+
if (agent.model) lines.push(`- **Default model**: ${agent.model}`);
|
|
66
|
+
if (agent.thinking) lines.push(`- **Default thinking**: ${agent.thinking}`);
|
|
67
|
+
if (agent.tools) lines.push(`- **Tools**: ${agent.tools.join(', ')}`);
|
|
68
|
+
if (agent.sessionStrategy) lines.push(`- **Session strategy**: ${agent.sessionStrategy}`);
|
|
69
|
+
lines.push('');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (discovery.projectPromptsDir) {
|
|
73
|
+
lines.push(`Project prompts directory: ${discovery.projectPromptsDir}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
78
|
+
details: {
|
|
79
|
+
agents: discovery.agents.map((a) => ({
|
|
80
|
+
name: a.name,
|
|
81
|
+
description: a.description,
|
|
82
|
+
source: a.source,
|
|
83
|
+
model: a.model,
|
|
84
|
+
tools: a.tools,
|
|
85
|
+
})),
|
|
86
|
+
scope: agentScope,
|
|
87
|
+
projectPromptsDir: discovery.projectPromptsDir,
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
}
|
package/package.json
CHANGED
|
@@ -114,6 +114,12 @@ Avoid:
|
|
|
114
114
|
- calling `advisor` when `planner` or `reviewer` already has enough signal to proceed alone
|
|
115
115
|
- sending speculative review findings back for fixes before they are validated when evidence is needed
|
|
116
116
|
|
|
117
|
+
## 8. Agent discovery and creation
|
|
118
|
+
Use `list_agents` before spawning when you're unsure which agents exist.
|
|
119
|
+
Use `/create-agent <name> [description]` to scaffold a new project-local agent in `.pi/prompts/` when the user needs a custom agent that doesn't exist yet.
|
|
120
|
+
- After creating, read and refine the prompt file to fit the use case.
|
|
121
|
+
- Remember to use `agentScope: "both"` or `"project"` to include project-local agents.
|
|
122
|
+
|
|
117
123
|
Execution rules:
|
|
118
124
|
- Use `manager` when the task is too large for one prompt but still needs coherent decisions.
|
|
119
125
|
- Use `advisor` for targeted second opinions on tricky or high-risk cases.
|