Merge pull request #3745 from mastertyko/fix/3705-subagent-tools-frontmatter

fix(subagent): support list-style tools frontmatter
This commit is contained in:
Jeremy McSpadden 2026-04-07 21:31:38 -05:00 committed by GitHub
commit ae5e4af61e
2 changed files with 77 additions and 6 deletions

View file

@ -42,3 +42,50 @@ test("discoverAgents falls back to legacy .pi/agents when needed", (t) => {
assert.equal(discovery.projectAgentsDir, agentsDir);
assert.deepEqual(discovery.agents.map((agent) => agent.name), ["ping"]);
});
test("discoverAgents accepts tools frontmatter as a YAML list", (t) => {
const root = makeProjectRoot(t);
const agentsDir = join(root, ".gsd", "agents");
mkdirSync(agentsDir, { recursive: true });
writeFileSync(
join(agentsDir, "reviewer.md"),
[
"---",
"name: reviewer",
"description: review agent",
"tools:",
" - bash",
" - read",
"---",
"Review code",
"",
].join("\n"),
);
const discovery = discoverAgents(root, "project");
assert.deepEqual(discovery.agents.map((agent) => agent.name), ["reviewer"]);
assert.deepEqual(discovery.agents[0]?.tools, ["bash", "read"]);
});
test("discoverAgents still accepts comma-separated tools frontmatter", (t) => {
const root = makeProjectRoot(t);
const agentsDir = join(root, ".gsd", "agents");
mkdirSync(agentsDir, { recursive: true });
writeFileSync(
join(agentsDir, "reviewer.md"),
[
"---",
"name: reviewer",
"description: review agent",
"tools: bash, read",
"---",
"Review code",
"",
].join("\n"),
);
const discovery = discoverAgents(root, "project");
assert.deepEqual(discovery.agents[0]?.tools, ["bash", "read"]);
});

View file

@ -25,6 +25,33 @@ export interface AgentDiscoveryResult {
projectAgentsDir: string | null;
}
interface AgentFrontmatter extends Record<string, unknown> {
name?: string;
description?: string;
tools?: string | string[];
model?: string;
}
function parseAgentTools(value: string | string[] | undefined): string[] | undefined {
if (typeof value === "string") {
const tools = value
.split(",")
.map((tool) => tool.trim())
.filter(Boolean);
return tools.length > 0 ? tools : undefined;
}
if (Array.isArray(value)) {
const tools = value
.flatMap((tool) => typeof tool === "string" ? tool.split(",") : [])
.map((tool) => tool.trim())
.filter(Boolean);
return tools.length > 0 ? tools : undefined;
}
return undefined;
}
function loadAgentsFromDir(dir: string, source: "user" | "project"): AgentConfig[] {
const agents: AgentConfig[] = [];
@ -51,16 +78,13 @@ function loadAgentsFromDir(dir: string, source: "user" | "project"): AgentConfig
continue;
}
const { frontmatter, body } = parseFrontmatter<Record<string, string>>(content);
const { frontmatter, body } = parseFrontmatter<AgentFrontmatter>(content);
if (!frontmatter.name || !frontmatter.description) {
if (typeof frontmatter.name !== "string" || typeof frontmatter.description !== "string") {
continue;
}
const tools = frontmatter.tools
?.split(",")
.map((t: string) => t.trim())
.filter(Boolean);
const tools = parseAgentTools(frontmatter.tools);
agents.push({
name: frontmatter.name,