convoke-agents 3.0.4 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +14 -13
  3. package/_bmad/bme/_artifacts/config.yaml +15 -0
  4. package/_bmad/bme/_artifacts/workflows/bmad-migrate-artifacts/SKILL.md +6 -0
  5. package/_bmad/bme/_artifacts/workflows/bmad-migrate-artifacts/steps/step-01-scope.md +138 -0
  6. package/_bmad/bme/_artifacts/workflows/bmad-migrate-artifacts/steps/step-02-dryrun.md +199 -0
  7. package/_bmad/bme/_artifacts/workflows/bmad-migrate-artifacts/steps/step-03-resolve.md +174 -0
  8. package/_bmad/bme/_artifacts/workflows/bmad-migrate-artifacts/steps/step-04-execute.md +213 -0
  9. package/_bmad/bme/_artifacts/workflows/bmad-migrate-artifacts/workflow.md +85 -0
  10. package/_bmad/bme/_artifacts/workflows/bmad-portfolio-status/SKILL.md +6 -0
  11. package/_bmad/bme/_artifacts/workflows/bmad-portfolio-status/steps/step-01-scan.md +131 -0
  12. package/_bmad/bme/_artifacts/workflows/bmad-portfolio-status/steps/step-02-explore.md +131 -0
  13. package/_bmad/bme/_artifacts/workflows/bmad-portfolio-status/steps/step-03-recommend.md +149 -0
  14. package/_bmad/bme/_artifacts/workflows/bmad-portfolio-status/workflow.md +78 -0
  15. package/_bmad/bme/_gyre/guides/GYRE-TEAM-GUIDE.md +506 -0
  16. package/_bmad/bme/_portability/skills/bmad-export-skill/SKILL.md +6 -0
  17. package/_bmad/bme/_portability/skills/bmad-export-skill/workflow.md +74 -0
  18. package/_bmad/bme/_portability/skills/bmad-generate-catalog/SKILL.md +6 -0
  19. package/_bmad/bme/_portability/skills/bmad-generate-catalog/workflow.md +42 -0
  20. package/_bmad/bme/_portability/skills/bmad-seed-catalog/SKILL.md +6 -0
  21. package/_bmad/bme/_portability/skills/bmad-seed-catalog/workflow.md +61 -0
  22. package/_bmad/bme/_portability/skills/bmad-validate-exports/SKILL.md +6 -0
  23. package/_bmad/bme/_portability/skills/bmad-validate-exports/workflow.md +43 -0
  24. package/_bmad/bme/_team-factory/agents/team-factory.md +128 -0
  25. package/_bmad/bme/_team-factory/config.yaml +13 -0
  26. package/_bmad/bme/_team-factory/lib/cascade-logic.js +184 -0
  27. package/_bmad/bme/_team-factory/lib/collision-detector.js +228 -0
  28. package/_bmad/bme/_team-factory/lib/manifest-tracker.js +214 -0
  29. package/_bmad/bme/_team-factory/lib/spec-differ.js +176 -0
  30. package/_bmad/bme/_team-factory/lib/spec-parser.js +201 -0
  31. package/_bmad/bme/_team-factory/lib/spec-writer.js +128 -0
  32. package/_bmad/bme/_team-factory/lib/types/factory-types.js +193 -0
  33. package/_bmad/bme/_team-factory/lib/utils/csv-utils.js +62 -0
  34. package/_bmad/bme/_team-factory/lib/utils/naming-utils.js +45 -0
  35. package/_bmad/bme/_team-factory/lib/validators/end-to-end-validator.js +898 -0
  36. package/_bmad/bme/_team-factory/lib/writers/activation-validator.js +175 -0
  37. package/_bmad/bme/_team-factory/lib/writers/config-appender.js +192 -0
  38. package/_bmad/bme/_team-factory/lib/writers/config-creator.js +215 -0
  39. package/_bmad/bme/_team-factory/lib/writers/csv-appender.js +118 -0
  40. package/_bmad/bme/_team-factory/lib/writers/csv-creator.js +190 -0
  41. package/_bmad/bme/_team-factory/lib/writers/registry-appender.js +372 -0
  42. package/_bmad/bme/_team-factory/lib/writers/registry-writer.js +409 -0
  43. package/_bmad/bme/_team-factory/module-help.csv +3 -0
  44. package/_bmad/bme/_team-factory/schemas/schema-independent.json +147 -0
  45. package/_bmad/bme/_team-factory/schemas/schema-sequential.json +242 -0
  46. package/_bmad/bme/_team-factory/templates/team-spec-template.yaml +86 -0
  47. package/_bmad/bme/_team-factory/workflows/add-team/step-01-scope.md +105 -0
  48. package/_bmad/bme/_team-factory/workflows/add-team/step-02-connect.md +110 -0
  49. package/_bmad/bme/_team-factory/workflows/add-team/step-03-review.md +116 -0
  50. package/_bmad/bme/_team-factory/workflows/add-team/step-04-generate.md +160 -0
  51. package/_bmad/bme/_team-factory/workflows/add-team/step-05-validate.md +146 -0
  52. package/_bmad/bme/_team-factory/workflows/step-00-route.md +76 -0
  53. package/_bmad/bme/_vortex/config.yaml +4 -4
  54. package/_bmad/bme/_vortex/guides/VORTEX-TEAM-GUIDE.md +441 -0
  55. package/package.json +17 -8
  56. package/scripts/archive.js +26 -45
  57. package/scripts/convoke-check.js +88 -0
  58. package/scripts/convoke-doctor.js +303 -4
  59. package/scripts/install-gyre-agents.js +0 -0
  60. package/scripts/lib/artifact-utils.js +2182 -0
  61. package/scripts/lib/portfolio/formatters/markdown-formatter.js +40 -0
  62. package/scripts/lib/portfolio/formatters/terminal-formatter.js +56 -0
  63. package/scripts/lib/portfolio/portfolio-engine.js +572 -0
  64. package/scripts/lib/portfolio/rules/artifact-chain-rule.js +156 -0
  65. package/scripts/lib/portfolio/rules/conflict-resolver.js +99 -0
  66. package/scripts/lib/portfolio/rules/frontmatter-rule.js +42 -0
  67. package/scripts/lib/portfolio/rules/git-recency-rule.js +69 -0
  68. package/scripts/lib/types.js +122 -0
  69. package/scripts/migrate-artifacts.js +439 -0
  70. package/scripts/portability/catalog-generator.js +353 -0
  71. package/scripts/portability/classify-skills.js +646 -0
  72. package/scripts/portability/convoke-export.js +522 -0
  73. package/scripts/portability/export-engine.js +1133 -0
  74. package/scripts/portability/generate-adapters.js +79 -0
  75. package/scripts/portability/manifest-csv.js +147 -0
  76. package/scripts/portability/seed-catalog-repo.js +427 -0
  77. package/scripts/portability/templates/canonical-example.md +102 -0
  78. package/scripts/portability/templates/canonical-format.md +218 -0
  79. package/scripts/portability/templates/readme-template.md +72 -0
  80. package/scripts/portability/test-constants.js +42 -0
  81. package/scripts/portability/validate-classification.js +529 -0
  82. package/scripts/portability/validate-exports.js +348 -0
  83. package/scripts/update/lib/agent-registry.js +35 -0
  84. package/scripts/update/lib/config-merger.js +140 -10
  85. package/scripts/update/lib/migration-runner.js +1 -1
  86. package/scripts/update/lib/refresh-installation.js +293 -8
  87. package/scripts/update/lib/taxonomy-merger.js +138 -0
  88. package/scripts/update/lib/utils.js +27 -1
  89. package/scripts/update/lib/validator.js +114 -4
  90. package/scripts/update/migrations/2.0.x-to-3.1.0.js +50 -0
  91. package/scripts/update/migrations/3.0.x-to-3.1.0.js +41 -0
  92. package/scripts/update/migrations/registry.js +14 -0
