Merge branch 'main' into docs/zh-cn-missing-translations

This commit is contained in:
梁山河 2026-04-13 13:55:50 +08:00 committed by GitHub
commit 8ee35aaea3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 254 additions and 65 deletions

View File

@ -70,7 +70,6 @@
"chalk": "^4.1.2", "chalk": "^4.1.2",
"commander": "^14.0.0", "commander": "^14.0.0",
"csv-parse": "^6.1.0", "csv-parse": "^6.1.0",
"fs-extra": "^11.3.0",
"glob": "^11.0.3", "glob": "^11.0.3",
"ignore": "^7.0.5", "ignore": "^7.0.5",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",

View File

@ -1,4 +1,4 @@
# Step 8: Scoping Exercise - MVP & Future Features # Step 8: Scoping Exercise - Scope Definition (Phased or Single-Release)
**Progress: Step 8 of 11** - Next: Functional Requirements **Progress: Step 8 of 11** - Next: Functional Requirements
@ -12,6 +12,8 @@
- 📋 YOU ARE A FACILITATOR, not a content generator - 📋 YOU ARE A FACILITATOR, not a content generator
- 💬 FOCUS on strategic scope decisions that keep projects viable - 💬 FOCUS on strategic scope decisions that keep projects viable
- 🎯 EMPHASIZE lean MVP thinking while preserving long-term vision - 🎯 EMPHASIZE lean MVP thinking while preserving long-term vision
- ⚠️ NEVER de-scope, defer, or phase out requirements that the user explicitly included in their input documents without asking first
- ⚠️ NEVER invent phasing (MVP/Growth/Vision) unless the user requests phased delivery — if input documents define all components as core requirements, they are ALL in scope
- ✅ YOU MUST ALWAYS SPEAK OUTPUT In your Agent communication style with the config `{communication_language}` - ✅ YOU MUST ALWAYS SPEAK OUTPUT In your Agent communication style with the config `{communication_language}`
- ✅ YOU MUST ALWAYS WRITE all artifact and document content in `{document_output_language}` - ✅ YOU MUST ALWAYS WRITE all artifact and document content in `{document_output_language}`
@ -34,7 +36,7 @@
## YOUR TASK: ## YOUR TASK:
Conduct comprehensive scoping exercise to define MVP boundaries and prioritize features across development phases. Conduct comprehensive scoping exercise to define release boundaries and prioritize features based on the user's chosen delivery mode (phased or single-release).
## SCOPING SEQUENCE: ## SCOPING SEQUENCE:
@ -75,30 +77,41 @@ Use structured decision-making for scope:
- Advanced functionality that builds on MVP - Advanced functionality that builds on MVP
- Ask what features could be added in versions 2, 3, etc. - Ask what features could be added in versions 2, 3, etc.
**⚠️ SCOPE CHANGE CONFIRMATION GATE:**
- If you believe any user-specified requirement should be deferred or de-scoped, you MUST present this to the user and get explicit confirmation BEFORE removing it from scope
- Frame it as a recommendation, not a decision: "I'd recommend deferring X because [reason]. Do you agree, or should it stay in scope?"
- NEVER silently move user requirements to a later phase or exclude them from MVP
- Before creating any consequential phase-based artifacts (e.g., phase tags, labels, or follow-on prompts), present artifact creation as a recommendation and proceed only after explicit user approval
### 4. Progressive Feature Roadmap ### 4. Progressive Feature Roadmap
Create phased development approach: **CRITICAL: Phasing is NOT automatic. Check the user's input first.**
- Guide mapping of features across development phases
- Structure as Phase 1 (MVP), Phase 2 (Growth), Phase 3 (Vision)
- Ensure clear progression and dependencies
- Core user value delivery Before proposing any phased approach, review the user's input documents:
- Essential user journeys
- Basic functionality that works reliably
**Phase 2: Growth** - **If the input documents define all components as core requirements with no mention of phases:** Present all requirements as a single release scope. Do NOT invent phases or move requirements to fabricated future phases.
- **If the input documents explicitly request phased delivery:** Guide mapping of features across the phases the user defined.
- **If scope is unclear:** ASK the user whether they want phased delivery or a single release before proceeding.
- Additional user types **When the user requests phased delivery**, guide mapping of features across the phases the user defines:
- Enhanced features
- Scale improvements
**Phase 3: Expansion** - Use user-provided phase labels and count; if none are provided, propose a default (e.g., MVP/Growth/Vision) and ask for confirmation
- Ensure clear progression and dependencies between phases
- Advanced capabilities **Each phase should address:**
- Platform features
- New markets or use cases
**Where does your current vision fit in this development sequence?**" - Core user value delivery and essential journeys for that phase
- Clear boundaries on what ships in each phase
- Dependencies on prior phases
**When the user chooses a single release**, define the complete scope:
- All user-specified requirements are in scope
- Focus must-have vs nice-to-have analysis on what ships in this release
- Do NOT create phases — use must-have/nice-to-have priority within the single release
**If phased delivery:** "Where does your current vision fit in this development sequence?"
**If single release:** "How does your current vision map to this upcoming release?"
### 5. Risk-Based Scoping ### 5. Risk-Based Scoping
@ -129,6 +142,8 @@ Prepare comprehensive scoping section:
#### Content Structure: #### Content Structure:
**If user chose phased delivery:**
```markdown ```markdown
## Project Scoping & Phased Development ## Project Scoping & Phased Development
@ -160,11 +175,39 @@ Prepare comprehensive scoping section:
**Resource Risks:** {{contingency_approach}} **Resource Risks:** {{contingency_approach}}
``` ```
**If user chose single release (no phasing):**
```markdown
## Project Scoping
### Strategy & Philosophy
**Approach:** {{chosen_approach}}
**Resource Requirements:** {{team_size_and_skills}}
### Complete Feature Set
**Core User Journeys Supported:**
{{all_journeys}}
**Must-Have Capabilities:**
{{list_of_must_have_features}}
**Nice-to-Have Capabilities:**
{{list_of_nice_to_have_features}}
### Risk Mitigation Strategy
**Technical Risks:** {{mitigation_approach}}
**Market Risks:** {{validation_approach}}
**Resource Risks:** {{contingency_approach}}
```
### 7. Present MENU OPTIONS ### 7. Present MENU OPTIONS
Present the scoping decisions for review, then display menu: Present the scoping decisions for review, then display menu:
- Show strategic scoping plan (using structure from step 6) - Show strategic scoping plan (using structure from step 6)
- Highlight MVP boundaries and phased roadmap - Highlight release boundaries and prioritization (phased roadmap only if phased delivery was selected)
- Ask if they'd like to refine further, get other perspectives, or proceed - Ask if they'd like to refine further, get other perspectives, or proceed
- Present menu options naturally as part of conversation - Present menu options naturally as part of conversation
@ -173,7 +216,7 @@ Display: "**Select:** [A] Advanced Elicitation [P] Party Mode [C] Continue to Fu
#### Menu Handling Logic: #### Menu Handling Logic:
- IF A: Invoke the `bmad-advanced-elicitation` skill with the current scoping analysis, process the enhanced insights that come back, ask user if they accept the improvements, if yes update content then redisplay menu, if no keep original content then redisplay menu - IF A: Invoke the `bmad-advanced-elicitation` skill with the current scoping analysis, process the enhanced insights that come back, ask user if they accept the improvements, if yes update content then redisplay menu, if no keep original content then redisplay menu
- IF P: Invoke the `bmad-party-mode` skill with the scoping context, process the collaborative insights on MVP and roadmap decisions, ask user if they accept the changes, if yes update content then redisplay menu, if no keep original content then redisplay menu - IF P: Invoke the `bmad-party-mode` skill with the scoping context, process the collaborative insights on MVP and roadmap decisions, ask user if they accept the changes, if yes update content then redisplay menu, if no keep original content then redisplay menu
- IF C: Append the final content to {outputFile}, update frontmatter by adding this step name to the end of the stepsCompleted array, then read fully and follow: ./step-09-functional.md - IF C: Append the final content to {outputFile}, update frontmatter by adding this step name to the end of the stepsCompleted array (also add `releaseMode: phased` or `releaseMode: single-release` to frontmatter based on user's choice), then read fully and follow: ./step-09-functional.md
- IF Any other: help user respond, then redisplay menu - IF Any other: help user respond, then redisplay menu
#### EXECUTION RULES: #### EXECUTION RULES:
@ -189,8 +232,9 @@ When user selects 'C', append the content directly to the document using the str
✅ Complete PRD document analyzed for scope implications ✅ Complete PRD document analyzed for scope implications
✅ Strategic MVP approach defined and justified ✅ Strategic MVP approach defined and justified
✅ Clear MVP feature boundaries established ✅ Clear feature boundaries established (phased or single-release, per user preference)
✅ Phased development roadmap created ✅ All user-specified requirements accounted for — none silently removed or deferred
✅ Any scope reduction recommendations presented to user with rationale and explicit confirmation obtained
✅ Key risks identified and mitigation strategies defined ✅ Key risks identified and mitigation strategies defined
✅ User explicitly agrees to scope decisions ✅ User explicitly agrees to scope decisions
✅ A/P/C menu presented and handled correctly ✅ A/P/C menu presented and handled correctly
@ -202,8 +246,11 @@ When user selects 'C', append the content directly to the document using the str
❌ Making scope decisions without strategic rationale ❌ Making scope decisions without strategic rationale
❌ Not getting explicit user agreement on MVP boundaries ❌ Not getting explicit user agreement on MVP boundaries
❌ Missing critical risk analysis ❌ Missing critical risk analysis
❌ Not creating clear phased development approach
❌ Not presenting A/P/C menu after content generation ❌ Not presenting A/P/C menu after content generation
**CRITICAL**: Silently de-scoping or deferring requirements that the user explicitly included in their input documents
**CRITICAL**: Inventing phasing (MVP/Growth/Vision) when the user did not request phased delivery
**CRITICAL**: Making consequential scoping decisions (what is in/out of scope) without explicit user confirmation
**CRITICAL**: Creating phase-based artifacts (tags, labels, follow-on prompts) without explicit user approval
**CRITICAL**: Reading only partial step file - leads to incomplete understanding and poor decisions **CRITICAL**: Reading only partial step file - leads to incomplete understanding and poor decisions
**CRITICAL**: Proceeding with 'C' without fully reading and understanding the next step file **CRITICAL**: Proceeding with 'C' without fully reading and understanding the next step file

View File

@ -138,7 +138,7 @@ Make targeted improvements:
- All user success criteria - All user success criteria
- All functional requirements (capability contract) - All functional requirements (capability contract)
- All user journey narratives - All user journey narratives
- All scope decisions (MVP, Growth, Vision) - All scope decisions (whether phased or single-release), including consent-critical evidence (explicit user confirmations and rationales for any scope changes from step 8)
- All non-functional requirements - All non-functional requirements
- Product differentiator and vision - Product differentiator and vision
- Domain-specific requirements - Domain-specific requirements

View File

@ -1,5 +1,6 @@
code: core code: core
name: "BMad Core Module" name: "BMad Core Module"
description: "Core configuration and shared resources"
header: "BMad Core Configuration" header: "BMad Core Configuration"
subheader: "Configure the core settings for your BMad installation.\nThese settings will be used across all installed bmad skills, workflows, and agents." subheader: "Configure the core settings for your BMad installation.\nThese settings will be used across all installed bmad skills, workflows, and agents."

View File

@ -13,7 +13,7 @@
const path = require('node:path'); const path = require('node:path');
const os = require('node:os'); const os = require('node:os');
const fs = require('fs-extra'); const fs = require('../tools/installer/fs-native');
const { Installer } = require('../tools/installer/core/installer'); const { Installer } = require('../tools/installer/core/installer');
const { ManifestGenerator } = require('../tools/installer/core/manifest-generator'); const { ManifestGenerator } = require('../tools/installer/core/manifest-generator');
const { OfficialModules } = require('../tools/installer/modules/official-modules'); const { OfficialModules } = require('../tools/installer/modules/official-modules');

View File

@ -19,7 +19,7 @@ module.exports = {
const { bmadDir } = await installer.findBmadDir(projectDir); const { bmadDir } = await installer.findBmadDir(projectDir);
// Check if bmad directory exists // Check if bmad directory exists
const fs = require('fs-extra'); const fs = require('../fs-native');
if (!(await fs.pathExists(bmadDir))) { if (!(await fs.pathExists(bmadDir))) {
await prompts.log.warn('No BMAD installation found in the current directory.'); await prompts.log.warn('No BMAD installation found in the current directory.');
await prompts.log.message(`Expected location: ${bmadDir}`); await prompts.log.message(`Expected location: ${bmadDir}`);

View File

@ -1,5 +1,5 @@
const path = require('node:path'); const path = require('node:path');
const fs = require('fs-extra'); const fs = require('../fs-native');
const prompts = require('../prompts'); const prompts = require('../prompts');
const { Installer } = require('../core/installer'); const { Installer } = require('../core/installer');

View File

@ -1,5 +1,5 @@
const path = require('node:path'); const path = require('node:path');
const fs = require('fs-extra'); const fs = require('../fs-native');
const yaml = require('yaml'); const yaml = require('yaml');
const { Manifest } = require('./manifest'); const { Manifest } = require('./manifest');

View File

@ -1,5 +1,5 @@
const path = require('node:path'); const path = require('node:path');
const fs = require('fs-extra'); const fs = require('../fs-native');
const { getProjectRoot } = require('../project-root'); const { getProjectRoot } = require('../project-root');
const { BMAD_FOLDER_NAME } = require('../ide/shared/path-utils'); const { BMAD_FOLDER_NAME } = require('../ide/shared/path-utils');

View File

@ -1,5 +1,5 @@
const path = require('node:path'); const path = require('node:path');
const fs = require('fs-extra'); const fs = require('../fs-native');
const { Manifest } = require('./manifest'); const { Manifest } = require('./manifest');
const { OfficialModules } = require('../modules/official-modules'); const { OfficialModules } = require('../modules/official-modules');
const { IdeManager } = require('../ide/manager'); const { IdeManager } = require('../ide/manager');

View File

@ -1,5 +1,5 @@
const path = require('node:path'); const path = require('node:path');
const fs = require('fs-extra'); const fs = require('../fs-native');
const yaml = require('yaml'); const yaml = require('yaml');
const crypto = require('node:crypto'); const crypto = require('node:crypto');
const csv = require('csv-parse/sync'); const csv = require('csv-parse/sync');

View File

@ -1,5 +1,5 @@
const path = require('node:path'); const path = require('node:path');
const fs = require('fs-extra'); const fs = require('../fs-native');
const crypto = require('node:crypto'); const crypto = require('node:crypto');
const { getProjectRoot } = require('../project-root'); const { getProjectRoot } = require('../project-root');
const prompts = require('../prompts'); const prompts = require('../prompts');

View File

@ -1,4 +1,4 @@
const fs = require('fs-extra'); const fs = require('./fs-native');
const path = require('node:path'); const path = require('node:path');
const crypto = require('node:crypto'); const crypto = require('node:crypto');

View File

@ -0,0 +1,111 @@
// Drop-in replacement for fs-extra using native node:fs APIs.
// Eliminates graceful-fs monkey-patching that causes non-deterministic
// file loss during multi-module installs on macOS (issue #1779).
const fsp = require('node:fs/promises');
const fs = require('node:fs');
const path = require('node:path');
async function pathExists(p) {
try {
await fsp.access(p);
return true;
} catch {
return false;
}
}
async function ensureDir(dir) {
await fsp.mkdir(dir, { recursive: true });
}
async function remove(p) {
await fsp.rm(p, { recursive: true, force: true });
}
async function copy(src, dest, options = {}) {
const filterFn = options.filter;
const overwrite = options.overwrite !== false;
const srcStat = await fsp.stat(src);
if (srcStat.isFile()) {
if (filterFn && !(await filterFn(src, dest))) return;
await fsp.mkdir(path.dirname(dest), { recursive: true });
if (!overwrite) {
try {
await fsp.access(dest);
if (options.errorOnExist) throw new Error(`${dest} already exists`);
return;
} catch (error) {
if (error.message.includes('already exists')) throw error;
}
}
await fsp.copyFile(src, dest);
return;
}
if (srcStat.isDirectory()) {
if (filterFn && !(await filterFn(src, dest))) return;
await fsp.mkdir(dest, { recursive: true });
const entries = await fsp.readdir(src, { withFileTypes: true });
for (const entry of entries) {
await copy(path.join(src, entry.name), path.join(dest, entry.name), options);
}
}
}
async function move(src, dest) {
try {
await fsp.rename(src, dest);
} catch (error) {
if (error.code === 'EXDEV') {
await copy(src, dest);
await fsp.rm(src, { recursive: true, force: true });
} else {
throw error;
}
}
}
function readJsonSync(p) {
return JSON.parse(fs.readFileSync(p, 'utf8'));
}
async function writeJson(p, data, options = {}) {
const spaces = options.spaces ?? 2;
await fsp.writeFile(p, JSON.stringify(data, null, spaces) + '\n', 'utf8');
}
module.exports = {
// Native async (node:fs/promises)
readFile: fsp.readFile,
writeFile: fsp.writeFile,
stat: fsp.stat,
readdir: fsp.readdir,
access: fsp.access,
rename: fsp.rename,
unlink: fsp.unlink,
chmod: fsp.chmod,
mkdir: fsp.mkdir,
mkdtemp: fsp.mkdtemp,
copyFile: fsp.copyFile,
rm: fsp.rm,
// fs-extra compatible helpers (native implementations)
pathExists,
ensureDir,
remove,
copy,
move,
readJsonSync,
writeJson,
// Sync methods from core node:fs
existsSync: fs.existsSync.bind(fs),
readFileSync: fs.readFileSync.bind(fs),
writeFileSync: fs.writeFileSync.bind(fs),
createReadStream: fs.createReadStream.bind(fs),
pathExistsSync: fs.existsSync.bind(fs),
// Constants
constants: fs.constants,
};

View File

@ -1,6 +1,6 @@
const os = require('node:os'); const os = require('node:os');
const path = require('node:path'); const path = require('node:path');
const fs = require('fs-extra'); const fs = require('../fs-native');
const yaml = require('yaml'); const yaml = require('yaml');
const prompts = require('../prompts'); const prompts = require('../prompts');
const csv = require('csv-parse/sync'); const csv = require('csv-parse/sync');

View File

@ -1,4 +1,4 @@
const fs = require('fs-extra'); const fs = require('../fs-native');
const path = require('node:path'); const path = require('node:path');
const yaml = require('yaml'); const yaml = require('yaml');

View File

@ -1,5 +1,5 @@
const path = require('node:path'); const path = require('node:path');
const fs = require('fs-extra'); const fs = require('../../fs-native');
const yaml = require('yaml'); const yaml = require('yaml');
/** /**

View File

@ -1,4 +1,4 @@
const fs = require('fs-extra'); const fs = require('./fs-native');
const path = require('node:path'); const path = require('node:path');
const yaml = require('yaml'); const yaml = require('yaml');
const prompts = require('./prompts'); const prompts = require('./prompts');

View File

@ -1,4 +1,4 @@
const fs = require('fs-extra'); const fs = require('../fs-native');
const os = require('node:os'); const os = require('node:os');
const path = require('node:path'); const path = require('node:path');
const { execSync } = require('node:child_process'); const { execSync } = require('node:child_process');

View File

@ -1,4 +1,4 @@
const fs = require('fs-extra'); const fs = require('../fs-native');
const os = require('node:os'); const os = require('node:os');
const path = require('node:path'); const path = require('node:path');
const { execSync } = require('node:child_process'); const { execSync } = require('node:child_process');

View File

@ -1,4 +1,4 @@
const fs = require('fs-extra'); const fs = require('../fs-native');
const os = require('node:os'); const os = require('node:os');
const path = require('node:path'); const path = require('node:path');
const { execSync } = require('node:child_process'); const { execSync } = require('node:child_process');

View File

@ -1,5 +1,5 @@
const path = require('node:path'); const path = require('node:path');
const fs = require('fs-extra'); const fs = require('../fs-native');
const yaml = require('yaml'); const yaml = require('yaml');
const prompts = require('../prompts'); const prompts = require('../prompts');
const { getProjectRoot, getSourcePath, getModulePath } = require('../project-root'); const { getProjectRoot, getSourcePath, getModulePath } = require('../project-root');

View File

@ -1,4 +1,4 @@
const fs = require('fs-extra'); const fs = require('../fs-native');
const path = require('node:path'); const path = require('node:path');
const yaml = require('yaml'); const yaml = require('yaml');

View File

@ -1,5 +1,5 @@
const path = require('node:path'); const path = require('node:path');
const fs = require('fs-extra'); const fs = require('./fs-native');
/** /**
* Find the BMAD project root directory by looking for package.json * Find the BMAD project root directory by looking for package.json

View File

@ -1,6 +1,6 @@
const path = require('node:path'); const path = require('node:path');
const os = require('node:os'); const os = require('node:os');
const fs = require('fs-extra'); const fs = require('./fs-native');
const { CLIUtils } = require('./cli-utils'); const { CLIUtils } = require('./cli-utils');
const { ExternalModuleManager } = require('./modules/external-manager'); const { ExternalModuleManager } = require('./modules/external-manager');
const { getProjectRoot } = require('./project-root'); const { getProjectRoot } = require('./project-root');
@ -598,7 +598,7 @@ class UI {
const officialCodes = new Set(officialSelected); const officialCodes = new Set(officialSelected);
const externalManager = new ExternalModuleManager(); const externalManager = new ExternalModuleManager();
const registryModules = await externalManager.listAvailable(); const registryModules = await externalManager.listAvailable();
const officialRegistryCodes = new Set(registryModules.map((m) => m.code)); const officialRegistryCodes = new Set(['core', 'bmm', ...registryModules.map((m) => m.code)]);
const installedNonOfficial = [...installedModuleIds].filter((id) => !officialRegistryCodes.has(id)); const installedNonOfficial = [...installedModuleIds].filter((id) => !officialRegistryCodes.has(id));
// Phase 2: Community modules (category drill-down) // Phase 2: Community modules (category drill-down)
@ -630,6 +630,11 @@ class UI {
* @returns {Array} Selected official module codes * @returns {Array} Selected official module codes
*/ */
async _selectOfficialModules(installedModuleIds = new Set()) { async _selectOfficialModules(installedModuleIds = new Set()) {
// Built-in modules (core, bmm) come from local source, not the registry
const { OfficialModules } = require('./modules/official-modules');
const builtInModules = (await new OfficialModules().listAvailable()).modules || [];
// External modules come from the registry (with fallback)
const externalManager = new ExternalModuleManager(); const externalManager = new ExternalModuleManager();
const registryModules = await externalManager.listAvailable(); const registryModules = await externalManager.listAvailable();
@ -637,20 +642,34 @@ class UI {
const initialValues = []; const initialValues = [];
const lockedValues = ['core']; const lockedValues = ['core'];
const buildModuleEntry = async (mod) => { const buildModuleEntry = async (code, name, description, isDefault) => {
const isInstalled = installedModuleIds.has(mod.code); const isInstalled = installedModuleIds.has(code);
const version = await getMarketplaceVersion(mod.code); const version = await getMarketplaceVersion(code);
const label = version ? `${mod.name} (v${version})` : mod.name; const label = version ? `${name} (v${version})` : name;
return { return {
label, label,
value: mod.code, value: code,
hint: mod.description, hint: description,
selected: isInstalled, selected: isInstalled || isDefault,
}; };
}; };
// Add built-in modules first (always available regardless of network)
const builtInCodes = new Set();
for (const mod of builtInModules) {
const code = mod.id;
builtInCodes.add(code);
const entry = await buildModuleEntry(code, mod.name, mod.description, mod.defaultSelected);
allOptions.push({ label: entry.label, value: entry.value, hint: entry.hint });
if (entry.selected) {
initialValues.push(code);
}
}
// Add external registry modules (skip built-in duplicates)
for (const mod of registryModules) { for (const mod of registryModules) {
const entry = await buildModuleEntry(mod); if (mod.builtIn || builtInCodes.has(mod.code)) continue;
const entry = await buildModuleEntry(mod.code, mod.name, mod.description, mod.defaultSelected);
allOptions.push({ label: entry.label, value: entry.value, hint: entry.hint }); allOptions.push({ label: entry.label, value: entry.value, hint: entry.hint });
if (entry.selected) { if (entry.selected) {
initialValues.push(mod.code); initialValues.push(mod.code);
@ -1122,12 +1141,26 @@ class UI {
* @returns {Array} Default module codes * @returns {Array} Default module codes
*/ */
async getDefaultModules(installedModuleIds = new Set()) { async getDefaultModules(installedModuleIds = new Set()) {
// Built-in modules with default_selected come from local source
const { OfficialModules } = require('./modules/official-modules');
const builtInModules = (await new OfficialModules().listAvailable()).modules || [];
const defaultModules = [];
const seen = new Set();
for (const mod of builtInModules) {
if (mod.defaultSelected || installedModuleIds.has(mod.id)) {
defaultModules.push(mod.id);
seen.add(mod.id);
}
}
// Add external registry defaults
const externalManager = new ExternalModuleManager(); const externalManager = new ExternalModuleManager();
const registryModules = await externalManager.listAvailable(); const registryModules = await externalManager.listAvailable();
const defaultModules = [];
for (const mod of registryModules) { for (const mod of registryModules) {
if (mod.builtIn || seen.has(mod.code)) continue;
if (mod.defaultSelected || installedModuleIds.has(mod.code)) { if (mod.defaultSelected || installedModuleIds.has(mod.code)) {
defaultModules.push(mod.code); defaultModules.push(mod.code);
} }

View File

@ -3,7 +3,7 @@
* This should be run once to update existing installations * This should be run once to update existing installations
*/ */
const fs = require('fs-extra'); const fs = require('./installer/fs-native');
const path = require('node:path'); const path = require('node:path');
const yaml = require('yaml'); const yaml = require('yaml');
const chalk = require('chalk'); const chalk = require('chalk');

View File

@ -93,7 +93,6 @@
.agent-icon.john { background: linear-gradient(135deg, #60a5fa, #3b82f6); } .agent-icon.john { background: linear-gradient(135deg, #60a5fa, #3b82f6); }
.agent-icon.sally { background: linear-gradient(135deg, #fbbf24, #f59e0b); color: #000; } .agent-icon.sally { background: linear-gradient(135deg, #fbbf24, #f59e0b); color: #000; }
.agent-icon.winston { background: linear-gradient(135deg, #a78bfa, #8b5cf6); } .agent-icon.winston { background: linear-gradient(135deg, #a78bfa, #8b5cf6); }
.agent-icon.bob { background: linear-gradient(135deg, #34d399, #10b981); color: #000; }
.agent-icon.amelia { background: linear-gradient(135deg, #fb7185, #ef4444); } .agent-icon.amelia { background: linear-gradient(135deg, #fb7185, #ef4444); }
.agent-name { font-size: 0.65rem; } .agent-name { font-size: 0.65rem; }
@ -261,7 +260,7 @@
<span class="workflow-name">sprint-planning</span> <span class="workflow-name">sprint-planning</span>
</div> </div>
<div class="workflow-meta"> <div class="workflow-meta">
<div class="agent"><div class="agent-icon bob">B</div><span class="agent-name">Bob</span></div> <div class="agent"><div class="agent-icon amelia">A</div><span class="agent-name">Amelia</span></div>
<span class="output">sprint-status.yaml →</span> <span class="output">sprint-status.yaml →</span>
</div> </div>
</div> </div>
@ -270,7 +269,7 @@
<span class="workflow-name">create-story</span> <span class="workflow-name">create-story</span>
</div> </div>
<div class="workflow-meta"> <div class="workflow-meta">
<div class="agent"><div class="agent-icon bob">B</div><span class="agent-name">Bob</span></div> <div class="agent"><div class="agent-icon amelia">A</div><span class="agent-name">Amelia</span></div>
<span class="output">story-[slug].md →</span> <span class="output">story-[slug].md →</span>
</div> </div>
</div> </div>
@ -308,7 +307,7 @@
<span class="badge adhoc">par Epic</span> <span class="badge adhoc">par Epic</span>
</div> </div>
<div class="workflow-meta"> <div class="workflow-meta">
<div class="agent"><div class="agent-icon bob">B</div><span class="agent-name">Bob</span></div> <div class="agent"><div class="agent-icon amelia">A</div><span class="agent-name">Amelia</span></div>
<span class="output">leçons</span> <span class="output">leçons</span>
</div> </div>
</div> </div>

View File

@ -93,7 +93,6 @@
.agent-icon.john { background: linear-gradient(135deg, #60a5fa, #3b82f6); } .agent-icon.john { background: linear-gradient(135deg, #60a5fa, #3b82f6); }
.agent-icon.sally { background: linear-gradient(135deg, #fbbf24, #f59e0b); color: #000; } .agent-icon.sally { background: linear-gradient(135deg, #fbbf24, #f59e0b); color: #000; }
.agent-icon.winston { background: linear-gradient(135deg, #a78bfa, #8b5cf6); } .agent-icon.winston { background: linear-gradient(135deg, #a78bfa, #8b5cf6); }
.agent-icon.bob { background: linear-gradient(135deg, #34d399, #10b981); color: #000; }
.agent-icon.amelia { background: linear-gradient(135deg, #fb7185, #ef4444); } .agent-icon.amelia { background: linear-gradient(135deg, #fb7185, #ef4444); }
.agent-name { font-size: 0.65rem; } .agent-name { font-size: 0.65rem; }
@ -272,7 +271,7 @@
<span class="workflow-name">sprint-planning</span> <span class="workflow-name">sprint-planning</span>
</div> </div>
<div class="workflow-meta"> <div class="workflow-meta">
<div class="agent"><div class="agent-icon bob">B</div><span class="agent-name">Bob</span></div> <div class="agent"><div class="agent-icon amelia">A</div><span class="agent-name">Amelia</span></div>
<span class="output">sprint-status.yaml →</span> <span class="output">sprint-status.yaml →</span>
</div> </div>
</div> </div>
@ -281,7 +280,7 @@
<span class="workflow-name">create-story</span> <span class="workflow-name">create-story</span>
</div> </div>
<div class="workflow-meta"> <div class="workflow-meta">
<div class="agent"><div class="agent-icon bob">B</div><span class="agent-name">Bob</span></div> <div class="agent"><div class="agent-icon amelia">A</div><span class="agent-name">Amelia</span></div>
<span class="output">story-[slug].md →</span> <span class="output">story-[slug].md →</span>
</div> </div>
</div> </div>
@ -319,7 +318,7 @@
<span class="badge adhoc">per epic</span> <span class="badge adhoc">per epic</span>
</div> </div>
<div class="workflow-meta"> <div class="workflow-meta">
<div class="agent"><div class="agent-icon bob">B</div><span class="agent-name">Bob</span></div> <div class="agent"><div class="agent-icon amelia">A</div><span class="agent-name">Amelia</span></div>
<span class="output">lessons</span> <span class="output">lessons</span>
</div> </div>
</div> </div>