@tinkcarlos/skillora 0.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 (234) hide show
  1. package/.claude/skills/.temp-skill-index.md +245 -0
  2. package/.claude/skills/SKILL.md +264 -0
  3. package/.claude/skills/api-scaffolding/SKILL.md +431 -0
  4. package/.claude/skills/api-scaffolding/agents/backend-architect.md +282 -0
  5. package/.claude/skills/api-scaffolding/agents/django-pro.md +144 -0
  6. package/.claude/skills/api-scaffolding/agents/fastapi-pro.md +156 -0
  7. package/.claude/skills/api-scaffolding/agents/graphql-architect.md +146 -0
  8. package/.claude/skills/api-scaffolding/skills/fastapi-templates/SKILL.md +171 -0
  9. package/.claude/skills/api-testing-observability/SKILL.md +583 -0
  10. package/.claude/skills/api-testing-observability/agents/api-documenter.md +146 -0
  11. package/.claude/skills/api-testing-observability/commands/api-mock.md +1320 -0
  12. package/.claude/skills/brainstorming/SKILL.md +283 -0
  13. package/.claude/skills/bug-fixing/SKILL.md +382 -0
  14. package/.claude/skills/bug-fixing/references/backend-guide.md +132 -0
  15. package/.claude/skills/bug-fixing/references/bug-guide.md +354 -0
  16. package/.claude/skills/bug-fixing/references/bug-record-template.md +134 -0
  17. package/.claude/skills/bug-fixing/references/bug-records.md +88 -0
  18. package/.claude/skills/bug-fixing/references/code-review-gate.md +81 -0
  19. package/.claude/skills/bug-fixing/references/common-bugs.md +140 -0
  20. package/.claude/skills/bug-fixing/references/complete-workflow.md +361 -0
  21. package/.claude/skills/bug-fixing/references/config-driven-fixes.md +136 -0
  22. package/.claude/skills/bug-fixing/references/context-isolation-protocol.md +268 -0
  23. package/.claude/skills/bug-fixing/references/cross-surface-regression.md +120 -0
  24. package/.claude/skills/bug-fixing/references/database-investigation.md +129 -0
  25. package/.claude/skills/bug-fixing/references/dependency-and-integrity-protocol.md +369 -0
  26. package/.claude/skills/bug-fixing/references/fix-completeness-checklist.md +239 -0
  27. package/.claude/skills/bug-fixing/references/frontend-guide.md +219 -0
  28. package/.claude/skills/bug-fixing/references/fullstack-joint-guide.md +123 -0
  29. package/.claude/skills/bug-fixing/references/functional-breakage.md +117 -0
  30. package/.claude/skills/bug-fixing/references/ide-lint-errors-guide.md +176 -0
  31. package/.claude/skills/bug-fixing/references/impact-analysis.md +511 -0
  32. package/.claude/skills/bug-fixing/references/investigation-checklist.md +263 -0
  33. package/.claude/skills/bug-fixing/references/knowledge-extraction-guide.md +531 -0
  34. package/.claude/skills/bug-fixing/references/knowledge-workflow.md +212 -0
  35. package/.claude/skills/bug-fixing/references/post-edit-quality-gate.md +30 -0
  36. package/.claude/skills/bug-fixing/references/python-env-and-testing.md +126 -0
  37. package/.claude/skills/bug-fixing/references/rca-guide.md +428 -0
  38. package/.claude/skills/bug-fixing/references/similar-bug-patterns.md +113 -0
  39. package/.claude/skills/bug-fixing/references/skill-delegation-guide.md +350 -0
  40. package/.claude/skills/bug-fixing/references/skill-orchestration.md +155 -0
  41. package/.claude/skills/bug-fixing/references/testing-strategy.md +350 -0
  42. package/.claude/skills/bug-fixing/references/tooling-build-scripts.md +162 -0
  43. package/.claude/skills/bug-fixing/references/user-input-validation.md +77 -0
  44. package/.claude/skills/bug-fixing/references/ux-patterns.md +158 -0
  45. package/.claude/skills/bug-fixing/references/windows-terminal-hygiene.md +106 -0
  46. package/.claude/skills/bug-fixing/references/zero-regression-matrix.md +239 -0
  47. package/.claude/skills/bug-fixing/references/zero-risk-protocol.md +102 -0
  48. package/.claude/skills/bug-fixing/scripts/format_code.py +611 -0
  49. package/.claude/skills/bug-fixing/scripts/generate_report_template.py +74 -0
  50. package/.claude/skills/bug-fixing/scripts/lint_check.py +816 -0
  51. package/.claude/skills/bug-fixing/scripts/requirements.txt +36 -0
  52. package/.claude/skills/cicd-pipeline/SKILL.md +300 -0
  53. package/.claude/skills/code-review/SKILL.md +535 -0
  54. package/.claude/skills/code-review/references/anti-pattern-scan.md +102 -0
  55. package/.claude/skills/code-review/references/automated-analysis.md +456 -0
  56. package/.claude/skills/code-review/references/backend-common-issues.md +589 -0
  57. package/.claude/skills/code-review/references/backend-expert-guide.md +415 -0
  58. package/.claude/skills/code-review/references/backend-review.md +868 -0
  59. package/.claude/skills/code-review/references/batch-processing-strategy.md +198 -0
  60. package/.claude/skills/code-review/references/call-chain-analysis-protocol.md +166 -0
  61. package/.claude/skills/code-review/references/common-patterns.md +321 -0
  62. package/.claude/skills/code-review/references/configuration-review.md +425 -0
  63. package/.claude/skills/code-review/references/control-flow-completeness.md +114 -0
  64. package/.claude/skills/code-review/references/database-review.md +298 -0
  65. package/.claude/skills/code-review/references/dependency-and-integrity-protocol.md +313 -0
  66. package/.claude/skills/code-review/references/external-standards.md +51 -0
  67. package/.claude/skills/code-review/references/feature-review.md +329 -0
  68. package/.claude/skills/code-review/references/file-review-template.md +326 -0
  69. package/.claude/skills/code-review/references/frontend-advanced.md +654 -0
  70. package/.claude/skills/code-review/references/frontend-common-issues.md +482 -0
  71. package/.claude/skills/code-review/references/frontend-expert-guide.md +342 -0
  72. package/.claude/skills/code-review/references/frontend-review.md +783 -0
  73. package/.claude/skills/code-review/references/fullstack-consistency.md +418 -0
  74. package/.claude/skills/code-review/references/fullstack-review.md +477 -0
  75. package/.claude/skills/code-review/references/functional-completeness.md +386 -0
  76. package/.claude/skills/code-review/references/hidden-bugs-detection.md +473 -0
  77. package/.claude/skills/code-review/references/ide-lint-errors-guide.md +173 -0
  78. package/.claude/skills/code-review/references/infrastructure-review.md +453 -0
  79. package/.claude/skills/code-review/references/iteration-review.md +264 -0
  80. package/.claude/skills/code-review/references/job-review.md +335 -0
  81. package/.claude/skills/code-review/references/layered-checklist-protocol.md +157 -0
  82. package/.claude/skills/code-review/references/logic-completeness.md +535 -0
  83. package/.claude/skills/code-review/references/mandatory-checklist.md +288 -0
  84. package/.claude/skills/code-review/references/multi-language-guide.md +800 -0
  85. package/.claude/skills/code-review/references/new-project-review.md +226 -0
  86. package/.claude/skills/code-review/references/non-code-files-review.md +451 -0
  87. package/.claude/skills/code-review/references/overlooked-issues.md +657 -0
  88. package/.claude/skills/code-review/references/platform-specific-review.md +195 -0
  89. package/.claude/skills/code-review/references/precision-analysis-protocol.md +260 -0
  90. package/.claude/skills/code-review/references/python-patterns.md +494 -0
  91. package/.claude/skills/code-review/references/rca-techniques.md +362 -0
  92. package/.claude/skills/code-review/references/report-template.md +430 -0
  93. package/.claude/skills/code-review/references/resource-limits-and-degradation.md +137 -0
  94. package/.claude/skills/code-review/references/review-dimensions.md +311 -0
  95. package/.claude/skills/code-review/references/review-guide.md +202 -0
  96. package/.claude/skills/code-review/references/review-knowledge-workflow.md +257 -0
  97. package/.claude/skills/code-review/references/review-progress-tracker-protocol.md +172 -0
  98. package/.claude/skills/code-review/references/review-record-template.md +195 -0
  99. package/.claude/skills/code-review/references/skill-orchestration.md +143 -0
  100. package/.claude/skills/code-review/references/ui-ux-review.md +470 -0
  101. package/.claude/skills/containerization/SKILL.md +313 -0
  102. package/.claude/skills/database-migrations/agents/database-admin.md +142 -0
  103. package/.claude/skills/database-migrations/agents/database-optimizer.md +144 -0
  104. package/.claude/skills/database-migrations/commands/migration-observability.md +408 -0
  105. package/.claude/skills/database-migrations/commands/sql-migrations.md +492 -0
  106. package/.claude/skills/finishing-a-development-branch/SKILL.md +319 -0
  107. package/.claude/skills/frontend-design/LICENSE.txt +177 -0
  108. package/.claude/skills/frontend-design/SKILL.md +587 -0
  109. package/.claude/skills/frontend-design/references/color-consistency.md +487 -0
  110. package/.claude/skills/frontend-design/references/color-palettes-full.md +657 -0
  111. package/.claude/skills/frontend-design/references/design-system-generator.md +285 -0
  112. package/.claude/skills/frontend-design/references/font-pairings-full.md +705 -0
  113. package/.claude/skills/frontend-design/references/industry-anti-patterns.md +281 -0
  114. package/.claude/skills/frontend-design/references/layout-anti-patterns.md +582 -0
  115. package/.claude/skills/frontend-design/references/motion-patterns.md +659 -0
  116. package/.claude/skills/frontend-design/references/pre-delivery-checklist.md +153 -0
  117. package/.claude/skills/frontend-design/references/responsive-design.md +555 -0
  118. package/.claude/skills/frontend-design/references/style-modification-rules.md +335 -0
  119. package/.claude/skills/frontend-design/references/ui-styles-full.md +383 -0
  120. package/.claude/skills/frontend-design/references/ui-styles-rating.md +191 -0
  121. package/.claude/skills/frontend-design/references/ux-guidelines.md +640 -0
  122. package/.claude/skills/fullstack-developer/SKILL.md +512 -0
  123. package/.claude/skills/fullstack-developer/references/api-contract-guide.md +312 -0
  124. package/.claude/skills/fullstack-developer/references/api-response-patterns.md +223 -0
  125. package/.claude/skills/fullstack-developer/references/async-patterns.md +220 -0
  126. package/.claude/skills/fullstack-developer/references/bug-prevention.md +914 -0
  127. package/.claude/skills/fullstack-developer/references/code-quality-checklist.md +271 -0
  128. package/.claude/skills/fullstack-developer/references/complete-development-workflow.md +278 -0
  129. package/.claude/skills/fullstack-developer/references/context-isolation-protocol.md +256 -0
  130. package/.claude/skills/fullstack-developer/references/database-migration.md +331 -0
  131. package/.claude/skills/fullstack-developer/references/dependency-and-integrity-protocol.md +390 -0
  132. package/.claude/skills/fullstack-developer/references/development-phases.md +333 -0
  133. package/.claude/skills/fullstack-developer/references/expert-guide.md +214 -0
  134. package/.claude/skills/fullstack-developer/references/file-import-patterns.md +114 -0
  135. package/.claude/skills/fullstack-developer/references/graceful-degradation-patterns.md +78 -0
  136. package/.claude/skills/fullstack-developer/references/ide-lint-errors-guide.md +183 -0
  137. package/.claude/skills/fullstack-developer/references/integration-testing.md +301 -0
  138. package/.claude/skills/fullstack-developer/references/mock-api-patterns.md +307 -0
  139. package/.claude/skills/fullstack-developer/references/phase-gate-template.md +249 -0
  140. package/.claude/skills/fullstack-developer/references/post-edit-quality-gate.md +30 -0
  141. package/.claude/skills/fullstack-developer/references/python-engineering.md +79 -0
  142. package/.claude/skills/fullstack-developer/references/skill-orchestration.md +214 -0
  143. package/.claude/skills/fullstack-developer/references/skill-router-table.md +304 -0
  144. package/.claude/skills/fullstack-developer/references/state-sync.md +217 -0
  145. package/.claude/skills/fullstack-developer/references/ui-testing-checklist.md +292 -0
  146. package/.claude/skills/fullstack-developer/scripts/format_code.py +611 -0
  147. package/.claude/skills/fullstack-developer/scripts/lint_check.py +816 -0
  148. package/.claude/skills/fullstack-developer/scripts/requirements.txt +36 -0
  149. package/.claude/skills/performance-optimization/SKILL.md +250 -0
  150. package/.claude/skills/product-requirements/SKILL.md +357 -0
  151. package/.claude/skills/product-requirements/references/acceptance-criteria.md +335 -0
  152. package/.claude/skills/product-requirements/references/answer-first-questioning-protocol.md +299 -0
  153. package/.claude/skills/product-requirements/references/competitive-analysis-guide.md +183 -0
  154. package/.claude/skills/product-requirements/references/document-accuracy-protocol.md +253 -0
  155. package/.claude/skills/product-requirements/references/document-management-protocol.md +278 -0
  156. package/.claude/skills/product-requirements/references/external-standards.md +62 -0
  157. package/.claude/skills/product-requirements/references/feature-spec-template.md +359 -0
  158. package/.claude/skills/product-requirements/references/knowledge-acquisition-protocol.md +251 -0
  159. package/.claude/skills/product-requirements/references/plan-execution-protocol.md +334 -0
  160. package/.claude/skills/product-requirements/references/plan-generation-protocol.md +264 -0
  161. package/.claude/skills/product-requirements/references/prioritization-frameworks.md +80 -0
  162. package/.claude/skills/product-requirements/references/requirement-decomposition-protocol.md +291 -0
  163. package/.claude/skills/product-requirements/references/user-story-examples.md +297 -0
  164. package/.claude/skills/product-requirements/references/workflow-templates.md +266 -0
  165. package/.claude/skills/react-best-practices/SKILL.md +198 -0
  166. package/.claude/skills/react-best-practices/references/advanced-patterns.md +94 -0
  167. package/.claude/skills/react-best-practices/references/bundle-optimization.md +182 -0
  168. package/.claude/skills/react-best-practices/references/client-data-fetching.md +112 -0
  169. package/.claude/skills/react-best-practices/references/complete-guide.md +2249 -0
  170. package/.claude/skills/react-best-practices/references/eliminating-waterfalls.md +169 -0
  171. package/.claude/skills/react-best-practices/references/javascript-performance.md +256 -0
  172. package/.claude/skills/react-best-practices/references/rendering-performance.md +230 -0
  173. package/.claude/skills/react-best-practices/references/rerender-optimization.md +214 -0
  174. package/.claude/skills/react-best-practices/references/server-performance.md +182 -0
  175. package/.claude/skills/security-audit/SKILL.md +226 -0
  176. package/.claude/skills/shared-references/advanced-debugging-techniques.md +186 -0
  177. package/.claude/skills/shared-references/code-quality-checklist.md +218 -0
  178. package/.claude/skills/shared-references/code-review-efficiency-guide.md +125 -0
  179. package/.claude/skills/shared-references/mcp-dependency-compatibility-protocol.md +276 -0
  180. package/.claude/skills/shared-references/skill-call-graph.md +230 -0
  181. package/.claude/skills/shared-references/skill-orchestration-protocol.md +281 -0
  182. package/.claude/skills/shared-references/subagent-dispatch-templates.md +199 -0
  183. package/.claude/skills/skill-expert-skills/LICENSE.txt +204 -0
  184. package/.claude/skills/skill-expert-skills/QUICK_NAVIGATION.md +374 -0
  185. package/.claude/skills/skill-expert-skills/SKILL.md +247 -0
  186. package/.claude/skills/skill-expert-skills/docs/_index.md +91 -0
  187. package/.claude/skills/skill-expert-skills/references/deep-research-methodology.md +389 -0
  188. package/.claude/skills/skill-expert-skills/references/docs-generation-workflow.md +398 -0
  189. package/.claude/skills/skill-expert-skills/references/domain-expertise-protocol.md +343 -0
  190. package/.claude/skills/skill-expert-skills/references/domain-knowledge/_index.md +54 -0
  191. package/.claude/skills/skill-expert-skills/references/domain-knowledge/backend-expertise.md +517 -0
  192. package/.claude/skills/skill-expert-skills/references/domain-knowledge/bug-fixing-expertise.md +363 -0
  193. package/.claude/skills/skill-expert-skills/references/domain-knowledge/code-review-expertise.md +392 -0
  194. package/.claude/skills/skill-expert-skills/references/domain-knowledge/frontend-expertise.md +410 -0
  195. package/.claude/skills/skill-expert-skills/references/domain-knowledge-template.md +503 -0
  196. package/.claude/skills/skill-expert-skills/references/examples.md +782 -0
  197. package/.claude/skills/skill-expert-skills/references/integration-examples.md +655 -0
  198. package/.claude/skills/skill-expert-skills/references/knowledge-validation-checklist.md +246 -0
  199. package/.claude/skills/skill-expert-skills/references/latest-knowledge-acquisition.md +461 -0
  200. package/.claude/skills/skill-expert-skills/references/mcp-tools-guide.md +439 -0
  201. package/.claude/skills/skill-expert-skills/references/official-best-practices.md +616 -0
  202. package/.claude/skills/skill-expert-skills/references/patterns.md +218 -0
  203. package/.claude/skills/skill-expert-skills/references/plugin-skills-guide.md +432 -0
  204. package/.claude/skills/skill-expert-skills/references/requirement-elicitation-protocol.md +290 -0
  205. package/.claude/skills/skill-expert-skills/references/skill-creator-SKILL.md +353 -0
  206. package/.claude/skills/skill-expert-skills/references/skill-templates.md +583 -0
  207. package/.claude/skills/skill-expert-skills/references/skills-knowledge-base.md +561 -0
  208. package/.claude/skills/skill-expert-skills/references/tools-guide.md +379 -0
  209. package/.claude/skills/skill-expert-skills/references/troubleshooting.md +378 -0
  210. package/.claude/skills/skill-expert-skills/references/universality-guide.md +205 -0
  211. package/.claude/skills/skill-expert-skills/references/writing-style-guide.md +466 -0
  212. package/.claude/skills/skill-expert-skills/scripts/__pycache__/quick_validate.cpython-313.pyc +0 -0
  213. package/.claude/skills/skill-expert-skills/scripts/__pycache__/universal_validate.cpython-313.pyc +0 -0
  214. package/.claude/skills/skill-expert-skills/scripts/analyze_trigger.py +425 -0
  215. package/.claude/skills/skill-expert-skills/scripts/diff_with_official.py +188 -0
  216. package/.claude/skills/skill-expert-skills/scripts/init_skill.py +349 -0
  217. package/.claude/skills/skill-expert-skills/scripts/package_skill.py +156 -0
  218. package/.claude/skills/skill-expert-skills/scripts/quick_validate.py +493 -0
  219. package/.claude/skills/skill-expert-skills/scripts/requirements.txt +2 -0
  220. package/.claude/skills/skill-expert-skills/scripts/universal_validate.py +182 -0
  221. package/.claude/skills/skill-expert-skills/scripts/upgrade_skill.py +431 -0
  222. package/.claude/skills/subagent-driven-development/SKILL.md +268 -0
  223. package/.claude/skills/test-driven-development/SKILL.md +246 -0
  224. package/.claude/skills/test-driven-development/references/testing-anti-patterns.md +192 -0
  225. package/.claude/skills/using-git-worktrees/SKILL.md +266 -0
  226. package/.claude/skills/using-skillstack/SKILL.md +127 -0
  227. package/.claude/skills/vercel-deploy/SKILL.md +166 -0
  228. package/.claude/skills/vercel-deploy/scripts/deploy.sh +249 -0
  229. package/.claude/skills/verification-before-completion/SKILL.md +305 -0
  230. package/.claude/skills/writing-plans/SKILL.md +259 -0
  231. package/README.md +69 -0
  232. package/bin/cli.js +468 -0
  233. package/lib/init.js +333 -0
  234. package/package.json +29 -0
