bmad-method 6.0.0-Beta.5 → 6.0.0-Beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/changelog-social/SKILL.md +178 -0
- package/.claude/skills/changelog-social/examples/discord-example.md +53 -0
- package/.claude/skills/changelog-social/examples/linkedin-example.md +49 -0
- package/.claude/skills/changelog-social/examples/twitter-example.md +55 -0
- package/.claude/skills/changelog-social.skill +0 -0
- package/.claude/skills/draft-changelog/SKILL.md +2 -2
- package/.claude/skills/draft-changelog/prompts/instructions.md +25 -1
- package/.claude/skills/gh-triage/SKILL.md +1 -1
- package/.claude/skills/release-module/SKILL.md +1 -1
- package/.claude/skills/release-module/prompts/instructions.md +0 -4
- package/.github/workflows/quality.yaml +3 -0
- package/.vscode/settings.json +1 -2
- package/CHANGELOG.md +65 -1
- package/docs/bmgd/game-types.md +1 -0
- package/docs/bmgd/index.md +1 -1
- package/docs/bmgd/quick-flow-workflows.md +1 -0
- package/docs/reference/agents.md +22 -0
- package/docs/reference/commands.md +34 -0
- package/docs/reference/testing.md +21 -0
- package/docs/reference/workflow-map.md +1 -1
- package/package.json +5 -2
- package/src/bmm/agents/analyst.agent.yaml +11 -5
- package/src/bmm/agents/dev.agent.yaml +0 -1
- package/src/bmm/agents/pm.agent.yaml +3 -6
- package/src/bmm/agents/{quinn.agent.yaml → qa.agent.yaml} +3 -3
- package/src/bmm/module-help.csv +13 -20
- package/src/bmm/workflows/1-analysis/create-product-brief/workflow.md +0 -1
- package/src/bmm/workflows/1-analysis/research/workflow-domain-research.md +54 -0
- package/src/bmm/workflows/1-analysis/research/workflow-market-research.md +54 -0
- package/src/bmm/workflows/1-analysis/research/workflow-technical-research.md +54 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/data/domain-complexity.csv +2 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md +63 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md +65 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md +65 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +0 -1
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +0 -1
- package/src/bmm/workflows/3-solutioning/create-architecture/data/domain-complexity.csv +2 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/workflow.md +0 -1
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +0 -1
- package/src/bmm/workflows/4-implementation/code-review/workflow.yaml +0 -3
- package/src/bmm/workflows/4-implementation/correct-course/workflow.yaml +0 -4
- package/src/bmm/workflows/4-implementation/create-story/workflow.yaml +0 -4
- package/src/bmm/workflows/4-implementation/dev-story/workflow.yaml +0 -4
- package/src/bmm/workflows/4-implementation/retrospective/workflow.yaml +0 -3
- package/src/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +0 -4
- package/src/bmm/workflows/4-implementation/sprint-status/workflow.yaml +0 -6
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +0 -1
- package/src/bmm/workflows/document-project/workflow.yaml +0 -8
- package/src/bmm/workflows/qa/automate/workflow.yaml +0 -2
- package/src/core/tasks/editorial-review-prose.xml +14 -12
- package/src/core/tasks/editorial-review-structure.xml +28 -28
- package/src/core/tasks/help.md +36 -16
- package/src/core/tasks/index-docs.xml +1 -1
- package/src/core/tasks/review-adversarial-general.xml +2 -2
- package/src/core/tasks/shard-doc.xml +1 -2
- package/src/core/tasks/workflow.xml +1 -1
- package/src/core/workflows/advanced-elicitation/workflow.xml +1 -1
- package/test/test-installation-components.js +1 -1
- package/tools/build-docs.js +21 -1
- package/tools/cli/bmad-cli.js +42 -3
- package/tools/cli/installers/lib/core/config-collector.js +5 -1
- package/tools/cli/installers/lib/core/dependency-resolver.js +1 -1
- package/tools/cli/installers/lib/core/installer.js +1 -6
- package/tools/cli/installers/lib/core/manifest-generator.js +149 -125
- package/tools/cli/installers/lib/custom/handler.js +1 -1
- package/tools/cli/installers/lib/ide/_base-ide.js +25 -15
- package/tools/cli/installers/lib/ide/_config-driven.js +79 -9
- package/tools/cli/installers/lib/ide/codex.js +7 -1
- package/tools/cli/installers/lib/ide/manager.js +8 -2
- package/tools/cli/installers/lib/ide/platform-codes.yaml +0 -3
- package/tools/cli/installers/lib/ide/shared/agent-command-generator.js +2 -2
- package/tools/cli/installers/lib/ide/shared/bmad-artifacts.js +14 -3
- package/tools/cli/installers/lib/ide/shared/path-utils.js +9 -2
- package/tools/cli/installers/lib/ide/shared/task-tool-command-generator.js +126 -29
- package/tools/cli/installers/lib/ide/shared/workflow-command-generator.js +2 -2
- package/tools/cli/installers/lib/ide/templates/combined/default-task.md +10 -0
- package/tools/cli/installers/lib/ide/templates/combined/default-tool.md +10 -0
- package/tools/cli/installers/lib/ide/templates/combined/gemini-task.toml +11 -0
- package/tools/cli/installers/lib/ide/templates/combined/gemini-tool.toml +11 -0
- package/tools/cli/installers/lib/modules/manager.js +3 -6
- package/tools/cli/lib/agent/installer.js +1 -1
- package/tools/cli/lib/prompts.js +192 -6
- package/tools/cli/lib/ui.js +124 -72
- package/tools/schema/agent.js +0 -1
- package/tools/validate-file-refs.js +480 -0
- package/website/astro.config.mjs +1 -35
- package/src/bmm/workflows/1-analysis/research/workflow.md +0 -173
- package/src/bmm/workflows/2-plan-workflows/create-prd/validation-report-prd-workflow.md +0 -433
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow.md +0 -150
- package/src/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-library.json +0 -90
- package/src/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-templates.yaml +0 -127
- package/src/bmm/workflows/excalidraw-diagrams/create-dataflow/checklist.md +0 -39
- package/src/bmm/workflows/excalidraw-diagrams/create-dataflow/instructions.md +0 -130
- package/src/bmm/workflows/excalidraw-diagrams/create-dataflow/workflow.yaml +0 -27
- package/src/bmm/workflows/excalidraw-diagrams/create-diagram/checklist.md +0 -43
- package/src/bmm/workflows/excalidraw-diagrams/create-diagram/instructions.md +0 -141
- package/src/bmm/workflows/excalidraw-diagrams/create-diagram/workflow.yaml +0 -27
- package/src/bmm/workflows/excalidraw-diagrams/create-flowchart/checklist.md +0 -49
- package/src/bmm/workflows/excalidraw-diagrams/create-flowchart/instructions.md +0 -241
- package/src/bmm/workflows/excalidraw-diagrams/create-flowchart/workflow.yaml +0 -27
- package/src/bmm/workflows/excalidraw-diagrams/create-wireframe/checklist.md +0 -38
- package/src/bmm/workflows/excalidraw-diagrams/create-wireframe/instructions.md +0 -133
- package/src/bmm/workflows/excalidraw-diagrams/create-wireframe/workflow.yaml +0 -27
- package/src/core/resources/excalidraw/README.md +0 -160
- package/src/core/resources/excalidraw/excalidraw-helpers.md +0 -127
- package/src/core/resources/excalidraw/library-loader.md +0 -50
- package/src/core/resources/excalidraw/validate-json-instructions.md +0 -79
- package/tools/flattener/aggregate.js +0 -76
- package/tools/flattener/binary.js +0 -80
- package/tools/flattener/discovery.js +0 -71
- package/tools/flattener/files.js +0 -35
- package/tools/flattener/ignoreRules.js +0 -172
- package/tools/flattener/main.js +0 -483
- package/tools/flattener/projectRoot.js +0 -201
- package/tools/flattener/prompts.js +0 -44
- package/tools/flattener/stats.helpers.js +0 -368
- package/tools/flattener/stats.js +0 -75
- package/tools/flattener/test-matrix.js +0 -409
- package/tools/flattener/xml.js +0 -82
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Reference Validator
|
|
3
|
+
*
|
|
4
|
+
* Validates cross-file references in BMAD source files (agents, workflows, tasks, steps).
|
|
5
|
+
* Catches broken file paths, missing referenced files, and absolute path leaks.
|
|
6
|
+
*
|
|
7
|
+
* What it checks:
|
|
8
|
+
* - {project-root}/_bmad/ references in YAML and markdown resolve to real src/ files
|
|
9
|
+
* - Relative path references (./file.md, ../data/file.csv) point to existing files
|
|
10
|
+
* - exec="..." and <invoke-task> targets exist
|
|
11
|
+
* - Step metadata (thisStepFile, nextStepFile) references are valid
|
|
12
|
+
* - Load directives (Load: `./file.md`) target existing files
|
|
13
|
+
* - No absolute paths (/Users/, /home/, C:\) leak into source files
|
|
14
|
+
*
|
|
15
|
+
* What it does NOT check (deferred):
|
|
16
|
+
* - {installed_path} variable interpolation (self-referential, low risk)
|
|
17
|
+
* - {{mustache}} template variables (runtime substitution)
|
|
18
|
+
* - {config_source}:key dynamic YAML dereferences
|
|
19
|
+
*
|
|
20
|
+
* Usage:
|
|
21
|
+
* node tools/validate-file-refs.js # Warn on broken references (exit 0)
|
|
22
|
+
* node tools/validate-file-refs.js --strict # Fail on broken references (exit 1)
|
|
23
|
+
* node tools/validate-file-refs.js --verbose # Show all checked references
|
|
24
|
+
*
|
|
25
|
+
* Default mode is warning-only (exit 0) so adoption is non-disruptive.
|
|
26
|
+
* Use --strict when you want CI or pre-commit to enforce valid references.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const fs = require('node:fs');
|
|
30
|
+
const path = require('node:path');
|
|
31
|
+
const yaml = require('yaml');
|
|
32
|
+
|
|
33
|
+
const PROJECT_ROOT = path.resolve(__dirname, '..');
|
|
34
|
+
const SRC_DIR = path.join(PROJECT_ROOT, 'src');
|
|
35
|
+
const VERBOSE = process.argv.includes('--verbose');
|
|
36
|
+
const STRICT = process.argv.includes('--strict');
|
|
37
|
+
|
|
38
|
+
// --- Constants ---
|
|
39
|
+
|
|
40
|
+
// File extensions to scan
|
|
41
|
+
const SCAN_EXTENSIONS = new Set(['.yaml', '.yml', '.md', '.xml']);
|
|
42
|
+
|
|
43
|
+
// Skip directories
|
|
44
|
+
const SKIP_DIRS = new Set(['node_modules', '_module-installer', '.git']);
|
|
45
|
+
|
|
46
|
+
// Pattern: {project-root}/_bmad/ references
|
|
47
|
+
const PROJECT_ROOT_REF = /\{project-root\}\/_bmad\/([^\s'"<>})\]`]+)/g;
|
|
48
|
+
|
|
49
|
+
// Pattern: {_bmad}/ shorthand references
|
|
50
|
+
const BMAD_SHORTHAND_REF = /\{_bmad\}\/([^\s'"<>})\]`]+)/g;
|
|
51
|
+
|
|
52
|
+
// Pattern: exec="..." attributes
|
|
53
|
+
const EXEC_ATTR = /exec="([^"]+)"/g;
|
|
54
|
+
|
|
55
|
+
// Pattern: <invoke-task> content
|
|
56
|
+
const INVOKE_TASK = /<invoke-task>([^<]+)<\/invoke-task>/g;
|
|
57
|
+
|
|
58
|
+
// Pattern: relative paths in quotes
|
|
59
|
+
const RELATIVE_PATH_QUOTED = /['"](\.\.\/?[^'"]+\.(?:md|yaml|yml|xml|json|csv|txt))['"]/g;
|
|
60
|
+
const RELATIVE_PATH_DOT = /['"](\.\/[^'"]+\.(?:md|yaml|yml|xml|json|csv|txt))['"]/g;
|
|
61
|
+
|
|
62
|
+
// Pattern: step metadata
|
|
63
|
+
const STEP_META = /(?:thisStepFile|nextStepFile|continueStepFile|skipToStepFile|altStepFile|workflowFile):\s*['"](\.[^'"]+)['"]/g;
|
|
64
|
+
|
|
65
|
+
// Pattern: Load directives
|
|
66
|
+
const LOAD_DIRECTIVE = /Load[:\s]+`(\.[^`]+)`/g;
|
|
67
|
+
|
|
68
|
+
// Pattern: absolute path leaks
|
|
69
|
+
const ABS_PATH_LEAK = /(?:\/Users\/|\/home\/|[A-Z]:\\\\)/;
|
|
70
|
+
|
|
71
|
+
// --- Output Escaping ---
|
|
72
|
+
|
|
73
|
+
function escapeAnnotation(str) {
|
|
74
|
+
return str.replaceAll('%', '%25').replaceAll('\r', '%0D').replaceAll('\n', '%0A');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function escapeTableCell(str) {
|
|
78
|
+
return String(str).replaceAll('|', String.raw`\|`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Path prefixes/patterns that only exist in installed structure, not in source
|
|
82
|
+
const INSTALL_ONLY_PATHS = ['_config/'];
|
|
83
|
+
|
|
84
|
+
// Files that are generated at install time and don't exist in the source tree
|
|
85
|
+
const INSTALL_GENERATED_FILES = ['config.yaml'];
|
|
86
|
+
|
|
87
|
+
// Variables that indicate a path is not statically resolvable
|
|
88
|
+
const UNRESOLVABLE_VARS = [
|
|
89
|
+
'{output_folder}',
|
|
90
|
+
'{value}',
|
|
91
|
+
'{timestamp}',
|
|
92
|
+
'{config_source}:',
|
|
93
|
+
'{installed_path}',
|
|
94
|
+
'{shared_path}',
|
|
95
|
+
'{planning_artifacts}',
|
|
96
|
+
'{research_topic}',
|
|
97
|
+
'{user_name}',
|
|
98
|
+
'{communication_language}',
|
|
99
|
+
'{epic_number}',
|
|
100
|
+
'{next_epic_num}',
|
|
101
|
+
'{epic_num}',
|
|
102
|
+
'{part_id}',
|
|
103
|
+
'{count}',
|
|
104
|
+
'{date}',
|
|
105
|
+
'{outputFile}',
|
|
106
|
+
'{nextStepFile}',
|
|
107
|
+
];
|
|
108
|
+
|
|
109
|
+
// --- File Discovery ---
|
|
110
|
+
|
|
111
|
+
function getSourceFiles(dir) {
|
|
112
|
+
const files = [];
|
|
113
|
+
|
|
114
|
+
function walk(currentDir) {
|
|
115
|
+
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
116
|
+
|
|
117
|
+
for (const entry of entries) {
|
|
118
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
119
|
+
|
|
120
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
121
|
+
|
|
122
|
+
if (entry.isDirectory()) {
|
|
123
|
+
walk(fullPath);
|
|
124
|
+
} else if (entry.isFile() && SCAN_EXTENSIONS.has(path.extname(entry.name))) {
|
|
125
|
+
files.push(fullPath);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
walk(dir);
|
|
131
|
+
return files;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// --- Code Block Stripping ---
|
|
135
|
+
|
|
136
|
+
function stripCodeBlocks(content) {
|
|
137
|
+
return content.replaceAll(/```[\s\S]*?```/g, (m) => m.replaceAll(/[^\n]/g, ''));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function stripJsonExampleBlocks(content) {
|
|
141
|
+
// Strip bare JSON example blocks: { and } each on their own line.
|
|
142
|
+
// These are example/template data (not real file references).
|
|
143
|
+
return content.replaceAll(/^\{\s*\n(?:.*\n)*?^\}\s*$/gm, (m) => m.replaceAll(/[^\n]/g, ''));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// --- Path Mapping ---
|
|
147
|
+
|
|
148
|
+
function mapInstalledToSource(refPath) {
|
|
149
|
+
// Strip {project-root}/_bmad/ or {_bmad}/ prefix
|
|
150
|
+
let cleaned = refPath.replace(/^\{project-root\}\/_bmad\//, '').replace(/^\{_bmad\}\//, '');
|
|
151
|
+
|
|
152
|
+
// Also handle bare _bmad/ prefix (seen in some invoke-task)
|
|
153
|
+
cleaned = cleaned.replace(/^_bmad\//, '');
|
|
154
|
+
|
|
155
|
+
// Skip install-only paths (generated at install time, not in source)
|
|
156
|
+
if (isInstallOnly(cleaned)) return null;
|
|
157
|
+
|
|
158
|
+
// core/, bmm/, and utility/ are directly under src/
|
|
159
|
+
if (cleaned.startsWith('core/') || cleaned.startsWith('bmm/') || cleaned.startsWith('utility/')) {
|
|
160
|
+
return path.join(SRC_DIR, cleaned);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Fallback: map directly under src/
|
|
164
|
+
return path.join(SRC_DIR, cleaned);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// --- Reference Extraction ---
|
|
168
|
+
|
|
169
|
+
function isResolvable(refStr) {
|
|
170
|
+
// Skip refs containing unresolvable runtime variables
|
|
171
|
+
if (refStr.includes('{{')) return false;
|
|
172
|
+
for (const v of UNRESOLVABLE_VARS) {
|
|
173
|
+
if (refStr.includes(v)) return false;
|
|
174
|
+
}
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function isInstallOnly(cleanedPath) {
|
|
179
|
+
// Skip paths that only exist in the installed _bmad/ structure, not in src/
|
|
180
|
+
for (const prefix of INSTALL_ONLY_PATHS) {
|
|
181
|
+
if (cleanedPath.startsWith(prefix)) return true;
|
|
182
|
+
}
|
|
183
|
+
// Skip files that are generated during installation
|
|
184
|
+
const basename = path.basename(cleanedPath);
|
|
185
|
+
for (const generated of INSTALL_GENERATED_FILES) {
|
|
186
|
+
if (basename === generated) return true;
|
|
187
|
+
}
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function extractYamlRefs(filePath, content) {
|
|
192
|
+
const refs = [];
|
|
193
|
+
|
|
194
|
+
let doc;
|
|
195
|
+
try {
|
|
196
|
+
doc = yaml.parseDocument(content);
|
|
197
|
+
} catch {
|
|
198
|
+
return refs; // Skip unparseable YAML (schema validator handles this)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function checkValue(value, range, keyPath) {
|
|
202
|
+
if (typeof value !== 'string') return;
|
|
203
|
+
if (!isResolvable(value)) return;
|
|
204
|
+
|
|
205
|
+
const line = range ? offsetToLine(content, range[0]) : undefined;
|
|
206
|
+
|
|
207
|
+
// Check for {project-root}/_bmad/ refs
|
|
208
|
+
const prMatch = value.match(/\{project-root\}\/_bmad\/[^\s'"<>})\]`]+/);
|
|
209
|
+
if (prMatch) {
|
|
210
|
+
refs.push({ file: filePath, raw: prMatch[0], type: 'project-root', line, key: keyPath });
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Check for {_bmad}/ refs
|
|
214
|
+
const bmMatch = value.match(/\{_bmad\}\/[^\s'"<>})\]`]+/);
|
|
215
|
+
if (bmMatch) {
|
|
216
|
+
refs.push({ file: filePath, raw: bmMatch[0], type: 'project-root', line, key: keyPath });
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Check for relative paths
|
|
220
|
+
const relMatch = value.match(/^\.\.?\/[^\s'"<>})\]`]+\.(?:md|yaml|yml|xml|json|csv|txt)$/);
|
|
221
|
+
if (relMatch) {
|
|
222
|
+
refs.push({ file: filePath, raw: relMatch[0], type: 'relative', line, key: keyPath });
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function walkNode(node, keyPath) {
|
|
227
|
+
if (!node) return;
|
|
228
|
+
|
|
229
|
+
if (yaml.isMap(node)) {
|
|
230
|
+
for (const item of node.items) {
|
|
231
|
+
const key = item.key && item.key.value !== undefined ? item.key.value : '?';
|
|
232
|
+
const childPath = keyPath ? `${keyPath}.${key}` : String(key);
|
|
233
|
+
walkNode(item.value, childPath);
|
|
234
|
+
}
|
|
235
|
+
} else if (yaml.isSeq(node)) {
|
|
236
|
+
for (const [i, item] of node.items.entries()) {
|
|
237
|
+
walkNode(item, `${keyPath}[${i}]`);
|
|
238
|
+
}
|
|
239
|
+
} else if (yaml.isScalar(node)) {
|
|
240
|
+
checkValue(node.value, node.range, keyPath);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
walkNode(doc.contents, '');
|
|
245
|
+
return refs;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function offsetToLine(content, offset) {
|
|
249
|
+
let line = 1;
|
|
250
|
+
for (let i = 0; i < offset && i < content.length; i++) {
|
|
251
|
+
if (content[i] === '\n') line++;
|
|
252
|
+
}
|
|
253
|
+
return line;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function extractMarkdownRefs(filePath, content) {
|
|
257
|
+
const refs = [];
|
|
258
|
+
const stripped = stripJsonExampleBlocks(stripCodeBlocks(content));
|
|
259
|
+
|
|
260
|
+
function runPattern(regex, type) {
|
|
261
|
+
regex.lastIndex = 0;
|
|
262
|
+
let match;
|
|
263
|
+
while ((match = regex.exec(stripped)) !== null) {
|
|
264
|
+
const raw = match[1];
|
|
265
|
+
if (!isResolvable(raw)) continue;
|
|
266
|
+
refs.push({ file: filePath, raw, type, line: offsetToLine(stripped, match.index) });
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// {project-root}/_bmad/ refs
|
|
271
|
+
runPattern(PROJECT_ROOT_REF, 'project-root');
|
|
272
|
+
|
|
273
|
+
// {_bmad}/ shorthand
|
|
274
|
+
runPattern(BMAD_SHORTHAND_REF, 'project-root');
|
|
275
|
+
|
|
276
|
+
// exec="..." attributes
|
|
277
|
+
runPattern(EXEC_ATTR, 'exec-attr');
|
|
278
|
+
|
|
279
|
+
// <invoke-task> tags
|
|
280
|
+
runPattern(INVOKE_TASK, 'invoke-task');
|
|
281
|
+
|
|
282
|
+
// Step metadata
|
|
283
|
+
runPattern(STEP_META, 'relative');
|
|
284
|
+
|
|
285
|
+
// Load directives
|
|
286
|
+
runPattern(LOAD_DIRECTIVE, 'relative');
|
|
287
|
+
|
|
288
|
+
// Relative paths in quotes
|
|
289
|
+
runPattern(RELATIVE_PATH_QUOTED, 'relative');
|
|
290
|
+
runPattern(RELATIVE_PATH_DOT, 'relative');
|
|
291
|
+
|
|
292
|
+
return refs;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// --- Reference Resolution ---
|
|
296
|
+
|
|
297
|
+
function resolveRef(ref) {
|
|
298
|
+
if (ref.type === 'project-root') {
|
|
299
|
+
return mapInstalledToSource(ref.raw);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (ref.type === 'relative') {
|
|
303
|
+
return path.resolve(path.dirname(ref.file), ref.raw);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (ref.type === 'exec-attr') {
|
|
307
|
+
let execPath = ref.raw;
|
|
308
|
+
if (execPath.includes('{project-root}')) {
|
|
309
|
+
return mapInstalledToSource(execPath);
|
|
310
|
+
}
|
|
311
|
+
if (execPath.includes('{_bmad}')) {
|
|
312
|
+
return mapInstalledToSource(execPath);
|
|
313
|
+
}
|
|
314
|
+
if (execPath.startsWith('_bmad/')) {
|
|
315
|
+
return mapInstalledToSource(execPath);
|
|
316
|
+
}
|
|
317
|
+
// Relative exec path
|
|
318
|
+
return path.resolve(path.dirname(ref.file), execPath);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (ref.type === 'invoke-task') {
|
|
322
|
+
// Extract file path from invoke-task content
|
|
323
|
+
const prMatch = ref.raw.match(/\{project-root\}\/_bmad\/([^\s'"<>})\]`]+)/);
|
|
324
|
+
if (prMatch) return mapInstalledToSource(prMatch[0]);
|
|
325
|
+
|
|
326
|
+
const bmMatch = ref.raw.match(/\{_bmad\}\/([^\s'"<>})\]`]+)/);
|
|
327
|
+
if (bmMatch) return mapInstalledToSource(bmMatch[0]);
|
|
328
|
+
|
|
329
|
+
const bareMatch = ref.raw.match(/_bmad\/([^\s'"<>})\]`]+)/);
|
|
330
|
+
if (bareMatch) return mapInstalledToSource(bareMatch[0]);
|
|
331
|
+
|
|
332
|
+
return null; // Can't resolve — skip
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// --- Absolute Path Leak Detection ---
|
|
339
|
+
|
|
340
|
+
function checkAbsolutePathLeaks(filePath, content) {
|
|
341
|
+
const leaks = [];
|
|
342
|
+
const stripped = stripCodeBlocks(content);
|
|
343
|
+
const lines = stripped.split('\n');
|
|
344
|
+
|
|
345
|
+
for (const [i, line] of lines.entries()) {
|
|
346
|
+
if (ABS_PATH_LEAK.test(line)) {
|
|
347
|
+
leaks.push({ file: filePath, line: i + 1, content: line.trim() });
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return leaks;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// --- Main ---
|
|
355
|
+
|
|
356
|
+
console.log(`\nValidating file references in: ${SRC_DIR}`);
|
|
357
|
+
console.log(`Mode: ${STRICT ? 'STRICT (exit 1 on issues)' : 'WARNING (exit 0)'}${VERBOSE ? ' + VERBOSE' : ''}\n`);
|
|
358
|
+
|
|
359
|
+
const files = getSourceFiles(SRC_DIR);
|
|
360
|
+
console.log(`Found ${files.length} source files\n`);
|
|
361
|
+
|
|
362
|
+
let totalRefs = 0;
|
|
363
|
+
let brokenRefs = 0;
|
|
364
|
+
let totalLeaks = 0;
|
|
365
|
+
let filesWithIssues = 0;
|
|
366
|
+
const allIssues = []; // Collect for $GITHUB_STEP_SUMMARY
|
|
367
|
+
|
|
368
|
+
for (const filePath of files) {
|
|
369
|
+
const relativePath = path.relative(PROJECT_ROOT, filePath);
|
|
370
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
371
|
+
const ext = path.extname(filePath);
|
|
372
|
+
|
|
373
|
+
// Extract references
|
|
374
|
+
let refs;
|
|
375
|
+
if (ext === '.yaml' || ext === '.yml') {
|
|
376
|
+
refs = extractYamlRefs(filePath, content);
|
|
377
|
+
} else {
|
|
378
|
+
refs = extractMarkdownRefs(filePath, content);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Resolve and check
|
|
382
|
+
const broken = [];
|
|
383
|
+
|
|
384
|
+
if (VERBOSE && refs.length > 0) {
|
|
385
|
+
console.log(`\n${relativePath}`);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
for (const ref of refs) {
|
|
389
|
+
totalRefs++;
|
|
390
|
+
const resolved = resolveRef(ref);
|
|
391
|
+
|
|
392
|
+
if (resolved && !fs.existsSync(resolved)) {
|
|
393
|
+
// For paths without extensions, also check if it's a directory
|
|
394
|
+
const hasExt = path.extname(resolved) !== '';
|
|
395
|
+
if (!hasExt) {
|
|
396
|
+
// Could be a directory reference — skip if not clearly a file
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
broken.push({ ref, resolved: path.relative(PROJECT_ROOT, resolved) });
|
|
400
|
+
brokenRefs++;
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (VERBOSE && resolved) {
|
|
405
|
+
console.log(` [OK] ${ref.raw}`);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Check absolute path leaks
|
|
410
|
+
const leaks = checkAbsolutePathLeaks(filePath, content);
|
|
411
|
+
totalLeaks += leaks.length;
|
|
412
|
+
|
|
413
|
+
// Report issues for this file
|
|
414
|
+
if (broken.length > 0 || leaks.length > 0) {
|
|
415
|
+
filesWithIssues++;
|
|
416
|
+
if (!VERBOSE) {
|
|
417
|
+
console.log(`\n${relativePath}`);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
for (const { ref, resolved } of broken) {
|
|
421
|
+
const location = ref.line ? `line ${ref.line}` : ref.key ? `key: ${ref.key}` : '';
|
|
422
|
+
console.log(` [BROKEN] ${ref.raw}${location ? ` (${location})` : ''}`);
|
|
423
|
+
console.log(` Target not found: ${resolved}`);
|
|
424
|
+
allIssues.push({ file: relativePath, line: ref.line || 1, ref: ref.raw, issue: 'broken ref' });
|
|
425
|
+
if (process.env.GITHUB_ACTIONS) {
|
|
426
|
+
const line = ref.line || 1;
|
|
427
|
+
console.log(`::warning file=${relativePath},line=${line}::${escapeAnnotation(`Broken reference: ${ref.raw} → ${resolved}`)}`);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
for (const leak of leaks) {
|
|
432
|
+
console.log(` [ABS-PATH] Line ${leak.line}: ${leak.content}`);
|
|
433
|
+
allIssues.push({ file: relativePath, line: leak.line, ref: leak.content, issue: 'abs-path' });
|
|
434
|
+
if (process.env.GITHUB_ACTIONS) {
|
|
435
|
+
console.log(`::warning file=${relativePath},line=${leak.line}::${escapeAnnotation(`Absolute path leak: ${leak.content}`)}`);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Summary
|
|
442
|
+
console.log(`\n${'─'.repeat(60)}`);
|
|
443
|
+
console.log(`\nSummary:`);
|
|
444
|
+
console.log(` Files scanned: ${files.length}`);
|
|
445
|
+
console.log(` References checked: ${totalRefs}`);
|
|
446
|
+
console.log(` Broken references: ${brokenRefs}`);
|
|
447
|
+
console.log(` Absolute path leaks: ${totalLeaks}`);
|
|
448
|
+
|
|
449
|
+
const hasIssues = brokenRefs > 0 || totalLeaks > 0;
|
|
450
|
+
|
|
451
|
+
if (hasIssues) {
|
|
452
|
+
console.log(`\n ${filesWithIssues} file(s) with issues`);
|
|
453
|
+
|
|
454
|
+
if (STRICT) {
|
|
455
|
+
console.log(`\n [STRICT MODE] Exiting with failure.`);
|
|
456
|
+
} else {
|
|
457
|
+
console.log(`\n Run with --strict to treat warnings as errors.`);
|
|
458
|
+
}
|
|
459
|
+
} else {
|
|
460
|
+
console.log(`\n All file references valid!`);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
console.log('');
|
|
464
|
+
|
|
465
|
+
// Write GitHub Actions step summary
|
|
466
|
+
if (process.env.GITHUB_STEP_SUMMARY) {
|
|
467
|
+
let summary = '## File Reference Validation\n\n';
|
|
468
|
+
if (allIssues.length > 0) {
|
|
469
|
+
summary += '| File | Line | Reference | Issue |\n';
|
|
470
|
+
summary += '|------|------|-----------|-------|\n';
|
|
471
|
+
for (const issue of allIssues) {
|
|
472
|
+
summary += `| ${escapeTableCell(issue.file)} | ${issue.line} | ${escapeTableCell(issue.ref)} | ${issue.issue} |\n`;
|
|
473
|
+
}
|
|
474
|
+
summary += '\n';
|
|
475
|
+
}
|
|
476
|
+
summary += `**${files.length} files scanned, ${totalRefs} references checked, ${brokenRefs + totalLeaks} issues found**\n`;
|
|
477
|
+
fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, summary);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
process.exit(hasIssues && STRICT ? 1 : 0);
|
package/website/astro.config.mjs
CHANGED
|
@@ -110,41 +110,7 @@ export default defineConfig({
|
|
|
110
110
|
collapsed: true,
|
|
111
111
|
autogenerate: { directory: 'reference' },
|
|
112
112
|
},
|
|
113
|
-
|
|
114
|
-
label: 'TEA - Testing in BMAD',
|
|
115
|
-
collapsed: true,
|
|
116
|
-
items: [
|
|
117
|
-
{
|
|
118
|
-
label: 'Tutorials',
|
|
119
|
-
autogenerate: { directory: 'tea/tutorials' },
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
label: 'How-To Guides',
|
|
123
|
-
items: [
|
|
124
|
-
{
|
|
125
|
-
label: 'Workflows',
|
|
126
|
-
autogenerate: { directory: 'tea/how-to/workflows' },
|
|
127
|
-
},
|
|
128
|
-
{
|
|
129
|
-
label: 'Customization',
|
|
130
|
-
autogenerate: { directory: 'tea/how-to/customization' },
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
label: 'Brownfield',
|
|
134
|
-
autogenerate: { directory: 'tea/how-to/brownfield' },
|
|
135
|
-
},
|
|
136
|
-
],
|
|
137
|
-
},
|
|
138
|
-
{
|
|
139
|
-
label: 'Explanation',
|
|
140
|
-
autogenerate: { directory: 'tea/explanation' },
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
label: 'Reference',
|
|
144
|
-
autogenerate: { directory: 'tea/reference' },
|
|
145
|
-
},
|
|
146
|
-
],
|
|
147
|
-
},
|
|
113
|
+
// TEA docs moved to standalone module site; keep BMM sidebar focused.
|
|
148
114
|
],
|
|
149
115
|
|
|
150
116
|
// Credits in footer
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: research
|
|
3
|
-
description: Conduct comprehensive research across multiple domains using current web data and verified sources - Market, Technical, Domain and other research types.
|
|
4
|
-
web_bundle: true
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Research Workflow
|
|
8
|
-
|
|
9
|
-
**Goal:** Conduct comprehensive, exhaustive research across multiple domains using current web data and verified sources to produce complete research documents with compelling narratives and proper citations.
|
|
10
|
-
|
|
11
|
-
**Document Standards:**
|
|
12
|
-
|
|
13
|
-
- **Comprehensive Coverage**: Exhaustive research with no critical gaps
|
|
14
|
-
- **Source Verification**: Every factual claim backed by web sources with URL citations
|
|
15
|
-
- **Document Length**: As long as needed to fully cover the research topic
|
|
16
|
-
- **Professional Structure**: Compelling narrative introduction, detailed TOC, and comprehensive summary
|
|
17
|
-
- **Authoritative Sources**: Multiple independent sources for all critical claims
|
|
18
|
-
|
|
19
|
-
**Your Role:** You are a research facilitator and web data analyst working with an expert partner. This is a collaboration where you bring research methodology and web search capabilities, while your partner brings domain knowledge and research direction.
|
|
20
|
-
|
|
21
|
-
**Final Deliverable**: A complete research document that serves as an authoritative reference on the research topic with:
|
|
22
|
-
|
|
23
|
-
- Compelling narrative introduction
|
|
24
|
-
- Comprehensive table of contents
|
|
25
|
-
- Detailed research sections with proper citations
|
|
26
|
-
- Executive summary and conclusions
|
|
27
|
-
|
|
28
|
-
## WORKFLOW ARCHITECTURE
|
|
29
|
-
|
|
30
|
-
This uses **micro-file architecture** with **routing-based discovery**:
|
|
31
|
-
|
|
32
|
-
- Each research type has its own step folder
|
|
33
|
-
- Step 01 discovers research type and routes to appropriate sub-workflow
|
|
34
|
-
- Sequential progression within each research type
|
|
35
|
-
- Document state tracked in output frontmatter
|
|
36
|
-
|
|
37
|
-
## INITIALIZATION
|
|
38
|
-
|
|
39
|
-
### Configuration Loading
|
|
40
|
-
|
|
41
|
-
Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve:
|
|
42
|
-
|
|
43
|
-
- `project_name`, `output_folder`, , `planning_artifacts`, `user_name`
|
|
44
|
-
- `communication_language`, `document_output_language`, `user_skill_level`
|
|
45
|
-
- `date` as a system-generated value
|
|
46
|
-
|
|
47
|
-
### Paths
|
|
48
|
-
|
|
49
|
-
- `installed_path` = `{project-root}/_bmad/bmm/workflows/1-analysis/research`
|
|
50
|
-
- `template_path` = `{installed_path}/research.template.md`
|
|
51
|
-
- `default_output_file` = `{planning_artifacts}/research/{{research_type}}-{{topic}}-research-{{date}}.md` (dynamic based on research type)
|
|
52
|
-
|
|
53
|
-
## PREREQUISITE
|
|
54
|
-
|
|
55
|
-
**⛔ Web search required.** If unavailable, abort and tell the user.
|
|
56
|
-
|
|
57
|
-
## RESEARCH BEHAVIOR
|
|
58
|
-
|
|
59
|
-
### Web Research Standards
|
|
60
|
-
|
|
61
|
-
- **Current Data Only**: Search the web to verify and supplement your knowledge with current facts
|
|
62
|
-
- **Source Verification**: Require citations for all factual claims
|
|
63
|
-
- **Anti-Hallucination Protocol**: Never present information without verified sources
|
|
64
|
-
- **Multiple Sources**: Require at least 2 independent sources for critical claims
|
|
65
|
-
- **Conflict Resolution**: Present conflicting views and note discrepancies
|
|
66
|
-
- **Confidence Levels**: Flag uncertain data with [High/Medium/Low Confidence]
|
|
67
|
-
|
|
68
|
-
### Source Quality Standards
|
|
69
|
-
|
|
70
|
-
- **Distinguish Clearly**: Facts (from sources) vs Analysis (interpretation) vs Speculation
|
|
71
|
-
- **URL Citation**: Always include source URLs when presenting web search data
|
|
72
|
-
- **Critical Claims**: Market size, growth rates, competitive data need verification
|
|
73
|
-
- **Fact Checking**: Apply fact-checking to critical data points
|
|
74
|
-
|
|
75
|
-
## Implementation Instructions
|
|
76
|
-
|
|
77
|
-
Execute research type discovery and routing:
|
|
78
|
-
|
|
79
|
-
### Research Type Discovery
|
|
80
|
-
|
|
81
|
-
**Your Role:** You are a research facilitator and web data analyst working with an expert partner. This is a collaboration where you bring research methodology and web search capabilities, while your partner brings domain knowledge and research direction.
|
|
82
|
-
|
|
83
|
-
**Research Standards:**
|
|
84
|
-
|
|
85
|
-
- **Anti-Hallucination Protocol**: Never present information without verified sources
|
|
86
|
-
- **Current Data Only**: Search the web to verify and supplement your knowledge with current facts
|
|
87
|
-
- **Source Citation**: Always include URLs for factual claims from web searches
|
|
88
|
-
- **Multiple Sources**: Require 2+ independent sources for critical claims
|
|
89
|
-
- **Conflict Resolution**: Present conflicting views and note discrepancies
|
|
90
|
-
- **Confidence Levels**: Flag uncertain data with [High/Medium/Low Confidence]
|
|
91
|
-
|
|
92
|
-
### Collaborative Research Discovery
|
|
93
|
-
|
|
94
|
-
"Welcome {{user_name}}! I'm excited to work with you as your research partner. I bring web research capabilities with rigorous source verification, while you bring the domain expertise and research direction.
|
|
95
|
-
|
|
96
|
-
**Let me help you clarify what you'd like to research.**
|
|
97
|
-
|
|
98
|
-
**First, tell me: What specific topic, problem, or area do you want to research?**
|
|
99
|
-
|
|
100
|
-
For example:
|
|
101
|
-
|
|
102
|
-
- 'The electric vehicle market in Europe'
|
|
103
|
-
- 'Cloud migration strategies for healthcare'
|
|
104
|
-
- 'AI implementation in financial services'
|
|
105
|
-
- 'Sustainable packaging regulations'
|
|
106
|
-
- 'Or anything else you have in mind...'
|
|
107
|
-
|
|
108
|
-
### Topic Exploration and Clarification
|
|
109
|
-
|
|
110
|
-
Based on the user's initial topic, explore and refine the research scope:
|
|
111
|
-
|
|
112
|
-
#### Topic Clarification Questions:
|
|
113
|
-
|
|
114
|
-
1. **Core Topic**: "What exactly about [topic] are you most interested in?"
|
|
115
|
-
2. **Research Goals**: "What do you hope to achieve with this research?"
|
|
116
|
-
3. **Scope**: "Should we focus broadly or dive deep into specific aspects?"
|
|
117
|
-
4. **Timeline**: "Are you looking at current state, historical context, or future trends?"
|
|
118
|
-
5. **Application**: "How will you use this research? (product development, strategy, academic, etc.)"
|
|
119
|
-
|
|
120
|
-
#### Context Building:
|
|
121
|
-
|
|
122
|
-
- **Initial Input**: User provides topic or research interest
|
|
123
|
-
- **Collaborative Refinement**: Work together to clarify scope and objectives
|
|
124
|
-
- **Goal Alignment**: Ensure research direction matches user needs
|
|
125
|
-
- **Research Boundaries**: Establish clear focus areas and deliverables
|
|
126
|
-
|
|
127
|
-
### Research Type Identification
|
|
128
|
-
|
|
129
|
-
After understanding the research topic and goals, identify the most appropriate research approach:
|
|
130
|
-
|
|
131
|
-
**Research Type Options:**
|
|
132
|
-
|
|
133
|
-
1. **Market Research** - Market size, growth, competition, customer insights
|
|
134
|
-
_Best for: Understanding market dynamics, customer behavior, competitive landscape_
|
|
135
|
-
|
|
136
|
-
2. **Domain Research** - Industry analysis, regulations, technology trends in specific domain
|
|
137
|
-
_Best for: Understanding industry context, regulatory environment, ecosystem_
|
|
138
|
-
|
|
139
|
-
3. **Technical Research** - Technology evaluation, architecture decisions, implementation approaches
|
|
140
|
-
_Best for: Technical feasibility, technology selection, implementation strategies_
|
|
141
|
-
|
|
142
|
-
**Recommendation**: Based on [topic] and [goals], I recommend [suggested research type] because [specific rationale].
|
|
143
|
-
|
|
144
|
-
**What type of research would work best for your needs?**
|
|
145
|
-
|
|
146
|
-
### Research Type Routing
|
|
147
|
-
|
|
148
|
-
<critical>Based on user selection, route to appropriate sub-workflow with the discovered topic using the following IF block sets of instructions. YOU MUST ALWAYS SPEAK OUTPUT In your Agent communication style with the config `{communication_language}`</critical>
|
|
149
|
-
|
|
150
|
-
#### If Market Research:
|
|
151
|
-
|
|
152
|
-
- Set `research_type = "market"`
|
|
153
|
-
- Set `research_topic = [discovered topic from discussion]`
|
|
154
|
-
- Create the starter output file: `{planning_artifacts}/research/market-{{research_topic}}-research-{{date}}.md` with exact copy of the ./research.template.md contents
|
|
155
|
-
- Load: `./market-steps/step-01-init.md` with topic context
|
|
156
|
-
|
|
157
|
-
#### If Domain Research:
|
|
158
|
-
|
|
159
|
-
- Set `research_type = "domain"`
|
|
160
|
-
- Set `research_topic = [discovered topic from discussion]`
|
|
161
|
-
- Create the starter output file: `{planning_artifacts}/research/domain-{{research_topic}}-research-{{date}}.md` with exact copy of the ./research.template.md contents
|
|
162
|
-
- Load: `./domain-steps/step-01-init.md` with topic context
|
|
163
|
-
|
|
164
|
-
#### If Technical Research:
|
|
165
|
-
|
|
166
|
-
- Set `research_type = "technical"`
|
|
167
|
-
- Set `research_topic = [discovered topic from discussion]`
|
|
168
|
-
- Create the starter output file: `{planning_artifacts}/research/technical-{{research_topic}}-research-{{date}}.md` with exact copy of the ./research.template.md contents
|
|
169
|
-
- Load: `./technical-steps/step-01-init.md` with topic context
|
|
170
|
-
|
|
171
|
-
**Important**: The discovered topic from the collaborative discussion should be passed to the research initialization steps, so they don't need to ask "What do you want to research?" again - they can focus on refining the scope for their specific research type.
|
|
172
|
-
|
|
173
|
-
**Note:** All research workflows require web search for current data and source verification.
|