feat: add scope variable for isolated planning/implementation artifacts (#2148)
When scope is set, planning_artifacts and implementation_artifacts resolve
to {output_folder}/{scope}/planning-artifacts/ and
{output_folder}/{scope}/implementation-artifacts/. The output_folder root
stays shared for project-context and brainstorming. Unscoped root folders
remain as the default bucket for quick-dev and unscoped work.
Empty or missing scope preserves current behavior (backward compatible).
Design validated by @alexeyv via Discord.
Fixes #2148
This commit is contained in:
parent
2302d9cdc5
commit
eac137afd9
|
|
@ -38,6 +38,7 @@ Nécessite [Node.js](https://nodejs.org) v20+ et `npx` (inclus avec npm).
|
||||||
| `--communication-language <langue>` | Langue de communication des agents | Anglais |
|
| `--communication-language <langue>` | Langue de communication des agents | Anglais |
|
||||||
| `--document-output-language <langue>` | Langue de sortie des documents | Anglais |
|
| `--document-output-language <langue>` | Langue de sortie des documents | Anglais |
|
||||||
| `--output-folder <chemin>` | Chemin du dossier de sortie (voir les règles de résolution ci-dessous) | `_bmad-output` |
|
| `--output-folder <chemin>` | Chemin du dossier de sortie (voir les règles de résolution ci-dessous) | `_bmad-output` |
|
||||||
|
| `--scope <nom>` | Nom de portée pour isoler les artefacts de planification/implémentation (voir ci-dessous) | _(vide - pas de portée)_ |
|
||||||
|
|
||||||
#### Résolution du chemin du dossier de sortie
|
#### Résolution du chemin du dossier de sortie
|
||||||
|
|
||||||
|
|
@ -46,10 +47,24 @@ La valeur passée à `--output-folder` (ou saisie de manière interactive) est r
|
||||||
| Type d'entrée | Exemple | Résolu comme |
|
| Type d'entrée | Exemple | Résolu comme |
|
||||||
|-------------------------------|----------------------------|--------------------------------------------------------------|
|
|-------------------------------|----------------------------|--------------------------------------------------------------|
|
||||||
| Chemin relatif (par défaut) | `_bmad-output` | `<racine-du-projet>/_bmad-output` |
|
| Chemin relatif (par défaut) | `_bmad-output` | `<racine-du-projet>/_bmad-output` |
|
||||||
| Chemin relatif avec traversée | `../../shared-outputs` | Chemin absolu normalisé — ex. `/Users/me/shared-outputs` |
|
| Chemin relatif avec traversée | `../../shared-outputs` | Chemin absolu normalisé - ex. `/Users/me/shared-outputs` |
|
||||||
| Chemin absolu | `/Users/me/shared-outputs` | Utilisé tel quel — la racine du projet n'est **pas** ajoutée |
|
| Chemin absolu | `/Users/me/shared-outputs` | Utilisé tel quel - la racine du projet n'est **pas** ajoutée |
|
||||||
|
|
||||||
Le chemin résolu est ce que les agents et les workflows vont utiliser lors de l'écriture des fichiers de sortie. L'utilisation d'un chemin absolu ou d'un chemin relatif avec traversée vous permet de diriger tous les artefacts générés vers un répertoire en dehors de l'arborescence de votre projet — utile pour les configurations partagées ou les monorepos.
|
Le chemin résolu est ce que les agents et les workflows vont utiliser lors de l'écriture des fichiers de sortie. L'utilisation d'un chemin absolu ou d'un chemin relatif avec traversée vous permet de diriger tous les artefacts générés vers un répertoire en dehors de l'arborescence de votre projet - utile pour les configurations partagées ou les monorepos.
|
||||||
|
|
||||||
|
#### Portée
|
||||||
|
|
||||||
|
Lorsque `--scope` est fourni, `planning_artifacts` et `implementation_artifacts` sont placés dans un sous-répertoire. Le dossier de sortie racine reste partagé pour le contexte projet et le brainstorming.
|
||||||
|
|
||||||
|
```
|
||||||
|
# Sans --scope :
|
||||||
|
_bmad-output/planning-artifacts/prd.md
|
||||||
|
|
||||||
|
# Avec --scope admin-portal :
|
||||||
|
_bmad-output/admin-portal/planning-artifacts/prd.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Le nom ne peut contenir que des lettres, chiffres, tirets, points ou underscores (`.` et `..` ne sont pas autorisés). L'omettre conserve la structure actuelle.
|
||||||
|
|
||||||
### Autres options
|
### Autres options
|
||||||
|
|
||||||
|
|
@ -60,16 +75,16 @@ Le chemin résolu est ce que les agents et les workflows vont utiliser lors de l
|
||||||
|
|
||||||
## IDs de modules
|
## IDs de modules
|
||||||
|
|
||||||
IDs de modules disponibles pour l’option `--modules` :
|
IDs de modules disponibles pour l'option `--modules` :
|
||||||
|
|
||||||
- `bmm` — méthode BMad Master
|
- `bmm` - méthode BMad Master
|
||||||
- `bmb` — BMad Builder
|
- `bmb` - BMad Builder
|
||||||
|
|
||||||
Consultez le [registre BMad](https://github.com/bmad-code-org) pour les modules externes disponibles.
|
Consultez le [registre BMad](https://github.com/bmad-code-org) pour les modules externes disponibles.
|
||||||
|
|
||||||
## IDs d'outils/IDE
|
## IDs d'outils/IDE
|
||||||
|
|
||||||
IDs d'outils disponibles pour l’option `--tools` :
|
IDs d'outils disponibles pour l'option `--tools` :
|
||||||
|
|
||||||
**Recommandés :** `claude-code`, `cursor`
|
**Recommandés :** `claude-code`, `cursor`
|
||||||
|
|
||||||
|
|
@ -140,15 +155,15 @@ npx bmad-method install \
|
||||||
|
|
||||||
BMad valide toutes les options fournis :
|
BMad valide toutes les options fournis :
|
||||||
|
|
||||||
- **Directory** — Doit être un chemin valide avec des permissions d'écriture
|
- **Directory** - Doit être un chemin valide avec des permissions d'écriture
|
||||||
- **Modules** — Avertit des IDs de modules invalides (mais n'échoue pas)
|
- **Modules** - Avertit des IDs de modules invalides (mais n'échoue pas)
|
||||||
- **Tools** — Avertit des IDs d'outils invalides (mais n'échoue pas)
|
- **Tools** - Avertit des IDs d'outils invalides (mais n'échoue pas)
|
||||||
- **Custom Content** — Chaque chemin doit contenir un fichier `module.yaml` valide
|
- **Custom Content** - Chaque chemin doit contenir un fichier `module.yaml` valide
|
||||||
- **Action** — Doit être l'une des suivantes : `install`, `update`, `quick-update`
|
- **Action** - Doit être l'une des suivantes : `install`, `update`, `quick-update`
|
||||||
|
|
||||||
Les valeurs invalides entraîneront soit :
|
Les valeurs invalides entraîneront soit :
|
||||||
1. L’affichage d’un message d'erreur suivi d’un exit (pour les options critiques comme le répertoire)
|
1. L'affichage d'un message d'erreur suivi d'un exit (pour les options critiques comme le répertoire)
|
||||||
2. Un avertissement puis la continuation de l’installation (pour les éléments optionnels comme le contenu personnalisé)
|
2. Un avertissement puis la continuation de l'installation (pour les éléments optionnels comme le contenu personnalisé)
|
||||||
3. Un retour aux invites interactives (pour les valeurs requises manquantes)
|
3. Un retour aux invites interactives (pour les valeurs requises manquantes)
|
||||||
|
|
||||||
:::tip[Bonnes pratiques]
|
:::tip[Bonnes pratiques]
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ Requires [Node.js](https://nodejs.org) v20+ and `npx` (included with npm).
|
||||||
| `--communication-language <lang>` | Agent communication language | English |
|
| `--communication-language <lang>` | Agent communication language | English |
|
||||||
| `--document-output-language <lang>` | Document output language | English |
|
| `--document-output-language <lang>` | Document output language | English |
|
||||||
| `--output-folder <path>` | Output folder path (see resolution rules below) | `_bmad-output` |
|
| `--output-folder <path>` | Output folder path (see resolution rules below) | `_bmad-output` |
|
||||||
|
| `--scope <name>` | Scope name to isolate planning/implementation artifacts (see below) | _(empty — no scope)_ |
|
||||||
|
|
||||||
#### Output Folder Path Resolution
|
#### Output Folder Path Resolution
|
||||||
|
|
||||||
|
|
@ -51,6 +52,20 @@ The value passed to `--output-folder` (or entered interactively) is resolved acc
|
||||||
|
|
||||||
The resolved path is what agents and workflows use at runtime when writing output files. Using an absolute path or a traversal-based relative path lets you direct all generated artifacts to a directory outside your project tree — useful for shared or monorepo setups.
|
The resolved path is what agents and workflows use at runtime when writing output files. Using an absolute path or a traversal-based relative path lets you direct all generated artifacts to a directory outside your project tree — useful for shared or monorepo setups.
|
||||||
|
|
||||||
|
#### Scope
|
||||||
|
|
||||||
|
When `--scope` is provided, `planning_artifacts` and `implementation_artifacts` are placed under a scoped subdirectory. The output folder root stays shared for project-context and brainstorming.
|
||||||
|
|
||||||
|
```
|
||||||
|
# Without --scope:
|
||||||
|
_bmad-output/planning-artifacts/prd.md
|
||||||
|
|
||||||
|
# With --scope admin-portal:
|
||||||
|
_bmad-output/admin-portal/planning-artifacts/prd.md
|
||||||
|
```
|
||||||
|
|
||||||
|
The name must contain only letters, numbers, hyphens, dots, or underscores (`.` and `..` are not allowed). Omitting it preserves the current flat structure.
|
||||||
|
|
||||||
### Other Options
|
### Other Options
|
||||||
|
|
||||||
| Flag | Description |
|
| Flag | Description |
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,21 @@ sidebar:
|
||||||
| `--communication-language <lang>` | 智能体通信语言 | 英语 |
|
| `--communication-language <lang>` | 智能体通信语言 | 英语 |
|
||||||
| `--document-output-language <lang>` | 文档输出语言 | 英语 |
|
| `--document-output-language <lang>` | 文档输出语言 | 英语 |
|
||||||
| `--output-folder <path>` | 输出文件夹路径 | _bmad-output |
|
| `--output-folder <path>` | 输出文件夹路径 | _bmad-output |
|
||||||
|
| `--scope <name>` | 范围名称,用于隔离规划/实施产物(见下文) | _(空 — 无范围)_ |
|
||||||
|
|
||||||
|
#### 范围
|
||||||
|
|
||||||
|
提供 `--scope` 时,`planning_artifacts` 和 `implementation_artifacts` 会放在范围子目录下。输出文件夹根目录保持共享。
|
||||||
|
|
||||||
|
```
|
||||||
|
# 不带 --scope:
|
||||||
|
_bmad-output/planning-artifacts/prd.md
|
||||||
|
|
||||||
|
# 带 --scope admin-portal:
|
||||||
|
_bmad-output/admin-portal/planning-artifacts/prd.md
|
||||||
|
```
|
||||||
|
|
||||||
|
名称只能包含字母、数字、连字符、点或下划线(不允许 `.` 和 `..`)。省略时保持当前的平面结构。
|
||||||
|
|
||||||
### 其他选项
|
### 其他选项
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,3 +23,9 @@ output_folder:
|
||||||
prompt: "Where should output files be saved?"
|
prompt: "Where should output files be saved?"
|
||||||
default: "_bmad-output"
|
default: "_bmad-output"
|
||||||
result: "{project-root}/{value}"
|
result: "{project-root}/{value}"
|
||||||
|
|
||||||
|
scope:
|
||||||
|
prompt: "Scope name to keep outputs separate (e.g., admin-portal). Enter to skip:"
|
||||||
|
default: ""
|
||||||
|
regex: "^$|^[a-zA-Z0-9._-]*[a-zA-Z0-9][a-zA-Z0-9._-]*$"
|
||||||
|
result: "{value}"
|
||||||
|
|
|
||||||
|
|
@ -1713,6 +1713,128 @@ async function runTests() {
|
||||||
|
|
||||||
console.log('');
|
console.log('');
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Test Suite 33: scope isolates planning/implementation artifacts
|
||||||
|
// ============================================================
|
||||||
|
console.log(`${colors.yellow}Test Suite 33: Scoped Output Isolation${colors.reset}\n`);
|
||||||
|
|
||||||
|
let tempBmadDir33;
|
||||||
|
try {
|
||||||
|
const { Installer } = require('../tools/installer/core/installer');
|
||||||
|
const testInstaller = new Installer();
|
||||||
|
const yaml = require('yaml');
|
||||||
|
|
||||||
|
tempBmadDir33 = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-scope-'));
|
||||||
|
const coreDir33 = path.join(tempBmadDir33, 'core');
|
||||||
|
const bmmDir33 = path.join(tempBmadDir33, 'bmm');
|
||||||
|
await fs.ensureDir(coreDir33);
|
||||||
|
await fs.ensureDir(bmmDir33);
|
||||||
|
|
||||||
|
// --- Scoped install ---
|
||||||
|
await testInstaller.generateModuleConfigs(tempBmadDir33, {
|
||||||
|
core: {
|
||||||
|
user_name: 'Test',
|
||||||
|
output_folder: '{project-root}/_bmad-output',
|
||||||
|
scope: 'admin-portal',
|
||||||
|
},
|
||||||
|
bmm: {
|
||||||
|
project_name: 'TestProject',
|
||||||
|
planning_artifacts: '{project-root}/_bmad-output/planning-artifacts',
|
||||||
|
implementation_artifacts: '{project-root}/_bmad-output/implementation-artifacts',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const core33 = yaml.parse(await fs.readFile(path.join(coreDir33, 'config.yaml'), 'utf8'));
|
||||||
|
const bmm33 = yaml.parse(await fs.readFile(path.join(bmmDir33, 'config.yaml'), 'utf8'));
|
||||||
|
|
||||||
|
assert(core33.output_folder === '{project-root}/_bmad-output', 'Core output_folder is NOT scoped');
|
||||||
|
assert(core33.scope === 'admin-portal', 'Core stores scope separately');
|
||||||
|
assert(bmm33.planning_artifacts === '{project-root}/_bmad-output/admin-portal/planning-artifacts', 'BMM planning_artifacts is scoped');
|
||||||
|
assert(
|
||||||
|
bmm33.implementation_artifacts === '{project-root}/_bmad-output/admin-portal/implementation-artifacts',
|
||||||
|
'BMM implementation_artifacts is scoped',
|
||||||
|
);
|
||||||
|
assert(bmm33.output_folder === '{project-root}/_bmad-output', 'BMM output_folder stays as shared root');
|
||||||
|
assert(bmm33.scope === 'admin-portal', 'BMM inherits scope from core spread');
|
||||||
|
|
||||||
|
// --- Empty scope (backward compat) ---
|
||||||
|
await testInstaller.generateModuleConfigs(tempBmadDir33, {
|
||||||
|
core: { user_name: 'Test', output_folder: '{project-root}/_bmad-output', scope: '' },
|
||||||
|
bmm: {
|
||||||
|
project_name: 'TestProject',
|
||||||
|
planning_artifacts: '{project-root}/_bmad-output/planning-artifacts',
|
||||||
|
implementation_artifacts: '{project-root}/_bmad-output/implementation-artifacts',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const bmmEmpty = yaml.parse(await fs.readFile(path.join(bmmDir33, 'config.yaml'), 'utf8'));
|
||||||
|
assert(
|
||||||
|
bmmEmpty.planning_artifacts === '{project-root}/_bmad-output/planning-artifacts',
|
||||||
|
'Empty scope preserves original planning_artifacts',
|
||||||
|
);
|
||||||
|
|
||||||
|
// --- Missing scope key (backward compat) ---
|
||||||
|
await testInstaller.generateModuleConfigs(tempBmadDir33, {
|
||||||
|
core: { user_name: 'Test', output_folder: '{project-root}/_bmad-output' },
|
||||||
|
bmm: {
|
||||||
|
project_name: 'TestProject',
|
||||||
|
planning_artifacts: '{project-root}/_bmad-output/planning-artifacts',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const bmmMissing = yaml.parse(await fs.readFile(path.join(bmmDir33, 'config.yaml'), 'utf8'));
|
||||||
|
assert(
|
||||||
|
bmmMissing.planning_artifacts === '{project-root}/_bmad-output/planning-artifacts',
|
||||||
|
'Missing scope key preserves original planning_artifacts',
|
||||||
|
);
|
||||||
|
|
||||||
|
// --- Scoped with raw output_folder (no {project-root} prefix, matches --yes path) ---
|
||||||
|
await testInstaller.generateModuleConfigs(tempBmadDir33, {
|
||||||
|
core: { user_name: 'Test', output_folder: '_bmad-output', scope: 'phase-2' },
|
||||||
|
bmm: {
|
||||||
|
project_name: 'TestProject',
|
||||||
|
planning_artifacts: '{project-root}/_bmad-output/planning-artifacts',
|
||||||
|
implementation_artifacts: '{project-root}/_bmad-output/implementation-artifacts',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const bmmRaw = yaml.parse(await fs.readFile(path.join(bmmDir33, 'config.yaml'), 'utf8'));
|
||||||
|
assert(
|
||||||
|
bmmRaw.planning_artifacts === '{project-root}/_bmad-output/phase-2/planning-artifacts',
|
||||||
|
'Raw output_folder (--yes path) still scopes correctly',
|
||||||
|
);
|
||||||
|
|
||||||
|
// --- Dot-segment scope rejected ---
|
||||||
|
let dotSegmentThrew = false;
|
||||||
|
try {
|
||||||
|
await testInstaller.generateModuleConfigs(tempBmadDir33, {
|
||||||
|
core: { user_name: 'Test', output_folder: '{project-root}/_bmad-output', scope: '..' },
|
||||||
|
bmm: { project_name: 'TestProject', planning_artifacts: '{project-root}/_bmad-output/planning-artifacts' },
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
dotSegmentThrew = error.message.includes('dot-segments');
|
||||||
|
}
|
||||||
|
assert(dotSegmentThrew, 'Dot-segment scope ".." is rejected');
|
||||||
|
|
||||||
|
// --- Custom path not under output_folder (scope bypassed) ---
|
||||||
|
await testInstaller.generateModuleConfigs(tempBmadDir33, {
|
||||||
|
core: { user_name: 'Test', output_folder: '{project-root}/_bmad-output', scope: 'test' },
|
||||||
|
bmm: {
|
||||||
|
project_name: 'TestProject',
|
||||||
|
planning_artifacts: '{project-root}/custom-folder/planning',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const bmmCustom = yaml.parse(await fs.readFile(path.join(bmmDir33, 'config.yaml'), 'utf8'));
|
||||||
|
assert(bmmCustom.planning_artifacts === '{project-root}/custom-folder/planning', 'Custom path not under output_folder bypasses scope');
|
||||||
|
} catch (error) {
|
||||||
|
assert(false, `Scoped output test error: ${error.message}`);
|
||||||
|
} finally {
|
||||||
|
if (tempBmadDir33) await fs.remove(tempBmadDir33).catch(() => {});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('');
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Summary
|
// Summary
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ module.exports = {
|
||||||
['--communication-language <lang>', 'Language for agent communication (default: English)'],
|
['--communication-language <lang>', 'Language for agent communication (default: English)'],
|
||||||
['--document-output-language <lang>', 'Language for document output (default: English)'],
|
['--document-output-language <lang>', 'Language for document output (default: English)'],
|
||||||
['--output-folder <path>', 'Output folder path relative to project root (default: _bmad-output)'],
|
['--output-folder <path>', 'Output folder path relative to project root (default: _bmad-output)'],
|
||||||
|
['--scope <name>', 'Scope name to isolate planning/implementation artifacts (e.g., admin-portal)'],
|
||||||
['-y, --yes', 'Accept all defaults and skip prompts where possible'],
|
['-y, --yes', 'Accept all defaults and skip prompts where possible'],
|
||||||
],
|
],
|
||||||
action: async (options) => {
|
action: async (options) => {
|
||||||
|
|
|
||||||
|
|
@ -823,6 +823,20 @@ class Installer {
|
||||||
|
|
||||||
// Create a comment section to identify core values
|
// Create a comment section to identify core values
|
||||||
coreSection = '\n# Core Configuration Values\n';
|
coreSection = '\n# Core Configuration Values\n';
|
||||||
|
|
||||||
|
// When scope is set, insert it after output_folder in artifact paths
|
||||||
|
if (finalConfig.scope && finalConfig.output_folder) {
|
||||||
|
if (finalConfig.scope === '.' || finalConfig.scope === '..') {
|
||||||
|
throw new Error(`Invalid scope "${finalConfig.scope}": dot-segments are not allowed`);
|
||||||
|
}
|
||||||
|
const folder = finalConfig.output_folder.replace(/[\\/]+$/, '');
|
||||||
|
for (const key of ['planning_artifacts', 'implementation_artifacts']) {
|
||||||
|
// Match only at a directory boundary (followed by / or end of string)
|
||||||
|
if (finalConfig[key]?.includes(`${folder}/`)) {
|
||||||
|
finalConfig[key] = finalConfig[key].replace(`${folder}/`, `${folder}/${finalConfig.scope}/`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean the config to remove any non-serializable values (like functions)
|
// Clean the config to remove any non-serializable values (like functions)
|
||||||
|
|
|
||||||
|
|
@ -713,7 +713,7 @@ class UI {
|
||||||
const configCollector = new OfficialModules();
|
const configCollector = new OfficialModules();
|
||||||
|
|
||||||
// Seed core config from CLI options if provided
|
// Seed core config from CLI options if provided
|
||||||
if (options.userName || options.communicationLanguage || options.documentOutputLanguage || options.outputFolder) {
|
if (options.userName || options.communicationLanguage || options.documentOutputLanguage || options.outputFolder || options.scope) {
|
||||||
const coreConfig = {};
|
const coreConfig = {};
|
||||||
if (options.userName) {
|
if (options.userName) {
|
||||||
coreConfig.user_name = options.userName;
|
coreConfig.user_name = options.userName;
|
||||||
|
|
@ -731,10 +731,36 @@ class UI {
|
||||||
coreConfig.output_folder = options.outputFolder;
|
coreConfig.output_folder = options.outputFolder;
|
||||||
await prompts.log.info(`Using output folder from command-line: ${options.outputFolder}`);
|
await prompts.log.info(`Using output folder from command-line: ${options.outputFolder}`);
|
||||||
}
|
}
|
||||||
|
if (options.scope) {
|
||||||
|
if (!/^[a-zA-Z0-9._-]+$/.test(options.scope) || options.scope === '.' || options.scope === '..') {
|
||||||
|
await prompts.log.error(
|
||||||
|
`Invalid scope "${options.scope}": use only letters, numbers, hyphens, dots, or underscores (not "." or "..")`,
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
coreConfig.scope = options.scope;
|
||||||
|
await prompts.log.info(`Using scope from command-line: ${options.scope}`);
|
||||||
|
}
|
||||||
|
|
||||||
// Load existing config to merge with provided options
|
// Load existing config to merge with provided options
|
||||||
await configCollector.loadExistingConfig(directory);
|
await configCollector.loadExistingConfig(directory);
|
||||||
const existingConfig = configCollector.collectedConfig.core || {};
|
const existingConfig = configCollector.collectedConfig.core || {};
|
||||||
|
// When --yes, fill in defaults for core values not provided via CLI
|
||||||
|
if (options.yes) {
|
||||||
|
if (!existingConfig.user_name) {
|
||||||
|
let safeUsername;
|
||||||
|
try {
|
||||||
|
safeUsername = os.userInfo().username;
|
||||||
|
} catch {
|
||||||
|
safeUsername = process.env.USER || process.env.USERNAME || 'User';
|
||||||
|
}
|
||||||
|
existingConfig.user_name = safeUsername.charAt(0).toUpperCase() + safeUsername.slice(1);
|
||||||
|
}
|
||||||
|
existingConfig.communication_language = existingConfig.communication_language || 'English';
|
||||||
|
existingConfig.document_output_language = existingConfig.document_output_language || 'English';
|
||||||
|
existingConfig.output_folder = existingConfig.output_folder || '_bmad-output';
|
||||||
|
existingConfig.scope = existingConfig.scope || '';
|
||||||
|
}
|
||||||
configCollector.collectedConfig.core = { ...existingConfig, ...coreConfig };
|
configCollector.collectedConfig.core = { ...existingConfig, ...coreConfig };
|
||||||
|
|
||||||
// If not all options are provided, collect the missing ones interactively (unless --yes flag)
|
// If not all options are provided, collect the missing ones interactively (unless --yes flag)
|
||||||
|
|
@ -743,6 +769,7 @@ class UI {
|
||||||
(!options.userName || !options.communicationLanguage || !options.documentOutputLanguage || !options.outputFolder)
|
(!options.userName || !options.communicationLanguage || !options.documentOutputLanguage || !options.outputFolder)
|
||||||
) {
|
) {
|
||||||
await configCollector.collectModuleConfig('core', directory, false, true);
|
await configCollector.collectModuleConfig('core', directory, false, true);
|
||||||
|
Object.assign(configCollector.collectedConfig.core, coreConfig);
|
||||||
}
|
}
|
||||||
} else if (options.yes) {
|
} else if (options.yes) {
|
||||||
// Use all defaults when --yes flag is set
|
// Use all defaults when --yes flag is set
|
||||||
|
|
@ -762,6 +789,7 @@ class UI {
|
||||||
communication_language: 'English',
|
communication_language: 'English',
|
||||||
document_output_language: 'English',
|
document_output_language: 'English',
|
||||||
output_folder: '_bmad-output',
|
output_folder: '_bmad-output',
|
||||||
|
scope: '',
|
||||||
};
|
};
|
||||||
await prompts.log.info('Using default configuration (--yes flag)');
|
await prompts.log.info('Using default configuration (--yes flag)');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue