codeforge-dev 1.7.0 → 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 (158) hide show
  1. package/.devcontainer/.env +4 -6
  2. package/.devcontainer/.env.example +29 -0
  3. package/.devcontainer/.gitignore +8 -0
  4. package/.devcontainer/.secrets.example +12 -0
  5. package/.devcontainer/CHANGELOG.md +181 -0
  6. package/.devcontainer/CLAUDE.md +57 -20
  7. package/.devcontainer/README.md +111 -56
  8. package/.devcontainer/config/{main-system-prompt.md → defaults/main-system-prompt.md} +72 -0
  9. package/.devcontainer/config/defaults/rules/spec-workflow.md +67 -0
  10. package/.devcontainer/config/defaults/rules/workspace-scope.md +7 -0
  11. package/.devcontainer/config/defaults/settings.json +67 -0
  12. package/.devcontainer/config/file-manifest.json +32 -0
  13. package/.devcontainer/devcontainer.json +20 -0
  14. package/.devcontainer/docs/configuration-reference.md +90 -0
  15. package/.devcontainer/docs/keybindings.md +100 -0
  16. package/.devcontainer/docs/optional-features.md +129 -0
  17. package/.devcontainer/docs/plugins.md +154 -0
  18. package/.devcontainer/docs/troubleshooting.md +128 -0
  19. package/.devcontainer/features/agent-browser/install.sh +6 -0
  20. package/.devcontainer/features/ast-grep/install.sh +6 -0
  21. package/.devcontainer/features/biome/README.md +27 -0
  22. package/.devcontainer/features/biome/install.sh +6 -0
  23. package/.devcontainer/features/ccburn/install.sh +6 -0
  24. package/.devcontainer/features/ccstatusline/devcontainer-feature.json +5 -0
  25. package/.devcontainer/features/ccstatusline/install.sh +7 -0
  26. package/.devcontainer/features/ccusage/install.sh +6 -0
  27. package/.devcontainer/features/claude-monitor/install.sh +6 -0
  28. package/.devcontainer/features/dprint/README.md +30 -0
  29. package/.devcontainer/features/dprint/devcontainer-feature.json +18 -0
  30. package/.devcontainer/features/dprint/install.sh +131 -0
  31. package/.devcontainer/features/hadolint/README.md +35 -0
  32. package/.devcontainer/features/hadolint/devcontainer-feature.json +13 -0
  33. package/.devcontainer/features/hadolint/install.sh +86 -0
  34. package/.devcontainer/features/lsp-servers/devcontainer-feature.json +5 -0
  35. package/.devcontainer/features/lsp-servers/install.sh +7 -0
  36. package/.devcontainer/features/mcp-qdrant/devcontainer-feature.json +5 -0
  37. package/.devcontainer/features/mcp-qdrant/install.sh +13 -6
  38. package/.devcontainer/features/mcp-reasoner/devcontainer-feature.json +5 -0
  39. package/.devcontainer/features/mcp-reasoner/install.sh +8 -1
  40. package/.devcontainer/features/notify-hook/devcontainer-feature.json +5 -0
  41. package/.devcontainer/features/notify-hook/install.sh +7 -0
  42. package/.devcontainer/features/ruff/README.md +26 -0
  43. package/.devcontainer/features/ruff/devcontainer-feature.json +21 -0
  44. package/.devcontainer/features/ruff/install.sh +74 -0
  45. package/.devcontainer/features/shellcheck/README.md +38 -0
  46. package/.devcontainer/features/shellcheck/devcontainer-feature.json +13 -0
  47. package/.devcontainer/features/shellcheck/install.sh +24 -0
  48. package/.devcontainer/features/shfmt/README.md +37 -0
  49. package/.devcontainer/features/shfmt/devcontainer-feature.json +13 -0
  50. package/.devcontainer/features/shfmt/install.sh +85 -0
  51. package/.devcontainer/features/splitrail/devcontainer-feature.json +5 -0
  52. package/.devcontainer/features/splitrail/install.sh +7 -0
  53. package/.devcontainer/features/tmux/install.sh +8 -0
  54. package/.devcontainer/features/tree-sitter/install.sh +6 -0
  55. package/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json +104 -104
  56. package/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/.claude-plugin/plugin.json +7 -0
  57. package/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/README.md +158 -0
  58. package/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/hooks/hooks.json +39 -0
  59. package/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/collect-edited-files.py +47 -0
  60. package/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/format-on-stop.py +297 -0
  61. package/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/lint-file.py +536 -0
  62. package/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/syntax-validator.py +146 -0
  63. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/.claude-plugin/plugin.json +1 -1
  64. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/scripts/__pycache__/format-on-stop.cpython-314.pyc +0 -0
  65. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/scripts/format-on-stop.py +114 -9
  66. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/.claude-plugin/plugin.json +1 -1
  67. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/hooks/hooks.json +4 -5
  68. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/scripts/__pycache__/lint-file.cpython-314.pyc +0 -0
  69. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/scripts/lint-file.py +478 -76
  70. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/.claude-plugin/plugin.json +1 -1
  71. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/AGENT-REDIRECTION.md +226 -0
  72. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/architect.md +94 -1
  73. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/bash-exec.md +4 -4
  74. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/claude-guide.md +14 -23
  75. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/debug-logs.md +20 -0
  76. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/dependency-analyst.md +20 -0
  77. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/doc-writer.md +99 -1
  78. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/explorer.md +20 -0
  79. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/generalist.md +152 -9
  80. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/git-archaeologist.md +18 -0
  81. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/migrator.md +114 -1
  82. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/perf-profiler.md +24 -0
  83. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/refactorer.md +101 -1
  84. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/researcher.md +33 -1
  85. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/security-auditor.md +24 -0
  86. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/spec-writer.md +65 -24
  87. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/statusline-config.md +3 -3
  88. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/test-writer.md +99 -1
  89. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/hooks/hooks.json +100 -56
  90. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/advisory-test-runner.cpython-314.pyc +0 -0
  91. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/collect-edited-files.cpython-314.pyc +0 -0
  92. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/commit-reminder.cpython-314.pyc +0 -0
  93. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/git-state-injector.cpython-314.pyc +0 -0
  94. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/redirect-builtin-agents.cpython-314.pyc +0 -0
  95. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/ticket-linker.cpython-314.pyc +0 -0
  96. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/todo-harvester.cpython-314.pyc +0 -0
  97. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/advisory-test-runner.py +174 -0
  98. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/collect-edited-files.py +8 -6
  99. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/commit-reminder.py +90 -0
  100. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/git-state-injector.py +114 -0
  101. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/skill-suggester.py +61 -0
  102. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/spec-reminder.py +121 -0
  103. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/ticket-linker.py +137 -0
  104. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/todo-harvester.py +130 -0
  105. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/api-design/SKILL.md +224 -0
  106. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/api-design/references/error-handling.md +166 -0
  107. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/api-design/references/rest-conventions.md +215 -0
  108. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/ast-grep-patterns/SKILL.md +211 -0
  109. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/ast-grep-patterns/references/language-patterns.md +327 -0
  110. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/dependency-management/SKILL.md +134 -0
  111. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/dependency-management/references/ecosystem-commands.md +264 -0
  112. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/dependency-management/references/license-compliance.md +80 -0
  113. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/documentation-patterns/SKILL.md +153 -0
  114. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/documentation-patterns/references/api-doc-templates.md +221 -0
  115. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/documentation-patterns/references/docstring-formats.md +296 -0
  116. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/migration-patterns/SKILL.md +150 -0
  117. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/migration-patterns/references/javascript-migrations.md +179 -0
  118. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/migration-patterns/references/python-migrations.md +141 -0
  119. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-check/SKILL.md +86 -0
  120. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-init/SKILL.md +97 -0
  121. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-init/references/backlog-template.md +7 -0
  122. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-init/references/roadmap-template.md +13 -0
  123. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-new/SKILL.md +101 -0
  124. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-new/references/template.md +110 -0
  125. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-update/SKILL.md +124 -0
  126. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/specification-writing/SKILL.md +32 -0
  127. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/__pycache__/block-dangerous.cpython-314.pyc +0 -0
  128. package/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/hooks/hooks.json +1 -1
  129. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/__pycache__/guard-protected.cpython-314.pyc +0 -0
  130. package/.devcontainer/scripts/check-setup.sh +72 -0
  131. package/.devcontainer/scripts/setup-aliases.sh +43 -3
  132. package/.devcontainer/scripts/setup-auth.sh +74 -0
  133. package/.devcontainer/scripts/setup-config.sh +117 -24
  134. package/.devcontainer/scripts/setup-update-claude.sh +8 -0
  135. package/.devcontainer/scripts/setup.sh +46 -13
  136. package/README.md +23 -190
  137. package/package.json +42 -42
  138. package/setup.js +245 -71
  139. package/.devcontainer/config/settings.json +0 -70
  140. package/.devcontainer/features/claude-code/README.md +0 -498
  141. package/.devcontainer/features/claude-code/config/settings.json +0 -72
  142. package/.devcontainer/features/claude-code/config/system-prompt.md +0 -118
  143. package/.devcontainer/features/claude-code/config/world-building-sp.md +0 -1432
  144. package/.devcontainer/features/claude-code/devcontainer-feature.json +0 -42
  145. package/.devcontainer/features/claude-code/install.sh +0 -466
  146. package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/.claude-plugin/plugin.json +0 -7
  147. package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/hooks/hooks.json +0 -17
  148. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/.claude-plugin/plugin.json +0 -6
  149. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/config/planning-instructions.md +0 -14
  150. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/functional-conjuring-map.md +0 -989
  151. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/hooks/hooks.json +0 -33
  152. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/__pycache__/post-enhance-task.cpython-314.pyc +0 -0
  153. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhance-planning.py +0 -71
  154. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhancers/enhance-plan.sh +0 -68
  155. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhancers/enhance-task.sh +0 -120
  156. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/post-enhance-plan.py +0 -133
  157. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/post-enhance-task.py +0 -253
  158. /package/.devcontainer/config/{keybindings.json → defaults/keybindings.json} +0 -0
