ma-agents 3.13.2 → 3.14.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 (70) hide show
  1. package/lib/agents.js +7 -0
  2. package/lib/bmad-cache/bmb/.claude-plugin/marketplace.json +3 -3
  3. package/lib/bmad-cache/bmb/_git_preserved/index +0 -0
  4. package/lib/bmad-cache/bmb/_git_preserved/objects/pack/pack-b388526edf307cf56f8e65673466cff074105abf.idx +0 -0
  5. package/lib/bmad-cache/bmb/_git_preserved/objects/pack/{pack-8f8b045fef5af6911495cf3b2a89f1ed75e120f7.pack → pack-b388526edf307cf56f8e65673466cff074105abf.pack} +0 -0
  6. package/lib/bmad-cache/bmb/_git_preserved/objects/pack/pack-b388526edf307cf56f8e65673466cff074105abf.rev +0 -0
  7. package/lib/bmad-cache/bmb/_git_preserved/packed-refs +1 -1
  8. package/lib/bmad-cache/bmb/_git_preserved/refs/heads/main +1 -1
  9. package/lib/bmad-cache/bmb/_git_preserved/shallow +1 -1
  10. package/lib/bmad-cache/bmb/package-lock.json +2 -2
  11. package/lib/bmad-cache/bmb/package.json +1 -1
  12. package/lib/bmad-cache/bmb/samples/bmad-agent-code-coach/scripts/wake.py +78 -0
  13. package/lib/bmad-cache/bmb/samples/bmad-agent-creative-muse/scripts/wake.py +78 -0
  14. package/lib/bmad-cache/bmb/samples/bmad-agent-sentinel/scripts/wake.py +78 -0
  15. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/assets/customize-template.toml +3 -0
  16. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/assets/init-sanctum-template.py +11 -5
  17. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/assets/report-shell.html +1073 -0
  18. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/assets/wake-template.py +78 -0
  19. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/customize.toml +48 -0
  20. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/count_tokens.py +78 -0
  21. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/prepass.py +258 -0
  22. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/process-template.py +20 -0
  23. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/render_report.py +387 -0
  24. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/assets/adapter-claude-code.json +9 -0
  25. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/aggregate_benchmark.py +236 -0
  26. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/memlog.py +197 -0
  27. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/run_evals.py +513 -390
  28. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/run_triggers.py +347 -251
  29. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/tests/test_env_isolation.py +79 -0
  30. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/tests/test_trigger_detection.py +123 -0
  31. package/lib/bmad-cache/bmb/skills/bmad-module-builder/scripts/tests/test-validate-module.py +82 -4
  32. package/lib/bmad-cache/bmb/skills/bmad-module-builder/scripts/validate-module.py +55 -11
  33. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/assets/customize-template.toml +3 -5
  34. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/assets/report-shell.html +860 -0
  35. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/customize.toml +59 -0
  36. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/count_tokens.py +77 -0
  37. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/init_skill.py +132 -0
  38. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/prepass-prompt-metrics.py +186 -221
  39. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/quick_validate.py +117 -0
  40. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/render_report.py +387 -0
  41. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/tests/test_canon_sync.py +46 -0
  42. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/tests/test_count_tokens.py +180 -0
  43. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/tests/test_render_report.py +171 -0
  44. package/lib/bmad-cache/cache-manifest.json +4 -4
  45. package/lib/bmad-extension/.claude-plugin/marketplace.json.template +1 -1
  46. package/lib/bmad-extension-plugin/.claude-plugin/marketplace.json +2 -2
  47. package/lib/bmad.js +6 -1
  48. package/lib/templates/instruction-block-git.template.md +25 -25
  49. package/lib/templates/instruction-block-onprem.template.md +86 -86
  50. package/lib/templates/instruction-block-universal.template.md +29 -29
  51. package/package.json +3 -3
  52. package/lib/.bmad-extension-plugin.build-22612-1782454014750/.claude-plugin/marketplace.json +0 -109
  53. package/lib/.bmad-extension-plugin.build-22612-1782454014750/skills/module-help.csv +0 -62
  54. package/lib/.bmad-extension-plugin.build-22612-1782454014750/skills/module.yaml +0 -20
  55. package/lib/bmad-cache/bmb/_git_preserved/objects/pack/pack-8f8b045fef5af6911495cf3b2a89f1ed75e120f7.idx +0 -0
  56. package/lib/bmad-cache/bmb/_git_preserved/objects/pack/pack-8f8b045fef5af6911495cf3b2a89f1ed75e120f7.rev +0 -0
  57. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/references/sample-init-sanctum.py +0 -274
  58. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/generate-html-report.py +0 -534
  59. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/prepass-execution-deps.py +0 -337
  60. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/prepass-prompt-metrics.py +0 -425
  61. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/prepass-sanctum-architecture.py +0 -385
  62. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/prepass-structure-capabilities.py +0 -482
  63. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/assets/Dockerfile +0 -29
  64. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/docker_setup.py +0 -115
  65. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/generate_report.py +0 -184
  66. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/pty_runner.py +0 -171
  67. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/utils.py +0 -260
  68. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/extract-report-json.py +0 -287
  69. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/generate-html-report.py +0 -588
  70. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/prepass-execution-deps.py +0 -288
