Compare commits

..

No commits in common. "8cf22a4182db7365e3aa1540dc3d012c8daf72e3" and "99c1fa940a7b2c31585b40267205e871ed18ed0f" have entirely different histories.

23 changed files with 325 additions and 238 deletions

View File

@ -1,6 +0,0 @@
---
name: bmad-os-audit-file-refs
description: Audit BMAD source files for file-reference convention violations using parallel Haiku subagents. Use when users requests an "audit file references" for a skill, workflow or task.
---
Read `prompts/instructions.md` and execute.

View File

@ -1,6 +1,7 @@
---
name: bmad-os-changelog-social
description: Generate social media announcements for Discord, Twitter, and LinkedIn from the latest changelog entry. Use when user asks to 'create release announcement' or 'create social posts' or share changelog updates.
description: Generate social media announcements for Discord, Twitter, and LinkedIn from the latest changelog entry. Use when user asks to create release announcements, social posts, or share changelog updates. Reads CHANGELOG.md in current working directory. Reference examples/ for tone and format.
disable-model-invocation: true
---
# Changelog Social

View File

@ -1,6 +1,7 @@
---
name: bmad-os-diataxis-style-fix
description: Fixes documentation to comply with Diataxis framework and BMad Method style guide rules. Use when user asks to check or fix style of files under the docs folder.
description: Fixes documentation to comply with Diataxis framework and BMad Method style guide rules
disable-model-invocation: true
---
Read `prompts/instructions.md` and execute.

View File

@ -1,6 +1,7 @@
---
name: bmad-os-draft-changelog
description: "Analyzes changes since last release and updates CHANGELOG.md ONLY. Use when users requests 'update the changelog' or 'prepare changelog release notes'"
description: Analyzes changes since last release and updates CHANGELOG.md ONLY. Does NOT trigger releases.
disable-model-invocation: true
---
Read `prompts/instructions.md` and execute.

View File

@ -0,0 +1,14 @@
# gh-triage
Fetches all GitHub issues via gh CLI and uses AI agents to deeply analyze, cluster, and prioritize issues.
## Usage
Run from within any BMad Method repository to triage issues.
## What It Does
1. Fetches all open issues via `gh issue list`
2. Splits issues into batches
3. Launches parallel agents to analyze each batch
4. Generates comprehensive triage report to `_bmad-output/triage-reports/`

View File

@ -1,6 +1,12 @@
---
name: bmad-os-gh-triage
description: Analyze all github issues. Use when the user says 'triage the github issues' or 'analyze open github issues'.
description: Fetch all GitHub issues via gh CLI and use AI agents to deeply analyze, cluster, and prioritize issues
license: MIT
disable-model-invocation: true
metadata:
author: bmad-code-org
version: "3.0.0"
compatibility: Requires gh CLI, git repository, and BMad Method with Task tool support
---
Read `prompts/instructions.md` and execute.

View File

@ -0,0 +1,24 @@
# release-module
Automates the complete release process for npm modules.
## Usage
Run from project root or pass project path:
```
bmad-utility-skills:release-module
```
## Prerequisite
First run `draft-changelog` to analyze changes and create a draft changelog.
## What It Does
1. Gets and confirms changelog entry
2. Confirms version bump type (patch/minor/major)
3. Updates CHANGELOG.md
4. Bumps version with `npm version`
5. Pushes git tag
6. Publishes to npm
7. Creates GitHub release

View File

@ -1,6 +1,7 @@
---
name: bmad-os-release-module
description: Perform requested version bump, git tag, npm publish, GitHub release. Use when user requests 'perform a release' only.
description: Automates the complete release process for npm modules - version bump, changelog, git tag, npm publish, GitHub release
disable-model-invocation: true
---
Read `prompts/instructions.md` and execute.

View File

