Merge branch 'main' into fix-code-r-absolute-paths

This commit is contained in:
Alex Verkhovsky 2026-03-21 12:22:44 -06:00 committed by GitHub
commit a68766c749
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 14 additions and 37 deletions

View File

@ -26,6 +26,8 @@ Change `{spec_file}` status to `in-progress` in the frontmatter before starting
Hand `{spec_file}` to a sub-agent/task and let it implement. If no sub-agents are available, implement directly. Hand `{spec_file}` to a sub-agent/task and let it implement. If no sub-agents are available, implement directly.
**Path formatting rule:** Any markdown links written into `{spec_file}` must use paths relative to `{spec_file}`'s directory so they are clickable in VS Code. Any file paths displayed in terminal/conversation output must use CWD-relative format with `:line` notation (e.g., `src/path/file.ts:42`) for terminal clickability. No leading `/` in either case.
## NEXT ## NEXT
Read fully and follow `./step-04-review.md` Read fully and follow `./step-04-review.md`

View File

@ -22,7 +22,7 @@ Build the trail as an ordered sequence of **stops** — clickable `path:line` re
2. **Lead with the entry point** — the single highest-leverage file:line a reviewer should look at first to grasp the design intent. 2. **Lead with the entry point** — the single highest-leverage file:line a reviewer should look at first to grasp the design intent.
3. **Inside each concern**, order stops from most important / architecturally interesting to supporting. Lightly bias toward higher-risk or boundary-crossing stops. 3. **Inside each concern**, order stops from most important / architecturally interesting to supporting. Lightly bias toward higher-risk or boundary-crossing stops.
4. **End with peripherals** — tests, config, types, and other supporting changes come last. 4. **End with peripherals** — tests, config, types, and other supporting changes come last.
5. **Every code reference is a clickable workspace-relative link** (project-root-relative for clickability in the editor). Format each stop as a markdown link: `[short-name:line](/project-root-relative/path/to/file.ts#L42)`. The link target uses a leading `/` (workspace root) with a `#L` line anchor. Use the file's basename (or shortest unambiguous suffix) plus line number as the link text. 5. **Every code reference is a clickable spec-file-relative link.** Compute each link target as a relative path from `{spec_file}`'s directory to the changed file. Format each stop as a markdown link: `[short-name:line](../../path/to/file.ts#L42)`. Use a `#L` line anchor. Use the file's basename (or shortest unambiguous suffix) plus line number as the link text. The relative path must be dynamically derived — never hardcode the depth.
6. **Each stop gets one ultra-concise line of framing** (≤15 words) — why this approach was chosen here and what it achieves in the context of the change. No paragraphs. 6. **Each stop gets one ultra-concise line of framing** (≤15 words) — why this approach was chosen here and what it achieves in the context of the change. No paragraphs.
Format each stop as framing first, link on the next indented line: Format each stop as framing first, link on the next indented line:
@ -33,17 +33,19 @@ Format each stop as framing first, link on the next indented line:
**{Concern name}** **{Concern name}**
- {one-line framing} - {one-line framing}
[`file.ts:42`](/src/path/to/file.ts#L42) [`file.ts:42`](../../src/path/to/file.ts#L42)
- {one-line framing} - {one-line framing}
[`other.ts:17`](/src/path/to/other.ts#L17) [`other.ts:17`](../../src/path/to/other.ts#L17)
**{Next concern}** **{Next concern}**
- {one-line framing} - {one-line framing}
[`file.ts:88`](/src/path/to/file.ts#L88) [`file.ts:88`](../../src/path/to/file.ts#L88)
``` ```
> The `../../` prefix above is illustrative — compute the actual relative path from `{spec_file}`'s directory to each target file.
When there is only one concern, omit the bold label — just list the stops directly. When there is only one concern, omit the bold label — just list the stops directly.
### Commit and Present ### Commit and Present
@ -53,7 +55,7 @@ When there is only one concern, omit the bold label — just list the stops dire
3. Open the spec in the user's editor so they can click through the Suggested Review Order: 3. Open the spec in the user's editor so they can click through the Suggested Review Order:
- Resolve two absolute paths: (1) the repository root (`git rev-parse --show-toplevel` — returns the worktree root when in a worktree, project root otherwise), (2) `{spec_file}`. Run `code -r "{absolute-root}" "{absolute-spec-file}"` — the root first so VS Code opens in the right context, then the spec file. Always double-quote paths to handle spaces and special characters. - Resolve two absolute paths: (1) the repository root (`git rev-parse --show-toplevel` — returns the worktree root when in a worktree, project root otherwise), (2) `{spec_file}`. Run `code -r "{absolute-root}" "{absolute-spec-file}"` — the root first so VS Code opens in the right context, then the spec file. Always double-quote paths to handle spaces and special characters.
- If `code` is not available (command fails), skip gracefully and tell the user the spec file path instead. - If `code` is not available (command fails), skip gracefully and tell the user the spec file path instead.
4. Display summary of your work to the user, including the commit hash if one was created. Any file paths shown in conversation/terminal output must use CWD-relative format (no leading `/`) for terminal clickability — this differs from spec-file links which use project-root-relative paths. Include: 4. Display summary of your work to the user, including the commit hash if one was created. Any file paths shown in conversation/terminal output must use CWD-relative format (no leading `/`) with `:line` notation (e.g., `src/path/file.ts:42`) for terminal clickability — the goal is to make paths clickable in terminal emulators. Include:
- A note that the spec is open in their editor (or the file path if it couldn't be opened). Mention that `{spec_file}` now contains a Suggested Review Order. - A note that the spec is open in their editor (or the file path if it couldn't be opened). Mention that `{spec_file}` now contains a Suggested Review Order.
- **Navigation tip:** "Ctrl+click (Cmd+click on macOS) the links in the Suggested Review Order to jump to each stop." - **Navigation tip:** "Ctrl+click (Cmd+click on macOS) the links in the Suggested Review Order to jump to each stop."
- Offer to push and/or create a pull request. - Offer to push and/or create a pull request.

View File

@ -40,7 +40,7 @@ If version control is available and the tree is dirty, create a local commit wit
- If `code` is not available (command fails), skip gracefully and list the file paths instead. - If `code` is not available (command fails), skip gracefully and list the file paths instead.
2. Display a summary in conversation output, including: 2. Display a summary in conversation output, including:
- The commit hash (if one was created). - The commit hash (if one was created).
- List of files changed with one-line descriptions. - List of files changed with one-line descriptions. Use CWD-relative paths with `:line` notation (e.g., `src/path/file.ts:42`) for terminal clickability. No leading `/`.
- Review findings breakdown: patches applied, items deferred, items rejected. If all findings were rejected, say so. - Review findings breakdown: patches applied, items deferred, items rejected. If all findings were rejected, say so.
3. Offer to push and/or create a pull request. 3. Offer to push and/or create a pull request.

View File

@ -704,21 +704,12 @@ class ManifestGenerator {
continue; continue;
} }
// Check if this looks like a module (has agents, workflows, or tasks directory) // Check if this looks like a module (has agents directory or skill manifests)
const modulePath = path.join(bmadDir, entry.name); const modulePath = path.join(bmadDir, entry.name);
const hasAgents = await fs.pathExists(path.join(modulePath, 'agents')); const hasAgents = await fs.pathExists(path.join(modulePath, 'agents'));
const hasWorkflows = await fs.pathExists(path.join(modulePath, 'workflows')); const hasSkills = await this._hasSkillMdRecursive(modulePath);
const hasTasks = await fs.pathExists(path.join(modulePath, 'tasks'));
const hasTools = await fs.pathExists(path.join(modulePath, 'tools'));
// Check for native-entrypoint-only modules: recursive scan for SKILL.md if (hasAgents || hasSkills) {
let hasSkills = false;
if (!hasAgents && !hasWorkflows && !hasTasks && !hasTools) {
hasSkills = await this._hasSkillMdRecursive(modulePath);
}
// If it has any of these directories or skill manifests, it's likely a module
if (hasAgents || hasWorkflows || hasTasks || hasTools || hasSkills) {
modules.push(entry.name); modules.push(entry.name);
} }
} }