@@ -0,0 +1,297 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Unified batch formatter — runs as a Stop hook.
4
+
5
+ Reads file paths collected by collect-edited-files.py during the
6
+ conversation turn, deduplicates them, and formats each based on
7
+ extension:
8
+ .py / .pyi → Ruff format (fallback: Black)
9
+ .go → gofmt
10
+ .js/.jsx/.ts/.tsx/.mjs/.cjs/.mts/.cts → Biome check --write
11
+ .css/.json/.jsonc/.graphql/.gql → Biome check --write
12
+ .html/.vue/.svelte/.astro → Biome check --write
13
+ .sh/.bash/.zsh/.mksh/.bats → shfmt -w
14
+ .md/.markdown → dprint fmt
15
+ .yaml/.yml → dprint fmt
16
+ .toml → dprint fmt
17
+ Dockerfile / .dockerfile → dprint fmt
18
+ .rs → rustfmt
19
+
20
+ Always cleans up the temp file. Always exits 0.
21
+ """
22
+
23
+ import json
24
+ import os
25
+ import subprocess
26
+ import sys
27
+ from pathlib import Path
28
+
29
+ # ── Extension sets ──────────────────────────────────────────────────
30
+
31
+ PYTHON_EXTS = {".py", ".pyi"}
32
+ GO_EXTS = {".go"}
33
+ BIOME_EXTS = {
34
+ ".js",
35
+ ".jsx",
36
+ ".ts",
37
+ ".tsx",
38
+ ".mjs",
39
+ ".cjs",
40
+ ".mts",
41
+ ".cts",
42
+ ".css",
43
+ ".json",
44
+ ".jsonc",
45
+ ".graphql",
46
+ ".gql",
47
+ ".html",
48
+ ".vue",
49
+ ".svelte",
50
+ ".astro",
51
+ }
52
+ SHELL_EXTS = {".sh", ".bash", ".zsh", ".mksh", ".bats"}
53
+ DPRINT_EXTS = {".md", ".markdown", ".yaml", ".yml", ".toml"}
54
+ RUST_EXTS = {".rs"}
55
+
56
+ # ── Fallback paths ──────────────────────────────────────────────────
57
+
58
+ BLACK_PATH_FALLBACK = "/usr/local/py-utils/bin/black"
59
+ GOFMT_PATH_FALLBACK = "/usr/local/go/bin/gofmt"
60
+ DPRINT_CONFIG = "/usr/local/share/dprint/dprint.json"
61
+
62
+ # ── Tool resolution ─────────────────────────────────────────────────
63
+
64
+
65
+ def _resolve_tool(name: str, fallback: str = "") -> str | None:
66
+ """Find tool via PATH first, fall back to hardcoded path."""
67
+ try:
68
+ result = subprocess.run(["which", name], capture_output=True, text=True)
69
+ if result.returncode == 0:
70
+ return result.stdout.strip()
71
+ except Exception:
72
+ pass
73
+ if fallback and os.path.exists(fallback):
74
+ return fallback
75
+ return None
76
+
77
+
78
+ def find_tool_upward(file_path: str, tool_name: str) -> str | None:
79
+ """Walk up from file directory looking for node_modules/.bin/<tool>."""
80
+ current = Path(file_path).parent
81
+ for _ in range(20):
82
+ candidate = current / "node_modules" / ".bin" / tool_name
83
+ if candidate.is_file():
84
+ return str(candidate)
85
+ parent = current.parent
86
+ if parent == current:
87
+ break
88
+ current = parent
89
+ return None
90
+
91
+
92
+ def find_global_tool(tool_name: str) -> str | None:
93
+ """Check if tool is available globally."""
94
+ try:
95
+ result = subprocess.run(
96
+ ["which", tool_name],
97
+ capture_output=True,
98
+ text=True,
99
+ )
100
+ if result.returncode == 0:
101
+ return result.stdout.strip()
102
+ except Exception:
103
+ pass
104
+ return None
105
+
106
+
107
+ def find_biome(file_path: str) -> str | None:
108
+ """Find biome binary: project-local first, then global."""
109
+ local = find_tool_upward(file_path, "biome")
110
+ if local:
111
+ return local
112
+ return find_global_tool("biome")
113
+
114
+
115
+ # ── Formatters ──────────────────────────────────────────────────────
116
+
117
+
118
+ def format_python(file_path: str) -> None:
119
+ """Format with Ruff (preferred) or Black (fallback)."""
120
+ ruff = _resolve_tool("ruff")
121
+ if ruff:
122
+ try:
123
+ subprocess.run(
124
+ [ruff, "format", "--quiet", file_path],
125
+ capture_output=True,
126
+ timeout=10,
127
+ )
128
+ return
129
+ except (subprocess.TimeoutExpired, OSError):
130
+ pass
131
+
132
+ # Fallback to Black
133
+ black = _resolve_tool("black", BLACK_PATH_FALLBACK)
134
+ if not black:
135
+ return
136
+ try:
137
+ subprocess.run(
138
+ [black, "--quiet", file_path],
139
+ capture_output=True,
140
+ timeout=10,
141
+ )
142
+ except (subprocess.TimeoutExpired, OSError):
143
+ pass
144
+
145
+
146
+ def format_go(file_path: str) -> None:
147
+ """Format with gofmt."""
148
+ gofmt = _resolve_tool("gofmt", GOFMT_PATH_FALLBACK)
149
+ if not gofmt:
150
+ return
151
+ try:
152
+ subprocess.run(
153
+ [gofmt, "-w", file_path],
154
+ capture_output=True,
155
+ timeout=10,
156
+ )
157
+ except (subprocess.TimeoutExpired, OSError):
158
+ pass
159
+
160
+
161
+ def format_biome(file_path: str) -> None:
162
+ """Format with Biome in safe mode (no --unsafe)."""
163
+ biome = find_biome(file_path)
164
+ if not biome:
165
+ return
166
+ try:
167
+ subprocess.run(
168
+ [biome, "check", "--write", file_path],
169
+ capture_output=True,
170
+ timeout=12,
171
+ )
172
+ except (subprocess.TimeoutExpired, OSError):
173
+ pass
174
+
175
+
176
+ def format_shell(file_path: str) -> None:
177
+ """Format with shfmt."""
178
+ shfmt = _resolve_tool("shfmt")
179
+ if not shfmt:
180
+ return
181
+ try:
182
+ subprocess.run(
183
+ [shfmt, "-w", file_path],
184
+ capture_output=True,
185
+ timeout=10,
186
+ )
187
+ except (subprocess.TimeoutExpired, OSError):
188
+ pass
189
+
190
+
191
+ def format_dprint(file_path: str) -> None:
192
+ """Format with dprint using the global config."""
193
+ dprint = _resolve_tool("dprint")
194
+ if not dprint:
195
+ return
196
+ if not os.path.isfile(DPRINT_CONFIG):
197
+ return
198
+ try:
199
+ subprocess.run(
200
+ [dprint, "fmt", "--config", DPRINT_CONFIG, file_path],
201
+ capture_output=True,
202
+ timeout=10,
203
+ )
204
+ except (subprocess.TimeoutExpired, OSError):
205
+ pass
206
+
207
+
208
+ def format_rust(file_path: str) -> None:
209
+ """Format with rustfmt (conditional — only if installed)."""
210
+ rustfmt = _resolve_tool("rustfmt")
211
+ if not rustfmt:
212
+ return
213
+ try:
214
+ subprocess.run(
215
+ [rustfmt, file_path],
216
+ capture_output=True,
217
+ timeout=10,
218
+ )
219
+ except (subprocess.TimeoutExpired, OSError):
220
+ pass
221
+
222
+
223
+ # ── Dispatch ────────────────────────────────────────────────────────
224
+
225
+
226
+ def format_file(file_path: str) -> None:
227
+ """Dispatch to the correct formatter based on extension / filename."""
228
+ path = Path(file_path)
229
+ ext = path.suffix.lower()
230
+ name = path.name
231
+
232
+ if ext in PYTHON_EXTS:
233
+ format_python(file_path)
234
+ elif ext in GO_EXTS:
235
+ format_go(file_path)
236
+ elif ext in BIOME_EXTS:
237
+ format_biome(file_path)
238
+ elif ext in SHELL_EXTS:
239
+ format_shell(file_path)
240
+ elif ext in DPRINT_EXTS:
241
+ format_dprint(file_path)
242
+ elif ext in RUST_EXTS:
243
+ format_rust(file_path)
244
+ elif name == "Dockerfile" or ext == ".dockerfile":
245
+ format_dprint(file_path)
246
+
247
+
248
+ # ── Main ────────────────────────────────────────────────────────────
249
+
250
+
251
+ def main():
252
+ try:
253
+ input_data = json.load(sys.stdin)
254
+ except (json.JSONDecodeError, ValueError):
255
+ sys.exit(0)
256
+
257
+ # Prevent infinite loops if Stop hook triggers another stop
258
+ if input_data.get("stop_hook_active"):
259
+ sys.exit(0)
260
+
261
+ session_id = input_data.get("session_id", "")
262
+ if not session_id:
263
+ sys.exit(0)
264
+
265
+ tmp_path = f"/tmp/claude-cq-edited-{session_id}"
266
+
267
+ try:
268
+ with open(tmp_path) as f:
269
+ raw_paths = f.read().splitlines()
270
+ except FileNotFoundError:
271
+ sys.exit(0)
272
+ except OSError:
273
+ sys.exit(0)
274
+ finally:
275
+ # Always clean up the temp file
276
+ try:
277
+ os.unlink(tmp_path)
278
+ except OSError:
279
+ pass
280
+
281
+ # Deduplicate while preserving order, filter to existing files
282
+ seen: set[str] = set()
283
+ paths: list[str] = []
284
+ for p in raw_paths:
285
+ p = p.strip()
286
+ if p and p not in seen and os.path.isfile(p):
287
+ seen.add(p)
288
+ paths.append(p)
289
+
290
+ for path in paths:
291
+ format_file(path)
292
+
293
+ sys.exit(0)
294
+
295
+
296
+ if __name__ == "__main__":
297
+ main()