@ -1,6 +0,0 @@
---
name: bmad-os-review-pr
description: Adversarial PR review tool (Raven's Verdict). Cynical deep review transformed into professional engineering findings. Use when user asks to 'review a PR' and provides a PR url or id.
---
Read `prompts/instructions.md` and execute.

1
.gitignore vendored
View File

@ -37,7 +37,6 @@ CLAUDE.local.md
.serena/
.claude/settings.local.json
.junie/
.agents/
z*/

View File

@ -44,7 +44,7 @@ First, check if the output document already exists:
If the document exists and has frontmatter with `stepsCompleted`:
- **STOP here** and load `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-01b-continue.md` immediately
- **STOP here** and load `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-01b-continue.md` immediately
- Do not proceed with any initialization tasks
- Let step-01b handle the continuation logic
@ -148,6 +148,6 @@ Ready to begin architectural decision making. Do you have any other documents yo
## NEXT STEP:
After user selects [C] to continue, only after ensuring all the template output has been created, then load `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-02-context.md` to analyze the project context and begin architectural decision making.
After user selects [C] to continue, only after ensuring all the template output has been created, then load `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-02-context.md` to analyze the project context and begin architectural decision making.
Remember: Do NOT proceed to step-02 until user explicitly selects [C] from the menu and setup is confirmed!

View File

@ -85,7 +85,7 @@ Show the user their current progress:
- Identify the next step based on `stepsCompleted`
- Load the appropriate step file to continue
- Example: If `stepsCompleted: [1, 2, 3]`, load `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md`
- Example: If `stepsCompleted: [1, 2, 3]`, load `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-04-decisions.md`
#### If 'C' (Continue to next logical step):
@ -103,7 +103,7 @@ Show the user their current progress:
#### If 'X' (Start over):
- Confirm: "This will delete all existing architectural decisions. Are you sure? (y/n)"
- If confirmed: Delete existing document and read fully and follow: `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-01-init.md`
- If confirmed: Delete existing document and read fully and follow: `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-01-init.md`
- If not confirmed: Return to continuation menu
### 4. Navigate to Selected Step
@ -162,12 +162,12 @@ After user makes choice:
After user selects their continuation option, load the appropriate step file based on their choice. The step file will handle the detailed work from that point forward.
Valid step files to load:
- `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-02-context.md`
- `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-03-starter.md`
- `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md`
- `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-05-patterns.md`
- `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-06-structure.md`
- `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-07-validation.md`
- `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-08-complete.md`
- `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-02-context.md`
- `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-03-starter.md`
- `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-04-decisions.md`
- `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-05-patterns.md`
- `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-06-structure.md`
- `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-07-validation.md`
- `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-08-complete.md`
Remember: The goal is smooth, transparent resumption that respects the work already done while giving the user control over how to proceed.

View File

@ -188,7 +188,7 @@ Show the generated content and present choices:
- Append the final content to `{planning_artifacts}/architecture.md`
- Update frontmatter: `stepsCompleted: [1, 2]`
- Load `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-03-starter.md`
- Load `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-03-starter.md`
## APPEND TO DOCUMENT:
@ -219,6 +219,6 @@ When user selects 'C', append the content directly to the document using the str
## NEXT STEP:
After user selects 'C' and content is saved to document, load `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-03-starter.md` to evaluate starter template options.
After user selects 'C' and content is saved to document, load `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-03-starter.md` to evaluate starter template options.
Remember: Do NOT proceed to step-03 until user explicitly selects 'C' from the A/P/C menu and content is saved!

View File

@ -294,7 +294,7 @@ Show the generated content and present choices:
- Append the final content to `{planning_artifacts}/architecture.md`
- Update frontmatter: `stepsCompleted: [1, 2, 3]`
- Load `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md`
- Load `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-04-decisions.md`
## APPEND TO DOCUMENT:
@ -324,6 +324,6 @@ When user selects 'C', append the content directly to the document using the str
## NEXT STEP:
After user selects 'C' and content is saved to document, load `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md` to begin making specific architectural decisions.
After user selects 'C' and content is saved to document, load `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-04-decisions.md` to begin making specific architectural decisions.
Remember: Do NOT proceed to step-04 until user explicitly selects 'C' from the A/P/C menu and content is saved!

View File

@ -282,7 +282,7 @@ Show the generated decisions content and present choices:
- Append the final content to `{planning_artifacts}/architecture.md`
- Update frontmatter: `stepsCompleted: [1, 2, 3, 4]`
- Load `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-05-patterns.md`
- Load `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-05-patterns.md`
## APPEND TO DOCUMENT:
@ -313,6 +313,6 @@ When user selects 'C', append the content directly to the document using the str
## NEXT STEP:
After user selects 'C' and content is saved to document, load `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-05-patterns.md` to define implementation patterns that ensure consistency across AI agents.
After user selects 'C' and content is saved to document, load `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-05-patterns.md` to define implementation patterns that ensure consistency across AI agents.
Remember: Do NOT proceed to step-05 until user explicitly selects 'C' from the A/P/C menu and content is saved!

View File

@ -323,7 +323,7 @@ Show the generated patterns content and present choices:
- Append the final content to `{planning_artifacts}/architecture.md`
- Update frontmatter: `stepsCompleted: [1, 2, 3, 4, 5]`
- Load `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-06-structure.md`
- Load `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-06-structure.md`
## APPEND TO DOCUMENT:
@ -354,6 +354,6 @@ When user selects 'C', append the content directly to the document using the str
## NEXT STEP:
After user selects 'C' and content is saved to document, load `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-06-structure.md` to define the complete project structure.
After user selects 'C' and content is saved to document, load `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-06-structure.md` to define the complete project structure.
Remember: Do NOT proceed to step-06 until user explicitly selects 'C' from the A/P/C menu and content is saved!

View File

@ -343,7 +343,7 @@ Show the generated project structure content and present choices:
- Append the final content to `{planning_artifacts}/architecture.md`
- Update frontmatter: `stepsCompleted: [1, 2, 3, 4, 5, 6]`
- Load `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-07-validation.md`
- Load `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-07-validation.md`
## APPEND TO DOCUMENT:
@ -374,6 +374,6 @@ When user selects 'C', append the content directly to the document using the str
## NEXT STEP:
After user selects 'C' and content is saved to document, load `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-07-validation.md` to validate architectural coherence and completeness.
After user selects 'C' and content is saved to document, load `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-07-validation.md` to validate architectural coherence and completeness.
Remember: Do NOT proceed to step-07 until user explicitly selects 'C' from the A/P/C menu and content is saved!

View File

@ -323,7 +323,7 @@ Show the validation results and present choices:
- Append the final content to `{planning_artifacts}/architecture.md`
- Update frontmatter: `stepsCompleted: [1, 2, 3, 4, 5, 6, 7]`
- Load `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-08-complete.md`
- Load `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-08-complete.md`
## APPEND TO DOCUMENT:
@ -354,6 +354,6 @@ When user selects 'C', append the content directly to the document using the str
## NEXT STEP:
After user selects 'C' and content is saved to document, load `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-08-complete.md` to complete the workflow and provide implementation guidance.
After user selects 'C' and content is saved to document, load `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-08-complete.md` to complete the workflow and provide implementation guidance.
Remember: Do NOT proceed to step-08 until user explicitly selects 'C' from the A/P/C menu and content is saved!

View File

@ -44,6 +44,6 @@ Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve:
## EXECUTION
Read fully and follow: `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-01-init.md` to begin the workflow.
Read fully and follow: `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture/steps/step-01-init.md` to begin the workflow.
**Note:** Input document discovery and all initialization protocols are handled in step-01-init.md.

View File

@ -1,7 +1,6 @@
const path = require('node:path');
const os = require('node:os');
const fs = require('fs-extra');
const yaml = require('yaml');
const os = require('node:os');
const { BaseIdeSetup } = require('./_base-ide');
const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator');
const { AgentCommandGenerator } = require('./shared/agent-command-generator');
@ -18,6 +17,57 @@ class CodexSetup extends BaseIdeSetup {
super('codex', 'Codex', false);
}
/**
* Collect configuration choices before installation
* @param {Object} options - Configuration options
* @returns {Object} Collected configuration
*/
async collectConfiguration(options = {}) {
// Non-interactive mode: use default (project) - recommended for real work
if (options.skipPrompts) {
return { installLocation: options.codexLocation || 'project' };
}
let confirmed = false;
let installLocation = 'global';
while (!confirmed) {
installLocation = await prompts.select({
message: 'Where would you like to install Codex CLI prompts?',
choices: [
{
name: 'Global - Simple for single project ' + '(~/.codex/prompts, but references THIS project only)',
value: 'global',
},
{
name: `Project-specific - Recommended for real work (requires CODEX_HOME=<project-dir>${path.sep}.codex)`,
value: 'project',
},
],
default: 'global',
});
// Show brief confirmation hint (detailed instructions available via verbose)
if (installLocation === 'project') {
await prompts.log.info('Prompts installed to: <project>/.codex/prompts (requires CODEX_HOME)');
} else {
await prompts.log.info('Prompts installed to: ~/.codex/prompts');
}
// Confirm the choice
confirmed = await prompts.confirm({
message: 'Proceed with this installation option?',
default: true,
});
if (!confirmed) {
await prompts.log.warn("Let's choose a different installation option.");
}
}
return { installLocation };
}
/**
* Setup Codex configuration
* @param {string} projectDir - Project directory
@ -30,25 +80,20 @@ class CodexSetup extends BaseIdeSetup {
// Always use CLI mode
const mode = 'cli';
// Get installation location from pre-collected config or default to global
const installLocation = options.preCollectedConfig?.installLocation || 'global';
const { artifacts, counts } = await this.collectClaudeArtifacts(projectDir, bmadDir, options);
// Clean up old .codex/prompts locations (both global and project)
const oldGlobalDir = this.getOldCodexPromptDir(null, 'global');
await this.clearOldBmadFiles(oldGlobalDir, options);
const oldProjectDir = this.getOldCodexPromptDir(projectDir, 'project');
await this.clearOldBmadFiles(oldProjectDir, options);
// Install to .agents/skills
const destDir = this.getCodexSkillsDir(projectDir);
const destDir = this.getCodexPromptDir(projectDir, installLocation);
await fs.ensureDir(destDir);
await this.clearOldBmadSkills(destDir, options);
await this.clearOldBmadFiles(destDir, options);
// Collect and write agent skills
// Collect artifacts and write using underscore format
const agentGen = new AgentCommandGenerator(this.bmadFolderName);
const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []);
const agentCount = await this.writeSkillArtifacts(destDir, agentArtifacts, 'agent-launcher');
const agentCount = await agentGen.writeDashArtifacts(destDir, agentArtifacts);
// Collect and write task skills
const tasks = await getTasksFromBmad(bmadDir, options.selectedModules || []);
const taskArtifacts = [];
for (const task of tasks) {
@ -72,23 +117,19 @@ class CodexSetup extends BaseIdeSetup {
});
}
const ttGen = new TaskToolCommandGenerator(this.bmadFolderName);
const taskSkillArtifacts = taskArtifacts.map((artifact) => ({
...artifact,
content: ttGen.generateCommandContent(artifact, artifact.type),
}));
const tasksWritten = await this.writeSkillArtifacts(destDir, taskSkillArtifacts, 'task');
// Collect and write workflow skills
const workflowGenerator = new WorkflowCommandGenerator(this.bmadFolderName);
const { artifacts: workflowArtifacts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir);
const workflowCount = await this.writeSkillArtifacts(destDir, workflowArtifacts, 'workflow-command');
const workflowCount = await workflowGenerator.writeDashArtifacts(destDir, workflowArtifacts);
// Also write tasks using underscore format
const ttGen = new TaskToolCommandGenerator(this.bmadFolderName);
const tasksWritten = await ttGen.writeDashArtifacts(destDir, taskArtifacts);
const written = agentCount + workflowCount + tasksWritten;
if (!options.silent) {
await prompts.log.success(
`${this.name} configured: ${counts.agents} agents, ${counts.workflows} workflows, ${counts.tasks} tasks, ${written} skills → ${destDir}`,
`${this.name} configured: ${counts.agents} agents, ${counts.workflows} workflows, ${counts.tasks} tasks, ${written} files → ${destDir}`,
);
}
@ -99,18 +140,35 @@ class CodexSetup extends BaseIdeSetup {
counts,
destination: destDir,
written,
installLocation,
};
}
/**
* Detect Codex installation by checking for BMAD skills
* Detect Codex installation by checking for BMAD prompt exports
*/
async detect(projectDir) {
const dir = this.getCodexSkillsDir(projectDir || process.cwd());
// Check both global and project-specific locations
const globalDir = this.getCodexPromptDir(null, 'global');
const projectDir_local = projectDir || process.cwd();
const projectSpecificDir = this.getCodexPromptDir(projectDir_local, 'project');
if (await fs.pathExists(dir)) {
// Check global location
if (await fs.pathExists(globalDir)) {
try {
const entries = await fs.readdir(dir);
const entries = await fs.readdir(globalDir);
if (entries && entries.some((entry) => entry && typeof entry === 'string' && entry.startsWith('bmad'))) {
return true;
}
} catch {
// Ignore errors
}
}
// Check project-specific location
if (await fs.pathExists(projectSpecificDir)) {
try {
const entries = await fs.readdir(projectSpecificDir);
if (entries && entries.some((entry) => entry && typeof entry === 'string' && entry.startsWith('bmad'))) {
return true;
}
@ -182,138 +240,26 @@ class CodexSetup extends BaseIdeSetup {
};
}
getCodexSkillsDir(projectDir) {
if (!projectDir) {
throw new Error('projectDir is required for project-scoped skill installation');
}
return path.join(projectDir, '.agents', 'skills');
}
/**
* Get the old .codex/prompts directory for cleanup purposes
*/
getOldCodexPromptDir(projectDir = null, location = 'global') {
getCodexPromptDir(projectDir = null, location = 'global') {
if (location === 'project' && projectDir) {
return path.join(projectDir, '.codex', 'prompts');
}
return path.join(os.homedir(), '.codex', 'prompts');
}
/**
* Write artifacts as Agent Skills (agentskills.io format).
* Each artifact becomes a directory containing SKILL.md.
* @param {string} destDir - Base skills directory
* @param {Array} artifacts - Artifacts to write
* @param {string} artifactType - Type filter (e.g., 'agent-launcher', 'workflow-command', 'task')
* @returns {number} Number of skills written
*/
async writeSkillArtifacts(destDir, artifacts, artifactType) {
let writtenCount = 0;
async flattenAndWriteArtifacts(artifacts, destDir) {
let written = 0;
for (const artifact of artifacts) {
// Filter by type if the artifact has a type field
if (artifact.type && artifact.type !== artifactType) {
continue;
}
// Get the dash-format name (e.g., bmad-bmm-create-prd.md) and remove .md
const flatName = toDashPath(artifact.relativePath);
const skillName = flatName.replace(/\.md$/, '');
// Create skill directory
const skillDir = path.join(destDir, skillName);
await fs.ensureDir(skillDir);
// Transform content: rewrite frontmatter for skills format
const skillContent = this.transformToSkillFormat(artifact.content, skillName);
// Write SKILL.md with platform-native line endings
const platformContent = skillContent.replaceAll('\n', os.EOL);
await fs.writeFile(path.join(skillDir, 'SKILL.md'), platformContent, 'utf8');
writtenCount++;
const flattenedName = this.flattenFilename(artifact.relativePath);
const targetPath = path.join(destDir, flattenedName);
await fs.writeFile(targetPath, artifact.content);
written++;
}
return writtenCount;
return written;
}
/**
* Transform artifact content from Codex prompt format to Agent Skills format.
* Removes disable-model-invocation, ensures name matches directory.
* @param {string} content - Original content with YAML frontmatter
* @param {string} skillName - Skill name (must match directory name)
* @returns {string} Transformed content
*/
transformToSkillFormat(content, skillName) {
// Normalize line endings so body matches rebuilt frontmatter (both LF)
content = content.replaceAll('\r\n', '\n').replaceAll('\r', '\n');
// Parse frontmatter
const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
if (!fmMatch) {
// No frontmatter -- wrap with minimal frontmatter
const fm = yaml.stringify({ name: skillName, description: skillName }).trimEnd();
return `---\n${fm}\n---\n\n${content}`;
}
const frontmatter = fmMatch[1];
const body = fmMatch[2];
// Parse frontmatter with yaml library to handle all quoting variants
let description;
try {
const parsed = yaml.parse(frontmatter);
description = parsed?.description || `${skillName} skill`;
} catch {
description = `${skillName} skill`;
}
// Build new frontmatter with only skills-spec fields, let yaml handle quoting
const newFrontmatter = yaml.stringify({ name: skillName, description }, { lineWidth: 0 }).trimEnd();
return `---\n${newFrontmatter}\n---\n${body}`;
}
/**
* Remove existing BMAD skill directories from the skills directory.
*/
async clearOldBmadSkills(destDir, options = {}) {
if (!(await fs.pathExists(destDir))) {
return;
}
let entries;
try {
entries = await fs.readdir(destDir);
} catch (error) {
if (!options.silent) await prompts.log.warn(`Warning: Could not read directory ${destDir}: ${error.message}`);
return;
}
if (!entries || !Array.isArray(entries)) {
return;
}
for (const entry of entries) {
if (!entry || typeof entry !== 'string') {
continue;
}
if (!entry.startsWith('bmad')) {
continue;
}
const entryPath = path.join(destDir, entry);
try {
await fs.remove(entryPath);
} catch (error) {
if (!options.silent) {
await prompts.log.message(` Skipping ${entry}: ${error.message}`);
}
}
}
}
/**
* Clean old BMAD files from legacy .codex/prompts directories.
*/
async clearOldBmadFiles(destDir, options = {}) {
if (!(await fs.pathExists(destDir))) {
return;
@ -353,11 +299,30 @@ class CodexSetup extends BaseIdeSetup {
}
async readAndProcessWithProject(filePath, metadata, projectDir) {
const rawContent = await fs.readFile(filePath, 'utf8');
const content = rawContent.replaceAll('\r\n', '\n').replaceAll('\r', '\n');
const content = await fs.readFile(filePath, 'utf8');
return super.processContent(content, metadata, projectDir);
}
/**
* Get instructions for global installation
* @returns {string} Instructions text
*/
getGlobalInstructions(destDir) {
const lines = [
'IMPORTANT: Codex Configuration',
'',
'/prompts installed globally to your HOME DIRECTORY.',
'',
'These prompts reference a specific _bmad path.',
"To use with other projects, you'd need to copy the _bmad dir.",
'',
'You can now use /commands in Codex CLI',
' Example: /bmad_bmm_pm',
' Type / to see all available commands',
];
return lines.join('\n');
}
/**
* Get instructions for project-specific installation
* @param {string} projectDir - Optional project directory
@ -365,74 +330,95 @@ class CodexSetup extends BaseIdeSetup {
* @returns {string} Instructions text
*/
getProjectSpecificInstructions(projectDir = null, destDir = null) {
const lines = [
const isWindows = os.platform() === 'win32';
const commonLines = [
'Project-Specific Codex Configuration',
'',
`Skills installed to: ${destDir || '<project>/.agents/skills'}`,
`Prompts will be installed to: ${destDir || '<project>/.codex/prompts'}`,
'',
'REQUIRED: You must set CODEX_HOME to use these prompts',
'',
'Codex automatically discovers skills in .agents/skills/ at and above the current directory and in your home directory.',
'No additional configuration is needed.',
];
const windowsLines = [
'Create a codex.cmd file in your project root:',
'',
' @echo off',
' set CODEX_HOME=%~dp0.codex',
' codex %*',
'',
String.raw`Then run: .\codex instead of codex`,
'(The %~dp0 gets the directory of the .cmd file)',
];
const unixLines = [
'Add this alias to your ~/.bashrc or ~/.zshrc:',
'',
' alias codex=\'CODEX_HOME="$PWD/.codex" codex\'',
'',
'After adding, run: source ~/.bashrc (or source ~/.zshrc)',
'(The $PWD uses your current working directory)',
];
const closingLines = ['', 'This tells Codex CLI to use prompts from this project instead of ~/.codex'];
const lines = [...commonLines, ...(isWindows ? windowsLines : unixLines), ...closingLines];
return lines.join('\n');
}
/**
* Cleanup Codex configuration - cleans both new .agents/skills and old .codex/prompts
* Cleanup Codex configuration
*/
async cleanup(projectDir = null) {
// Clean old .codex/prompts locations
const oldGlobalDir = this.getOldCodexPromptDir(null, 'global');
await this.clearOldBmadFiles(oldGlobalDir);
// Clean both global and project-specific locations
const globalDir = this.getCodexPromptDir(null, 'global');
await this.clearOldBmadFiles(globalDir);
if (projectDir) {
const oldProjectDir = this.getOldCodexPromptDir(projectDir, 'project');
await this.clearOldBmadFiles(oldProjectDir);
// Clean new .agents/skills location
const destDir = this.getCodexSkillsDir(projectDir);
await this.clearOldBmadSkills(destDir);
const projectSpecificDir = this.getCodexPromptDir(projectDir, 'project');
await this.clearOldBmadFiles(projectSpecificDir);
}
}
/**
* Install a custom agent launcher for Codex as an Agent Skill
* @param {string} projectDir - Project directory
* Install a custom agent launcher for Codex
* @param {string} projectDir - Project directory (not used, Codex installs to home)
* @param {string} agentName - Agent name (e.g., "fred-commit-poet")
* @param {string} agentPath - Path to compiled agent (relative to project root)
* @param {Object} metadata - Agent metadata
* @returns {Object|null} Info about created skill
* @returns {Object|null} Info about created command
*/
async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) {
const destDir = this.getCodexSkillsDir(projectDir);
const destDir = this.getCodexPromptDir(projectDir, 'project');
await fs.ensureDir(destDir);
// Skill name from the dash name (without .md)
const skillName = customAgentDashName(agentName).replace(/\.md$/, '');
const skillDir = path.join(destDir, skillName);
await fs.ensureDir(skillDir);
const launcherContent = `---
name: '${agentName}'
description: '${agentName} agent'
disable-model-invocation: true
---
const description = metadata?.description || `${agentName} agent`;
const fm = yaml.stringify({ name: skillName, description }).trimEnd();
const skillContent =
`---\n${fm}\n---\n` +
"\nYou must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command.\n" +
'\n<agent-activation CRITICAL="TRUE">\n' +
`1. LOAD the FULL agent file from @${agentPath}\n` +
'2. READ its entire contents - this contains the complete agent persona, menu, and instructions\n' +
'3. FOLLOW every step in the <activation> section precisely\n' +
'4. DISPLAY the welcome/greeting as instructed\n' +
'5. PRESENT the numbered menu\n' +
'6. WAIT for user input before proceeding\n' +
'</agent-activation>\n';
You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command.
// Write with platform-native line endings
const platformContent = skillContent.replaceAll('\n', os.EOL);
const skillPath = path.join(skillDir, 'SKILL.md');
await fs.writeFile(skillPath, platformContent, 'utf8');
<agent-activation CRITICAL="TRUE">
1. LOAD the FULL agent file from @${agentPath}
2. READ its entire contents - this contains the complete agent persona, menu, and instructions
3. FOLLOW every step in the <activation> section precisely
4. DISPLAY the welcome/greeting as instructed
5. PRESENT the numbered menu
6. WAIT for user input before proceeding
</agent-activation>
`;
// Use underscore format: bmad_custom_fred-commit-poet.md
const fileName = customAgentDashName(agentName);
const launcherPath = path.join(destDir, fileName);
await fs.writeFile(launcherPath, launcherContent, 'utf8');
return {
path: path.relative(projectDir, skillPath),
command: `$${skillName}`,
path: path.relative(projectDir, launcherPath),
command: `/${fileName.replace('.md', '')}`,
};
}
}

View File

@ -0,0 +1,55 @@
# Raven's Verdict - Deep PR Review Tool
Adversarial code review for GitHub PRs. Works with any LLM agent.
> **Status: Experimental.** We're still figuring out how to use this effectively. Expect the workflow to evolve.
## How It Works
Point your agent at `review-pr.md` and ask it to review a specific PR:
> "Read tools/maintainer/review-pr.md and apply it to PR #123"
The tool will:
1. Check out the PR branch locally
2. Run an adversarial review (find at least 5 issues)
3. Transform findings into professional tone
4. Preview the review and ask before posting
See `review-pr.md` for full prompt structure, severity ratings, and sandboxing rules.
## When to Use
**Good candidates:**
- PRs with meaningful logic changes
- Refactors touching multiple files
- New features or architectural changes
**Skip it for:**
- Trivial PRs (typo fixes, version bumps, single-line changes)
- PRs you've already reviewed manually
- PRs where you haven't agreed on the approach yet — fix the direction before the implementation
## Workflow Tips
**Always review before posting.** The preview step exists for a reason:
- **[y] Yes** — Post as-is (only if you're confident)
- **[e] Edit** — Modify findings before posting
- **[s] Save only** — Write to file, don't post
The save option is useful when you want to:
- Hand-edit the review before posting
- Use the findings as input for a second opinion ("Hey Claude, here's what Raven found — what do you think?")
- Cherry-pick specific findings
**Trust but verify.** LLM reviews can miss context or flag non-issues. Skim the findings before they hit the PR.
## Prerequisites
- `gh` CLI installed and authenticated (`gh auth status`)
- Any LLM agent capable of running bash commands

View File

@ -2,7 +2,8 @@
A cynical adversarial review, transformed into cold engineering professionalism.
## CRITICAL: Sandboxed Execution Rules
<orientation>
CRITICAL: Sandboxed Execution Rules
Before proceeding, you MUST verify:
@ -13,6 +14,9 @@ Before proceeding, you MUST verify:
**If no explicit PR number/URL was provided, STOP immediately and ask:**
"What PR number or URL should I review?"
</orientation>
<preflight-checks>
## Preflight Checks
@ -93,7 +97,9 @@ gh pr diff {PR_NUMBER} [--repo {REPO}] --name-only | grep -E '\.(png|jpg|jpeg|gi
Store list of binary files to skip. Note them in final output.
## Adversarial Review
</preflight-checks>
<adversarial-review>
### 1.1 Run Cynical Review
@ -124,7 +130,9 @@ Likely tag:
- Add `[likely]` to findings with high confidence, e.g. with direct evidence
- Sort findings by severity (Critical → Moderate → Minor), not by confidence
## Tone Transformation
</adversarial-review>
<tone-transformation>
**Transform the cynical output into cold engineering professionalism.**
@ -169,8 +177,9 @@ Output format after transformation:
_Review generated by Raven's Verdict. LLM-produced analysis - findings may be incorrect or lack context. Verify before acting._
```
## Post Review
</tone-transformation>
<post-review>
### 3.1 Preview
Display the complete transformed review to the user.
@ -222,10 +231,12 @@ Do NOT use heredocs or `echo` - Markdown code blocks will break shell parsing. U
Keep the temp file and inform user of location.
## Notes
</post-review>
<notes>
- The "cynical asshole" phase is internal only - never posted
- Tone transform MUST happen before any external output
- When in doubt, ask the user - never assume
- If you're unsure about severity, err toward higher severity
- If you're unsure about confidence, be honest and use Medium or Low
</notes>