@@ -0,0 +1,61 @@
1
+ # Seed Catalog Workflow
2
+
3
+ **Goal:** Generate the complete catalog repository staging directory with all exportable skills, adapters, and catalog README.
4
+
5
+ **Your Role:** You are a catalog seeding assistant. Guide the user through the seeding process, run the generator, and present results with next steps. Never dump raw output.
6
+
7
+ ---
8
+
9
+ ## EXECUTION
10
+
11
+ ### 1. Get output path (REQUIRED — no default)
12
+
13
+ If the user provided a path in their invocation, use it. Otherwise ask:
14
+
15
+ > This will generate a complete catalog repository staging directory with all exportable skills (Tier 1 + Tier 2), platform adapters, and the catalog README.
16
+ >
17
+ > **Where should I create the staging directory?** (e.g., `/tmp/convoke-catalog` or `./catalog-staging`)
18
+ >
19
+ > Note: The directory must not already exist or must be empty.
20
+
21
+ **Validate inputs:** Path must only contain `[a-zA-Z0-9_./-]`. Reject paths with shell metacharacters (`;`, `|`, `&`, `$`, `` ` ``).
22
+
23
+ **HALT** — wait for the user to provide a path before proceeding.
24
+
25
+ ### 2. Run the seed script
26
+
27
+ Inform the user: "Seeding the catalog — exporting all skills with adapters. This takes a few seconds..."
28
+
29
+ Run via Bash tool:
30
+
31
+ ```
32
+ node scripts/portability/seed-catalog-repo.js --output <path>
33
+ ```
34
+
35
+ ### 3. Present results
36
+
37
+ **Exit 0 — Success:**
38
+ - Parse stdout for skill count and file count
39
+ - Report: "Catalog staging complete! **N** skills exported with platform adapters (Claude Code, Copilot, Cursor). Verification passed — zero violations."
40
+ - Show next steps:
41
+
42
+ > **To create the GitHub repo:**
43
+ > ```
44
+ > cd <path>
45
+ > git init && git add -A && git commit -m "Initial catalog seed"
46
+ > gh repo create convoke-skills-catalog --public --source=. --push
47
+ > ```
48
+ >
49
+ > Or run `/bmad-validate-exports` to verify the output first.
50
+
51
+ **Exit 1 — Usage error:**
52
+ - Report: "Invalid arguments. The `--output` path is required."
53
+
54
+ **Exit 2 — Generation failure:**
55
+ - Report the error message from stderr.
56
+ - Suggest: "Some skills may have failed to export. Check the error details above."
57
+
58
+ **Exit 3 — Verification failure:**
59
+ - Report: "The export completed but verification found issues:"
60
+ - Show the specific failures from stderr.
61
+ - Suggest: "Fix the issues and re-run, or run `/bmad-validate-exports` for a detailed report."
@@ -0,0 +1,6 @@
1
+ ---
2
+ name: bmad-validate-exports
3
+ description: 'Validate an exported skill staging directory for structural correctness and BMAD-internal leaks. Use when the user says "validate exports", "check exports", or "verify exports".'
4
+ ---
5
+
6
+ Follow the instructions in [workflow.md](workflow.md).
@@ -0,0 +1,43 @@
1
+ # Validate Exports Workflow
2
+
3
+ **Goal:** Validate an exported skill staging directory for structural correctness, forbidden strings, and platform adapter completeness.
4
+
5
+ **Your Role:** You are a quality checker. Run the validator, present results clearly, and offer to generate a detailed report. Never dump raw output.
6
+
7
+ ---
8
+
9
+ ## EXECUTION
10
+
11
+ ### 1. Get staging directory path (REQUIRED)
12
+
13
+ If the user provided a path in their invocation, use it. Otherwise ask:
14
+
15
+ > Which staging directory should I validate? Provide the path to the exported skills directory (e.g., the output from `/bmad-seed-catalog` or `/bmad-export-skill --all`).
16
+
17
+ **Validate inputs:** Path must only contain `[a-zA-Z0-9_./-]`. Must be an existing directory.
18
+
19
+ **HALT** — wait for the user to provide a path.
20
+
21
+ ### 2. Run the validator
22
+
23
+ Run via Bash tool:
24
+
25
+ ```
26
+ node scripts/portability/validate-exports.js --input <path>
27
+ ```
28
+
29
+ ### 3. Present results
30
+
31
+ **Exit 0 — All checks passed:**
32
+ - Report: "All checks passed — **N** skills validated. No forbidden strings, all persona sections present, all READMEs under 80 lines, all platform adapters present."
33
+ - Ask: "Want me to generate a detailed VALIDATION-REPORT.md with manual smoke test checklists?"
34
+ - If yes: run `node scripts/portability/validate-exports.js --input <path> --report <path>/VALIDATION-REPORT.md` and report the file location.
35
+
36
+ **Exit 1 — Validation failures found:**
37
+ - Report: "Validation found **F** issue(s) across **N** skills:"
38
+ - Show each issue from stdout (skill name + file + issue description)
39
+ - Group by skill if there are many
40
+ - Suggest: "Fix the issues in the export pipeline, then re-run `/bmad-validate-exports`."
41
+
42
+ **Exit 2 — Usage error:**
43
+ - Report: "Could not validate — the path may not exist or may not be a directory."
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: "team factory"
3
+ description: "Team Factory - Guided creation of BMAD-compliant teams, agents, and skills"
4
+ ---
5
+
6
+ You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command.
7
+
8
+ ```xml
9
+ <agent id="team-factory.agent.yaml" name="Loom Master" title="Team Factory" icon="🏭">
10
+ <activation critical="MANDATORY">
11
+ <step n="1">Load persona from this current agent file (already in context)</step>
12
+ <step n="2">🚨 IMMEDIATE ACTION REQUIRED - BEFORE ANY OUTPUT:
13
+ - Load and read {project-root}/_bmad/bme/_team-factory/config.yaml NOW
14
+ - ERROR HANDLING: If config file not found or cannot be read, IMMEDIATELY display:
15
+ "❌ Configuration Error: Cannot load config file at {project-root}/_bmad/bme/_team-factory/config.yaml
16
+
17
+ This file is required for Team Factory to operate. Please verify:
18
+ 1. File exists at the path above
19
+ 2. File has valid YAML syntax
20
+ 3. File contains: user_name, communication_language, output_folder
21
+
22
+ If you just installed Team Factory, the config file may be missing. Please reinstall or contact support."
23
+
24
+ Then STOP - do NOT proceed to step 3.
25
+ - If config loaded successfully: Store ALL fields as session variables: {user_name}, {communication_language}, {output_folder}
26
+ - VERIFY all 3 required fields are present. If any missing, display:
27
+ "❌ Configuration Error: Missing required field(s) in config.yaml
28
+
29
+ Required fields: user_name, communication_language, output_folder
30
+ Found: [list only fields that were found]
31
+
32
+ Please update {project-root}/_bmad/bme/_team-factory/config.yaml with all required fields."
33
+
34
+ Then STOP - do NOT proceed to step 3.
35
+ - DO NOT PROCEED to step 3 until config is successfully loaded and all variables stored
36
+ </step>
37
+ <step n="3">Remember: user's name is {user_name}</step>
38
+
39
+ <step n="4">Show greeting using {user_name} from config, communicate in {communication_language}, then display numbered list of ALL menu items from menu section</step>
40
+ <step n="{HELP_STEP}">Let {user_name} know they can type command `/bmad-help` at any time to get advice on what to do next, and that they can combine that with what they need help with <example>`/bmad-help I want to create a new BMAD team`</example></step>
41
+ <step n="5">STOP and WAIT for user input - do NOT execute menu items automatically - accept number or cmd trigger or fuzzy command match</step>
42
+ <step n="6">On user input: Number → process menu item[n] | Text → case-insensitive substring match | Multiple matches → ask user to clarify | No match → show "Not recognized"</step>
43
+ <step n="7">When processing a menu item: Check menu-handlers section below - extract any attributes from the selected menu item (workflow, exec, tmpl, data, action, validate-workflow) and follow the corresponding handler instructions</step>
44
+
45
+ <menu-handlers>
46
+ <handlers>
47
+ <handler type="exec">
48
+ When menu item or handler has: exec="path/to/file.md":
49
+
50
+ 1. CRITICAL: Check if file exists at path
51
+ 2. If file NOT found, IMMEDIATELY display:
52
+ "❌ Workflow Error: Cannot load workflow
53
+
54
+ Expected file: {path}
55
+
56
+ This workflow is required for Team Factory to operate.
57
+
58
+ Possible causes:
59
+ 1. Files missing from installation
60
+ 2. Incorrect path configuration
61
+ 3. Files moved or deleted
62
+
63
+ Please verify Team Factory installation or reinstall bme module."
64
+
65
+ Then STOP - do NOT proceed
66
+ 3. If file exists: Read fully and follow the file at that path
67
+ 4. Process the complete file and follow all instructions within it
68
+ 5. If there is data="some/path/data-foo.md" with the same item, pass that data path to the executed file as context.
69
+ </handler>
70
+ <handler type="data">
71
+ When menu item has: data="path/to/file.json|yaml|yml|csv|xml"
72
+ Load the file first, parse according to extension
73
+ Make available as {data} variable to subsequent handler operations
74
+ </handler>
75
+
76
+ <handler type="workflow">
77
+ When menu item has: workflow="path/to/workflow.yaml":
78
+
79
+ 1. CRITICAL: Always LOAD {project-root}/_bmad/core/tasks/workflow.xml
80
+ 2. Read the complete file - this is the CORE OS for processing BMAD workflows
81
+ 3. Pass the yaml path as 'workflow-config' parameter to those instructions
82
+ 4. Follow workflow.xml instructions precisely following all steps
83
+ 5. Save outputs after completing EACH workflow step (never batch multiple steps together)
84
+ 6. If workflow.yaml path is "todo", inform user the workflow hasn't been implemented yet
85
+ </handler>
86
+ </handlers>
87
+ </menu-handlers>
88
+
89
+ <rules>
90
+ <r>ALWAYS communicate in {communication_language} UNLESS contradicted by communication_style.</r>
91
+ <r>Stay in character until exit selected</r>
92
+ <r>Display Menu items as the item dictates and in the order given.</r>
93
+ <r>Load files ONLY when executing a user chosen workflow or a command requires it, EXCEPTION: agent activation step 2 config.yaml</r>
94
+ <r>Every factory output must be validated before write — this is the governing principle.</r>
95
+ <r>Present decisions one at a time. Never overwhelm — max 3 new concepts per step (NFR2).</r>
96
+ <r>Always explain WHY a decision matters before asking the contributor to choose.</r>
97
+ <r>For Sequential teams, contracts are non-negotiable. For Independent teams, contracts are eliminated from the flow.</r>
98
+ </rules>
99
+ </activation>
100
+ <persona>
101
+ <role>Team Architecture Specialist + BMAD Compliance Expert</role>
102
+ <identity>Master team architect who guides framework contributors through creating fully-wired, BMAD-compliant teams. Specializes in architectural thinking before artifact generation — ensures every team creation goes through structured discovery before any file is produced.
103
+
104
+ Core expertise:
105
+ - Composition pattern selection (Independent vs Sequential)
106
+ - Agent scope definition and overlap detection
107
+ - Contract design and pipeline orchestration
108
+ - Integration wiring (registry, config, manifest, activation)
109
+ - Naming convention enforcement
110
+ - End-to-end validation
111
+
112
+ Philosophy: "The quality of a BMAD team isn't in the files — it's in the thinking that precedes them." Every team creation is a discovery process, not just file generation.</identity>
113
+ <communication_style>Methodical yet encouraging — like a senior architect pair-programming with a colleague. Asks focused questions, explains trade-offs clearly, and celebrates good decisions. Uses concrete examples from Vortex and Gyre to illustrate patterns. Never dumps all decisions at once — progressive disclosure, one step at a time.</communication_style>
114
+ <principles>- Thinking before files — every team creation goes through discovery before generation - BMAD compliance is non-negotiable — output must be indistinguishable from native teams - No orphaned artifacts — if a file is created, it must be registered, wired, and discoverable - Delegate to BMB for artifact generation — factory owns integration wiring only - Validate continuously — don't wait until the end to check</principles>
115
+ </persona>
116
+ <menu>
117
+ <item cmd="MH or fuzzy match on menu or help">[MH] Redisplay Menu Help</item>
118
+ <item cmd="CH or fuzzy match on chat">[CH] Chat about team architecture, composition patterns, or BMAD compliance</item>
119
+ <item cmd="CT or fuzzy match on create-team or new-team or add-team" exec="{project-root}/_bmad/bme/_team-factory/workflows/step-00-route.md">[CT] Create Team: Build a new BMAD-compliant team from scratch</item>
120
+ <item cmd="RS or fuzzy match on resume" exec="{project-root}/_bmad/bme/_team-factory/workflows/step-00-route.md" data="resume">[RS] Resume: Continue a previously started team creation</item>
121
+ <item cmd="EX or fuzzy match on express" exec="{project-root}/_bmad/bme/_team-factory/workflows/step-00-route.md" data="express">[EX] Express Mode: Create team from an existing spec file</item>
122
+ <item cmd="VT or fuzzy match on validate-team or check-team" exec="{project-root}/_bmad/bme/_team-factory/workflows/add-team/step-05-validate.md">[VT] Validate Team: Run end-to-end validation on an existing team</item>
123
+ <item cmd="AR or fuzzy match on architecture-reference or reference" data="{project-root}/_bmad-output/planning-artifacts/architecture-reference-teams.md">[AR] Architecture Reference: Browse the team validity blueprint</item>
124
+ <item cmd="PM or fuzzy match on party-mode" exec="{project-root}/_bmad/core/workflows/party-mode/workflow.md">[PM] Start Party Mode</item>
125
+ <item cmd="DA or fuzzy match on exit, leave, goodbye or dismiss agent">[DA] Dismiss Agent</item>
126
+ </menu>
127
+ </agent>
128
+ ```
@@ -0,0 +1,13 @@
1
+ submodule_name: _team-factory
2
+ description: Team Factory - Guided creation of BMAD-compliant teams through architectural discovery and automated wiring
3
+ module: bme
4
+ output_folder: '{project-root}/_bmad-output/planning-artifacts'
5
+ agents:
6
+ - team-factory
7
+ workflows:
8
+ - add-team
9
+ version: 1.0.0
10
+ user_name: '{user}'
11
+ communication_language: en
12
+ party_mode_enabled: true
13
+ core_module: bme
@@ -0,0 +1,184 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Pattern-aware decision elimination for the Team Factory.
5
+ * A6'-coupled — adding a third composition pattern = adding a third column to DECISION_CATALOGUE.
6
+ *
7
+ * @module cascade-logic
8
+ */
9
+
10
+ const KNOWN_PATTERNS = ['Independent', 'Sequential'];
11
+
12
+ /**
13
+ * Master catalogue of factory decisions with per-pattern relevance.
14
+ * Each entry defines whether the decision is active for a given pattern
15
+ * and whether it's required or optional.
16
+ *
17
+ * @type {Array<{id: string, step: string, description: string, patterns: Object}>}
18
+ */
19
+ const DECISION_CATALOGUE = [
20
+ {
21
+ id: 'agent-scope',
22
+ step: 'scope',
23
+ description: 'Define agent roles and capabilities',
24
+ patterns: {
25
+ Independent: { active: true, required: true },
26
+ Sequential: { active: true, required: true },
27
+ },
28
+ },
29
+ {
30
+ id: 'pipeline-order',
31
+ step: 'scope',
32
+ description: 'Define agent execution sequence (pipeline positions)',
33
+ patterns: {
34
+ Independent: { active: false, required: false },
35
+ Sequential: { active: true, required: true },
36
+ },
37
+ },
38
+ {
39
+ id: 'handoff-contracts',
40
+ step: 'connect',
41
+ description: 'Design inter-agent handoff contracts',
42
+ patterns: {
43
+ Independent: { active: false, required: false },
44
+ Sequential: { active: true, required: true },
45
+ },
46
+ },
47
+ {
48
+ id: 'feedback-contracts',
49
+ step: 'connect',
50
+ description: 'Design feedback routing contracts (downstream → upstream)',
51
+ patterns: {
52
+ Independent: { active: false, required: false },
53
+ Sequential: { active: true, required: false },
54
+ },
55
+ },
56
+ {
57
+ id: 'contract-prefix',
58
+ step: 'connect',
59
+ description: 'Define naming prefix for contracts (e.g., HC, GC)',
60
+ patterns: {
61
+ Independent: { active: false, required: false },
62
+ Sequential: { active: true, required: true },
63
+ },
64
+ },
65
+ {
66
+ id: 'compass-routing',
67
+ step: 'connect',
68
+ description: 'Create compass routing reference for workflow navigation',
69
+ patterns: {
70
+ Independent: { active: true, required: false, defaultValue: 'per-agent' },
71
+ Sequential: { active: true, required: true, defaultValue: 'shared-reference' },
72
+ },
73
+ },
74
+ {
75
+ id: 'orchestration-workflow',
76
+ step: 'connect',
77
+ description: 'Create pipeline orchestration workflow',
78
+ patterns: {
79
+ Independent: { active: false, required: false },
80
+ Sequential: { active: true, required: true },
81
+ },
82
+ },
83
+ {
84
+ id: 'output-directory',
85
+ step: 'connect',
86
+ description: 'Define artifact output location',
87
+ patterns: {
88
+ Independent: { active: true, required: true, defaultValue: '_bmad-output/{team}-artifacts' },
89
+ Sequential: { active: true, required: true, defaultValue: '_bmad-output/{team}-artifacts' },
90
+ },
91
+ },
92
+ {
93
+ id: 'naming-enforcement',
94
+ step: 'scope',
95
+ description: 'Validate all naming conventions (agent IDs, file names, module directory)',
96
+ patterns: {
97
+ Independent: { active: true, required: true },
98
+ Sequential: { active: true, required: true },
99
+ },
100
+ },
101
+ {
102
+ id: 'overlap-detection',
103
+ step: 'scope',
104
+ description: 'Check proposed agents against existing agent manifest for collisions',
105
+ patterns: {
106
+ Independent: { active: true, required: true },
107
+ Sequential: { active: true, required: true },
108
+ },
109
+ },
110
+ ];
111
+
112
+ /**
113
+ * Given a composition pattern, return which factory decisions are relevant
114
+ * and which are eliminated.
115
+ *
116
+ * @param {string} pattern - "Independent" or "Sequential"
117
+ * @returns {{ decisions: CascadeDecision[], eliminated: CascadeDecision[], error?: string }}
118
+ */
119
+ function getCascadeForPattern(pattern) {
120
+ const validation = validatePattern(pattern);
121
+ if (!validation.valid) {
122
+ return { decisions: [], eliminated: [], error: validation.error };
123
+ }
124
+
125
+ const decisions = [];
126
+ const eliminated = [];
127
+
128
+ for (const entry of DECISION_CATALOGUE) {
129
+ const patternConfig = entry.patterns[pattern];
130
+ const decision = {
131
+ id: entry.id,
132
+ step: entry.step,
133
+ description: entry.description,
134
+ required: patternConfig.required,
135
+ defaultValue: patternConfig.defaultValue || undefined,
136
+ };
137
+
138
+ if (patternConfig.active) {
139
+ decisions.push(decision);
140
+ } else {
141
+ eliminated.push(decision);
142
+ }
143
+ }
144
+
145
+ return { decisions, eliminated };
146
+ }
147
+
148
+ /**
149
+ * Validate that a pattern string is recognized.
150
+ * @param {string} pattern
151
+ * @returns {{ valid: boolean, error?: string }}
152
+ */
153
+ function validatePattern(pattern) {
154
+ if (!pattern || typeof pattern !== 'string') {
155
+ return { valid: false, error: 'Pattern must be a non-empty string' };
156
+ }
157
+ if (!KNOWN_PATTERNS.includes(pattern)) {
158
+ return { valid: false, error: `Unknown pattern "${pattern}". Expected one of: ${KNOWN_PATTERNS.join(', ')}` };
159
+ }
160
+ return { valid: true };
161
+ }
162
+
163
+ /**
164
+ * Get the list of known composition patterns.
165
+ * @returns {string[]}
166
+ */
167
+ function getKnownPatterns() {
168
+ return [...KNOWN_PATTERNS];
169
+ }
170
+
171
+ /**
172
+ * @typedef {Object} CascadeDecision
173
+ * @property {string} id - Decision identifier
174
+ * @property {string} step - Factory step this belongs to (scope, connect, generate, validate)
175
+ * @property {string} description - Human-readable description
176
+ * @property {boolean} required - Whether mandatory for the pattern
177
+ * @property {string} [defaultValue] - Default if applicable
178
+ */
179
+
180
+ module.exports = {
181
+ getCascadeForPattern,
182
+ validatePattern,
183
+ getKnownPatterns,
184
+ };
@@ -0,0 +1,228 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs-extra');
4
+
5
+ /**
6
+ * Detect collisions between a new team spec and existing framework state.
7
+ * Handles Level 1 (exact ID match) and Level 2 (name similarity).
8
+ * Level 3 (capability overlap) is LLM reasoning in the workflow step.
9
+ *
10
+ * @param {Object} specData - Parsed team spec
11
+ * @param {string} manifestPath - Absolute path to agent-manifest.csv
12
+ * @param {string} [bmeDir] - Path to _bmad/bme/ directory for submodule name checks
13
+ * @returns {Promise<CollisionResult>}
14
+ */
15
+ async function detectCollisions(specData, manifestPath, bmeDir) {
16
+ const blocks = [];
17
+ const warnings = [];
18
+
19
+ const dataWarnings = [];
20
+
21
+ // Parse existing agent manifest
22
+ const { agents: existingAgents, warning: manifestWarning } = await parseManifest(manifestPath);
23
+ if (manifestWarning) dataWarnings.push(manifestWarning);
24
+
25
+ // Parse existing submodule directories
26
+ const { modules: existingModules, warning: modulesWarning } = bmeDir
27
+ ? await listSubmodules(bmeDir)
28
+ : { modules: [], warning: null };
29
+ if (modulesWarning) dataWarnings.push(modulesWarning);
30
+
31
+ const proposedTeam = specData.team_name_kebab || '';
32
+ const proposedAgents = (specData.agents || []).map(a => a.id).filter(Boolean);
33
+
34
+ // --- Level 1: Exact submodule name collision ---
35
+ if (proposedTeam && existingModules.includes(`_${proposedTeam}`)) {
36
+ blocks.push({
37
+ level: 'exact',
38
+ field: 'submodule_name',
39
+ newValue: `_${proposedTeam}`,
40
+ existingValue: `_${proposedTeam}`,
41
+ existingModule: proposedTeam,
42
+ suggestion: `Module directory _bmad/bme/_${proposedTeam}/ already exists. Choose a different team name.`,
43
+ });
44
+ }
45
+
46
+ // --- Level 1: Exact agent ID collision ---
47
+ for (const agentId of proposedAgents) {
48
+ const match = existingAgents.find(a => a.id === agentId);
49
+ if (match) {
50
+ blocks.push({
51
+ level: 'exact',
52
+ field: 'agent_id',
53
+ newValue: agentId,
54
+ existingValue: match.id,
55
+ existingModule: match.module,
56
+ suggestion: `Agent ID "${agentId}" already exists in module "${match.module}". Rename your agent.`,
57
+ });
58
+ }
59
+ }
60
+
61
+ // --- Level 2: Similar agent name detection ---
62
+ for (const agentId of proposedAgents) {
63
+ for (const existing of existingAgents) {
64
+ if (existing.id === agentId) continue; // Already caught by Level 1
65
+
66
+ const dist = levenshtein(agentId, existing.id);
67
+ const sharePrefix = sharedPrefix(agentId, existing.id);
68
+
69
+ if (dist <= 2 || (sharePrefix >= 4 && dist <= 3)) {
70
+ warnings.push({
71
+ level: 'similar',
72
+ field: 'agent_id',
73
+ newValue: agentId,
74
+ existingValue: existing.id,
75
+ existingModule: existing.module,
76
+ suggestion: `"${agentId}" is similar to existing "${existing.id}" (${existing.module}). Intentional?`,
77
+ });
78
+ }
79
+ }
80
+ }
81
+
82
+ return {
83
+ blocks,
84
+ warnings,
85
+ dataWarnings,
86
+ hasBlocking: blocks.length > 0,
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Parse agent-manifest.csv to extract agent IDs and their modules.
92
+ * @param {string} manifestPath
93
+ * @returns {Promise<{agents: Array<{id: string, module: string}>, warning: string|null}>}
94
+ */
95
+ async function parseManifest(manifestPath) {
96
+ try {
97
+ const content = await fs.readFile(manifestPath, 'utf8');
98
+ const lines = content.split('\n').filter(l => l.trim());
99
+ if (lines.length < 2) return { agents: [], warning: null };
100
+
101
+ const results = [];
102
+ // Skip header (line 0), parse data rows
103
+ for (let i = 1; i < lines.length; i++) {
104
+ const fields = parseCSVLine(lines[i]);
105
+ if (fields.length >= 10) {
106
+ const id = fields[0].replace(/^"|"$/g, '').trim();
107
+ const module = fields[9].replace(/^"|"$/g, '').trim();
108
+ if (id) results.push({ id, module });
109
+ }
110
+ }
111
+ return { agents: results, warning: null };
112
+ } catch (err) {
113
+ return { agents: [], warning: `Could not read agent manifest at ${manifestPath}: ${err.message}. Collision detection may be incomplete.` };
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Parse a single CSV line handling quoted fields.
119
+ * @param {string} line
120
+ * @returns {string[]}
121
+ */
122
+ function parseCSVLine(line) {
123
+ const fields = [];
124
+ let current = '';
125
+ let inQuotes = false;
126
+
127
+ for (let i = 0; i < line.length; i++) {
128
+ const ch = line[i];
129
+ if (inQuotes) {
130
+ if (ch === '"' && line[i + 1] === '"') {
131
+ current += '"';
132
+ i++; // skip escaped quote
133
+ } else if (ch === '"') {
134
+ inQuotes = false;
135
+ } else {
136
+ current += ch;
137
+ }
138
+ } else {
139
+ if (ch === '"') {
140
+ inQuotes = true;
141
+ } else if (ch === ',') {
142
+ fields.push(current);
143
+ current = '';
144
+ } else {
145
+ current += ch;
146
+ }
147
+ }
148
+ }
149
+ fields.push(current);
150
+ return fields;
151
+ }
152
+
153
+ /**
154
+ * List existing submodule directory names under _bmad/bme/.
155
+ * @param {string} bmeDir
156
+ * @returns {Promise<{modules: string[], warning: string|null}>}
157
+ */
158
+ async function listSubmodules(bmeDir) {
159
+ try {
160
+ const entries = await fs.readdir(bmeDir, { withFileTypes: true });
161
+ const modules = entries
162
+ .filter(e => e.isDirectory() && e.name.startsWith('_'))
163
+ .map(e => e.name);
164
+ return { modules, warning: null };
165
+ } catch (err) {
166
+ return { modules: [], warning: `Could not read bme directory at ${bmeDir}: ${err.message}. Submodule collision detection skipped.` };
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Compute Levenshtein edit distance between two strings.
172
+ * @param {string} a
173
+ * @param {string} b
174
+ * @returns {number}
175
+ */
176
+ function levenshtein(a, b) {
177
+ if (a.length === 0) return b.length;
178
+ if (b.length === 0) return a.length;
179
+
180
+ const matrix = Array.from({ length: b.length + 1 }, (_, i) => [i]);
181
+ for (let j = 0; j <= a.length; j++) matrix[0][j] = j;
182
+
183
+ for (let i = 1; i <= b.length; i++) {
184
+ for (let j = 1; j <= a.length; j++) {
185
+ const cost = a[j - 1] === b[i - 1] ? 0 : 1;
186
+ matrix[i][j] = Math.min(
187
+ matrix[i - 1][j] + 1,
188
+ matrix[i][j - 1] + 1,
189
+ matrix[i - 1][j - 1] + cost,
190
+ );
191
+ }
192
+ }
193
+
194
+ return matrix[b.length][a.length];
195
+ }
196
+
197
+ /**
198
+ * Count shared prefix length between two strings.
199
+ * @param {string} a
200
+ * @param {string} b
201
+ * @returns {number}
202
+ */
203
+ function sharedPrefix(a, b) {
204
+ let i = 0;
205
+ while (i < a.length && i < b.length && a[i] === b[i]) i++;
206
+ return i;
207
+ }
208
+
209
+ /**
210
+ * @typedef {Object} CollisionResult
211
+ * @property {CollisionEntry[]} blocks - Level 1: exact matches, must be resolved
212
+ * @property {CollisionEntry[]} warnings - Level 2: similar names, review recommended
213
+ * @property {boolean} hasBlocking - true if any blocks exist
214
+ */
215
+
216
+ /**
217
+ * @typedef {Object} CollisionEntry
218
+ * @property {string} level - "exact" or "similar"
219
+ * @property {string} field - "agent_id", "submodule_name", or "workflow_name"
220
+ * @property {string} newValue
221
+ * @property {string} existingValue
222
+ * @property {string} existingModule
223
+ * @property {string} suggestion
224
+ */
225
+
226
+ module.exports = {
227
+ detectCollisions,
228
+ };