@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,539 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Validate Setup - User-friendly setup verification for Devflow.
4
+
5
+ Checks that all components are properly configured and working.
6
+
7
+ Usage:
8
+ python tooling/scripts/validate_setup.py
9
+ python tooling/scripts/validate_setup.py --verbose
10
+ python tooling/scripts/validate_setup.py --fix
11
+
12
+ Exit codes:
13
+ 0 - All checks passed
14
+ 1 - One or more checks failed
15
+ 2 - Critical error during validation
16
+ """
17
+
18
+ import argparse
19
+ import json
20
+ import os
21
+ import sys
22
+ from dataclasses import dataclass
23
+ from enum import Enum
24
+ from pathlib import Path
25
+ from typing import Optional
26
+
27
+
28
+ class CheckStatus(Enum):
29
+ """Status of a validation check."""
30
+
31
+ PASS = "✅"
32
+ FAIL = "❌"
33
+ WARN = "⚠️"
34
+ SKIP = "⏭️"
35
+ INFO = "ℹ️"
36
+
37
+
38
+ @dataclass
39
+ class CheckResult:
40
+ """Result of a validation check."""
41
+
42
+ name: str
43
+ status: CheckStatus
44
+ message: str
45
+ details: Optional[str] = None
46
+ fix_command: Optional[str] = None
47
+
48
+
49
+ class Colors:
50
+ """ANSI color codes."""
51
+
52
+ RESET = "\033[0m"
53
+ RED = "\033[31m"
54
+ GREEN = "\033[32m"
55
+ YELLOW = "\033[33m"
56
+ BLUE = "\033[34m"
57
+ CYAN = "\033[36m"
58
+ BOLD = "\033[1m"
59
+ DIM = "\033[2m"
60
+
61
+ @classmethod
62
+ def disable(cls):
63
+ """Disable colors (for non-TTY output)."""
64
+ cls.RESET = ""
65
+ cls.RED = ""
66
+ cls.GREEN = ""
67
+ cls.YELLOW = ""
68
+ cls.BLUE = ""
69
+ cls.CYAN = ""
70
+ cls.BOLD = ""
71
+ cls.DIM = ""
72
+
73
+
74
+ # Detect if running in non-TTY
75
+ if not sys.stdout.isatty():
76
+ Colors.disable()
77
+
78
+
79
+ # Project paths
80
+ SCRIPT_DIR = Path(__file__).parent
81
+ PROJECT_ROOT = SCRIPT_DIR.parent.parent
82
+ LIB_DIR = SCRIPT_DIR / "lib"
83
+ AUTOMATION_DIR = PROJECT_ROOT / "tooling" / ".automation"
84
+
85
+
86
+ class SetupValidator:
87
+ """Validates the Devflow setup."""
88
+
89
+ def __init__(self, verbose: bool = False, fix: bool = False):
90
+ self.verbose = verbose
91
+ self.fix = fix
92
+ self.results: list[CheckResult] = []
93
+
94
+ def add_result(self, result: CheckResult):
95
+ """Add a check result."""
96
+ self.results.append(result)
97
+ self._print_result(result)
98
+
99
+ def _print_result(self, result: CheckResult):
100
+ """Print a check result."""
101
+ status_color = {
102
+ CheckStatus.PASS: Colors.GREEN,
103
+ CheckStatus.FAIL: Colors.RED,
104
+ CheckStatus.WARN: Colors.YELLOW,
105
+ CheckStatus.SKIP: Colors.DIM,
106
+ CheckStatus.INFO: Colors.BLUE,
107
+ }.get(result.status, "")
108
+
109
+ print(
110
+ f" {result.status.value} {status_color}{result.name}{Colors.RESET}: {result.message}"
111
+ )
112
+
113
+ if self.verbose and result.details:
114
+ for line in result.details.split("\n"):
115
+ print(f" {Colors.DIM}{line}{Colors.RESET}")
116
+
117
+ if result.status == CheckStatus.FAIL and result.fix_command:
118
+ print(f" {Colors.CYAN}Fix: {result.fix_command}{Colors.RESET}")
119
+
120
+ def check_python_version(self):
121
+ """Check Python version is supported."""
122
+ version = sys.version_info
123
+ version_str = f"{version.major}.{version.minor}.{version.micro}"
124
+
125
+ if version >= (3, 9):
126
+ self.add_result(
127
+ CheckResult(
128
+ name="Python Version",
129
+ status=CheckStatus.PASS,
130
+ message=f"Python {version_str} is supported",
131
+ )
132
+ )
133
+ elif version >= (3, 7):
134
+ self.add_result(
135
+ CheckResult(
136
+ name="Python Version",
137
+ status=CheckStatus.WARN,
138
+ message=f"Python {version_str} may work but 3.9+ recommended",
139
+ )
140
+ )
141
+ else:
142
+ self.add_result(
143
+ CheckResult(
144
+ name="Python Version",
145
+ status=CheckStatus.FAIL,
146
+ message=f"Python {version_str} is not supported (need 3.9+)",
147
+ fix_command="pyenv install 3.11 && pyenv local 3.11",
148
+ )
149
+ )
150
+
151
+ def check_project_structure(self):
152
+ """Check required project structure exists."""
153
+ required_dirs = [
154
+ PROJECT_ROOT / "tooling",
155
+ PROJECT_ROOT / "tooling" / "scripts",
156
+ PROJECT_ROOT / "tooling" / "scripts" / "lib",
157
+ ]
158
+
159
+ optional_dirs = [
160
+ AUTOMATION_DIR,
161
+ AUTOMATION_DIR / "costs",
162
+ AUTOMATION_DIR / "costs" / "sessions",
163
+ ]
164
+
165
+ all_exist = True
166
+ missing = []
167
+
168
+ for dir_path in required_dirs:
169
+ if not dir_path.exists():
170
+ all_exist = False
171
+ missing.append(str(dir_path.relative_to(PROJECT_ROOT)))
172
+
173
+ if all_exist:
174
+ self.add_result(
175
+ CheckResult(
176
+ name="Project Structure",
177
+ status=CheckStatus.PASS,
178
+ message="All required directories exist",
179
+ )
180
+ )
181
+ else:
182
+ self.add_result(
183
+ CheckResult(
184
+ name="Project Structure",
185
+ status=CheckStatus.FAIL,
186
+ message=f"Missing directories: {', '.join(missing)}",
187
+ fix_command=f"mkdir -p {' '.join(missing)}",
188
+ )
189
+ )
190
+
191
+ # Check optional dirs
192
+ for dir_path in optional_dirs:
193
+ if not dir_path.exists():
194
+ if self.fix:
195
+ dir_path.mkdir(parents=True, exist_ok=True)
196
+ self.add_result(
197
+ CheckResult(
198
+ name=f"Directory {dir_path.name}",
199
+ status=CheckStatus.PASS,
200
+ message="Created missing directory",
201
+ )
202
+ )
203
+ else:
204
+ self.add_result(
205
+ CheckResult(
206
+ name=f"Directory {dir_path.name}",
207
+ status=CheckStatus.WARN,
208
+ message="Optional directory missing (will be created on first use)",
209
+ fix_command=f"mkdir -p {dir_path}",
210
+ )
211
+ )
212
+
213
+ def check_core_modules(self):
214
+ """Check core Python modules exist and are importable."""
215
+ modules = [
216
+ ("cost_tracker", LIB_DIR / "cost_tracker.py"),
217
+ ("cost_config", LIB_DIR / "cost_config.py"),
218
+ ("cost_display", LIB_DIR / "cost_display.py"),
219
+ ("currency_converter", LIB_DIR / "currency_converter.py"),
220
+ ]
221
+
222
+ # Add lib to path for imports
223
+ sys.path.insert(0, str(LIB_DIR))
224
+
225
+ for module_name, module_path in modules:
226
+ if not module_path.exists():
227
+ self.add_result(
228
+ CheckResult(
229
+ name=f"Module {module_name}",
230
+ status=CheckStatus.FAIL,
231
+ message=f"File not found: {module_path.name}",
232
+ )
233
+ )
234
+ continue
235
+
236
+ try:
237
+ __import__(module_name)
238
+ self.add_result(
239
+ CheckResult(
240
+ name=f"Module {module_name}",
241
+ status=CheckStatus.PASS,
242
+ message="Imports successfully",
243
+ )
244
+ )
245
+ except ImportError as e:
246
+ self.add_result(
247
+ CheckResult(
248
+ name=f"Module {module_name}",
249
+ status=CheckStatus.FAIL,
250
+ message=f"Import error: {e}",
251
+ details=str(e),
252
+ )
253
+ )
254
+ except Exception as e:
255
+ self.add_result(
256
+ CheckResult(
257
+ name=f"Module {module_name}",
258
+ status=CheckStatus.WARN,
259
+ message=f"Warning during import: {type(e).__name__}",
260
+ details=str(e),
261
+ )
262
+ )
263
+
264
+ def check_cost_tracker_functionality(self):
265
+ """Test core cost tracker functionality."""
266
+ try:
267
+ sys.path.insert(0, str(LIB_DIR))
268
+ from cost_tracker import PRICING, CostTracker
269
+
270
+ # Check pricing is defined
271
+ if not PRICING:
272
+ self.add_result(
273
+ CheckResult(
274
+ name="Pricing Configuration",
275
+ status=CheckStatus.FAIL,
276
+ message="No pricing data defined",
277
+ )
278
+ )
279
+ else:
280
+ models = list(PRICING.keys())
281
+ self.add_result(
282
+ CheckResult(
283
+ name="Pricing Configuration",
284
+ status=CheckStatus.PASS,
285
+ message=f"{len(models)} models configured",
286
+ details=f"Models: {', '.join(models[:5])}...",
287
+ )
288
+ )
289
+
290
+ # Test cost calculation
291
+ tracker = CostTracker(
292
+ story_key="validation-test", budget_limit_usd=10.00, auto_save=False
293
+ )
294
+ cost = tracker.calculate_cost("sonnet", 1000, 500)
295
+
296
+ if cost > 0:
297
+ self.add_result(
298
+ CheckResult(
299
+ name="Cost Calculation",
300
+ status=CheckStatus.PASS,
301
+ message=f"Calculated ${cost:.6f} for test tokens",
302
+ )
303
+ )
304
+ else:
305
+ self.add_result(
306
+ CheckResult(
307
+ name="Cost Calculation",
308
+ status=CheckStatus.WARN,
309
+ message="Cost calculation returned 0",
310
+ )
311
+ )
312
+
313
+ # Test budget checking
314
+ ok, level, msg = tracker.check_budget()
315
+ self.add_result(
316
+ CheckResult(
317
+ name="Budget Monitoring",
318
+ status=CheckStatus.PASS,
319
+ message=f"Budget check working (status: {level})",
320
+ )
321
+ )
322
+
323
+ except Exception as e:
324
+ self.add_result(
325
+ CheckResult(
326
+ name="Cost Tracker",
327
+ status=CheckStatus.FAIL,
328
+ message=f"Error testing cost tracker: {e}",
329
+ details=str(e),
330
+ )
331
+ )
332
+
333
+ def check_environment_config(self):
334
+ """Check environment configuration."""
335
+ env_vars = {
336
+ "MAX_BUDGET_CONTEXT": ("Budget for context phase", "3.00"),
337
+ "MAX_BUDGET_DEV": ("Budget for development phase", "15.00"),
338
+ "MAX_BUDGET_REVIEW": ("Budget for review phase", "5.00"),
339
+ "COST_DISPLAY_CURRENCY": ("Display currency", "USD"),
340
+ }
341
+
342
+ configured = 0
343
+ for var, (_desc, _default) in env_vars.items():
344
+ value = os.getenv(var)
345
+ if value:
346
+ configured += 1
347
+ if self.verbose:
348
+ self.add_result(
349
+ CheckResult(
350
+ name=f"Env: {var}", status=CheckStatus.INFO, message=f"Set to '{value}'"
351
+ )
352
+ )
353
+
354
+ if configured > 0:
355
+ self.add_result(
356
+ CheckResult(
357
+ name="Environment Variables",
358
+ status=CheckStatus.PASS,
359
+ message=f"{configured}/{len(env_vars)} custom settings configured",
360
+ )
361
+ )
362
+ else:
363
+ self.add_result(
364
+ CheckResult(
365
+ name="Environment Variables",
366
+ status=CheckStatus.INFO,
367
+ message="Using default configuration (all optional)",
368
+ )
369
+ )
370
+
371
+ def check_storage_writable(self):
372
+ """Check that storage directories are writable."""
373
+ test_dirs = [
374
+ AUTOMATION_DIR / "costs" / "sessions",
375
+ ]
376
+
377
+ for test_dir in test_dirs:
378
+ test_dir.mkdir(parents=True, exist_ok=True)
379
+ test_file = test_dir / ".write_test"
380
+
381
+ try:
382
+ test_file.write_text("test")
383
+ test_file.unlink()
384
+ self.add_result(
385
+ CheckResult(
386
+ name=f"Storage: {test_dir.name}",
387
+ status=CheckStatus.PASS,
388
+ message="Directory is writable",
389
+ )
390
+ )
391
+ except PermissionError:
392
+ self.add_result(
393
+ CheckResult(
394
+ name=f"Storage: {test_dir.name}",
395
+ status=CheckStatus.FAIL,
396
+ message="Directory is not writable",
397
+ fix_command=f"chmod 755 {test_dir}",
398
+ )
399
+ )
400
+ except Exception as e:
401
+ self.add_result(
402
+ CheckResult(
403
+ name=f"Storage: {test_dir.name}",
404
+ status=CheckStatus.WARN,
405
+ message=f"Could not verify: {e}",
406
+ )
407
+ )
408
+
409
+ def check_shell_scripts(self):
410
+ """Check shell scripts are executable."""
411
+ shell_scripts = list(SCRIPT_DIR.glob("*.sh"))
412
+
413
+ non_executable = []
414
+ for script in shell_scripts:
415
+ if not os.access(script, os.X_OK):
416
+ non_executable.append(script.name)
417
+ if self.fix:
418
+ os.chmod(script, 0o755)
419
+
420
+ if not shell_scripts:
421
+ self.add_result(
422
+ CheckResult(
423
+ name="Shell Scripts", status=CheckStatus.INFO, message="No shell scripts found"
424
+ )
425
+ )
426
+ elif non_executable and not self.fix:
427
+ self.add_result(
428
+ CheckResult(
429
+ name="Shell Scripts",
430
+ status=CheckStatus.WARN,
431
+ message=f"{len(non_executable)} scripts not executable",
432
+ fix_command="chmod +x tooling/scripts/*.sh",
433
+ )
434
+ )
435
+ else:
436
+ self.add_result(
437
+ CheckResult(
438
+ name="Shell Scripts",
439
+ status=CheckStatus.PASS,
440
+ message=f"{len(shell_scripts)} scripts are executable",
441
+ )
442
+ )
443
+
444
+ def run_all_checks(self) -> bool:
445
+ """Run all validation checks."""
446
+ print(f"\n{Colors.BOLD}🔍 Devflow Setup Validation{Colors.RESET}\n")
447
+ print(f" Project: {PROJECT_ROOT}")
448
+ print(f" Python: {sys.executable}")
449
+ print()
450
+
451
+ print(f"{Colors.BOLD}Core Checks:{Colors.RESET}")
452
+ self.check_python_version()
453
+ self.check_project_structure()
454
+ self.check_core_modules()
455
+
456
+ print(f"\n{Colors.BOLD}Functionality Checks:{Colors.RESET}")
457
+ self.check_cost_tracker_functionality()
458
+
459
+ print(f"\n{Colors.BOLD}Configuration Checks:{Colors.RESET}")
460
+ self.check_environment_config()
461
+ self.check_storage_writable()
462
+ self.check_shell_scripts()
463
+
464
+ # Summary
465
+ passed = sum(1 for r in self.results if r.status == CheckStatus.PASS)
466
+ failed = sum(1 for r in self.results if r.status == CheckStatus.FAIL)
467
+ warned = sum(1 for r in self.results if r.status == CheckStatus.WARN)
468
+
469
+ print(f"\n{Colors.BOLD}━━━ Summary ━━━{Colors.RESET}")
470
+ print(f" {Colors.GREEN}Passed:{Colors.RESET} {passed}")
471
+ if warned:
472
+ print(f" {Colors.YELLOW}Warnings:{Colors.RESET} {warned}")
473
+ if failed:
474
+ print(f" {Colors.RED}Failed:{Colors.RESET} {failed}")
475
+
476
+ if failed == 0:
477
+ print(
478
+ f"\n{Colors.GREEN}{Colors.BOLD}✅ All checks passed! Devflow is ready to use.{Colors.RESET}\n"
479
+ )
480
+ return True
481
+ else:
482
+ print(
483
+ f"\n{Colors.RED}{Colors.BOLD}❌ {failed} check(s) failed. Please fix the issues above.{Colors.RESET}"
484
+ )
485
+ if not self.fix:
486
+ print(f" {Colors.DIM}Run with --fix to auto-fix some issues.{Colors.RESET}\n")
487
+ return False
488
+
489
+
490
+ def main():
491
+ """Main entry point."""
492
+ parser = argparse.ArgumentParser(
493
+ description="Validate Devflow setup",
494
+ formatter_class=argparse.RawDescriptionHelpFormatter,
495
+ epilog="""
496
+ Examples:
497
+ python validate_setup.py Run basic validation
498
+ python validate_setup.py -v Run with verbose output
499
+ python validate_setup.py --fix Auto-fix fixable issues
500
+ """,
501
+ )
502
+ parser.add_argument("-v", "--verbose", action="store_true", help="Show detailed output")
503
+ parser.add_argument("--fix", action="store_true", help="Attempt to auto-fix issues")
504
+ parser.add_argument("--json", action="store_true", help="Output results as JSON")
505
+
506
+ args = parser.parse_args()
507
+
508
+ try:
509
+ validator = SetupValidator(verbose=args.verbose, fix=args.fix)
510
+ success = validator.run_all_checks()
511
+
512
+ if args.json:
513
+ results = [
514
+ {
515
+ "name": r.name,
516
+ "status": r.status.name,
517
+ "message": r.message,
518
+ "details": r.details,
519
+ }
520
+ for r in validator.results
521
+ ]
522
+ print(json.dumps(results, indent=2))
523
+
524
+ sys.exit(0 if success else 1)
525
+
526
+ except KeyboardInterrupt:
527
+ print("\n\nValidation cancelled.")
528
+ sys.exit(2)
529
+ except Exception as e:
530
+ print(f"\n{Colors.RED}Critical error during validation: {e}{Colors.RESET}")
531
+ if args.verbose:
532
+ import traceback
533
+
534
+ traceback.print_exc()
535
+ sys.exit(2)
536
+
537
+
538
+ if __name__ == "__main__":
539
+ main()