diff --git a/pr.md b/pr.md new file mode 100644 index 00000000..11fb53ce --- /dev/null +++ b/pr.md @@ -0,0 +1,81 @@ +## Summary + +This PR fixes bugs affecting task/tool installation across IDEs: + +1. **CRLF Line Ending Bug** - Frontmatter parsing failed on Windows due to CRLF (`\r\n`) line endings +2. **Gemini CLI TOML Support** - Tasks/tools were generated as `.md` files instead of `.toml` for Gemini CLI +3. **File Extension Preservation** - `.xml` task/tool files had incorrect paths (hardcoded `.md`) + +## Problem + +### Issue 1: Tasks not installed on Windows +The manifest generator's regex `^---\n` expected LF-only line endings, but Windows files have CRLF. This caused: +- YAML frontmatter parsing to silently fail +- All `.md` tasks defaulting to `standalone: false` +- Tasks like `bmad-help` not being installed despite having `standalone: true` in their frontmatter + +### Issue 2: Gemini CLI incompatibility +The `TaskToolCommandGenerator` hardcoded markdown format for all IDEs, but Gemini CLI requires TOML format. Agents and workflows already used the template system correctly, but tasks/tools did not. + +### Issue 3: Incorrect file extensions in paths +The `relativePath` property was hardcoded to `.md`, so tasks/tools with `.xml` extension got incorrect paths like `bmm/tasks/foo.md` instead of `bmm/tasks/foo.xml`. + +## Solution + +### Fix 1: CRLF-aware regex (4 files) +Changed frontmatter regex from `^---\n` to `^---\r?\n` to handle both Windows (CRLF) and Unix (LF) line endings. + +**Files modified:** +- `tools/cli/installers/lib/core/manifest-generator.js` (3 occurrences) +- `tools/cli/installers/lib/core/dependency-resolver.js` (1 occurrence) + +### Fix 2: Template-based task/tool generation +Extended the existing template system (used by agents/workflows) to also handle tasks/tools. + +**New files:** +- `tools/cli/installers/lib/ide/templates/combined/gemini-task.toml` +- `tools/cli/installers/lib/ide/templates/combined/gemini-tool.toml` +- `tools/cli/installers/lib/ide/templates/combined/default-task.md` +- `tools/cli/installers/lib/ide/templates/combined/default-tool.md` + +**Modified files:** +- `tools/cli/installers/lib/ide/shared/task-tool-command-generator.js` + - Added `collectTaskToolArtifacts()` method + - Added constructor with `bmadFolderName` parameter +- `tools/cli/installers/lib/ide/_config-driven.js` + - Added `writeTaskToolArtifacts()` method with `artifact_types` filtering + - Updated `installToTarget()` to use template system + - Updated `renderTemplate()` to handle task/tool paths + +### Fix 3: File extension preservation (4d7ca00) +The `relativePath` property was hardcoded to `.md` extension, causing incorrect paths for `.xml` task/tool files. + +**Modified files:** +- `tools/cli/installers/lib/ide/shared/task-tool-command-generator.js` + - Extract actual extension from source path with `.md` fallback + - Fixed misleading comments ("underscore format" → "dash format") +- `tools/cli/installers/lib/ide/templates/combined/gemini-task.toml` + - Fixed branding: "BMad" → "BMAD" +- `tools/cli/installers/lib/ide/templates/combined/gemini-tool.toml` + - Fixed branding: "BMad" → "BMAD" + +## Test Plan + +- [x] Windows install (CMD) - tasks installed with correct frontmatter parsing +- [x] WSL/Linux install - tasks installed correctly +- [x] Gemini CLI generates `.toml` files for tasks/tools +- [x] Claude Code generates `.md` files for tasks/tools +- [x] All other IDEs (Cursor, Windsurf, Trae, etc.) generate `.md` files +- [x] `bmad-help` task now correctly has `standalone: true` in manifest +- [x] Existing agent/workflow installation unaffected +- [x] `.xml` tasks/tools get correct extension in `relativePath` + +## Breaking Changes + +None - this is purely a bug fix. Existing installations will work correctly after reinstall. + +--- + +Generated with [Claude Code](https://claude.ai/code) + +Co-Authored-By: Claude Opus 4.5 diff --git a/tools/cli/installers/lib/ide/_config-driven.js b/tools/cli/installers/lib/ide/_config-driven.js index 089d62fd..26b76577 100644 --- a/tools/cli/installers/lib/ide/_config-driven.js +++ b/tools/cli/installers/lib/ide/_config-driven.js @@ -411,8 +411,9 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}} // Reuse central logic to ensure consistent naming conventions const standardName = toDashPath(artifact.relativePath); - // Clean up potential double extensions from source files (e.g. .yaml.md -> .md) - const baseName = standardName.replace(/\.(yaml|yml)\.md$/, '.md'); + // Clean up potential double extensions from source files (e.g. .yaml.md, .xml.md -> .md) + // This handles any extensions that might slip through toDashPath() + const baseName = standardName.replace(/\.(md|yaml|yml|json|xml|toml)\.md$/i, '.md'); // If using default markdown, preserve the bmad-agent- prefix for agents if (extension === '.md') { diff --git a/tools/cli/installers/lib/ide/shared/path-utils.js b/tools/cli/installers/lib/ide/shared/path-utils.js index d6ad00f5..561a9029 100644 --- a/tools/cli/installers/lib/ide/shared/path-utils.js +++ b/tools/cli/installers/lib/ide/shared/path-utils.js @@ -59,7 +59,9 @@ function toDashPath(relativePath) { return 'bmad-unknown.md'; } - const withoutExt = relativePath.replace('.md', ''); + // Strip common file extensions to avoid double extensions in generated filenames + // e.g., 'create-story.xml' → 'create-story', 'workflow.yaml' → 'workflow' + const withoutExt = relativePath.replace(/\.(md|yaml|yml|json|xml|toml)$/i, ''); const parts = withoutExt.split(/[/\\]/); const module = parts[0]; @@ -183,7 +185,8 @@ function toUnderscoreName(module, type, name) { * @deprecated Use toDashPath instead */ function toUnderscorePath(relativePath) { - const withoutExt = relativePath.replace('.md', ''); + // Strip common file extensions (same as toDashPath for consistency) + const withoutExt = relativePath.replace(/\.(md|yaml|yml|json|xml|toml)$/i, ''); const parts = withoutExt.split(/[/\\]/); const module = parts[0];