@pjmendonca/devflow 1.9.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 (124) hide show
  1. package/CHANGELOG.md +526 -0
  2. package/LICENSE +21 -0
  3. package/README.md +620 -0
  4. package/bin/devflow-checkpoint.js +10 -0
  5. package/bin/devflow-collab.js +10 -0
  6. package/bin/devflow-cost.js +10 -0
  7. package/bin/devflow-create-persona.js +10 -0
  8. package/bin/devflow-init.js +10 -0
  9. package/bin/devflow-memory.js +10 -0
  10. package/bin/devflow-new-doc.js +10 -0
  11. package/bin/devflow-personalize.js +10 -0
  12. package/bin/devflow-setup-checkpoint.js +10 -0
  13. package/bin/devflow-story.js +10 -0
  14. package/bin/devflow-tech-debt.js +10 -0
  15. package/bin/devflow-validate-overrides.js +10 -0
  16. package/bin/devflow-validate.js +10 -0
  17. package/bin/devflow-version.js +10 -0
  18. package/lib/constants.js +30 -0
  19. package/lib/exec-python.js +78 -0
  20. package/lib/python-check.js +178 -0
  21. package/package.json +64 -0
  22. package/tooling/.automation/agents/architect.md +135 -0
  23. package/tooling/.automation/agents/ba.md +70 -0
  24. package/tooling/.automation/agents/dev.md +79 -0
  25. package/tooling/.automation/agents/maintainer.md +97 -0
  26. package/tooling/.automation/agents/pm.md +116 -0
  27. package/tooling/.automation/agents/reviewer.md +141 -0
  28. package/tooling/.automation/agents/sm.md +61 -0
  29. package/tooling/.automation/agents/writer.md +193 -0
  30. package/tooling/.automation/config.ps1.template +61 -0
  31. package/tooling/.automation/config.sh.template +48 -0
  32. package/tooling/.automation/memory/.gitkeep +6 -0
  33. package/tooling/.automation/memory/knowledge/kg_integration-test.json +94 -0
  34. package/tooling/.automation/memory/knowledge/kg_test-story.json +300 -0
  35. package/tooling/.automation/memory/shared/shared_integration-test.json +30 -0
  36. package/tooling/.automation/memory/shared/shared_test-story.json +78 -0
  37. package/tooling/.automation/overrides/templates/README.md +113 -0
  38. package/tooling/.automation/overrides/templates/architect/README.md +27 -0
  39. package/tooling/.automation/overrides/templates/architect/cloud-native.yaml +92 -0
  40. package/tooling/.automation/overrides/templates/architect/enterprise-architect.yaml +85 -0
  41. package/tooling/.automation/overrides/templates/architect/pragmatic-minimalist.yaml +88 -0
  42. package/tooling/.automation/overrides/templates/ba/README.md +27 -0
  43. package/tooling/.automation/overrides/templates/ba/agile-storyteller.yaml +86 -0
  44. package/tooling/.automation/overrides/templates/ba/domain-expert.yaml +91 -0
  45. package/tooling/.automation/overrides/templates/ba/requirements-engineer.yaml +89 -0
  46. package/tooling/.automation/overrides/templates/dev/README.md +32 -0
  47. package/tooling/.automation/overrides/templates/dev/junior-mentored.yaml +39 -0
  48. package/tooling/.automation/overrides/templates/dev/performance-engineer.yaml +43 -0
  49. package/tooling/.automation/overrides/templates/dev/rapid-prototyper.yaml +52 -0
  50. package/tooling/.automation/overrides/templates/dev/security-focused.yaml +43 -0
  51. package/tooling/.automation/overrides/templates/dev/senior-fullstack.yaml +39 -0
  52. package/tooling/.automation/overrides/templates/maintainer/README.md +27 -0
  53. package/tooling/.automation/overrides/templates/maintainer/devops-maintainer.yaml +113 -0
  54. package/tooling/.automation/overrides/templates/maintainer/legacy-steward.yaml +94 -0
  55. package/tooling/.automation/overrides/templates/maintainer/oss-maintainer.yaml +94 -0
  56. package/tooling/.automation/overrides/templates/pm/README.md +27 -0
  57. package/tooling/.automation/overrides/templates/pm/agile-pm.yaml +91 -0
  58. package/tooling/.automation/overrides/templates/pm/hybrid-delivery.yaml +87 -0
  59. package/tooling/.automation/overrides/templates/pm/traditional-pm.yaml +91 -0
  60. package/tooling/.automation/overrides/templates/reviewer/README.md +11 -0
  61. package/tooling/.automation/overrides/templates/reviewer/mentoring-reviewer.yaml +45 -0
  62. package/tooling/.automation/overrides/templates/reviewer/quick-sanity.yaml +50 -0
  63. package/tooling/.automation/overrides/templates/reviewer/thorough-critic.yaml +48 -0
  64. package/tooling/.automation/overrides/templates/sm/README.md +11 -0
  65. package/tooling/.automation/overrides/templates/sm/agile-coach.yaml +52 -0
  66. package/tooling/.automation/overrides/templates/sm/startup-pm.yaml +50 -0
  67. package/tooling/.automation/overrides/templates/sm/technical-lead.yaml +47 -0
  68. package/tooling/.automation/overrides/templates/user-profile.template.yaml +62 -0
  69. package/tooling/.automation/overrides/templates/writer/README.md +27 -0
  70. package/tooling/.automation/overrides/templates/writer/api-documentarian.yaml +99 -0
  71. package/tooling/.automation/overrides/templates/writer/docs-as-code.yaml +108 -0
  72. package/tooling/.automation/overrides/templates/writer/user-guide-author.yaml +100 -0
  73. package/tooling/completions/DevflowCompletion.ps1 +213 -0
  74. package/tooling/completions/_run-story +116 -0
  75. package/tooling/completions/run-story-completion.bash +136 -0
  76. package/tooling/docs/DOC-STANDARD.md +717 -0
  77. package/tooling/docs/sprint-status.yaml.template +24 -0
  78. package/tooling/docs/templates/bug-report.md +234 -0
  79. package/tooling/docs/templates/migration-spec.md +274 -0
  80. package/tooling/docs/templates/refactor-spec.md +86 -0
  81. package/tooling/docs/templates/tech-debt.md +86 -0
  82. package/tooling/scripts/context_checkpoint.py +556 -0
  83. package/tooling/scripts/cost_dashboard.py +617 -0
  84. package/tooling/scripts/create-persona.py +690 -0
  85. package/tooling/scripts/create-persona.sh +435 -0
  86. package/tooling/scripts/init-project-workflow.ps1 +651 -0
  87. package/tooling/scripts/init-project-workflow.py +70 -0
  88. package/tooling/scripts/init-project-workflow.sh +746 -0
  89. package/tooling/scripts/lib/__init__.py +35 -0
  90. package/tooling/scripts/lib/agent_handoff.py +526 -0
  91. package/tooling/scripts/lib/agent_router.py +698 -0
  92. package/tooling/scripts/lib/checkpoint-integration.ps1 +245 -0
  93. package/tooling/scripts/lib/checkpoint-integration.sh +191 -0
  94. package/tooling/scripts/lib/claude-cli.ps1 +952 -0
  95. package/tooling/scripts/lib/claude-cli.sh +1293 -0
  96. package/tooling/scripts/lib/cost_config.py +222 -0
  97. package/tooling/scripts/lib/cost_display.py +443 -0
  98. package/tooling/scripts/lib/cost_tracker.py +710 -0
  99. package/tooling/scripts/lib/currency_converter.py +328 -0
  100. package/tooling/scripts/lib/errors.py +438 -0
  101. package/tooling/scripts/lib/override-loader.sh +286 -0
  102. package/tooling/scripts/lib/pair_programming.py +589 -0
  103. package/tooling/scripts/lib/shared_memory.py +637 -0
  104. package/tooling/scripts/lib/swarm_orchestrator.py +689 -0
  105. package/tooling/scripts/memory_summarize.py +324 -0
  106. package/tooling/scripts/new-doc.ps1 +405 -0
  107. package/tooling/scripts/new-doc.py +93 -0
  108. package/tooling/scripts/new-doc.sh +534 -0
  109. package/tooling/scripts/personalize_agent.py +385 -0
  110. package/tooling/scripts/rollback-migration.sh +540 -0
  111. package/tooling/scripts/run-collab.ps1 +251 -0
  112. package/tooling/scripts/run-collab.py +605 -0
  113. package/tooling/scripts/run-collab.sh +110 -0
  114. package/tooling/scripts/run-story.ps1 +490 -0
  115. package/tooling/scripts/run-story.py +387 -0
  116. package/tooling/scripts/run-story.sh +467 -0
  117. package/tooling/scripts/setup-checkpoint-service.ps1 +219 -0
  118. package/tooling/scripts/setup-checkpoint-service.py +87 -0
  119. package/tooling/scripts/setup-checkpoint-service.sh +236 -0
  120. package/tooling/scripts/tech-debt-tracker.py +608 -0
  121. package/tooling/scripts/update_version.py +244 -0
  122. package/tooling/scripts/validate-overrides.py +511 -0
  123. package/tooling/scripts/validate-overrides.sh +432 -0
  124. package/tooling/scripts/validate_setup.py +539 -0