View File

@ -27,7 +27,7 @@ async function loadSkillManifest(dirPath) {
/** /**
* Get the canonicalId for a specific file from a loaded skill manifest. * Get the canonicalId for a specific file from a loaded skill manifest.
* @param {Object|null} manifest - Loaded manifest (from loadSkillManifest) * @param {Object|null} manifest - Loaded manifest (from loadSkillManifest)
* @param {string} filename - Source filename to look up (e.g., 'pm.md', 'help.md', 'pm.agent.yaml') * @param {string} filename - Source filename to look up (e.g., 'pm.md', 'help.md')
* @returns {string} canonicalId or empty string * @returns {string} canonicalId or empty string
*/ */
function getCanonicalId(manifest, filename) { function getCanonicalId(manifest, filename) {
@ -36,12 +36,6 @@ function getCanonicalId(manifest, filename) {
if (manifest.__single) return manifest.__single.canonicalId || ''; if (manifest.__single) return manifest.__single.canonicalId || '';
// Multi-entry: look up by filename directly // Multi-entry: look up by filename directly
if (manifest[filename]) return manifest[filename].canonicalId || ''; if (manifest[filename]) return manifest[filename].canonicalId || '';
// Fallback: try alternate extensions for compiled files
const baseName = filename.replace(/\.(md|xml)$/i, '');
const agentKey = `${baseName}.agent.yaml`;
if (manifest[agentKey]) return manifest[agentKey].canonicalId || '';
const xmlKey = `${baseName}.xml`;
if (manifest[xmlKey]) return manifest[xmlKey].canonicalId || '';
return ''; return '';
} }
@ -57,12 +51,6 @@ function getArtifactType(manifest, filename) {
if (manifest.__single) return manifest.__single.type || null; if (manifest.__single) return manifest.__single.type || null;
// Multi-entry: look up by filename directly // Multi-entry: look up by filename directly
if (manifest[filename]) return manifest[filename].type || null; if (manifest[filename]) return manifest[filename].type || null;
// Fallback: try alternate extensions for compiled files
const baseName = filename.replace(/\.(md|xml)$/i, '');
const agentKey = `${baseName}.agent.yaml`;
if (manifest[agentKey]) return manifest[agentKey].type || null;
const xmlKey = `${baseName}.xml`;
if (manifest[xmlKey]) return manifest[xmlKey].type || null;
return null; return null;
} }
@ -78,12 +66,6 @@ function getInstallToBmad(manifest, filename) {
if (manifest.__single) return manifest.__single.install_to_bmad !== false; if (manifest.__single) return manifest.__single.install_to_bmad !== false;
// Multi-entry: look up by filename directly // Multi-entry: look up by filename directly
if (manifest[filename]) return manifest[filename].install_to_bmad !== false; if (manifest[filename]) return manifest[filename].install_to_bmad !== false;
// Fallback: try alternate extensions for compiled files
const baseName = filename.replace(/\.(md|xml)$/i, '');
const agentKey = `${baseName}.agent.yaml`;
if (manifest[agentKey]) return manifest[agentKey].install_to_bmad !== false;
const xmlKey = `${baseName}.xml`;
if (manifest[xmlKey]) return manifest[xmlKey].install_to_bmad !== false;
return true; return true;
} }