feat(pi-workflow): reuse standard agent profiles

This commit is contained in:
jb
2026-08-26 22:51:20 -04:00
parent a4784b75c3
commit aa50f0fd16
12 changed files with 606 additions and 54 deletions
+65
View File
@@ -7,6 +7,8 @@ import {
MAX_ASSIGNMENTS_PER_RUN,
MAX_CONCURRENCY,
MAX_REVIEW_CYCLES,
PROFILE_ROLES,
type ProfileRole,
type WorkflowConfig,
} from "./domain.ts";
@@ -15,10 +17,13 @@ const TOP_LEVEL_KEYS = new Set([
"maxAssignmentsPerRun",
"maxReviewCycles",
"analysisTools",
"standardAgents",
"worktrees",
]);
const CONCURRENCY_KEYS = new Set(["default", "ceiling"]);
const STANDARD_AGENT_KEYS = new Set(["enabled", "roles"]);
const WORKTREE_KEYS = new Set(["root"]);
const PROFILE_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
export interface LoadWorkflowConfigOptions {
cwd: string;
@@ -97,6 +102,15 @@ function cloneConfig(config: WorkflowConfig): WorkflowConfig {
maxAssignmentsPerRun: config.maxAssignmentsPerRun,
maxReviewCycles: config.maxReviewCycles,
analysisTools: [...config.analysisTools],
standardAgents: {
enabled: config.standardAgents.enabled,
roles: Object.fromEntries(
Object.entries(config.standardAgents.roles).map(([name, roles]) => [
name,
[...roles],
]),
),
},
worktrees: { ...config.worktrees },
};
}
@@ -192,6 +206,57 @@ function applyConfigLayer(
);
}
if (value.standardAgents !== undefined) {
if (!isRecord(value.standardAgents)) {
throw new Error(`${label}.standardAgents must be an object`);
}
assertKnownKeys(
value.standardAgents,
STANDARD_AGENT_KEYS,
`${label}.standardAgents`,
);
if (value.standardAgents.enabled !== undefined) {
if (typeof value.standardAgents.enabled !== "boolean") {
throw new Error(`${label}.standardAgents.enabled must be a boolean`);
}
next.standardAgents.enabled = value.standardAgents.enabled;
}
if (value.standardAgents.roles !== undefined) {
if (!isRecord(value.standardAgents.roles)) {
throw new Error(`${label}.standardAgents.roles must be an object`);
}
const roles: Record<string, ProfileRole[]> = {};
for (const [name, rawRoles] of Object.entries(
value.standardAgents.roles,
)) {
if (!PROFILE_NAME.test(name)) {
throw new Error(
`${label}.standardAgents.roles agent names must be lowercase kebab case`,
);
}
const requested = stringArray(
rawRoles,
`${label}.standardAgents.roles.${name}`,
);
if (requested.length === 0) {
throw new Error(
`${label}.standardAgents.roles.${name} must not be empty`,
);
}
const invalid = requested.filter(
(role) => !PROFILE_ROLES.includes(role as ProfileRole),
);
if (invalid.length > 0) {
throw new Error(
`${label}.standardAgents.roles.${name} contains unsupported role ${invalid.join(", ")}`,
);
}
roles[name] = requested as ProfileRole[];
}
next.standardAgents.roles = roles;
}
}
if (value.worktrees !== undefined) {
if (!isRecord(value.worktrees)) {
throw new Error(`${label}.worktrees must be an object`);