ma-agents 3.13.2 → 3.14.1

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 (107) hide show
  1. package/README.md +3 -3
  2. package/lib/agents.js +9 -2
  3. package/lib/bmad-cache/bmb/.claude-plugin/marketplace.json +3 -3
  4. package/lib/bmad-cache/bmb/_git_preserved/index +0 -0
  5. package/lib/bmad-cache/bmb/_git_preserved/objects/pack/pack-b388526edf307cf56f8e65673466cff074105abf.idx +0 -0
  6. package/lib/bmad-cache/bmb/_git_preserved/objects/pack/{pack-8f8b045fef5af6911495cf3b2a89f1ed75e120f7.pack → pack-b388526edf307cf56f8e65673466cff074105abf.pack} +0 -0
  7. package/lib/bmad-cache/bmb/_git_preserved/objects/pack/pack-b388526edf307cf56f8e65673466cff074105abf.rev +0 -0
  8. package/lib/bmad-cache/bmb/_git_preserved/packed-refs +1 -1
  9. package/lib/bmad-cache/bmb/_git_preserved/refs/heads/main +1 -1
  10. package/lib/bmad-cache/bmb/_git_preserved/shallow +1 -1
  11. package/lib/bmad-cache/bmb/package-lock.json +2 -2
  12. package/lib/bmad-cache/bmb/package.json +1 -1
  13. package/lib/bmad-cache/bmb/samples/bmad-agent-code-coach/scripts/wake.py +78 -0
  14. package/lib/bmad-cache/bmb/samples/bmad-agent-creative-muse/scripts/wake.py +78 -0
  15. package/lib/bmad-cache/bmb/samples/bmad-agent-sentinel/scripts/wake.py +78 -0
  16. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/assets/customize-template.toml +3 -0
  17. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/assets/init-sanctum-template.py +11 -5
  18. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/assets/report-shell.html +1073 -0
  19. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/assets/wake-template.py +78 -0
  20. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/customize.toml +48 -0
  21. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/count_tokens.py +78 -0
  22. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/prepass.py +258 -0
  23. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/process-template.py +20 -0
  24. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/render_report.py +387 -0
  25. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/assets/adapter-claude-code.json +9 -0
  26. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/aggregate_benchmark.py +236 -0
  27. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/memlog.py +197 -0
  28. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/run_evals.py +513 -390
  29. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/run_triggers.py +347 -251
  30. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/tests/test_env_isolation.py +79 -0
  31. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/tests/test_trigger_detection.py +123 -0
  32. package/lib/bmad-cache/bmb/skills/bmad-module-builder/scripts/tests/test-validate-module.py +82 -4
  33. package/lib/bmad-cache/bmb/skills/bmad-module-builder/scripts/validate-module.py +55 -11
  34. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/assets/customize-template.toml +3 -5
  35. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/assets/report-shell.html +860 -0
  36. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/customize.toml +59 -0
  37. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/count_tokens.py +77 -0
  38. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/init_skill.py +132 -0
  39. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/prepass-prompt-metrics.py +186 -221
  40. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/quick_validate.py +117 -0
  41. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/render_report.py +387 -0
  42. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/tests/test_canon_sync.py +46 -0
  43. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/tests/test_count_tokens.py +180 -0
  44. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/tests/test_render_report.py +171 -0
  45. package/lib/bmad-cache/cache-manifest.json +4 -4
  46. package/lib/bmad-customize/bmm-qa.customize.yaml +3 -3
  47. package/lib/bmad-extension/.claude-plugin/marketplace.json.template +3 -2
  48. package/lib/bmad-extension/skills/bmad-dev-epic/SKILL.md +101 -0
  49. package/lib/bmad-extension/skills/bmad-dev-epic/bmad-skill-manifest.yaml +3 -0
  50. package/lib/bmad-extension/skills/bmad-dev-epic/checklist.md +44 -0
  51. package/lib/bmad-extension/skills/bmad-dev-epic/customize.toml +54 -0
  52. package/lib/bmad-extension/skills/bmad-dev-epic/references/story-pipeline.md +112 -0
  53. package/lib/bmad-extension/skills/bmad-dev-epic/steps/step-01-intake.md +59 -0
  54. package/lib/bmad-extension/skills/bmad-dev-epic/steps/step-02-plan-waves.md +56 -0
  55. package/lib/bmad-extension/skills/bmad-dev-epic/steps/step-03-epic-branch.md +35 -0
  56. package/lib/bmad-extension/skills/bmad-dev-epic/steps/step-04-run-waves.md +49 -0
  57. package/lib/bmad-extension/skills/bmad-dev-epic/steps/step-05-epic-adversarial.md +31 -0
  58. package/lib/bmad-extension/skills/bmad-dev-epic/steps/step-06-release.md +57 -0
  59. package/lib/bmad-extension/skills/bmad-dev-epic/steps/step-07-report.md +40 -0
  60. package/lib/bmad-extension/skills/ma-agent-sqa/SKILL.md +3 -3
  61. package/lib/bmad-extension/skills/ma-agent-sqa/bmad-skill-manifest.yaml +1 -1
  62. package/lib/bmad-extension/skills/module-help.csv +2 -1
  63. package/lib/bmad-extension/skills/module.yaml +1 -1
  64. package/lib/bmad-extension/skills/sqa-audit/SKILL.md +2 -2
  65. package/lib/bmad-extension/skills/sqa-ieee12207/SKILL.md +3 -3
  66. package/lib/bmad-extension/skills/sqa-requirements-quality/SKILL.md +2 -2
  67. package/lib/bmad-extension-plugin/.claude-plugin/marketplace.json +4 -3
  68. package/lib/bmad-extension-plugin/skills/bmad-dev-epic/SKILL.md +101 -0
  69. package/lib/bmad-extension-plugin/skills/bmad-dev-epic/bmad-skill-manifest.yaml +3 -0
  70. package/lib/bmad-extension-plugin/skills/bmad-dev-epic/checklist.md +44 -0
  71. package/lib/bmad-extension-plugin/skills/bmad-dev-epic/customize.toml +54 -0
  72. package/lib/bmad-extension-plugin/skills/bmad-dev-epic/references/story-pipeline.md +112 -0
  73. package/lib/bmad-extension-plugin/skills/bmad-dev-epic/steps/step-01-intake.md +59 -0
  74. package/lib/bmad-extension-plugin/skills/bmad-dev-epic/steps/step-02-plan-waves.md +56 -0
  75. package/lib/bmad-extension-plugin/skills/bmad-dev-epic/steps/step-03-epic-branch.md +35 -0
  76. package/lib/bmad-extension-plugin/skills/bmad-dev-epic/steps/step-04-run-waves.md +49 -0
  77. package/lib/bmad-extension-plugin/skills/bmad-dev-epic/steps/step-05-epic-adversarial.md +31 -0
  78. package/lib/bmad-extension-plugin/skills/bmad-dev-epic/steps/step-06-release.md +57 -0
  79. package/lib/bmad-extension-plugin/skills/bmad-dev-epic/steps/step-07-report.md +40 -0
  80. package/lib/bmad-extension-plugin/skills/ma-agent-sqa/SKILL.md +3 -3
  81. package/lib/bmad-extension-plugin/skills/ma-agent-sqa/bmad-skill-manifest.yaml +1 -1
  82. package/lib/bmad-extension-plugin/skills/module-help.csv +2 -1
  83. package/lib/bmad-extension-plugin/skills/module.yaml +1 -1
  84. package/lib/bmad-extension-plugin/skills/sqa-audit/SKILL.md +2 -2
  85. package/lib/bmad-extension-plugin/skills/sqa-ieee12207/SKILL.md +3 -3
  86. package/lib/bmad-extension-plugin/skills/sqa-requirements-quality/SKILL.md +2 -2
  87. package/lib/bmad.js +6 -1
  88. package/package.json +3 -3
  89. package/lib/.bmad-extension-plugin.build-22612-1782454014750/.claude-plugin/marketplace.json +0 -109
  90. package/lib/.bmad-extension-plugin.build-22612-1782454014750/skills/module-help.csv +0 -62
  91. package/lib/.bmad-extension-plugin.build-22612-1782454014750/skills/module.yaml +0 -20
  92. package/lib/bmad-cache/bmb/_git_preserved/objects/pack/pack-8f8b045fef5af6911495cf3b2a89f1ed75e120f7.idx +0 -0
  93. package/lib/bmad-cache/bmb/_git_preserved/objects/pack/pack-8f8b045fef5af6911495cf3b2a89f1ed75e120f7.rev +0 -0
  94. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/references/sample-init-sanctum.py +0 -274
  95. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/generate-html-report.py +0 -534
  96. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/prepass-execution-deps.py +0 -337
  97. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/prepass-prompt-metrics.py +0 -425
  98. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/prepass-sanctum-architecture.py +0 -385
  99. package/lib/bmad-cache/bmb/skills/bmad-agent-builder/scripts/prepass-structure-capabilities.py +0 -482
  100. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/assets/Dockerfile +0 -29
  101. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/docker_setup.py +0 -115
  102. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/generate_report.py +0 -184
  103. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/pty_runner.py +0 -171
  104. package/lib/bmad-cache/bmb/skills/bmad-eval-runner/scripts/utils.py +0 -260
  105. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/extract-report-json.py +0 -287
  106. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/generate-html-report.py +0 -588
  107. package/lib/bmad-cache/bmb/skills/bmad-workflow-builder/scripts/prepass-execution-deps.py +0 -288
