fix(gsd): discover project subagents in .gsd
Prefer the documented .gsd/agents location for project-local subagents while keeping a legacy fallback to .pi/agents so existing workarounds continue to function. Add a regression test covering both paths. Closes #2864
This commit is contained in:
parent
1d5590c19a
commit
2c0f2b3893
2 changed files with 52 additions and 2 deletions
|
|
@ -0,0 +1,44 @@
|
|||
import assert from "node:assert/strict";
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import test from "node:test";
|
||||
|
||||
import { discoverAgents } from "../../subagent/agents.ts";
|
||||
|
||||
function makeProjectRoot(t: test.TestContext): string {
|
||||
const root = mkdtempSync(join(tmpdir(), "gsd-subagent-agents-"));
|
||||
t.after(() => rmSync(root, { recursive: true, force: true }));
|
||||
return root;
|
||||
}
|
||||
|
||||
function writeAgent(root: string, configDirName: ".gsd" | ".pi", name = "ping"): string {
|
||||
const agentsDir = join(root, configDirName, "agents");
|
||||
mkdirSync(agentsDir, { recursive: true });
|
||||
writeFileSync(
|
||||
join(agentsDir, `${name}.md`),
|
||||
`---\nname: ${name}\ndescription: ${name} agent\n---\nSay hello\n`,
|
||||
);
|
||||
return agentsDir;
|
||||
}
|
||||
|
||||
test("discoverAgents finds project agents in .gsd/agents", (t) => {
|
||||
const root = makeProjectRoot(t);
|
||||
const agentsDir = writeAgent(root, ".gsd");
|
||||
|
||||
const discovery = discoverAgents(root, "project");
|
||||
|
||||
assert.equal(discovery.projectAgentsDir, agentsDir);
|
||||
assert.deepEqual(discovery.agents.map((agent) => agent.name), ["ping"]);
|
||||
assert.equal(discovery.agents[0]?.source, "project");
|
||||
});
|
||||
|
||||
test("discoverAgents falls back to legacy .pi/agents when needed", (t) => {
|
||||
const root = makeProjectRoot(t);
|
||||
const agentsDir = writeAgent(root, ".pi");
|
||||
|
||||
const discovery = discoverAgents(root, "project");
|
||||
|
||||
assert.equal(discovery.projectAgentsDir, agentsDir);
|
||||
assert.deepEqual(discovery.agents.map((agent) => agent.name), ["ping"]);
|
||||
});
|
||||
|
|
@ -6,6 +6,8 @@ import * as fs from "node:fs";
|
|||
import * as path from "node:path";
|
||||
import { getAgentDir, parseFrontmatter } from "@gsd/pi-coding-agent";
|
||||
|
||||
const PROJECT_AGENT_DIR_CANDIDATES = [".gsd", ".pi"] as const;
|
||||
|
||||
export type AgentScope = "user" | "project" | "both";
|
||||
|
||||
export interface AgentConfig {
|
||||
|
|
@ -85,8 +87,12 @@ function isDirectory(p: string): boolean {
|
|||
function findNearestProjectAgentsDir(cwd: string): string | null {
|
||||
let currentDir = cwd;
|
||||
while (true) {
|
||||
const candidate = path.join(currentDir, ".pi", "agents");
|
||||
if (isDirectory(candidate)) return candidate;
|
||||
// Prefer the documented project-local location while preserving support
|
||||
// for older workarounds that placed agents under .pi/agents.
|
||||
for (const configDir of PROJECT_AGENT_DIR_CANDIDATES) {
|
||||
const candidate = path.join(currentDir, configDir, "agents");
|
||||
if (isDirectory(candidate)) return candidate;
|
||||
}
|
||||
|
||||
const parentDir = path.dirname(currentDir);
|
||||
if (parentDir === currentDir) return null;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue