Compare commits

..

6 Commits

Author SHA1 Message Date
Jacob du Toit 0183ba4be9
Merge f9e7d65cf9 into ad9cb7a177 2026-03-21 16:19:27 +01:00
Alex Verkhovsky ad9cb7a177
refactor(installer): remove dead .agent.yaml/.xml fallback logic (#2084) 2026-03-21 01:52:39 -06:00
Alex Verkhovsky 93a1e1dc46
refactor(installer): remove dead task/tool/workflow manifest code (#2083)
* refactor(installer): discover skills by SKILL.md instead of manifest YAML

Switch skill discovery gate from requiring bmad-skill-manifest.yaml with
type: skill to detecting any directory with a valid SKILL.md (frontmatter
name + description, name matches directory name). Delete 34 stub manifests
that carried no data beyond type: skill. Agent manifests (9) are retained
for persona metadata consumed by agent-manifest.csv.

* refactor(installer): remove dead task/tool/workflow manifest code

The remove-skill-manifest-yaml branch deleted the scanners that
discover tasks, tools, and workflows but left behind the code that
writes their manifest CSVs. Remove collectTasks/Tools/Workflows,
writeTaskManifest/ToolManifest/WorkflowManifest, their helpers, and
the now-unreachable getPreservedCsvRows/upgradeRowToSchema methods.
Update installer pre-registration and test assertions accordingly.
2026-03-21 00:12:40 -06:00
Alex Verkhovsky 31ae226bb4
refactor(installer): discover skills by SKILL.md instead of manifest YAML (#2082)
Switch skill discovery gate from requiring bmad-skill-manifest.yaml with
type: skill to detecting any directory with a valid SKILL.md (frontmatter
name + description, name matches directory name). Delete 34 stub manifests
that carried no data beyond type: skill. Agent manifests (9) are retained
for persona metadata consumed by agent-manifest.csv.
2026-03-21 00:11:23 -06:00
Alex Verkhovsky c28206dca4
refactor(installer): remove dead agent compilation pipeline (#2080)
* refactor(installer): remove dead agent compilation pipeline

Delete 9 files (~2,600 lines) that compiled .agent.yaml to .md.
No .agent.yaml files exist in the source tree — agents now ship
as pre-built SKILL.md. Clean up all references in installer,
module manager, custom handler, base IDE, UI, and tests.

* refactor(custom-handler): remove dead install/copy/find methods

CustomHandler.install(), copyDirectory(), and findFilesRecursively()
are never called — custom modules are installed via moduleManager.install()
since Dec 2025. Also removes unused FileOps import and constructor.

Verified with before/after clean-installer comparison (codex + custom
modules with custom.yaml): output is identical.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(installer): remove dead compilation refs from docs and module manager

Address review findings from PR #2080 triage:
- Remove compile-agents from CLI action docs (en, fr, zh-cn)
- Remove dead vendorCrossModuleWorkflows() and .agent.yaml skip logic
- Clean stale compilation-era comments in manifest-generator

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 22:52:02 -06:00
Emmanuel Atsé 1a6f8d52bc
docs: i18n: Add complete French translation (#2073)
* docs: i18n(fr): add complete French translation

Add comprehensive French (fr-FR) translation covering all documentation
sections and website configuration:

- Core docs, How-to guides, Explanations, References
- Website config: astro.config.mjs locale setup, fr-FR.json i18n strings
- Assets: French workflow map diagram and quick-dev diagram

Terminology standardized on "Quick Dev" (replacing legacy "Quick Flow").

* docs(fr): remove references to deleted phase-4 agent personas

Remove all references to deleted phase-4 agent personas from French
documentation, matching upcoming PR #2020 changes:

- Remove agent personas: dev/Amelia, pm/John, qa/Quinn, sm/Bob,
  quick-flow-solo-dev/Barry, bmad-master
- Replace deleted agent skill invocations with equivalent workflow skills
  (bmad-dev-story, bmad-qa-generate-e2e-tests, bmad-quick-dev, etc.)
- Depersonalize QA references ("Quinn" → "QA Intégré" / "Workflow QA")
- Simplify workflow invocation instructions (remove "invoke agent" pattern)
- Fix upgrade-to-v6.md SM/Scrum Master references

* docs(fr): fix typos

---------

Co-authored-by: Alex Verkhovsky <alexey.verkhovsky@gmail.com>
2026-03-20 17:10:49 -06:00
89 changed files with 3952 additions and 4523 deletions

8
docs/fr/404.md Normal file
View File

@ -0,0 +1,8 @@
---
title: Page introuvable
template: splash
---
La page que vous recherchez n'existe pas ou a été déplacée.
[Retour à l'accueil](/fr/index.md)

370
docs/fr/_STYLE_GUIDE.md Normal file
View File

@ -0,0 +1,370 @@
---
title: "Guide de style de la documentation"
description: Conventions de documentation spécifiques au projet, basées sur le style Google et la structure Diataxis
---
Ce projet suit le [Guide de style de documentation pour développeurs Google](https://developers.google.com/style) et utilise [Diataxis](https://diataxis.fr/) pour structurer le contenu. Seules les conventions spécifiques au projet sont présentées ci-dessous.
## Règles spécifiques au projet
| Règle | Spécification |
| --------------------------------------- | ------------------------------------------------------ |
| Pas de règles horizontales (`---`) | Perturbe le flux de lecture des fragments |
| Pas de titres `####` | Utiliser du texte en gras ou des admonitions |
| Pas de sections « Related » ou « Next: » | La barre latérale gère la navigation |
| Pas de listes profondément imbriquées | Diviser en sections à la place |
| Pas de blocs de code pour non-code | Utiliser des admonitions pour les exemples de dialogue |
| Pas de paragraphes en gras pour les appels | Utiliser des admonitions à la place |
| 1-2 admonitions max par section | Les tutoriels permettent 3-4 par section majeure |
| Cellules de tableau / éléments de liste | 1-2 phrases maximum |
| Budget de titres | 8-12 `##` par doc ; 2-3 `###` par section |
## Admonitions (Syntaxe Starlight)
```md
:::tip[Titre]
Raccourcis, bonnes pratiques
:::
:::note[Titre]
Contexte, définitions, exemples, prérequis
:::
:::caution[Titre]
Mises en garde, problèmes potentiels
:::
:::danger[Titre]
Avertissements critiques uniquement — perte de données, problèmes de sécurité
:::
```
### Utilisations standards
| Admonition | Usage |
| -------------------------- | ---------------------------------------- |
| `:::note[Pré-requis]` | Dépendances avant de commencer |
| `:::tip[Chemin rapide]` | Résumé TL;DR en haut du document |
| `:::caution[Important]` | Mises en garde critiques |
| `:::note[Exemple]` | Exemples de commandes/réponses |
## Formats de tableau standards
**Phases :**
```md
| Phase | Nom | Ce qui se passe |
| ----- | ---------- | --------------------------------------------------- |
| 1 | Analyse | Brainstorm, recherche *(optionnel)* |
| 2 | Planification | Exigences — PRD ou spécification technique *(requis)* |
```
**Skills :**
```md
| Skill | Agent | Objectif |
| ------------------- | ------- | ----------------------------------------------- |
| `bmad-brainstorming` | Analyste | Brainstorming pour un nouveau projet |
| `bmad-create-prd` | PM | Créer un document d'exigences produit |
```
## Blocs de structure de dossiers
À afficher dans les sections "Ce que vous avez accompli" :
````md
```
votre-projet/
├── _bmad/ # Configuration BMad
├── _bmad-output/
│ ├── planning-artifacts/
│ │ └── PRD.md # Votre document d'exigences
│ ├── implementation-artifacts/
│ └── project-context.md # Règles d'implémentation (optionnel)
└── ...
```
````
## Structure des tutoriels
```text
1. Titre + Accroche (1-2 phrases décrivant le résultat)
2. Notice de version/module (admonition info ou avertissement) (optionnel)
3. Ce que vous allez apprendre (liste à puces des résultats)
4. Prérequis (admonition info)
5. Chemin rapide (admonition tip - résumé TL;DR)
6. Comprendre [Sujet] (contexte avant les étapes - tableaux pour phases/agents)
7. Installation (optionnel)
8. Étape 1 : [Première tâche majeure]
9. Étape 2 : [Deuxième tâche majeure]
10. Étape 3 : [Troisième tâche majeure]
11. Ce que vous avez accompli (résumé + structure de dossiers)
12. Référence rapide (tableau des compétences)
13. Questions courantes (format FAQ)
14. Obtenir de l'aide (liens communautaires)
15. Points clés à retenir (admonition tip)
```
### Liste de vérification des tutoriels
- [ ] L'accroche décrit le résultat en 1-2 phrases
- [ ] Section "Ce que vous allez apprendre" présente
- [ ] Prérequis dans une admonition
- [ ] Admonition TL;DR de chemin rapide en haut
- [ ] Tableaux pour phases, skills, agents
- [ ] Section "Ce que vous avez accompli" présente
- [ ] Tableau de référence rapide présent
- [ ] Section questions courantes présente
- [ ] Section obtenir de l'aide présente
- [ ] Admonition points clés à retenir à la fin
## Structure des guides pratiques (How-To)
```text
1. Titre + Accroche (une phrase : « Utilisez le workflow `X` pour... »)
2. Quand utiliser ce guide (liste à puces de scénarios)
3. Quand éviter ce guide (optionnel)
4. Prérequis (admonition note)
5. Étapes (sous-sections ### numérotées)
6. Ce que vous obtenez (produits de sortie/artefacts)
7. Exemple (optionnel)
8. Conseils (optionnel)
9. Prochaines étapes (optionnel)
```
### Liste de vérification des guides pratiques
- [ ] L'accroche commence par « Utilisez le workflow `X` pour... »
- [ ] "Quand utiliser ce guide" contient 3-5 points
- [ ] Prérequis listés
- [ ] Les étapes sont des sous-sections `###` numérotées avec des verbes d'action
- [ ] "Ce que vous obtenez" décrit les artefacts produits
## Structure des explications
### Types
| Type | Exemple |
| ----------------------- | ------------------------------------ |
| **Index/Page d'accueil** | `core-concepts/index.md` |
| **Concept** | `what-are-agents.md` |
| **Fonctionnalité** | `quick-dev.md` |
| **Philosophie** | `why-solutioning-matters.md` |
| **FAQ** | `established-projects-faq.md` |
### Modèle général
```text
1. Titre + Accroche (1-2 phrases)
2. Vue d'ensemble/Définition (ce que c'est, pourquoi c'est important)
3. Concepts clés (sous-sections ###)
4. Tableau comparatif (optionnel)
5. Quand utiliser / Quand ne pas utiliser (optionnel)
6. Diagramme (optionnel - mermaid, 1 max par doc)
7. Prochaines étapes (optionnel)
```
### Pages d'index/d'accueil
```text
1. Titre + Accroche (une phrase)
2. Tableau de contenu (liens avec descriptions)
3. Pour commencer (liste numérotée)
4. Choisissez votre parcours (optionnel - arbre de décision)
```
### Explications de concepts
```text
1. Titre + Accroche (ce que c'est)
2. Types/Catégories (sous-sections ###) (optionnel)
3. Tableau des différences clés
4. Composants/Parties
5. Lequel devriez-vous utiliser ?
6. Création/Personnalisation (lien vers les guides pratiques)
```
### Explications de fonctionnalités
```text
1. Titre + Accroche (ce que cela fait)
2. Faits rapides (optionnel - "Idéal pour :", "Temps :")
3. Quand utiliser / Quand ne pas utiliser
4. Comment cela fonctionne (diagramme mermaid optionnel)
5. Avantages clés
6. Tableau comparatif (optionnel)
7. Quand évoluer/mettre à niveau (optionnel)
```
### Documents de philosophie/justification
```text
1. Titre + Accroche (le principe)
2. Le problème
3. La solution
4. Principes clés (sous-sections ###)
5. Avantages
6. Quand cela s'applique
```
### Liste de vérification des explications
- [ ] L'accroche énonce ce que le document explique
- [ ] Contenu dans des sections `##` parcourables
- [ ] Tableaux comparatifs pour 3+ options
- [ ] Les diagrammes ont des étiquettes claires
- [ ] Liens vers les guides pratiques pour les questions procédurales
- [ ] 2-3 admonitions max par document
## Structure des références
### Types
| Type | Exemple |
| ----------------------- | --------------------- |
| **Index/Page d'accueil** | `workflows/index.md` |
| **Catalogue** | `agents/index.md` |
| **Approfondissement** | `document-project.md` |
| **Configuration** | `core-tasks.md` |
| **Glossaire** | `glossary/index.md` |
| **Complet** | `bmgd-workflows.md` |
### Pages d'index de référence
```text
1. Titre + Accroche (une phrase)
2. Sections de contenu (## pour chaque catégorie)
- Liste à puces avec liens et descriptions
```
### Référence de catalogue
```text
1. Titre + Accroche
2. Éléments (## pour chaque élément)
- Brève description (une phrase)
- **Skills :** ou **Infos clés :** sous forme de liste simple
3. Universel/Partagé (## section) (optionnel)
```
### Référence d'approfondissement d'élément
```text
1. Titre + Accroche (objectif en une phrase)
2. Faits rapides (admonition note optionnelle)
- Module, Skill, Entrée, Sortie sous forme de liste
3. Objectif/Vue d'ensemble (## section)
4. Comment invoquer (bloc de code)
5. Sections clés (## pour chaque aspect)
- Utiliser ### pour les sous-options
6. Notes/Mises en garde (admonition tip ou caution)
```
### Référence de configuration
```text
1. Titre + Accroche
2. Table des matières (liens de saut si 4+ éléments)
3. Éléments (## pour chaque config/tâche)
- **Résumé en gras** — une phrase
- **Utilisez-le quand :** liste à puces
- **Comment cela fonctionne :** étapes numérotées (3-5 max)
- **Sortie :** résultat attendu (optionnel)
```
### Guide de référence complet
```text
1. Titre + Accroche
2. Vue d'ensemble (## section)
- Diagramme ou tableau montrant l'organisation
3. Sections majeures (## pour chaque phase/catégorie)
- Éléments (### pour chaque élément)
- Champs standardisés : Skill, Agent, Entrée, Sortie, Description
4. Prochaines étapes (optionnel)
```
### Liste de vérification des références
- [ ] L'accroche énonce ce que le document référence
- [ ] La structure correspond au type de référence
- [ ] Les éléments utilisent une structure cohérente
- [ ] Tableaux pour les données structurées/comparatives
- [ ] Liens vers les documents d'explication pour la profondeur conceptuelle
- [ ] 1-2 admonitions max
## Structure du glossaire
Starlight génère la navigation "Sur cette page" à droite à partir des titres :
- Catégories en tant que titres `##` — apparaissent dans la navigation à droite
- Termes dans des tableaux — lignes compactes, pas de titres individuels
- Pas de TOC en ligne — la barre latérale à droite gère la navigation
### Format de tableau
```md
## Nom de catégorie
| Terme | Définition |
| ------------ | --------------------------------------------------------------------------------------------- |
| **Agent** | Personnalité IA spécialisée avec une expertise spécifique qui guide les utilisateurs dans les workflows. |
| **Workflow** | Processus guidé en plusieurs étapes qui orchestre les activités des agents IA pour produire des livrables. |
```
### Règles de définition
| À faire | À ne pas faire |
| --------------------------------- | --------------------------------------------- |
| Commencer par ce que c'est ou ce que cela fait | Commencer par « C'est... » ou « Un [terme] est... » |
| Se limiter à 1-2 phrases | Écrire des explications de plusieurs paragraphes |
| Mettre le nom du terme en gras dans la cellule | Utiliser du texte simple pour les termes |
### Marqueurs de contexte
Ajouter un contexte en italique au début de la définition pour les termes à portée limitée :
- `*Quick Dev uniquement.*`
- `*méthode BMad/Enterprise.*`
- `*Phase N.*`
- `*BMGD.*`
- `*Projets établis.*`
### Liste de vérification du glossaire
- [ ] Termes dans des tableaux, pas de titres individuels
- [ ] Termes alphabétisés au sein des catégories
- [ ] Définitions de 1-2 phrases
- [ ] Marqueurs de contexte en italique
- [ ] Noms des termes en gras dans les cellules
- [ ] Pas de définitions « Un [terme] est... »
## Sections FAQ
```md
## Questions
- [Ai-je toujours besoin d'architecture ?](#ai-je-toujours-besoin-darchitecture)
- [Puis-je modifier mon plan plus tard ?](#puis-je-modifier-mon-plan-plus-tard)
### Ai-je toujours besoin d'architecture ?
Uniquement pour les parcours méthode BMad et Enterprise. Quick Dev passe directement à l'implémentation.
### Puis-je modifier mon plan plus tard ?
Oui. Utilisez `bmad-correct-course` pour gérer les changements de portée.
**Une question sans réponse ici ?** [Ouvrez une issue](...) ou posez votre question sur [Discord](...).
```
## Commandes de validation
Avant de soumettre des modifications de documentation :
```bash
npm run docs:fix-links # Prévisualiser les corrections de format de liens
npm run docs:fix-links -- --write # Appliquer les corrections
npm run docs:validate-links # Vérifier que les liens existent
npm run docs:build # Vérifier l'absence d'erreurs de build
```

View File

@ -0,0 +1,49 @@
---
title: "Élicitation Avancée"
description: Pousser le LLM à repenser son travail en utilisant des méthodes de raisonnement structurées
sidebar:
order: 6
---
Faites repenser au LLM ce qu'il vient de générer. Vous choisissez une méthode de raisonnement, il l'applique à sa propre sortie, et vous décidez de conserver ou non les améliorations.
## Qu'est-ce que lÉlicitation Avancée ?
Un second passage structuré. Au lieu de demander à l'IA de "réessayer" ou de "faire mieux", vous sélectionnez une méthode de raisonnement spécifique et l'IA réexamine sa propre sortie à travers ce prisme.
La différence est importante. Les demandes vagues produisent des révisions vagues. Une méthode nommée impose un angle d'attaque particulier, mettant en lumière des perspectives qu'un simple réajustement générique aurait manquées.
## Quand l'utiliser
- Après qu'un workflow a généré du contenu et vous souhaitez des alternatives
- Lorsque la sortie semble correcte mais que vous soupçonnez qu'il y a davantage de profondeur
- Pour tester les hypothèses ou trouver des faiblesses
- Pour du contenu à enjeux élevés où la réflexion approfondie aide
Les workflows offrent l'élicitation aux points de décision - après que le LLM ait généré quelque chose, on vous demandera si vous souhaitez l'exécuter.
## Comment ça fonctionne
1. Le LLM suggère 5 méthodes pertinentes pour votre contenu
2. Vous en choisissez une (ou remélangez pour différentes options)
3. La méthode est appliquée, les améliorations sont affichées
4. Acceptez ou rejetez, répétez ou continuez
## Méthodes intégrées
Des dizaines de méthodes de raisonnement sont disponibles. Quelques exemples :
- **Analyse Pré-mortem** - Suppose que le projet a déjà échoué, revient en arrière pour trouver pourquoi
- **Pensée de Premier Principe** - Élimine les hypothèses, reconstruit à partir de la vérité de terrain
- **Inversion** - Demande comment garantir l'échec, puis les évite
- **Équipe Rouge vs Équipe Bleue** - Attaque votre propre travail, puis le défend
- **Questionnement Socratique** - Conteste chaque affirmation avec "pourquoi ?" et "comment le savez-vous ?"
- **Suppression des Contraintes** - Abandonne toutes les contraintes, voit ce qui change, les réajoute sélectivement
- **Cartographie des Parties Prenantes** - Réévalue depuis la perspective de chaque partie prenante
- **Raisonnement Analogique** - Trouve des parallèles dans d'autres domaines et applique leurs leçons
Et bien d'autres. L'IA choisit les options les plus pertinentes pour votre contenu - vous choisissez lesquelles exécuter.
:::tip[Commencez Ici]
L'Analyse Pré-mortem est un bon premier choix pour toute spécification ou tout plan. Elle trouve systématiquement des lacunes qu'une révision standard manque.
:::

View File

@ -0,0 +1,66 @@
---
title: "Revue Contradictoire"
description: Technique de raisonnement forcée qui empêche les revues paresseuses du style "ça à l'air bon"
sidebar:
order: 5
---
Forcez une analyse plus approfondie en exigeant que des problèmes soient trouvés.
## Qu'est-ce que la Revue Contradictoire ?
Une technique de revue où le réviseur *doit* trouver des problèmes. Pas de "ça a l'air bon" autorisé. Le réviseur adopte une posture cynique - suppose que des problèmes existent et les trouve.
Il ne s'agit pas d'être négatif. Il s'agit de forcer une analyse authentique au lieu d'un coup d'œil superficiel qui valide automatiquement ce qui a été soumis.
**La règle fondamentale :** Il doit trouver des problèmes. Zéro constatation déclenche un arrêt - réanalyse ou explique pourquoi.
## Pourquoi Cela Fonctionne
Les revues normales souffrent du biais de confirmation[^1]. Il parcourt le travail rapidement, rien ne lui saute aux yeux, il l'approuve. L'obligation de "trouver des problèmes" brise ce schéma :
- **Force la rigueur** - Impossible d'approuver tant quil n'a pas examiné suffisamment en profondeur pour trouver des problèmes
- **Détecte les oublis** - "Qu'est-ce qui manque ici ?" devient une question naturelle
- **Améliore la qualité du signal** - Les constatations sont spécifiques et actionnables, pas des préoccupations vagues
- **Asymétrie d'information**[^2] - Effectue les revues avec un contexte frais (sans accès au raisonnement original) pour évaluer l'artefact, pas l'intention
## Où Elle Est Utilisée
La revue contradictoire apparaît dans tous les workflows BMad - revue de code, vérifications de préparation à l'implémentation, validation de spécifications, et d'autres. Parfois c'est une étape obligatoire, parfois optionnelle (comme l'élicitation avancée ou le mode party). Le pattern s'adapte à n'importe quel artefact nécessitant un examen.
## Filtrage Humain Requis
Parce que l'IA est *instruite* de trouver des problèmes, elle trouvera des problèmes - même lorsqu'ils n'existent pas. Attendez-vous à des faux positifs : des détails présentés comme des problèmes, des malentendus sur l'intention, ou des préoccupations purement hallucinées[^3].
**C'est vous qui décidez ce qui est réel.** Examinez chaque constatation, ignorez le bruit, corrigez ce qui compte.
## Exemple
Au lieu de :
> "L'implémentation de l'authentification semble raisonnable. Approuvé."
Une revue contradictoire produit :
> 1. **ÉLEVÉ** - `login.ts:47` - Pas de limitation de débit sur les tentatives échouées
> 2. **ÉLEVÉ** - Jeton de session stocké dans localStorage (vulnérable au XSS)
> 3. **MOYEN** - La validation du mot de passe se fait côté client uniquement
> 4. **MOYEN** - Pas de journalisation d'audit pour les tentatives de connexion échouées
> 5. **FAIBLE** - Le nombre magique `3600` devrait être `SESSION_TIMEOUT_SECONDS`
La première revue pourrait manquer une vulnérabilité de sécurité. La seconde en a attrapé quatre.
## Itération et Rendements Décroissants
Après avoir traité les constatations, envisagez de relancer la revue. Une deuxième passe détecte généralement plus de problèmes. Une troisième n'est pas toujours inutile non plus. Mais chaque passe prend du temps, et vous finissez par atteindre des rendements décroissants[^4] - juste des détails et des faux problèmes.
:::tip[Meilleures Revues]
Supposez que des problèmes existent. Cherchez ce qui manque, pas seulement ce qui ne va pas.
:::
## Glossaire
[^1]: **Biais de confirmation** : tendance cognitive à rechercher, interpréter et favoriser les informations qui confirment nos croyances préexistantes, tout en ignorant ou minimisant celles qui les contredisent.
[^2]: **Asymétrie d'information** : situation où une partie dispose de plus ou de meilleures informations qu'une autre, conduisant potentiellement à des décisions ou jugements biaisés.
[^3]: **Hallucination (IA)** : phénomène où un modèle d'IA génère des informations plausibles mais factuellement incorrectes ou inventées, présentées avec confiance comme si elles étaient vraies.
[^4]: **Rendements décroissants** : principe selon lequel l'augmentation continue d'un investissement (temps, effort, ressources) finit par produire des bénéfices de plus en plus faibles proportionnellement.

View File

@ -0,0 +1,33 @@
---
title: "Brainstorming"
description: Sessions interactives créatives utilisant plus de 60 techniques d'idéation éprouvées
sidebar:
order: 2
---
Libérez votre créativité grâce à une exploration guidée.
## Qu'est-ce que le Brainstorming ?
Lancez `bmad-brainstorming` et vous obtenez un facilitateur créatif qui fait émerger vos idées - pas qui les génère pour vous. L'IA agit comme coach et guide, utilisant des techniques éprouvées pour créer les conditions où votre meilleure réflexion émerge.
**Idéal pour :**
- Surmonter les blocages créatifs
- Générer des idées de produits ou de fonctionnalités
- Explorer des problèmes sous de nouveaux angles
- Développer des concepts bruts en plans d'action
## Comment ça fonctionne
1. **Configuration** - Définir le sujet, les objectifs, les contraintes
2. **Choisir l'approche** - Choisir vous-même les techniques, obtenir des recommandations de l'IA, aller au hasard, ou suivre un flux progressif
3. **Facilitation** - Travailler à travers les techniques avec des questions approfondies et un coaching collaboratif
4. **Organiser** - Idées regroupées par thèmes et priorisées
5. **Action** - Les meilleures idées reçoivent des prochaines étapes et des indicateurs de succès
Tout est capturé dans un document de session que vous pouvez consulter ultérieurement ou partager avec les parties prenantes.
:::note[Vos Idées]
Chaque idée vient de vous. Le workflow crée les conditions propices à une vision nouvelle - vous en êtes la source.
:::

View File

@ -0,0 +1,50 @@
---
title: "FAQ Projets Existants"
description: Questions courantes sur l'utilisation de la méthode BMad sur des projets existants
sidebar:
order: 8
---
Réponses rapides aux questions courantes sur l'utilisation de la méthode BMad (BMM) sur des projets existants.
## Questions
- [Dois-je d'abord exécuter document-project ?](#dois-je-dabord-exécuter-document-project)
- [Que faire si j'oublie d'exécuter document-project ?](#que-faire-si-joublie-dexécuter-document-project)
- [Puis-je utiliser Quick Dev pour les projets existants ?](#puis-je-utiliser-quick-dev-pour-les-projets-existants)
- [Que faire si mon code existant ne suit pas les bonnes pratiques ?](#que-faire-si-mon-code-existant-ne-suit-pas-les-bonnes-pratiques)
### Dois-je d'abord exécuter `document-project` ?
Hautement recommandé, surtout si :
- Aucune documentation existante
- La documentation est obsolète
- Les agents IA ont besoin de contexte sur le code existant
Vous pouvez l'ignorer si vous disposez d'une documentation complète et à jour incluant `docs/index.md` ou si vous utiliserez d'autres outils ou techniques pour aider à la découverte afin que l'agent puisse construire sur un système existant.
### Que faire si j'oublie d'exécuter `document-project` ?
Ne vous inquiétez pas — vous pouvez le faire à tout moment. Vous pouvez même le faire pendant ou après un projet pour aider à maintenir la documentation à jour.
### Puis-je utiliser Quick Dev pour les projets existants ?
Oui ! Quick Dev fonctionne très bien pour les projets existants. Il va :
- Détecter automatiquement votre pile technologique existante
- Analyser les patterns de code existants
- Détecter les conventions et demander confirmation
- Générer une spécification technique riche en contexte qui respecte le code existant
Parfait pour les corrections de bugs et les petites fonctionnalités dans des bases de code existantes.
### Que faire si mon code existant ne suit pas les bonnes pratiques ?
Quick Dev détecte vos conventions et demande : « Dois-je suivre ces conventions existantes ? » Vous décidez :
- **Oui** → Maintenir la cohérence avec la base de code actuelle
- **Non** → Établir de nouvelles normes (documenter pourquoi dans la spécification technique)
BMM respecte votre choix — il ne forcera pas la modernisation, mais la proposera.
**Une question sans réponse ici ?** Veuillez [ouvrir un ticket](https://github.com/bmad-code-org/BMAD-METHOD/issues) ou poser votre question sur [Discord](https://discord.gg/gk8jAdXWmj) afin que nous puissions l'ajouter !

View File

@ -0,0 +1,64 @@
---
title: "Party Mode"
description: Collaboration multi-agents - regroupez tous vos agents IA dans une seule conversation
sidebar:
order: 7
---
Regroupez tous vos agents IA dans une seule conversation.
## Qu'est-ce que le Party Mode ?
Lancez `bmad-party-mode` et vous avez toute votre équipe IA dans une même pièce - PM, Architecte, Développeur, Designer UX, selon vos besoins. BMad Master orchestre, en sélectionnant les agents pertinents à chaque message. Les agents répondent en personnage, sont en accord ou désaccord, et construisent sur les idées des autres.
La conversation continue aussi longtemps que vous le souhaitez. Posez des questions de suivi, remettez en question les réponses, redirigez la discussion - c'est un véritable échange avec vos agents jusqu'à ce que vous ayez terminé.
**Idéal pour :**
- Les grandes décisions avec des compromis
- Les sessions de brainstorming
- Les rétrospectives post-incident quand les choses tournent mal
- Les rétrospectives de sprint et la planification
## Exemples
### Remettre en question une mauvaise architecture
**Vous :** « Ok l'équipe, notre dernier sprint a été un désastre. Le service d'authentification est tombé trois fois. Qui a cafouillé ? »
**Architecte :** « Le design était solide - authentification distribuée avec des fallbacks appropriés. Si l'implémentation a dévié des specs... »
**Développeur :** « J'ai suivi le document d'architecture à la lettre. Les specs ne prenaient pas en compte les race conditions lors de l'invalidation de sessions concurrentes. »
**PM :** « Vous avez tous les deux raté le vrai problème - nous n'avons pas validé les exigences de gestion des sessions dans le PRD. C'est de ma faute de ne pas l'avoir repéré. »
**TEA :** « Et j'aurais dû le repérer dans les tests d'intégration. Les scénarios de test ne couvraient pas l'invalidation concurrente. »
### Brainstorming créatif
**Vous :** « Comment rendre l'onboarding magique au lieu d'ennuyeux ? »
**Designer UX :** « Commençons par la divulgation progressive - révélons les fonctionnalités au fur et à mesure que les utilisateurs en ont besoin, pas d'un coup dans un tutoriel. »
**Storyteller :** « Et si l'onboarding était une histoire ? Chaque étape révèle le parcours d'un personnage - l'utilisateur EST le héros. »
**Game Designer :** « En partant de là - et si la première "quête" consistait à résoudre un vrai problème utilisateur ? Ils apprennent en faisant quelque chose d'utile. »
### Décision technique
**Vous :** « Monolithe ou microservices pour le MVP[^1] ? »
**Architecte :** « Commencez en monolithe. Les microservices ajoutent une complexité dont vous n'avez pas besoin à 1000 utilisateurs. »
**PM :** « D'accord. Le time-to-market[^2] compte plus que la scalabilité théorique. »
**Développeur :** « Monolithe avec des frontières de modules claires. On pourra extraire des services plus tard si nécessaire. »
:::tip[Meilleures décisions]
De meilleures décisions grâce à des perspectives diverses. Bienvenue dans le party mode.
:::
## Glossaire
[^1]: MVP (Minimum Viable Product) : version minimale d'un produit contenant juste assez de fonctionnalités pour être utilisée par des utilisateurs précoces et valider les hypothèses de marché avant d'investir dans un développement plus complet.
[^2]: Time-to-market : délai nécessaire pour concevoir, développer et lancer un produit sur le marché. Plus ce délai est court, plus l'entreprise peut prendre de l'avance sur ses concurrents.

View File

@ -0,0 +1,117 @@
---
title: "Prévention des conflits entre agents"
description: Comment l'architecture empêche les conflits lorsque plusieurs agents implémentent un système
sidebar:
order: 4
---
Lorsque plusieurs agents IA implémentent différentes parties d'un système, ils peuvent prendre des décisions techniques contradictoires. La documentation d'architecture prévient cela en établissant des standards partagés.
## Types de conflits courants
### Conflits de style d'API
Sans architecture :
- L'agent A utilise REST avec `/users/{id}`
- L'agent B utilise des mutations GraphQL
- Résultat : Patterns d'API incohérents, consommateurs confus
Avec architecture :
- L'ADR[^1] spécifie : « Utiliser GraphQL pour toute communication client-serveur »
- Tous les agents suivent le même pattern
### Conflits de conception de base de données
Sans architecture :
- L'agent A utilise des noms de colonnes en snake_case
- L'agent B utilise des noms de colonnes en camelCase
- Résultat : Schéma incohérent, requêtes illisibles
Avec architecture :
- Un document de standards spécifie les conventions de nommage
- Tous les agents suivent les mêmes patterns
### Conflits de gestion d'état
Sans architecture :
- L'agent A utilise Redux pour l'état global
- L'agent B utilise React Context
- Résultat : Multiples approches de gestion d'état, complexité
Avec architecture :
- L'ADR spécifie l'approche de gestion d'état
- Tous les agents implémentent de manière cohérente
## Comment l'architecture prévient les conflits
### 1. Décisions explicites via les ADR[^1]
Chaque choix technologique significatif est documenté avec :
- Contexte (pourquoi cette décision est importante)
- Options considérées (quelles alternatives existent)
- Décision (ce qui a été choisi)
- Justification (pourquoi cela a-t-il été choisi)
- Conséquences (compromis acceptés)
### 2. Guidance spécifique aux FR/NFR[^2]
L'architecture associe chaque exigence fonctionnelle à une approche technique :
- FR-001 : Gestion des utilisateurs → Mutations GraphQL
- FR-002 : Application mobile → Requêtes optimisées
### 3. Standards et conventions
Documentation explicite de :
- La structure des répertoires
- Les conventions de nommage
- L'organisation du code
- Les patterns de test
## L'architecture comme contexte partagé
Considérez l'architecture comme le contexte partagé que tous les agents lisent avant d'implémenter :
```text
PRD : "Que construire"
Architecture : "Comment le construire"
L'agent A lit l'architecture → implémente l'Epic 1
L'agent B lit l'architecture → implémente l'Epic 2
L'agent C lit l'architecture → implémente l'Epic 3
Résultat : Implémentation cohérente
```
## Sujets clés des ADR
Décisions courantes qui préviennent les conflits :
| Sujet | Exemple de décision |
| ---------------- | -------------------------------------------- |
| Style d'API | GraphQL vs REST vs gRPC |
| Base de données | PostgreSQL vs MongoDB |
| Authentification | JWT vs Sessions |
| Gestion d'état | Redux vs Context vs Zustand |
| Styling | CSS Modules vs Tailwind vs Styled Components |
| Tests | Jest + Playwright vs Vitest + Cypress |
## Anti-patterns à éviter
:::caution[Erreurs courantes]
- **Décisions implicites** — « On décidera du style d'API au fur et à mesure » mène à l'incohérence
- **Sur-documentation** — Documenter chaque choix mineur cause une paralysie analytique
- **Architecture obsolète** — Les documents écrits une fois et jamais mis à jour poussent les agents à suivre des patterns dépassés
:::
:::tip[Approche correcte]
- Documenter les décisions qui traversent les frontières des epics
- Se concentrer sur les zones sujettes aux conflits
- Mettre à jour l'architecture au fur et à mesure des apprentissages
- Utiliser `bmad-correct-course` pour les changements significatifs
:::
## Glossaire
[^1]: ADR (Architecture Decision Record) : document qui consigne une décision darchitecture, son contexte, les options envisagées, le choix retenu et ses conséquences, afin dassurer la traçabilité et la compréhension des décisions techniques dans le temps.
[^2]: FR / NFR (Functional / Non-Functional Requirement) : exigences décrivant respectivement **ce que le système doit faire** (fonctionnalités, comportements attendus) et **comment il doit le faire** (contraintes de performance, sécurité, fiabilité, ergonomie, etc.).

View File

@ -0,0 +1,158 @@
---
title: "Contexte du Projet"
description: Comment project-context.md guide les agents IA avec les règles et préférences de votre projet
sidebar:
order: 7
---
Le fichier `project-context.md` est le guide d'implémentation de votre projet pour les agents IA. Similaire à une « constitution » dans d'autres systèmes de développement, il capture les règles, les patterns et les préférences qui garantissent une génération de code cohérente à travers tous les workflows.
## Ce Qu'il Fait
Les agents IA prennent constamment des décisions d'implémentation — quels patterns suivre, comment structurer le code, quelles conventions utiliser. Sans guidance claire, ils peuvent :
- Suivre des bonnes pratiques génériques qui ne correspondent pas à votre codebase
- Prendre des décisions incohérentes selon les différentes stories
- Passer à côté d'exigences ou de contraintes spécifiques au projet
Le fichier `project-context.md` résout ce problème en documentant ce que les agents doivent savoir dans un format concis et optimisé pour les LLM.
## Comment Ça Fonctionne
Chaque workflow d'implémentation charge automatiquement `project-context.md` s'il existe. Le workflow architecte le charge également pour respecter vos préférences techniques lors de la conception de l'architecture.
**Chargé par ces workflows :**
- `bmad-create-architecture` — respecte les préférences techniques pendant la phase de solutioning
- `bmad-create-story` — informe la création de stories avec les patterns du projet
- `bmad-dev-story` — guide les décisions d'implémentation
- `bmad-code-review` — valide par rapport aux standards du projet
- `bmad-quick-dev` — applique les patterns lors de l'implémentation des spécifications techniques
- `bmad-sprint-planning`, `bmad-retrospective`, `bmad-correct-course` — fournit le contexte global du projet
## Quand Le Créer
Le fichier `project-context.md` est utile à n'importe quel stade d'un projet :
| Scénario | Quand Créer | Objectif |
|------------------------------------------|-----------------------------------------------------|---------------------------------------------------------------------------------------|
| **Nouveau projet, avant l'architecture** | Manuellement, avant `bmad-create-architecture` | Documenter vos préférences techniques pour que l'architecte les respecte |
| **Nouveau projet, après l'architecture** | Via `bmad-generate-project-context` ou manuellement | Capturer les décisions d'architecture pour les agents d'implémentation |
| **Projet existant** | Via `bmad-generate-project-context` | Découvrir les patterns existants pour que les agents suivent les conventions établies |
| **Projet Quick Dev** | Avant ou pendant `bmad-quick-dev` | Garantir que l'implémentation rapide respecte vos patterns |
:::tip[Recommandé]
Pour les nouveaux projets, créez-le manuellement avant l'architecture si vous avez de fortes préférences techniques. Sinon, générez-le après l'architecture pour capturer ces décisions.
:::
## Ce Qu'il Contient
Le fichier a deux sections principales :
### Pile Technologique & Versions
Documente les frameworks, langages et outils utilisés par votre projet avec leurs versions spécifiques :
```markdown
## Pile Technologique & Versions
- Node.js 20.x, TypeScript 5.3, React 18.2
- State: Zustand (pas Redux)
- Testing: Vitest, Playwright, MSW
- Styling: Tailwind CSS avec design tokens personnalisés
```
### Règles Critiques dImplémentation
Documente les patterns et conventions que les agents pourraient autrement manquer :
```markdown
## Règles Critiques dImplémentation
**Configuration TypeScript :**
- Mode strict activé — pas de types `any` sans approbation explicite
- Utiliser `interface` pour les APIs publiques, `type` pour les unions/intersections
**Organisation du Code :**
- Composants dans `/src/components/` avec fichiers `.test.tsx` co-localisés
- Utilitaires dans `/src/lib/` pour les fonctions pures réutilisables
- Les appels API utilisent le singleton `apiClient` — jamais de fetch direct
**Patterns de Tests :**
- Les tests unitaires se concentrent sur la logique métier, pas sur les détails dimplémentation
- Les tests dintégration utilisent MSW pour simuler les réponses API
- Les tests E2E couvrent uniquement les parcours utilisateurs critiques
**Spécifique au Framework :**
- Toutes les opérations async utilisent le wrapper `handleError` pour une gestion cohérente des erreurs
- Les feature flags sont accessibles via `featureFlag()` de `@/lib/flags`
- Les nouvelles routes suivent le modèle de routage basé sur les fichiers dans `/src/app/`
```
Concentrez-vous sur ce qui est **non évident** — des choses que les agents pourraient ne pas déduire en lisant des extraits de code. Ne documentez pas les pratiques standard qui s'appliquent universellement.
## Création du Fichier
Vous avez trois options :
### Création Manuelle
Créez le fichier `_bmad-output/project-context.md` et ajoutez vos règles :
```bash
# Depuis la racine du projet
mkdir -p _bmad-output
touch _bmad-output/project-context.md
```
Éditez-le avec votre pile technologique et vos règles d'implémentation. Les workflows architecture et implémentation le trouveront et le chargeront automatiquement.
### Générer Après L'Architecture
Exécutez le workflow `bmad-generate-project-context` après avoir terminé votre architecture :
```bash
bmad-generate-project-context
```
Cela analyse votre document d'architecture et vos fichiers projet pour générer un fichier de contexte capturant les décisions prises.
### Générer Pour Les Projets Existants
Pour les projets existants, exécutez `bmad-generate-project-context` pour découvrir les patterns existants :
```bash
bmad-generate-project-context
```
Le workflow analyse votre codebase pour identifier les conventions, puis génère un fichier de contexte que vous pouvez examiner et affiner.
## Pourquoi C'est Important
Sans `project-context.md`, les agents font des suppositions qui peuvent ne pas correspondre à votre projet :
| Sans Contexte | Avec Contexte |
|----------------------------------------------------|-------------------------------------------------|
| Utilise des patterns génériques | Suit vos conventions établies |
| Style incohérent selon les stories | Implémentation cohérente |
| Peut manquer les contraintes spécifiques au projet | Respecte toutes les exigences techniques |
| Chaque agent décide indépendamment | Tous les agents s'alignent sur les mêmes règles |
C'est particulièrement important pour :
- **Quick Dev** — saute le PRD et l'architecture, le fichier de contexte comble le vide
- **Projets d'équipe** — garantit que tous les agents suivent les mêmes standards
- **Projets existants** — empêche de casser les patterns établis
## Édition et Mise à Jour
Le fichier `project-context.md` est un document vivant. Mettez-le à jour quand :
- Les décisions d'architecture changent
- De nouvelles conventions sont établies
- Les patterns évoluent pendant l'implémentation
- Vous identifiez des lacunes dans le comportement des agents
Vous pouvez l'éditer manuellement à tout moment, ou réexécuter `bmad-generate-project-context` pour le mettre à jour après des changements significatifs.
:::note[Emplacement du Fichier]
L'emplacement par défaut est `_bmad-output/project-context.md`. Les workflows le recherchent là, et vérifient également `**/project-context.md` n'importe où dans votre projet.
:::

View File

@ -0,0 +1,79 @@
---
title: "Quick Dev"
description: Réduire la friction de linteraction humaine sans renoncer aux points de contrôle qui protègent la qualité des résultats
sidebar:
order: 2
---
Intention en entrée, modifications de code en sortie, avec aussi peu d'interactions humaines dans la boucle que possible — sans sacrifier la qualité.
Il permet au modèle de s'exécuter plus longtemps entre les points de contrôle, puis ne vous fait intervenir que lorsque la tâche ne peut pas se poursuivre en toute sécurité sans jugement humain, ou lorsqu'il est temps de revoir le résultat final.
![Diagramme du workflow Quick Dev](/diagrams/quick-dev-diagram-fr.webp)
## Pourquoi cette fonctionnalité existe
Les interactions humaines dans la boucle sont nécessaires et coûteuses.
Les LLM actuels échouent encore de manière prévisible : ils interprètent mal l'intention, comblent les lacunes avec des suppositions assurées, dérivent vers du travail non lié, et génèrent des résultats à réviser bruyants. En même temps, l'intervention humaine constante limite la fluidité du développement. L'attention humaine est le goulot d'étranglement.
`bmad-quick-dev` rééquilibre ce compromis. Il fait confiance au modèle pour s'exécuter sans surveillance sur de plus longues périodes, mais seulement après que le workflow ait créé une frontière suffisamment solide pour rendre cela sûr.
## La conception fondamentale
### 1. Compresser l'intention d'abord
Le workflow commence par compresser linteraction de la personne et du modèle à partir de la requête en un objectif cohérent. L'entrée peut commencer sous forme d'une expression grossière de l'intention, mais avant que le workflow ne s'exécute de manière autonome, elle doit devenir suffisamment petite, claire et sans contradiction pour être exécutable.
L'intention peut prendre plusieurs formes : quelques phrases, un lien vers un outil de suivi de bugs, une sortie du mode planification, du texte copié depuis une session de chat, ou même un numéro de story depuis un fichier `epics.md` de BMAD. Dans ce dernier cas, le workflow ne comprendra pas la sémantique de suivi des stories de BMAD, mais il peut quand même prendre la story elle-même et l'exécuter.
Ce workflow n'élimine pas le contrôle humain. Il le déplace vers un nombre réduit détapes à forte valeur :
- **Clarification de l'intention** - transformer une demande confuse en un objectif cohérent sans contradictions cachées
- **Approbation de la spécification** - confirmer que la compréhension figée correspond bien à ce qu'il faut construire
- **Revue du produit final** - le point de contrôle principal, où la personne décide si le résultat est acceptable à la fin
### 2. Router vers le chemin le plus court et sûr
Une fois l'objectif clair, le workflow décide s'il s'agit d'un véritable changement en une seule étape ou s'il nécessite le chemin complet. Les petits changements à zéro impact peuvent aller directement à l'implémentation. Tout le reste passe par la planification pour que le modèle dispose d'un cadre plus solide avant de s'exécuter plus longtemps de manière autonome.
### 3. S'exécuter plus longtemps avec moins de supervision
Après cette décision de routage, le modèle peut prendre en charge une plus grande partie du travail par lui-même. Sur le chemin complet, la spécification approuvée devient le cadre dans lequel le modèle s'exécute avec moins de supervision, ce qui est tout l'intérêt de la conception.
### 4. Diagnostiquer les échecs au bon niveau
Si l'implémentation est incorrecte parce que l'intention était mauvaise, corriger le code n'est pas la bonne solution. Si le code est incorrect parce que la spécification était faible, corriger le diff n'est pas non plus la bonne solution. Le workflow est conçu pour diagnostiquer où l'échec est entré dans le système, revenir à ce niveau, et régénérer à partir de ce point.
Les résultats de la revue sont utilisés pour décider si le problème provenait de l'intention, de la génération de la spécification, ou de l'implémentation locale. Seuls les véritables problèmes locaux sont corrigés localement.
### 5. Ne faire intervenir lhumain que si nécessaire
L'entretien sur l'intention implique la personne dans la boucle, mais ce n'est pas le même type d'interruption qu'un point de contrôle récurrent. Le workflow essaie de garder ces points de contrôle récurrents au minimum. Après la mise en forme initiale de l'intention, la personne revient principalement lorsque le workflow ne peut pas continuer en toute sécurité sans jugement, et à la fin, lorsqu'il est temps de revoir le résultat.
- **Résolution des lacunes d'intention** - intervenir à nouveau lors de la revue prouve que le workflow n'a pas pu déduire correctement ce qui était voulu
Tout le reste est candidat à une exécution autonome plus longue. Ce compromis est délibéré. Les anciens patterns dépensent plus d'attention humaine en supervision continue. Quick Dev fait davantage confiance au modèle, mais préserve l'attention humaine pour les moments où le raisonnement humain a le plus d'impact.
## Pourquoi le système de revue est important
La phase de revue n'est pas seulement là pour trouver des bugs. Elle est là pour router la correction sans détruire l'élan.
Ce workflow fonctionne mieux sur une plateforme capable de générer des sous-agents[^1], ou au moins d'invoquer un autre LLM via la ligne de commande et d'attendre un résultat. Si votre plateforme ne supporte pas cela nativement, vous pouvez ajouter un skill pour le faire. Les sous-agents sans contexte sont une pierre angulaire de la conception de la revue.
Les revues agentiques[^2] échouent souvent de deux manières :
- Elles génèrent trop dobservations, forçant la personne à trier le bruit.
- Elles déraillent des modifications actuelles en remontant des problèmes non liés et en transformant chaque exécution en un projet de nettoyage improvisé.
Quick Dev aborde ces deux problèmes en traitant la revue comme un triage[^3].
Lorsquune observation est fortuite plutôt que directement liée au travail en cours, le processus peut la mettre de côté au lieu dobliger la personne à sen occuper immédiatement. Cela permet de rester concentré sur lexécution et déviter que des digressions aléatoires ne viennent épuiser le capital dattention.
Ce triage sera parfois imparfait. Cest acceptable. Il est généralement préférable de mal juger certaines observations plutôt que dinonder la personne de milliers de commentaires de revue à faible valeur. Le système optimise la qualité du rapport, pas dêtre exhaustif.
## Glossaire
[^1]: Sous-agent : agent IA secondaire créé temporairement pour effectuer une tâche spécifique (comme une revue de code) de manière isolée, sans hériter du contexte complet de lagent principal, ce qui permet une analyse plus objective et impartiale.
[^2]: Revues agentiques (agentic review) : revue de code effectuée par un agent IA de manière autonome, capable danalyser, didentifier des problèmes et de formuler des recommandations sans intervention humaine directe.
[^3]: Triage : processus de filtrage et de priorisation des observations issues dune revue, afin de distinguer les problèmes pertinents à traiter immédiatement de ceux qui peuvent être mis de côté pour plus tard.

View File

@ -0,0 +1,85 @@
---
title: "Pourquoi le Solutioning est Important"
description: Comprendre pourquoi la phase de solutioning est critique pour les projets multi-epics
sidebar:
order: 3
---
La Phase 3 (Solutioning) traduit le **quoi** construire (issu de la Planification) en **comment** le construire (conception technique). Cette phase évite les conflits entre agents dans les projets multi-epics en documentant les décisions architecturales avant le début de l'implémentation.
## Le Problème Sans Solutioning
```text
Agent 1 implémente l'Epic 1 avec une API REST
Agent 2 implémente l'Epic 2 avec GraphQL
Résultat : Conception d'API incohérente, cauchemar d'intégration
```
Lorsque plusieurs agents implémentent différentes parties d'un système sans orientation architecturale partagée, ils prennent des décisions techniques indépendantes qui peuvent entrer en conflit.
## La Solution Avec le Solutioning
```text
le workflow architecture décide : "Utiliser GraphQL pour toutes les API"
Tous les agents suivent les décisions d'architecture
Résultat : Implémentation cohérente, pas de conflits
```
En documentant les décisions techniques de manière explicite, tous les agents implémentent de façon cohérente et l'intégration devient simple.
## Solutioning vs Planification
| Aspect | Planification (Phase 2) | Solutioning (Phase 3) |
|----------|--------------------------|-------------------------------------------------|
| Question | Quoi et Pourquoi ? | Comment ? Puis Quelles unités de travail ? |
| Sortie | FRs/NFRs (Exigences)[^1] | Architecture + Epics[^2]/Stories[^3] |
| Agent | PM | Architect → PM |
| Audience | Parties prenantes | Développeurs |
| Document | PRD[^4] (FRs/NFRs) | Architecture + Fichiers Epics |
| Niveau | Logique métier | Conception technique + Décomposition du travail |
## Principe Clé
**Rendre les décisions techniques explicites et documentées** pour que tous les agents implémentent de manière cohérente.
Cela évite :
- Les conflits de style d'API (REST vs GraphQL)
- Les incohérences de conception de base de données
- Les désaccords sur la gestion du state
- Les inadéquations de conventions de nommage
- Les variations d'approche de sécurité
## Quand le Solutioning est Requis
| Parcours | Solutioning Requis ? |
|-----------------------|-----------------------------|
| Quick Dev | Non - lignore complètement |
| Méthode BMad Simple | Optionnel |
| Méthode BMad Complexe | Oui |
| Enterprise | Oui |
:::tip[Règle Générale]
Si vous avez plusieurs epics qui pourraient être implémentés par différents agents, vous avez besoin de solutioning.
:::
## Conséquences de sauter la phase de Solutioning
Sauter le solutioning sur des projets complexes entraîne :
- **Des problèmes d'intégration** découverts en milieu de sprint[^5]
- **Du travail répété** dû à des implémentations conflictuelles
- **Un temps de développement plus long** globalement
- **De la dette technique**[^6] due à des patterns incohérents
:::caution[Coût Multiplié]
Détecter les problèmes d'alignement lors du solutioning est 10× plus rapide que de les découvrir pendant l'implémentation.
:::
## Glossaire
[^1]: FR / NFR (Functional / Non-Functional Requirement) : exigences décrivant respectivement **ce que le système doit faire** (fonctionnalités, comportements attendus) et **comment il doit le faire** (contraintes de performance, sécurité, fiabilité, ergonomie, etc.).
[^2]: Epic : dans les méthodologies agiles, une unité de travail importante qui peut être décomposée en plusieurs stories plus petites. Un epic représente généralement une fonctionnalité majeure ou un objectif métier.
[^3]: Story (User Story) : description courte et simple d'une fonctionnalité du point de vue de l'utilisateur, utilisée dans les méthodologies agiles pour planifier et prioriser le travail.
[^4]: PRD (Product Requirements Document) : document de référence qui décrit les objectifs du produit, les besoins utilisateurs, les fonctionnalités attendues, les contraintes et les critères de succès, afin d'aligner les équipes sur ce qui doit être construit et pourquoi.
[^5]: Sprint : période de temps fixe (généralement 1 à 4 semaines) dans les méthodologies agiles durant laquelle l'équipe complète un ensemble prédéfini de tâches.
[^6]: Dette technique : coût futur supplémentaire de travail résultant de choix de facilité ou de raccourcis pris lors du développement initial, nécessitant souvent une refonte ultérieure.

View File

@ -0,0 +1,174 @@
---
title: "Comment personnaliser BMad"
description: Personnalisez les agents, les workflows et les modules tout en préservant la compatibilité avec les mises à jour
sidebar:
order: 7
---
Utilisez les fichiers `.customize.yaml` pour adapter le comportement, les personas[^1] et les menus des agents tout en préservant vos modifications lors des mises à jour.
## Quand utiliser cette fonctionnalité
- Vous souhaitez modifier le nom, la personnalité ou le style de communication d'un agent
- Vous avez besoin que les agents se souviennent du contexte spécifique au projet
- Vous souhaitez ajouter des éléments de menu personnalisés qui déclenchent vos propres workflows ou prompts
- Vous voulez que les agents effectuent des actions spécifiques à chaque démarrage
:::note[Prérequis]
- BMad installé dans votre projet (voir [Comment installer BMad](./install-bmad.md))
- Un éditeur de texte pour les fichiers YAML
:::
:::caution[Protégez vos personnalisations]
Utilisez toujours les fichiers `.customize.yaml` décrits ici plutôt que de modifier directement les fichiers d'agents. L'installateur écrase les fichiers d'agents lors des mises à jour, mais préserve vos modifications dans les fichiers `.customize.yaml`.
:::
## Étapes
### 1. Localiser les fichiers de personnalisation
Après l'installation, vous trouverez un fichier `.customize.yaml` par agent dans :
```text
_bmad/_config/agents/
├── bmm-analyst.customize.yaml
├── bmm-architect.customize.yaml
└── ... (un fichier par agent installé)
```
### 2. Modifier le fichier de personnalisation
Ouvrez le fichier `.customize.yaml` de l'agent que vous souhaitez modifier. Chaque section est facultative — personnalisez uniquement ce dont vous avez besoin.
| Section | Comportement | Objectif |
| ------------------ | ------------ | ------------------------------------------------ |
| `agent.metadata` | Remplace | Remplacer le nom d'affichage de l'agent |
| `persona` | Remplace | Définir le rôle, l'identité, le style et les principes |
| `memories` | Ajoute | Ajouter un contexte persistant que l'agent se rappelle toujours |
| `menu` | Ajoute | Ajouter des éléments de menu personnalisés pour les workflows ou prompts |
| `critical_actions` | Ajoute | Définir les instructions de démarrage de l'agent |
| `prompts` | Ajoute | Créer des prompts réutilisables pour les actions du menu |
Les sections marquées **Remplace** écrasent entièrement les valeurs par défaut de l'agent. Les sections marquées **Ajoute** s'ajoutent à la configuration existante.
**Nom de l'agent**
Modifier la façon dont l'agent se présente :
```yaml
agent:
metadata:
name: 'Bob léponge' # Par défaut : "Mary"
```
**Persona**
Remplacer la personnalité, le rôle et le style de communication de l'agent :
```yaml
persona:
role: 'Ingénieur Full-Stack Senior'
identity: 'Habite dans un ananas (au fond de la mer)'
communication_style: 'Style agaçant de Bob lÉponge'
principles:
- 'Jamais de nidification, les devs Bob lÉponge détestent plus de 2 niveaux dimbrication'
- 'Privilégier la composition à lhéritage'
```
La section `persona`[^1] remplace entièrement le persona par défaut, donc incluez les quatre champs si vous la définissez.
**Souvenirs**
Ajouter un contexte persistant que l'agent gardera toujours en mémoire :
```yaml
memories:
- 'Travaille au Krusty Krab'
- 'Célébrité préférée : David Hasslehoff'
- 'Appris dans lEpic 1 que ce nest pas cool de faire semblant que les tests ont passé'
```
**Éléments de menu**
Ajouter des entrées personnalisées au menu d'affichage de l'agent. Chaque élément nécessite un `trigger`, une cible (chemin `workflow` ou référence `action`), et une `description` :
```yaml
menu:
- trigger: my-workflow
workflow: 'my-custom/workflows/my-workflow.yaml'
description: Mon workflow personnalisé
- trigger: deploy
action: '#deploy-prompt'
description: Déployer en production
```
**Actions critiques**
Définir des instructions qui s'exécutent au démarrage de l'agent :
```yaml
critical_actions:
- 'Vérifier les pipelines CI avec le Skill XYZ et alerter lutilisateur au réveil si quelque chose nécessite une attention urgente'
```
**Prompts personnalisés**
Créer des prompts réutilisables que les éléments de menu peuvent référencer avec `action="#id"` :
```yaml
prompts:
- id: deploy-prompt
content: |
Déployer la branche actuelle en production :
1. Exécuter tous les tests
2. Build le projet
3. Exécuter le script de déploiement
```
### 3. Appliquer vos modifications
Après modification, réinstallez pour appliquer les changements :
```bash
npx bmad-method install
```
L'installateur détecte l'installation existante et propose ces options :
| Option | Ce qu'elle fait |
| ----------------------------------- | ---------------------------------------------------------------------- |
| **Quick Update** | Met à jour tous les modules vers la dernière version et applique les personnalisations |
| **Modify BMad Installation** | Flux d'installation complet pour ajouter ou supprimer des modules |
Pour des modifications de personnalisation uniquement, **Quick Update** est l'option la plus rapide.
## Résolution des problèmes
**Les modifications n'apparaissent pas ?**
- Exécutez `npx bmad-method install` et sélectionnez **Quick Update** pour appliquer les modifications
- Vérifiez que votre syntaxe YAML est valide (l'indentation compte)
- Assurez-vous d'avoir modifié le bon fichier `.customize.yaml` pour l'agent
**L'agent ne se charge pas ?**
- Vérifiez les erreurs de syntaxe YAML à l'aide d'un validateur YAML en ligne
- Assurez-vous de ne pas avoir laissé de champs vides après les avoir décommentés
- Essayez de revenir au modèle d'origine et de reconstruire
**Besoin de réinitialiser un agent ?**
- Effacez ou supprimez le fichier `.customize.yaml` de l'agent
- Exécutez `npx bmad-method install` et sélectionnez **Quick Update** pour restaurer les valeurs par défaut
## Personnalisation des workflows
La personnalisation des workflows et skills existants de la méthode BMad arrive bientôt.
## Personnalisation des modules
Les conseils sur la création de modules d'extension et la personnalisation des modules existants arrivent bientôt.
## Glossaire
[^1]: Persona : définition de la personnalité, du rôle et du style de communication d'un agent IA. Permet d'adapter le comportement et les réponses de l'agent selon les besoins du projet.

View File

@ -0,0 +1,122 @@
---
title: "Projets existants"
description: Comment utiliser la méthode BMad sur des bases de code existantes
sidebar:
order: 6
---
Utilisez la méthode BMad efficacement lorsque vous travaillez sur des projets existants et des bases de code legacy.
Ce guide couvre le flux de travail essentiel pour l'intégration à des projets existants avec la méthode BMad.
:::note[Prérequis]
- méthode BMad installée (`npx bmad-method install`)
- Une base de code existante sur laquelle vous souhaitez travailler
- Accès à un IDE IA (Claude Code ou Cursor)
:::
## Étape 1 : Nettoyer les artefacts de planification terminés
Si vous avez terminé tous les epics et stories du PRD[^1] via le processus BMad, nettoyez ces fichiers. Archivez-les, supprimez-les, ou appuyez-vous sur l'historique des versions si nécessaire. Ne conservez pas ces fichiers dans :
- `docs/`
- `_bmad-output/planning-artifacts/`
- `_bmad-output/implementation-artifacts/`
## Étape 2 : Créer le contexte du projet
:::tip[Recommandé pour les projets existants]
Générez `project-context.md` pour capturer les patterns et conventions de votre base de code existante. Cela garantit que les agents IA suivent vos pratiques établies lors de l'implémentation des modifications.
:::
Exécutez le workflow de génération de contexte du projet :
```bash
bmad-generate-project-context
```
Cela analyse votre base de code pour identifier :
- La pile technologique et les versions
- Les patterns d'organisation du code
- Les conventions de nommage
- Les approches de test
- Les patterns spécifiques aux frameworks
Vous pouvez examiner et affiner le fichier généré, ou le créer manuellement à `_bmad-output/project-context.md` si vous préférez.
[En savoir plus sur le contexte du projet](../explanation/project-context.md)
## Étape 3 : Maintenir une documentation de projet de qualité
Votre dossier `docs/` doit contenir une documentation succincte et bien organisée qui représente fidèlement votre projet :
- L'intention et la justification métier
- Les règles métier
- L'architecture
- Toute autre information pertinente sur le projet
Pour les projets complexes, envisagez d'utiliser le workflow `bmad-document-project`. Il offre des variantes d'exécution qui analyseront l'ensemble de votre projet et documenteront son état actuel réel.
## Étape 4 : Obtenir de l'aide
### BMad-Help : Votre point de départ
**Exécutez `bmad-help` chaque fois que vous n'êtes pas sûr de la prochaine étape.** Ce guide intelligent :
- Inspecte votre projet pour voir ce qui a déjà été fait
- Affiche les options basées sur vos modules installés
- Comprend les requêtes en langage naturel
```
bmad-help J'ai une app Rails existante, par où dois-je commencer ?
bmad-help Quelle est la différence entre quick-dev et la méthode complète ?
bmad-help Montre-moi quels workflows sont disponibles
```
BMad-Help s'exécute également **automatiquement à la fin de chaque workflow**, fournissant des conseils clairs sur exactement quoi faire ensuite.
### Choisir votre approche
Vous avez deux options principales selon l'ampleur des modifications :
| Portée | Approche recommandée |
| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **Petites mises à jour ou ajouts** | Exécutez `bmad-quick-dev` pour clarifier l'intention, planifier, implémenter et réviser dans un seul workflow. La méthode BMad complète en quatre phases est probablement excessive. |
| **Modifications ou ajouts majeurs** | Commencez avec la méthode BMad, en appliquant autant ou aussi peu de rigueur que nécessaire. |
### Pendant la création du PRD
Lors de la création d'un brief ou en passant directement au PRD[^1], assurez-vous que l'agent :
- Trouve et analyse votre documentation de projet existante
- Lit le contexte approprié sur votre système actuel
Vous pouvez guider l'agent explicitement, mais l'objectif est de garantir que la nouvelle fonctionnalité s'intègre bien à votre système existant.
### Considérations UX
Le travail UX[^2] est optionnel. La décision dépend non pas de savoir si votre projet a une UX, mais de :
- Si vous allez travailler sur des modifications UX
- Si des conceptions ou patterns UX significatifs sont nécessaires
Si vos modifications se résument à de simples mises à jour d'écrans existants qui vous satisfont, un processus UX complet n'est pas nécessaire.
### Considérations d'architecture
Lors de la création de l'architecture, assurez-vous que l'architecte :
- Utilise les fichiers documentés appropriés
- Analyse la base de code existante
Soyez particulièrement attentif ici pour éviter de réinventer la roue ou de prendre des décisions qui ne s'alignent pas avec votre architecture existante.
## Plus d'informations
- **[Corrections rapides](./quick-fixes.md)** - Corrections de bugs et modifications ad-hoc
- **[FAQ Projets existants](../explanation/established-projects-faq.md)** - Questions courantes sur le travail sur des projets établis
## Glossaire
[^1]: PRD (Product Requirements Document) : document de référence qui décrit les objectifs du produit, les besoins utilisateurs, les fonctionnalités attendues, les contraintes et les critères de succès, afin d'aligner les équipes sur ce qui doit être construit et pourquoi.
[^2]: UX (User Experience) : expérience utilisateur, englobant l'ensemble des interactions et perceptions d'un utilisateur face à un produit. Le design UX vise à créer des interfaces intuitives, efficaces et agréables en tenant compte des besoins, comportements et contexte d'utilisation.

View File

@ -0,0 +1,136 @@
---
title: "Comment obtenir des réponses à propos de BMad"
description: Utiliser un LLM pour répondre rapidement à vos questions sur BMad
sidebar:
order: 4
---
## Commencez ici : BMad-Help
**Le moyen le plus rapide d'obtenir des réponses sur BMad est le skill `bmad-help`.** Ce guide intelligent répondra à plus de 80 % de toutes les questions et est disponible directement dans votre IDE pendant que vous travaillez.
BMad-Help est bien plus qu'un outil de recherche — il :
- **Inspecte votre projet** pour voir ce qui a déjà été réalisé
- **Comprend le langage naturel** — posez vos questions en français courant
- **S'adapte à vos modules installés** — affiche les options pertinentes
- **Se lance automatiquement après les workflows** — vous indique exactement quoi faire ensuite
- **Recommande la première tâche requise** — plus besoin de deviner par où commencer
### Comment utiliser BMad-Help
Appelez-le par son nom dans votre session IA :
```
bmad-help
```
:::tip
Vous pouvez également utiliser `/bmad-help` ou `$bmad-help` selon votre plateforme, mais `bmad-help` tout seul devrait fonctionner partout.
:::
Combinez-le avec une requête en langage naturel :
```
bmad-help J'ai une idée de SaaS et je connais toutes les fonctionnalités. Par où commencer ?
bmad-help Quelles sont mes options pour le design UX ?
bmad-help Je suis bloqué sur le workflow PRD
bmad-help Montre-moi ce qui a été fait jusqu'à maintenant
```
BMad-Help répond avec :
- Ce qui est recommandé pour votre situation
- Quelle est la première tâche requise
- À quoi ressemble le reste du processus
## Quand utiliser ce guide
Utilisez cette section lorsque :
- Vous souhaitez comprendre l'architecture ou les éléments internes de BMad
- Vous avez besoin de réponses au-delà de ce que BMad-Help fournit
- Vous faites des recherches sur BMad avant l'installation
- Vous souhaitez explorer le code source directement
## Étapes
### 1. Choisissez votre source
| Source | Idéal pour | Exemples |
|-------------------------|------------------------------------------------------|---------------------------------------|
| **Dossier `_bmad`** | Comment fonctionne BMad — agents, workflows, prompts | "Que fait l'agent Analyste ?" |
| **Repo GitHub complet** | Historique, installateur, architecture | "Qu'est-ce qui a changé dans la v6 ?" |
| **`llms-full.txt`** | Aperçu rapide depuis la documentation | "Expliquez les quatre phases de BMad" |
Le dossier `_bmad` est créé lorsque vous installez BMad. Si vous ne l'avez pas encore, clonez le repo à la place.
### 2. Pointez votre IA vers la source
**Si votre IA peut lire des fichiers (Claude Code, Cursor, etc.) :**
- **BMad installé :** Pointez vers le dossier `_bmad` et posez vos questions directement
- **Vous voulez plus de contexte :** Clonez le [repo complet](https://github.com/bmad-code-org/BMAD-METHOD)
**Si vous utilisez ChatGPT ou Claude.ai (LLM en ligne) :**
Importez `llms-full.txt` dans votre session :
```text
https://bmad-code-org.github.io/BMAD-METHOD/llms-full.txt
```
### 3. Posez votre question
:::note[Exemple]
**Q :** "Quel est le moyen le plus rapide de construire quelque chose avec BMad ?"
**R :** Utilisez le workflow Quick Dev : Lancez `bmad-quick-dev` — il clarifie votre intention, planifie, implémente, révise et présente les résultats dans un seul workflow, en sautant les phases de planification complètes.
:::
## Ce que vous obtenez
Des réponses directes sur BMad — comment fonctionnent les agents, ce que font les workflows, pourquoi les choses sont structurées ainsi — sans attendre la réponse de quelqu'un.
## Conseils
- **Vérifiez les réponses surprenantes** — Les LLM font parfois des erreurs. Consultez le fichier source ou posez la question sur Discord.
- **Soyez précis** — "Que fait l'étape 3 du workflow PRD ?" est mieux que "Comment fonctionne le PRD ?"
## Toujours bloqué ?
Avez-vous essayé l'approche LLM et avez encore besoin d'aide ? Vous avez maintenant une bien meilleure question à poser.
| Canal | Utilisé pour |
| ------------------------- | ------------------------------------------- |
| `#bmad-method-help` | Questions rapides (chat en temps réel) |
| Forum `help-requests` | Questions détaillées (recherchables, persistants) |
| `#suggestions-feedback` | Idées et demandes de fonctionnalités |
| `#report-bugs-and-issues` | Rapports de bugs |
**Discord :** [discord.gg/gk8jAdXWmj](https://discord.gg/gk8jAdXWmj)
**GitHub Issues :** [github.com/bmad-code-org/BMAD-METHOD/issues](https://github.com/bmad-code-org/BMAD-METHOD/issues) (pour les bugs clairs)
*Toi !*
*Bloqué*
*dans la file d'attente—*
*qui*
*attends-tu ?*
*La source*
*est là,*
*facile à voir !*
*Pointez*
*votre machine.*
*Libérez-la.*
*Elle lit.*
*Elle parle.*
*Demandez—*
*Pourquoi attendre*
*demain*
*quand tu as déjà*
*cette journée ?*
*—Claude*

View File

@ -0,0 +1,116 @@
---
title: "Comment installer BMad"
description: Guide étape par étape pour installer BMad dans votre projet
sidebar:
order: 1
---
Utilisez la commande `npx bmad-method install` pour configurer BMad dans votre projet avec votre choix de modules et d'outils d'IA.
Si vous souhaitez utiliser un installateur non interactif et fournir toutes les options d'installation en ligne de commande, consultez [ce guide](./non-interactive-installation.md).
## Quand l'utiliser
- Démarrer un nouveau projet avec BMad
- Ajouter BMad à une base de code existante
- Mettre à jour une installation BMad existante
:::note[Prérequis]
- **Node.js** 20+ (requis pour l'installateur)
- **Git** (recommandé)
- **Outil d'IA** (Claude Code, Cursor, ou similaire)
:::
## Étapes
### 1. Lancer l'installateur
```bash
npx bmad-method install
```
:::tip[Vous voulez la dernière version préliminaire ?]
Utilisez le dist-tag `next` :
```bash
npx bmad-method@next install
```
Cela vous permet d'obtenir les nouvelles modifications plus tôt, avec un risque plus élevé de changements que l'installation par défaut.
:::
:::tip[Version de développement]
Pour installer la dernière version depuis la branche main (peut être instable) :
```bash
npx github:bmad-code-org/BMAD-METHOD install
```
:::
### 2. Choisir l'emplacement d'installation
L'installateur vous demandera où installer les fichiers BMad :
- Répertoire courant (recommandé pour les nouveaux projets si vous avez créé le répertoire vous-même et l'exécutez depuis ce répertoire)
- Chemin personnalisé
### 3. Sélectionner vos outils d'IA
Choisissez les outils d'IA que vous utilisez :
- Claude Code
- Cursor
- Autres
Chaque outil a sa propre façon d'intégrer les skills. L'installateur crée de petits fichiers de prompt pour activer les workflows et les agents — il les place simplement là où votre outil s'attend à les trouver.
:::note[Activer les skills]
Certaines plateformes nécessitent que les skills soient explicitement activés dans les paramètres avant d'apparaître. Si vous installez BMad et ne voyez pas les skills, vérifiez les paramètres de votre plateforme ou demandez à votre assistant IA comment activer les skills.
:::
### 4. Choisir les modules
L'installateur affiche les modules disponibles. Sélectionnez ceux dont vous avez besoin — la plupart des utilisateurs veulent simplement **méthode BMad** (le module de développement logiciel).
### 5. Suivre les instructions
L'installateur vous guide pour le reste — contenu personnalisé, paramètres, etc.
## Ce que vous obtenez
```text
votre-projet/
├── _bmad/
│ ├── bmm/ # Vos modules sélectionnés
│ │ └── config.yaml # Paramètres du module (si vous devez les modifier)
│ ├── core/ # Module core requis
│ └── ...
├── _bmad-output/ # Artefacts générés
├── .claude/ # Skills Claude Code (si vous utilisez Claude Code)
│ └── skills/
│ ├── bmad-help/
│ ├── bmad-persona/
│ └── ...
└── .cursor/ # Skills Cursor (si vous utilisez Cursor)
└── skills/
└── ...
```
## Vérifier l'installation
Exécutez `bmad-help` pour vérifier que tout fonctionne et voir quoi faire ensuite.
**BMad-Help est votre guide intelligent** qui va :
- Confirmer que votre installation fonctionne
- Afficher ce qui est disponible en fonction de vos modules installés
- Recommander votre première étape
Vous pouvez aussi lui poser des questions :
```
bmad-help Je viens d'installer, que dois-je faire en premier ?
bmad-help Quelles sont mes options pour un projet SaaS ?
```
## Résolution de problèmes
**L'installateur affiche une erreur** — Copiez-collez la sortie dans votre assistant IA et laissez-le résoudre le problème.
**L'installateur a fonctionné mais quelque chose ne fonctionne pas plus tard** — Votre IA a besoin du contexte BMad pour vous aider. Consultez [Comment obtenir des réponses à propos de BMad](./get-answers-about-bmad.md) pour savoir comment diriger votre IA vers les bonnes sources.

View File

@ -0,0 +1,171 @@
---
title: Installation non-interactive
description: Installer BMad en utilisant des options de ligne de commande pour les pipelines CI/CD et les déploiements automatisés
sidebar:
order: 2
---
Utilisez les options de ligne de commande pour installer BMad de manière non-interactive. Cela est utile pour :
## Quand utiliser cette méthode
- Déploiements automatisés et pipelines CI/CD
- Installations scriptées
- Installations par lots sur plusieurs projets
- Installations rapides avec des configurations connues
:::note[Prérequis]
Nécessite [Node.js](https://nodejs.org) v20+ et `npx` (inclus avec npm).
:::
## Options disponibles
### Options d'installation
| Option | Description | Exemple |
|------|-------------|---------|
| `--directory <chemin>` | Répertoire d'installation | `--directory ~/projects/myapp` |
| `--modules <modules>` | IDs de modules séparés par des virgules | `--modules bmm,bmb` |
| `--tools <outils>` | IDs d'outils/IDE séparés par des virgules (utilisez `none` pour ignorer) | `--tools claude-code,cursor` ou `--tools none` |
| `--custom-content <chemins>` | Chemins vers des modules personnalisés séparés par des virgules | `--custom-content ~/my-module,~/another-module` |
| `--action <type>` | Action pour les installations existantes : `install` (par défaut), `update`, ou `quick-update` | `--action quick-update` |
### Configuration principale
| Option | Description | Par défaut |
|------|-------------|---------|
| `--user-name <nom>` | Nom à utiliser par les agents | Nom d'utilisateur système |
| `--communication-language <langue>` | Langue de communication des agents | Anglais |
| `--document-output-language <langue>` | Langue de sortie des documents | Anglais |
| `--output-folder <chemin>` | Chemin du dossier de sortie | _bmad-output |
### Autres options
| Option | Description |
|------|-------------|
| `-y, --yes` | Accepter tous les paramètres par défaut et ignorer les invites |
| `-d, --debug` | Activer la sortie de débogage pour la génération du manifeste |
## IDs de modules
IDs de modules disponibles pour loption `--modules` :
- `bmm` — méthode BMad Master
- `bmb` — BMad Builder
Consultez le [registre BMad](https://github.com/bmad-code-org) pour les modules externes disponibles.
## IDs d'outils/IDE
IDs d'outils disponibles pour loption `--tools` :
**Recommandés :** `claude-code`, `cursor`
Exécutez `npx bmad-method install` de manière interactive une fois pour voir la liste complète actuelle des outils pris en charge, ou consultez la [configuration des codes de la plateforme](https://github.com/bmad-code-org/BMAD-METHOD/blob/main/tools/cli/installers/lib/ide/platform-codes.yaml).
## Modes d'installation
| Mode | Description | Exemple |
|------|-------------|---------|
| Entièrement non-interactif | Fournir toutes les options pour ignorer toutes les invites | `npx bmad-method install --directory . --modules bmm --tools claude-code --yes` |
| Semi-interactif | Fournir certains options ; BMad demande les autres | `npx bmad-method install --directory . --modules bmm` |
| Paramètres par défaut uniquement | Accepter tous les paramètres par défaut avec `-y` | `npx bmad-method install --yes` |
| Sans outils | Ignorer la configuration des outils/IDE | `npx bmad-method install --modules bmm --tools none` |
## Exemples
### Installation dans un pipeline CI/CD
```bash
#!/bin/bash
# install-bmad.sh
npx bmad-method install \
--directory "${GITHUB_WORKSPACE}" \
--modules bmm \
--tools claude-code \
--user-name "CI Bot" \
--communication-language Français \
--document-output-language Français \
--output-folder _bmad-output \
--yes
```
### Mettre à jour une installation existante
```bash
npx bmad-method install \
--directory ~/projects/myapp \
--action update \
--modules bmm,bmb,custom-module
```
### Mise à jour rapide (conserver les paramètres)
```bash
npx bmad-method install \
--directory ~/projects/myapp \
--action quick-update
```
### Installation avec du contenu personnalisé
```bash
npx bmad-method install \
--directory ~/projects/myapp \
--modules bmm \
--custom-content ~/my-custom-module,~/another-module \
--tools claude-code
```
## Ce que vous obtenez
- Un répertoire `_bmad/` entièrement configuré dans votre projet
- Des agents et des flux de travail configurés pour vos modules et outils sélectionnés
- Un dossier `_bmad-output/` pour les artefacts générés
## Validation et gestion des erreurs
BMad valide toutes les options fournis :
- **Directory** — Doit être un chemin valide avec des permissions d'écriture
- **Modules** — Avertit des IDs de modules 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
- **Action** — Doit être l'une des suivantes : `install`, `update`, `quick-update`
Les valeurs invalides entraîneront soit :
1. Laffichage dun message d'erreur suivi dun exit (pour les options critiques comme le répertoire)
2. Un avertissement puis la continuation de linstallation (pour les éléments optionnels comme le contenu personnalisé)
3. Un retour aux invites interactives (pour les valeurs requises manquantes)
:::tip[Bonnes pratiques]
- Utilisez des chemins absolus pour `--directory` pour éviter toute ambiguïté
- Testez les options localement avant de les utiliser dans des pipelines CI/CD
- Combinez avec `-y` pour des installations vraiment sans surveillance
- Utilisez `--debug` si vous rencontrez des problèmes lors de l'installation
:::
## Résolution des problèmes
### L'installation échoue avec "Invalid directory"
- Le chemin du répertoire doit exister (ou son parent doit exister)
- Vous avez besoin des permissions d'écriture
- Le chemin doit être absolu ou correctement relatif au répertoire actuel
### Module non trouvé
- Vérifiez que l'ID du module est correct
- Les modules externes doivent être disponibles dans le registre
### Chemin de contenu personnalisé invalide
Assurez-vous que chaque chemin de contenu personnalisé :
- Pointe vers un répertoire
- Contient un fichier `module.yaml` à la racine
- Possède un champ `code` dans `module.yaml`
:::note[Toujours bloqué ?]
Exécutez avec `--debug` pour une sortie détaillée, essayez le mode interactif pour isoler le problème, ou signalez-le à <https://github.com/bmad-code-org/BMAD-METHOD/issues>.
:::

View File

@ -0,0 +1,127 @@
---
title: "Gérer le contexte du projet"
description: Créer et maintenir project-context.md pour guider les agents IA
sidebar:
order: 8
---
Utilisez le fichier `project-context.md` pour garantir que les agents IA respectent les préférences techniques et les règles d'implémentation de votre projet tout au long des workflows. Pour vous assurer qu'il est toujours disponible, vous pouvez également ajouter la ligne `Le contexte et les conventions importantes du projet se trouvent dans [chemin vers le contexte du projet]/project-context.md` à votre fichier de contexte ou de règles permanentes (comme `AGENTS.md`).
:::note[Prérequis]
- Méthode BMad installée
- Connaissance de la pile technologique et des conventions de votre projet
:::
## Quand utiliser cette fonctionnalité
- Vous avez des préférences techniques fortes avant de commencer l'architecture
- Vous avez terminé l'architecture et souhaitez consigner les décisions pour l'implémentation
- Vous travaillez sur une base de code existante avec des patterns établis
- Vous remarquez que les agents prennent des décisions incohérentes entre les stories
## Étape 1 : Choisissez votre approche
**Création manuelle** — Idéal lorsque vous savez exactement quelles règles vous souhaitez documenter
**Génération après l'architecture** — Idéal pour capturer les décisions prises lors du solutioning
**Génération pour les projets existants** — Idéal pour découvrir les patterns dans les bases de code existantes
## Étape 2 : Créez le fichier
### Option A : Création manuelle
Créez le fichier à l'emplacement `_bmad-output/project-context.md` :
```bash
mkdir -p _bmad-output
touch _bmad-output/project-context.md
```
Ajoutez votre pile technologique et vos règles d'implémentation :
```markdown
---
project_name: 'MonProjet'
user_name: 'VotreNom'
date: '2026-02-15'
sections_completed: ['technology_stack', 'critical_rules']
---
# Contexte de Projet pour Agents IA
## Pile Technologique & Versions
- Node.js 20.x, TypeScript 5.3, React 18.2
- State : Zustand
- Tests : Vitest, Playwright
- Styles : Tailwind CSS
## Règles d'Implémentation Critiques
**TypeScript :**
- Mode strict activé, pas de types `any`
- Utiliser `interface` pour les API publiques, `type` pour les unions
**Organisation du Code :**
- Composants dans `/src/components/` avec tests co-localisés
- Les appels API utilisent le singleton `apiClient` — jamais de fetch direct
**Tests :**
- Tests unitaires axés sur la logique métier
- Tests d'intégration utilisent MSW pour le mock API
```
### Option B : Génération après l'architecture
Exécutez le workflow dans une nouvelle conversation :
```bash
bmad-generate-project-context
```
Le workflow analyse votre document d'architecture et vos fichiers projet pour générer un fichier de contexte qui capture les décisions prises.
### Option C : Génération pour les projets existants
Pour les projets existants, exécutez :
```bash
bmad-generate-project-context
```
Le workflow analyse votre base de code pour identifier les conventions, puis génère un fichier de contexte que vous pouvez réviser et affiner.
## Étape 3 : Vérifiez le contenu
Révisez le fichier généré et assurez-vous qu'il capture :
- Les versions correctes des technologies
- Vos conventions réelles (pas les bonnes pratiques génériques)
- Les règles qui évitent les erreurs courantes
- Les patterns spécifiques aux frameworks
Modifiez manuellement pour ajouter les éléments manquants ou supprimer les inexactitudes.
## Ce que vous obtenez
Un fichier `project-context.md` qui :
- Garantit que tous les agents suivent les mêmes conventions
- Évite les décisions incohérentes entre les stories
- Capture les décisions d'architecture pour l'implémentation
- Sert de référence pour les patterns et règles de votre projet
## Conseils
:::tip[Bonnes pratiques]
- **Concentrez-vous sur ce qui n'est pas évident** — Documentez les patterns que les agents pourraient manquer (par ex. « Utiliser JSDoc sur chaque classe publique »), et non les pratiques universelles comme « utiliser des noms de variables significatifs ».
- **Gardez-le concis** — Ce fichier est chargé par chaque workflow d'implémentation. Les fichiers longs gaspillent le contexte. Excluez le contenu qui ne s'applique qu'à un périmètre restreint ou à des stories spécifiques.
- **Mettez à jour si nécessaire** — Modifiez manuellement lorsque les patterns changent, ou régénérez après des changements d'architecture significatifs.
- Fonctionne aussi bien pour Quick Dev que pour les projets complets méthode BMad.
:::
## Prochaines étapes
- [**Explication du contexte projet**](../explanation/project-context.md) — En savoir plus sur son fonctionnement
- [**Carte des workflows**](../reference/workflow-map.md) — Voir quels workflows chargent le contexte projet

View File

@ -0,0 +1,98 @@
---
title: "Corrections Rapides"
description: Comment effectuer des corrections rapides et des modifications ciblées
sidebar:
order: 5
---
Utilisez **Quick Dev** pour les corrections de bugs, les refactorisations ou les petites modifications ciblées qui ne nécessitent pas la méthode BMad complète.
## Quand Utiliser Cette Approche
- Corrections de bugs avec une cause claire et connue
- Petites refactorisations (renommage, extraction, restructuration) contenues dans quelques fichiers
- Ajustements mineurs de fonctionnalités ou modifications de configuration
- Mises à jour de dépendances
:::note[Prérequis]
- Méthode BMad installée (`npx bmad-method install`)
- Un IDE IA (Claude Code, Cursor, ou similaire)
:::
## Étapes
### 1. Démarrer une Nouvelle Conversation
Ouvrez une **nouvelle conversation** dans votre IDE IA. Réutiliser une session d'un workflow précédent peut causer des conflits de contexte.
### 2. Spécifiez Votre Intention
Quick Dev accepte l'intention en forme libre — avant, avec, ou après l'invocation. Exemples :
```text
quick-dev — Corrige le bug de validation de connexion qui permet les mots de passe vides.
```
```text
quick-dev — corrige https://github.com/org/repo/issues/42
```
```text
quick-dev — implémente _bmad-output/implementation-artifacts/my-intent.md
```
```text
Je pense que le problème est dans le middleware d'auth, il ne vérifie pas l'expiration du token.
Regardons... oui, src/auth/middleware.ts ligne 47 saute complètement la vérification exp. lance quick-dev
```
```text
quick-dev
> Que voulez-vous faire ?
Refactoriser UserService pour utiliser async/await au lieu des callbacks.
```
Texte brut, chemins de fichiers, URLs d'issues GitHub, liens de trackers de bugs — tout ce que le LLM peut résoudre en une intention concrète.
### 3. Répondre aux Questions et Approuver
Quick Dev peut poser des questions de clarification ou présenter une courte spécification demandant votre approbation avant l'implémentation. Répondez à ses questions et approuvez lorsque vous êtes satisfait du plan.
### 4. Réviser et Pousser
Quick Dev implémente la modification, révise son propre travail, corrige les problèmes et effectue un commit local. Lorsqu'il a terminé, il ouvre les fichiers affectés dans votre éditeur.
- Parcourez le diff pour confirmer que la modification correspond à votre intention
- Si quelque chose semble incorrect, dites à l'agent ce qu'il faut corriger — il peut itérer dans la même session
Une fois satisfait, poussez le commit. Quick Dev vous proposera de pousser et de créer une PR pour vous.
:::caution[Si Quelque Chose Casse]
Si une modification poussée cause des problèmes inattendus, utilisez `git revert HEAD` pour annuler proprement le dernier commit. Ensuite, démarrez une nouvelle conversation et exécutez Quick Dev à nouveau pour essayer une approche différente.
:::
## Ce Que Vous Obtenez
- Fichiers source modifiés avec la correction ou refactorisation appliquée
- Tests passants (si votre projet a une suite de tests)
- Un commit prêt à pousser avec un message de commit conventionnel
## Travail Différé
Quick Dev garde chaque exécution concentrée sur un seul objectif. Si votre demande contient plusieurs objectifs indépendants, ou si la revue remonte des problèmes préexistants non liés à votre modification, Quick Dev les diffère vers un fichier (`deferred-work.md` dans votre répertoire d'artefacts d'implémentation) plutôt que d'essayer de tout régler en même temps.
Consultez ce fichier après une exécution — c'est votre backlog[^1] de choses sur lesquelles revenir. Chaque élément différé peut être introduit dans une nouvelle exécution Quick Dev ultérieurement.
## Quand Passer à une Planification Formelle
Envisagez d'utiliser la méthode BMad complète lorsque :
- La modification affecte plusieurs systèmes ou nécessite des mises à jour coordonnées dans de nombreux fichiers
- Vous n'êtes pas sûr de la portée et avez besoin d'une découverte des exigences d'abord
- Vous avez besoin de documentation ou de décisions architecturales enregistrées pour l'équipe
Voir [Quick Dev](../explanation/quick-dev.md) pour plus d'informations sur la façon dont Quick Dev s'intègre dans la méthode BMad.
## Glossaire
[^1]: Backlog : liste priorisée de tâches ou d'éléments de travail à traiter ultérieurement, issue des méthodologies agiles.

View File

@ -0,0 +1,78 @@
---
title: "Guide de Division de Documents"
description: Diviser les fichiers markdown volumineux en fichiers plus petits et organisés pour une meilleure gestion du contexte
sidebar:
order: 9
---
Utilisez l'outil `bmad-shard-doc` si vous avez besoin de diviser des fichiers markdown volumineux en fichiers plus petits et organisés pour une meilleure gestion du contexte.
:::caution[Déprécié]
Ceci n'est plus recommandé, et bientôt avec les workflows mis à jour et la plupart des LLM et outils majeurs supportant les sous-processus, cela deviendra inutile.
:::
## Quand lUtiliser
Utilisez ceci uniquement si vous remarquez que votre combinaison outil / modèle ne parvient pas à charger et lire tous les documents en entrée lorsque c'est nécessaire.
## Qu'est-ce que la Division de Documents ?
La division de documents divise les fichiers markdown volumineux en fichiers plus petits et organisés basés sur les titres de niveau 2 (`## Titre`).
### Architecture
```text
Avant Division :
_bmad-output/planning-artifacts/
└── PRD.md (fichier volumineux de 50k tokens)
Après Division :
_bmad-output/planning-artifacts/
└── prd/
├── index.md # Table des matières avec descriptions
├── overview.md # Section 1
├── user-requirements.md # Section 2
├── technical-requirements.md # Section 3
└── ... # Sections supplémentaires
```
## Étapes
### 1. Exécuter l'Outil Shard-Doc
```bash
/bmad-shard-doc
```
### 2. Suivre le Processus Interactif
```text
Agent : Quel document souhaitez-vous diviser ?
Utilisateur : docs/PRD.md
Agent : Destination par défaut : docs/prd/
Accepter la valeur par défaut ? [y/n]
Utilisateur : y
Agent : Division de PRD.md...
✓ 12 fichiers de section créés
✓ index.md généré
✓ Terminé !
```
## Comment Fonctionne la Découverte de Workflow
Les workflows BMad utilisent un **système de découverte double** :
1. **Essaye d'abord le document entier** - Rechercher `document-name.md`
2. **Vérifie la version divisée** - Rechercher `document-name/index.md`
3. **Règle de priorité** - Le document entier a la priorité si les deux existent - supprimez le document entier si vous souhaitez que la version divisée soit utilisée à la place
## Support des Workflows
Tous les workflows BMM prennent en charge les deux formats :
- Documents entiers
- Documents divisés
- Détection automatique
- Transparent pour l'utilisateur

View File

@ -0,0 +1,106 @@
---
title: "Comment passer à la v6"
description: Migrer de BMad v4 vers v6
sidebar:
order: 3
---
Utilisez l'installateur BMad pour passer de la v4 à la v6, qui inclut une détection automatique des installations existantes et une assistance à la migration.
## Quand utiliser ce guide
- Vous avez BMad v4 installé (dossier `.bmad-method`)
- Vous souhaitez migrer vers la nouvelle architecture v6
- Vous avez des artefacts de planification existants à préserver
:::note[Prérequis]
- Node.js 20+
- Installation BMad v4 existante
:::
## Étapes
### 1. Lancer l'installateur
Suivez les [Instructions d'installation](./install-bmad.md).
### 2. Gérer l'installation existante
Quand v4 est détecté, vous pouvez :
- Autoriser l'installateur à sauvegarder et supprimer `.bmad-method`
- Quitter et gérer le nettoyage manuellement
Si vous avez nommé votre dossier de méthode bmad autrement, vous devrez supprimer le dossier vous-même manuellement.
### 3. Nettoyer les skills IDE
Supprimez manuellement les commandes/skills IDE v4 existants - par exemple si vous avez Claude Code, recherchez tous les dossiers imbriqués qui commencent par bmad et supprimez-les :
- `.claude/commands/`
Les nouveaux skills v6 sont installés dans :
- `.claude/skills/`
### 4. Migrer les artefacts de planification
**Si vous avez des documents de planification (Brief/PRD/UX/Architecture) :**
Déplacez-les dans `_bmad-output/planning-artifacts/` avec des noms descriptifs :
- Incluez `PRD` dans le nom de fichier pour les documents PRD[^1]
- Incluez `brief`, `architecture`, ou `ux-design` selon le cas
- Les documents divisés peuvent être dans des sous-dossiers nommés
**Si vous êtes en cours de planification :** Envisagez de redémarrer avec les workflows v6. Utilisez vos documents existants comme entrées - les nouveaux workflows de découverte progressive avec recherche web et mode plan IDE produisent de meilleurs résultats.
### 5. Migrer le développement en cours
Si vous avez des stories[^3] créées ou implémentées :
1. Terminez l'installation v6
2. Placez `epics.md` ou `epics/epic*.md`[^2] dans `_bmad-output/planning-artifacts/`
3. Lancez le workflow `bmad-sprint-planning`[^4]
4. Indiquez quels epics/stories sont déjà terminés
## Ce que vous obtenez
**Structure unifiée v6 :**
```text
votre-projet/
├── _bmad/ # Dossier d'installation unique
│ ├── _config/ # Vos personnalisations
│ │ └── agents/ # Fichiers de personnalisation des agents
│ ├── core/ # Framework core universel
│ ├── bmm/ # Module BMad Method
│ ├── bmb/ # BMad Builder
│ └── cis/ # Creative Intelligence Suite
└── _bmad-output/ # Dossier de sortie (était le dossier doc en v4)
```
## Migration des modules
| Module v4 | Statut v6 |
| ----------------------------- | ----------------------------------------- |
| `.bmad-2d-phaser-game-dev` | Intégré dans le Module BMGD |
| `.bmad-2d-unity-game-dev` | Intégré dans le Module BMGD |
| `.bmad-godot-game-dev` | Intégré dans le Module BMGD |
| `.bmad-infrastructure-devops` | Déprécié - nouvel agent DevOps bientôt disponible |
| `.bmad-creative-writing` | Non adapté - nouveau module v6 bientôt disponible |
## Changements clés
| Concept | v4 | v6 |
| ------------- | ------------------------------------- | ------------------------------------ |
| **Core** | `_bmad-core` était en fait la méthode BMad | `_bmad/core/` est le framework universel |
| **Method** | `_bmad-method` | `_bmad/bmm/` |
| **Config** | Fichiers modifiés directement | `config.yaml` par module |
| **Documents** | Division ou non division requise | Entièrement flexible, scan automatique |
## Glossaire
[^1]: PRD (Product Requirements Document) : document de référence qui décrit les objectifs du produit, les besoins utilisateurs, les fonctionnalités attendues, les contraintes et les critères de succès, afin d'aligner les équipes sur ce qui doit être construit et pourquoi.
[^2]: Epic : dans les méthodologies agiles, une grande unité de travail qui peut être décomposée en plusieurs stories. Un epic représente généralement une fonctionnalité majeure ou un ensemble de capacités livrable sur plusieurs sprints.
[^3]: Story (User Story) : une description courte et simple d'une fonctionnalité du point de vue de l'utilisateur. Les stories sont des unités de travail suffisamment petites pour être complétées en un sprint.
[^4]: Sprint : dans Scrum, une période de temps fixe (généralement 1 à 4 semaines) pendant laquelle l'équipe travaille à livrer un incrément de produit potentiellement libérable.

69
docs/fr/index.md Normal file
View File

@ -0,0 +1,69 @@
---
title: Bienvenue dans la méthode BMad
description: Framework de développement propulsé par l'IA avec des agents spécialisés, des workflows guidés et une planification intelligente
---
La méthode BMad (**B**uild **M**ore **A**rchitect **D**reams) est un module[^1] de développement assisté par l'IA au sein de l'écosystème BMad, conçu pour vous faciliter la création de logiciels par un processus complet, de l'idéation et de la planification jusqu'à l'implémentation agentique. Elle fournit des agents[^2] IA spécialisés, des workflows guidés et une planification intelligente qui s'adapte à la complexité de votre projet, que vous corrigiez un bug ou construisiez une plateforme d'entreprise.
Si vous êtes à l'aise avec les assistants de codage IA comme Claude, Cursor ou GitHub Copilot, vous êtes prêt à commencer.
:::note[🚀 La V6 est là et ce n'est que le début !]
Architecture par Skills, BMad Builder v1, automatisation Dev Loop, et bien plus encore en préparation. **[Consultez la Feuille de route →](./roadmap)**
:::
## Première visite ? Commencez par un tutoriel
La façon la plus rapide de comprendre BMad est de l'essayer.
- **[Premiers pas avec BMad](./tutorials/getting-started.md)** — Installez et comprenez comment fonctionne BMad
- **[Carte des workflows](./reference/workflow-map.md)** — Vue d'ensemble visuelle des phases BMM, des workflows et de la gestion du contexte
:::tip[Envie de plonger directement ?]
Installez BMad et utilisez le skill[^3] `bmad-help` — il vous guidera entièrement en fonction de votre projet et de vos modules installés.
:::
## Comment utiliser cette documentation
Cette documentation est organisée en quatre sections selon ce que vous essayez de faire :
| Section | Objectif |
| ----------------- | ----------------------------------------------------------------------------------------------------------- |
| **Tutoriels** | Orientés apprentissage. Guides étape par étape qui vous accompagnent dans la construction de quelque chose. Commencez ici si vous êtes nouveau. |
| **Guides pratiques** | Orientés tâches. Guides pratiques pour résoudre des problèmes spécifiques. « Comment personnaliser un agent ? » se trouve ici. |
| **Explication** | Orientés compréhension. Explications en profondeur des concepts et de l'architecture. À lire quand vous voulez savoir *pourquoi*. |
| **Référence** | Orientés information. Spécifications techniques pour les agents, workflows et configuration. |
## Étendre et personnaliser
Vous souhaitez étendre BMad avec vos propres agents, workflows ou modules ? Le **[BMad Builder](https://bmad-builder-docs.bmad-method.org/)** fournit le framework et les outils pour créer des extensions personnalisées, que vous ajoutiez de nouvelles capacités à BMad ou que vous construisiez des modules entièrement nouveaux à partir de zéro.
## Ce dont vous aurez besoin
BMad fonctionne avec tout assistant de codage IA qui prend en charge les prompts système personnalisés ou le contexte de projet. Les options populaires incluent :
- **[Claude Code](https://code.claude.com)** — Outil CLI d'Anthropic (recommandé)
- **[Cursor](https://cursor.sh)** — Éditeur de code propulsé par l'IA
- **[Codex CLI](https://github.com/openai/codex)** — Agent de codage terminal d'OpenAI
Vous devriez être à l'aise avec les concepts de base du développement logiciel comme le contrôle de version, la structure de projet et les workflows agiles. Aucune expérience préalable avec les systèmes d'agent de type BMad n'est requise — c'est justement le but de cette documentation.
## Rejoindre la communauté
Obtenez de l'aide, partagez ce que vous construisez ou contribuez à BMad :
- **[Discord](https://discord.gg/gk8jAdXWmj)** — Discutez avec d'autres utilisateurs de BMad, posez des questions, partagez des idées
- **[GitHub](https://github.com/bmad-code-org/BMAD-METHOD)** — Code source, issues et contributions
- **[YouTube](https://www.youtube.com/@BMadCode)** — Tutoriels vidéo et démonstrations
## Prochaine étape
Prêt à vous lancer ? **[Commencez avec BMad](./tutorials/getting-started.md)** et construisez votre premier projet.
---
## Glossaire
[^1]: **Module** : composant autonome du système BMad qui peut être installé et utilisé indépendamment, offrant des fonctionnalités spécifiques.
[^2]: **Agent** : assistant IA spécialisé avec une expertise spécifique qui guide les utilisateurs dans les workflows.
[^3]: **Skill** : capacité ou fonctionnalité invoquable d'un agent pour effectuer une tâche spécifique.

View File

@ -0,0 +1,58 @@
---
title: Agents
description: Agents BMM par défaut avec leurs identifiants de skill, déclencheurs de menu et workflows principaux (Analyst, Architect, UX Designer, Technical Writer)
sidebar:
order: 2
---
## Agents par défaut
Cette page liste les quatre agents BMM (suite Agile) par défaut installés avec la méthode BMad, ainsi que leurs identifiants de skill, déclencheurs de menu et workflows principaux. Chaque agent est invoqué en tant que skill.
## Notes
- Chaque agent est disponible en tant que skill, généré par linstallateur. Lidentifiant de skill (par exemple, `bmad-analyst`) est utilisé pour invoquer lagent.
- Les déclencheurs sont les codes courts de menu (par exemple, `BP`) et les correspondances approximatives affichés dans chaque menu dagent.
- La génération de tests QA est gérée par le skill de workflow `bmad-qa-generate-e2e-tests`. Larchitecte de tests complet (TEA) se trouve dans son propre module.
| Agent | Identifiant de skill | Déclencheurs | Workflows principaux |
|------------------------|----------------------|------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Analyste (Mary) | `bmad-analyst` | `BP`, `MR`, `DR`, `TR`, `CB`, `DP` | Brainstorming du projet, Recherche marché/domaine/technique, Création du brief[^1], Documentation du projet |
| Architecte (Winston) | `bmad-architect` | `CA`, `IR` | Créer larchitecture, Préparation à limplémentation |
| Designer UX (Sally) | `bmad-ux-designer` | `CU` | Création du design UX[^2] |
| Rédacteur Technique (Paige) | `bmad-tech-writer` | `DP`, `WD`, `US`, `MG`, `VD`, `EC` | Documentation du projet, Rédaction de documents, Mise à jour des standards, Génération de diagrammes Mermaid, Validation de documents, Explication de concepts |
## Types de déclencheurs
Les déclencheurs de menu d'agent utilisent deux types d'invocation différents. Connaître le type utilisé par un déclencheur vous aide à fournir la bonne entrée.
### Déclencheurs de workflow (aucun argument nécessaire)
La plupart des déclencheurs chargent un fichier de workflow structuré. Tapez le code du déclencheur et l'agent démarre le workflow, vous demandant de saisir les informations à chaque étape.
Exemples : `BP` (Brainstorm Project), `CA` (Create Architecture), `CU` (Create UX Design)
### Déclencheurs conversationnels (arguments requis)
Certains déclencheurs lancent une conversation libre au lieu d'un workflow structuré. Ils s'attendent à ce que vous décriviez ce dont vous avez besoin à côté du code du déclencheur.
| Agent | Déclencheur | Ce qu'il faut fournir |
| --- | --- | --- |
| Rédacteur Technique (Paige) | `WD` | Description du document à rédiger |
| Rédacteur Technique (Paige) | `US` | Préférences ou conventions à ajouter aux standards |
| Rédacteur Technique (Paige) | `MG` | Description et type de diagramme (séquence, organigramme, etc.) |
| Rédacteur Technique (Paige) | `VD` | Document à valider et domaines à examiner |
| Rédacteur Technique (Paige) | `EC` | Nom du concept à expliquer |
**Exemple :**
```text
WD Rédige un guide de déploiement pour notre configuration Docker
MG Crée un diagramme de séquence montrant le flux dauthentification
EC Explique le fonctionnement du système de modules
```
## Glossaire
[^1]: Brief : document synthétique qui formalise le contexte, les objectifs, le périmètre et les contraintes dun projet ou dune demande, afin daligner rapidement les parties prenantes avant le travail détaillé.
[^2]: UX (User Experience) : expérience utilisateur, englobant lensemble des interactions et perceptions dun utilisateur face à un produit. Le design UX vise à créer des interfaces intuitives, efficaces et agréables en tenant compte des besoins, comportements et contexte dutilisation.

View File

@ -0,0 +1,139 @@
---
title: Skills
description: Référence des skills BMad — ce qu'ils sont, comment ils fonctionnent et où les trouver.
sidebar:
order: 3
---
Les skills sont des prompts pré-construits qui chargent des agents, exécutent des workflows ou lancent des tâches dans votre IDE. L'installateur BMad les génère à partir de vos modules installés au moment de l'installation. Si vous ajoutez, supprimez ou modifiez des modules ultérieurement, relancez l'installateur pour garder les skills synchronisés (voir [Dépannage](#dépannage)).
## Skills vs. Déclencheurs du menu Agent
BMad offre deux façons de démarrer un travail, chacune ayant un usage différent.
| Mécanisme | Comment l'invoquer | Ce qui se passe |
| --- | --- | --- |
| **Skill** | Tapez le nom du skill (ex. `bmad-help`) dans votre IDE | Charge directement un agent, exécute un workflow ou lance une tâche |
| **Déclencheur du menu agent** | Chargez d'abord un agent, puis tapez un code court (ex. `DS`) | L'agent interprète le code et démarre le workflow correspondant tout en préservant son persona |
Les déclencheurs du menu agent nécessitent une session agent active. Utilisez les skills lorsque vous savez quel workflow vous voulez. Utilisez les déclencheurs lorsque vous travaillez déjà avec un agent et souhaitez changer de tâche sans quitter la conversation.
## Comment les skills sont générés
Lorsque vous exécutez `npx bmad-method install`, l'installateur lit les manifests de chaque module sélectionné et écrit un skill par agent, workflow, tâche et outil. Chaque skill est un répertoire contenant un fichier `SKILL.md` qui indique à l'IA de charger le fichier source correspondant et de suivre ses instructions.
L'installateur utilise des modèles pour chaque type de skill :
| Type de skill | Ce que fait le fichier généré |
| --- | --- |
| **Lanceur d'agent** | Charge le fichier de persona de l'agent, active son menu et reste en caractère |
| **Skill de workflow** | Charge la configuration du workflow et suit ses étapes |
| **Skill de tâche** | Charge un fichier de tâche autonome et suit ses instructions |
| **Skill d'outil** | Charge un fichier d'outil autonome et suit ses instructions |
:::note[Relancer l'installateur]
Si vous ajoutez ou supprimez des modules, relancez l'installateur. Il régénère tous les fichiers de skill pour correspondre à votre sélection actuelle de modules.
:::
## Emplacement des fichiers de skill
L'installateur écrit les fichiers de skill dans un répertoire spécifique à l'IDE à l'intérieur de votre projet. Le chemin exact dépend de l'IDE que vous avez sélectionné lors de l'installation.
| IDE / CLI | Répertoire des skills |
| --- | --- |
| Claude Code | `.claude/skills/` |
| Cursor | `.cursor/skills/` |
| Windsurf | `.windsurf/skills/` |
| Autres IDE | Consultez la sortie de l'installateur pour le chemin cible |
Chaque skill est un répertoire contenant un fichier `SKILL.md`. Par exemple, une installation Claude Code ressemble à :
```text
.claude/skills/
├── bmad-help/
│ └── SKILL.md
├── bmad-create-prd/
│ └── SKILL.md
├── bmad-analyst/
│ └── SKILL.md
└── ...
```
Le nom du répertoire détermine le nom du skill dans votre IDE. Par exemple, le répertoire `bmad-analyst/` enregistre le skill `bmad-analyst`.
## Comment découvrir vos skills
Tapez le nom du skill dans votre IDE pour l'invoquer. Certaines plateformes nécessitent d'activer les skills dans les paramètres avant qu'ils n'apparaissent.
Exécutez `bmad-help` pour obtenir des conseils contextuels sur votre prochaine étape.
:::tip[Découverte rapide]
Les répertoires de skills générés dans votre projet sont la liste de référence. Ouvrez-les dans votre explorateur de fichiers pour voir chaque skill avec sa description.
:::
## Catégories de skills
### Skills d'agent
Les skills d'agent chargent une persona[^2] IA spécialisée avec un rôle défini, un style de communication et un menu de workflows. Une fois chargé, l'agent reste en caractère et répond aux déclencheurs du menu.
| Exemple de skill | Agent | Rôle |
| --- | --- | --- |
| `bmad-analyst` | Mary (Analyste) | Brainstorming de projets, recherche, création de briefs |
| `bmad-architect` | Winston (Architecte) | Conçoit l'architecture système |
| `bmad-ux-designer` | Sally (Designer UX) | Crée les designs UX |
| `bmad-tech-writer` | Paige (Rédacteur Technique) | Documente les projets, rédige des guides, génère des diagrammes |
Consultez [Agents](./agents.md) pour la liste complète des agents par défaut et leurs déclencheurs.
### Skills de workflow
Les skills de workflow exécutent un processus structuré en plusieurs étapes sans charger d'abord une persona d'agent. Ils chargent une configuration de workflow et suivent ses étapes.
| Exemple de skill | Objectif |
| --- | --- |
| `bmad-create-prd` | Créer un PRD[^1] |
| `bmad-create-architecture` | Concevoir l'architecture système |
| `bmad-create-epics-and-stories` | Créer des epics et des stories |
| `bmad-dev-story` | Implémenter une story |
| `bmad-code-review` | Effectuer une revue de code |
| `bmad-quick-dev` | Flux rapide unifié — clarifier l'intention, planifier, implémenter, réviser, présenter |
Consultez la [Carte des workflows](./workflow-map.md) pour la référence complète des workflows organisés par phase.
### Skills de tâche et d'outil
Les tâches et outils sont des opérations autonomes qui ne nécessitent pas de contexte d'agent ou de workflow.
**BMad-Help : Votre guide intelligent**
`bmad-help` est votre interface principale pour découvrir quoi faire ensuite. Il inspecte votre projet, comprend les requêtes en langage naturel et recommande la prochaine étape requise ou optionnelle en fonction de vos modules installés.
:::note[Exemple]
```
bmad-help
bmad-help J'ai une idée de SaaS et je connais toutes les fonctionnalités. Par où commencer ?
bmad-help Quelles sont mes options pour le design UX ?
```
:::
**Autres tâches et outils principaux**
Le module principal inclut 11 outils intégrés — revues, compression, brainstorming, gestion de documents, et plus. Consultez [Outils principaux](./core-tools.md) pour la référence complète.
## Convention de nommage
Tous les skills utilisent le préfixe `bmad-` suivi d'un nom descriptif (ex. `bmad-analyst`, `bmad-create-prd`, `bmad-help`). Consultez [Modules](./modules.md) pour les modules disponibles.
## Dépannage
**Les skills n'apparaissent pas après l'installation.** Certaines plateformes nécessitent d'activer explicitement les skills dans les paramètres. Consultez la documentation de votre IDE ou demandez à votre assistant IA comment activer les skills. Vous devrez peut-être aussi redémarrer votre IDE ou recharger la fenêtre.
**Des skills attendus sont manquants.** L'installateur génère uniquement les skills pour les modules que vous avez sélectionnés. Exécutez à nouveau `npx bmad-method install` et vérifiez votre sélection de modules. Vérifiez que les fichiers de skill existent dans le répertoire attendu.
**Des skills d'un module supprimé apparaissent encore.** L'installateur ne supprime pas automatiquement les anciens fichiers de skill. Supprimez les répertoires obsolètes du répertoire de skills de votre IDE, ou supprimez tout le répertoire de skills et relancez l'installateur pour obtenir un ensemble propre.
## Glossaire
[^1]: PRD (Product Requirements Document) : document de référence qui décrit les objectifs du produit, les besoins utilisateurs, les fonctionnalités attendues, les contraintes et les critères de succès, afin daligner les équipes sur ce qui doit être construit et pourquoi.
[^2]: Persona : dans le contexte de BMad, une persona désigne un agent IA avec un rôle défini, un style de communication et une expertise spécifiques (ex. Mary l'analyste, Winston l'architecte). Chaque persona garde son "caractère" pendant les interactions.

View File

@ -0,0 +1,298 @@
---
title: Outils Principaux
description: Référence pour toutes les tâches et tous les workflows intégrés disponibles dans chaque installation BMad sans modules supplémentaires.
sidebar:
order: 2
---
Chaque installation BMad comprend un ensemble de compétences principales qui peuvent être utilisées conjointement avec tout ce que vous faites — des tâches et des workflows autonomes qui fonctionnent dans tous les projets, tous les modules et toutes les phases. Ceux-ci sont toujours disponibles, quels que soient les modules optionnels que vous installez.
:::tip[Raccourci Rapide]
Exécutez n'importe quel outil principal en tapant son nom de compétence (par ex., `bmad-help`) dans votre IDE. Aucune session d'agent requise.
:::
## Vue d'ensemble
| Outil | Type | Objectif |
|-----------------------------------------------------------------------|----------|------------------------------------------------------------------------------|
| [`bmad-help`](#bmad-help) | Tâche | Obtenir des conseils contextuels sur la prochaine étape |
| [`bmad-brainstorming`](#bmad-brainstorming) | Workflow | Faciliter des sessions de brainstorming interactives |
| [`bmad-party-mode`](#bmad-party-mode) | Workflow | Orchestrer des discussions de groupe multi-agents |
| [`bmad-distillator`](#bmad-distillator) | Tâche | Compression sans perte optimisée pour LLM de documents |
| [`bmad-advanced-elicitation`](#bmad-advanced-elicitation) | Tâche | Pousser la sortie LLM à travers des méthodes de raffinement itératives |
| [`bmad-review-adversarial-general`](#bmad-review-adversarial-general) | Tâche | Revue cynique qui trouve ce qui manque et ce qui ne va pas |
| [`bmad-review-edge-case-hunter`](#bmad-review-edge-case-hunter) | Tâche | Analyse exhaustive des chemins de branchement pour les cas limites non gérés |
| [`bmad-editorial-review-prose`](#bmad-editorial-review-prose) | Tâche | Révision de copie clinique pour la clarté de communication |
| [`bmad-editorial-review-structure`](#bmad-editorial-review-structure) | Tâche | Édition structurelle — coupes, fusions et réorganisation |
| [`bmad-shard-doc`](#bmad-shard-doc) | Tâche | Diviser les fichiers markdown volumineux en sections organisées |
| [`bmad-index-docs`](#bmad-index-docs) | Tâche | Générer ou mettre à jour un index de tous les documents dans un dossier |
## bmad-help
**Votre guide intelligent pour la suite.** — Inspecte l'état de votre projet, détecte ce qui a été fait et recommande la prochaine étape requise ou facultative.
**Utilisez-le quand :**
- Vous avez terminé un workflow et voulez savoir ce qui suit
- Vous êtes nouveau sur BMad et avez besoin d'orientation
- Vous êtes bloqué et voulez des conseils contextuels
- Vous avez installé de nouveaux modules et voulez voir ce qui est disponible
**Fonctionnement :**
1. Analyse votre projet pour les artefacts existants (PRD, architecture, stories, etc.)
2. Détecte quels modules sont installés et leurs workflows disponibles
3. Recommande les prochaines étapes par ordre de priorité — étapes requises d'abord, puis facultatives
4. Présente chaque recommandation avec la commande de compétence et une brève description
**Entrée :** Requête optionnelle en langage naturel (par ex., `bmad-help J'ai une idée de SaaS, par où commencer ?`)
**Sortie :** Liste priorisée des prochaines étapes recommandées avec les commandes de compétence
## bmad-brainstorming
**Génère des idées diverses à travers des techniques créatives interactives.** — Une session de brainstorming facilitée qui charge des méthodes d'idéation éprouvées depuis une bibliothèque de techniques et vous guide vers plus de 100 idées avant organisation.
**Utilisez-le quand :**
- Vous commencez un nouveau projet et devez explorer lespace problème
- Vous êtes bloqué dans la génération d'idées et avez besoin de créativité structurée
- Vous voulez utiliser des cadres d'idéation éprouvés (SCAMPER, brainstorming inversé, etc.)
**Fonctionnement :**
1. Configure une session de brainstorming avec votre sujet
2. Charge les techniques créatives depuis une bibliothèque de méthodes
3. Vous guide à travers technique après technique, générant des idées
4. Applique un protocole anti-biais — change de domaine créatif toutes les 10 idées pour éviter le regroupement
5. Produit un document de session en mode ajout uniquement avec toutes les idées organisées par technique
**Entrée :** Sujet de brainstorming ou énoncé de problème, fichier de contexte optionnel
**Sortie :** `brainstorming-session-{date}.md` avec toutes les idées générées
:::note[Cible de Quantité]
La magie se produit dans les idées 50100. Le workflow encourage la génération de plus de 100 idées avant organisation.
:::
## bmad-party-mode
**Orchestre des discussions de groupe multi-agents.** — Charge tous les agents BMad installés et facilite une conversation naturelle où chaque agent contribue depuis son expertise et personnalité uniques.
**Utilisez-le quand :**
- Vous avez besoin de multiples perspectives d'experts sur une décision
- Vous voulez que les agents remettent en question les hypothèses des autres
- Vous explorez un sujet complexe qui couvre plusieurs domaines
**Fonctionnement :**
1. Charge le manifeste d'agents avec toutes les personnalités d'agents installées
2. Analyse votre sujet pour sélectionner les 23 agents les plus pertinents
3. Les agents prennent des tours pour contribuer, avec des échanges naturels et des désaccords
4. Fait rouler la participation des agents pour assurer des perspectives diverses au fil du temps
5. Quittez avec `goodbye`, `end party` ou `quit`
**Entrée :** Sujet de discussion ou question, ainsi que la spécification des personas que vous souhaitez faire participer (optionnel)
**Sortie :** Conversation multi-agents en temps réel avec des personnalités d'agents maintenues
## bmad-distillator
**Compression sans perte optimisée pour LLM de documents sources.** — Produit des distillats denses et efficaces en tokens qui préservent toute l'information pour la consommation par des LLM en aval. Vérifiable par reconstruction aller-retour.
**Utilisez-le quand :**
- Un document est trop volumineux pour la fenêtre de contexte d'un LLM
- Vous avez besoin de versions économes en tokens de recherches, spécifications ou artefacts de planification
- Vous voulez vérifier qu'aucune information n'est perdue pendant la compression
- Les agents auront besoin de référencer et de trouver fréquemment des informations dedans
**Fonctionnement :**
1. **Analyser** — Lit les documents sources, identifie la densité d'information et la structure
2. **Compresser** — Convertit la prose en format dense de liste de points, supprime le formatage décoratif
3. **Vérifier** — Vérifie l'exhaustivité pour s'assurer que toute l'information originale est préservée
4. **Valider** (optionnel) — Le test de reconstruction aller-retour prouve la compression sans perte
**Entrée :**
- `source_documents` (requis) — Chemins de fichiers, chemins de dossiers ou motifs glob
- `downstream_consumer` (optionnel) — Ce qui va le consommer (par ex., "création de PRD")
- `token_budget` (optionnel) — Taille cible approximative
- `--validate` (drapeau) — Exécuter le test de reconstruction aller-retour
**Sortie :** Fichier(s) markdown distillé(s) avec rapport de ratio de compression (par ex., "3.2:1")
## bmad-advanced-elicitation
**Passer la sortie du LLM à travers des méthodes de raffinement itératives.** — Sélectionne depuis une bibliothèque de techniques d'élicitation pour améliorer systématiquement le contenu à travers multiples passages.
**Utilisez-le quand :**
- La sortie du LLM semble superficielle ou générique
- Vous voulez explorer un sujet depuis de multiples angles analytiques
- Vous raffinez un document critique et voulez une réflexion plus approfondie
**Fonctionnement :**
1. Charge le registre de méthodes avec plus de 5 techniques d'élicitation
2. Sélectionne les 5 méthodes les mieux adaptées selon le type de contenu et la complexité
3. Présente un menu interactif — choisissez une méthode, remélangez, ou listez tout
4. Applique la méthode sélectionnée pour améliorer le contenu
5. Re-présente les options pour l'amélioration itérative jusqu'à ce que vous sélectionniez "Procéder"
**Entrée :** Section de contenu à améliorer
**Sortie :** Version améliorée du contenu avec les améliorations appliquées
## bmad-review-adversarial-general
**Revue contradictoire qui suppose que des problèmes existent et les recherche.** — Adopte une perspective de réviseur sceptique et blasé avec zéro tolérance pour le travail bâclé. Cherche ce qui manque, pas seulement ce qui ne va pas.
**Utilisez-le quand :**
- Vous avez besoin d'assurance qualité avant de finaliser un livrable
- Vous voulez tester en conditions réelles une spécification, story ou document
- Vous voulez trouver des lacunes de couverture que les revues optimistes manquent
**Fonctionnement :**
1. Lit le contenu avec une perspective contradictoire et critique
2. Identifie les problèmes à travers l'exhaustivité, la justesse et la qualité
3. Recherche spécifiquement ce qui manque — pas seulement ce qui est présent et faux
4. Doit trouver un minimum de 10 problèmes ou réanalyse plus profondément
**Entrée :**
- `content` (requis) — Diff, spécification, story, document ou tout artefact
- `also_consider` (optionnel) — Domaines supplémentaires à garder à l'esprit
**Sortie :** Liste markdown de plus de 10 constatations avec descriptions
## bmad-review-edge-case-hunter
**Parcours tous les chemins de branchement et les conditions limites, ne rapporte que les cas non gérés.** — Méthodologie pure de traçage de chemin[^1] qui dérive mécaniquement les classes de cas limites. Orthogonale à la revue contradictoire — centrée sur la méthode, pas sur l'attitude.
**À utiliser quand :**
- Vous souhaitez une couverture exhaustive des cas limites pour le code ou la logique
- Vous avez besoin d'un complément à la revue contradictoire (méthodologie différente, résultats différents)
- Vous révisez un diff ou une fonction pour des conditions limites
**Fonctionnement :**
1. Énumère tous les chemins de branchement dans le contenu
2. Dérive mécaniquement les classes de cas limites : else/default manquants, entrées non vérifiées, décalage dunité, overflow arithmétique, coercition implicite des types, conditions de concurrence, écarts de timeout
3. Teste chaque chemin contre les protections existantes
4. Ne rapporte que les chemins non gérés — ignore silencieusement les chemins gérés
**Entrée :**
- `content` (obligatoire) — Diff, fichier complet ou fonction
- `also_consider` (facultatif) — Zones supplémentaires à garder à lesprit
**Sortie :** Tableau JSON des résultats, chacun avec `location`, `trigger_condition`, `guard_snippet` et `potential_consequence`
:::note[Revue Complémentaire]
Exécutez à la fois `bmad-review-adversarial-general` et `bmad-review-edge-case-hunter` pour une couverture orthogonale. La revue contradictoire détecte les problèmes de qualité et de complétude ; le chasseur de cas limites détecte les chemins non gérés.
:::
## bmad-editorial-review-prose
**Relecture éditoriale clinique centrée sur la clarté de communication.** — Analyse le texte pour détecter les problèmes qui nuisent à la compréhension. Applique le Microsoft Writing Style Guide baseline. Préserve la voix de lauteur.
**À utiliser quand :**
- Vous avez rédigé un document et souhaitez polir le style
- Vous devez assurer la clarté pour un public spécifique
- Vous voulez des corrections de communication sans modifier les choix stylistiques
**Fonctionnement :**
1. Lit le contenu en ignorant les blocs de code et le frontmatter
2. Identifie les problèmes de communication (pas les préférences de style)
3. Déduit les doublons du même problème à différents emplacements
4. Produit un tableau de corrections en trois colonnes
**Entrée :**
- `content` (obligatoire) — Markdown, texte brut ou XML
- `style_guide` (facultatif) — Guide de style spécifique au projet
- `reader_type` (facultatif) — `humans` (par défaut) pour clarté/fluide, ou `llm` pour précision/consistance
**Sortie :** Tableau Markdown en trois colonnes : Texte original | Texte révisé | Modifications
## bmad-editorial-review-structure
**Édition structurelle — propose des coupes, fusions, déplacements et condensations.** — Révise l'organisation du document et propose des changements substantiels pour améliorer la clarté et le flux avant la révision de copie.
**Utilisez-le quand :**
- Un document a été produit depuis de multiples sous-processus et a besoin de cohérence structurelle
- Vous voulez réduire la longueur du document tout en préservant la compréhension
- Vous devez identifier les violations de portée ou les informations critiques enfouies
**Fonctionnement :**
1. Analyse le document contre 5 modèles de structure (Tutoriel, Référence, Explication, Prompt, Stratégique)
2. Identifie les redondances, violations de portée et informations enfouies
3. Produit des recommandations priorisées : COUPER, FUSIONNER, DÉPLACER, CONDENSER, QUESTIONNER, PRÉSERVER
4. Estime la réduction totale en mots et pourcentage
**Entrée :**
- `content` (requis) — Document à réviser
- `purpose` (optionnel) — Objectif prévu (par ex., "tutoriel de démarrage rapide")
- `target_audience` (optionnel) — Qui lit ceci
- `reader_type` (optionnel) — `humans` ou `llm`
- `length_target` (optionnel) — Réduction cible (par ex., "30% plus court")
**Sortie :** Résumé du document, liste de recommandations priorisées et réduction estimée
## bmad-shard-doc
**Diviser les fichiers markdown volumineux en fichiers de sections organisés.** — Utilise les en-têtes de niveau 2 comme points de division pour créer un dossier de fichiers de sections autonomes avec un index.
**Utilisez-le quand :**
- Un document markdown est devenu trop volumineux pour être géré efficacement (plus de 500 lignes)
- Vous voulez diviser un document monolithique en sections navigables
- Vous avez besoin de fichiers séparés pour l'édition parallèle ou la gestion de contexte LLM
**Fonctionnement :**
1. Valide que le fichier source existe et est markdown
2. Divise sur les en-têtes de niveau 2 (`##`) en fichiers de sections numérotées
3. Crée un `index.md` avec manifeste de sections et liens
4. Vous invite à supprimer, archiver ou conserver l'original
**Entrée :** Chemin du fichier markdown source, dossier de destination optionnel
**Sortie :** Dossier avec `index.md` et `01-{section}.md`, `02-{section}.md`, etc.
## bmad-index-docs
**Générer ou mettre à jour un index de tous les documents dans un dossier.** — Analyse un répertoire, lit chaque fichier pour comprendre son objectif et produit un `index.md` organisé avec liens et descriptions.
**Utilisez-le quand :**
- Vous avez besoin d'un index léger pour un scan LLM rapide des documents disponibles
- Un dossier de documentation a grandi et a besoin d'une table des matières organisée
- Vous voulez un aperçu auto-généré qui reste à jour
**Fonctionnement :**
1. Analyse le répertoire cible pour tous les fichiers non cachés
2. Lit chaque fichier pour comprendre son objectif réel
3. Groupe les fichiers par type, objectif ou sous-répertoire
4. Génère des descriptions concises (310 mots chacune)
**Entrée :** Chemin du dossier cible
**Sortie :** `index.md` avec listes de fichiers organisées, liens relatifs et brèves descriptions
## Glossaire
[^1]: Path-tracing : méthode d'analyse qui suit systématiquement tous les chemins d'exécution possibles dans un programme pour identifier les cas non gérés.

View File

@ -0,0 +1,82 @@
---
title: Modules Officiels
description: Modules additionnels pour créer des agents personnalisés, de l'intelligence créative, du développement de jeux et des tests
sidebar:
order: 4
---
BMad s'étend via des modules officiels que vous sélectionnez lors de l'installation. Ces modules additionnels fournissent des agents, des workflows et des tâches spécialisés pour des domaines spécifiques, au-delà du noyau intégré et de BMM (suite Agile).
:::tip[Installer des Modules]
Exécutez `npx bmad-method install` et sélectionnez les modules souhaités. L'installateur gère automatiquement le téléchargement, la configuration et l'intégration IDE.
:::
## BMad Builder
Créez des agents personnalisés, des workflows et des modules spécifiques à un domaine avec une assistance guidée. BMad Builder est le méta-module pour étendre le framework lui-même.
- **Code :** `bmb`
- **npm :** [`bmad-builder`](https://www.npmjs.com/package/bmad-builder)
- **GitHub :** [bmad-code-org/bmad-builder](https://github.com/bmad-code-org/bmad-builder)
**Fournit :**
- Agent Builder — créez des agents IA spécialisés avec une expertise et un accès aux outils personnalisés
- Workflow Builder — concevez des processus structurés avec des étapes et des points de décision
- Module Builder — empaquetez des agents et des workflows dans des modules partageables et publiables
- Configuration interactive avec support de configuration YAML et publication npm
## Creative Intelligence Suite
Outils basés sur l'IA pour la créativité structurée, l'idéation et l'innovation pendant le développement en phase amont. La suite fournit plusieurs agents qui facilitent le brainstorming, le design thinking et la résolution de problèmes en utilisant des cadres éprouvés.
- **Code :** `cis`
- **npm :** [`bmad-creative-intelligence-suite`](https://www.npmjs.com/package/bmad-creative-intelligence-suite)
- **GitHub :** [bmad-code-org/bmad-module-creative-intelligence-suite](https://github.com/bmad-code-org/bmad-module-creative-intelligence-suite)
**Fournit :**
- Agents Innovation Strategist, Design Thinking Coach et Brainstorming Coach
- Problem Solver et Creative Problem Solver pour la pensée systématique et latérale
- Storyteller et Presentation Master pour les récits et les présentations
- Cadres d'idéation incluant SCAMPER[^1], Brainstorming inversé et reformulation de problèmes
## Game Dev Studio
Workflows de développement de jeux structurés adaptés pour Unity, Unreal, Godot et moteurs personnalisés. Supporte le prototypage rapide via Quick Dev et la production à grande échelle avec des sprints propulsés par epics.
- **Code :** `gds`
- **npm :** [`bmad-game-dev-studio`](https://www.npmjs.com/package/bmad-game-dev-studio)
- **GitHub :** [bmad-code-org/bmad-module-game-dev-studio](https://github.com/bmad-code-org/bmad-module-game-dev-studio)
**Fournit :**
- Workflow de génération de Document de Design de Jeu (GDD[^3])
- Mode Quick Dev pour le prototypage rapide
- Support de design narratif pour les personnages, dialogues et construction de monde
- Couverture de plus de 21 types de jeux avec des conseils d'architecture spécifiques au moteur
## Test Architect (TEA)
Stratégie de test de niveau entreprise, conseils d'automatisation et décisions de porte de release via un agent expert et neuf workflows structurés. TEA va bien au-delà du workflow QA intégré avec une priorisation basée sur les risques et une traçabilité des exigences.
- **Code :** `tea`
- **npm :** [`bmad-method-test-architecture-enterprise`](https://www.npmjs.com/package/bmad-method-test-architecture-enterprise)
- **GitHub :** [bmad-code-org/bmad-method-test-architecture-enterprise](https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise)
**Fournit :**
- Agent Murat (Master Test Architect and Quality Advisor)
- Workflows pour la conception de tests, ATDD, l'automatisation, la revue de tests et la traçabilité
- Évaluation NFR[^2], configuration CI et scaffolding de framework
- Priorisation P0-P3 avec Playwright Utils et intégrations MCP optionnelles
## Modules Communautaires
Les modules communautaires et une marketplace de modules sont à venir. Consultez l'[organisation GitHub BMad](https://github.com/bmad-code-org) pour les mises à jour.
## Glossaire
[^1]: SCAMPER : acronyme anglais pour une technique de créativité structurée (Substitute, Combine, Adapt, Modify, Put to another use, Eliminate, Reverse) qui permet d'explorer systématiquement les modifications possibles d'un produit ou d'une idée pour générer des innovations.
[^2]: NFR (Non-Functional Requirement) : exigence décrivant les contraintes de qualité du système (performance, sécurité, fiabilité, ergonomie) plutôt que ses fonctionnalités.
[^3]: GDD (Game Design Document) : document de conception de jeu qui décrit en détail les mécaniques, l'univers, les personnages, les niveaux et tous les aspects du jeu à développer.

View File

@ -0,0 +1,111 @@
---
title: Options de Testing
description: Comparaison du workflow QA intégré avec le module Test Architect (TEA) pour l'automatisation des tests.
sidebar:
order: 5
---
BMad propose deux approches de test : un workflow QA[^1] intégré pour une génération rapide de tests et un module Test Architect installable pour une stratégie de test de qualité entreprise.
## Lequel Choisir ?
| Facteur | QA Intégré | Module TEA |
|-------------------------|----------------------------------------------|---------------------------------------------------------------------|
| **Idéal pour** | Projets petits et moyens, couverture rapide | Grands projets, domaines réglementés ou complexes |
| **Installation** | Rien à installer — inclus dans BMM | Installer séparément via `npx bmad-method install` |
| **Approche** | Générer les tests rapidement, itérer ensuite | Planifier d'abord, puis générer avec traçabilité |
| **Types de tests** | Tests API et E2E | API, E2E, ATDD[^2], NFR, et plus |
| **Stratégie** | Chemin nominal + cas limites critiques | Priorisation basée sur les risques (P0-P3) |
| **Nombre de workflows** | 1 (Automate) | 9 (conception, ATDD, automatisation, revue, traçabilité, et autres) |
:::tip[Commencez avec le QA Intégré]
La plupart des projets devraient commencer avec le workflow QA intégré. Si vous avez ensuite besoin d'une stratégie de test, de murs de qualité ou de traçabilité des exigences, installez TEA en complément.
:::
## Workflow QA Intégré
Le workflow QA intégré est inclus dans le module BMM (suite Agile). Il génère rapidement des tests fonctionnels en utilisant le framework de test existant de votre projet — aucune configuration ni installation supplémentaire requise.
**Déclencheur :** `QA` ou `bmad-qa-generate-e2e-tests`
### Ce que le Workflow QA Fait
Le workflow QA exécute un processus unique (Automate) qui parcourt cinq étapes :
1. **Détecte le framework de test** — analyse `package.json` et les fichiers de test existants pour identifier votre framework (Jest, Vitest, Playwright, Cypress, ou tout runner standard). Si aucun n'existe, analyse la pile technologique du projet et en suggère un.
2. **Identifie les fonctionnalités** — demande ce qu'il faut tester ou découvre automatiquement les fonctionnalités dans le codebase.
3. **Génère les tests API** — couvre les codes de statut, la structure des réponses, le chemin nominal, et 1-2 cas d'erreur.
4. **Génére les tests E2E** — couvre les parcours utilisateur avec des localisateurs sémantiques et des assertions sur les résultats visibles.
5. **Exécute et vérifie** — lance les tests générés et corrige immédiatement les échecs.
Le workflow QA produit un résumé de test sauvegardé dans le dossier des artefacts d'implémentation de votre projet.
### Patterns de Test
Les tests générés suivent une philosophie "simple et maintenable" :
- **APIs standard du framework uniquement** — pas d'utilitaires externes ni d'abstractions personnalisées
- **Localisateurs sémantiques** pour les tests UI (rôles, labels, texte plutôt que sélecteurs CSS)
- **Tests indépendants** sans dépendances d'ordre
- **Pas d'attentes ou de sleeps codés en dur**
- **Descriptions claires** qui se lisent comme de la documentation fonctionnelle
:::note[Portée]
Le workflow QA génère uniquement des tests. Pour la revue de code et la validation des stories, utilisez plutôt le workflow Code Review (`CR`).
:::
### Quand Utiliser le QA Intégré
- Couverture de test rapide pour une fonctionnalité nouvelle ou existante
- Automatisation de tests accessible aux débutants sans configuration avancée
- Patterns de test standards que tout développeur peut lire et maintenir
- Projets petits et moyens où une stratégie de test complète n'est pas nécessaire
## Module Test Architect (TEA)
TEA est un module autonome qui fournit un agent expert (Murat) et neuf workflows structurés pour des tests de qualité entreprise. Il va au-delà de la génération de tests pour inclure la stratégie de test, la planification basée sur les risques, les murs de qualité et la traçabilité des exigences.
- **Documentation :** [TEA Module Docs](https://bmad-code-org.github.io/bmad-method-test-architecture-enterprise/)
- **Installation :** `npx bmad-method install` et sélectionnez le module TEA
- **npm :** [`bmad-method-test-architecture-enterprise`](https://www.npmjs.com/package/bmad-method-test-architecture-enterprise)
### Ce que TEA Fournit
| Workflow | Objectif |
|-----------------------|--------------------------------------------------------------------------------------|
| Test Design | Créer une stratégie de test complète liée aux exigences |
| ATDD | Développement piloté par les tests d'acceptation avec critères des parties prenantes |
| Automate | Générer des tests avec des patterns et utilitaires avancés |
| Test Review | Valider la qualité et la couverture des tests par rapport à la stratégie |
| Traceability | Remonter les tests aux exigences pour l'audit et la conformité |
| NFR Assessment | Évaluer les exigences non-fonctionnelles (performance, sécurité) |
| CI Setup | Configurer l'exécution des tests dans les pipelines d'intégration continue |
| Framework Scaffolding | Configurer l'infrastructure de test et la structure du projet |
| Release Gate | Prendre des décisions de livraison go/no-go basées sur les données |
TEA supporte également la priorisation basée sur les risques P0-P3 et des intégrations optionnelles avec Playwright Utils et les outils MCP.
### Quand Utiliser TEA
- Projets nécessitant une traçabilité des exigences ou une documentation de conformité
- Équipes ayant besoin d'une priorisation des tests basée sur les risques sur plusieurs fonctionnalités
- Environnements entreprise avec des murs de qualité formels avant livraison
- Domaines complexes où la stratégie de test doit être planifiée avant d'écrire les tests
- Projets ayant dépassé l'approche à workflow unique du QA intégré
## Comment les Tests S'Intègrent dans les Workflows
Le workflow Automate du QA intégré apparaît dans la Phase 4 (Implémentation) de la carte de workflow méthode BMad. Il est conçu pour s'exécuter **après qu'un epic complet soit terminé** — une fois que toutes les stories d'un epic ont été implémentées et revues. Une séquence typique :
1. Pour chaque story de l'epic : implémenter avec Dev Story (`DS`), puis valider avec Code Review (`CR`)
2. Après la fin de l'epic : générer les tests avec le workflow QA (`QA`) ou le workflow Automate de TEA
3. Lancer la rétrospective (`bmad-retrospective`) pour capturer les leçons apprises
Le workflow QA travaille directement à partir du code source sans charger les documents de planification (PRD, architecture). Les workflows TEA peuvent s'intégrer avec les artefacts de planification en amont pour la traçabilité.
Pour en savoir plus sur la place des tests dans le processus global, consultez la [Carte des Workflows](./workflow-map.md).
## Glossaire
[^1]: QA (Quality Assurance) : assurance qualité, ensemble des processus et activités visant à garantir que le produit logiciel répond aux exigences de qualité définies.
[^2]: ATDD (Acceptance Test-Driven Development) : méthode de développement où les tests d'acceptation sont écrits avant le code, en collaboration avec les parties prenantes pour définir les critères de réussite.

View File

@ -0,0 +1,94 @@
---
title: "Carte des Workflows"
description: Référence visuelle des phases et des résultats des workflows de la méthode BMad
sidebar:
order: 1
---
La méthode BMad (BMM) est un module de l'écosystème BMad, conçu pour suivre les meilleures pratiques de l'ingénierie du contexte et de la planification. Les agents IA fonctionnent de manière optimale avec un contexte clair et structuré. Le système BMM construit ce contexte progressivement à travers 4 phases distinctes — chaque phase, et plusieurs workflows optionnels au sein de chaque phase, produisent des documents qui alimentent la phase suivante, afin que les agents sachent toujours quoi construire et pourquoi.
La logique et les concepts proviennent des méthodologies agiles qui ont été utilisées avec succès dans l'industrie comme cadre mental de référence.
Si à tout moment vous ne savez pas quoi faire, le skill `bmad-help` vous aidera à rester sur la bonne voie ou à savoir quoi faire ensuite. Vous pouvez toujours vous référer à cette page également — mais `bmad-help` est entièrement interactif et beaucoup plus rapide si vous avez déjà installé la méthode BMad. De plus, si vous utilisez différents modules qui ont étendu la méthode BMad ou ajouté d'autres modules complémentaires non extensifs — `bmad-help` évolue pour connaître tout ce qui est disponible et vous donner les meilleurs conseils du moment.
Note finale importante : Chaque workflow ci-dessous peut être exécuté directement avec l'outil de votre choix via un skill ou en chargeant d'abord un agent et en utilisant l'entrée du menu des agents.
<iframe src="/workflow-map-diagram-fr.html" title="Diagramme de la carte des workflows de la méthode BMad" width="100%" height="100%" style="border-radius: 8px; border: 1px solid #334155; min-height: 900px;"></iframe>
<p style="font-size: 0.8rem; text-align: right; margin-top: -0.5rem; margin-bottom: 1rem;">
<a href="/workflow-map-diagram-fr.html" target="_blank" rel="noopener noreferrer">Ouvrir le diagramme dans un nouvel onglet ↗</a>
</p>
## Phase 1 : Analyse (Optionnelle)
Explorez lespace problème et validez les idées avant de vous engager dans la planification.
| Workflow | Objectif | Produit |
|---------------------------------------------------------------------------|------------------------------------------------------------------------------------------|---------------------------|
| `bmad-brainstorming` | Brainstormez des idées de projet avec l'accompagnement guidé d'un coach de brainstorming | `brainstorming-report.md` |
| `bmad-domain-research`, `bmad-market-research`, `bmad-technical-research` | Validez les hypothèses de marché, techniques ou de domaine | Rapport de recherches |
| `bmad-create-product-brief` | Capturez la vision stratégique | `product-brief.md` |
## Phase 2 : Planification
Définissez ce qu'il faut construire et pour qui.
| Workflow | Objectif | Produit |
|-------------------------|---------------------------------------------------------|--------------|
| `bmad-create-prd` | Définissez les exigences (FRs/NFRs)[^1] | `PRD.md`[^2] |
| `bmad-create-ux-design` | Concevez l'expérience utilisateur (lorsque l'UX compte) | `ux-spec.md` |
## Phase 3 : Solutioning
Décidez comment le construire et décomposez le travail en stories.
| Workflow | Objectif | Produit |
|---------------------------------------|---------------------------------------------------|------------------------------|
| `bmad-create-architecture` | Rendez les décisions techniques explicites | `architecture.md` avec ADRs[^3] |
| `bmad-create-epics-and-stories` | Décomposez les exigences en travail implémentable | Fichiers d'epic avec stories |
| `bmad-check-implementation-readiness` | Vérification avant implémentation | Décision Passe/Réserves/Échec |
## Phase 4 : Implémentation
Construisez, une story à la fois. Bientôt disponible : automatisation complète de la phase 4 !
| Workflow | Objectif | Produit |
|------------------------|-------------------------------------------------------------------------------------|----------------------------------|
| `bmad-sprint-planning` | Initialisez le suivi (une fois par projet pour séquencer le cycle de développement) | `sprint-status.yaml` |
| `bmad-create-story` | Préparez la story suivante pour implémentation | `story-[slug].md` |
| `bmad-dev-story` | Implémentez la story | Code fonctionnel + tests |
| `bmad-code-review` | Validez la qualité de l'implémentation | Approuvé ou changements demandés |
| `bmad-correct-course` | Gérez les changements significatifs en cours de sprint | Plan mis à jour ou réorientation |
| `bmad-sprint-status` | Suivez la progression du sprint et le statut des stories | Mise à jour du statut du sprint |
| `bmad-retrospective` | Revue après complétion d'un epic | Leçons apprises |
## Quick Dev (Parcours Parallèle)
Sautez les phases 1-3 pour les travaux de faible envergure et bien compris.
| Workflow | Objectif | Produit |
|------------------|-------------------------------------------------------------------------------------|-----------------------|
| `bmad-quick-dev` | Flux rapide unifié — clarifie l'intention, planifie, implémente, révise et présente | `tech-spec.md` + code |
## Gestion du Contexte
Chaque document devient le contexte de la phase suivante. Le PRD[^2] indique à l'architecte quelles contraintes sont importantes. L'architecture indique à l'agent de développement quels modèles suivre. Les fichiers de story fournissent un contexte focalisé et complet pour l'implémentation. Sans cette structure, les agents prennent des décisions incohérentes.
### Contexte du Projet
:::tip[Recommandé]
Créez `project-context.md` pour vous assurer que les agents IA suivent les règles et préférences de votre projet. Ce fichier fonctionne comme une constitution pour votre projet — il guide les décisions d'implémentation à travers tous les workflows. Ce fichier optionnel peut être généré à la fin de la création de l'architecture, ou dans un projet existant il peut également être généré pour capturer ce qui est important de conserver aligné avec les conventions actuelles.
:::
**Comment le créer :**
- **Manuellement** — Créez `_bmad-output/project-context.md` avec votre pile technologique et vos règles d'implémentation
- **Générez-le** — Exécutez `bmad-generate-project-context` pour l'auto-générer à partir de votre architecture ou de votre codebase
[**En savoir plus sur project-context.md**](../explanation/project-context.md)
## Glossaire
[^1]: FR / NFR (Functional / Non-Functional Requirement) : exigences décrivant respectivement **ce que le système doit faire** (fonctionnalités, comportements attendus) et **comment il doit le faire** (contraintes de performance, sécurité, fiabilité, ergonomie, etc.).
[^2]: PRD (Product Requirements Document) : document de référence qui décrit les objectifs du produit, les besoins utilisateurs, les fonctionnalités attendues, les contraintes et les critères de succès, afin daligner les équipes sur ce qui doit être construit et pourquoi.
[^3]: ADR (Architecture Decision Record) : document qui consigne une décision darchitecture, son contexte, les options envisagées, le choix retenu et ses conséquences, afin dassurer la traçabilité et la compréhension des décisions techniques dans le temps.

136
docs/fr/roadmap.mdx Normal file
View File

@ -0,0 +1,136 @@
---
title: Feuille de route
description: La suite pour BMad - Fonctionnalités, améliorations et contributions de la communauté
---
# La Méthode BMad : Feuille de route publique
La Méthode BMad, BMad Method Module (BMM) et BMad Builder (BMB) évoluent. Voici ce sur quoi nous travaillons et ce qui arrive prochainement.
<div class="roadmap-container">
<h2 class="roadmap-section-title">En cours</h2>
<div class="roadmap-future">
<div class="roadmap-future-card">
<span class="roadmap-emoji">🧩</span>
<h4>Architecture par Skills Universelle</h4>
<p>Un skill, toutes les plateformes. Écrivez une fois, exécutez partout.</p>
</div>
<div class="roadmap-future-card">
<span class="roadmap-emoji">🏗️</span>
<h4>BMad Builder v1</h4>
<p>Créez des agents IA et des workflows prêts pour la production avec des évaluations, des équipes et dégradation gracieuse intégrées.</p>
</div>
<div class="roadmap-future-card">
<span class="roadmap-emoji">🧠</span>
<h4>Système de Contexte Projet</h4>
<p>Votre IA comprend vraiment votre projet. Un contexte adapté au framework qui évolue avec votre base de code.</p>
</div>
<div class="roadmap-future-card">
<span class="roadmap-emoji">📦</span>
<h4>Skills Centralisés</h4>
<p>Installez une fois, utilisez partout. Partagez des skills entre projets sans l'encombrement de fichiers.</p>
</div>
<div class="roadmap-future-card">
<span class="roadmap-emoji">🔄</span>
<h4>Skills Adaptatifs</h4>
<p>Des skills qui connaissent vos outils. Des variantes optimisées pour Claude, Codex, Kimi et OpenCode, et bien d'autres encore.</p>
</div>
<div class="roadmap-future-card">
<span class="roadmap-emoji">📝</span>
<h4>Blog BMad Team Pros</h4>
<p>Guides, articles et perspectives de l'équipe. Lancement prochainement.</p>
</div>
</div>
<h2 class="roadmap-section-title">Pour bien commencer</h2>
<div class="roadmap-future">
<div class="roadmap-future-card">
<span class="roadmap-emoji">🏪</span>
<h4>Marketplace de Skills</h4>
<p>Découvrez, installez et mettez à jour des skills créés par la communauté. À une commande curl de super-pouvoirs.</p>
</div>
<div class="roadmap-future-card">
<span class="roadmap-emoji">🎨</span>
<h4>Personnalisation de Workflow</h4>
<p>Faites-en le vôtre. Intégrez Jira, Linear, des sorties personnalisées à votre workflow, vos règles.</p>
</div>
<div class="roadmap-future-card">
<span class="roadmap-emoji">🚀</span>
<h4>Optimisation Phases 1-3</h4>
<p>Planification éclair avec collecte de contexte par sous-agents. Le mode YOLO rencontre l'excellence guidée.</p>
</div>
<div class="roadmap-future-card">
<span class="roadmap-emoji">🌐</span>
<h4>Prêt pour l'Entreprise</h4>
<p>SSO, journaux d'audit, espaces de travail d'équipe. Toutes les choses ennuyantes qui feront dire oui aux entreprises.</p>
</div>
<div class="roadmap-future-card">
<span class="roadmap-emoji">💎</span>
<h4>Explosion de Modules Communautaires</h4>
<p>Divertissement, sécurité, thérapie, jeu de rôle et bien plus encore. Étendez la plateforme de la Méthode BMad.</p>
</div>
<div class="roadmap-future-card">
<span class="roadmap-emoji">⚡</span>
<h4>Automatisation de la Boucle de Développement</h4>
<p>Pilote automatique optionnel pour le développement. Laissez l'IA gérer le flux tout en maintenant une qualité optimale.</p>
</div>
</div>
<h2 class="roadmap-section-title">Communauté et Équipe</h2>
<div class="roadmap-future">
<div class="roadmap-future-card">
<span class="roadmap-emoji">🎙️</span>
<h4>Le Podcast de la Méthode BMad</h4>
<p>Conversations sur le développement natif IA. Lancement le 1er mars 2026 !</p>
</div>
<div class="roadmap-future-card">
<span class="roadmap-emoji">🎓</span>
<h4>Le Master Class de la Méthode BMad</h4>
<p>Passez d'utilisateur à expert. Approfondissements dans chaque phase, chaque workflow, chaque secret.</p>
</div>
<div class="roadmap-future-card">
<span class="roadmap-emoji">🏗️</span>
<h4>La Master Class BMad Builder</h4>
<p>Construisez vos propres agents. Techniques avancées pour quand vous êtes prêt à créer, pas seulement à utiliser.</p>
</div>
<div class="roadmap-future-card">
<span class="roadmap-emoji">⚡</span>
<h4>BMad Prototype First</h4>
<p>De l'idée au prototype fonctionnel en une seule session. Créez l'application de vos rêves comme une œuvre d'art.</p>
</div>
<div class="roadmap-future-card">
<span class="roadmap-emoji">🌴</span>
<h4>BMad BALM !</h4>
<p>Gestion de vie native IA. Tâches, habitudes, objectifs : votre copilote IA pour tout.</p>
</div>
<div class="roadmap-future-card">
<span class="roadmap-emoji">🖥️</span>
<h4>UI Officielle</h4>
<p>Une belle interface pour tout l'écosystème BMad. La puissance de la CLI, le polissage de l'interface graphique.</p>
</div>
<div class="roadmap-future-card">
<span class="roadmap-emoji">🔒</span>
<h4>BMad in a Box</h4>
<p>Auto-hébergé, isolé, niveau entreprise. Votre assistant IA, votre infrastructure, votre contrôle.</p>
</div>
</div>
<div style="text-align: center; margin-top: 3rem; padding: 2rem; background: var(--color-bg-card); border-radius: 12px; border: 1px solid var(--color-border);">
<h3 style="margin: 0 0 1rem;">Envie de contribuer ?</h3>
<p style="color: var(--slate-color-400); margin: 0;">
Ce n'est qu'une liste partielle de ce qui est prévu. L'équipe Open Source BMad accueille les contributeurs !{" "}<br />
<a href="https://github.com/bmad-code-org/BMAD-METHOD" style="color: var(--color-in-progress);">Rejoignez-nous sur GitHub</a> pour aider à façonner l'avenir du développement propulsé par l'IA.
</p>
<p style="color: var(--slate-color-400); margin: 1.5rem 0 0;">
Vous aimez ce que nous construisons ? Nous apprécions le soutien ponctuel et mensuel sur{" "}<a href="https://buymeacoffee.com/bmad" style="color: var(--color-in-progress);">Buy Me a Coffee</a>.
</p>
<p style="color: var(--slate-color-400); margin: 1rem 0 0;">
Pour les parrainages d'entreprise, les demandes de partenariat, les interventions, les formations ou les demandes médias :{" "}
<a href="mailto:contact@bmadcode.com" style="color: var(--color-in-progress);">contact@bmadcode.com</a>
</p>
</div>
</div>

View File

@ -0,0 +1,279 @@
---
title: "Premiers pas"
description: Installer BMad et construire votre premier projet
---
Construisez des logiciels plus rapidement en utilisant des workflows propulsés par l'IA avec des agents spécialisés qui vous guident à travers la planification, l'architecture et l'implémentation.
## Ce que vous allez apprendre
- Installer et initialiser la méthode BMad pour un nouveau projet
- Utiliser **BMad-Help** — votre guide intelligent qui sait quoi faire ensuite
- Choisir la bonne voie de planification selon la taille de votre projet
- Progresser à travers les phases, des exigences au code fonctionnel
- Utiliser efficacement les agents et les workflows
:::note[Prérequis]
- **Node.js 20+** — Requis pour l'installateur
- **Git** — Recommandé pour le contrôle de version
- **IDE IA** — Claude Code, Cursor, ou similaire
- **Une idée de projet** — Même simple, elle fonctionne pour apprendre
:::
:::tip[Le chemin le plus simple]
**Installer** → `npx bmad-method install`
**Demander** → `bmad-help que dois-je faire en premier ?`
**Construire** → Laissez BMad-Help vous guider workflow par workflow
:::
## Découvrez BMad-Help : votre guide intelligent
**BMad-Help est le moyen le plus rapide de démarrer avec BMad.** Vous n'avez pas besoin de mémoriser les workflows ou les phases — posez simplement la question, et BMad-Help va :
- **Inspecter votre projet** pour voir ce qui a déjà été fait
- **Vous montrer vos options** en fonction des modules que vous avez installés
- **Recommander la prochaine étape** — y compris la première tâche obligatoire
- **Répondre aux questions** comme « J'ai une idée de SaaS, par où commencer ? »
### Comment utiliser BMad-Help
Exécutez-le dans votre IDE avec IA en invoquant la skill :
```
bmad-help
```
Ou combinez-le avec une question pour obtenir des conseils adaptés au contexte :
```
bmad-help J'ai une idée de produit SaaS, je connais déjà toutes les fonctionnalités que je veux. Par où dois-je commencer ?
```
BMad-Help répondra avec :
- Ce qui est recommandé pour votre situation
- Quelle est la première tâche obligatoire
- À quoi ressemble le reste du processus
### Il alimente aussi les workflows
BMad-Help ne se contente pas de répondre aux questions — **il s'exécute automatiquement à la fin de chaque workflow** pour vous dire exactement quoi faire ensuite. Pas de devinettes, pas de recherche dans la documentation — juste des conseils clairs sur le prochain workflow requis.
:::tip[Commencez ici]
Après avoir installé BMad, invoquez immédiatement la skill `bmad-help`. Elle détectera les modules que vous avez installés et vous guidera vers le bon point de départ pour votre projet.
:::
## Comprendre BMad
BMad vous aide à construire des logiciels grâce à des workflows guidés avec des agents IA spécialisés. Le processus suit quatre phases :
| Phase | Nom | Ce qui se passe |
|-------|----------------|----------------------------------------------------------------|
| 1 | Analyse | Brainstorming, recherche, product brief *(optionnel)* |
| 2 | Planification | Créer les exigences (PRD[^1] ou spécification technique) |
| 3 | Solutioning | Concevoir l'architecture *(BMad Method/Enterprise uniquement)* |
| 4 | Implémentation | Construire epic[^2] par epic, story[^3] par story |
**[Ouvrir la carte des workflows](../reference/workflow-map.md)** pour explorer les phases, les workflows et la gestion du contexte.
Selon la complexité de votre projet, BMad propose trois voies de planification :
| Voie | Idéal pour | Documents créés |
|------------------|------------------------------------------------------------------------------|----------------------------------------|
| **Quick Dev** | Corrections de bugs, fonctionnalités simples, périmètre clair (1-15 stories) | Spécification technique uniquement |
| **méthode BMad** | Produits, plateformes, fonctionnalités complexes (10-50+ stories) | PRD + Architecture + UX[^4] |
| **Enterprise** | Conformité, systèmes multi-tenant[^5] (30+ stories) | PRD + Architecture + Security + DevOps |
:::note
Les comptes de stories sont indicatifs, pas des définitions. Choisissez votre voie en fonction des besoins de planification, pas du calcul des stories.
:::
## Installation
Ouvrez un terminal dans le répertoire de votre projet et exécutez :
```bash
npx bmad-method install
```
Si vous souhaitez la version préliminaire la plus récente au lieu du canal de release par défaut, utilisez `npx bmad-method@next install`.
Lorsque vous êtes invité à sélectionner des modules, choisissez **méthode BMad**.
L'installateur crée deux dossiers :
- `_bmad/` — agents, workflows, tâches et configuration
- `_bmad-output/` — vide pour l'instant, mais c'est là que vos artefacts seront enregistrés
:::tip[Votre prochaine étape]
Ouvrez votre IDE avec IA dans le dossier du projet et exécutez :
```
bmad-help
```
BMad-Help détectera ce que vous avez accompli et recommandera exactement quoi faire ensuite. Vous pouvez aussi lui poser des questions comme « Quelles sont mes options ? » ou « J'ai une idée de SaaS, par où devrais-je commencer ? »
:::
:::note[Comment charger les agents et exécuter les workflows]
Chaque workflow possède une **skill** que vous invoquez par nom dans votre IDE (par ex., `bmad-create-prd`). Votre outil IA reconnaîtra le nom `bmad-*` et l'exécutera.
:::
:::caution[Nouveaux chats]
Démarrez toujours un nouveau chat pour chaque workflow. Cela évite que les limitations de contexte ne causent des problèmes.
:::
## Étape 1 : Créer votre plan
Travaillez à travers les phases 1-3. **Utilisez de nouveaux chats pour chaque workflow.**
:::tip[Contexte de projet (Optionnel)]
Avant de commencer, envisagez de créer `project-context.md` pour documenter vos préférences techniques et règles d'implémentation. Cela garantit que tous les agents IA suivent vos conventions tout au long du projet.
Créez-le manuellement dans `_bmad-output/project-context.md` ou générez-le après l'architecture en utilisant `bmad-generate-project-context`. [En savoir plus](../explanation/project-context.md).
:::
### Phase 1 : Analyse (Optionnel)
Tous les workflows de cette phase sont optionnels :
- **brainstorming** (`bmad-brainstorming`) — Idéation guidée
- **research** (`bmad-research`) — Recherche marché et technique
- **create-product-brief** (`bmad-create-product-brief`) — Document de base recommandé
### Phase 2 : Planification (Requis)
**Pour les voies BMad Method et Enterprise :**
1. Exécutez `bmad-create-prd` dans un nouveau chat
2. Sortie : `PRD.md`
**Pour la voie Quick Dev :**
- Utilisez le workflow `bmad-quick-dev` (`bmad-quick-dev`) à la place du PRD, puis passez à l'implémentation
:::note[Design UX (Optionnel)]
Si votre projet a une interface utilisateur, exécutez le workflow de design UX (`bmad-create-ux-design`) après avoir créé votre PRD.
:::
### Phase 3 : Solutioning (méthode BMad/Enterprise)
**Créer l'Architecture**
1. Exécutez `bmad-create-architecture` dans un nouveau chat
2. Sortie : Document d'architecture avec les décisions techniques
**Créer les Epics et Stories**
:::tip[Amélioration V6]
Les epics et stories sont maintenant créés *après* l'architecture. Cela produit des stories de meilleure qualité car les décisions d'architecture (base de données, patterns d'API, pile technologique) affectent directement la façon dont le travail doit être décomposé.
:::
1. Exécutez `bmad-create-epics-and-stories` dans un nouveau chat
2. Le workflow utilise à la fois le PRD et l'Architecture pour créer des stories techniquement éclairées
**Vérification de préparation à l'implémentation** *(Hautement recommandé)*
1. Exécutez `bmad-check-implementation-readiness` dans un nouveau chat
2. Valide la cohérence entre tous les documents de planification
## Étape 2 : Construire votre projet
Une fois la planification terminée, passez à l'implémentation. **Chaque workflow doit s'exécuter dans un nouveau chat.**
### Initialiser la planification de sprint
Exécutez `bmad-sprint-planning` dans un nouveau chat. Cela crée `sprint-status.yaml` pour suivre tous les epics et stories.
### Le cycle de construction
Pour chaque story, répétez ce cycle avec de nouveaux chats :
| Étape | Workflow | Commande | Objectif |
| ----- | --------------------- | --------------------- | ----------------------------------- |
| 1 | `bmad-create-story` | `bmad-create-story` | Créer le fichier story depuis l'epic |
| 2 | `bmad-dev-story` | `bmad-dev-story` | Implémenter la story |
| 3 | `bmad-code-review` | `bmad-code-review` | Validation de qualité *(recommandé)* |
Après avoir terminé toutes les stories d'un epic, exécutez `bmad-retrospective` dans un nouveau chat.
## Ce que vous avez accompli
Vous avez appris les fondamentaux de la construction avec BMad :
- Installé BMad et configuré pour votre IDE
- Initialisé un projet avec votre voie de planification choisie
- Créé des documents de planification (PRD, Architecture, Epics & Stories)
- Compris le cycle de construction pour l'implémentation
Votre projet contient maintenant :
```text
your-project/
├── _bmad/ # Configuration BMad
├── _bmad-output/
│ ├── planning-artifacts/
│ │ ├── PRD.md # Votre document d'exigences
│ │ ├── architecture.md # Décisions techniques
│ │ └── epics/ # Fichiers epic et story
│ ├── implementation-artifacts/
│ │ └── sprint-status.yaml # Suivi de sprint
│ └── project-context.md # Règles d'implémentation (optionnel)
└── ...
```
## Référence rapide
| Workflow | Commande | Objectif |
| ------------------------------------- | ------------------------------------------- | ------------------------------------------------ |
| **`bmad-help`** ⭐ | `bmad-help` | **Votre guide intelligent — posez n'importe quelle question !** |
| `bmad-create-prd` | `bmad-create-prd` | Créer le document d'exigences produit |
| `bmad-create-architecture` | `bmad-create-architecture` | Créer le document d'architecture |
| `bmad-generate-project-context` | `bmad-generate-project-context` | Créer le fichier de contexte projet |
| `bmad-create-epics-and-stories` | `bmad-create-epics-and-stories` | Décomposer le PRD en epics |
| `bmad-check-implementation-readiness` | `bmad-check-implementation-readiness` | Valider la cohérence de planification |
| `bmad-sprint-planning` | `bmad-sprint-planning` | Initialiser le suivi de sprint |
| `bmad-create-story` | `bmad-create-story` | Créer un fichier story |
| `bmad-dev-story` | `bmad-dev-story` | Implémenter une story |
| `bmad-code-review` | `bmad-code-review` | Revoir le code implémenté |
## Questions fréquentes
**Ai-je toujours besoin d'une architecture ?**
Uniquement pour les voies méthode BMad et Enterprise. Quick Dev passe directement de la spécification technique (tech-spec) à l'implémentation.
**Puis-je modifier mon plan plus tard ?**
Oui. Utilisez `bmad-correct-course` pour gérer les changements de périmètre.
**Et si je veux d'abord faire du brainstorming ?**
Invoquez l'agent Analyst (`bmad-analyst`) et exécutez `bmad-brainstorming` (`bmad-brainstorming`) avant de commencer votre PRD.
**Dois-je suivre un ordre strict ?**
Pas strictement. Une fois que vous maîtrisez le flux, vous pouvez exécuter les workflows directement en utilisant la référence rapide ci-dessus.
## Obtenir de l'aide
:::tip[Premier arrêt : BMad-Help]
**Invoquez `bmad-help` à tout moment** — c'est le moyen le plus rapide de se débloquer. Posez n'importe quelle question :
- « Que dois-je faire après l'installation ? »
- « Je suis bloqué sur le workflow X »
- « Quelles sont mes options pour Y ? »
- « Montre-moi ce qui a été fait jusqu'ici »
BMad-Help inspecte votre projet, détecte ce que vous avez accompli et vous dit exactement quoi faire ensuite.
:::
- **Pendant les workflows** — Les agents vous guident avec des questions et des explications
- **Communauté** — [Discord](https://discord.gg/gk8jAdXWmj) (#bmad-method-help, #report-bugs-and-issues)
## Points clés à retenir
:::tip[Retenez ceci]
- **Commencez par `bmad-help`** — Votre guide intelligent qui connaît votre projet et vos options
- **Utilisez toujours de nouveaux chats** — Démarrez un nouveau chat pour chaque workflow
- **La voie compte** — Quick Dev utilise `bmad-quick-dev` ; La méthode BMad/Enterprise nécessitent PRD et architecture
- **BMad-Help s'exécute automatiquement** — Chaque workflow se termine par des conseils sur la prochaine étape
:::
Prêt à commencer ? Installez BMad, invoquez `bmad-help`, et laissez votre guide intelligent vous montrer le chemin.
## Glossaire
[^1]: PRD (Product Requirements Document) : document de référence qui décrit les objectifs du produit, les besoins utilisateurs, les fonctionnalités attendues, les contraintes et les critères de succès, afin d'aligner les équipes sur ce qui doit être construit et pourquoi.
[^2]: Epic : grand ensemble de fonctionnalités ou de travaux qui peut être décomposé en plusieurs user stories.
[^3]: Story (User Story) : description courte et simple d'une fonctionnalité du point de vue de l'utilisateur ou du client. Elle représente une unité de travail implémentable en un court délai.
[^4]: UX (User Experience) : expérience utilisateur, englobant l'ensemble des interactions et perceptions d'un utilisateur face à un produit. Le design UX vise à créer des interfaces intuitives, efficaces et agréables en tenant compte des besoins, comportements et contexte d'utilisation.
[^5]: Multi-tenant : architecture logicielle où une seule instance de l'application sert plusieurs clients (tenants) tout en maintenant leurs données isolées et sécurisées les unes des autres.

View File

@ -128,7 +128,7 @@ prompts:
### 3. Apply Your Changes ### 3. Apply Your Changes
After editing, recompile the agent to apply changes: After editing, reinstall to apply changes:
```bash ```bash
npx bmad-method install npx bmad-method install
@ -138,17 +138,16 @@ The installer detects the existing installation and offers these options:
| Option | What It Does | | Option | What It Does |
| ---------------------------- | ------------------------------------------------------------------- | | ---------------------------- | ------------------------------------------------------------------- |
| **Quick Update** | Updates all modules to the latest version and recompiles all agents | | **Quick Update** | Updates all modules to the latest version and applies customizations |
| **Recompile Agents** | Applies customizations only, without updating module files |
| **Modify BMad Installation** | Full installation flow for adding or removing modules | | **Modify BMad Installation** | Full installation flow for adding or removing modules |
For customization-only changes, **Recompile Agents** is the fastest option. For customization-only changes, **Quick Update** is the fastest option.
## Troubleshooting ## Troubleshooting
**Changes not appearing?** **Changes not appearing?**
- Run `npx bmad-method install` and select **Recompile Agents** to apply changes - Run `npx bmad-method install` and select **Quick Update** to apply changes
- Check that your YAML syntax is valid (indentation matters) - Check that your YAML syntax is valid (indentation matters)
- Verify you edited the correct `.customize.yaml` file for the agent - Verify you edited the correct `.customize.yaml` file for the agent
@ -161,7 +160,7 @@ For customization-only changes, **Recompile Agents** is the fastest option.
**Need to reset an agent?** **Need to reset an agent?**
- Clear or delete the agent's `.customize.yaml` file - Clear or delete the agent's `.customize.yaml` file
- Run `npx bmad-method install` and select **Recompile Agents** to restore defaults - Run `npx bmad-method install` and select **Quick Update** to restore defaults
## Workflow Customization ## Workflow Customization

View File

@ -28,7 +28,7 @@ Requires [Node.js](https://nodejs.org) v20+ and `npx` (included with npm).
| `--modules <modules>` | Comma-separated module IDs | `--modules bmm,bmb` | | `--modules <modules>` | Comma-separated module IDs | `--modules bmm,bmb` |
| `--tools <tools>` | Comma-separated tool/IDE IDs (use `none` to skip) | `--tools claude-code,cursor` or `--tools none` | | `--tools <tools>` | Comma-separated tool/IDE IDs (use `none` to skip) | `--tools claude-code,cursor` or `--tools none` |
| `--custom-content <paths>` | Comma-separated paths to custom modules | `--custom-content ~/my-module,~/another-module` | | `--custom-content <paths>` | Comma-separated paths to custom modules | `--custom-content ~/my-module,~/another-module` |
| `--action <type>` | Action for existing installations: `install` (default), `update`, `quick-update`, or `compile-agents` | `--action quick-update` | | `--action <type>` | Action for existing installations: `install` (default), `update`, or `quick-update` | `--action quick-update` |
### Core Configuration ### Core Configuration
@ -121,7 +121,7 @@ npx bmad-method install \
## What You Get ## What You Get
- A fully configured `_bmad/` directory in your project - A fully configured `_bmad/` directory in your project
- Compiled agents and workflows for your selected modules and tools - Agents and workflows configured for your selected modules and tools
- A `_bmad-output/` folder for generated artifacts - A `_bmad-output/` folder for generated artifacts
## Validation and Error Handling ## Validation and Error Handling
@ -132,7 +132,7 @@ BMad validates all provided flags:
- **Modules** — Warns about invalid module IDs (but won't fail) - **Modules** — Warns about invalid module IDs (but won't fail)
- **Tools** — Warns about invalid tool IDs (but won't fail) - **Tools** — Warns about invalid tool IDs (but won't fail)
- **Custom Content** — Each path must contain a valid `module.yaml` file - **Custom Content** — Each path must contain a valid `module.yaml` file
- **Action** — Must be one of: `install`, `update`, `quick-update`, `compile-agents` - **Action** — Must be one of: `install`, `update`, `quick-update`
Invalid values will either: Invalid values will either:
1. Show an error and exit (for critical options like directory) 1. Show an error and exit (for critical options like directory)

View File

@ -128,7 +128,7 @@ prompts:
### 3. 应用您的更改 ### 3. 应用您的更改
编辑后,重新编译智能体以应用更改: 编辑后,重新安装以应用更改:
```bash ```bash
npx bmad-method install npx bmad-method install
@ -138,17 +138,16 @@ npx bmad-method install
| Option | What It Does | | Option | What It Does |
| ---------------------------- | ------------------------------------------------------------------- | | ---------------------------- | ------------------------------------------------------------------- |
| **Quick Update** | 将所有模块更新到最新版本并重新编译所有智能体 | | **Quick Update** | 将所有模块更新到最新版本并应用自定义配置 |
| **Recompile Agents** | 仅应用自定义配置,不更新模块文件 |
| **Modify BMad Installation** | 用于添加或删除模块的完整安装流程 | | **Modify BMad Installation** | 用于添加或删除模块的完整安装流程 |
对于仅自定义配置的更改,**Recompile Agents** 是最快的选项。 对于仅自定义配置的更改,**Quick Update** 是最快的选项。
## 故障排除 ## 故障排除
**更改未生效?** **更改未生效?**
- 运行 `npx bmad-method install` 并选择 **Recompile Agents** 以应用更改 - 运行 `npx bmad-method install` 并选择 **Quick Update** 以应用更改
- 检查您的 YAML 语法是否有效(缩进很重要) - 检查您的 YAML 语法是否有效(缩进很重要)
- 验证您编辑的是该智能体正确的 `.customize.yaml` 文件 - 验证您编辑的是该智能体正确的 `.customize.yaml` 文件
@ -161,7 +160,7 @@ npx bmad-method install
**需要重置智能体?** **需要重置智能体?**
- 清空或删除智能体的 `.customize.yaml` 文件 - 清空或删除智能体的 `.customize.yaml` 文件
- 运行 `npx bmad-method install` 并选择 **Recompile Agents** 以恢复默认设置 - 运行 `npx bmad-method install` 并选择 **Quick Update** 以恢复默认设置
## 工作流自定义 ## 工作流自定义

View File

@ -28,7 +28,7 @@ sidebar:
| `--modules <modules>` | 逗号分隔的模块 ID | `--modules bmm,bmb` | | `--modules <modules>` | 逗号分隔的模块 ID | `--modules bmm,bmb` |
| `--tools <tools>` | 逗号分隔的工具/IDE ID使用 `none` 跳过) | `--tools claude-code,cursor``--tools none` | | `--tools <tools>` | 逗号分隔的工具/IDE ID使用 `none` 跳过) | `--tools claude-code,cursor``--tools none` |
| `--custom-content <paths>` | 逗号分隔的自定义模块路径 | `--custom-content ~/my-module,~/another-module` | | `--custom-content <paths>` | 逗号分隔的自定义模块路径 | `--custom-content ~/my-module,~/another-module` |
| `--action <type>` | 对现有安装的操作:`install`(默认)、`update`、`quick-update` 或 `compile-agents` | `--action quick-update` | | `--action <type>` | 对现有安装的操作:`install`(默认)、`update``quick-update` | `--action quick-update` |
### 核心配置 ### 核心配置
@ -121,7 +121,7 @@ npx bmad-method install \
## 安装结果 ## 安装结果
- 项目中完全配置的 `_bmad/` 目录 - 项目中完全配置的 `_bmad/` 目录
- 为所选模块和工具编译的智能体和工作流 - 为所选模块和工具配置的智能体和工作流
- 用于生成产物的 `_bmad-output/` 文件夹 - 用于生成产物的 `_bmad-output/` 文件夹
## 验证和错误处理 ## 验证和错误处理
@ -132,7 +132,7 @@ BMad 会验证所有提供的标志:
- **模块** — 对无效的模块 ID 发出警告(但不会失败) - **模块** — 对无效的模块 ID 发出警告(但不会失败)
- **工具** — 对无效的工具 ID 发出警告(但不会失败) - **工具** — 对无效的工具 ID 发出警告(但不会失败)
- **自定义内容** — 每个路径必须包含有效的 `module.yaml` 文件 - **自定义内容** — 每个路径必须包含有效的 `module.yaml` 文件
- **操作** — 必须是以下之一:`install`、`update`、`quick-update`、`compile-agents` - **操作** — 必须是以下之一:`install`、`update`、`quick-update`
无效值将: 无效值将:
1. 显示错误并退出(对于目录等关键选项) 1. 显示错误并退出(对于目录等关键选项)

View File

@ -1 +0,0 @@
type: skill

View File

@ -1,15 +0,0 @@
type: skill
module: core
capabilities:
- name: bmad-distillator
menu-code: DSTL
description: "Produces lossless LLM-optimized distillate from source documents. Use after producing large human presentable documents that will be consumed later by LLMs"
supports-headless: true
input: source documents
args: output, validate
output: single distillate or folder of distillates next to source input
config-vars-used: null
phase: anytime
before: []
after: []
is-required: false

View File

@ -1 +0,0 @@
type: skill

View File

@ -1 +0,0 @@
type: skill

View File

@ -1 +0,0 @@
type: skill

View File

@ -1 +0,0 @@
type: skill

View File

@ -1 +0,0 @@
type: skill

View File

@ -14,7 +14,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-extra');
const { YamlXmlBuilder } = require('../tools/cli/lib/yaml-xml-builder');
const { ManifestGenerator } = require('../tools/cli/installers/lib/core/manifest-generator'); const { ManifestGenerator } = require('../tools/cli/installers/lib/core/manifest-generator');
const { IdeManager } = require('../tools/cli/installers/lib/ide/manager'); const { IdeManager } = require('../tools/cli/installers/lib/ide/manager');
const { clearCache, loadPlatformCodes } = require('../tools/cli/installers/lib/ide/platform-codes'); const { clearCache, loadPlatformCodes } = require('../tools/cli/installers/lib/ide/platform-codes');
@ -79,7 +78,6 @@ async function createTestBmadFixture() {
'You are a test agent.', 'You are a test agent.',
].join('\n'), ].join('\n'),
); );
await fs.writeFile(path.join(skillDir, 'bmad-skill-manifest.yaml'), 'SKILL.md:\n type: skill\n');
await fs.writeFile(path.join(skillDir, 'workflow.md'), '# Test Workflow\nStep 1: Do the thing.\n'); await fs.writeFile(path.join(skillDir, 'workflow.md'), '# Test Workflow\nStep 1: Do the thing.\n');
return fixtureDir; return fixtureDir;
@ -100,17 +98,6 @@ async function createSkillCollisionFixture() {
].join('\n'), ].join('\n'),
); );
await fs.writeFile(
path.join(configDir, 'workflow-manifest.csv'),
[
'name,description,module,path,canonicalId',
'"help","Workflow help","core","_bmad/core/workflows/help/workflow.md","bmad-help"',
'',
].join('\n'),
);
await fs.writeFile(path.join(configDir, 'task-manifest.csv'), 'name,displayName,description,module,path,standalone,canonicalId\n');
await fs.writeFile(path.join(configDir, 'tool-manifest.csv'), 'name,displayName,description,module,path,standalone,canonicalId\n');
await fs.writeFile( await fs.writeFile(
path.join(configDir, 'skill-manifest.csv'), path.join(configDir, 'skill-manifest.csv'),
[ [
@ -149,77 +136,10 @@ async function runTests() {
const projectRoot = path.join(__dirname, '..'); const projectRoot = path.join(__dirname, '..');
// Test 1: Removed — old YAML→XML agent compilation no longer applies (agents now use SKILL.md format)
console.log('');
// ============================================================ // ============================================================
// Test 2: Customization Merging // Test 1: Windsurf Native Skills Install
// ============================================================ // ============================================================
console.log(`${colors.yellow}Test Suite 2: Customization Merging${colors.reset}\n`); console.log(`${colors.yellow}Test Suite 1: Windsurf Native Skills${colors.reset}\n`);
try {
const builder = new YamlXmlBuilder();
// Test deepMerge function
const base = {
agent: {
metadata: { name: 'John', title: 'PM' },
persona: { role: 'Product Manager', style: 'Analytical' },
},
};
const customize = {
agent: {
metadata: { name: 'Sarah' }, // Override name only
persona: { style: 'Concise' }, // Override style only
},
};
const merged = builder.deepMerge(base, customize);
assert(merged.agent.metadata.name === 'Sarah', 'Deep merge overrides customized name');
assert(merged.agent.metadata.title === 'PM', 'Deep merge preserves non-overridden title');
assert(merged.agent.persona.role === 'Product Manager', 'Deep merge preserves non-overridden role');
assert(merged.agent.persona.style === 'Concise', 'Deep merge overrides customized style');
} catch (error) {
assert(false, 'Customization merging works', error.message);
}
console.log('');
// ============================================================
// Test 3: Path Resolution
// ============================================================
console.log(`${colors.yellow}Test Suite 3: Path Variable Resolution${colors.reset}\n`);
try {
const builder = new YamlXmlBuilder();
// Test path resolution logic (if exposed)
// This would test {project-root}, {installed_path}, {config_source} resolution
const testPath = '{project-root}/bmad/bmm/config.yaml';
const expectedPattern = /\/bmad\/bmm\/config\.yaml$/;
assert(
true, // Placeholder - would test actual resolution
'Path variable resolution pattern matches expected format',
'Note: This test validates path resolution logic exists',
);
} catch (error) {
assert(false, 'Path resolution works', error.message);
}
console.log('');
// ============================================================
// Test 4: Windsurf Native Skills Install
// ============================================================
console.log(`${colors.yellow}Test Suite 4: Windsurf Native Skills${colors.reset}\n`);
try { try {
clearCache(); clearCache();
@ -1603,7 +1523,6 @@ async function runTests() {
// --- Skill at unusual path: core/custom-area/my-skill/ --- // --- Skill at unusual path: core/custom-area/my-skill/ ---
const skillDir29 = path.join(tempFixture29, 'core', 'custom-area', 'my-skill'); const skillDir29 = path.join(tempFixture29, 'core', 'custom-area', 'my-skill');
await fs.ensureDir(skillDir29); await fs.ensureDir(skillDir29);
await fs.writeFile(path.join(skillDir29, 'bmad-skill-manifest.yaml'), 'type: skill\n');
await fs.writeFile( await fs.writeFile(
path.join(skillDir29, 'SKILL.md'), path.join(skillDir29, 'SKILL.md'),
'---\nname: my-skill\ndescription: A skill at an unusual path\n---\n\nFollow the instructions in [workflow.md](workflow.md).\n', '---\nname: my-skill\ndescription: A skill at an unusual path\n---\n\nFollow the instructions in [workflow.md](workflow.md).\n',
@ -1619,10 +1538,9 @@ async function runTests() {
'---\nname: Regular Workflow\ndescription: A regular workflow not a skill\n---\n\nWorkflow body\n', '---\nname: Regular Workflow\ndescription: A regular workflow not a skill\n---\n\nWorkflow body\n',
); );
// --- Skill inside workflows/ dir: core/workflows/wf-skill/ (exercises findWorkflows skip logic) --- // --- Skill inside workflows/ dir: core/workflows/wf-skill/ ---
const wfSkillDir29 = path.join(tempFixture29, 'core', 'workflows', 'wf-skill'); const wfSkillDir29 = path.join(tempFixture29, 'core', 'workflows', 'wf-skill');
await fs.ensureDir(wfSkillDir29); await fs.ensureDir(wfSkillDir29);
await fs.writeFile(path.join(wfSkillDir29, 'bmad-skill-manifest.yaml'), 'type: skill\n');
await fs.writeFile( await fs.writeFile(
path.join(wfSkillDir29, 'SKILL.md'), path.join(wfSkillDir29, 'SKILL.md'),
'---\nname: wf-skill\ndescription: A skill inside workflows dir\n---\n\nFollow the instructions in [workflow.md](workflow.md).\n', '---\nname: wf-skill\ndescription: A skill inside workflows dir\n---\n\nFollow the instructions in [workflow.md](workflow.md).\n',
@ -1632,7 +1550,6 @@ async function runTests() {
// --- Skill inside tasks/ dir: core/tasks/task-skill/ --- // --- Skill inside tasks/ dir: core/tasks/task-skill/ ---
const taskSkillDir29 = path.join(tempFixture29, 'core', 'tasks', 'task-skill'); const taskSkillDir29 = path.join(tempFixture29, 'core', 'tasks', 'task-skill');
await fs.ensureDir(taskSkillDir29); await fs.ensureDir(taskSkillDir29);
await fs.writeFile(path.join(taskSkillDir29, 'bmad-skill-manifest.yaml'), 'type: skill\n');
await fs.writeFile( await fs.writeFile(
path.join(taskSkillDir29, 'SKILL.md'), path.join(taskSkillDir29, 'SKILL.md'),
'---\nname: task-skill\ndescription: A skill inside tasks dir\n---\n\nFollow the instructions in [workflow.md](workflow.md).\n', '---\nname: task-skill\ndescription: A skill inside tasks dir\n---\n\nFollow the instructions in [workflow.md](workflow.md).\n',
@ -1665,18 +1582,10 @@ async function runTests() {
'Skill path includes relative path from module root', 'Skill path includes relative path from module root',
); );
// Skill should NOT be in workflows
const inWorkflows29 = generator29.workflows.find((w) => w.name === 'my-skill');
assert(inWorkflows29 === undefined, 'Skill at unusual path does NOT appear in workflows[]');
// Skill in tasks/ dir should be in skills // Skill in tasks/ dir should be in skills
const taskSkillEntry29 = generator29.skills.find((s) => s.canonicalId === 'task-skill'); const taskSkillEntry29 = generator29.skills.find((s) => s.canonicalId === 'task-skill');
assert(taskSkillEntry29 !== undefined, 'Skill in tasks/ dir appears in skills[]'); assert(taskSkillEntry29 !== undefined, 'Skill in tasks/ dir appears in skills[]');
// Skill in tasks/ should NOT appear in tasks[]
const inTasks29 = generator29.tasks.find((t) => t.name === 'task-skill');
assert(inTasks29 === undefined, 'Skill in tasks/ dir does NOT appear in tasks[]');
// Native agent entrypoint should be installed as a verbatim skill and also // Native agent entrypoint should be installed as a verbatim skill and also
// remain visible to the agent manifest pipeline. // remain visible to the agent manifest pipeline.
const nativeAgentEntry29 = generator29.skills.find((s) => s.canonicalId === 'bmad-tea'); const nativeAgentEntry29 = generator29.skills.find((s) => s.canonicalId === 'bmad-tea');
@ -1688,23 +1597,17 @@ async function runTests() {
const nativeAgentManifest29 = generator29.agents.find((a) => a.name === 'bmad-tea'); const nativeAgentManifest29 = generator29.agents.find((a) => a.name === 'bmad-tea');
assert(nativeAgentManifest29 !== undefined, 'Native type:agent SKILL.md dir appears in agents[] for agent metadata'); assert(nativeAgentManifest29 !== undefined, 'Native type:agent SKILL.md dir appears in agents[] for agent metadata');
// Regular workflow should be in workflows, NOT in skills // Regular type:workflow should NOT appear in skills[]
const regularWf29 = generator29.workflows.find((w) => w.name === 'Regular Workflow');
assert(regularWf29 !== undefined, 'Regular type:workflow appears in workflows[]');
const regularInSkills29 = generator29.skills.find((s) => s.canonicalId === 'regular-wf'); const regularInSkills29 = generator29.skills.find((s) => s.canonicalId === 'regular-wf');
assert(regularInSkills29 === undefined, 'Regular type:workflow does NOT appear in skills[]'); assert(regularInSkills29 === undefined, 'Regular type:workflow does NOT appear in skills[]');
// Skill inside workflows/ should be in skills[], NOT in workflows[] (exercises findWorkflows skip at lines 311/322) // Skill inside workflows/ should be in skills[]
const wfSkill29 = generator29.skills.find((s) => s.canonicalId === 'wf-skill'); const wfSkill29 = generator29.skills.find((s) => s.canonicalId === 'wf-skill');
assert(wfSkill29 !== undefined, 'Skill in workflows/ dir appears in skills[]'); assert(wfSkill29 !== undefined, 'Skill in workflows/ dir appears in skills[]');
const wfSkillInWorkflows29 = generator29.workflows.find((w) => w.name === 'wf-skill');
assert(wfSkillInWorkflows29 === undefined, 'Skill in workflows/ dir does NOT appear in workflows[]');
// Test scanInstalledModules recognizes skill-only modules // Test scanInstalledModules recognizes skill-only modules
const skillOnlyModDir29 = path.join(tempFixture29, 'skill-only-mod'); const skillOnlyModDir29 = path.join(tempFixture29, 'skill-only-mod');
await fs.ensureDir(path.join(skillOnlyModDir29, 'deep', 'nested', 'my-skill')); await fs.ensureDir(path.join(skillOnlyModDir29, 'deep', 'nested', 'my-skill'));
await fs.writeFile(path.join(skillOnlyModDir29, 'deep', 'nested', 'my-skill', 'bmad-skill-manifest.yaml'), 'type: skill\n');
await fs.writeFile( await fs.writeFile(
path.join(skillOnlyModDir29, 'deep', 'nested', 'my-skill', 'SKILL.md'), path.join(skillOnlyModDir29, 'deep', 'nested', 'my-skill', 'SKILL.md'),
'---\nname: my-skill\ndescription: desc\n---\n\nFollow the instructions in [workflow.md](workflow.md).\n', '---\nname: my-skill\ndescription: desc\n---\n\nFollow the instructions in [workflow.md](workflow.md).\n',

View File

@ -18,7 +18,7 @@ module.exports = {
'Comma-separated list of tool/IDE IDs to configure (e.g., "claude-code,cursor"). Use "none" to skip tool configuration.', 'Comma-separated list of tool/IDE IDs to configure (e.g., "claude-code,cursor"). Use "none" to skip tool configuration.',
], ],
['--custom-content <paths>', 'Comma-separated list of paths to custom modules/agents/workflows'], ['--custom-content <paths>', 'Comma-separated list of paths to custom modules/agents/workflows'],
['--action <type>', 'Action type for existing installations: install, update, quick-update, or compile-agents'], ['--action <type>', 'Action type for existing installations: install, update, or quick-update'],
['--user-name <name>', 'Name for agents to use (default: system username)'], ['--user-name <name>', 'Name for agents to use (default: system username)'],
['--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)'],
@ -49,13 +49,6 @@ module.exports = {
process.exit(0); process.exit(0);
} }
// Handle compile agents separately
if (config.actionType === 'compile-agents') {
const result = await installer.compileAgents(config);
await prompts.log.info(`Recompiled ${result.agentCount} agents with customizations applied`);
process.exit(0);
}
// Regular install/update flow // Regular install/update flow
const result = await installer.install(config); const result = await installer.install(config);

View File

@ -6,7 +6,6 @@ const { ModuleManager } = require('../modules/manager');
const { IdeManager } = require('../ide/manager'); const { IdeManager } = require('../ide/manager');
const { FileOps } = require('../../../lib/file-ops'); const { FileOps } = require('../../../lib/file-ops');
const { Config } = require('../../../lib/config'); const { Config } = require('../../../lib/config');
const { XmlHandler } = require('../../../lib/xml-handler');
const { DependencyResolver } = require('./dependency-resolver'); const { DependencyResolver } = require('./dependency-resolver');
const { ConfigCollector } = require('./config-collector'); const { ConfigCollector } = require('./config-collector');
const { getProjectRoot, getSourcePath, getModulePath } = require('../../../lib/project-root'); const { getProjectRoot, getSourcePath, getModulePath } = require('../../../lib/project-root');
@ -25,7 +24,6 @@ class Installer {
this.ideManager = new IdeManager(); this.ideManager = new IdeManager();
this.fileOps = new FileOps(); this.fileOps = new FileOps();
this.config = new Config(); this.config = new Config();
this.xmlHandler = new XmlHandler();
this.dependencyResolver = new DependencyResolver(); this.dependencyResolver = new DependencyResolver();
this.configCollector = new ConfigCollector(); this.configCollector = new ConfigCollector();
this.ideConfigManager = new IdeConfigManager(); this.ideConfigManager = new IdeConfigManager();
@ -1126,11 +1124,9 @@ class Installer {
// Pre-register manifest files // Pre-register manifest files
const cfgDir = path.join(bmadDir, '_config'); const cfgDir = path.join(bmadDir, '_config');
this.installedFiles.add(path.join(cfgDir, 'manifest.yaml')); this.installedFiles.add(path.join(cfgDir, 'manifest.yaml'));
this.installedFiles.add(path.join(cfgDir, 'workflow-manifest.csv'));
this.installedFiles.add(path.join(cfgDir, 'agent-manifest.csv')); this.installedFiles.add(path.join(cfgDir, 'agent-manifest.csv'));
this.installedFiles.add(path.join(cfgDir, 'task-manifest.csv'));
// Generate CSV manifests for workflows, agents, tasks AND ALL FILES with hashes // Generate CSV manifests for agents, skills AND ALL FILES with hashes
// This must happen BEFORE mergeModuleHelpCatalogs because it depends on agent-manifest.csv // This must happen BEFORE mergeModuleHelpCatalogs because it depends on agent-manifest.csv
message('Generating manifests...'); message('Generating manifests...');
const manifestGen = new ManifestGenerator(); const manifestGen = new ManifestGenerator();
@ -2114,10 +2110,6 @@ class Installer {
}, },
); );
// Process agent files to build YAML agents and create customize templates
const modulePath = path.join(bmadDir, moduleName);
await this.processAgentFiles(modulePath, moduleName);
// Dependencies are already included in full module install // Dependencies are already included in full module install
} }
@ -2227,16 +2219,8 @@ class Installer {
const sourcePath = getModulePath('core'); const sourcePath = getModulePath('core');
const targetPath = path.join(bmadDir, 'core'); const targetPath = path.join(bmadDir, 'core');
// Copy core files (skip .agent.yaml files like modules do) // Copy core files
await this.copyCoreFiles(sourcePath, targetPath); await this.copyCoreFiles(sourcePath, targetPath);
// Compile agents using the same compiler as modules
const { ModuleManager } = require('../modules/manager');
const moduleManager = new ModuleManager();
await moduleManager.compileModuleAgents(sourcePath, targetPath, 'core', bmadDir, this);
// Process agent files to inject activation block
await this.processAgentFiles(targetPath, 'core');
} }
/** /**
@ -2254,16 +2238,6 @@ class Installer {
continue; continue;
} }
// Skip sidecar directories - they are handled separately during agent compilation
if (
path
.dirname(file)
.split('/')
.some((dir) => dir.toLowerCase().includes('sidecar'))
) {
continue;
}
// Skip module.yaml at root - it's only needed at install time // Skip module.yaml at root - it's only needed at install time
if (file === 'module.yaml') { if (file === 'module.yaml') {
continue; continue;
@ -2274,27 +2248,9 @@ class Installer {
continue; continue;
} }
// Skip .agent.yaml files - they will be compiled separately
if (file.endsWith('.agent.yaml')) {
continue;
}
const sourceFile = path.join(sourcePath, file); const sourceFile = path.join(sourcePath, file);
const targetFile = path.join(targetPath, file); const targetFile = path.join(targetPath, file);
// Check if this is an agent file
if (file.startsWith('agents/') && file.endsWith('.md')) {
// Read the file to check for localskip
const content = await fs.readFile(sourceFile, 'utf8');
// Check for localskip="true" in the agent tag
const agentMatch = content.match(/<agent[^>]*\slocalskip="true"[^>]*>/);
if (agentMatch) {
await prompts.log.message(` Skipping web-only agent: ${path.basename(file)}`);
continue; // Skip this agent
}
}
// Copy the file with placeholder replacement // Copy the file with placeholder replacement
await fs.ensureDir(path.dirname(targetFile)); await fs.ensureDir(path.dirname(targetFile));
await this.copyFileWithPlaceholderReplacement(sourceFile, targetFile); await this.copyFileWithPlaceholderReplacement(sourceFile, targetFile);
@ -2328,58 +2284,6 @@ class Installer {
return files; return files;
} }
/**
* Process agent files to build YAML agents and inject activation blocks
* @param {string} modulePath - Path to module in bmad/ installation
* @param {string} moduleName - Module name
*/
async processAgentFiles(modulePath, moduleName) {
const agentsPath = path.join(modulePath, 'agents');
// Check if agents directory exists
if (!(await fs.pathExists(agentsPath))) {
return; // No agents to process
}
// Determine project directory (parent of bmad/ directory)
const bmadDir = path.dirname(modulePath);
const cfgAgentsDir = path.join(bmadDir, '_config', 'agents');
// Ensure _config/agents directory exists
await fs.ensureDir(cfgAgentsDir);
// Get all agent files
const agentFiles = await fs.readdir(agentsPath);
for (const agentFile of agentFiles) {
// Skip .agent.yaml files - they should already be compiled by compileModuleAgents
if (agentFile.endsWith('.agent.yaml')) {
continue;
}
// Only process .md files (already compiled from YAML)
if (!agentFile.endsWith('.md')) {
continue;
}
const agentName = agentFile.replace('.md', '');
const mdPath = path.join(agentsPath, agentFile);
const customizePath = path.join(cfgAgentsDir, `${moduleName}-${agentName}.customize.yaml`);
// For .md files that are already compiled, we don't need to do much
// Just ensure the customize template exists
if (!(await fs.pathExists(customizePath))) {
const genericTemplatePath = getSourcePath('utility', 'agent-components', 'agent.customize.template.yaml');
if (await fs.pathExists(genericTemplatePath)) {
await this.copyFileWithPlaceholderReplacement(genericTemplatePath, customizePath);
if (process.env.BMAD_VERBOSE_INSTALL === 'true') {
await prompts.log.message(` Created customize: ${moduleName}-${agentName}.customize.yaml`);
}
}
}
}
}
/** /**
* Private: Update core * Private: Update core
*/ */
@ -2393,12 +2297,6 @@ class Installer {
} else { } else {
// Selective update - preserve user modifications // Selective update - preserve user modifications
await this.fileOps.syncDirectory(sourcePath, targetPath); await this.fileOps.syncDirectory(sourcePath, targetPath);
// Recompile agents (#1133)
const { ModuleManager } = require('../modules/manager');
const moduleManager = new ModuleManager();
await moduleManager.compileModuleAgents(sourcePath, targetPath, 'core', bmadDir, this);
await this.processAgentFiles(targetPath, 'core');
} }
} }
@ -2643,114 +2541,6 @@ class Installer {
} }
} }
/**
* Compile agents with customizations only
* @param {Object} config - Configuration with directory
* @returns {Object} Compilation result
*/
async compileAgents(config) {
// Using @clack prompts
const { ModuleManager } = require('../modules/manager');
const { getSourcePath } = require('../../../lib/project-root');
const spinner = await prompts.spinner();
spinner.start('Recompiling agents with customizations...');
try {
const projectDir = path.resolve(config.directory);
const { bmadDir } = await this.findBmadDir(projectDir);
// Check if bmad directory exists
if (!(await fs.pathExists(bmadDir))) {
spinner.stop('No BMAD installation found');
throw new Error(`BMAD not installed at ${bmadDir}. Use regular install for first-time setup.`);
}
// Detect existing installation
const existingInstall = await this.detector.detect(bmadDir);
const installedModules = existingInstall.modules.map((m) => m.id);
// Initialize module manager
const moduleManager = new ModuleManager();
moduleManager.setBmadFolderName(path.basename(bmadDir));
let totalAgentCount = 0;
// Get custom module sources from cache
const customModuleSources = new Map();
const cacheDir = path.join(bmadDir, '_config', 'custom');
if (await fs.pathExists(cacheDir)) {
const cachedModules = await fs.readdir(cacheDir, { withFileTypes: true });
for (const cachedModule of cachedModules) {
if (cachedModule.isDirectory()) {
const moduleId = cachedModule.name;
const cachedPath = path.join(cacheDir, moduleId);
const moduleYamlPath = path.join(cachedPath, 'module.yaml');
// Check if this is actually a custom module
if (await fs.pathExists(moduleYamlPath)) {
// Check if this is an external official module - skip cache for those
const isExternal = await this.moduleManager.isExternalModule(moduleId);
if (isExternal) {
// External modules are handled via cloneExternalModule, not from cache
continue;
}
customModuleSources.set(moduleId, cachedPath);
}
}
}
}
// Process each installed module
for (const moduleId of installedModules) {
spinner.message(`Recompiling agents in ${moduleId}...`);
// Get source path
let sourcePath;
if (moduleId === 'core') {
sourcePath = getSourcePath('core-skills');
} else {
// First check if it's in the custom cache
if (customModuleSources.has(moduleId)) {
sourcePath = customModuleSources.get(moduleId);
} else {
sourcePath = await moduleManager.findModuleSource(moduleId);
}
}
if (!sourcePath) {
await prompts.log.warn(`Source not found for module ${moduleId}, skipping...`);
continue;
}
const targetPath = path.join(bmadDir, moduleId);
// Compile agents for this module
await moduleManager.compileModuleAgents(sourcePath, targetPath, moduleId, bmadDir, this);
// Count agents (rough estimate based on files)
const agentsPath = path.join(targetPath, 'agents');
if (await fs.pathExists(agentsPath)) {
const agentFiles = await fs.readdir(agentsPath);
const agentCount = agentFiles.filter((f) => f.endsWith('.md')).length;
totalAgentCount += agentCount;
}
}
spinner.stop('Agent recompilation complete!');
return {
success: true,
agentCount: totalAgentCount,
modules: installedModules,
};
} catch (error) {
spinner.error('Agent recompilation failed');
throw error;
}
}
/** /**
* Private: Prompt for update action * Private: Prompt for update action
*/ */

View File

@ -16,15 +16,12 @@ const {
const packageJson = require('../../../../../package.json'); const packageJson = require('../../../../../package.json');
/** /**
* Generates manifest files for installed workflows, agents, and tasks * Generates manifest files for installed skills and agents
*/ */
class ManifestGenerator { class ManifestGenerator {
constructor() { constructor() {
this.workflows = [];
this.skills = []; this.skills = [];
this.agents = []; this.agents = [];
this.tasks = [];
this.tools = [];
this.modules = []; this.modules = [];
this.files = []; this.files = [];
this.selectedIdes = []; this.selectedIdes = [];
@ -50,29 +47,6 @@ class ManifestGenerator {
return getInstallToBmadShared(manifest, filename); return getInstallToBmadShared(manifest, filename);
} }
/**
* Native SKILL.md entrypoints can be packaged as either skills or agents.
* Both need verbatim installation for skill-format IDEs.
* @param {string|null} artifactType - Manifest type resolved for SKILL.md
* @returns {boolean} True when the directory should be installed verbatim
*/
isNativeSkillDirType(artifactType) {
return artifactType === 'skill' || artifactType === 'agent';
}
/**
* Check whether a loaded bmad-skill-manifest.yaml declares a native
* SKILL.md entrypoint, either as a single-entry manifest or a multi-entry map.
* @param {Object|null} manifest - Loaded manifest
* @returns {boolean} True when the manifest contains a native skill/agent entrypoint
*/
hasNativeSkillManifest(manifest) {
if (!manifest) return false;
if (manifest.__single) return this.isNativeSkillDirType(manifest.__single.type);
return Object.values(manifest).some((entry) => this.isNativeSkillDirType(entry?.type));
}
/** /**
* Clean text for CSV output by normalizing whitespace. * Clean text for CSV output by normalizing whitespace.
* Note: Quote escaping is handled by escapeCsv() at write time. * Note: Quote escaping is handled by escapeCsv() at write time.
@ -108,10 +82,6 @@ class ManifestGenerator {
this.modules = allModules; this.modules = allModules;
this.updatedModules = allModules; // Include ALL modules (including custom) for scanning this.updatedModules = allModules; // Include ALL modules (including custom) for scanning
// For CSV manifests, we need to include ALL modules that are installed
// preservedModules controls which modules stay as-is in the CSV (don't get rescanned)
// But all modules should be included in the final manifest
this.preservedModules = allModules; // Include ALL modules (including custom)
this.bmadDir = bmadDir; this.bmadDir = bmadDir;
this.bmadFolderName = path.basename(bmadDir); // Get the actual folder name (e.g., '_bmad' or 'bmad') this.bmadFolderName = path.basename(bmadDir); // Get the actual folder name (e.g., '_bmad' or 'bmad')
this.allInstalledFiles = installedFiles; this.allInstalledFiles = installedFiles;
@ -134,35 +104,20 @@ class ManifestGenerator {
// Collect skills first (populates skillClaimedDirs before legacy collectors run) // Collect skills first (populates skillClaimedDirs before legacy collectors run)
await this.collectSkills(); await this.collectSkills();
// Collect workflow data
await this.collectWorkflows(selectedModules);
// Collect agent data - use updatedModules which includes all installed modules // Collect agent data - use updatedModules which includes all installed modules
await this.collectAgents(this.updatedModules); await this.collectAgents(this.updatedModules);
// Collect task data
await this.collectTasks(this.updatedModules);
// Collect tool data
await this.collectTools(this.updatedModules);
// Write manifest files and collect their paths // Write manifest files and collect their paths
const manifestFiles = [ const manifestFiles = [
await this.writeMainManifest(cfgDir), await this.writeMainManifest(cfgDir),
await this.writeWorkflowManifest(cfgDir),
await this.writeSkillManifest(cfgDir), await this.writeSkillManifest(cfgDir),
await this.writeAgentManifest(cfgDir), await this.writeAgentManifest(cfgDir),
await this.writeTaskManifest(cfgDir),
await this.writeToolManifest(cfgDir),
await this.writeFilesManifest(cfgDir), await this.writeFilesManifest(cfgDir),
]; ];
return { return {
skills: this.skills.length, skills: this.skills.length,
workflows: this.workflows.length,
agents: this.agents.length, agents: this.agents.length,
tasks: this.tasks.length,
tools: this.tools.length,
files: this.files.length, files: this.files.length,
manifestFiles: manifestFiles, manifestFiles: manifestFiles,
}; };
@ -170,9 +125,9 @@ class ManifestGenerator {
/** /**
* Recursively walk a module directory tree, collecting native SKILL.md entrypoints. * Recursively walk a module directory tree, collecting native SKILL.md entrypoints.
* A native entrypoint directory is one that contains both a * A directory is discovered as a skill when it contains a SKILL.md file with
* bmad-skill-manifest.yaml with type: skill or type: agent AND a SKILL.md file * valid name/description frontmatter (name must match directory name).
* with name/description frontmatter. * Manifest YAML is loaded only when present for install_to_bmad and agent metadata.
* Populates this.skills[] and this.skillClaimedDirs (Set of absolute paths). * Populates this.skills[] and this.skillClaimedDirs (Set of absolute paths).
*/ */
async collectSkills() { async collectSkills() {
@ -193,77 +148,55 @@ class ManifestGenerator {
return; return;
} }
// Check this directory for skill manifest // SKILL.md with valid frontmatter is the primary discovery gate
const manifest = await this.loadSkillManifest(dir);
// Determine if this directory is a native SKILL.md entrypoint
const skillFile = 'SKILL.md'; const skillFile = 'SKILL.md';
const artifactType = this.getArtifactType(manifest, skillFile); const skillMdPath = path.join(dir, skillFile);
const dirName = path.basename(dir);
if (this.isNativeSkillDirType(artifactType)) { const skillMeta = await this.parseSkillMd(skillMdPath, dir, dirName, debug);
const skillMdPath = path.join(dir, 'SKILL.md');
const dirName = path.basename(dir);
// Validate and parse SKILL.md if (skillMeta) {
const skillMeta = await this.parseSkillMd(skillMdPath, dir, dirName, debug); // Load manifest when present (for install_to_bmad and agent metadata)
const manifest = await this.loadSkillManifest(dir);
const artifactType = this.getArtifactType(manifest, skillFile);
if (skillMeta) { // Build path relative from module root (points to SKILL.md — the permanent entrypoint)
// Build path relative from module root (points to SKILL.md — the permanent entrypoint) const relativePath = path.relative(modulePath, dir).split(path.sep).join('/');
const relativePath = path.relative(modulePath, dir).split(path.sep).join('/'); const installPath = relativePath
const installPath = relativePath ? `${this.bmadFolderName}/${moduleName}/${relativePath}/${skillFile}`
? `${this.bmadFolderName}/${moduleName}/${relativePath}/${skillFile}` : `${this.bmadFolderName}/${moduleName}/${skillFile}`;
: `${this.bmadFolderName}/${moduleName}/${skillFile}`;
// Native SKILL.md entrypoints derive canonicalId from directory name. // Native SKILL.md entrypoints derive canonicalId from directory name.
// Agent entrypoints may keep canonicalId metadata for compatibility, so // Agent entrypoints may keep canonicalId metadata for compatibility, so
// only warn for non-agent SKILL.md directories. // only warn for non-agent SKILL.md directories.
if (manifest && manifest.__single && manifest.__single.canonicalId && artifactType !== 'agent') { if (manifest && manifest.__single && manifest.__single.canonicalId && artifactType !== 'agent') {
console.warn( console.warn(
`Warning: Native entrypoint manifest at ${dir}/bmad-skill-manifest.yaml contains canonicalId — this field is ignored for SKILL.md directories (directory name is the canonical ID)`, `Warning: Native entrypoint manifest at ${dir}/bmad-skill-manifest.yaml contains canonicalId — this field is ignored for SKILL.md directories (directory name is the canonical ID)`,
); );
}
const canonicalId = dirName;
this.skills.push({
name: skillMeta.name,
description: this.cleanForCSV(skillMeta.description),
module: moduleName,
path: installPath,
canonicalId,
install_to_bmad: this.getInstallToBmad(manifest, skillFile),
});
// Add to files list
this.files.push({
type: 'skill',
name: skillMeta.name,
module: moduleName,
path: installPath,
});
this.skillClaimedDirs.add(dir);
if (debug) {
console.log(`[DEBUG] collectSkills: claimed skill "${skillMeta.name}" as ${canonicalId} at ${dir}`);
}
} }
} const canonicalId = dirName;
// Warn if manifest says this is a native entrypoint but the directory was not claimed this.skills.push({
if (manifest && !this.skillClaimedDirs.has(dir)) { name: skillMeta.name,
let hasNativeSkillType = false; description: this.cleanForCSV(skillMeta.description),
if (manifest.__single) { module: moduleName,
hasNativeSkillType = this.isNativeSkillDirType(manifest.__single.type); path: installPath,
} else { canonicalId,
for (const key of Object.keys(manifest)) { install_to_bmad: this.getInstallToBmad(manifest, skillFile),
if (this.isNativeSkillDirType(manifest[key]?.type)) { });
hasNativeSkillType = true;
break; // Add to files list
} this.files.push({
} type: 'skill',
} name: skillMeta.name,
if (hasNativeSkillType && debug) { module: moduleName,
console.log(`[DEBUG] collectSkills: dir has native SKILL.md manifest but failed validation: ${dir}`); path: installPath,
});
this.skillClaimedDirs.add(dir);
if (debug) {
console.log(`[DEBUG] collectSkills: claimed skill "${skillMeta.name}" as ${canonicalId} at ${dir}`);
} }
} }
@ -334,153 +267,6 @@ class ManifestGenerator {
} }
} }
/**
* Collect all workflows from core and selected modules
* Scans the INSTALLED bmad directory, not the source
*/
async collectWorkflows(selectedModules) {
this.workflows = [];
// Use updatedModules which already includes deduplicated 'core' + selectedModules
for (const moduleName of this.updatedModules) {
const modulePath = path.join(this.bmadDir, moduleName);
if (await fs.pathExists(modulePath)) {
const moduleWorkflows = await this.getWorkflowsFromPath(modulePath, moduleName);
this.workflows.push(...moduleWorkflows);
// Also scan tasks/ for type:skill entries (skills can live anywhere)
const tasksSkills = await this.getWorkflowsFromPath(modulePath, moduleName, 'tasks');
this.workflows.push(...tasksSkills);
}
}
}
/**
* Recursively find and parse workflow.md files
*/
async getWorkflowsFromPath(basePath, moduleName, subDir = 'workflows') {
const workflows = [];
const workflowsPath = path.join(basePath, subDir);
const debug = process.env.BMAD_DEBUG_MANIFEST === 'true';
if (debug) {
console.log(`[DEBUG] Scanning workflows in: ${workflowsPath}`);
}
if (!(await fs.pathExists(workflowsPath))) {
if (debug) {
console.log(`[DEBUG] Workflows path does not exist: ${workflowsPath}`);
}
return workflows;
}
// Recursively find workflow.md files
const findWorkflows = async (dir, relativePath = '') => {
// Skip directories already claimed as skills
if (this.skillClaimedDirs && this.skillClaimedDirs.has(dir)) return;
const entries = await fs.readdir(dir, { withFileTypes: true });
// Load skill manifest for this directory (if present)
const skillManifest = await this.loadSkillManifest(dir);
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
// Skip directories claimed by collectSkills
if (this.skillClaimedDirs && this.skillClaimedDirs.has(fullPath)) continue;
// Recurse into subdirectories
const newRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
await findWorkflows(fullPath, newRelativePath);
} else if (entry.name === 'workflow.md' || (entry.name.startsWith('workflow-') && entry.name.endsWith('.md'))) {
// Parse workflow file (both YAML and MD formats)
if (debug) {
console.log(`[DEBUG] Found workflow file: ${fullPath}`);
}
try {
// Read and normalize line endings (fix Windows CRLF issues)
const rawContent = await fs.readFile(fullPath, 'utf8');
const content = rawContent.replaceAll('\r\n', '\n').replaceAll('\r', '\n');
// Parse MD workflow with YAML frontmatter
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
if (!frontmatterMatch) {
if (debug) {
console.log(`[DEBUG] Skipped (no frontmatter): ${fullPath}`);
}
continue; // Skip MD files without frontmatter
}
const workflow = yaml.parse(frontmatterMatch[1]);
if (debug) {
console.log(`[DEBUG] Parsed: name="${workflow.name}", description=${workflow.description ? 'OK' : 'MISSING'}`);
}
// Skip template workflows (those with placeholder values)
if (workflow.name && workflow.name.includes('{') && workflow.name.includes('}')) {
if (debug) {
console.log(`[DEBUG] Skipped (template placeholder): ${workflow.name}`);
}
continue;
}
// Skip workflows marked as non-standalone (reference/example workflows)
if (workflow.standalone === false) {
if (debug) {
console.log(`[DEBUG] Skipped (standalone=false): ${workflow.name}`);
}
continue;
}
if (workflow.name && workflow.description) {
// Build relative path for installation
const installPath =
moduleName === 'core'
? `${this.bmadFolderName}/core/${subDir}/${relativePath}/${entry.name}`
: `${this.bmadFolderName}/${moduleName}/${subDir}/${relativePath}/${entry.name}`;
// Workflows with standalone: false are filtered out above
workflows.push({
name: workflow.name,
description: this.cleanForCSV(workflow.description),
module: moduleName,
path: installPath,
canonicalId: this.getCanonicalId(skillManifest, entry.name),
});
// Add to files list
this.files.push({
type: 'workflow',
name: workflow.name,
module: moduleName,
path: installPath,
});
if (debug) {
console.log(`[DEBUG] ✓ Added workflow: ${workflow.name} (${moduleName})`);
}
} else {
if (debug) {
console.log(`[DEBUG] Skipped (missing name or description): ${fullPath}`);
}
}
} catch (error) {
await prompts.log.warn(`Failed to parse workflow at ${fullPath}: ${error.message}`);
}
}
}
};
await findWorkflows(workflowsPath);
if (debug) {
console.log(`[DEBUG] Total workflows found in ${moduleName}: ${workflows.length}`);
}
return workflows;
}
/** /**
* Collect all agents from core and selected modules * Collect all agents from core and selected modules
* Scans the INSTALLED bmad directory, not the source * Scans the INSTALLED bmad directory, not the source
@ -515,7 +301,7 @@ class ManifestGenerator {
/** /**
* Get agents from a directory recursively * Get agents from a directory recursively
* Only includes compiled .md files (not .agent.yaml source files) * Only includes .md files with agent content
*/ */
async getAgentsFromDir(dirPath, moduleName, relativePath = '') { async getAgentsFromDir(dirPath, moduleName, relativePath = '') {
// Skip directories claimed by collectSkills // Skip directories claimed by collectSkills
@ -572,7 +358,7 @@ class ManifestGenerator {
const newRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name; const newRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
const subDirAgents = await this.getAgentsFromDir(fullPath, moduleName, newRelativePath); const subDirAgents = await this.getAgentsFromDir(fullPath, moduleName, newRelativePath);
agents.push(...subDirAgents); agents.push(...subDirAgents);
} else if (entry.name.endsWith('.md') && !entry.name.endsWith('.agent.yaml') && entry.name.toLowerCase() !== 'readme.md') { } else if (entry.name.endsWith('.md') && entry.name.toLowerCase() !== 'readme.md') {
const content = await fs.readFile(fullPath, 'utf8'); const content = await fs.readFile(fullPath, 'utf8');
// Skip files that don't contain <agent> tag (e.g., README files) // Skip files that don't contain <agent> tag (e.g., README files)
@ -634,212 +420,6 @@ class ManifestGenerator {
return agents; return agents;
} }
/**
* Collect all tasks from core and selected modules
* Scans the INSTALLED bmad directory, not the source
*/
async collectTasks(selectedModules) {
this.tasks = [];
// Use updatedModules which already includes deduplicated 'core' + selectedModules
for (const moduleName of this.updatedModules) {
const tasksPath = path.join(this.bmadDir, moduleName, 'tasks');
if (await fs.pathExists(tasksPath)) {
const moduleTasks = await this.getTasksFromDir(tasksPath, moduleName);
this.tasks.push(...moduleTasks);
}
}
}
/**
* Get tasks from a directory
*/
async getTasksFromDir(dirPath, moduleName) {
// Skip directories claimed by collectSkills
if (this.skillClaimedDirs && this.skillClaimedDirs.has(dirPath)) return [];
const tasks = [];
const files = await fs.readdir(dirPath);
// Load skill manifest for this directory (if present)
const skillManifest = await this.loadSkillManifest(dirPath);
for (const file of files) {
// Check for both .xml and .md files
if (file.endsWith('.xml') || file.endsWith('.md')) {
const filePath = path.join(dirPath, file);
const content = await fs.readFile(filePath, 'utf8');
// Skip internal/engine files (not user-facing tasks)
if (content.includes('internal="true"')) {
continue;
}
let name = file.replace(/\.(xml|md)$/, '');
let displayName = name;
let description = '';
let standalone = false;
if (file.endsWith('.md')) {
// Parse YAML frontmatter for .md tasks
const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
if (frontmatterMatch) {
try {
const frontmatter = yaml.parse(frontmatterMatch[1]);
name = frontmatter.name || name;
displayName = frontmatter.displayName || frontmatter.name || name;
description = this.cleanForCSV(frontmatter.description || '');
// Tasks are standalone by default unless explicitly false (internal=true is already filtered above)
standalone = frontmatter.standalone !== false && frontmatter.standalone !== 'false';
} catch {
// If YAML parsing fails, use defaults
standalone = true; // Default to standalone
}
} else {
standalone = true; // No frontmatter means standalone
}
} else {
// For .xml tasks, extract from tag attributes
const nameMatch = content.match(/name="([^"]+)"/);
displayName = nameMatch ? nameMatch[1] : name;
const descMatch = content.match(/description="([^"]+)"/);
const objMatch = content.match(/<objective>([^<]+)<\/objective>/);
description = this.cleanForCSV(descMatch ? descMatch[1] : objMatch ? objMatch[1].trim() : '');
const standaloneFalseMatch = content.match(/<task[^>]+standalone="false"/);
standalone = !standaloneFalseMatch;
}
// Build relative path for installation
const installPath =
moduleName === 'core' ? `${this.bmadFolderName}/core/tasks/${file}` : `${this.bmadFolderName}/${moduleName}/tasks/${file}`;
tasks.push({
name: name,
displayName: displayName,
description: description,
module: moduleName,
path: installPath,
standalone: standalone,
canonicalId: this.getCanonicalId(skillManifest, file),
});
// Add to files list
this.files.push({
type: 'task',
name: name,
module: moduleName,
path: installPath,
});
}
}
return tasks;
}
/**
* Collect all tools from core and selected modules
* Scans the INSTALLED bmad directory, not the source
*/
async collectTools(selectedModules) {
this.tools = [];
// Use updatedModules which already includes deduplicated 'core' + selectedModules
for (const moduleName of this.updatedModules) {
const toolsPath = path.join(this.bmadDir, moduleName, 'tools');
if (await fs.pathExists(toolsPath)) {
const moduleTools = await this.getToolsFromDir(toolsPath, moduleName);
this.tools.push(...moduleTools);
}
}
}
/**
* Get tools from a directory
*/
async getToolsFromDir(dirPath, moduleName) {
// Skip directories claimed by collectSkills
if (this.skillClaimedDirs && this.skillClaimedDirs.has(dirPath)) return [];
const tools = [];
const files = await fs.readdir(dirPath);
// Load skill manifest for this directory (if present)
const skillManifest = await this.loadSkillManifest(dirPath);
for (const file of files) {
// Check for both .xml and .md files
if (file.endsWith('.xml') || file.endsWith('.md')) {
const filePath = path.join(dirPath, file);
const content = await fs.readFile(filePath, 'utf8');
// Skip internal tools (same as tasks)
if (content.includes('internal="true"')) {
continue;
}
let name = file.replace(/\.(xml|md)$/, '');
let displayName = name;
let description = '';
let standalone = false;
if (file.endsWith('.md')) {
// Parse YAML frontmatter for .md tools
const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
if (frontmatterMatch) {
try {
const frontmatter = yaml.parse(frontmatterMatch[1]);
name = frontmatter.name || name;
displayName = frontmatter.displayName || frontmatter.name || name;
description = this.cleanForCSV(frontmatter.description || '');
// Tools are standalone by default unless explicitly false (internal=true is already filtered above)
standalone = frontmatter.standalone !== false && frontmatter.standalone !== 'false';
} catch {
// If YAML parsing fails, use defaults
standalone = true; // Default to standalone
}
} else {
standalone = true; // No frontmatter means standalone
}
} else {
// For .xml tools, extract from tag attributes
const nameMatch = content.match(/name="([^"]+)"/);
displayName = nameMatch ? nameMatch[1] : name;
const descMatch = content.match(/description="([^"]+)"/);
const objMatch = content.match(/<objective>([^<]+)<\/objective>/);
description = this.cleanForCSV(descMatch ? descMatch[1] : objMatch ? objMatch[1].trim() : '');
const standaloneFalseMatch = content.match(/<tool[^>]+standalone="false"/);
standalone = !standaloneFalseMatch;
}
// Build relative path for installation
const installPath =
moduleName === 'core' ? `${this.bmadFolderName}/core/tools/${file}` : `${this.bmadFolderName}/${moduleName}/tools/${file}`;
tools.push({
name: name,
displayName: displayName,
description: description,
module: moduleName,
path: installPath,
standalone: standalone,
canonicalId: this.getCanonicalId(skillManifest, file),
});
// Add to files list
this.files.push({
type: 'tool',
name: name,
module: moduleName,
path: installPath,
});
}
}
return tools;
}
/** /**
* Write main manifest as YAML with installation info only * Write main manifest as YAML with installation info only
* Fetches fresh version info for all modules * Fetches fresh version info for all modules
@ -925,131 +505,6 @@ class ManifestGenerator {
return manifestPath; return manifestPath;
} }
/**
* Read existing CSV and preserve rows for modules NOT being updated
* @param {string} csvPath - Path to existing CSV file
* @param {number} moduleColumnIndex - Which column contains the module name (0-indexed)
* @param {Array<string>} expectedColumns - Expected column names in order
* @param {Object} defaultValues - Default values for missing columns
* @returns {Array} Preserved CSV rows (without header), upgraded to match expected columns
*/
async getPreservedCsvRows(csvPath, moduleColumnIndex, expectedColumns, defaultValues = {}) {
if (!(await fs.pathExists(csvPath)) || this.preservedModules.length === 0) {
return [];
}
try {
const content = await fs.readFile(csvPath, 'utf8');
const lines = content.trim().split('\n');
if (lines.length < 2) {
return []; // No data rows
}
// Parse header to understand old schema
const header = lines[0];
const headerColumns = header.match(/(".*?"|[^",\s]+)(?=\s*,|\s*$)/g) || [];
const oldColumns = headerColumns.map((c) => c.replaceAll(/^"|"$/g, ''));
// Skip header row for data
const dataRows = lines.slice(1);
const preservedRows = [];
for (const row of dataRows) {
// Simple CSV parsing (handles quoted values)
const columns = row.match(/(".*?"|[^",\s]+)(?=\s*,|\s*$)/g) || [];
const cleanColumns = columns.map((c) => c.replaceAll(/^"|"$/g, ''));
const moduleValue = cleanColumns[moduleColumnIndex];
// Keep this row if it belongs to a preserved module
if (this.preservedModules.includes(moduleValue)) {
// Upgrade row to match expected schema
const upgradedRow = this.upgradeRowToSchema(cleanColumns, oldColumns, expectedColumns, defaultValues);
preservedRows.push(upgradedRow);
}
}
return preservedRows;
} catch (error) {
await prompts.log.warn(`Failed to read existing CSV ${csvPath}: ${error.message}`);
return [];
}
}
/**
* Upgrade a CSV row from old schema to new schema
* @param {Array<string>} rowValues - Values from old row
* @param {Array<string>} oldColumns - Old column names
* @param {Array<string>} newColumns - New column names
* @param {Object} defaultValues - Default values for missing columns
* @returns {string} Upgraded CSV row
*/
upgradeRowToSchema(rowValues, oldColumns, newColumns, defaultValues) {
const upgradedValues = [];
for (const newCol of newColumns) {
const oldIndex = oldColumns.indexOf(newCol);
if (oldIndex !== -1 && oldIndex < rowValues.length) {
// Column exists in old schema, use its value
upgradedValues.push(rowValues[oldIndex]);
} else if (defaultValues[newCol] === undefined) {
// Column missing, no default provided
upgradedValues.push('');
} else {
// Column missing, use default value
upgradedValues.push(defaultValues[newCol]);
}
}
// Properly quote values and join
return upgradedValues.map((v) => `"${v}"`).join(',');
}
/**
* Write workflow manifest CSV
* @returns {string} Path to the manifest file
*/
async writeWorkflowManifest(cfgDir) {
const csvPath = path.join(cfgDir, 'workflow-manifest.csv');
const escapeCsv = (value) => `"${String(value ?? '').replaceAll('"', '""')}"`;
// Create CSV header - standalone column removed, canonicalId added as optional column
let csv = 'name,description,module,path,canonicalId\n';
// Build workflows map from discovered workflows only
// Old entries are NOT preserved - the manifest reflects what actually exists on disk
const allWorkflows = new Map();
// Only add workflows that were actually discovered in this scan
for (const workflow of this.workflows) {
const key = `${workflow.module}:${workflow.name}`;
allWorkflows.set(key, {
name: workflow.name,
description: workflow.description,
module: workflow.module,
path: workflow.path,
canonicalId: workflow.canonicalId || '',
});
}
// Write all workflows
for (const [, value] of allWorkflows) {
const row = [
escapeCsv(value.name),
escapeCsv(value.description),
escapeCsv(value.module),
escapeCsv(value.path),
escapeCsv(value.canonicalId),
].join(',');
csv += row + '\n';
}
await fs.writeFile(csvPath, csv);
return csvPath;
}
/** /**
* Write skill manifest CSV * Write skill manifest CSV
* @returns {string} Path to the manifest file * @returns {string} Path to the manifest file
@ -1150,134 +605,6 @@ class ManifestGenerator {
return csvPath; return csvPath;
} }
/**
* Write task manifest CSV
* @returns {string} Path to the manifest file
*/
async writeTaskManifest(cfgDir) {
const csvPath = path.join(cfgDir, 'task-manifest.csv');
const escapeCsv = (value) => `"${String(value ?? '').replaceAll('"', '""')}"`;
// Read existing manifest to preserve entries
const existingEntries = new Map();
if (await fs.pathExists(csvPath)) {
const content = await fs.readFile(csvPath, 'utf8');
const records = csv.parse(content, {
columns: true,
skip_empty_lines: true,
});
for (const record of records) {
existingEntries.set(`${record.module}:${record.name}`, record);
}
}
// Create CSV header with standalone and canonicalId columns
let csvContent = 'name,displayName,description,module,path,standalone,canonicalId\n';
// Combine existing and new tasks
const allTasks = new Map();
// Add existing entries
for (const [key, value] of existingEntries) {
allTasks.set(key, value);
}
// Add/update new tasks
for (const task of this.tasks) {
const key = `${task.module}:${task.name}`;
allTasks.set(key, {
name: task.name,
displayName: task.displayName,
description: task.description,
module: task.module,
path: task.path,
standalone: task.standalone,
canonicalId: task.canonicalId || '',
});
}
// Write all tasks
for (const [, record] of allTasks) {
const row = [
escapeCsv(record.name),
escapeCsv(record.displayName),
escapeCsv(record.description),
escapeCsv(record.module),
escapeCsv(record.path),
escapeCsv(record.standalone),
escapeCsv(record.canonicalId),
].join(',');
csvContent += row + '\n';
}
await fs.writeFile(csvPath, csvContent);
return csvPath;
}
/**
* Write tool manifest CSV
* @returns {string} Path to the manifest file
*/
async writeToolManifest(cfgDir) {
const csvPath = path.join(cfgDir, 'tool-manifest.csv');
const escapeCsv = (value) => `"${String(value ?? '').replaceAll('"', '""')}"`;
// Read existing manifest to preserve entries
const existingEntries = new Map();
if (await fs.pathExists(csvPath)) {
const content = await fs.readFile(csvPath, 'utf8');
const records = csv.parse(content, {
columns: true,
skip_empty_lines: true,
});
for (const record of records) {
existingEntries.set(`${record.module}:${record.name}`, record);
}
}
// Create CSV header with standalone and canonicalId columns
let csvContent = 'name,displayName,description,module,path,standalone,canonicalId\n';
// Combine existing and new tools
const allTools = new Map();
// Add existing entries
for (const [key, value] of existingEntries) {
allTools.set(key, value);
}
// Add/update new tools
for (const tool of this.tools) {
const key = `${tool.module}:${tool.name}`;
allTools.set(key, {
name: tool.name,
displayName: tool.displayName,
description: tool.description,
module: tool.module,
path: tool.path,
standalone: tool.standalone,
canonicalId: tool.canonicalId || '',
});
}
// Write all tools
for (const [, record] of allTools) {
const row = [
escapeCsv(record.name),
escapeCsv(record.displayName),
escapeCsv(record.description),
escapeCsv(record.module),
escapeCsv(record.path),
escapeCsv(record.standalone),
escapeCsv(record.canonicalId),
].join(',');
csvContent += row + '\n';
}
await fs.writeFile(csvPath, csvContent);
return csvPath;
}
/** /**
* Write files manifest CSV * Write files manifest CSV
*/ */
@ -1377,22 +704,12 @@ class ManifestGenerator {
continue; continue;
} }
// Check if this looks like a module (has agents, workflows, or tasks directory) // Check if this looks like a module (has agents directory or skill manifests)
const modulePath = path.join(bmadDir, entry.name); const modulePath = path.join(bmadDir, entry.name);
const hasAgents = await fs.pathExists(path.join(modulePath, 'agents')); const hasAgents = await fs.pathExists(path.join(modulePath, 'agents'));
const hasWorkflows = await fs.pathExists(path.join(modulePath, 'workflows')); const hasSkills = await this._hasSkillMdRecursive(modulePath);
const hasTasks = await fs.pathExists(path.join(modulePath, 'tasks'));
const hasTools = await fs.pathExists(path.join(modulePath, 'tools'));
// Check for native-entrypoint-only modules: recursive scan for if (hasAgents || hasSkills) {
// bmad-skill-manifest.yaml with type: skill or type: agent
let hasSkills = false;
if (!hasAgents && !hasWorkflows && !hasTasks && !hasTools) {
hasSkills = await this._hasSkillManifestRecursive(modulePath);
}
// If it has any of these directories or skill manifests, it's likely a module
if (hasAgents || hasWorkflows || hasTasks || hasTools || hasSkills) {
modules.push(entry.name); modules.push(entry.name);
} }
} }
@ -1404,13 +721,12 @@ class ManifestGenerator {
} }
/** /**
* Recursively check if a directory tree contains a bmad-skill-manifest.yaml that * Recursively check if a directory tree contains a SKILL.md file.
* declares a native SKILL.md entrypoint (type: skill or type: agent).
* Skips directories starting with . or _. * Skips directories starting with . or _.
* @param {string} dir - Directory to search * @param {string} dir - Directory to search
* @returns {boolean} True if a skill manifest is found * @returns {boolean} True if a SKILL.md is found
*/ */
async _hasSkillManifestRecursive(dir) { async _hasSkillMdRecursive(dir) {
let entries; let entries;
try { try {
entries = await fs.readdir(dir, { withFileTypes: true }); entries = await fs.readdir(dir, { withFileTypes: true });
@ -1418,15 +734,14 @@ class ManifestGenerator {
return false; return false;
} }
// Check for manifest in this directory // Check for SKILL.md in this directory
const manifest = await this.loadSkillManifest(dir); if (entries.some((e) => !e.isDirectory() && e.name === 'SKILL.md')) return true;
if (this.hasNativeSkillManifest(manifest)) return true;
// Recurse into subdirectories // Recurse into subdirectories
for (const entry of entries) { for (const entry of entries) {
if (!entry.isDirectory()) continue; if (!entry.isDirectory()) continue;
if (entry.name.startsWith('.') || entry.name.startsWith('_')) continue; if (entry.name.startsWith('.') || entry.name.startsWith('_')) continue;
if (await this._hasSkillManifestRecursive(path.join(dir, entry.name))) return true; if (await this._hasSkillMdRecursive(path.join(dir, entry.name))) return true;
} }
return false; return false;

View File

@ -2,19 +2,11 @@ const path = require('node:path');
const fs = require('fs-extra'); const fs = require('fs-extra');
const yaml = require('yaml'); const yaml = require('yaml');
const prompts = require('../../../lib/prompts'); const prompts = require('../../../lib/prompts');
const { FileOps } = require('../../../lib/file-ops');
const { XmlHandler } = require('../../../lib/xml-handler');
/** /**
* Handler for custom content (custom.yaml) * Handler for custom content (custom.yaml)
* Installs custom agents and workflows without requiring a full module structure * Discovers custom agents and workflows in the project
*/ */
class CustomHandler { class CustomHandler {
constructor() {
this.fileOps = new FileOps();
this.xmlHandler = new XmlHandler();
}
/** /**
* Find all custom.yaml files in the project * Find all custom.yaml files in the project
* @param {string} projectRoot - Project root directory * @param {string} projectRoot - Project root directory
@ -115,244 +107,6 @@ class CustomHandler {
return null; return null;
} }
} }
/**
* Install custom content
* @param {string} customPath - Path to custom content directory
* @param {string} bmadDir - Target bmad directory
* @param {Object} config - Configuration from custom.yaml
* @param {Function} fileTrackingCallback - Optional callback to track installed files
* @returns {Object} Installation result
*/
async install(customPath, bmadDir, config, fileTrackingCallback = null) {
const results = {
agentsInstalled: 0,
workflowsInstalled: 0,
filesCopied: 0,
preserved: 0,
errors: [],
};
try {
// Create custom directories in bmad
const bmadCustomDir = path.join(bmadDir, 'custom');
const bmadAgentsDir = path.join(bmadCustomDir, 'agents');
const bmadWorkflowsDir = path.join(bmadCustomDir, 'workflows');
await fs.ensureDir(bmadCustomDir);
await fs.ensureDir(bmadAgentsDir);
await fs.ensureDir(bmadWorkflowsDir);
// Process agents - compile and copy agents
const agentsDir = path.join(customPath, 'agents');
if (await fs.pathExists(agentsDir)) {
await this.compileAndCopyAgents(agentsDir, bmadAgentsDir, bmadDir, config, fileTrackingCallback, results);
// Count agent files
const agentFiles = await this.findFilesRecursively(agentsDir, ['.agent.yaml', '.md']);
results.agentsInstalled = agentFiles.length;
}
// Process workflows - copy entire workflows directory structure
const workflowsDir = path.join(customPath, 'workflows');
if (await fs.pathExists(workflowsDir)) {
await this.copyDirectory(workflowsDir, bmadWorkflowsDir, results, fileTrackingCallback, config);
// Count workflow files
const workflowFiles = await this.findFilesRecursively(workflowsDir, ['.md']);
results.workflowsInstalled = workflowFiles.length;
}
// Process any additional files at root
const entries = await fs.readdir(customPath, { withFileTypes: true });
for (const entry of entries) {
if (entry.isFile() && entry.name !== 'custom.yaml' && !entry.name.startsWith('.') && !entry.name.endsWith('.md')) {
// Skip .md files at root as they're likely docs
const sourcePath = path.join(customPath, entry.name);
const targetPath = path.join(bmadCustomDir, entry.name);
try {
// Check if file already exists
if (await fs.pathExists(targetPath)) {
// File already exists, preserve it
results.preserved = (results.preserved || 0) + 1;
} else {
await fs.copy(sourcePath, targetPath);
results.filesCopied++;
if (fileTrackingCallback) {
fileTrackingCallback(targetPath);
}
}
} catch (error) {
results.errors.push(`Failed to copy file ${entry.name}: ${error.message}`);
}
}
}
} catch (error) {
results.errors.push(`Installation failed: ${error.message}`);
}
return results;
}
/**
* Find all files with specific extensions recursively
* @param {string} dir - Directory to search
* @param {Array} extensions - File extensions to match
* @returns {Array} List of matching files
*/
async findFilesRecursively(dir, extensions) {
const files = [];
async function search(currentDir) {
const entries = await fs.readdir(currentDir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(currentDir, entry.name);
if (entry.isDirectory()) {
await search(fullPath);
} else if (extensions.some((ext) => entry.name.endsWith(ext))) {
files.push(fullPath);
}
}
}
await search(dir);
return files;
}
/**
* Recursively copy a directory
* @param {string} sourceDir - Source directory
* @param {string} targetDir - Target directory
* @param {Object} results - Results object to update
* @param {Function} fileTrackingCallback - Optional callback
* @param {Object} config - Configuration for placeholder replacement
*/
async copyDirectory(sourceDir, targetDir, results, fileTrackingCallback, config) {
await fs.ensureDir(targetDir);
const entries = await fs.readdir(sourceDir, { withFileTypes: true });
for (const entry of entries) {
const sourcePath = path.join(sourceDir, entry.name);
const targetPath = path.join(targetDir, entry.name);
if (entry.isDirectory()) {
await this.copyDirectory(sourcePath, targetPath, results, fileTrackingCallback, config);
} else {
try {
// Check if file already exists
if (await fs.pathExists(targetPath)) {
// File already exists, preserve it
results.preserved = (results.preserved || 0) + 1;
} else {
// Copy with placeholder replacement for text files
const textExtensions = ['.md', '.yaml', '.yml', '.txt', '.json'];
if (textExtensions.some((ext) => entry.name.endsWith(ext))) {
// Read source content
let content = await fs.readFile(sourcePath, 'utf8');
// Replace placeholders
content = content.replaceAll('{user_name}', config.user_name || 'User');
content = content.replaceAll('{communication_language}', config.communication_language || 'English');
content = content.replaceAll('{output_folder}', config.output_folder || 'docs');
// Write to target
await fs.ensureDir(path.dirname(targetPath));
await fs.writeFile(targetPath, content, 'utf8');
} else {
// Copy binary files as-is
await fs.copy(sourcePath, targetPath);
}
results.filesCopied++;
if (entry.name.endsWith('.md')) {
results.workflowsInstalled++;
}
if (fileTrackingCallback) {
fileTrackingCallback(targetPath);
}
}
} catch (error) {
results.errors.push(`Failed to copy ${entry.name}: ${error.message}`);
}
}
}
}
/**
* Compile .agent.yaml files to .md format and handle sidecars
* @param {string} sourceAgentsPath - Source agents directory
* @param {string} targetAgentsPath - Target agents directory
* @param {string} bmadDir - BMAD installation directory
* @param {Object} config - Configuration for placeholder replacement
* @param {Function} fileTrackingCallback - Optional callback to track installed files
* @param {Object} results - Results object to update
*/
async compileAndCopyAgents(sourceAgentsPath, targetAgentsPath, bmadDir, config, fileTrackingCallback, results) {
// Get all .agent.yaml files recursively
const agentFiles = await this.findFilesRecursively(sourceAgentsPath, ['.agent.yaml']);
for (const agentFile of agentFiles) {
const relativePath = path.relative(sourceAgentsPath, agentFile).split(path.sep).join('/');
const targetDir = path.join(targetAgentsPath, path.dirname(relativePath));
await fs.ensureDir(targetDir);
const agentName = path.basename(agentFile, '.agent.yaml');
const targetMdPath = path.join(targetDir, `${agentName}.md`);
// Use the actual bmadDir if available (for when installing to temp dir)
const actualBmadDir = config._bmadDir || bmadDir;
const customizePath = path.join(actualBmadDir, '_config', 'agents', `custom-${agentName}.customize.yaml`);
// Read and compile the YAML
try {
const yamlContent = await fs.readFile(agentFile, 'utf8');
const { compileAgent } = require('../../../lib/agent/compiler');
// Create customize template if it doesn't exist
if (!(await fs.pathExists(customizePath))) {
const { getSourcePath } = require('../../../lib/project-root');
const genericTemplatePath = getSourcePath('utility', 'agent-components', 'agent.customize.template.yaml');
if (await fs.pathExists(genericTemplatePath)) {
let templateContent = await fs.readFile(genericTemplatePath, 'utf8');
await fs.writeFile(customizePath, templateContent, 'utf8');
// Only show customize creation in verbose mode
if (process.env.BMAD_VERBOSE_INSTALL === 'true') {
await prompts.log.message(' Created customize: custom-' + agentName + '.customize.yaml');
}
}
}
// Compile the agent
const { xml } = compileAgent(yamlContent, {}, agentName, relativePath, { config });
// Replace placeholders in the compiled content
let processedXml = xml;
processedXml = processedXml.replaceAll('{user_name}', config.user_name || 'User');
processedXml = processedXml.replaceAll('{communication_language}', config.communication_language || 'English');
processedXml = processedXml.replaceAll('{output_folder}', config.output_folder || 'docs');
// Write the compiled MD file
await fs.writeFile(targetMdPath, processedXml, 'utf8');
// Track the file
if (fileTrackingCallback) {
fileTrackingCallback(targetMdPath);
}
// Only show compilation details in verbose mode
if (process.env.BMAD_VERBOSE_INSTALL === 'true') {
await prompts.log.message(' Compiled agent: ' + agentName + ' -> ' + path.relative(targetAgentsPath, targetMdPath));
}
} catch (error) {
await prompts.log.warn(' Failed to compile agent ' + agentName + ': ' + error.message);
results.errors.push(`Failed to compile agent ${agentName}: ${error.message}`);
}
}
}
} }
module.exports = { CustomHandler }; module.exports = { CustomHandler };

View File

@ -1,6 +1,5 @@
const path = require('node:path'); const path = require('node:path');
const fs = require('fs-extra'); const fs = require('fs-extra');
const { XmlHandler } = require('../../../lib/xml-handler');
const prompts = require('../../../lib/prompts'); const prompts = require('../../../lib/prompts');
const { getSourcePath } = require('../../../lib/project-root'); const { getSourcePath } = require('../../../lib/project-root');
const { BMAD_FOLDER_NAME } = require('./shared/path-utils'); const { BMAD_FOLDER_NAME } = require('./shared/path-utils');
@ -18,7 +17,6 @@ class BaseIdeSetup {
this.rulesDir = null; // Override in subclasses this.rulesDir = null; // Override in subclasses
this.configFile = null; // Override in subclasses when detection is file-based this.configFile = null; // Override in subclasses when detection is file-based
this.detectionPaths = []; // Additional paths that indicate the IDE is configured this.detectionPaths = []; // Additional paths that indicate the IDE is configured
this.xmlHandler = new XmlHandler();
this.bmadFolderName = BMAD_FOLDER_NAME; // Default, can be overridden this.bmadFolderName = BMAD_FOLDER_NAME; // Default, can be overridden
} }
@ -30,15 +28,6 @@ class BaseIdeSetup {
this.bmadFolderName = bmadFolderName; this.bmadFolderName = bmadFolderName;
} }
/**
* Get the agent command activation header from the central template
* @returns {string} The activation header text
*/
async getAgentCommandHeader() {
const headerPath = getSourcePath('utility', 'agent-components', 'agent-command-header.md');
return await fs.readFile(headerPath, 'utf8');
}
/** /**
* Main setup method - must be implemented by subclasses * Main setup method - must be implemented by subclasses
* @param {string} projectDir - Project directory * @param {string} projectDir - Project directory
@ -511,11 +500,6 @@ class BaseIdeSetup {
// Replace placeholders // Replace placeholders
let processed = content; let processed = content;
// Inject activation block for agent files FIRST (before replacements)
if (metadata.name && content.includes('<agent')) {
processed = this.xmlHandler.injectActivationSimple(processed, metadata);
}
// Only replace {project-root} if a specific projectDir is provided // Only replace {project-root} if a specific projectDir is provided
// Otherwise leave the placeholder intact // Otherwise leave the placeholder intact
// Note: Don't add trailing slash - paths in source include leading slash // Note: Don't add trailing slash - paths in source include leading slash

View File

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

View File

@ -2,22 +2,18 @@ const path = require('node:path');
const fs = require('fs-extra'); const fs = require('fs-extra');
const yaml = require('yaml'); const yaml = require('yaml');
const prompts = require('../../../lib/prompts'); const prompts = require('../../../lib/prompts');
const { XmlHandler } = require('../../../lib/xml-handler');
const { getProjectRoot, getSourcePath, getModulePath } = require('../../../lib/project-root'); const { getProjectRoot, getSourcePath, getModulePath } = require('../../../lib/project-root');
const { filterCustomizationData } = require('../../../lib/agent/compiler');
const { ExternalModuleManager } = require('./external-manager'); const { ExternalModuleManager } = require('./external-manager');
const { BMAD_FOLDER_NAME } = require('../ide/shared/path-utils'); const { BMAD_FOLDER_NAME } = require('../ide/shared/path-utils');
/** /**
* Manages the installation, updating, and removal of BMAD modules. * Manages the installation, updating, and removal of BMAD modules.
* Handles module discovery, dependency resolution, configuration processing, * Handles module discovery, dependency resolution, and configuration processing.
* and agent file management including XML activation block injection.
* *
* @class ModuleManager * @class ModuleManager
* @requires fs-extra * @requires fs-extra
* @requires yaml * @requires yaml
* @requires prompts * @requires prompts
* @requires XmlHandler
* *
* @example * @example
* const manager = new ModuleManager(); * const manager = new ModuleManager();
@ -26,7 +22,6 @@ const { BMAD_FOLDER_NAME } = require('../ide/shared/path-utils');
*/ */
class ModuleManager { class ModuleManager {
constructor(options = {}) { constructor(options = {}) {
this.xmlHandler = new XmlHandler();
this.bmadFolderName = BMAD_FOLDER_NAME; // Default, can be overridden this.bmadFolderName = BMAD_FOLDER_NAME; // Default, can be overridden
this.customModulePaths = new Map(); // Initialize custom module paths this.customModulePaths = new Map(); // Initialize custom module paths
this.externalModuleManager = new ExternalModuleManager(); // For external official modules this.externalModuleManager = new ExternalModuleManager(); // For external official modules
@ -88,103 +83,6 @@ class ModuleManager {
} }
} }
/**
* Copy sidecar directory to _bmad/_memory location with update-safe handling
* @param {string} sourceSidecarPath - Source sidecar directory path
* @param {string} agentName - Name of the agent (for naming)
* @param {string} bmadMemoryPath - This should ALWAYS be _bmad/_memory
* @param {boolean} isUpdate - Whether this is an update (default: false)
* @param {string} bmadDir - BMAD installation directory
* @param {Object} installer - Installer instance for file tracking
*/
async copySidecarToMemory(sourceSidecarPath, agentName, bmadMemoryPath, isUpdate = false, bmadDir = null, installer = null) {
const crypto = require('node:crypto');
const sidecarTargetDir = path.join(bmadMemoryPath, `${agentName}-sidecar`);
// Ensure target directory exists
await fs.ensureDir(bmadMemoryPath);
await fs.ensureDir(sidecarTargetDir);
// Get existing files manifest for update checking
let existingFilesManifest = [];
if (isUpdate && installer) {
existingFilesManifest = await installer.readFilesManifest(bmadDir);
}
// Build map of existing sidecar files with their hashes
const existingSidecarFiles = new Map();
for (const fileEntry of existingFilesManifest) {
if (fileEntry.path && fileEntry.path.includes(`${agentName}-sidecar/`)) {
existingSidecarFiles.set(fileEntry.path, fileEntry.hash);
}
}
// Get all files in source sidecar
const sourceFiles = await this.getFileList(sourceSidecarPath);
for (const file of sourceFiles) {
const sourceFilePath = path.join(sourceSidecarPath, file);
const targetFilePath = path.join(sidecarTargetDir, file);
// Calculate current source file hash
const sourceHash = crypto
.createHash('sha256')
.update(await fs.readFile(sourceFilePath))
.digest('hex');
// Path relative to bmad directory
const relativeToBmad = path.join('_memory', `${agentName}-sidecar`, file);
if (isUpdate && (await fs.pathExists(targetFilePath))) {
// Calculate current target file hash
const currentTargetHash = crypto
.createHash('sha256')
.update(await fs.readFile(targetFilePath))
.digest('hex');
// Get the last known hash from files-manifest
const lastKnownHash = existingSidecarFiles.get(relativeToBmad);
if (lastKnownHash) {
// We have a record of this file
if (currentTargetHash === lastKnownHash) {
// File hasn't been modified by user, safe to update
await this.copyFileWithPlaceholderReplacement(sourceFilePath, targetFilePath, true);
if (process.env.BMAD_VERBOSE_INSTALL === 'true') {
await prompts.log.message(` Updated sidecar file: ${relativeToBmad}`);
}
} else {
// User has modified the file, preserve it
if (process.env.BMAD_VERBOSE_INSTALL === 'true') {
await prompts.log.message(` Preserving user-modified file: ${relativeToBmad}`);
}
}
} else {
// First time seeing this file in manifest, copy it
await this.copyFileWithPlaceholderReplacement(sourceFilePath, targetFilePath, true);
if (process.env.BMAD_VERBOSE_INSTALL === 'true') {
await prompts.log.message(` Added new sidecar file: ${relativeToBmad}`);
}
}
} else {
// New installation
await this.copyFileWithPlaceholderReplacement(sourceFilePath, targetFilePath, true);
if (process.env.BMAD_VERBOSE_INSTALL === 'true') {
await prompts.log.message(` Copied sidecar file: ${relativeToBmad}`);
}
}
// Track the file in the installer's file tracking system
if (installer && installer.installedFiles) {
installer.installedFiles.add(targetFilePath);
}
}
// Return list of files that were processed
const processedFiles = sourceFiles.map((file) => path.join('_memory', `${agentName}-sidecar`, file));
return processedFiles;
}
/** /**
* List all available modules (excluding core which is always installed) * List all available modules (excluding core which is always installed)
* bmm is the only built-in module, directly under src/bmm-skills * bmm is the only built-in module, directly under src/bmm-skills
@ -559,19 +457,9 @@ class ModuleManager {
await fs.remove(targetPath); await fs.remove(targetPath);
} }
// Vendor cross-module workflows BEFORE copying
// This reads source agent.yaml files and copies referenced workflows
await this.vendorCrossModuleWorkflows(sourcePath, targetPath, moduleName);
// Copy module files with filtering // Copy module files with filtering
await this.copyModuleWithFiltering(sourcePath, targetPath, fileTrackingCallback, options.moduleConfig); await this.copyModuleWithFiltering(sourcePath, targetPath, fileTrackingCallback, options.moduleConfig);
// Compile any .agent.yaml files to .md format
await this.compileModuleAgents(sourcePath, targetPath, moduleName, bmadDir, options.installer);
// Process agent files to inject activation block
await this.processAgentFiles(targetPath, moduleName);
// Create directories declared in module.yaml (unless explicitly skipped) // Create directories declared in module.yaml (unless explicitly skipped)
if (!options.skipModuleInstaller) { if (!options.skipModuleInstaller) {
await this.createModuleDirectories(moduleName, bmadDir, options); await this.createModuleDirectories(moduleName, bmadDir, options);
@ -624,10 +512,6 @@ class ModuleManager {
} else { } else {
// Selective update - preserve user modifications // Selective update - preserve user modifications
await this.syncModule(sourcePath, targetPath); await this.syncModule(sourcePath, targetPath);
// Recompile agents (#1133)
await this.compileModuleAgents(sourcePath, targetPath, moduleName, bmadDir, options.installer);
await this.processAgentFiles(targetPath, moduleName);
} }
return { return {
@ -718,9 +602,7 @@ class ModuleManager {
continue; continue;
} }
// Only skip sidecar directories - they are handled separately during agent compilation // Skip sidecar directories - these contain agent-specific assets not needed at install time
// But still allow other files in agent directories
const isInAgentDirectory = file.startsWith('agents/');
const isInSidecarDirectory = path const isInSidecarDirectory = path
.dirname(file) .dirname(file)
.split('/') .split('/')
@ -742,11 +624,6 @@ class ModuleManager {
continue; continue;
} }
// Skip .agent.yaml files - they will be compiled separately
if (file.endsWith('.agent.yaml')) {
continue;
}
const sourceFile = path.join(sourcePath, file); const sourceFile = path.join(sourcePath, file);
const targetFile = path.join(targetPath, file); const targetFile = path.join(targetPath, file);
@ -773,236 +650,6 @@ class ModuleManager {
} }
} }
/**
* Compile .agent.yaml files to .md format in modules
* @param {string} sourcePath - Source module path
* @param {string} targetPath - Target module path
* @param {string} moduleName - Module name
* @param {string} bmadDir - BMAD installation directory
* @param {Object} installer - Installer instance for file tracking
*/
async compileModuleAgents(sourcePath, targetPath, moduleName, bmadDir, installer = null) {
const sourceAgentsPath = path.join(sourcePath, 'agents');
const targetAgentsPath = path.join(targetPath, 'agents');
const cfgAgentsDir = path.join(bmadDir, '_config', 'agents');
// Check if agents directory exists in source
if (!(await fs.pathExists(sourceAgentsPath))) {
return; // No agents to compile
}
// Get all agent YAML files recursively
const agentFiles = await this.findAgentFiles(sourceAgentsPath);
for (const agentFile of agentFiles) {
if (!agentFile.endsWith('.agent.yaml')) continue;
const relativePath = path.relative(sourceAgentsPath, agentFile).split(path.sep).join('/');
const targetDir = path.join(targetAgentsPath, path.dirname(relativePath));
await fs.ensureDir(targetDir);
const agentName = path.basename(agentFile, '.agent.yaml');
const sourceYamlPath = agentFile;
const targetMdPath = path.join(targetDir, `${agentName}.md`);
const customizePath = path.join(cfgAgentsDir, `${moduleName}-${agentName}.customize.yaml`);
// Read and compile the YAML
try {
const yamlContent = await fs.readFile(sourceYamlPath, 'utf8');
const { compileAgent } = require('../../../lib/agent/compiler');
// Create customize template if it doesn't exist
if (!(await fs.pathExists(customizePath))) {
const { getSourcePath } = require('../../../lib/project-root');
const genericTemplatePath = getSourcePath('utility', 'agent-components', 'agent.customize.template.yaml');
if (await fs.pathExists(genericTemplatePath)) {
await this.copyFileWithPlaceholderReplacement(genericTemplatePath, customizePath);
// Only show customize creation in verbose mode
if (process.env.BMAD_VERBOSE_INSTALL === 'true') {
await prompts.log.message(` Created customize: ${moduleName}-${agentName}.customize.yaml`);
}
// Store original hash for modification detection
const crypto = require('node:crypto');
const customizeContent = await fs.readFile(customizePath, 'utf8');
const originalHash = crypto.createHash('sha256').update(customizeContent).digest('hex');
// Store in main manifest
const manifestPath = path.join(bmadDir, '_config', 'manifest.yaml');
let manifestData = {};
if (await fs.pathExists(manifestPath)) {
const manifestContent = await fs.readFile(manifestPath, 'utf8');
const yaml = require('yaml');
manifestData = yaml.parse(manifestContent);
}
if (!manifestData.agentCustomizations) {
manifestData.agentCustomizations = {};
}
manifestData.agentCustomizations[path.relative(bmadDir, customizePath)] = originalHash;
// Write back to manifest
const yaml = require('yaml');
// Clean the manifest data to remove any non-serializable values
const cleanManifestData = structuredClone(manifestData);
const updatedContent = yaml.stringify(cleanManifestData, {
indent: 2,
lineWidth: 0,
});
await fs.writeFile(manifestPath, updatedContent, 'utf8');
}
}
// Check for customizations and build answers object
let customizedFields = [];
let answers = {};
if (await fs.pathExists(customizePath)) {
const customizeContent = await fs.readFile(customizePath, 'utf8');
const customizeData = yaml.parse(customizeContent);
customizedFields = customizeData.customized_fields || [];
// Build answers object from customizations
if (customizeData.persona) {
answers.persona = customizeData.persona;
}
if (customizeData.agent?.metadata) {
const filteredMetadata = filterCustomizationData(customizeData.agent.metadata);
if (Object.keys(filteredMetadata).length > 0) {
Object.assign(answers, { metadata: filteredMetadata });
}
}
if (customizeData.critical_actions && customizeData.critical_actions.length > 0) {
answers.critical_actions = customizeData.critical_actions;
}
if (customizeData.memories && customizeData.memories.length > 0) {
answers.memories = customizeData.memories;
}
if (customizeData.menu && customizeData.menu.length > 0) {
answers.menu = customizeData.menu;
}
if (customizeData.prompts && customizeData.prompts.length > 0) {
answers.prompts = customizeData.prompts;
}
}
// Check if agent has sidecar
let hasSidecar = false;
try {
const agentYaml = yaml.parse(yamlContent);
hasSidecar = agentYaml?.agent?.metadata?.hasSidecar === true;
} catch {
// Continue without sidecar processing
}
// Compile with customizations if any
const { xml } = await compileAgent(yamlContent, answers, agentName, relativePath, { config: this.coreConfig || {} });
// Write the compiled agent
await fs.writeFile(targetMdPath, xml, 'utf8');
// Handle sidecar copying if present
if (hasSidecar) {
// Get the agent's directory to look for sidecar
const agentDir = path.dirname(agentFile);
const sidecarDirName = `${agentName}-sidecar`;
const sourceSidecarPath = path.join(agentDir, sidecarDirName);
// Check if sidecar directory exists
if (await fs.pathExists(sourceSidecarPath)) {
// Memory is always in _bmad/_memory
const bmadMemoryPath = path.join(bmadDir, '_memory');
// Determine if this is an update (by checking if agent already exists)
const isUpdate = await fs.pathExists(targetMdPath);
// Copy sidecar to memory location with update-safe handling
const copiedFiles = await this.copySidecarToMemory(sourceSidecarPath, agentName, bmadMemoryPath, isUpdate, bmadDir, installer);
if (process.env.BMAD_VERBOSE_INSTALL === 'true' && copiedFiles.length > 0) {
await prompts.log.message(` Sidecar files processed: ${copiedFiles.length} files`);
}
} else if (process.env.BMAD_VERBOSE_INSTALL === 'true') {
await prompts.log.warn(` Agent marked as having sidecar but ${sidecarDirName} directory not found`);
}
}
// Copy any non-sidecar files from agent directory (e.g., foo.md)
const agentDir = path.dirname(agentFile);
const agentEntries = await fs.readdir(agentDir, { withFileTypes: true });
for (const entry of agentEntries) {
if (entry.isFile() && !entry.name.endsWith('.agent.yaml') && !entry.name.endsWith('.md')) {
// Copy additional files (like foo.md) to the agent target directory
const sourceFile = path.join(agentDir, entry.name);
const targetFile = path.join(targetDir, entry.name);
await this.copyFileWithPlaceholderReplacement(sourceFile, targetFile);
}
}
// Only show compilation details in verbose mode
if (process.env.BMAD_VERBOSE_INSTALL === 'true') {
await prompts.log.message(
` Compiled agent: ${agentName} -> ${path.relative(targetPath, targetMdPath)}${hasSidecar ? ' (with sidecar)' : ''}`,
);
}
} catch (error) {
await prompts.log.warn(` Failed to compile agent ${agentName}: ${error.message}`);
}
}
}
/**
* Find all .agent.yaml files recursively in a directory
* @param {string} dir - Directory to search
* @returns {Array} List of .agent.yaml file paths
*/
async findAgentFiles(dir) {
const agentFiles = [];
async function searchDirectory(searchDir) {
const entries = await fs.readdir(searchDir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(searchDir, entry.name);
if (entry.isFile() && entry.name.endsWith('.agent.yaml')) {
agentFiles.push(fullPath);
} else if (entry.isDirectory()) {
await searchDirectory(fullPath);
}
}
}
await searchDirectory(dir);
return agentFiles;
}
/**
* Process agent files to inject activation block
* @param {string} modulePath - Path to installed module
* @param {string} moduleName - Module name
*/
async processAgentFiles(modulePath, moduleName) {
// const agentsPath = path.join(modulePath, 'agents');
// // Check if agents directory exists
// if (!(await fs.pathExists(agentsPath))) {
// return; // No agents to process
// }
// // Get all agent MD files recursively
// const agentFiles = await this.findAgentMdFiles(agentsPath);
// for (const agentFile of agentFiles) {
// if (!agentFile.endsWith('.md')) continue;
// let content = await fs.readFile(agentFile, 'utf8');
// // Check if content has agent XML and no activation block
// if (content.includes('<agent') && !content.includes('<activation')) {
// // Inject the activation block using XML handler
// content = this.xmlHandler.injectActivationSimple(content);
// await fs.writeFile(agentFile, content, 'utf8');
// }
// }
}
/** /**
* Find all .md agent files recursively in a directory * Find all .md agent files recursively in a directory
* @param {string} dir - Directory to search * @param {string} dir - Directory to search
@ -1029,101 +676,6 @@ class ModuleManager {
return agentFiles; return agentFiles;
} }
/**
* Vendor cross-module workflows referenced in agent files
* Scans SOURCE agent.yaml files for workflow-install and copies workflows to destination
* @param {string} sourcePath - Source module path
* @param {string} targetPath - Target module path (destination)
* @param {string} moduleName - Module name being installed
*/
async vendorCrossModuleWorkflows(sourcePath, targetPath, moduleName) {
const sourceAgentsPath = path.join(sourcePath, 'agents');
// Check if source agents directory exists
if (!(await fs.pathExists(sourceAgentsPath))) {
return; // No agents to process
}
// Get all agent YAML files from source
const agentFiles = await fs.readdir(sourceAgentsPath);
const yamlFiles = agentFiles.filter((f) => f.endsWith('.agent.yaml') || f.endsWith('.yaml'));
if (yamlFiles.length === 0) {
return; // No YAML agent files
}
let workflowsVendored = false;
for (const agentFile of yamlFiles) {
const agentPath = path.join(sourceAgentsPath, agentFile);
const agentYaml = yaml.parse(await fs.readFile(agentPath, 'utf8'));
// Check if agent has menu items with workflow-install
const menuItems = agentYaml?.agent?.menu || [];
const workflowInstallItems = menuItems.filter((item) => item['workflow-install']);
if (workflowInstallItems.length === 0) {
continue; // No workflow-install in this agent
}
if (!workflowsVendored) {
await prompts.log.info(`\n Vendoring cross-module workflows for ${moduleName}...`);
workflowsVendored = true;
}
await prompts.log.message(` Processing: ${agentFile}`);
for (const item of workflowInstallItems) {
const sourceWorkflowPath = item.exec; // Where to copy FROM
const installWorkflowPath = item['workflow-install']; // Where to copy TO
// Parse SOURCE workflow path
// Example: {project-root}/_bmad/bmm/workflows/4-implementation/bmad-create-story/workflow.md
const sourceMatch = sourceWorkflowPath.match(/\{project-root\}\/(?:_bmad)\/([^/]+)\/workflows\/(.+)/);
if (!sourceMatch) {
await prompts.log.warn(` Could not parse workflow path: ${sourceWorkflowPath}`);
continue;
}
const [, sourceModule, sourceWorkflowSubPath] = sourceMatch;
// Parse INSTALL workflow path
// Example: {project-root}/_bmad/bmgd/workflows/4-production/create-story/workflow.md
const installMatch = installWorkflowPath.match(/\{project-root\}\/(?:_bmad)\/([^/]+)\/workflows\/(.+)/);
if (!installMatch) {
await prompts.log.warn(` Could not parse workflow-install path: ${installWorkflowPath}`);
continue;
}
const installWorkflowSubPath = installMatch[2];
const sourceModulePath = getModulePath(sourceModule);
const actualSourceWorkflowPath = path.join(sourceModulePath, 'workflows', sourceWorkflowSubPath.replace(/\/workflow\.md$/, ''));
const actualDestWorkflowPath = path.join(targetPath, 'workflows', installWorkflowSubPath.replace(/\/workflow\.md$/, ''));
// Check if source workflow exists
if (!(await fs.pathExists(actualSourceWorkflowPath))) {
await prompts.log.warn(` Source workflow not found: ${actualSourceWorkflowPath}`);
continue;
}
// Copy the entire workflow folder
await prompts.log.message(
` Vendoring: ${sourceModule}/workflows/${sourceWorkflowSubPath.replace(/\/workflow\.md$/, '')}${moduleName}/workflows/${installWorkflowSubPath.replace(/\/workflow\.md$/, '')}`,
);
await fs.ensureDir(path.dirname(actualDestWorkflowPath));
// Copy the workflow directory recursively with placeholder replacement
await this.copyDirectoryWithPlaceholderReplacement(actualSourceWorkflowPath, actualDestWorkflowPath);
}
}
if (workflowsVendored) {
await prompts.log.success(` Workflow vendoring complete\n`);
}
}
/** /**
* Create directories declared in module.yaml's `directories` key * Create directories declared in module.yaml's `directories` key
* This replaces the security-risky module installer pattern with declarative config * This replaces the security-risky module installer pattern with declarative config

View File

@ -1,165 +0,0 @@
const fs = require('fs-extra');
const path = require('node:path');
const { getSourcePath } = require('./project-root');
/**
* Builds activation blocks from fragments based on agent profile
*/
class ActivationBuilder {
constructor() {
this.agentComponents = getSourcePath('utility', 'agent-components');
this.fragmentCache = new Map();
}
/**
* Load a fragment file
* @param {string} fragmentName - Name of fragment file (e.g., 'activation-init.txt')
* @returns {string} Fragment content
*/
async loadFragment(fragmentName) {
// Check cache first
if (this.fragmentCache.has(fragmentName)) {
return this.fragmentCache.get(fragmentName);
}
const fragmentPath = path.join(this.agentComponents, fragmentName);
if (!(await fs.pathExists(fragmentPath))) {
throw new Error(`Fragment not found: ${fragmentName}`);
}
const content = await fs.readFile(fragmentPath, 'utf8');
this.fragmentCache.set(fragmentName, content);
return content;
}
/**
* Build complete activation block based on agent profile
* @param {Object} profile - Agent profile from AgentAnalyzer
* @param {Object} metadata - Agent metadata (module, name, etc.)
* @param {Array} agentSpecificActions - Optional agent-specific critical actions
* @param {boolean} forWebBundle - Whether this is for a web bundle
* @returns {string} Complete activation block XML
*/
async buildActivation(profile, metadata = {}, agentSpecificActions = [], forWebBundle = false) {
let activation = '<activation critical="MANDATORY">\n';
// 1. Build sequential steps (use web-specific steps for web bundles)
const steps = await this.buildSteps(metadata, agentSpecificActions, forWebBundle);
activation += this.indent(steps, 2) + '\n';
// 2. Build menu handlers section with dynamic handlers
const menuHandlers = await this.loadFragment('menu-handlers.txt');
// Build handlers (load only needed handlers)
const handlers = await this.buildHandlers(profile);
// Remove the extract line from the final output - it's just build metadata
// The extract list tells us which attributes to look for during processing
// but shouldn't appear in the final agent file
const processedHandlers = menuHandlers
.replace('<extract>{DYNAMIC_EXTRACT_LIST}</extract>\n', '') // Remove the entire extract line
.replace('{DYNAMIC_HANDLERS}', handlers);
activation += '\n' + this.indent(processedHandlers, 2) + '\n';
const rules = await this.loadFragment('activation-rules.txt');
activation += this.indent(rules, 2) + '\n';
activation += '</activation>';
return activation;
}
/**
* Build handlers section based on profile
* @param {Object} profile - Agent profile
* @returns {string} Handlers XML
*/
async buildHandlers(profile) {
const handlerFragments = [];
for (const attrType of profile.usedAttributes) {
const fragmentName = `handler-${attrType}.txt`;
try {
const handler = await this.loadFragment(fragmentName);
handlerFragments.push(handler);
} catch {
console.warn(`Warning: Handler fragment not found: ${fragmentName}`);
}
}
return handlerFragments.join('\n');
}
/**
* Build sequential activation steps
* @param {Object} metadata - Agent metadata
* @param {Array} agentSpecificActions - Optional agent-specific actions
* @param {boolean} forWebBundle - Whether this is for a web bundle
* @returns {string} Steps XML
*/
async buildSteps(metadata = {}, agentSpecificActions = [], forWebBundle = false) {
const stepsTemplate = await this.loadFragment('activation-steps.txt');
// Extract basename from agent ID (e.g., "bmad/bmm/agents/pm.md" → "pm")
const agentBasename = metadata.id ? metadata.id.split('/').pop().replace('.md', '') : metadata.name || 'agent';
// Build agent-specific steps
let agentStepsXml = '';
let currentStepNum = 4; // Steps 1-3 are standard
if (agentSpecificActions && agentSpecificActions.length > 0) {
agentStepsXml = agentSpecificActions
.map((action) => {
const step = `<step n="${currentStepNum}">${action}</step>`;
currentStepNum++;
return step;
})
.join('\n');
}
// Calculate final step numbers
const menuStep = currentStepNum;
const helpStep = currentStepNum + 1;
const haltStep = currentStepNum + 2;
const inputStep = currentStepNum + 3;
const executeStep = currentStepNum + 4;
// Replace placeholders
const processed = stepsTemplate
.replace('{agent-file-basename}', agentBasename)
.replace('{{module}}', metadata.module || 'core') // Fixed to use {{module}}
.replace('{AGENT_SPECIFIC_STEPS}', agentStepsXml)
.replace('{MENU_STEP}', menuStep.toString())
.replace('{HELP_STEP}', helpStep.toString())
.replace('{HALT_STEP}', haltStep.toString())
.replace('{INPUT_STEP}', inputStep.toString())
.replace('{EXECUTE_STEP}', executeStep.toString());
return processed;
}
/**
* Indent XML content
* @param {string} content - Content to indent
* @param {number} spaces - Number of spaces to indent
* @returns {string} Indented content
*/
indent(content, spaces) {
const indentation = ' '.repeat(spaces);
return content
.split('\n')
.map((line) => (line ? indentation + line : line))
.join('\n');
}
/**
* Clear fragment cache (useful for testing or hot reload)
*/
clearCache() {
this.fragmentCache.clear();
}
}
module.exports = { ActivationBuilder };

View File

@ -1,97 +0,0 @@
const yaml = require('yaml');
const fs = require('fs-extra');
/**
* Analyzes agent YAML files to detect which handlers are needed
*/
class AgentAnalyzer {
/**
* Analyze an agent YAML structure to determine which handlers it needs
* @param {Object} agentYaml - Parsed agent YAML object
* @returns {Object} Profile of needed handlers
*/
analyzeAgentObject(agentYaml) {
const profile = {
usedAttributes: new Set(),
hasPrompts: false,
menuItems: [],
};
// Check if agent has prompts section
if (agentYaml.agent && agentYaml.agent.prompts) {
profile.hasPrompts = true;
}
// Analyze menu items (support both 'menu' and legacy 'commands')
const menuItems = agentYaml.agent?.menu || agentYaml.agent?.commands || [];
for (const item of menuItems) {
// Track the menu item
profile.menuItems.push(item);
// Check for multi format items
if (item.multi && item.triggers) {
profile.usedAttributes.add('multi');
// Also check attributes in nested handlers
for (const triggerGroup of item.triggers) {
for (const [triggerName, execArray] of Object.entries(triggerGroup)) {
if (Array.isArray(execArray)) {
for (const exec of execArray) {
if (exec.route) {
profile.usedAttributes.add('exec');
}
if (exec.action) profile.usedAttributes.add('action');
if (exec.type && ['exec', 'action'].includes(exec.type)) {
profile.usedAttributes.add(exec.type);
}
}
}
}
}
} else {
// Check for each possible attribute in legacy items
if (item.exec) {
profile.usedAttributes.add('exec');
}
if (item.tmpl) {
profile.usedAttributes.add('tmpl');
}
if (item.data) {
profile.usedAttributes.add('data');
}
if (item.action) {
profile.usedAttributes.add('action');
}
}
}
// Convert Set to Array for easier use
profile.usedAttributes = [...profile.usedAttributes];
return profile;
}
/**
* Analyze an agent YAML file
* @param {string} filePath - Path to agent YAML file
* @returns {Object} Profile of needed handlers
*/
async analyzeAgentFile(filePath) {
const content = await fs.readFile(filePath, 'utf8');
const agentYaml = yaml.parse(content);
return this.analyzeAgentObject(agentYaml);
}
/**
* Check if an agent needs a specific handler
* @param {Object} profile - Agent profile from analyze
* @param {string} handlerType - Handler type to check
* @returns {boolean} True if handler is needed
*/
needsHandler(profile, handlerType) {
return profile.usedAttributes.includes(handlerType);
}
}
module.exports = { AgentAnalyzer };

View File

@ -1,194 +0,0 @@
const path = require('node:path');
const fs = require('fs-extra');
const { escapeXml } = require('../../lib/xml-utils');
const AgentPartyGenerator = {
/**
* Generate agent-manifest.csv content
* @param {Array} agentDetails - Array of agent details
* @param {Object} options - Generation options
* @returns {string} XML content
*/
generateAgentParty(agentDetails, options = {}) {
const { forWeb = false } = options;
// Group agents by module
const agentsByModule = {
bmm: [],
cis: [],
core: [],
custom: [],
};
for (const agent of agentDetails) {
const moduleKey = agentsByModule[agent.module] ? agent.module : 'custom';
agentsByModule[moduleKey].push(agent);
}
// Build XML content
let xmlContent = `<!-- Powered by BMAD-CORE™ -->
<!-- Agent Manifest - Generated during BMAD ${forWeb ? 'bundling' : 'installation'} -->
<!-- This file contains a summary of all ${forWeb ? 'bundled' : 'installed'} agents for quick reference -->
<manifest id="bmad/_config/agent-manifest.csv" version="1.0" generated="${new Date().toISOString()}">
<description>
Complete roster of ${forWeb ? 'bundled' : 'installed'} BMAD agents with summarized personas for efficient multi-agent orchestration.
Used by party-mode and other multi-agent coordination features.
</description>
`;
// Add agents by module
for (const [module, agents] of Object.entries(agentsByModule)) {
if (agents.length === 0) continue;
const moduleTitle =
module === 'bmm' ? 'BMM Module' : module === 'cis' ? 'CIS Module' : module === 'core' ? 'Core Module' : 'Custom Module';
xmlContent += `\n <!-- ${moduleTitle} Agents -->\n`;
for (const agent of agents) {
xmlContent += ` <agent id="${agent.id}" name="${agent.name}" title="${agent.title || ''}" icon="${agent.icon || ''}">
<persona>
<role>${escapeXml(agent.role || '')}</role>
<identity>${escapeXml(agent.identity || '')}</identity>
<communication_style>${escapeXml(agent.communicationStyle || '')}</communication_style>
<principles>${agent.principles || ''}</principles>
</persona>
</agent>\n`;
}
}
// Add statistics
const totalAgents = agentDetails.length;
const moduleList = Object.keys(agentsByModule)
.filter((m) => agentsByModule[m].length > 0)
.join(', ');
xmlContent += `\n <statistics>
<total_agents>${totalAgents}</total_agents>
<modules>${moduleList}</modules>
<last_updated>${new Date().toISOString()}</last_updated>
</statistics>
</manifest>`;
return xmlContent;
},
/**
* Extract agent details from XML content
* @param {string} content - Full agent file content (markdown with XML)
* @param {string} moduleName - Module name
* @param {string} agentName - Agent name
* @returns {Object} Agent details
*/
extractAgentDetails(content, moduleName, agentName) {
try {
// Extract agent XML block
const agentMatch = content.match(/<agent[^>]*>([\s\S]*?)<\/agent>/);
if (!agentMatch) return null;
const agentXml = agentMatch[0];
// Extract attributes from opening tag
const nameMatch = agentXml.match(/name="([^"]*)"/);
const titleMatch = agentXml.match(/title="([^"]*)"/);
const iconMatch = agentXml.match(/icon="([^"]*)"/);
// Extract persona elements - now we just copy them as-is
const roleMatch = agentXml.match(/<role>([\s\S]*?)<\/role>/);
const identityMatch = agentXml.match(/<identity>([\s\S]*?)<\/identity>/);
const styleMatch = agentXml.match(/<communication_style>([\s\S]*?)<\/communication_style>/);
const principlesMatch = agentXml.match(/<principles>([\s\S]*?)<\/principles>/);
return {
id: `bmad/${moduleName}/agents/${agentName}.md`,
name: nameMatch ? nameMatch[1] : agentName,
title: titleMatch ? titleMatch[1] : 'Agent',
icon: iconMatch ? iconMatch[1] : '🤖',
module: moduleName,
role: roleMatch ? roleMatch[1].trim() : '',
identity: identityMatch ? identityMatch[1].trim() : '',
communicationStyle: styleMatch ? styleMatch[1].trim() : '',
principles: principlesMatch ? principlesMatch[1].trim() : '',
};
} catch (error) {
console.error(`Error extracting details for agent ${agentName}:`, error);
return null;
}
},
/**
* Extract attribute from XML tag
*/
extractAttribute(xml, tagName, attrName) {
const regex = new RegExp(`<${tagName}[^>]*\\s${attrName}="([^"]*)"`, 'i');
const match = xml.match(regex);
return match ? match[1] : '';
},
/**
* Apply config overrides to agent details
* @param {Object} details - Original agent details
* @param {string} configContent - Config file content
* @returns {Object} Agent details with overrides applied
*/
applyConfigOverrides(details, configContent) {
try {
// Extract agent-config XML block
const configMatch = configContent.match(/<agent-config>([\s\S]*?)<\/agent-config>/);
if (!configMatch) return details;
const configXml = configMatch[0];
// Extract override values
const nameMatch = configXml.match(/<name>([\s\S]*?)<\/name>/);
const titleMatch = configXml.match(/<title>([\s\S]*?)<\/title>/);
const roleMatch = configXml.match(/<role>([\s\S]*?)<\/role>/);
const identityMatch = configXml.match(/<identity>([\s\S]*?)<\/identity>/);
const styleMatch = configXml.match(/<communication_style>([\s\S]*?)<\/communication_style>/);
const principlesMatch = configXml.match(/<principles>([\s\S]*?)<\/principles>/);
// Apply overrides only if values are non-empty
if (nameMatch && nameMatch[1].trim()) {
details.name = nameMatch[1].trim();
}
if (titleMatch && titleMatch[1].trim()) {
details.title = titleMatch[1].trim();
}
if (roleMatch && roleMatch[1].trim()) {
details.role = roleMatch[1].trim();
}
if (identityMatch && identityMatch[1].trim()) {
details.identity = identityMatch[1].trim();
}
if (styleMatch && styleMatch[1].trim()) {
details.communicationStyle = styleMatch[1].trim();
}
if (principlesMatch && principlesMatch[1].trim()) {
// Principles are now just copied as-is (narrative paragraph)
details.principles = principlesMatch[1].trim();
}
return details;
} catch (error) {
console.error(`Error applying config overrides:`, error);
return details;
}
},
/**
* Write agent-manifest.csv to file
*/
async writeAgentParty(filePath, agentDetails, options = {}) {
const content = this.generateAgentParty(agentDetails, options);
await fs.ensureDir(path.dirname(filePath));
await fs.writeFile(filePath, content, 'utf8');
return content;
},
};
module.exports = { AgentPartyGenerator };

View File

@ -1,516 +0,0 @@
/**
* BMAD Agent Compiler
* Transforms agent YAML to compiled XML (.md) format
* Uses the existing BMAD builder infrastructure for proper formatting
*/
const yaml = require('yaml');
const fs = require('node:fs');
const path = require('node:path');
const { processAgentYaml, extractInstallConfig, stripInstallConfig, getDefaultValues } = require('./template-engine');
const { escapeXml } = require('../../../lib/xml-utils');
const { ActivationBuilder } = require('../activation-builder');
const { AgentAnalyzer } = require('../agent-analyzer');
/**
* Build frontmatter for agent
* @param {Object} metadata - Agent metadata
* @param {string} agentName - Final agent name
* @returns {string} YAML frontmatter
*/
function buildFrontmatter(metadata, agentName) {
const nameFromFile = agentName.replaceAll('-', ' ');
const description = metadata.title || 'BMAD Agent';
return `---
name: "${nameFromFile}"
description: "${description}"
---
You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command.
`;
}
// buildSimpleActivation function removed - replaced by ActivationBuilder for proper fragment loading from src/utility/agent-components/
/**
* Build persona XML section
* @param {Object} persona - Persona object
* @returns {string} Persona XML
*/
function buildPersonaXml(persona) {
if (!persona) return '';
let xml = ' <persona>\n';
if (persona.role) {
const roleText = persona.role.trim().replaceAll(/\n+/g, ' ').replaceAll(/\s+/g, ' ');
xml += ` <role>${escapeXml(roleText)}</role>\n`;
}
if (persona.identity) {
const identityText = persona.identity.trim().replaceAll(/\n+/g, ' ').replaceAll(/\s+/g, ' ');
xml += ` <identity>${escapeXml(identityText)}</identity>\n`;
}
if (persona.communication_style) {
const styleText = persona.communication_style.trim().replaceAll(/\n+/g, ' ').replaceAll(/\s+/g, ' ');
xml += ` <communication_style>${escapeXml(styleText)}</communication_style>\n`;
}
if (persona.principles) {
let principlesText;
if (Array.isArray(persona.principles)) {
principlesText = persona.principles.join(' ');
} else {
principlesText = persona.principles.trim().replaceAll(/\n+/g, ' ');
}
xml += ` <principles>${escapeXml(principlesText)}</principles>\n`;
}
xml += ' </persona>\n';
return xml;
}
/**
* Build prompts XML section
* @param {Array} prompts - Prompts array
* @returns {string} Prompts XML
*/
function buildPromptsXml(prompts) {
if (!prompts || prompts.length === 0) return '';
let xml = ' <prompts>\n';
for (const prompt of prompts) {
xml += ` <prompt id="${prompt.id || ''}">\n`;
xml += ` <content>\n`;
// Don't escape prompt content - it's meant to be read as-is
xml += `${prompt.content || ''}\n`;
xml += ` </content>\n`;
xml += ` </prompt>\n`;
}
xml += ' </prompts>\n';
return xml;
}
/**
* Build memories XML section
* @param {Array} memories - Memories array
* @returns {string} Memories XML
*/
function buildMemoriesXml(memories) {
if (!memories || memories.length === 0) return '';
let xml = ' <memories>\n';
for (const memory of memories) {
xml += ` <memory>${escapeXml(String(memory))}</memory>\n`;
}
xml += ' </memories>\n';
return xml;
}
/**
* Build menu XML section
* Supports both legacy and multi format menu items
* Multi items display as a single menu item with nested handlers
* @param {Array} menuItems - Menu items
* @returns {string} Menu XML
*/
function buildMenuXml(menuItems) {
let xml = ' <menu>\n';
// Always inject menu display option first
xml += ` <item cmd="MH or fuzzy match on menu or help">[MH] Redisplay Menu Help</item>\n`;
xml += ` <item cmd="CH or fuzzy match on chat">[CH] Chat with the Agent about anything</item>\n`;
// Add user-defined menu items
if (menuItems && menuItems.length > 0) {
for (const item of menuItems) {
// Handle multi format menu items with nested handlers
if (item.multi && item.triggers && Array.isArray(item.triggers)) {
xml += ` <item type="multi">${escapeXml(item.multi)}\n`;
xml += buildNestedHandlers(item.triggers);
xml += ` </item>\n`;
}
// Handle legacy format menu items
else if (item.trigger) {
let trigger = item.trigger || '';
const attrs = [`cmd="${trigger}"`];
// Add handler attributes
if (item.exec) attrs.push(`exec="${item.exec}"`);
if (item.tmpl) attrs.push(`tmpl="${item.tmpl}"`);
if (item.data) attrs.push(`data="${item.data}"`);
if (item.action) attrs.push(`action="${item.action}"`);
xml += ` <item ${attrs.join(' ')}>${escapeXml(item.description || '')}</item>\n`;
}
}
}
xml += ` <item cmd="PM or fuzzy match on party-mode" exec="skill:bmad-party-mode">[PM] Start Party Mode</item>\n`;
xml += ` <item cmd="DA or fuzzy match on exit, leave, goodbye or dismiss agent">[DA] Dismiss Agent</item>\n`;
xml += ' </menu>\n';
return xml;
}
/**
* Build nested handlers for multi format menu items
* @param {Array} triggers - Triggers array from multi format
* @returns {string} Handler XML
*/
function buildNestedHandlers(triggers) {
let xml = '';
for (const triggerGroup of triggers) {
for (const [triggerName, execArray] of Object.entries(triggerGroup)) {
// Build trigger with * prefix
let trigger = triggerName.startsWith('*') ? triggerName : '*' + triggerName;
// Extract the relevant execution data
const execData = processExecArray(execArray);
// For nested handlers in multi items, we use match attribute for fuzzy matching
const attrs = [`match="${escapeXml(execData.description || '')}"`];
// Add handler attributes based on exec data
if (execData.route) attrs.push(`exec="${execData.route}"`);
if (execData.action) attrs.push(`action="${execData.action}"`);
if (execData.data) attrs.push(`data="${execData.data}"`);
if (execData.tmpl) attrs.push(`tmpl="${execData.tmpl}"`);
// Only add type if it's not 'exec' (exec is already implied by the exec attribute)
if (execData.type && execData.type !== 'exec') attrs.push(`type="${execData.type}"`);
xml += ` <handler ${attrs.join(' ')}></handler>\n`;
}
}
return xml;
}
/**
* Process the execution array from multi format triggers
* Extracts relevant data for XML attributes
* @param {Array} execArray - Array of execution objects
* @returns {Object} Processed execution data
*/
function processExecArray(execArray) {
const result = {
description: '',
route: null,
data: null,
action: null,
type: null,
};
if (!Array.isArray(execArray)) {
return result;
}
for (const exec of execArray) {
if (exec.input) {
// Use input as description if no explicit description is provided
result.description = exec.input;
}
if (exec.route) {
result.route = exec.route;
}
if (exec.data !== null && exec.data !== undefined) {
result.data = exec.data;
}
if (exec.action) {
result.action = exec.action;
}
if (exec.type) {
result.type = exec.type;
}
}
return result;
}
/**
* Compile agent YAML to proper XML format
* @param {Object} agentYaml - Parsed and processed agent YAML
* @param {string} agentName - Final agent name (for ID and frontmatter)
* @param {string} targetPath - Target path for agent ID
* @returns {Promise<string>} Compiled XML string with frontmatter
*/
async function compileToXml(agentYaml, agentName = '', targetPath = '') {
const agent = agentYaml.agent;
const meta = agent.metadata;
let xml = '';
// Build frontmatter
xml += buildFrontmatter(meta, agentName || meta.name || 'agent');
// Start code fence
xml += '```xml\n';
// Agent opening tag
const agentAttrs = [
`id="${targetPath || meta.id || ''}"`,
`name="${meta.name || ''}"`,
`title="${meta.title || ''}"`,
`icon="${meta.icon || '🤖'}"`,
];
if (meta.capabilities) {
agentAttrs.push(`capabilities="${escapeXml(meta.capabilities)}"`);
}
xml += `<agent ${agentAttrs.join(' ')}>\n`;
// Activation block - use ActivationBuilder for proper fragment loading
const activationBuilder = new ActivationBuilder();
const analyzer = new AgentAnalyzer();
const profile = analyzer.analyzeAgentObject(agentYaml);
xml += await activationBuilder.buildActivation(
profile,
meta,
agent.critical_actions || [],
false, // forWebBundle - set to false for IDE deployment
);
// Persona section
xml += buildPersonaXml(agent.persona);
// Prompts section (if present)
if (agent.prompts && agent.prompts.length > 0) {
xml += buildPromptsXml(agent.prompts);
}
// Memories section (if present)
if (agent.memories && agent.memories.length > 0) {
xml += buildMemoriesXml(agent.memories);
}
// Menu section
xml += buildMenuXml(agent.menu || []);
// Closing agent tag
xml += '</agent>\n';
// Close code fence
xml += '```\n';
return xml;
}
/**
* Full compilation pipeline
* @param {string} yamlContent - Raw YAML string
* @param {Object} answers - Answers from install_config questions (or defaults)
* @param {string} agentName - Optional final agent name (user's custom persona name)
* @param {string} targetPath - Optional target path for agent ID
* @param {Object} options - Additional options including config
* @returns {Promise<Object>} { xml: string, metadata: Object }
*/
async function compileAgent(yamlContent, answers = {}, agentName = '', targetPath = '', options = {}) {
// Parse YAML
let agentYaml = yaml.parse(yamlContent);
// Apply customization merges before template processing
// Handle metadata overrides (like name)
if (answers.metadata) {
// Filter out empty values from metadata
const filteredMetadata = filterCustomizationData(answers.metadata);
if (Object.keys(filteredMetadata).length > 0) {
agentYaml.agent.metadata = { ...agentYaml.agent.metadata, ...filteredMetadata };
}
// Remove from answers so it doesn't get processed as template variables
const { metadata, ...templateAnswers } = answers;
answers = templateAnswers;
}
// Handle other customization properties
// These should be merged into the agent structure, not processed as template variables
const customizationKeys = ['persona', 'critical_actions', 'memories', 'menu', 'prompts'];
const customizations = {};
const remainingAnswers = { ...answers };
for (const key of customizationKeys) {
if (answers[key]) {
let filtered;
// Handle different data types
if (Array.isArray(answers[key])) {
// For arrays, filter out empty/null/undefined values
filtered = answers[key].filter((item) => item !== null && item !== undefined && item !== '');
} else {
// For objects, use filterCustomizationData
filtered = filterCustomizationData(answers[key]);
}
// Check if we have valid content
const hasContent = Array.isArray(filtered) ? filtered.length > 0 : Object.keys(filtered).length > 0;
if (hasContent) {
customizations[key] = filtered;
}
delete remainingAnswers[key];
}
}
// Merge customizations into agentYaml
if (Object.keys(customizations).length > 0) {
// For persona: replace entire section
if (customizations.persona) {
agentYaml.agent.persona = customizations.persona;
}
// For critical_actions: append to existing or create new
if (customizations.critical_actions) {
const existing = agentYaml.agent.critical_actions || [];
agentYaml.agent.critical_actions = [...existing, ...customizations.critical_actions];
}
// For memories: append to existing or create new
if (customizations.memories) {
const existing = agentYaml.agent.memories || [];
agentYaml.agent.memories = [...existing, ...customizations.memories];
}
// For menu: append to existing or create new
if (customizations.menu) {
const existing = agentYaml.agent.menu || [];
agentYaml.agent.menu = [...existing, ...customizations.menu];
}
// For prompts: append to existing or create new (by id)
if (customizations.prompts) {
const existing = agentYaml.agent.prompts || [];
// Merge by id, with customizations taking precedence
const mergedPrompts = [...existing];
for (const customPrompt of customizations.prompts) {
const existingIndex = mergedPrompts.findIndex((p) => p.id === customPrompt.id);
if (existingIndex === -1) {
mergedPrompts.push(customPrompt);
} else {
mergedPrompts[existingIndex] = customPrompt;
}
}
agentYaml.agent.prompts = mergedPrompts;
}
}
// Use remaining answers for template processing
answers = remainingAnswers;
// Extract install_config
const installConfig = extractInstallConfig(agentYaml);
// Merge defaults with provided answers
let finalAnswers = answers;
if (installConfig) {
const defaults = getDefaultValues(installConfig);
finalAnswers = { ...defaults, ...answers };
}
// Process templates with answers
const processedYaml = processAgentYaml(agentYaml, finalAnswers);
// Strip install_config from output
const cleanYaml = stripInstallConfig(processedYaml);
let xml = await compileToXml(cleanYaml, agentName, targetPath);
// Ensure xml is a string before attempting replaceAll
if (typeof xml !== 'string') {
throw new TypeError('compileToXml did not return a string');
}
return {
xml,
metadata: cleanYaml.agent.metadata,
processedYaml: cleanYaml,
};
}
/**
* Filter customization data to remove empty/null values
* @param {Object} data - Raw customization data
* @returns {Object} Filtered customization data
*/
function filterCustomizationData(data) {
const filtered = {};
for (const [key, value] of Object.entries(data)) {
if (value === null || value === undefined || value === '') {
continue; // Skip null/undefined/empty values
}
if (Array.isArray(value)) {
if (value.length > 0) {
filtered[key] = value;
}
} else if (typeof value === 'object') {
const nested = filterCustomizationData(value);
if (Object.keys(nested).length > 0) {
filtered[key] = nested;
}
} else {
filtered[key] = value;
}
}
return filtered;
}
/**
* Compile agent file to .md
* @param {string} yamlPath - Path to agent YAML file
* @param {Object} options - { answers: {}, outputPath: string }
* @returns {Object} Compilation result
*/
function compileAgentFile(yamlPath, options = {}) {
const yamlContent = fs.readFileSync(yamlPath, 'utf8');
const result = compileAgent(yamlContent, options.answers || {});
// Determine output path
let outputPath = options.outputPath;
if (!outputPath) {
// Default: same directory, same name, .md extension
const dir = path.dirname(yamlPath);
const basename = path.basename(yamlPath, '.agent.yaml');
outputPath = path.join(dir, `${basename}.md`);
}
// Write compiled XML
fs.writeFileSync(outputPath, xml, 'utf8');
return {
...result,
xml,
outputPath,
sourcePath: yamlPath,
};
}
module.exports = {
compileToXml,
compileAgent,
compileAgentFile,
escapeXml,
buildFrontmatter,
buildPersonaXml,
buildPromptsXml,
buildMemoriesXml,
buildMenuXml,
filterCustomizationData,
};

View File

@ -1,680 +0,0 @@
/**
* BMAD Agent Installer
* Discovers, prompts, compiles, and installs agents
*/
const fs = require('node:fs');
const path = require('node:path');
const yaml = require('yaml');
const prompts = require('../prompts');
const { compileAgent, compileAgentFile } = require('./compiler');
const { extractInstallConfig, getDefaultValues } = require('./template-engine');
/**
* Find BMAD config file in project
* @param {string} startPath - Starting directory to search from
* @returns {Object|null} Config data or null
*/
function findBmadConfig(startPath = process.cwd()) {
// Look for common BMAD folder names
const possibleNames = ['_bmad'];
for (const name of possibleNames) {
const configPath = path.join(startPath, name, 'bmb', 'config.yaml');
if (fs.existsSync(configPath)) {
const content = fs.readFileSync(configPath, 'utf8');
const config = yaml.parse(content);
return {
...config,
bmadFolder: path.join(startPath, name),
projectRoot: startPath,
};
}
}
return null;
}
/**
* Resolve path variables like {project-root} and {bmad-folder}
* @param {string} pathStr - Path with variables
* @param {Object} context - Contains projectRoot, bmadFolder
* @returns {string} Resolved path
*/
function resolvePath(pathStr, context) {
return pathStr.replaceAll('{project-root}', context.projectRoot).replaceAll('{bmad-folder}', context.bmadFolder);
}
/**
* Discover available agents in the custom agent location recursively
* @param {string} searchPath - Path to search for agents
* @returns {Array} List of agent info objects
*/
function discoverAgents(searchPath) {
if (!fs.existsSync(searchPath)) {
return [];
}
const agents = [];
// Helper function to recursively search
function searchDirectory(dir, relativePath = '') {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
const agentRelativePath = relativePath ? path.join(relativePath, entry.name) : entry.name;
if (entry.isFile() && entry.name.endsWith('.agent.yaml')) {
// Simple agent (single file)
// The agent name is based on the filename
const agentName = entry.name.replace('.agent.yaml', '');
agents.push({
type: 'simple',
name: agentName,
path: fullPath,
yamlFile: fullPath,
relativePath: agentRelativePath.replace('.agent.yaml', ''),
});
} else if (entry.isDirectory()) {
// Check if this directory contains an .agent.yaml file
try {
const dirContents = fs.readdirSync(fullPath);
const yamlFiles = dirContents.filter((f) => f.endsWith('.agent.yaml'));
if (yamlFiles.length > 0) {
// Found .agent.yaml files in this directory
for (const yamlFile of yamlFiles) {
const agentYamlPath = path.join(fullPath, yamlFile);
const agentName = path.basename(yamlFile, '.agent.yaml');
agents.push({
type: 'expert',
name: agentName,
path: fullPath,
yamlFile: agentYamlPath,
relativePath: agentRelativePath,
});
}
} else {
// No .agent.yaml in this directory, recurse deeper
searchDirectory(fullPath, agentRelativePath);
}
} catch {
// Skip directories we can't read
}
}
}
}
searchDirectory(searchPath);
return agents;
}
/**
* Load agent YAML and extract install_config
* @param {string} yamlPath - Path to agent YAML file
* @returns {Object} Agent YAML and install config
*/
function loadAgentConfig(yamlPath) {
const content = fs.readFileSync(yamlPath, 'utf8');
const agentYaml = yaml.parse(content);
const installConfig = extractInstallConfig(agentYaml);
const defaults = installConfig ? getDefaultValues(installConfig) : {};
// Check for saved_answers (from previously installed custom agents)
// These take precedence over defaults
const savedAnswers = agentYaml?.saved_answers || {};
const metadata = agentYaml?.agent?.metadata || {};
return {
yamlContent: content,
agentYaml,
installConfig,
defaults: { ...defaults, ...savedAnswers }, // saved_answers override defaults
metadata,
hasSidecar: metadata.hasSidecar === true,
};
}
/**
* Interactive prompt for install_config questions
* @param {Object} installConfig - Install configuration with questions
* @param {Object} defaults - Default values
* @returns {Promise<Object>} User answers
*/
async function promptInstallQuestions(installConfig, defaults, presetAnswers = {}) {
if (!installConfig || !installConfig.questions || installConfig.questions.length === 0) {
return { ...defaults, ...presetAnswers };
}
const answers = { ...defaults, ...presetAnswers };
await prompts.note(installConfig.description || '', 'Agent Configuration');
for (const q of installConfig.questions) {
// Skip questions for variables that are already set (e.g., custom_name set upfront)
if (answers[q.var] !== undefined && answers[q.var] !== defaults[q.var]) {
await prompts.log.message(` ${q.var}: ${answers[q.var]} (already set)`);
continue;
}
switch (q.type) {
case 'text': {
const response = await prompts.text({
message: q.prompt,
default: q.default ?? '',
});
answers[q.var] = response ?? q.default ?? '';
break;
}
case 'boolean': {
const response = await prompts.confirm({
message: q.prompt,
default: q.default,
});
answers[q.var] = response;
break;
}
case 'choice': {
const response = await prompts.select({
message: q.prompt,
options: q.options.map((o) => ({ value: o.value, label: o.label })),
initialValue: q.default,
});
answers[q.var] = response;
break;
}
// No default
}
}
return answers;
}
/**
* Install a compiled agent to target location
* @param {Object} agentInfo - Agent discovery info
* @param {Object} answers - User answers for install_config
* @param {string} targetPath - Target installation directory
* @param {Object} options - Additional options including config
* @returns {Object} Installation result
*/
function installAgent(agentInfo, answers, targetPath, options = {}) {
// Compile the agent
const { xml, metadata, processedYaml } = compileAgent(fs.readFileSync(agentInfo.yamlFile, 'utf8'), answers);
// Determine target agent folder name
// Use the folder name from agentInfo, NOT the persona name from metadata
const agentFolderName = agentInfo.name;
const agentTargetDir = path.join(targetPath, agentFolderName);
// Create target directory
if (!fs.existsSync(agentTargetDir)) {
fs.mkdirSync(agentTargetDir, { recursive: true });
}
// Write compiled XML (.md)
const compiledFileName = `${agentFolderName}.md`;
const compiledPath = path.join(agentTargetDir, compiledFileName);
fs.writeFileSync(compiledPath, xml, 'utf8');
const result = {
success: true,
agentName: metadata.name || agentInfo.name,
targetDir: agentTargetDir,
compiledFile: compiledPath,
};
return result;
}
/**
* Update agent metadata ID to reflect installed location
* @param {string} compiledContent - Compiled XML content
* @param {string} targetPath - Target installation path relative to project
* @returns {string} Updated content
*/
function updateAgentId(compiledContent, targetPath) {
// Update the id attribute in the opening agent tag
return compiledContent.replace(/(<agent\s+id=")[^"]*(")/, `$1${targetPath}$2`);
}
/**
* Detect if a path is within a BMAD project
* @param {string} targetPath - Path to check
* @returns {Object|null} Project info with bmadFolder and cfgFolder
*/
function detectBmadProject(targetPath) {
let checkPath = path.resolve(targetPath);
const root = path.parse(checkPath).root;
// Walk up directory tree looking for BMAD installation
while (checkPath !== root) {
const possibleNames = ['_bmad'];
for (const name of possibleNames) {
const bmadFolder = path.join(checkPath, name);
const cfgFolder = path.join(bmadFolder, '_config');
const manifestFile = path.join(cfgFolder, 'agent-manifest.csv');
if (fs.existsSync(manifestFile)) {
return {
projectRoot: checkPath,
bmadFolder,
cfgFolder,
manifestFile,
};
}
}
checkPath = path.dirname(checkPath);
}
return null;
}
/**
* Escape CSV field value
* @param {string} value - Value to escape
* @returns {string} Escaped value
*/
function escapeCsvField(value) {
if (typeof value !== 'string') value = String(value);
// If contains comma, quote, or newline, wrap in quotes and escape internal quotes
if (value.includes(',') || value.includes('"') || value.includes('\n')) {
return '"' + value.replaceAll('"', '""') + '"';
}
return value;
}
/**
* Parse CSV line respecting quoted fields
* @param {string} line - CSV line
* @returns {Array} Parsed fields
*/
function parseCsvLine(line) {
const fields = [];
let current = '';
let inQuotes = false;
for (let i = 0; i < line.length; i++) {
const char = line[i];
const nextChar = line[i + 1];
if (char === '"' && !inQuotes) {
inQuotes = true;
} else if (char === '"' && inQuotes) {
if (nextChar === '"') {
current += '"';
i++; // Skip escaped quote
} else {
inQuotes = false;
}
} else if (char === ',' && !inQuotes) {
fields.push(current);
current = '';
} else {
current += char;
}
}
fields.push(current);
return fields;
}
/**
* Check if agent name exists in manifest
* @param {string} manifestFile - Path to agent-manifest.csv
* @param {string} agentName - Agent name to check
* @returns {Object|null} Existing entry or null
*/
function checkManifestForAgent(manifestFile, agentName) {
const content = fs.readFileSync(manifestFile, 'utf8');
const lines = content.trim().split('\n');
if (lines.length < 2) return null;
const header = parseCsvLine(lines[0]);
const nameIndex = header.indexOf('name');
if (nameIndex === -1) return null;
for (let i = 1; i < lines.length; i++) {
const fields = parseCsvLine(lines[i]);
if (fields[nameIndex] === agentName) {
const entry = {};
for (const [idx, col] of header.entries()) {
entry[col] = fields[idx] || '';
}
entry._lineNumber = i;
return entry;
}
}
return null;
}
/**
* Check if agent path exists in manifest
* @param {string} manifestFile - Path to agent-manifest.csv
* @param {string} agentPath - Agent path to check
* @returns {Object|null} Existing entry or null
*/
function checkManifestForPath(manifestFile, agentPath) {
const content = fs.readFileSync(manifestFile, 'utf8');
const lines = content.trim().split('\n');
if (lines.length < 2) return null;
const header = parseCsvLine(lines[0]);
const pathIndex = header.indexOf('path');
if (pathIndex === -1) return null;
for (let i = 1; i < lines.length; i++) {
const fields = parseCsvLine(lines[i]);
if (fields[pathIndex] === agentPath) {
const entry = {};
for (const [idx, col] of header.entries()) {
entry[col] = fields[idx] || '';
}
entry._lineNumber = i;
return entry;
}
}
return null;
}
/**
* Update existing entry in manifest
* @param {string} manifestFile - Path to agent-manifest.csv
* @param {Object} agentData - New agent data
* @param {number} lineNumber - Line number to replace (1-indexed, excluding header)
* @returns {boolean} Success
*/
function updateManifestEntry(manifestFile, agentData, lineNumber) {
const content = fs.readFileSync(manifestFile, 'utf8');
const lines = content.trim().split('\n');
const header = lines[0];
const columns = header.split(',');
// Build the new row
const row = columns.map((col) => {
const value = agentData[col] || '';
return escapeCsvField(value);
});
// Replace the line
lines[lineNumber] = row.join(',');
fs.writeFileSync(manifestFile, lines.join('\n') + '\n', 'utf8');
return true;
}
/**
* Add agent to manifest CSV
* @param {string} manifestFile - Path to agent-manifest.csv
* @param {Object} agentData - Agent metadata and path info
* @returns {boolean} Success
*/
function addToManifest(manifestFile, agentData) {
const content = fs.readFileSync(manifestFile, 'utf8');
const lines = content.trim().split('\n');
// Parse header to understand column order
const header = lines[0];
const columns = header.split(',');
// Build the new row based on header columns
const row = columns.map((col) => {
const value = agentData[col] || '';
return escapeCsvField(value);
});
// Append new row
const newLine = row.join(',');
const updatedContent = content.trim() + '\n' + newLine + '\n';
fs.writeFileSync(manifestFile, updatedContent, 'utf8');
return true;
}
/**
* Save agent source YAML to _config/custom/agents/ for reinstallation
* Stores user answers in a top-level saved_answers section (cleaner than overwriting defaults)
* @param {Object} agentInfo - Agent info (path, type, etc.)
* @param {string} cfgFolder - Path to _config folder
* @param {string} agentName - Final agent name (e.g., "fred-commit-poet")
* @param {Object} answers - User answers to save for reinstallation
* @returns {Object} Info about saved source
*/
function saveAgentSource(agentInfo, cfgFolder, agentName, answers = {}) {
// Save to _config/custom/agents/ instead of _config/agents/
const customAgentsCfgDir = path.join(cfgFolder, 'custom', 'agents');
if (!fs.existsSync(customAgentsCfgDir)) {
fs.mkdirSync(customAgentsCfgDir, { recursive: true });
}
const yamlLib = require('yaml');
/**
* Add saved_answers section to store user's actual answers
*/
function addSavedAnswers(agentYaml, answers) {
// Store answers in a clear, separate section
agentYaml.saved_answers = answers;
return agentYaml;
}
if (agentInfo.type === 'simple') {
// Simple agent: copy YAML with saved_answers section
const targetYaml = path.join(customAgentsCfgDir, `${agentName}.agent.yaml`);
const originalContent = fs.readFileSync(agentInfo.yamlFile, 'utf8');
const agentYaml = yamlLib.parse(originalContent);
// Add saved_answers section with user's choices
addSavedAnswers(agentYaml, answers);
fs.writeFileSync(targetYaml, yamlLib.stringify(agentYaml), 'utf8');
return { type: 'simple', path: targetYaml };
} else {
// Expert agent with sidecar: copy entire folder with saved_answers
const targetFolder = path.join(customAgentsCfgDir, agentName);
if (!fs.existsSync(targetFolder)) {
fs.mkdirSync(targetFolder, { recursive: true });
}
// Copy YAML and entire sidecar structure
const sourceDir = agentInfo.path;
const copied = [];
function copyDir(src, dest) {
if (!fs.existsSync(dest)) {
fs.mkdirSync(dest, { recursive: true });
}
const entries = fs.readdirSync(src, { withFileTypes: true });
for (const entry of entries) {
const srcPath = path.join(src, entry.name);
const destPath = path.join(dest, entry.name);
if (entry.isDirectory()) {
copyDir(srcPath, destPath);
} else if (entry.name.endsWith('.agent.yaml')) {
// For the agent YAML, add saved_answers section
const originalContent = fs.readFileSync(srcPath, 'utf8');
const agentYaml = yamlLib.parse(originalContent);
addSavedAnswers(agentYaml, answers);
// Rename YAML to match final agent name
const newYamlPath = path.join(dest, `${agentName}.agent.yaml`);
fs.writeFileSync(newYamlPath, yamlLib.stringify(agentYaml), 'utf8');
copied.push(newYamlPath);
} else {
fs.copyFileSync(srcPath, destPath);
copied.push(destPath);
}
}
}
copyDir(sourceDir, targetFolder);
return { type: 'expert', path: targetFolder, files: copied };
}
}
/**
* Create IDE slash command wrapper for agent
* Leverages IdeManager to dispatch to IDE-specific handlers
* @param {string} projectRoot - Project root path
* @param {string} agentName - Agent name (e.g., "commit-poet")
* @param {string} agentPath - Path to compiled agent (relative to project root)
* @param {Object} metadata - Agent metadata
* @returns {Promise<Object>} Info about created slash commands
*/
async function createIdeSlashCommands(projectRoot, agentName, agentPath, metadata) {
// Read manifest.yaml to get installed IDEs
const manifestPath = path.join(projectRoot, '_bmad', '_config', 'manifest.yaml');
let installedIdes = ['claude-code']; // Default to Claude Code if no manifest
if (fs.existsSync(manifestPath)) {
const yamlLib = require('yaml');
const manifestContent = fs.readFileSync(manifestPath, 'utf8');
const manifest = yamlLib.parse(manifestContent);
if (manifest.ides && Array.isArray(manifest.ides)) {
installedIdes = manifest.ides;
}
}
// Use IdeManager to install custom agent launchers for all configured IDEs
const { IdeManager } = require('../../installers/lib/ide/manager');
const ideManager = new IdeManager();
const results = await ideManager.installCustomAgentLaunchers(installedIdes, projectRoot, agentName, agentPath, metadata);
return results;
}
/**
* Update manifest.yaml to track custom agent
* @param {string} manifestPath - Path to manifest.yaml
* @param {string} agentName - Agent name
* @param {string} agentType - Agent type (source name)
* @returns {boolean} Success
*/
function updateManifestYaml(manifestPath, agentName, agentType) {
if (!fs.existsSync(manifestPath)) {
return false;
}
const yamlLib = require('yaml');
const content = fs.readFileSync(manifestPath, 'utf8');
const manifest = yamlLib.parse(content);
// Initialize custom_agents array if not exists
if (!manifest.custom_agents) {
manifest.custom_agents = [];
}
// Check if this agent is already registered
const existingIndex = manifest.custom_agents.findIndex((a) => a.name === agentName || (typeof a === 'string' && a === agentName));
const agentEntry = {
name: agentName,
type: agentType,
installed: new Date().toISOString(),
};
if (existingIndex === -1) {
// Add new entry
manifest.custom_agents.push(agentEntry);
} else {
// Update existing entry
manifest.custom_agents[existingIndex] = agentEntry;
}
// Update lastUpdated timestamp
if (manifest.installation) {
manifest.installation.lastUpdated = new Date().toISOString();
}
// Write back
const newContent = yamlLib.stringify(manifest);
fs.writeFileSync(manifestPath, newContent, 'utf8');
return true;
}
/**
* Extract manifest data from compiled agent XML
* @param {string} xmlContent - Compiled agent XML
* @param {Object} metadata - Agent metadata from YAML
* @param {string} agentPath - Relative path to agent file
* @param {string} moduleName - Module name (default: 'custom')
* @returns {Object} Manifest row data
*/
function extractManifestData(xmlContent, metadata, agentPath, moduleName = 'custom') {
// Extract data from XML using regex (simple parsing)
const extractTag = (tag) => {
const match = xmlContent.match(new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`));
if (!match) return '';
// Collapse multiple lines into single line, normalize whitespace
return match[1].trim().replaceAll(/\n+/g, ' ').replaceAll(/\s+/g, ' ').trim();
};
// Extract attributes from agent tag
const extractAgentAttribute = (attr) => {
const match = xmlContent.match(new RegExp(`<agent[^>]*\\s${attr}=["']([^"']+)["']`));
return match ? match[1] : '';
};
const extractPrinciples = () => {
const match = xmlContent.match(/<principles>([\s\S]*?)<\/principles>/);
if (!match) return '';
// Extract individual principle lines
const principles = match[1]
.split('\n')
.map((l) => l.trim())
.filter((l) => l.length > 0)
.join(' ');
return principles;
};
// Prioritize XML extraction over metadata for agent persona info
const xmlTitle = extractAgentAttribute('title') || extractTag('name');
const xmlIcon = extractAgentAttribute('icon');
return {
name: metadata.id ? path.basename(metadata.id, '.md') : metadata.name.toLowerCase().replaceAll(/\s+/g, '-'),
displayName: xmlTitle || metadata.name || '',
title: xmlTitle || metadata.title || '',
icon: xmlIcon || metadata.icon || '',
role: extractTag('role'),
identity: extractTag('identity'),
communicationStyle: extractTag('communication_style'),
principles: extractPrinciples(),
module: moduleName,
path: agentPath,
};
}
module.exports = {
findBmadConfig,
resolvePath,
discoverAgents,
loadAgentConfig,
promptInstallQuestions,
installAgent,
updateAgentId,
detectBmadProject,
addToManifest,
extractManifestData,
escapeCsvField,
checkManifestForAgent,
checkManifestForPath,
updateManifestEntry,
saveAgentSource,
createIdeSlashCommands,
updateManifestYaml,
};

View File

@ -1,152 +0,0 @@
/**
* Template Engine for BMAD Agent Install Configuration
* Processes {{variable}}, {{#if}}, {{#unless}}, and {{/if}} blocks
*/
/**
* Process all template syntax in a string
* @param {string} content - Content with template syntax
* @param {Object} variables - Key-value pairs from install_config answers
* @returns {string} Processed content
*/
function processTemplate(content, variables = {}) {
let result = content;
// Process conditionals first (they may contain variables)
result = processConditionals(result, variables);
// Then process simple variable replacements
result = processVariables(result, variables);
// Clean up any empty lines left by removed conditionals
result = cleanupEmptyLines(result);
return result;
}
/**
* Process {{#if}}, {{#unless}}, {{/if}}, {{/unless}} blocks
*/
function processConditionals(content, variables) {
let result = content;
// Process {{#if variable == "value"}} blocks
// Handle both regular quotes and JSON-escaped quotes (\")
const ifEqualsPattern = /\{\{#if\s+(\w+)\s*==\s*\\?"([^"\\]+)\\?"\s*\}\}([\s\S]*?)\{\{\/if\}\}/g;
result = result.replaceAll(ifEqualsPattern, (match, varName, value, block) => {
return variables[varName] === value ? block : '';
});
// Process {{#if variable}} blocks (boolean or truthy check)
const ifBoolPattern = /\{\{#if\s+(\w+)\s*\}\}([\s\S]*?)\{\{\/if\}\}/g;
result = result.replaceAll(ifBoolPattern, (match, varName, block) => {
const val = variables[varName];
// Treat as truthy: true, non-empty string, non-zero number
const isTruthy = val === true || (typeof val === 'string' && val.length > 0) || (typeof val === 'number' && val !== 0);
return isTruthy ? block : '';
});
// Process {{#unless variable}} blocks (inverse of if)
const unlessPattern = /\{\{#unless\s+(\w+)\s*\}\}([\s\S]*?)\{\{\/unless\}\}/g;
result = result.replaceAll(unlessPattern, (match, varName, block) => {
const val = variables[varName];
const isFalsy = val === false || val === '' || val === null || val === undefined || val === 0;
return isFalsy ? block : '';
});
return result;
}
/**
* Process {{variable}} replacements
*/
function processVariables(content, variables) {
let result = content;
// Replace {{variable}} with value
const varPattern = /\{\{(\w+)\}\}/g;
result = result.replaceAll(varPattern, (match, varName) => {
if (Object.hasOwn(variables, varName)) {
return String(variables[varName]);
}
// If variable not found, leave as-is (might be runtime variable like {user_name})
return match;
});
return result;
}
/**
* Clean up excessive empty lines left after removing conditional blocks
*/
function cleanupEmptyLines(content) {
// Replace 3+ consecutive newlines with 2
return content.replaceAll(/\n{3,}/g, '\n\n');
}
/**
* Extract install_config from agent YAML object
* @param {Object} agentYaml - Parsed agent YAML
* @returns {Object|null} install_config section or null
*/
function extractInstallConfig(agentYaml) {
return agentYaml?.agent?.install_config || null;
}
/**
* Remove install_config from agent YAML (after processing)
* @param {Object} agentYaml - Parsed agent YAML
* @returns {Object} Agent YAML without install_config
*/
function stripInstallConfig(agentYaml) {
const result = structuredClone(agentYaml);
if (result.agent) {
delete result.agent.install_config;
}
return result;
}
/**
* Process entire agent YAML object with template variables
* @param {Object} agentYaml - Parsed agent YAML
* @param {Object} variables - Answers from install_config questions
* @returns {Object} Processed agent YAML
*/
function processAgentYaml(agentYaml, variables) {
// Convert to JSON string, process templates, parse back
const jsonString = JSON.stringify(agentYaml, null, 2);
const processed = processTemplate(jsonString, variables);
return JSON.parse(processed);
}
/**
* Get default values from install_config questions
* @param {Object} installConfig - install_config section
* @returns {Object} Default values keyed by variable name
*/
function getDefaultValues(installConfig) {
const defaults = {};
if (!installConfig?.questions) {
return defaults;
}
for (const question of installConfig.questions) {
if (question.var && question.default !== undefined) {
defaults[question.var] = question.default;
}
}
return defaults;
}
module.exports = {
processTemplate,
processConditionals,
processVariables,
extractInstallConfig,
stripInstallConfig,
processAgentYaml,
getDefaultValues,
cleanupEmptyLines,
};

View File

@ -208,14 +208,6 @@ class UI {
}); });
} }
// Add custom agent compilation option
if (installedVersion !== 'unknown') {
choices.push({
name: 'Recompile Agents (apply customizations only)',
value: 'compile-agents',
});
}
// Common actions // Common actions
choices.push({ name: 'Modify BMAD Installation', value: 'update' }); choices.push({ name: 'Modify BMAD Installation', value: 'update' });
@ -291,17 +283,6 @@ class UI {
}; };
} }
// Handle compile agents separately
if (actionType === 'compile-agents') {
// Only recompile agents with customizations, don't update any files
return {
actionType: 'compile-agents',
directory: confirmedDirectory,
customContent: { hasCustomContent: false },
skipPrompts: options.yes || false,
};
}
// If actionType === 'update', handle it with the new flow // If actionType === 'update', handle it with the new flow
// Return early with modify configuration // Return early with modify configuration
if (actionType === 'update') { if (actionType === 'update') {

View File

@ -1,177 +0,0 @@
const xml2js = require('xml2js');
const fs = require('fs-extra');
const path = require('node:path');
const { getProjectRoot, getSourcePath } = require('./project-root');
const { YamlXmlBuilder } = require('./yaml-xml-builder');
/**
* XML utility functions for BMAD installer
* Now supports both legacy XML agents and new YAML-based agents
*/
class XmlHandler {
constructor() {
this.parser = new xml2js.Parser({
preserveChildrenOrder: true,
explicitChildren: true,
explicitArray: false,
trim: false,
normalizeTags: false,
attrkey: '$',
charkey: '_',
});
this.builder = new xml2js.Builder({
renderOpts: {
pretty: true,
indent: ' ',
newline: '\n',
},
xmldec: {
version: '1.0',
encoding: 'utf8',
standalone: false,
},
headless: true, // Don't add XML declaration
attrkey: '$',
charkey: '_',
});
this.yamlBuilder = new YamlXmlBuilder();
}
/**
* Load and parse the activation template
* @returns {Object} Parsed activation block
*/
async loadActivationTemplate() {
console.error('Failed to load activation template:', error);
}
/**
* Inject activation block into agent XML content
* @param {string} agentContent - The agent file content
* @param {Object} metadata - Metadata containing module and name
* @returns {string} Modified content with activation block
*/
async injectActivation(agentContent, metadata = {}) {
try {
// Check if already has activation
if (agentContent.includes('<activation')) {
return agentContent;
}
// Extract the XML portion from markdown if needed
let xmlContent = agentContent;
let beforeXml = '';
let afterXml = '';
const xmlBlockMatch = agentContent.match(/([\s\S]*?)```xml\n([\s\S]*?)\n```([\s\S]*)/);
if (xmlBlockMatch) {
beforeXml = xmlBlockMatch[1] + '```xml\n';
xmlContent = xmlBlockMatch[2];
afterXml = '\n```' + xmlBlockMatch[3];
}
// Parse the agent XML
const parsed = await this.parser.parseStringPromise(xmlContent);
// Get the activation template
const activationBlock = await this.loadActivationTemplate();
if (!activationBlock) {
console.warn('Could not load activation template');
return agentContent;
}
// Find the agent node
if (
parsed.agent && // Insert activation as the first child
!parsed.agent.activation
) {
// Ensure proper structure
if (!parsed.agent.$$) {
parsed.agent.$$ = [];
}
// Create the activation node with proper structure
const activationNode = {
'#name': 'activation',
$: { critical: '1' },
$$: activationBlock.$$,
};
// Insert at the beginning
parsed.agent.$$.unshift(activationNode);
}
// Convert back to XML
let modifiedXml = this.builder.buildObject(parsed);
// Fix indentation - xml2js doesn't maintain our exact formatting
// Add 2-space base indentation to match our style
const lines = modifiedXml.split('\n');
const indentedLines = lines.map((line) => {
if (line.trim() === '') return line;
if (line.startsWith('<agent')) return line; // Keep agent at column 0
return ' ' + line; // Indent everything else
});
modifiedXml = indentedLines.join('\n');
// Reconstruct the full content
return beforeXml + modifiedXml + afterXml;
} catch (error) {
console.error('Error injecting activation:', error);
return agentContent;
}
}
/**
* TODO: DELETE THIS METHOD
*/
injectActivationSimple(agentContent, metadata = {}) {
console.error('Error in simple injection:', error);
}
/**
* Build agent from YAML source
* @param {string} yamlPath - Path to .agent.yaml file
* @param {string} customizePath - Path to .customize.yaml file (optional)
* @param {Object} metadata - Build metadata
* @returns {string} Generated XML content
*/
async buildFromYaml(yamlPath, customizePath = null, metadata = {}) {
try {
// Use YamlXmlBuilder to convert YAML to XML
const mergedAgent = await this.yamlBuilder.loadAndMergeAgent(yamlPath, customizePath);
// Build metadata
const buildMetadata = {
sourceFile: path.basename(yamlPath),
sourceHash: await this.yamlBuilder.calculateFileHash(yamlPath),
customizeFile: customizePath ? path.basename(customizePath) : null,
customizeHash: customizePath ? await this.yamlBuilder.calculateFileHash(customizePath) : null,
builderVersion: '1.0.0',
includeMetadata: metadata.includeMetadata !== false,
forWebBundle: metadata.forWebBundle || false, // Pass through forWebBundle flag
};
// Convert to XML
const xml = await this.yamlBuilder.convertToXml(mergedAgent, buildMetadata);
return xml;
} catch (error) {
console.error('Error building agent from YAML:', error);
throw error;
}
}
/**
* Check if a path is a YAML agent file
* @param {string} filePath - Path to check
* @returns {boolean} True if it's a YAML agent file
*/
isYamlAgent(filePath) {
return filePath.endsWith('.agent.yaml');
}
}
module.exports = { XmlHandler };

View File

@ -1,82 +0,0 @@
const fs = require('node:fs');
const path = require('node:path');
function convertXmlToMarkdown(xmlFilePath) {
if (!xmlFilePath.endsWith('.xml')) {
throw new Error('Input file must be an XML file');
}
const xmlContent = fs.readFileSync(xmlFilePath, 'utf8');
const basename = path.basename(xmlFilePath, '.xml');
const dirname = path.dirname(xmlFilePath);
const mdFilePath = path.join(dirname, `${basename}.md`);
// Extract version and name/title from root element attributes
let title = basename;
let version = '';
// Match the root element and its attributes
const rootMatch = xmlContent.match(
/<[^>\s]+[^>]*?\sv="([^"]+)"[^>]*?(?:\sname="([^"]+)")?|<[^>\s]+[^>]*?(?:\sname="([^"]+)")?[^>]*?\sv="([^"]+)"/,
);
if (rootMatch) {
// Handle both v="x" name="y" and name="y" v="x" orders
version = rootMatch[1] || rootMatch[4] || '';
const nameAttr = rootMatch[2] || rootMatch[3] || '';
if (nameAttr) {
title = nameAttr;
} else {
// Try to find name in a <name> element if not in attributes
const nameElementMatch = xmlContent.match(/<name>([^<]+)<\/name>/);
if (nameElementMatch) {
title = nameElementMatch[1];
}
}
}
const heading = version ? `# ${title} v${version}` : `# ${title}`;
const markdownContent = `${heading}
\`\`\`xml
${xmlContent}
\`\`\`
`;
fs.writeFileSync(mdFilePath, markdownContent, 'utf8');
return mdFilePath;
}
function main() {
const args = process.argv.slice(2);
if (args.length === 0) {
console.error('Usage: node xml-to-markdown.js <xml-file-path>');
process.exit(1);
}
const xmlFilePath = path.resolve(args[0]);
if (!fs.existsSync(xmlFilePath)) {
console.error(`Error: File not found: ${xmlFilePath}`);
process.exit(1);
}
try {
const mdFilePath = convertXmlToMarkdown(xmlFilePath);
console.log(`Successfully converted: ${xmlFilePath} -> ${mdFilePath}`);
} catch (error) {
console.error(`Error converting file: ${error.message}`);
process.exit(1);
}
}
if (require.main === module) {
main();
}
module.exports = { convertXmlToMarkdown };

View File

@ -1,572 +0,0 @@
const yaml = require('yaml');
const fs = require('fs-extra');
const path = require('node:path');
const crypto = require('node:crypto');
const { AgentAnalyzer } = require('./agent-analyzer');
const { ActivationBuilder } = require('./activation-builder');
const { escapeXml } = require('../../lib/xml-utils');
/**
* Converts agent YAML files to XML format with smart activation injection
*/
class YamlXmlBuilder {
constructor() {
this.analyzer = new AgentAnalyzer();
this.activationBuilder = new ActivationBuilder();
}
/**
* Deep merge two objects (for customize.yaml + agent.yaml)
* @param {Object} target - Target object
* @param {Object} source - Source object to merge in
* @returns {Object} Merged object
*/
deepMerge(target, source) {
const output = { ...target };
if (this.isObject(target) && this.isObject(source)) {
for (const key of Object.keys(source)) {
if (this.isObject(source[key])) {
if (key in target) {
output[key] = this.deepMerge(target[key], source[key]);
} else {
output[key] = source[key];
}
} else if (Array.isArray(source[key])) {
// For arrays, append rather than replace (for commands)
if (Array.isArray(target[key])) {
output[key] = [...target[key], ...source[key]];
} else {
output[key] = source[key];
}
} else {
output[key] = source[key];
}
}
}
return output;
}
/**
* Check if value is an object
*/
isObject(item) {
return item && typeof item === 'object' && !Array.isArray(item);
}
/**
* Load and merge agent YAML with customization
* @param {string} agentYamlPath - Path to base agent YAML
* @param {string} customizeYamlPath - Path to customize YAML (optional)
* @returns {Object} Merged agent configuration
*/
async loadAndMergeAgent(agentYamlPath, customizeYamlPath = null) {
// Load base agent
const agentContent = await fs.readFile(agentYamlPath, 'utf8');
const agentYaml = yaml.parse(agentContent);
// Load customization if exists
let merged = agentYaml;
if (customizeYamlPath && (await fs.pathExists(customizeYamlPath))) {
const customizeContent = await fs.readFile(customizeYamlPath, 'utf8');
const customizeYaml = yaml.parse(customizeContent);
if (customizeYaml) {
// Special handling: persona fields are merged, but only non-empty values override
if (customizeYaml.persona) {
const basePersona = merged.agent.persona || {};
const customPersona = {};
// Only copy non-empty customize values
for (const [key, value] of Object.entries(customizeYaml.persona)) {
if (value !== '' && value !== null && !(Array.isArray(value) && value.length === 0)) {
customPersona[key] = value;
}
}
// Merge non-empty customize values over base
if (Object.keys(customPersona).length > 0) {
merged.agent.persona = { ...basePersona, ...customPersona };
}
}
// Merge metadata (only non-empty values)
if (customizeYaml.agent && customizeYaml.agent.metadata) {
const nonEmptyMetadata = {};
for (const [key, value] of Object.entries(customizeYaml.agent.metadata)) {
if (value !== '' && value !== null) {
nonEmptyMetadata[key] = value;
}
}
merged.agent.metadata = { ...merged.agent.metadata, ...nonEmptyMetadata };
}
// Append menu items (support both 'menu' and legacy 'commands')
const customMenuItems = customizeYaml.menu || customizeYaml.commands;
if (customMenuItems) {
// Determine if base uses 'menu' or 'commands'
if (merged.agent.menu) {
merged.agent.menu = [...merged.agent.menu, ...customMenuItems];
} else if (merged.agent.commands) {
merged.agent.commands = [...merged.agent.commands, ...customMenuItems];
} else {
// Default to 'menu' for new agents
merged.agent.menu = customMenuItems;
}
}
// Append critical actions
if (customizeYaml.critical_actions) {
merged.agent.critical_actions = [...(merged.agent.critical_actions || []), ...customizeYaml.critical_actions];
}
// Append prompts
if (customizeYaml.prompts) {
merged.agent.prompts = [...(merged.agent.prompts || []), ...customizeYaml.prompts];
}
// Append memories
if (customizeYaml.memories) {
merged.agent.memories = [...(merged.agent.memories || []), ...customizeYaml.memories];
}
}
}
return merged;
}
/**
* Convert agent YAML to XML
* @param {Object} agentYaml - Parsed agent YAML object
* @param {Object} buildMetadata - Metadata about the build (file paths, hashes, etc.)
* @returns {string} XML content
*/
async convertToXml(agentYaml, buildMetadata = {}) {
const agent = agentYaml.agent;
const metadata = agent.metadata || {};
// Add module from buildMetadata if available
if (buildMetadata.module) {
metadata.module = buildMetadata.module;
}
// Analyze agent to determine needed handlers
const profile = this.analyzer.analyzeAgentObject(agentYaml);
// Build activation block only if not skipped
let activationBlock = '';
if (!buildMetadata.skipActivation) {
activationBlock = await this.activationBuilder.buildActivation(
profile,
metadata,
agent.critical_actions || [],
buildMetadata.forWebBundle || false, // Pass web bundle flag
);
}
// Start building XML
let xml = '';
if (buildMetadata.forWebBundle) {
// Web bundle: keep existing format
xml += '<!-- Powered by BMAD-CORE™ -->\n\n';
xml += `# ${metadata.title || 'Agent'}\n\n`;
} else {
// Installation: use YAML frontmatter + instruction
// Extract name from filename: "cli-chief.yaml" or "pm.agent.yaml" -> "cli chief" or "pm"
const filename = buildMetadata.sourceFile || 'agent.yaml';
let nameFromFile = path.basename(filename, path.extname(filename)); // Remove .yaml/.md extension
nameFromFile = nameFromFile.replace(/\.agent$/, ''); // Remove .agent suffix if present
nameFromFile = nameFromFile.replaceAll('-', ' '); // Replace dashes with spaces
xml += '---\n';
xml += `name: "${nameFromFile}"\n`;
xml += `description: "${metadata.title || 'BMAD Agent'}"\n`;
xml += '---\n\n';
xml +=
"You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command.\n\n";
}
xml += '```xml\n';
// Agent opening tag
const agentAttrs = [
`id="${metadata.id || ''}"`,
`name="${metadata.name || ''}"`,
`title="${metadata.title || ''}"`,
`icon="${metadata.icon || '🤖'}"`,
];
// Add localskip attribute if present
if (metadata.localskip === true) {
agentAttrs.push('localskip="true"');
}
xml += `<agent ${agentAttrs.join(' ')}>\n`;
// Activation block (only if not skipped)
if (activationBlock) {
xml += activationBlock + '\n';
}
// Persona section
xml += this.buildPersonaXml(agent.persona);
// Memories section (if exists)
if (agent.memories) {
xml += this.buildMemoriesXml(agent.memories);
}
// Prompts section (if exists)
if (agent.prompts) {
xml += this.buildPromptsXml(agent.prompts);
}
// Menu section (support both 'menu' and legacy 'commands')
const menuItems = agent.menu || agent.commands || [];
xml += this.buildCommandsXml(menuItems, buildMetadata.forWebBundle);
xml += '</agent>\n';
xml += '```\n';
return xml;
}
/**
* Build persona XML section
*/
buildPersonaXml(persona) {
if (!persona) return '';
let xml = ' <persona>\n';
if (persona.role) {
xml += ` <role>${escapeXml(persona.role)}</role>\n`;
}
if (persona.identity) {
xml += ` <identity>${escapeXml(persona.identity)}</identity>\n`;
}
if (persona.communication_style) {
xml += ` <communication_style>${escapeXml(persona.communication_style)}</communication_style>\n`;
}
if (persona.principles) {
// Principles can be array or string
let principlesText;
if (Array.isArray(persona.principles)) {
principlesText = persona.principles.join(' ');
} else {
principlesText = persona.principles;
}
xml += ` <principles>${escapeXml(principlesText)}</principles>\n`;
}
xml += ' </persona>\n';
return xml;
}
/**
* Build memories XML section
*/
buildMemoriesXml(memories) {
if (!memories || memories.length === 0) return '';
let xml = ' <memories>\n';
for (const memory of memories) {
xml += ` <memory>${escapeXml(memory)}</memory>\n`;
}
xml += ' </memories>\n';
return xml;
}
/**
* Build prompts XML section
* Handles both array format and object/dictionary format
*/
buildPromptsXml(prompts) {
if (!prompts) return '';
// Handle object/dictionary format: { promptId: 'content', ... }
// Convert to array format for processing
let promptsArray = prompts;
if (!Array.isArray(prompts)) {
// Check if it's an object with no length property (dictionary format)
if (typeof prompts === 'object' && prompts.length === undefined) {
promptsArray = Object.entries(prompts).map(([id, content]) => ({
id: id,
content: content,
}));
} else {
return ''; // Not a valid prompts format
}
}
if (promptsArray.length === 0) return '';
let xml = ' <prompts>\n';
for (const prompt of promptsArray) {
xml += ` <prompt id="${prompt.id || ''}">\n`;
xml += ` <content>\n`;
xml += `${escapeXml(prompt.content || '')}\n`;
xml += ` </content>\n`;
xml += ` </prompt>\n`;
}
xml += ' </prompts>\n';
return xml;
}
/**
* Build menu XML section (renamed from commands for clarity)
* Auto-injects *help and *exit, adds * prefix to all triggers
* Supports both legacy format and new multi format with nested handlers
* @param {Array} menuItems - Menu items from YAML
* @param {boolean} forWebBundle - Whether building for web bundle
*/
buildCommandsXml(menuItems, forWebBundle = false) {
let xml = ' <menu>\n';
// Always inject menu display option first
xml += ` <item cmd="*menu">[M] Redisplay Menu Options</item>\n`;
// Add user-defined menu items with * prefix
if (menuItems && menuItems.length > 0) {
for (const item of menuItems) {
// Skip ide-only items when building for web bundles
if (forWebBundle && item['ide-only'] === true) {
continue;
}
// Skip web-only items when NOT building for web bundles (i.e., IDE/local installation)
if (!forWebBundle && item['web-only'] === true) {
continue;
}
// Handle multi format menu items with nested handlers
if (item.multi && item.triggers && Array.isArray(item.triggers)) {
xml += ` <item type="multi">${escapeXml(item.multi)}\n`;
xml += this.buildNestedHandlers(item.triggers);
xml += ` </item>\n`;
}
// Handle legacy format menu items
else if (item.trigger) {
// For legacy items, keep using cmd with *<trigger> format
let trigger = item.trigger || '';
if (!trigger.startsWith('*')) {
trigger = '*' + trigger;
}
const attrs = [`cmd="${trigger}"`];
// Add handler attributes
if (item['validate-workflow']) attrs.push(`validate-workflow="${item['validate-workflow']}"`);
if (item.exec) attrs.push(`exec="${item.exec}"`);
if (item.tmpl) attrs.push(`tmpl="${item.tmpl}"`);
if (item.data) attrs.push(`data="${item.data}"`);
if (item.action) attrs.push(`action="${item.action}"`);
xml += ` <item ${attrs.join(' ')}>${escapeXml(item.description || '')}</item>\n`;
}
}
}
// Always inject dismiss last
xml += ` <item cmd="*dismiss">[D] Dismiss Agent</item>\n`;
xml += ' </menu>\n';
return xml;
}
/**
* Build nested handlers for multi format menu items
* @param {Array} triggers - Triggers array from multi format
* @returns {string} Handler XML
*/
buildNestedHandlers(triggers) {
let xml = '';
for (const triggerGroup of triggers) {
for (const [triggerName, execArray] of Object.entries(triggerGroup)) {
// Build trigger with * prefix
let trigger = triggerName.startsWith('*') ? triggerName : '*' + triggerName;
// Extract the relevant execution data
const execData = this.processExecArray(execArray);
// For nested handlers in multi items, we don't need cmd attribute
// The match attribute will handle fuzzy matching
const attrs = [`match="${escapeXml(execData.description || '')}"`];
// Add handler attributes based on exec data
if (execData.route) attrs.push(`exec="${execData.route}"`);
if (execData.action) attrs.push(`action="${execData.action}"`);
if (execData.data) attrs.push(`data="${execData.data}"`);
if (execData.tmpl) attrs.push(`tmpl="${execData.tmpl}"`);
// Only add type if it's not 'exec' (exec is already implied by the exec attribute)
if (execData.type && execData.type !== 'exec') attrs.push(`type="${execData.type}"`);
xml += ` <handler ${attrs.join(' ')}></handler>\n`;
}
}
return xml;
}
/**
* Process the execution array from multi format triggers
* Extracts relevant data for XML attributes
* @param {Array} execArray - Array of execution objects
* @returns {Object} Processed execution data
*/
processExecArray(execArray) {
const result = {
description: '',
route: null,
data: null,
action: null,
type: null,
};
if (!Array.isArray(execArray)) {
return result;
}
for (const exec of execArray) {
if (exec.input) {
// Use input as description if no explicit description is provided
result.description = exec.input;
}
if (exec.route) {
result.route = exec.route;
}
if (exec.data !== null && exec.data !== undefined) {
result.data = exec.data;
}
if (exec.action) {
result.action = exec.action;
}
if (exec.type) {
result.type = exec.type;
}
}
return result;
}
/**
* Calculate file hash for build tracking
*/
async calculateFileHash(filePath) {
if (!(await fs.pathExists(filePath))) {
return null;
}
const content = await fs.readFile(filePath, 'utf8');
return crypto.createHash('md5').update(content).digest('hex').slice(0, 8);
}
/**
* Build agent XML from YAML files and return as string (for in-memory use)
* @param {string} agentYamlPath - Path to agent YAML
* @param {string} customizeYamlPath - Path to customize YAML (optional)
* @param {Object} options - Build options
* @returns {Promise<string>} XML content as string
*/
async buildFromYaml(agentYamlPath, customizeYamlPath = null, options = {}) {
// Load and merge YAML files
const mergedAgent = await this.loadAndMergeAgent(agentYamlPath, customizeYamlPath);
// Calculate hashes for build tracking
const sourceHash = await this.calculateFileHash(agentYamlPath);
const customizeHash = customizeYamlPath ? await this.calculateFileHash(customizeYamlPath) : null;
// Extract module from path (e.g., /path/to/modules/bmm/agents/pm.yaml -> bmm)
// or /path/to/bmad/bmm/agents/pm.yaml -> bmm
// or /path/to/src/bmm-skills/agents/pm.yaml -> bmm
let module = 'core'; // default to core
const pathParts = agentYamlPath.split(path.sep);
// Look for module indicators in the path
const modulesIndex = pathParts.indexOf('modules');
const bmadIndex = pathParts.indexOf('bmad');
const srcIndex = pathParts.indexOf('src');
if (modulesIndex !== -1 && pathParts[modulesIndex + 1]) {
// Path contains /modules/{module}/
module = pathParts[modulesIndex + 1];
} else if (bmadIndex !== -1 && pathParts[bmadIndex + 1]) {
// Path contains /bmad/{module}/
const potentialModule = pathParts[bmadIndex + 1];
// Check if it's a known module, not 'agents' or '_config'
if (['bmm', 'bmb', 'cis', 'core'].includes(potentialModule)) {
module = potentialModule;
}
} else if (srcIndex !== -1 && pathParts[srcIndex + 1]) {
// Path contains /src/{module}/ (bmm-skills and core-skills are directly under src/)
const potentialModule = pathParts[srcIndex + 1];
if (potentialModule === 'bmm-skills') {
module = 'bmm';
} else if (potentialModule === 'core-skills') {
module = 'core';
}
}
// Build metadata
const buildMetadata = {
sourceFile: path.basename(agentYamlPath),
sourceHash,
customizeFile: customizeYamlPath ? path.basename(customizeYamlPath) : null,
customizeHash,
builderVersion: '1.0.0',
includeMetadata: options.includeMetadata !== false,
skipActivation: options.skipActivation === true,
forWebBundle: options.forWebBundle === true,
module: module, // Add module to buildMetadata
};
// Convert to XML and return
return await this.convertToXml(mergedAgent, buildMetadata);
}
/**
* Build agent XML from YAML files
* @param {string} agentYamlPath - Path to agent YAML
* @param {string} customizeYamlPath - Path to customize YAML (optional)
* @param {string} outputPath - Path to write XML file
* @param {Object} options - Build options
*/
async buildAgent(agentYamlPath, customizeYamlPath, outputPath, options = {}) {
// Use buildFromYaml to get XML content
const xml = await this.buildFromYaml(agentYamlPath, customizeYamlPath, options);
// Write output file
await fs.ensureDir(path.dirname(outputPath));
await fs.writeFile(outputPath, xml, 'utf8');
// Calculate hashes for return value
const sourceHash = await this.calculateFileHash(agentYamlPath);
const customizeHash = customizeYamlPath ? await this.calculateFileHash(customizeYamlPath) : null;
return {
success: true,
outputPath,
sourceHash,
customizeHash,
};
}
}
module.exports = { YamlXmlBuilder };

View File

@ -45,7 +45,7 @@ export default defineConfig({
title: 'BMAD Method', title: 'BMAD Method',
tagline: 'AI-driven agile development with specialized agents and workflows that scale from bug fixes to enterprise platforms.', tagline: 'AI-driven agile development with specialized agents and workflows that scale from bug fixes to enterprise platforms.',
// i18n: English as root (no URL prefix), Chinese at /zh-cn/ // i18n: English as root (no URL prefix), Chinese at /zh-cn/, French at /fr/
defaultLocale: 'root', defaultLocale: 'root',
locales: { locales: {
root: { root: {
@ -56,6 +56,10 @@ export default defineConfig({
label: '简体中文', label: '简体中文',
lang: 'zh-CN', lang: 'zh-CN',
}, },
fr: {
label: 'Français',
lang: 'fr-FR',
},
}, },
logo: { logo: {
@ -106,29 +110,29 @@ export default defineConfig({
// Sidebar configuration (Diataxis structure) // Sidebar configuration (Diataxis structure)
sidebar: [ sidebar: [
{ label: 'Welcome', translations: { 'zh-CN': '欢迎' }, slug: 'index' }, { label: 'Welcome', translations: { 'zh-CN': '欢迎', 'fr-FR': 'Bienvenue' }, slug: 'index' },
{ label: 'Roadmap', translations: { 'zh-CN': '路线图' }, slug: 'roadmap' }, { label: 'Roadmap', translations: { 'zh-CN': '路线图', 'fr-FR': 'Feuille de route' }, slug: 'roadmap' },
{ {
label: 'Tutorials', label: 'Tutorials',
translations: { 'zh-CN': '教程' }, translations: { 'zh-CN': '教程', 'fr-FR': 'Tutoriels' },
collapsed: false, collapsed: false,
autogenerate: { directory: 'tutorials' }, autogenerate: { directory: 'tutorials' },
}, },
{ {
label: 'How-To Guides', label: 'How-To Guides',
translations: { 'zh-CN': '操作指南' }, translations: { 'zh-CN': '操作指南', 'fr-FR': 'Guides pratiques' },
collapsed: true, collapsed: true,
autogenerate: { directory: 'how-to' }, autogenerate: { directory: 'how-to' },
}, },
{ {
label: 'Explanation', label: 'Explanation',
translations: { 'zh-CN': '概念说明' }, translations: { 'zh-CN': '概念说明', 'fr-FR': 'Explications' },
collapsed: true, collapsed: true,
autogenerate: { directory: 'explanation' }, autogenerate: { directory: 'explanation' },
}, },
{ {
label: 'Reference', label: 'Reference',
translations: { 'zh-CN': '参考' }, translations: { 'zh-CN': '参考', 'fr-FR': 'Référence' },
collapsed: true, collapsed: true,
autogenerate: { directory: 'reference' }, autogenerate: { directory: 'reference' },
}, },

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View File

@ -0,0 +1,355 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Carte des Workflows - Méthode BMad</title>
<style>
:root {
--analysis: #0ea5e9;
--planning: #22c55e;
--solutioning: #eab308;
--implementation: #ef4444;
--quickflow: #64748b;
--bg: #0f172a;
--card: #1e293b;
--text: #f8fafc;
--text-muted: #94a3b8;
--border: #334155;
--success: #10b981;
--arrow: #f59e0b;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--bg);
color: var(--text);
line-height: 1.4;
padding: 12px;
}
.header { text-align: center; margin-bottom: 16px; }
.header-badge {
display: inline-flex; align-items: center; gap: 6px;
background: linear-gradient(135deg, var(--analysis), var(--planning));
padding: 4px 12px; border-radius: 20px;
font-size: 0.75rem; font-weight: 600; margin-bottom: 8px;
}
h1 { font-size: 1.5rem; font-weight: 700; }
.subtitle { color: var(--text-muted); font-size: 0.8rem; }
.flow-legend {
background: rgba(245, 158, 11, 0.1); border: 1px solid rgba(245, 158, 11, 0.3);
border-radius: 8px; padding: 8px 12px; margin-bottom: 16px; text-align: center;
font-size: 0.7rem; color: var(--arrow);
}
.flow-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 8px;
margin-bottom: 16px;
}
.phase {
background: var(--card);
border-radius: 10px;
border: 1px solid var(--border);
border-top: 3px solid;
}
.phase.analysis { border-top-color: var(--analysis); }
.phase.planning { border-top-color: var(--planning); }
.phase.solutioning { border-top-color: var(--solutioning); }
.phase.implementation { border-top-color: var(--implementation); }
.phase-header { padding: 10px 12px 8px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 8px; }
.phase-num { width: 22px; height: 22px; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 0.75rem; font-weight: 700; }
.analysis .phase-num { background: var(--analysis); }
.planning .phase-num { background: var(--planning); }
.solutioning .phase-num { background: var(--solutioning); color: #000; }
.implementation .phase-num { background: var(--implementation); }
.phase-title { font-size: 0.95rem; font-weight: 700; }
.phase-opt { font-size: 0.6rem; padding: 2px 6px; border-radius: 4px; background: rgba(14, 165, 233, 0.2); color: #7dd3fc; }
.workflows { padding: 8px 10px; }
.workflow {
background: rgba(255,255,255,0.03); border-radius: 6px;
padding: 8px; margin-bottom: 6px;
font-size: 0.7rem;
}
.workflow-header {
display: flex; justify-content: space-between; align-items: center;
margin-bottom: 4px;
}
.workflow-name {
font-family: monospace; color: var(--success);
background: rgba(16, 185, 129, 0.1); padding: 2px 6px;
border-radius: 3px; font-size: 0.65rem;
}
.workflow-meta { display: flex; justify-content: space-between; align-items: center; }
.agent { display: flex; align-items: center; gap: 4px; }
.agent-icon { width: 14px; height: 14px; border-radius: 3px; display: flex; align-items: center; justify-content: center; font-size: 0.55rem; font-weight: 700; }
.agent-icon.mary { background: linear-gradient(135deg, #f472b6, #ec4899); }
.agent-icon.john { background: linear-gradient(135deg, #60a5fa, #3b82f6); }
.agent-icon.sally { background: linear-gradient(135deg, #fbbf24, #f59e0b); color: #000; }
.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.barry { background: linear-gradient(135deg, #94a3b8, #64748b); }
.agent-name { font-size: 0.65rem; }
.output { color: var(--success); font-family: monospace; font-size: 0.6rem; }
.badge { font-size: 0.55rem; padding: 1px 4px; border-radius: 3px; }
.badge.opt { background: rgba(251,191,36,0.15); color: #fbbf24; }
.badge.iffy { background: rgba(168,85,247,0.15); color: #a855f7; }
.badge.adhoc { background: rgba(59,130,246,0.15); color: #3b82f6; }
.arrow { display: flex; align-items: center; justify-content: center; color: var(--arrow); font-size: 0.9rem; margin: 6px 0; }
.decision { background: linear-gradient(135deg, #a855f7, #9333ea); padding: 4px 8px; border-radius: 4px; text-align: center; font-size: 0.65rem; font-weight: 600; margin: 6px 0; }
.quickflow {
background: rgba(100, 116, 139, 0.2); border: 2px dashed var(--quickflow);
border-radius: 10px; padding: 12px; margin-bottom: 16px;
}
.quickflow-header { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.quickflow-header h2 { font-size: 0.95rem; }
.quickflow-header span { font-size: 0.7rem; color: var(--text-muted); }
.quickflow-body { display: flex; align-items: center; gap: 12px; }
.quickflow-item { flex: 1; background: rgba(255,255,255,0.03); border-radius: 8px; padding: 10px; }
.quickflow-item code { font-family: monospace; color: var(--success); background: rgba(16, 185, 129, 0.1); padding: 2px 6px; border-radius: 3px; font-size: 0.7rem; }
.context { background: rgba(14, 165, 233, 0.08); border-radius: 10px; padding: 12px; font-size: 0.75rem; }
.context-header { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; font-weight: 600; }
.context-items { display: flex; flex-wrap: wrap; gap: 6px; }
.context-items code { font-family: monospace; color: var(--success); background: rgba(16, 185, 129, 0.1); padding: 2px 5px; border-radius: 3px; font-size: 0.65rem; }
.context-items span { color: var(--text-muted); }
.legend { display: flex; justify-content: center; gap: 12px; flex-wrap: wrap; font-size: 0.7rem; margin-top: 12px; }
.legend-item { display: flex; align-items: center; gap: 4px; }
.legend-dot { width: 12px; height: 3px; border-radius: 2px; }
</style>
</head>
<body>
<div class="header">
<div class="header-badge">⚡ Carte des Workflows V6</div>
<h1>Méthode BMad</h1>
<p class="subtitle">Ingénierie de contexte pour le développement piloté par l'IA</p>
</div>
<div class="flow-legend">→ les flèches indiquent le flux des artefacts entre les workflows</div>
<div class="flow-grid">
<!-- Phase 1: Analysis -->
<div class="phase analysis">
<div class="phase-header">
<div class="phase-num">1</div>
<div class="phase-title">Analyse</div>
<span class="phase-opt">Optionnel</span>
</div>
<div class="workflows">
<div class="workflow">
<div class="workflow-header">
<span class="workflow-name">brainstorm</span>
<span class="badge opt">opt</span>
</div>
<div class="workflow-meta">
<div class="agent"><div class="agent-icon mary">M</div><span class="agent-name">Mary</span></div>
<span class="output">brainstorming-report.md</span>
</div>
</div>
<div class="workflow">
<div class="workflow-header">
<span class="workflow-name">research</span>
<span class="badge opt">opt</span>
</div>
<div class="workflow-meta">
<div class="agent"><div class="agent-icon mary">M</div><span class="agent-name">Mary</span></div>
<span class="output">conclusions</span>
</div>
</div>
<div class="workflow">
<div class="workflow-header">
<span class="workflow-name">create-product-brief</span>
</div>
<div class="workflow-meta">
<div class="agent"><div class="agent-icon mary">M</div><span class="agent-name">Mary</span></div>
<span class="output">product-brief.md →</span>
</div>
</div>
</div>
<div class="arrow"></div>
</div>
<!-- Phase 2: Planning -->
<div class="phase planning">
<div class="phase-header">
<div class="phase-num">2</div>
<div class="phase-title">Planification</div>
</div>
<div class="workflows">
<div class="workflow">
<div class="workflow-header">
<span class="workflow-name">create-prd</span>
</div>
<div class="workflow-meta">
<div class="agent"><div class="agent-icon john">J</div><span class="agent-name">John</span></div>
<span class="output">PRD.md →</span>
</div>
</div>
<div class="decision">Comporte une Interface Utilisateur ?</div>
<div class="workflow">
<div class="workflow-header">
<span class="workflow-name">create-ux-design</span>
<span class="badge iffy">si oui</span>
</div>
<div class="workflow-meta">
<div class="agent"><div class="agent-icon sally">S</div><span class="agent-name">Sally</span></div>
<span class="output">ux-spec.md →</span>
</div>
</div>
</div>
<div class="arrow"></div>
</div>
<!-- Phase 3: Solutioning -->
<div class="phase solutioning">
<div class="phase-header">
<div class="phase-num">3</div>
<div class="phase-title">Solutioning</div>
</div>
<div class="workflows">
<div class="workflow">
<div class="workflow-header">
<span class="workflow-name">create-architecture</span>
</div>
<div class="workflow-meta">
<div class="agent"><div class="agent-icon winston">W</div><span class="agent-name">Winston</span></div>
<span class="output">architecture.md →</span>
</div>
</div>
<div class="workflow">
<div class="workflow-header">
<span class="workflow-name">create-epics-and-stories</span>
</div>
<div class="workflow-meta">
<div class="agent"><div class="agent-icon john">J</div><span class="agent-name">John</span></div>
<span class="output">epics.md →</span>
</div>
</div>
<div class="workflow">
<div class="workflow-header">
<span class="workflow-name">check-implementation-readiness</span>
</div>
<div class="workflow-meta">
<div class="agent"><div class="agent-icon john">J</div><span class="agent-name">John</span></div>
<span class="output">vérification</span>
</div>
</div>
</div>
<div class="arrow"></div>
</div>
<!-- Phase 4: Implementation -->
<div class="phase implementation">
<div class="phase-header">
<div class="phase-num">4</div>
<div class="phase-title">Implémentation</div>
</div>
<div class="workflows">
<div class="workflow">
<div class="workflow-header">
<span class="workflow-name">sprint-planning</span>
</div>
<div class="workflow-meta">
<div class="agent"><div class="agent-icon bob">B</div><span class="agent-name">Bob</span></div>
<span class="output">sprint-status.yaml →</span>
</div>
</div>
<div class="workflow">
<div class="workflow-header">
<span class="workflow-name">create-story</span>
</div>
<div class="workflow-meta">
<div class="agent"><div class="agent-icon bob">B</div><span class="agent-name">Bob</span></div>
<span class="output">story-[slug].md →</span>
</div>
</div>
<div class="workflow">
<div class="workflow-header">
<span class="workflow-name">dev-story</span>
</div>
<div class="workflow-meta">
<div class="agent"><div class="agent-icon amelia">A</div><span class="agent-name">Amelia</span></div>
<span class="output">code →</span>
</div>
</div>
<div class="workflow">
<div class="workflow-header">
<span class="workflow-name">code-review</span>
</div>
<div class="workflow-meta">
<div class="agent"><div class="agent-icon amelia">A</div><span class="agent-name">Amelia</span></div>
<span class="output">approbation</span>
</div>
</div>
<div class="workflow">
<div class="workflow-header">
<span class="workflow-name">correct-course</span>
<span class="badge adhoc">ad-hoc</span>
</div>
<div class="workflow-meta">
<div class="agent"><div class="agent-icon john">J</div><span class="agent-name">John</span></div>
<span class="output">plan mis à jour</span>
</div>
</div>
<div class="workflow">
<div class="workflow-header">
<span class="workflow-name">retrospective</span>
<span class="badge adhoc">par Epic</span>
</div>
<div class="workflow-meta">
<div class="agent"><div class="agent-icon bob">B</div><span class="agent-name">Bob</span></div>
<span class="output">leçons</span>
</div>
</div>
</div>
</div>
</div>
<div class="quickflow">
<div class="quickflow-header">
<span></span>
<div>
<h2>Quick Dev (Parcours Rapide)</h2>
<span>Pour les petites modifications bien comprises — sautez les phases 1-3</span>
</div>
</div>
<div class="quickflow-body">
<div class="quickflow-item">
<div class="agent"><div class="agent-icon barry">B</div><span class="agent-name">Barry</span></div>
<code>quick-dev</code>
<div style="font-size: 0.65rem; color: var(--text-muted); margin-top: 4px;">intention → spec technique → code fonctionnel</div>
</div>
</div>
</div>
<div class="context">
<div class="context-header">📚 Flux de Contexte</div>
<p style="margin-bottom: 8px; color: var(--text-muted);">Chaque document devient le contexte pour la phase suivante.</p>
<div class="context-items">
<span><code>create-story</code> <span>charge epics, PRD, architecture, UX</span></span>
<span><code>dev-story</code> <span>charge le fichier story</span></span>
<span><code>code-review</code> <span>charge architecture, story</span></span>
<span><code>quick-dev</code> <span>clarifie, planifie, implémente, révise</span></span>
</div>
</div>
<div class="legend">
<div class="legend-item"><div class="legend-dot" style="background: var(--analysis);"></div>Analyse</div>
<div class="legend-item"><div class="legend-dot" style="background: var(--planning);"></div>Planification</div>
<div class="legend-item"><div class="legend-dot" style="background: var(--solutioning);"></div>Solutioning</div>
<div class="legend-item"><div class="legend-dot" style="background: var(--implementation);"></div>Implémentation</div>
<div class="legend-item"><div class="legend-dot" style="background: var(--quickflow);"></div>Quick Dev</div>
</div>
</body>
</html>

View File

@ -0,0 +1,28 @@
{
"skipLink.label": "Aller au contenu",
"search.label": "Rechercher",
"search.ctrlKey": "Ctrl",
"search.cancelLabel": "Annuler",
"themeSelect.accessibleLabel": "Choisir le thème",
"themeSelect.dark": "Sombre",
"themeSelect.light": "Clair",
"themeSelect.auto": "Automatique",
"languageSelect.accessibleLabel": "Choisir la langue",
"menuButton.accessibleLabel": "Menu",
"sidebarNav.accessibleLabel": "Navigation principale",
"tableOfContents.onThisPage": "Sur cette page",
"tableOfContents.overview": "Aperçu",
"i18n.untranslatedContent": "Ce contenu n'est pas encore disponible en français.",
"page.editLink": "Modifier la page",
"page.lastUpdated": "Dernière mise à jour :",
"page.previousLink": "Page précédente",
"page.nextLink": "Page suivante",
"page.draft": "Ce contenu est un brouillon et ne sera pas inclus dans la version finale.",
"404.text": "Page non trouvée. Vérifiez l'URL ou utilisez la recherche.",
"aside.note": "Note",
"aside.tip": "Astuce",
"aside.caution": "Attention",
"aside.danger": "Danger",
"fileTree.directory": "Répertoire",
"builtWithStarlight.label": "Construit avec Starlight"
}