@@ -0,0 +1,611 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Code Formatting Script - Auto-format code files after modification.
4
+
5
+ Features:
6
+ - Supports Python (ruff/black), TypeScript/JavaScript (prettier), Go, Rust
7
+ - Auto-detects and suggests missing formatters
8
+ - Parallel processing for multiple files
9
+ - JSON output for CI/CD integration
10
+ - Respects project config files (.prettierrc, pyproject.toml, etc.)
11
+
12
+ Usage:
13
+ python format_code.py <file_path> [--check-only] [--json]
14
+ python format_code.py <file1> <file2> ... # Multiple files (parallel)
15
+ python format_code.py --dir <directory> # Format directory
16
+ python format_code.py --check-deps # Check dependencies
17
+
18
+ Exit codes:
19
+ 0 - Success (formatted or already formatted)
20
+ 1 - Formatting failed or file not found
21
+ 2 - Formatter not available (install required)
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import argparse
27
+ import subprocess
28
+ import sys
29
+ import os
30
+ import json as json_module
31
+ from pathlib import Path
32
+ from typing import List, Tuple, Optional, Dict, Any
33
+ from dataclasses import dataclass, asdict
34
+ from concurrent.futures import ThreadPoolExecutor, as_completed
35
+ import threading
36
+
37
+
38
+ # Configure stdout/stderr encoding for Windows
39
+ def _configure_stdio() -> None:
40
+ """Ensure UTF-8 encoding for stdout/stderr on Windows"""
41
+ if sys.platform == 'win32':
42
+ os.environ.setdefault('PYTHONIOENCODING', 'utf-8')
43
+ for stream in (sys.stdout, sys.stderr):
44
+ try:
45
+ stream.reconfigure(encoding='utf-8', errors='replace')
46
+ except Exception:
47
+ pass
48
+
49
+ _configure_stdio()
50
+
51
+
52
+ @dataclass
53
+ class FormatResult:
54
+ """Result of formatting a single file"""
55
+ file_path: str
56
+ success: bool
57
+ changed: bool
58
+ message: str
59
+ formatter_used: Optional[str] = None
60
+
61
+ def to_dict(self) -> Dict[str, Any]:
62
+ return asdict(self)
63
+
64
+
65
+ @dataclass
66
+ class DependencyStatus:
67
+ """Status of a formatter dependency"""
68
+ name: str
69
+ available: bool
70
+ version: Optional[str] = None
71
+ install_cmd: Optional[str] = None
72
+
73
+
74
+ class CodeFormatter:
75
+ """Multi-language code formatter with fallback support"""
76
+
77
+ # File extension to language mapping
78
+ EXTENSION_MAP = {
79
+ # Python
80
+ '.py': 'python', '.pyw': 'python', '.pyi': 'python',
81
+ # JavaScript/TypeScript
82
+ '.ts': 'typescript', '.tsx': 'typescript',
83
+ '.js': 'javascript', '.jsx': 'javascript',
84
+ '.mjs': 'javascript', '.cjs': 'javascript',
85
+ # Web
86
+ '.json': 'json', '.css': 'css', '.scss': 'scss', '.less': 'less',
87
+ '.html': 'html', '.vue': 'vue', '.svelte': 'svelte',
88
+ '.yaml': 'yaml', '.yml': 'yaml', '.md': 'markdown',
89
+ # Other languages
90
+ '.go': 'go', '.rs': 'rust', '.sql': 'sql',
91
+ }
92
+
93
+ # Formatter install commands
94
+ INSTALL_COMMANDS = {
95
+ 'ruff': 'pip install ruff',
96
+ 'black': 'pip install black',
97
+ 'prettier': 'npm install -g prettier',
98
+ 'gofmt': 'Included with Go installation',
99
+ 'rustfmt': 'rustup component add rustfmt',
100
+ }
101
+
102
+ def __init__(self, verbose: bool = False, max_workers: int = 4):
103
+ self.verbose = verbose
104
+ self.max_workers = max_workers
105
+ self._formatter_cache: Dict[str, bool] = {}
106
+ self._version_cache: Dict[str, str] = {}
107
+ self._cache_lock = threading.Lock()
108
+
109
+ def _log(self, message: str) -> None:
110
+ if self.verbose:
111
+ print(f"[FORMAT] {message}", file=sys.stderr)
112
+
113
+ def _check_command(self, cmd: str) -> bool:
114
+ """Check if a command is available (thread-safe)"""
115
+ with self._cache_lock:
116
+ if cmd in self._formatter_cache:
117
+ return self._formatter_cache[cmd]
118
+
119
+ try:
120
+ check_cmd = 'where' if sys.platform == 'win32' else 'which'
121
+ result = subprocess.run(
122
+ [check_cmd, cmd],
123
+ capture_output=True,
124
+ text=True,
125
+ timeout=5
126
+ )
127
+ available = result.returncode == 0
128
+ except Exception:
129
+ available = False
130
+
131
+ with self._cache_lock:
132
+ self._formatter_cache[cmd] = available
133
+ return available
134
+
135
+ def _get_version(self, cmd: str, version_flag: str = '--version') -> Optional[str]:
136
+ """Get version of a command"""
137
+ with self._cache_lock:
138
+ if cmd in self._version_cache:
139
+ return self._version_cache[cmd]
140
+
141
+ try:
142
+ result = subprocess.run(
143
+ [cmd, version_flag],
144
+ capture_output=True,
145
+ text=True,
146
+ timeout=5
147
+ )
148
+ if result.returncode == 0:
149
+ version = result.stdout.strip().split('\n')[0]
150
+ with self._cache_lock:
151
+ self._version_cache[cmd] = version
152
+ return version
153
+ except Exception:
154
+ pass
155
+ return None
156
+
157
+ def check_dependencies(self) -> List[DependencyStatus]:
158
+ """Check all formatter dependencies"""
159
+ deps = []
160
+
161
+ # Python formatters
162
+ for name in ['ruff', 'black']:
163
+ available = self._check_command(name)
164
+ deps.append(DependencyStatus(
165
+ name=name,
166
+ available=available,
167
+ version=self._get_version(name) if available else None,
168
+ install_cmd=self.INSTALL_COMMANDS.get(name)
169
+ ))
170
+
171
+ # Node.js formatters
172
+ for name in ['prettier']:
173
+ # Check both global and npx
174
+ available = self._check_command(name) or self._check_command('npx')
175
+ deps.append(DependencyStatus(
176
+ name=name,
177
+ available=available,
178
+ version=self._get_version('prettier') if self._check_command('prettier') else 'via npx',
179
+ install_cmd=self.INSTALL_COMMANDS.get(name)
180
+ ))
181
+
182
+ # Go formatter
183
+ available = self._check_command('gofmt')
184
+ deps.append(DependencyStatus(
185
+ name='gofmt',
186
+ available=available,
187
+ version=self._get_version('go', 'version') if available else None,
188
+ install_cmd=self.INSTALL_COMMANDS.get('gofmt')
189
+ ))
190
+
191
+ # Rust formatter
192
+ available = self._check_command('rustfmt')
193
+ deps.append(DependencyStatus(
194
+ name='rustfmt',
195
+ available=available,
196
+ version=self._get_version('rustfmt') if available else None,
197
+ install_cmd=self.INSTALL_COMMANDS.get('rustfmt')
198
+ ))
199
+
200
+ return deps
201
+
202
+ def _run_formatter(self, cmd: List[str], file_path: str, timeout: int = 30) -> Tuple[bool, str]:
203
+ """Run a formatter command and return (success, message)"""
204
+ try:
205
+ self._log(f"Running: {' '.join(cmd)}")
206
+ result = subprocess.run(
207
+ cmd,
208
+ capture_output=True,
209
+ text=True,
210
+ encoding='utf-8',
211
+ errors='replace',
212
+ timeout=timeout,
213
+ cwd=Path(file_path).parent # Run in file's directory for config detection
214
+ )
215
+ if result.returncode == 0:
216
+ return True, "Formatted successfully"
217
+ else:
218
+ error_msg = result.stderr.strip() or result.stdout.strip() or "Unknown error"
219
+ # Truncate long error messages
220
+ if len(error_msg) > 200:
221
+ error_msg = error_msg[:200] + "..."
222
+ return False, f"Formatter error: {error_msg}"
223
+ except subprocess.TimeoutExpired:
224
+ return False, f"Formatter timeout ({timeout}s)"
225
+ except Exception as e:
226
+ return False, f"Failed to run formatter: {e}"
227
+
228
+ def _format_python(self, file_path: str, check_only: bool = False) -> FormatResult:
229
+ """Format Python file using ruff (preferred) or black"""
230
+ path = Path(file_path).resolve()
231
+
232
+ # Try ruff first (faster, more features)
233
+ if self._check_command('ruff'):
234
+ cmd = ['ruff', 'format']
235
+ if check_only:
236
+ cmd.append('--check')
237
+ cmd.append(str(path))
238
+
239
+ success, message = self._run_formatter(cmd, file_path)
240
+ return FormatResult(
241
+ file_path=str(path),
242
+ success=success,
243
+ changed=success and not check_only,
244
+ message=message,
245
+ formatter_used='ruff'
246
+ )
247
+
248
+ # Fall back to black
249
+ if self._check_command('black'):
250
+ cmd = ['black']
251
+ if check_only:
252
+ cmd.append('--check')
253
+ cmd.append(str(path))
254
+
255
+ success, message = self._run_formatter(cmd, file_path)
256
+ return FormatResult(
257
+ file_path=str(path),
258
+ success=success,
259
+ changed=success and not check_only,
260
+ message=message,
261
+ formatter_used='black'
262
+ )
263
+
264
+ return FormatResult(
265
+ file_path=str(path),
266
+ success=False,
267
+ changed=False,
268
+ message="No Python formatter available. Install: pip install ruff"
269
+ )
270
+
271
+ def _format_js_ts(self, file_path: str, check_only: bool = False) -> FormatResult:
272
+ """Format JavaScript/TypeScript using prettier"""
273
+ path = Path(file_path).resolve()
274
+
275
+ prettier_cmd = None
276
+ if self._check_command('prettier'):
277
+ prettier_cmd = ['prettier']
278
+ elif self._check_command('npx'):
279
+ prettier_cmd = ['npx', '--yes', 'prettier']
280
+
281
+ if prettier_cmd:
282
+ cmd = prettier_cmd.copy()
283
+ if check_only:
284
+ cmd.append('--check')
285
+ else:
286
+ cmd.append('--write')
287
+ cmd.append(str(path))
288
+
289
+ success, message = self._run_formatter(cmd, file_path)
290
+ return FormatResult(
291
+ file_path=str(path),
292
+ success=success,
293
+ changed=success and not check_only,
294
+ message=message,
295
+ formatter_used='prettier'
296
+ )
297
+
298
+ return FormatResult(
299
+ file_path=str(path),
300
+ success=False,
301
+ changed=False,
302
+ message="No JS/TS formatter available. Install: npm install -g prettier"
303
+ )
304
+
305
+ def _format_go(self, file_path: str, check_only: bool = False) -> FormatResult:
306
+ """Format Go file using gofmt"""
307
+ path = Path(file_path).resolve()
308
+
309
+ if self._check_command('gofmt'):
310
+ if check_only:
311
+ # gofmt -d returns diff, non-empty means needs formatting
312
+ cmd = ['gofmt', '-d', str(path)]
313
+ success, message = self._run_formatter(cmd, file_path)
314
+ return FormatResult(
315
+ file_path=str(path),
316
+ success=True,
317
+ changed=bool(message and 'diff' not in message.lower()),
318
+ message="Check completed",
319
+ formatter_used='gofmt'
320
+ )
321
+ else:
322
+ cmd = ['gofmt', '-w', str(path)]
323
+ success, message = self._run_formatter(cmd, file_path)
324
+ return FormatResult(
325
+ file_path=str(path),
326
+ success=success,
327
+ changed=success,
328
+ message=message,
329
+ formatter_used='gofmt'
330
+ )
331
+
332
+ return FormatResult(
333
+ file_path=str(path),
334
+ success=False,
335
+ changed=False,
336
+ message="gofmt not available. Install Go from https://golang.org/"
337
+ )
338
+
339
+ def _format_rust(self, file_path: str, check_only: bool = False) -> FormatResult:
340
+ """Format Rust file using rustfmt"""
341
+ path = Path(file_path).resolve()
342
+
343
+ if self._check_command('rustfmt'):
344
+ cmd = ['rustfmt']
345
+ if check_only:
346
+ cmd.append('--check')
347
+ cmd.append(str(path))
348
+
349
+ success, message = self._run_formatter(cmd, file_path)
350
+ return FormatResult(
351
+ file_path=str(path),
352
+ success=success,
353
+ changed=success and not check_only,
354
+ message=message,
355
+ formatter_used='rustfmt'
356
+ )
357
+
358
+ return FormatResult(
359
+ file_path=str(path),
360
+ success=False,
361
+ changed=False,
362
+ message="rustfmt not available. Install: rustup component add rustfmt"
363
+ )
364
+
365
+ def _format_json(self, file_path: str, check_only: bool = False) -> FormatResult:
366
+ """Format JSON using prettier or Python json module"""
367
+ path = Path(file_path).resolve()
368
+
369
+ # Try prettier first
370
+ prettier_result = self._format_js_ts(file_path, check_only)
371
+ if prettier_result.success:
372
+ return prettier_result
373
+
374
+ # Fall back to Python json module
375
+ try:
376
+ content = path.read_text(encoding='utf-8')
377
+ parsed = json_module.loads(content)
378
+ formatted = json_module.dumps(parsed, indent=2, ensure_ascii=False) + '\n'
379
+
380
+ if check_only:
381
+ changed = content != formatted
382
+ return FormatResult(
383
+ file_path=str(path),
384
+ success=True,
385
+ changed=changed,
386
+ message="Check passed" if not changed else "File needs formatting",
387
+ formatter_used='python-json'
388
+ )
389
+
390
+ if content != formatted:
391
+ path.write_text(formatted, encoding='utf-8')
392
+ return FormatResult(
393
+ file_path=str(path),
394
+ success=True,
395
+ changed=True,
396
+ message="Formatted successfully",
397
+ formatter_used='python-json'
398
+ )
399
+
400
+ return FormatResult(
401
+ file_path=str(path),
402
+ success=True,
403
+ changed=False,
404
+ message="Already formatted",
405
+ formatter_used='python-json'
406
+ )
407
+ except Exception as e:
408
+ return FormatResult(
409
+ file_path=str(path),
410
+ success=False,
411
+ changed=False,
412
+ message=f"JSON format error: {e}"
413
+ )
414
+
415
+ def format_file(self, file_path: str, check_only: bool = False) -> FormatResult:
416
+ """Format a single file based on its extension"""
417
+ path = Path(file_path)
418
+
419
+ if not path.exists():
420
+ return FormatResult(
421
+ file_path=str(path),
422
+ success=False,
423
+ changed=False,
424
+ message=f"File not found: {file_path}"
425
+ )
426
+
427
+ ext = path.suffix.lower()
428
+ language = self.EXTENSION_MAP.get(ext)
429
+
430
+ if language == 'python':
431
+ return self._format_python(file_path, check_only)
432
+ elif language in ('typescript', 'javascript', 'css', 'scss', 'less',
433
+ 'markdown', 'html', 'vue', 'svelte', 'yaml'):
434
+ return self._format_js_ts(file_path, check_only)
435
+ elif language == 'json':
436
+ return self._format_json(file_path, check_only)
437
+ elif language == 'go':
438
+ return self._format_go(file_path, check_only)
439
+ elif language == 'rust':
440
+ return self._format_rust(file_path, check_only)
441
+ elif language == 'sql':
442
+ # SQL formatting via prettier-plugin-sql (if available)
443
+ return self._format_js_ts(file_path, check_only)
444
+ else:
445
+ return FormatResult(
446
+ file_path=str(path),
447
+ success=True,
448
+ changed=False,
449
+ message=f"No formatter configured for extension: {ext}"
450
+ )
451
+
452
+ def format_files(self, file_paths: List[str], check_only: bool = False) -> List[FormatResult]:
453
+ """Format multiple files (parallel processing)"""
454
+ if len(file_paths) == 1:
455
+ return [self.format_file(file_paths[0], check_only)]
456
+
457
+ results = []
458
+ with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
459
+ future_to_path = {
460
+ executor.submit(self.format_file, path, check_only): path
461
+ for path in file_paths
462
+ }
463
+ for future in as_completed(future_to_path):
464
+ result = future.result()
465
+ results.append(result)
466
+ self._log(f"{result.file_path}: {result.message}")
467
+
468
+ # Sort by original order
469
+ path_order = {path: i for i, path in enumerate(file_paths)}
470
+ results.sort(key=lambda r: path_order.get(r.file_path, 999))
471
+ return results
472
+
473
+ def format_directory(self, dir_path: str, check_only: bool = False,
474
+ extensions: Optional[List[str]] = None) -> List[FormatResult]:
475
+ """Format all supported files in a directory"""
476
+ path = Path(dir_path)
477
+ if not path.exists():
478
+ return [FormatResult(
479
+ file_path=str(path),
480
+ success=False,
481
+ changed=False,
482
+ message=f"Directory not found: {dir_path}"
483
+ )]
484
+
485
+ if extensions is None:
486
+ extensions = list(self.EXTENSION_MAP.keys())
487
+
488
+ files = []
489
+ for ext in extensions:
490
+ files.extend(path.rglob(f'*{ext}'))
491
+
492
+ # Filter out common exclusions
493
+ exclude_patterns = [
494
+ 'node_modules', '__pycache__', '.git', 'dist', 'build',
495
+ 'venv', '.venv', 'target', '.next', '.nuxt', 'coverage'
496
+ ]
497
+ files = [f for f in files if not any(p in str(f) for p in exclude_patterns)]
498
+
499
+ return self.format_files([str(f) for f in files], check_only)
500
+
501
+
502
+ def print_dependencies(deps: List[DependencyStatus]) -> None:
503
+ """Print dependency status"""
504
+ print("\n" + "=" * 60)
505
+ print("🔧 FORMATTER DEPENDENCIES")
506
+ print("=" * 60)
507
+
508
+ for dep in deps:
509
+ status = "✅" if dep.available else "❌"
510
+ version = f" ({dep.version})" if dep.version else ""
511
+ print(f" {status} {dep.name}{version}")
512
+ if not dep.available and dep.install_cmd:
513
+ print(f" Install: {dep.install_cmd}")
514
+
515
+ print("=" * 60)
516
+
517
+
518
+ def print_summary(results: List[FormatResult], json_output: bool = False) -> None:
519
+ """Print a summary of formatting results"""
520
+ if json_output:
521
+ output = {
522
+ 'results': [r.to_dict() for r in results],
523
+ 'summary': {
524
+ 'total': len(results),
525
+ 'success': sum(1 for r in results if r.success),
526
+ 'changed': sum(1 for r in results if r.changed),
527
+ 'failed': sum(1 for r in results if not r.success)
528
+ }
529
+ }
530
+ print(json_module.dumps(output, indent=2, ensure_ascii=False))
531
+ return
532
+
533
+ print("\n" + "=" * 60)
534
+ print("📋 FORMATTING SUMMARY")
535
+ print("=" * 60)
536
+
537
+ success_count = sum(1 for r in results if r.success)
538
+ changed_count = sum(1 for r in results if r.changed)
539
+ failed_count = len(results) - success_count
540
+
541
+ for result in results:
542
+ status = "✅" if result.success else "❌"
543
+ change = "(changed)" if result.changed else ""
544
+ formatter = f"[{result.formatter_used}]" if result.formatter_used else ""
545
+ print(f" {status} {result.file_path} {formatter} {change}")
546
+ if not result.success:
547
+ print(f" → {result.message}")
548
+
549
+ print("-" * 60)
550
+ print(f"Total: {len(results)} | ✅ Success: {success_count} | 🔄 Changed: {changed_count} | ❌ Failed: {failed_count}")
551
+ print("=" * 60)
552
+
553
+
554
+ def main() -> int:
555
+ parser = argparse.ArgumentParser(
556
+ description="Format code files (Python, TypeScript, JavaScript, Go, Rust, etc.)",
557
+ formatter_class=argparse.RawDescriptionHelpFormatter,
558
+ epilog="""
559
+ Examples:
560
+ python format_code.py src/app.py # Format single file
561
+ python format_code.py src/app.py src/utils.ts # Format multiple files (parallel)
562
+ python format_code.py --dir src/ # Format directory
563
+ python format_code.py src/app.py --check-only # Check without modifying
564
+ python format_code.py --check-deps # Check formatter dependencies
565
+ python format_code.py src/app.py --json # JSON output for CI/CD
566
+ """
567
+ )
568
+ parser.add_argument('files', nargs='*', help='Files to format')
569
+ parser.add_argument('--dir', '-d', help='Format all supported files in directory')
570
+ parser.add_argument('--check-only', '-c', action='store_true', help='Check if files need formatting without modifying')
571
+ parser.add_argument('--check-deps', action='store_true', help='Check formatter dependencies')
572
+ parser.add_argument('--json', '-j', action='store_true', help='Output results as JSON')
573
+ parser.add_argument('--verbose', '-v', action='store_true', help='Show detailed output')
574
+ parser.add_argument('--ext', action='append', help='File extensions to format (can specify multiple)')
575
+ parser.add_argument('--workers', '-w', type=int, default=4, help='Number of parallel workers (default: 4)')
576
+
577
+ args = parser.parse_args()
578
+
579
+ formatter = CodeFormatter(verbose=args.verbose, max_workers=args.workers)
580
+
581
+ # Check dependencies mode
582
+ if args.check_deps:
583
+ deps = formatter.check_dependencies()
584
+ if args.json:
585
+ print(json_module.dumps([asdict(d) for d in deps], indent=2))
586
+ else:
587
+ print_dependencies(deps)
588
+ return 0 if all(d.available for d in deps) else 2
589
+
590
+ if not args.files and not args.dir:
591
+ parser.print_help()
592
+ return 1
593
+
594
+ if args.dir:
595
+ results = formatter.format_directory(args.dir, check_only=args.check_only, extensions=args.ext)
596
+ else:
597
+ results = formatter.format_files(args.files, check_only=args.check_only)
598
+
599
+ print_summary(results, json_output=args.json)
600
+
601
+ # Return appropriate exit code
602
+ if all(r.success for r in results):
603
+ return 0
604
+ elif any("not available" in (r.message or "") for r in results):
605
+ return 2
606
+ else:
607
+ return 1
608
+
609
+
610
+ if __name__ == "__main__":
611
+ sys.exit(main())