@@ -0,0 +1,511 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Validate Overrides - Override YAML Validation and Linting
4
+
5
+ This script validates all override YAML files in the overrides directory.
6
+ It checks for:
7
+ - Valid YAML syntax
8
+ - Required fields
9
+ - Valid field types
10
+ - Valid model names
11
+ - Valid budget ranges
12
+ - Schema compliance
13
+
14
+ Usage:
15
+ python validate-overrides.py # Validate all overrides
16
+ python validate-overrides.py dev # Validate specific agent override
17
+ python validate-overrides.py --fix # Auto-fix common issues
18
+ python validate-overrides.py --verbose # Show detailed output
19
+ python validate-overrides.py --json # Output results as JSON
20
+ """
21
+
22
+ import argparse
23
+ import json
24
+ import os
25
+ import re
26
+ import sys
27
+ from dataclasses import dataclass, field
28
+ from pathlib import Path
29
+ from typing import Any
30
+
31
+
32
+ # Colors for terminal output
33
+ class Colors:
34
+ RED = "\033[0;31m"
35
+ GREEN = "\033[0;32m"
36
+ YELLOW = "\033[1;33m"
37
+ BLUE = "\033[0;34m"
38
+ CYAN = "\033[0;36m"
39
+ NC = "\033[0m" # No Color
40
+
41
+
42
+ # Valid values
43
+ VALID_MODELS = ["sonnet", "opus", "haiku"]
44
+ MIN_BUDGET = 0.01
45
+ MAX_BUDGET = 100.00
46
+ VALID_TECH_LEVELS = ["beginner", "intermediate", "advanced", "expert"]
47
+
48
+ # Schema definitions
49
+ OVERRIDE_SCHEMA = {
50
+ "persona": {
51
+ "type": "object",
52
+ "properties": {
53
+ "role": {"type": "string"},
54
+ "identity": {"type": "string"},
55
+ "communication_style": {"type": "string"},
56
+ "principles": {"type": "list"},
57
+ },
58
+ },
59
+ "additional_rules": {"type": "list"},
60
+ "memories": {"type": "list"},
61
+ "critical_actions": {"type": "list"},
62
+ "model": {"type": "string", "enum": VALID_MODELS},
63
+ "max_budget_usd": {"type": "number", "min": MIN_BUDGET, "max": MAX_BUDGET},
64
+ "tools": {"type": "string"},
65
+ }
66
+
67
+ USER_PROFILE_SCHEMA = {
68
+ "user": {
69
+ "type": "object",
70
+ "properties": {
71
+ "name": {"type": "string"},
72
+ "technical_level": {"type": "string", "enum": VALID_TECH_LEVELS},
73
+ "communication_style": {"type": "string"},
74
+ },
75
+ },
76
+ "preferences": {
77
+ "type": "object",
78
+ "properties": {
79
+ "code_style": {"type": "list"},
80
+ "documentation": {"type": "string"},
81
+ },
82
+ },
83
+ "memories": {"type": "list"},
84
+ }
85
+
86
+
87
+ @dataclass
88
+ class ValidationResult:
89
+ """Result of validating a single file."""
90
+
91
+ file_path: str
92
+ errors: list[str] = field(default_factory=list)
93
+ warnings: list[str] = field(default_factory=list)
94
+ info: list[str] = field(default_factory=list)
95
+
96
+ @property
97
+ def is_valid(self) -> bool:
98
+ return len(self.errors) == 0
99
+
100
+ def to_dict(self) -> dict[str, Any]:
101
+ return {
102
+ "file": self.file_path,
103
+ "valid": self.is_valid,
104
+ "errors": self.errors,
105
+ "warnings": self.warnings,
106
+ "info": self.info,
107
+ }
108
+
109
+
110
+ class YAMLValidator:
111
+ """Simple YAML validator without external dependencies."""
112
+
113
+ def __init__(self, content: str):
114
+ self.content = content
115
+ self.lines = content.split("\n")
116
+ self.data: dict[str, Any] = {}
117
+
118
+ def parse(self) -> tuple[bool, str]:
119
+ """Parse YAML content and return (success, error_message)."""
120
+ try:
121
+ self._parse_lines()
122
+ return True, ""
123
+ except Exception as e:
124
+ return False, str(e)
125
+
126
+ def _parse_lines(self):
127
+ """Parse YAML lines into a dictionary structure."""
128
+ current_list_key = None
129
+ current_list: list[str] = []
130
+
131
+ for i, line in enumerate(self.lines, 1):
132
+ # Skip empty lines and comments
133
+ if not line.strip() or line.strip().startswith("#"):
134
+ continue
135
+
136
+ # Check for tabs (should be spaces)
137
+ if "\t" in line:
138
+ raise ValueError(f"Line {i}: Contains tabs (use spaces instead)")
139
+
140
+ # Check for unclosed quotes
141
+ double_quotes = line.count('"')
142
+ single_quotes = line.count("'")
143
+ if double_quotes % 2 != 0:
144
+ raise ValueError(f"Line {i}: Unclosed double quote")
145
+ if single_quotes % 2 != 0:
146
+ raise ValueError(f"Line {i}: Unclosed single quote")
147
+
148
+ # Parse key-value pairs and lists
149
+ stripped = line.strip()
150
+
151
+ if stripped.startswith("- "):
152
+ # List item
153
+ if current_list_key:
154
+ value = stripped[2:].strip().strip("\"'")
155
+ current_list.append(value)
156
+ elif ":" in stripped:
157
+ # Save previous list
158
+ if current_list_key and current_list:
159
+ self.data[current_list_key] = current_list
160
+ current_list = []
161
+
162
+ # Key-value pair
163
+ parts = stripped.split(":", 1)
164
+ key = parts[0].strip()
165
+ value = parts[1].strip() if len(parts) > 1 else ""
166
+
167
+ if value == "":
168
+ # This might be a list or nested object
169
+ current_list_key = key
170
+ current_list = []
171
+ else:
172
+ # Simple key-value
173
+ value = value.strip("\"'")
174
+ self.data[key] = value
175
+ current_list_key = None
176
+
177
+ # Save final list
178
+ if current_list_key and current_list:
179
+ self.data[current_list_key] = current_list
180
+
181
+ def check_syntax(self) -> list[str]:
182
+ """Check for common YAML syntax issues."""
183
+ issues = []
184
+
185
+ for i, line in enumerate(self.lines, 1):
186
+ if not line.strip() or line.strip().startswith("#"):
187
+ continue
188
+
189
+ # Trailing whitespace
190
+ if line.rstrip() != line:
191
+ issues.append(f"Line {i}: Trailing whitespace")
192
+
193
+ # Missing space after colon
194
+ match = re.match(r"^(\s*)([a-zA-Z_][a-zA-Z0-9_]*):([^\s])", line)
195
+ if match and not line.strip().startswith("#"):
196
+ # Allow URLs
197
+ if "http:" not in line and "https:" not in line:
198
+ issues.append(f"Line {i}: Missing space after colon")
199
+
200
+ return issues
201
+
202
+ def get(self, key: str, default: Any = None) -> Any:
203
+ """Get a value from parsed data."""
204
+ return self.data.get(key, default)
205
+
206
+ def has_key(self, key: str) -> bool:
207
+ """Check if key exists."""
208
+ return key in self.data
209
+
210
+
211
+ def validate_override_file(
212
+ file_path: Path, agents_dir: Path, verbose: bool = False
213
+ ) -> ValidationResult:
214
+ """Validate an agent override file."""
215
+ result = ValidationResult(file_path=str(file_path))
216
+
217
+ # Extract agent name
218
+ agent_name = file_path.stem.replace(".override", "")
219
+ agent_file = agents_dir / f"{agent_name}.md"
220
+
221
+ # Check if corresponding agent exists
222
+ if not agent_file.exists():
223
+ result.warnings.append(f"No corresponding agent file found: {agent_name}.md")
224
+ else:
225
+ result.info.append(f"Agent file found: {agent_name}.md")
226
+
227
+ # Read and parse file
228
+ try:
229
+ content = file_path.read_text()
230
+ except Exception as e:
231
+ result.errors.append(f"Failed to read file: {e}")
232
+ return result
233
+
234
+ # Parse YAML
235
+ validator = YAMLValidator(content)
236
+ success, error = validator.parse()
237
+ if not success:
238
+ result.errors.append(f"YAML syntax error: {error}")
239
+ return result
240
+
241
+ result.info.append("YAML syntax is valid")
242
+
243
+ # Check for syntax issues
244
+ syntax_issues = validator.check_syntax()
245
+ for issue in syntax_issues:
246
+ result.warnings.append(issue)
247
+
248
+ # Validate model
249
+ model = validator.get("model")
250
+ if model:
251
+ if model in VALID_MODELS:
252
+ result.info.append(f"Model override is valid: {model}")
253
+ else:
254
+ result.errors.append(
255
+ f"Invalid model: '{model}'. Valid options: {', '.join(VALID_MODELS)}"
256
+ )
257
+
258
+ # Validate budget
259
+ budget = validator.get("max_budget_usd")
260
+ if budget:
261
+ try:
262
+ budget_val = float(budget)
263
+ if budget_val < MIN_BUDGET:
264
+ result.errors.append(f"Budget too low: {budget_val} (minimum: {MIN_BUDGET})")
265
+ elif budget_val > MAX_BUDGET:
266
+ result.warnings.append(
267
+ f"Budget unusually high: {budget_val} (max recommended: {MAX_BUDGET})"
268
+ )
269
+ else:
270
+ result.info.append(f"Budget override is valid: ${budget_val}")
271
+ except ValueError:
272
+ result.errors.append(f"Invalid budget format: '{budget}' (must be a number)")
273
+
274
+ # Check lists
275
+ if validator.has_key("additional_rules"):
276
+ rules = validator.get("additional_rules", [])
277
+ result.info.append(f"Additional rules defined: {len(rules)} rules")
278
+
279
+ if validator.has_key("memories"):
280
+ memories = validator.get("memories", [])
281
+ result.info.append(f"Memories defined: {len(memories)} items")
282
+
283
+ if validator.has_key("critical_actions"):
284
+ actions = validator.get("critical_actions", [])
285
+ result.info.append(f"Critical actions defined: {len(actions)} actions")
286
+
287
+ return result
288
+
289
+
290
+ def validate_user_profile(file_path: Path, verbose: bool = False) -> ValidationResult:
291
+ """Validate the user profile file."""
292
+ result = ValidationResult(file_path=str(file_path))
293
+
294
+ if not file_path.exists():
295
+ result.warnings.append("No user-profile.yaml found")
296
+ return result
297
+
298
+ # Read and parse file
299
+ try:
300
+ content = file_path.read_text()
301
+ except Exception as e:
302
+ result.errors.append(f"Failed to read file: {e}")
303
+ return result
304
+
305
+ # Parse YAML
306
+ validator = YAMLValidator(content)
307
+ success, error = validator.parse()
308
+ if not success:
309
+ result.errors.append(f"YAML syntax error: {error}")
310
+ return result
311
+
312
+ result.info.append("YAML syntax is valid")
313
+
314
+ # Check for syntax issues
315
+ syntax_issues = validator.check_syntax()
316
+ for issue in syntax_issues:
317
+ result.warnings.append(issue)
318
+
319
+ # Check user section (basic check since our parser is simple)
320
+ if "user" not in content:
321
+ result.warnings.append("No user section found in profile")
322
+ else:
323
+ result.info.append("User section found")
324
+
325
+ return result
326
+
327
+
328
+ def fix_file(file_path: Path) -> list[str]:
329
+ """Auto-fix common issues in a file."""
330
+ fixes = []
331
+
332
+ try:
333
+ content = file_path.read_text()
334
+ original = content
335
+
336
+ # Fix trailing whitespace
337
+ lines = content.split("\n")
338
+ fixed_lines = [line.rstrip() for line in lines]
339
+ if lines != fixed_lines:
340
+ fixes.append("Fixed trailing whitespace")
341
+
342
+ # Fix tabs
343
+ content = "\n".join(fixed_lines)
344
+ if "\t" in content:
345
+ content = content.replace("\t", " ")
346
+ fixes.append("Converted tabs to spaces")
347
+
348
+ # Write if changed
349
+ if content != original:
350
+ # Backup
351
+ backup_path = file_path.with_suffix(file_path.suffix + ".bak")
352
+ backup_path.write_text(original)
353
+ fixes.append(f"Backup saved to {backup_path.name}")
354
+
355
+ # Write fixed content
356
+ file_path.write_text(content)
357
+ else:
358
+ fixes.append("No changes needed")
359
+
360
+ except Exception as e:
361
+ fixes.append(f"Error fixing file: {e}")
362
+
363
+ return fixes
364
+
365
+
366
+ def print_result(result: ValidationResult, verbose: bool = False):
367
+ """Print validation result to terminal."""
368
+ print()
369
+ print(f"{Colors.BLUE}Validating:{Colors.NC} {os.path.basename(result.file_path)}")
370
+
371
+ for error in result.errors:
372
+ print(f"{Colors.RED} ✗ ERROR:{Colors.NC} {error}")
373
+
374
+ for warning in result.warnings:
375
+ print(f"{Colors.YELLOW} ⚠ WARNING:{Colors.NC} {warning}")
376
+
377
+ if verbose:
378
+ for info in result.info:
379
+ print(f"{Colors.GREEN} ✓{Colors.NC} {info}")
380
+ else:
381
+ # Show success messages only for key validations
382
+ for info in result.info:
383
+ if any(key in info for key in ["syntax is valid", "override is valid", "defined:"]):
384
+ print(f"{Colors.GREEN} ✓{Colors.NC} {info}")
385
+
386
+
387
+ def main():
388
+ parser = argparse.ArgumentParser(description="Validate override YAML files")
389
+ parser.add_argument("target", nargs="?", help="Specific agent name to validate")
390
+ parser.add_argument("--fix", action="store_true", help="Auto-fix common issues")
391
+ parser.add_argument("--verbose", "-v", action="store_true", help="Show detailed output")
392
+ parser.add_argument("--json", action="store_true", help="Output results as JSON")
393
+ args = parser.parse_args()
394
+
395
+ # Find directories
396
+ script_dir = Path(__file__).parent
397
+ project_root = script_dir.parent
398
+ overrides_dir = project_root / ".automation" / "overrides"
399
+ agents_dir = project_root / ".automation" / "agents"
400
+
401
+ if not overrides_dir.exists():
402
+ print(f"{Colors.RED}Error: Overrides directory not found: {overrides_dir}{Colors.NC}")
403
+ sys.exit(1)
404
+
405
+ results: list[ValidationResult] = []
406
+
407
+ # Print header
408
+ if not args.json:
409
+ print()
410
+ print(
411
+ f"{Colors.CYAN}═══════════════════════════════════════════════════════════════{Colors.NC}"
412
+ )
413
+ print(f"{Colors.CYAN} OVERRIDE VALIDATOR (Python){Colors.NC}")
414
+ print(
415
+ f"{Colors.CYAN}═══════════════════════════════════════════════════════════════{Colors.NC}"
416
+ )
417
+ print()
418
+ print(f"{Colors.BLUE}Scanning:{Colors.NC} {overrides_dir}")
419
+
420
+ if args.target:
421
+ # Validate specific override
422
+ file_path = overrides_dir / f"{args.target}.override.yaml"
423
+ if not file_path.exists():
424
+ print(f"{Colors.RED}Error: Override file not found: {file_path}{Colors.NC}")
425
+ sys.exit(1)
426
+
427
+ if args.fix:
428
+ fixes = fix_file(file_path)
429
+ if not args.json:
430
+ print(f"\n{Colors.YELLOW}Auto-fixing:{Colors.NC} {file_path.name}")
431
+ for fix in fixes:
432
+ print(f"{Colors.GREEN} ✓{Colors.NC} {fix}")
433
+
434
+ result = validate_override_file(file_path, agents_dir, args.verbose)
435
+ results.append(result)
436
+ else:
437
+ # Validate all overrides
438
+
439
+ # User profile first
440
+ profile_path = overrides_dir / "user-profile.yaml"
441
+ if profile_path.exists():
442
+ if args.fix:
443
+ fixes = fix_file(profile_path)
444
+ if not args.json:
445
+ print(f"\n{Colors.YELLOW}Auto-fixing:{Colors.NC} {profile_path.name}")
446
+ for fix in fixes:
447
+ print(f"{Colors.GREEN} ✓{Colors.NC} {fix}")
448
+
449
+ result = validate_user_profile(profile_path, args.verbose)
450
+ results.append(result)
451
+
452
+ # All override files
453
+ for file_path in sorted(overrides_dir.glob("*.override.yaml")):
454
+ if args.fix:
455
+ fixes = fix_file(file_path)
456
+ if not args.json:
457
+ print(f"\n{Colors.YELLOW}Auto-fixing:{Colors.NC} {file_path.name}")
458
+ for fix in fixes:
459
+ print(f"{Colors.GREEN} ✓{Colors.NC} {fix}")
460
+
461
+ result = validate_override_file(file_path, agents_dir, args.verbose)
462
+ results.append(result)
463
+
464
+ # Output results
465
+ if args.json:
466
+ output = {
467
+ "results": [r.to_dict() for r in results],
468
+ "summary": {
469
+ "total": len(results),
470
+ "valid": sum(1 for r in results if r.is_valid),
471
+ "errors": sum(len(r.errors) for r in results),
472
+ "warnings": sum(len(r.warnings) for r in results),
473
+ },
474
+ }
475
+ print(json.dumps(output, indent=2))
476
+ else:
477
+ # Print results
478
+ for result in results:
479
+ print_result(result, args.verbose)
480
+
481
+ # Summary
482
+ total_errors = sum(len(r.errors) for r in results)
483
+ total_warnings = sum(len(r.warnings) for r in results)
484
+
485
+ print()
486
+ print(
487
+ f"{Colors.CYAN}═══════════════════════════════════════════════════════════════{Colors.NC}"
488
+ )
489
+ print(f"{Colors.CYAN} VALIDATION SUMMARY{Colors.NC}")
490
+ print(
491
+ f"{Colors.CYAN}═══════════════════════════════════════════════════════════════{Colors.NC}"
492
+ )
493
+ print()
494
+ print(f" Files validated: {Colors.GREEN}{len(results)}{Colors.NC}")
495
+ print(f" Errors: {Colors.RED}{total_errors}{Colors.NC}")
496
+ print(f" Warnings: {Colors.YELLOW}{total_warnings}{Colors.NC}")
497
+ print()
498
+
499
+ if total_errors > 0:
500
+ print(f"{Colors.RED}❌ Validation failed with {total_errors} error(s){Colors.NC}")
501
+ sys.exit(1)
502
+ elif total_warnings > 0:
503
+ print(
504
+ f"{Colors.YELLOW}⚠️ Validation passed with {total_warnings} warning(s){Colors.NC}"
505
+ )
506
+ else:
507
+ print(f"{Colors.GREEN}✅ All validations passed!{Colors.NC}")
508
+
509
+
510
+ if __name__ == "__main__":
511
+ main()