Compare commits

..

2 Commits

Author SHA1 Message Date
Dave Masino c0eccdaea8 fix: include tools in codex artifacts 2026-01-31 21:44:47 -06:00
Dave Masino a23d5a52f9 fix: generate tasks via manifest-driven task tool generator (#1464) 2026-01-31 20:36:18 -06:00
1 changed files with 27 additions and 78 deletions

View File

@ -2,13 +2,11 @@ const path = require('node:path');
const fs = require('fs-extra'); const fs = require('fs-extra');
const os = require('node:os'); const os = require('node:os');
const chalk = require('chalk'); const chalk = require('chalk');
const yaml = require('yaml');
const { BaseIdeSetup } = require('./_base-ide'); const { BaseIdeSetup } = require('./_base-ide');
const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator'); const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator');
const { AgentCommandGenerator } = require('./shared/agent-command-generator'); const { AgentCommandGenerator } = require('./shared/agent-command-generator');
const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator'); const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator');
const { getTasksFromBmad } = require('./shared/bmad-artifacts'); const { customAgentDashName } = require('./shared/path-utils');
const { toDashPath, customAgentDashName } = require('./shared/path-utils');
const prompts = require('../../../lib/prompts'); const prompts = require('../../../lib/prompts');
/** /**
@ -92,56 +90,16 @@ class CodexSetup extends BaseIdeSetup {
const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []); const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []);
const agentCount = await agentGen.writeDashArtifacts(destDir, agentArtifacts); const agentCount = await agentGen.writeDashArtifacts(destDir, agentArtifacts);
const tasks = await getTasksFromBmad(bmadDir, options.selectedModules || []);
const taskArtifacts = [];
for (const task of tasks) {
const content = await this.readAndProcessWithProject(
task.path,
{
module: task.module,
name: task.name,
},
projectDir,
);
let displayName = task.name;
let description;
let declaredName = task.name;
const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
if (frontmatterMatch) {
try {
const frontmatter = yaml.parse(frontmatterMatch[1]);
if (frontmatter && typeof frontmatter === 'object') {
declaredName = frontmatter.name || declaredName;
displayName = frontmatter.displayName || frontmatter.name || displayName;
description = frontmatter.description || description;
}
} catch {
// Ignore frontmatter parse errors
}
}
const taskPath = path.posix.join(this.bmadFolderName, task.module, 'tasks', `${task.name}.md`);
taskArtifacts.push({
type: 'task',
module: task.module,
name: declaredName,
displayName,
description,
path: taskPath,
sourcePath: task.path,
relativePath: path.join(task.module, 'tasks', `${task.name}.md`),
content,
});
}
const workflowGenerator = new WorkflowCommandGenerator(this.bmadFolderName); const workflowGenerator = new WorkflowCommandGenerator(this.bmadFolderName);
const { artifacts: workflowArtifacts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir); const { artifacts: workflowArtifacts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir);
const workflowCount = await workflowGenerator.writeDashArtifacts(destDir, workflowArtifacts); const workflowCount = await workflowGenerator.writeDashArtifacts(destDir, workflowArtifacts);
// Also write tasks using underscore format // Also write tasks using manifest-driven generator
const ttGen = new TaskToolCommandGenerator(); const ttGen = new TaskToolCommandGenerator();
const tasksWritten = await ttGen.writeDashArtifacts(destDir, taskArtifacts); const taskToolResult = await ttGen.generateDashTaskToolCommands(projectDir, bmadDir, destDir);
const tasksWritten = taskToolResult.generated || 0;
counts.tasks = taskToolResult.tasks || 0;
counts.tools = taskToolResult.tools || 0;
const written = agentCount + workflowCount + tasksWritten; const written = agentCount + workflowCount + tasksWritten;
@ -149,6 +107,7 @@ class CodexSetup extends BaseIdeSetup {
console.log(chalk.dim(` - Mode: CLI`)); console.log(chalk.dim(` - Mode: CLI`));
console.log(chalk.dim(` - ${counts.agents} agents exported`)); console.log(chalk.dim(` - ${counts.agents} agents exported`));
console.log(chalk.dim(` - ${counts.tasks} tasks exported`)); console.log(chalk.dim(` - ${counts.tasks} tasks exported`));
console.log(chalk.dim(` - ${counts.tools} tools exported`));
console.log(chalk.dim(` - ${counts.workflows} workflow commands exported`)); console.log(chalk.dim(` - ${counts.workflows} workflow commands exported`));
if (counts.workflowLaunchers > 0) { if (counts.workflowLaunchers > 0) {
console.log(chalk.dim(` - ${counts.workflowLaunchers} workflow launchers exported`)); console.log(chalk.dim(` - ${counts.workflowLaunchers} workflow launchers exported`));
@ -225,17 +184,13 @@ class CodexSetup extends BaseIdeSetup {
}); });
} }
const tasks = await getTasksFromBmad(bmadDir, selectedModules); const taskToolGen = new TaskToolCommandGenerator();
for (const task of tasks) { const taskManifest = await taskToolGen.loadTaskManifest(bmadDir);
const content = await this.readAndProcessWithProject( const toolManifest = await taskToolGen.loadToolManifest(bmadDir);
task.path, const standaloneTasks = taskManifest ? taskManifest.filter((task) => task.standalone === 'true' || task.standalone === true) : [];
{ const standaloneTools = toolManifest ? toolManifest.filter((tool) => tool.standalone === 'true' || tool.standalone === true) : [];
module: task.module, for (const task of standaloneTasks) {
name: task.name, const content = taskToolGen.generateCommandContent(task, 'task');
},
projectDir,
);
artifacts.push({ artifacts.push({
type: 'task', type: 'task',
module: task.module, module: task.module,
@ -245,6 +200,17 @@ class CodexSetup extends BaseIdeSetup {
}); });
} }
for (const tool of standaloneTools) {
const content = taskToolGen.generateCommandContent(tool, 'tool');
artifacts.push({
type: 'tool',
module: tool.module,
sourcePath: tool.path,
relativePath: path.join(tool.module, 'tools', `${tool.name}.md`),
content,
});
}
const workflowGenerator = new WorkflowCommandGenerator(this.bmadFolderName); const workflowGenerator = new WorkflowCommandGenerator(this.bmadFolderName);
const { artifacts: workflowArtifacts, counts: workflowCounts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir); const { artifacts: workflowArtifacts, counts: workflowCounts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir);
artifacts.push(...workflowArtifacts); artifacts.push(...workflowArtifacts);
@ -253,7 +219,8 @@ class CodexSetup extends BaseIdeSetup {
artifacts, artifacts,
counts: { counts: {
agents: agentArtifacts.length, agents: agentArtifacts.length,
tasks: tasks.length, tasks: standaloneTasks.length,
tools: standaloneTools.length,
workflows: workflowCounts.commands, workflows: workflowCounts.commands,
workflowLaunchers: workflowCounts.launchers, workflowLaunchers: workflowCounts.launchers,
}, },
@ -267,19 +234,6 @@ class CodexSetup extends BaseIdeSetup {
return path.join(os.homedir(), '.codex', 'prompts'); return path.join(os.homedir(), '.codex', 'prompts');
} }
async flattenAndWriteArtifacts(artifacts, destDir) {
let written = 0;
for (const artifact of artifacts) {
const flattenedName = this.flattenFilename(artifact.relativePath);
const targetPath = path.join(destDir, flattenedName);
await fs.writeFile(targetPath, artifact.content);
written++;
}
return written;
}
async clearOldBmadFiles(destDir) { async clearOldBmadFiles(destDir) {
if (!(await fs.pathExists(destDir))) { if (!(await fs.pathExists(destDir))) {
return; return;
@ -322,11 +276,6 @@ class CodexSetup extends BaseIdeSetup {
} }
} }
async readAndProcessWithProject(filePath, metadata, projectDir) {
const content = await fs.readFile(filePath, 'utf8');
return super.processContent(content, metadata, projectDir);
}
/** /**
* Get instructions for global installation * Get instructions for global installation
* @returns {string} Instructions text * @returns {string} Instructions text