@@ -1,337 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Deterministic pre-pass for execution efficiency scanner (agent builder).
3
-
4
- Extracts dependency graph data and execution patterns from a BMad agent skill
5
- so the LLM scanner can evaluate efficiency from compact structured data.
6
-
7
- Covers:
8
- - Dependency graph from skill structure
9
- - Circular dependency detection
10
- - Transitive dependency redundancy
11
- - Parallelizable stage groups (independent nodes)
12
- - Sequential pattern detection in prompts (numbered Read/Grep/Glob steps)
13
- - Subagent-from-subagent detection
14
- - Loop patterns (read all, analyze each, for each file)
15
- - Memory loading pattern detection (load all memory, read all memory, etc.)
16
- - Multi-source operation detection
17
- """
18
-
19
- # /// script
20
- # requires-python = ">=3.9"
21
- # ///
22
-
23
- from __future__ import annotations
24
-
25
- import argparse
26
- import json
27
- import re
28
- import sys
29
- from datetime import datetime, timezone
30
- from pathlib import Path
31
-
32
-
33
- def detect_cycles(graph: dict[str, list[str]]) -> list[list[str]]:
34
- """Detect circular dependencies in a directed graph using DFS."""
35
- cycles = []
36
- visited = set()
37
- path = []
38
- path_set = set()
39
-
40
- def dfs(node: str) -> None:
41
- if node in path_set:
42
- cycle_start = path.index(node)
43
- cycles.append(path[cycle_start:] + [node])
44
- return
45
- if node in visited:
46
- return
47
- visited.add(node)
48
- path.append(node)
49
- path_set.add(node)
50
- for neighbor in graph.get(node, []):
51
- dfs(neighbor)
52
- path.pop()
53
- path_set.discard(node)
54
-
55
- for node in graph:
56
- dfs(node)
57
-
58
- return cycles
59
-
60
-
61
- def find_transitive_redundancy(graph: dict[str, list[str]]) -> list[dict]:
62
- """Find cases where A declares dependency on C, but A->B->C already exists."""
63
- redundancies = []
64
-
65
- def get_transitive(node: str, visited: set | None = None) -> set[str]:
66
- if visited is None:
67
- visited = set()
68
- for dep in graph.get(node, []):
69
- if dep not in visited:
70
- visited.add(dep)
71
- get_transitive(dep, visited)
72
- return visited
73
-
74
- for node, direct_deps in graph.items():
75
- for dep in direct_deps:
76
- # Check if dep is reachable through other direct deps
77
- other_deps = [d for d in direct_deps if d != dep]
78
- for other in other_deps:
79
- transitive = get_transitive(other)
80
- if dep in transitive:
81
- redundancies.append({
82
- 'node': node,
83
- 'redundant_dep': dep,
84
- 'already_via': other,
85
- 'issue': f'"{node}" declares "{dep}" as dependency, but already reachable via "{other}"',
86
- })
87
-
88
- return redundancies
89
-
90
-
91
- def find_parallel_groups(graph: dict[str, list[str]], all_nodes: set[str]) -> list[list[str]]:
92
- """Find groups of nodes that have no dependencies on each other (can run in parallel)."""
93
- independent_groups = []
94
-
95
- # Simple approach: find all nodes at each "level" of the DAG
96
- remaining = set(all_nodes)
97
- while remaining:
98
- # Nodes whose dependencies are all satisfied (not in remaining)
99
- ready = set()
100
- for node in remaining:
101
- deps = set(graph.get(node, []))
102
- if not deps & remaining:
103
- ready.add(node)
104
- if not ready:
105
- break # Circular dependency, can't proceed
106
- if len(ready) > 1:
107
- independent_groups.append(sorted(ready))
108
- remaining -= ready
109
-
110
- return independent_groups
111
-
112
-
113
- def scan_sequential_patterns(filepath: Path, rel_path: str) -> list[dict]:
114
- """Detect sequential operation patterns that could be parallel."""
115
- content = filepath.read_text(encoding='utf-8')
116
- patterns = []
117
-
118
- # Sequential numbered steps with Read/Grep/Glob
119
- tool_steps = re.findall(
120
- r'^\s*\d+\.\s+.*?\b(Read|Grep|Glob|read|grep|glob)\b.*$',
121
- content, re.MULTILINE
122
- )
123
- if len(tool_steps) >= 3:
124
- patterns.append({
125
- 'file': rel_path,
126
- 'type': 'sequential-tool-calls',
127
- 'count': len(tool_steps),
128
- 'issue': f'{len(tool_steps)} sequential tool call steps found — check if independent calls can be parallel',
129
- })
130
-
131
- # "Read all files" / "for each" loop patterns
132
- loop_patterns = [
133
- (r'[Rr]ead all (?:files|documents|prompts)', 'read-all'),
134
- (r'[Ff]or each (?:file|document|prompt|stage)', 'for-each-loop'),
135
- (r'[Aa]nalyze each', 'analyze-each'),
136
- (r'[Ss]can (?:through|all|each)', 'scan-all'),
137
- (r'[Rr]eview (?:all|each)', 'review-all'),
138
- ]
139
- for pattern, ptype in loop_patterns:
140
- matches = re.findall(pattern, content)
141
- if matches:
142
- patterns.append({
143
- 'file': rel_path,
144
- 'type': ptype,
145
- 'count': len(matches),
146
- 'issue': f'"{matches[0]}" pattern found — consider parallel subagent delegation',
147
- })
148
-
149
- # Memory loading patterns (agent-specific)
150
- memory_loading_patterns = [
151
- (r'[Ll]oad all (?:memory|memories)', 'load-all-memory'),
152
- (r'[Rr]ead all (?:memory|agent memory) (?:files|data)', 'read-all-memory'),
153
- (r'[Ll]oad (?:entire|full|complete) (?:memory|agent memory)', 'load-entire-memory'),
154
- (r'[Ll]oad all (?:context|state)', 'load-all-context'),
155
- (r'[Rr]ead (?:entire|full|complete) memory', 'read-entire-memory'),
156
- ]
157
- for pattern, ptype in memory_loading_patterns:
158
- matches = re.findall(pattern, content)
159
- if matches:
160
- patterns.append({
161
- 'file': rel_path,
162
- 'type': ptype,
163
- 'count': len(matches),
164
- 'issue': f'"{matches[0]}" pattern found — bulk memory loading is expensive, load specific paths',
165
- })
166
-
167
- # Multi-source operation detection (agent-specific)
168
- multi_source_patterns = [
169
- (r'[Rr]ead all\b', 'multi-source-read-all'),
170
- (r'[Aa]nalyze each\b', 'multi-source-analyze-each'),
171
- (r'[Ff]or each file\b', 'multi-source-for-each-file'),
172
- ]
173
- for pattern, ptype in multi_source_patterns:
174
- matches = re.findall(pattern, content)
175
- if matches:
176
- # Only add if not already captured by loop_patterns above
177
- existing_types = {p['type'] for p in patterns}
178
- if ptype not in existing_types:
179
- patterns.append({
180
- 'file': rel_path,
181
- 'type': ptype,
182
- 'count': len(matches),
183
- 'issue': f'"{matches[0]}" pattern found — multi-source operation may be parallelizable',
184
- })
185
-
186
- # Subagent spawning from subagent (impossible)
187
- if re.search(r'(?i)spawn.*subagent|launch.*subagent|create.*subagent', content):
188
- # Check if this file IS a subagent (quality-scan-* or report-* files at root)
189
- if re.match(r'(?:quality-scan-|report-)', rel_path):
190
- patterns.append({
191
- 'file': rel_path,
192
- 'type': 'subagent-chain-violation',
193
- 'count': 1,
194
- 'issue': 'Subagent file references spawning other subagents — subagents cannot spawn subagents',
195
- })
196
-
197
- return patterns
198
-
199
-
200
- def scan_execution_deps(skill_path: Path) -> dict:
201
- """Run all deterministic execution efficiency checks."""
202
- # Build dependency graph from skill structure
203
- dep_graph: dict[str, list[str]] = {}
204
- prefer_after: dict[str, list[str]] = {}
205
- all_stages: set[str] = set()
206
-
207
- # Check for stage definitions in prompt files
208
- prompts_dir = skill_path / 'prompts'
209
- if prompts_dir.exists():
210
- for f in sorted(prompts_dir.iterdir()):
211
- if f.is_file() and f.suffix == '.md':
212
- all_stages.add(f.stem)
213
-
214
- # Cycle detection
215
- cycles = detect_cycles(dep_graph)
216
-
217
- # Transitive redundancy
218
- redundancies = find_transitive_redundancy(dep_graph)
219
-
220
- # Parallel groups
221
- parallel_groups = find_parallel_groups(dep_graph, all_stages)
222
-
223
- # Sequential pattern detection across all prompt and agent files
224
- sequential_patterns = []
225
- for scan_dir in ['prompts', 'agents']:
226
- d = skill_path / scan_dir
227
- if d.exists():
228
- for f in sorted(d.iterdir()):
229
- if f.is_file() and f.suffix == '.md':
230
- patterns = scan_sequential_patterns(f, f'{scan_dir}/{f.name}')
231
- sequential_patterns.extend(patterns)
232
-
233
- # Also scan SKILL.md
234
- skill_md = skill_path / 'SKILL.md'
235
- if skill_md.exists():
236
- sequential_patterns.extend(scan_sequential_patterns(skill_md, 'SKILL.md'))
237
-
238
- # Build issues from deterministic findings
239
- issues = []
240
- for cycle in cycles:
241
- issues.append({
242
- 'severity': 'critical',
243
- 'category': 'circular-dependency',
244
- 'issue': f'Circular dependency detected: {" → ".join(cycle)}',
245
- })
246
- for r in redundancies:
247
- issues.append({
248
- 'severity': 'medium',
249
- 'category': 'dependency-bloat',
250
- 'issue': r['issue'],
251
- })
252
- for p in sequential_patterns:
253
- if p['type'] == 'subagent-chain-violation':
254
- severity = 'critical'
255
- elif p['type'] in ('load-all-memory', 'read-all-memory', 'load-entire-memory',
256
- 'load-all-context', 'read-entire-memory'):
257
- severity = 'high'
258
- else:
259
- severity = 'medium'
260
- issues.append({
261
- 'file': p['file'],
262
- 'severity': severity,
263
- 'category': p['type'],
264
- 'issue': p['issue'],
265
- })
266
-
267
- by_severity = {'critical': 0, 'high': 0, 'medium': 0, 'low': 0}
268
- for issue in issues:
269
- sev = issue['severity']
270
- if sev in by_severity:
271
- by_severity[sev] += 1
272
-
273
- status = 'pass'
274
- if by_severity['critical'] > 0:
275
- status = 'fail'
276
- elif by_severity['high'] > 0 or by_severity['medium'] > 0:
277
- status = 'warning'
278
-
279
- return {
280
- 'scanner': 'execution-efficiency-prepass',
281
- 'script': 'prepass-execution-deps.py',
282
- 'version': '1.0.0',
283
- 'skill_path': str(skill_path),
284
- 'timestamp': datetime.now(timezone.utc).isoformat(),
285
- 'status': status,
286
- 'dependency_graph': {
287
- 'stages': sorted(all_stages),
288
- 'hard_dependencies': dep_graph,
289
- 'soft_dependencies': prefer_after,
290
- 'cycles': cycles,
291
- 'transitive_redundancies': redundancies,
292
- 'parallel_groups': parallel_groups,
293
- },
294
- 'sequential_patterns': sequential_patterns,
295
- 'issues': issues,
296
- 'summary': {
297
- 'total_issues': len(issues),
298
- 'by_severity': by_severity,
299
- },
300
- }
301
-
302
-
303
- def main() -> int:
304
- parser = argparse.ArgumentParser(
305
- description='Extract execution dependency graph and patterns for LLM scanner pre-pass (agent builder)',
306
- )
307
- parser.add_argument(
308
- 'skill_path',
309
- type=Path,
310
- help='Path to the skill directory to scan',
311
- )
312
- parser.add_argument(
313
- '--output', '-o',
314
- type=Path,
315
- help='Write JSON output to file instead of stdout',
316
- )
317
- args = parser.parse_args()
318
-
319
- if not args.skill_path.is_dir():
320
- print(f"Error: {args.skill_path} is not a directory", file=sys.stderr)
321
- return 2
322
-
323
- result = scan_execution_deps(args.skill_path)
324
- output = json.dumps(result, indent=2)
325
-
326
- if args.output:
327
- args.output.parent.mkdir(parents=True, exist_ok=True)
328
- args.output.write_text(output)
329
- print(f"Results written to {args.output}", file=sys.stderr)
330
- else:
331
- print(output)
332
-
333
- return 0
334
-
335
-
336
- if __name__ == '__main__':
337
- sys.exit(main())