feat: add extraction of 'whenToUse' from agents markdown files for improved agent configuration in opencode

This commit is contained in:
Javier Gomez 2025-09-10 12:51:08 +02:00
parent b381290863
commit 81f8020613
1 changed files with 61 additions and 2 deletions

View File

@ -207,6 +207,55 @@ class IdeSetup extends BaseIdeSetup {
// Helper: detect orchestrator agents to set as primary mode // Helper: detect orchestrator agents to set as primary mode
const isOrchestratorAgent = (agentId) => /(^|-)orchestrator$/i.test(agentId); const isOrchestratorAgent = (agentId) => /(^|-)orchestrator$/i.test(agentId);
// Helper: extract whenToUse string from an agent markdown file
const extractWhenToUseFromFile = async (absPath) => {
try {
const raw = await fileManager.readFile(absPath);
const yamlMatch = raw.match(/```ya?ml\r?\n([\s\S]*?)```/);
const yamlBlock = yamlMatch ? yamlMatch[1].trim() : null;
if (!yamlBlock) return null;
// Try quoted first, then unquoted
const quoted = yamlBlock.match(/whenToUse:\s*"([^"]+)"/i);
if (quoted && quoted[1]) return quoted[1].trim();
const unquoted = yamlBlock.match(/whenToUse:\s*([^\n\r]+)/i);
if (unquoted && unquoted[1]) return unquoted[1].trim();
} catch {
// ignore
}
return null;
};
// Helper: extract Purpose string from a task markdown file's YAML
const extractTaskPurposeFromFile = async (absPath) => {
try {
const raw = await fileManager.readFile(absPath);
const yamlMatch = raw.match(/```ya?ml\r?\n([\s\S]*?)```/);
const yamlBlock = yamlMatch ? yamlMatch[1].trim() : null;
if (!yamlBlock) return null;
// Try parsing YAML for better robustness
try {
const data = yaml.load(yamlBlock);
if (data) {
let val = data.Purpose ?? data.purpose;
if (!val && data.task && (data.task.Purpose || data.task.purpose)) {
val = data.task.Purpose ?? data.task.purpose;
}
if (typeof val === 'string') return val.trim();
}
} catch {
// ignore YAML parse errors
}
// Fallback regex
const quoted = yamlBlock.match(/(?:^|\n)\s*(?:Purpose|purpose):\s*"([^"]+)"/);
if (quoted && quoted[1]) return quoted[1].trim();
const unquoted = yamlBlock.match(/(?:^|\n)\s*(?:Purpose|purpose):\s*([^\n\r]+)/);
if (unquoted && unquoted[1]) return unquoted[1].trim();
} catch {
// ignore
}
return null;
};
// Build core sets // Build core sets
const coreAgentIds = new Set(); const coreAgentIds = new Set();
const coreTaskIds = new Set(); const coreTaskIds = new Set();
@ -266,10 +315,12 @@ class IdeSetup extends BaseIdeSetup {
: `bmad-${baseKey}` : `bmad-${baseKey}`
: baseKey; : baseKey;
const existing = configObj.agent[key]; const existing = configObj.agent[key];
const whenToUse = await extractWhenToUseFromFile(p);
const agentDef = { const agentDef = {
prompt: fileRef, prompt: fileRef,
mode: isOrchestratorAgent(agentId) ? 'primary' : 'subagent', mode: isOrchestratorAgent(agentId) ? 'primary' : 'subagent',
tools: { write: true, edit: true, bash: true }, tools: { write: true, edit: true, bash: true },
...(whenToUse ? { description: whenToUse } : {}),
}; };
if (!existing) { if (!existing) {
configObj.agent[key] = agentDef; configObj.agent[key] = agentDef;
@ -282,6 +333,7 @@ class IdeSetup extends BaseIdeSetup {
) { ) {
existing.prompt = agentDef.prompt; existing.prompt = agentDef.prompt;
existing.mode = agentDef.mode; existing.mode = agentDef.mode;
if (whenToUse) existing.description = whenToUse;
existing.tools = { write: true, edit: true, bash: true }; existing.tools = { write: true, edit: true, bash: true };
configObj.agent[key] = existing; configObj.agent[key] = existing;
summary.agentsUpdated++; summary.agentsUpdated++;
@ -299,10 +351,12 @@ class IdeSetup extends BaseIdeSetup {
const fileRef = `{file:./${rel}}`; const fileRef = `{file:./${rel}}`;
const prefixedKey = `bmad-${pack.packKey}-${agentId}`; const prefixedKey = `bmad-${pack.packKey}-${agentId}`;
const existing = configObj.agent[prefixedKey]; const existing = configObj.agent[prefixedKey];
const whenToUse = await extractWhenToUseFromFile(p);
const agentDef = { const agentDef = {
prompt: fileRef, prompt: fileRef,
mode: isOrchestratorAgent(agentId) ? 'primary' : 'subagent', mode: isOrchestratorAgent(agentId) ? 'primary' : 'subagent',
tools: { write: true, edit: true, bash: true }, tools: { write: true, edit: true, bash: true },
...(whenToUse ? { description: whenToUse } : {}),
}; };
if (!existing) { if (!existing) {
configObj.agent[prefixedKey] = agentDef; configObj.agent[prefixedKey] = agentDef;
@ -315,6 +369,7 @@ class IdeSetup extends BaseIdeSetup {
) { ) {
existing.prompt = agentDef.prompt; existing.prompt = agentDef.prompt;
existing.mode = agentDef.mode; existing.mode = agentDef.mode;
if (whenToUse) existing.description = whenToUse;
existing.tools = { write: true, edit: true, bash: true }; existing.tools = { write: true, edit: true, bash: true };
configObj.agent[prefixedKey] = existing; configObj.agent[prefixedKey] = existing;
summary.agentsUpdated++; summary.agentsUpdated++;
@ -332,7 +387,8 @@ class IdeSetup extends BaseIdeSetup {
const fileRef = `{file:./${rel}}`; const fileRef = `{file:./${rel}}`;
const key = useCommandPrefix ? `bmad:tasks:${taskId}` : `${taskId}`; const key = useCommandPrefix ? `bmad:tasks:${taskId}` : `${taskId}`;
const existing = configObj.command[key]; const existing = configObj.command[key];
const cmdDef = { template: fileRef }; const purpose = await extractTaskPurposeFromFile(p);
const cmdDef = { template: fileRef, ...(purpose ? { description: purpose } : {}) };
if (!existing) { if (!existing) {
configObj.command[key] = cmdDef; configObj.command[key] = cmdDef;
summary.commandsAdded++; summary.commandsAdded++;
@ -343,6 +399,7 @@ class IdeSetup extends BaseIdeSetup {
existing.template.includes(rel) existing.template.includes(rel)
) { ) {
existing.template = cmdDef.template; existing.template = cmdDef.template;
if (purpose) existing.description = purpose;
configObj.command[key] = existing; configObj.command[key] = existing;
summary.commandsUpdated++; summary.commandsUpdated++;
} else { } else {
@ -359,7 +416,8 @@ class IdeSetup extends BaseIdeSetup {
const fileRef = `{file:./${rel}}`; const fileRef = `{file:./${rel}}`;
const prefixedKey = `bmad:${pack.packKey}:${taskId}`; const prefixedKey = `bmad:${pack.packKey}:${taskId}`;
const existing = configObj.command[prefixedKey]; const existing = configObj.command[prefixedKey];
const cmdDef = { template: fileRef }; const purpose = await extractTaskPurposeFromFile(p);
const cmdDef = { template: fileRef, ...(purpose ? { description: purpose } : {}) };
if (!existing) { if (!existing) {
configObj.command[prefixedKey] = cmdDef; configObj.command[prefixedKey] = cmdDef;
summary.commandsAdded++; summary.commandsAdded++;
@ -370,6 +428,7 @@ class IdeSetup extends BaseIdeSetup {
existing.template.includes(rel) existing.template.includes(rel)
) { ) {
existing.template = cmdDef.template; existing.template = cmdDef.template;
if (purpose) existing.description = purpose;
configObj.command[prefixedKey] = existing; configObj.command[prefixedKey] = existing;
summary.commandsUpdated++; summary.commandsUpdated++;
} else { } else {