@@ -1,287 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Deterministic extraction of report-data.json from analysis outputs.
3
-
4
- Reads scanner outputs (markdown + JSON) and extracts structured data without
5
- LLM synthesis. Ensures no data loss and completes in <10 seconds.
6
-
7
- Usage:
8
- python3 extract-report-json.py {skill-path} {quality-report-dir} -o {output-file}
9
- """
10
-
11
- from __future__ import annotations
12
-
13
- import argparse
14
- import json
15
- import re
16
- import sys
17
- from datetime import datetime, timezone
18
- from pathlib import Path
19
-
20
-
21
- def extract_section(content: str, section_name: str, level: int = 2) -> str | None:
22
- """Extract a section from markdown by heading name."""
23
- pattern = r'^#{' + str(level) + r'}\s+' + re.escape(section_name) + r'\s*\n(.*?)(?=^#{1,' + str(level) + r'}\s|\Z)'
24
- match = re.search(pattern, content, re.MULTILINE | re.DOTALL)
25
- return match.group(1).strip() if match else None
26
-
27
-
28
- def extract_journeys(content: str) -> list[dict]:
29
- """Extract user journey archetypes from enhancement-analysis.md."""
30
- journeys = []
31
- # Match ### N. {Name}: {Description}
32
- pattern = r'^###\s+\d+\.\s+([^:]+):\s+(.+?)(?=^###|\Z)'
33
- for match in re.finditer(pattern, content, re.MULTILINE | re.DOTALL):
34
- name = match.group(1).strip()
35
- section = match.group(2)
36
-
37
- # Extract narrative (after "Narrative." or "Narrative\n")
38
- narrative_match = re.search(r'(?:Narrative[:.]\s*)?([^\n]+(?:\n[^*\n][^\n]*)*?)(?=\n\*\*|\n[A-Z])', section)
39
- summary = narrative_match.group(1).strip() if narrative_match else ""
40
-
41
- # Extract friction points
42
- friction_points = []
43
- friction_section = re.search(r'\*\*Friction points?[:\*]*\*\*\s*\n(.*?)(?=\n\*\*|\n[A-Z]|$)', section, re.DOTALL)
44
- if friction_section:
45
- for line in friction_section.group(1).split('\n'):
46
- line = line.strip()
47
- if line.startswith('- '):
48
- friction_points.append(line[2:].strip())
49
-
50
- # Extract bright spots
51
- bright_spots = []
52
- bright_section = re.search(r'\*\*Bright spots?[:\*]*\*\*\s*\n(.*?)(?=\n\*\*|\n[A-Z]|$)', section, re.DOTALL)
53
- if bright_section:
54
- for line in bright_section.group(1).split('\n'):
55
- line = line.strip()
56
- if line.startswith('- '):
57
- bright_spots.append(line[2:].strip())
58
-
59
- journeys.append({
60
- 'archetype': name,
61
- 'summary': summary,
62
- 'friction_points': friction_points,
63
- 'bright_spots': bright_spots
64
- })
65
-
66
- return journeys
67
-
68
-
69
- def extract_autonomous(content: str) -> dict:
70
- """Extract headless/automation assessment from enhancement-analysis.md."""
71
- assessment_section = extract_section(content, 'Headless Assessment', level=2)
72
- if not assessment_section:
73
- return {}
74
-
75
- # Look for "Current Level:" or "Potential:" pattern
76
- potential_match = re.search(r'(?:Current Level|Potential)[:\*]*\s*([^\n.]+)', assessment_section)
77
- potential = potential_match.group(1).strip() if potential_match else "unknown"
78
-
79
- # Get the rest as notes
80
- notes = assessment_section
81
- if potential_match:
82
- notes = assessment_section[potential_match.end():].strip()
83
-
84
- return {
85
- 'potential': potential,
86
- 'notes': notes[:200] if notes else "" # Truncate to 200 chars
87
- }
88
-
89
-
90
- def extract_findings_from_md(content: str, source_scanner: str) -> list[dict]:
91
- """Extract individual findings from analysis markdown.
92
-
93
- Handles multiple formats:
94
- - Architecture: level 4 headings under severity sections (### HIGH, etc)
95
- - Determinism: bold headings with severity markers [HIGH], [LOW]
96
- - Customization: bold headings with opportunity markers (HIGH-OPPORTUNITY, etc)
97
- - Enhancement: numbered findings with severity/opportunity markers
98
- """
99
- findings = []
100
-
101
- if source_scanner == 'architecture':
102
- # Architecture format: ### SEVERITY followed by #### N. Title
103
- severity_pattern = r'^###\s+(CRITICAL|HIGH|MEDIUM|LOW)\s*$'
104
- severity_sections = re.split(severity_pattern, content, flags=re.MULTILINE)
105
-
106
- for i in range(1, len(severity_sections), 2):
107
- severity = severity_sections[i].lower() if i < len(severity_sections) else "medium"
108
- section_content = severity_sections[i + 1] if i + 1 < len(severity_sections) else ""
109
-
110
- if not section_content.strip() or section_content.strip() == "None":
111
- continue
112
-
113
- # Extract level 4 findings (#### N. Title)
114
- finding_pattern = r'^####\s+(\d+\.\s+)?(.+?)$'
115
- for match in re.finditer(finding_pattern, section_content, re.MULTILINE):
116
- finding_title = match.group(2).strip()
117
- if finding_title:
118
- findings.append({
119
- 'title': finding_title,
120
- 'severity': severity,
121
- 'source': source_scanner
122
- })
123
-
124
- elif source_scanner == 'determinism':
125
- # Determinism format: ### **[SEVERITY] Title**
126
- pattern = r'###\s+\*\*\[([A-Z]+)\]\s+([^*]+)\*\*'
127
- for match in re.finditer(pattern, content, re.MULTILINE):
128
- severity = match.group(1).lower()
129
- title = match.group(2).strip()
130
- if title:
131
- findings.append({
132
- 'title': title,
133
- 'severity': severity,
134
- 'source': source_scanner
135
- })
136
-
137
- elif source_scanner == 'customization':
138
- # Customization format: ### N. **Title** (OPPORTUNITY-TYPE)
139
- pattern = r'###\s+\d+\.\s+\*\*([^*]+)\*\*\s+\(([A-Z-]+)\)'
140
- for match in re.finditer(pattern, content, re.MULTILINE):
141
- title = match.group(1).strip()
142
- opportunity = match.group(2).lower()
143
- # Map opportunity to severity
144
- severity = 'high' if 'high' in opportunity else 'medium' if 'medium' in opportunity else 'low'
145
- if title:
146
- findings.append({
147
- 'title': title,
148
- 'severity': severity,
149
- 'source': source_scanner
150
- })
151
-
152
- elif source_scanner == 'enhancement':
153
- # Enhancement format: ### LEVEL Findings section followed by #### N. Title
154
- # Extract opportunity sections (HIGH-OPPORTUNITY, SECONDARY-OPPORTUNITY, etc)
155
- opportunity_pattern = r'^###\s+([A-Z-]+)\s+(?:Findings|Opportunities?)'
156
- opportunity_sections = re.split(opportunity_pattern, content, flags=re.MULTILINE)
157
-
158
- for i in range(1, len(opportunity_sections), 2):
159
- opportunity = opportunity_sections[i].lower() if i < len(opportunity_sections) else "medium"
160
- section_content = opportunity_sections[i + 1] if i + 1 < len(opportunity_sections) else ""
161
-
162
- if not section_content.strip():
163
- continue
164
-
165
- # Map opportunity to severity
166
- severity = 'high' if 'high' in opportunity else 'medium' if 'secondary' in opportunity else 'low'
167
-
168
- # Extract level 4 findings (#### N. Title)
169
- finding_pattern = r'^####\s+(\d+\.\s+)?(.+?)$'
170
- for match in re.finditer(finding_pattern, section_content, re.MULTILINE):
171
- finding_title = match.group(2).strip()
172
- if finding_title:
173
- findings.append({
174
- 'title': finding_title,
175
- 'severity': severity,
176
- 'source': source_scanner
177
- })
178
-
179
- return findings
180
-
181
-
182
- def merge_prepass_data(report_dir: Path) -> dict:
183
- """Load and merge all prepass JSON data."""
184
- merged = {}
185
-
186
- for json_file in report_dir.glob('*-prepass.json'):
187
- try:
188
- data = json.loads(json_file.read_text(encoding='utf-8'))
189
- merged.update(data)
190
- except Exception:
191
- pass # Skip if not valid JSON
192
-
193
- return merged
194
-
195
-
196
- def build_report_json(skill_path: str, quality_report_dir: str) -> dict:
197
- """Extract and build complete report-data.json."""
198
- report_dir = Path(quality_report_dir)
199
- skill_name = Path(skill_path).name
200
- timestamp = datetime.now(timezone.utc).isoformat()
201
-
202
- # Read all analysis files
203
- architecture_content = (report_dir / 'architecture-analysis.md').read_text(encoding='utf-8') if (report_dir / 'architecture-analysis.md').exists() else ""
204
- determinism_content = (report_dir / 'determinism-analysis.md').read_text(encoding='utf-8') if (report_dir / 'determinism-analysis.md').exists() else ""
205
- customization_content = (report_dir / 'customization-analysis.md').read_text(encoding='utf-8') if (report_dir / 'customization-analysis.md').exists() else ""
206
- enhancement_content = (report_dir / 'enhancement-analysis.md').read_text(encoding='utf-8') if (report_dir / 'enhancement-analysis.md').exists() else ""
207
-
208
- # Extract assessments
209
- arch_assessment = extract_section(architecture_content, 'Assessment', level=2) or ""
210
- det_assessment = extract_section(determinism_content, 'Assessment', level=2) or ""
211
- cust_assessment = extract_section(customization_content, 'Overall Assessment', level=2) or ""
212
- enh_assessment = extract_section(enhancement_content, 'Summary', level=2) or ""
213
-
214
- # Extract journeys and autonomous from enhancement
215
- journeys = extract_journeys(enhancement_content)
216
- autonomous = extract_autonomous(enhancement_content)
217
-
218
- # Build detailed_analysis
219
- detailed_analysis = {
220
- 'architecture': {
221
- 'assessment': arch_assessment[:500], # First 500 chars
222
- 'findings': extract_findings_from_md(architecture_content, 'architecture')
223
- },
224
- 'determinism': {
225
- 'assessment': det_assessment[:500],
226
- 'findings': extract_findings_from_md(determinism_content, 'determinism')
227
- },
228
- 'customization': {
229
- 'assessment': cust_assessment[:500],
230
- 'posture': 'not-opted-in', # From content
231
- 'findings': extract_findings_from_md(customization_content, 'customization')
232
- },
233
- 'enhancement': {
234
- 'assessment': enh_assessment[:500],
235
- 'journeys': journeys,
236
- 'autonomous': autonomous,
237
- 'findings': extract_findings_from_md(enhancement_content, 'enhancement')
238
- }
239
- }
240
-
241
- # Build basic structure - minimal for now, will be expanded by report creator if needed
242
- report_data = {
243
- 'meta': {
244
- 'skill_name': skill_name,
245
- 'skill_path': skill_path,
246
- 'timestamp': timestamp,
247
- 'scanner_count': 4
248
- },
249
- 'narrative': enh_assessment[:150] if enh_assessment else "", # Placeholder
250
- 'grade': 'Good', # Placeholder - report creator sets this
251
- 'broken': [],
252
- 'opportunities': [],
253
- 'strengths': [],
254
- 'recommendations': [],
255
- 'detailed_analysis': detailed_analysis
256
- }
257
-
258
- return report_data
259
-
260
-
261
- def main():
262
- parser = argparse.ArgumentParser(description='Extract report-data.json from analysis outputs')
263
- parser.add_argument('skill_path', help='Path to the skill being analyzed')
264
- parser.add_argument('quality_report_dir', help='Directory with analysis outputs and where to write report')
265
- parser.add_argument('-o', '--output', help='Output file path (default: {quality_report_dir}/report-data.json)')
266
-
267
- args = parser.parse_args()
268
-
269
- output_path = args.output or str(Path(args.quality_report_dir) / 'report-data.json')
270
-
271
- try:
272
- report_json = build_report_json(args.skill_path, args.quality_report_dir)
273
-
274
- # Write output
275
- output_file = Path(output_path)
276
- output_file.write_text(json.dumps(report_json, indent=2, ensure_ascii=False), encoding='utf-8')
277
-
278
- print(f'Report JSON written to {output_path}', file=sys.stderr)
279
- print(json.dumps({'status': 'success', 'output': output_path}, indent=2))
280
-
281
- except Exception as e:
282
- print(f'Error: {e}', file=sys.stderr)
283
- sys.exit(1)
284
-
285
-
286
- if __name__ == '__main__':
287
- main()