feat(cli): add /list-envs and enhance /set-project interactivity

This commit is contained in:
sno 2026-02-20 12:20:59 +01:00
parent 89e310a6ce
commit eb6568c8c1
4 changed files with 55 additions and 15 deletions

View File

@ -49,18 +49,21 @@ Core and BMM workflows automatically check for the existence of `{project-root}/
- **If found**: It reads the content (e.g., "app-alpha") and overrides the `output_folder` to `_bmad-output/app-alpha`.
- **If not found**: It behaves like a standard single-project installation, outputting to `_bmad-output` root.
### The /list-envs Command
You can view all available environments created in your monorepo by running the `/list-envs` command. This will scan your `_bmad-output/` directory and display all existing project environments, as well as indicate which one is currently active.
### The /set-project Command
You can easily manage the active project context using the `/set-project` workflow.
**To set a context:**
1. Run `/set-project` in your chat.
2. Select "Set Project Context".
3. Enter the name of your project (e.g., `frontend`, `backend`, `mobile-app`).
1. Run `/set-project <env_name>` in your chat.
2. If the environment does not exist, you will be prompted to create it interactively.
3. If you run `/set-project` without an argument, it will automatically list available environments and prompt you to select one or create a new one.
**To clear context (return to single-project mode):**
1. Run `/set-project`.
2. Select "Clear Project Context".
1. Run `/set-project CLEAR`.
### Inline Override

View File

@ -0,0 +1,26 @@
---
name: list-envs
description: List available project environments for monorepo support
main_config: '{project-root}/_bmad/bmm/config.yaml'
---
# List Project Environments
**Goal:** List the available project context environments for BMAD artifacts.
**Your Role:** Configuration Assistant.
## WORKFLOW ARCHITECTURE
### 1. Identify Environments
- Use your file listing/system capabilities to examine the `{project-root}/_bmad-output/` directory.
- Identify all subdirectories within this path. Each subdirectory represents an available environment (ignore hidden directories starting with `.`).
- The root `_bmad-output/` directory itself represents the `default (root)` unset environment.
### 2. Output Results
1. Display a formatted bulleted list of the existing environments you found.
2. Clearly indicate the `default (root)` environment.
3. Check if there is an active environment currently set. Read `{project-root}/{{bmadFolderName}}/.current_project`. If it exists, indicate which environment from the list is currently active (e.g. by adding `(ACTIVE)` next to the bullet point).
4. Inform the user that they can switch to another environment or create a new one using the `/set-project <env_name>` command.

View File

@ -20,24 +20,35 @@ Load and read full config from {main_config} and resolve variables and artifact
### 2. Context Management
1. **Ask User:** "Please enter the **project name** or path relative to `_bmad-output/` (e.g. `project-name` or `auth-lib`). Enter `CLEAR` to reset to root."
2. **Wait for Input.**
3. **Process Input:**
1. **Analyze Request**: Determine the requested project name from the user's initial invocation (e.g., `/set-project my-app`).
2. **Wait for Input (If Missing)**: If the user did NOT provide a project name:
- Use your file listing capabilities to examine the `{project-root}/_bmad-output/` directory.
- Output: A formatted list of the existing environments (subdirectories) you found, explicitly noting the `default (root)` environment.
- Ask the user: "Please select an existing environment from the list above, or type a new name to create one. Enter `CLEAR` to reset to root."
- **Wait for Input.**
3. **Process Input**: Once a project name is established:
- **Case: CLEAR**:
- Delete file: `{project-root}/_bmad/.current_project`
- Delete file: `{project-root}/{{bmadFolderName}}/.current_project`
- Output: "✅ Project context cleared. Artifacts will go to root `_bmad-output/`."
- **HALT**
- **Case: Path Provided**:
- **1. Cleanup**: Remove leading/trailing slashes and any occurrences of `_bmad-output/`.
- **2. Validate - No Traversal**: Reject if path contains `..`.
- **3. Validate - No Absolute**: Reject if path starts with `/` or drive letter (e.g., `C:`).
- **4. Validate - Empty/Whitespace**: Reject if empty or only whitespace.
- **5. Validate - Whitelist**: Match against regex `^[a-zA-Z0-9._-/]+$`.
- **2. Validate Existence**: Check if `{project-root}/_bmad-output/<sanitized_path>` exists on the file system.
- **If it DOES NOT exist**:
- Ask: "The environment `<sanitized_path>` is not present. Do you want to create a new one? (Yes/No)"
- **Wait for Input.**
- **If No**: Inform the user to run `/list-envs` to see available environments or `/set-project` to try again. **HALT**.
- **If Yes**: Proceed to validation.
- **3. Validate - No Traversal**: Reject if path contains `..`.
- **4. Validate - No Absolute**: Reject if path starts with `/` or drive letter (e.g., `C:`).
- **5. Validate - Empty/Whitespace**: Reject if empty or only whitespace.
- **6. Validate - Whitelist**: Match against regex `^[a-zA-Z0-9._-/]+$`.
- **Check Results**:
- **If Invalid**:
- Output: "❌ Error: Invalid project context — must be a relative path and contain only alphanumeric characters, dots, dashes, underscores, or slashes. Traversal (..) is strictly forbidden."
- **HALT**
- **If Valid**:
- Write file: `{project-root}/_bmad/.current_project` with content `<sanitized_path>`
- Write file: `{project-root}/{{bmadFolderName}}/.current_project` with content `<sanitized_path>`
- Output: "✅ Project context set to: `<sanitized_path>`. Artifacts will go to `_bmad-output/<sanitized_path>/`."
### 3. Verification

View File

@ -115,7 +115,7 @@ async function runTests() {
assert(exists, 'set-project workflow file exists');
if (exists) {
const content = await fs.readFile(setProjectPath, 'utf8');
assert(content.includes('_bmad/.current_project'), 'set-project implementation manages .current_project');
assert(content.includes('{{bmadFolderName}}/.current_project'), 'set-project implementation manages .current_project');
const examplePattern = /(?:example|my[-_ ]?app|[a-z0-9]+-[a-z0-9]+)/i;
assert(examplePattern.test(content), 'set-project examples use generic public-friendly names');
}