codeforge-dev 1.5.8 → 1.8.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 (176) hide show
  1. package/.devcontainer/.env +4 -5
  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 +186 -0
  6. package/.devcontainer/CLAUDE.md +108 -21
  7. package/.devcontainer/README.md +173 -57
  8. package/.devcontainer/config/defaults/keybindings.json +5 -0
  9. package/.devcontainer/config/{main-system-prompt.md → defaults/main-system-prompt.md} +135 -2
  10. package/.devcontainer/config/{settings.json → defaults/settings.json} +25 -6
  11. package/.devcontainer/config/file-manifest.json +20 -0
  12. package/.devcontainer/devcontainer.json +38 -2
  13. package/.devcontainer/docs/configuration-reference.md +90 -0
  14. package/.devcontainer/docs/keybindings.md +100 -0
  15. package/.devcontainer/docs/optional-features.md +129 -0
  16. package/.devcontainer/docs/plugins.md +154 -0
  17. package/.devcontainer/docs/troubleshooting.md +128 -0
  18. package/.devcontainer/features/README.md +21 -7
  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/README.md +60 -0
  24. package/.devcontainer/features/ccburn/devcontainer-feature.json +38 -0
  25. package/.devcontainer/features/ccburn/install.sh +180 -0
  26. package/.devcontainer/features/ccstatusline/README.md +22 -21
  27. package/.devcontainer/features/ccstatusline/devcontainer-feature.json +6 -1
  28. package/.devcontainer/features/ccstatusline/install.sh +55 -16
  29. package/.devcontainer/features/ccusage/install.sh +6 -0
  30. package/.devcontainer/features/claude-monitor/install.sh +6 -0
  31. package/.devcontainer/features/dprint/README.md +30 -0
  32. package/.devcontainer/features/dprint/devcontainer-feature.json +18 -0
  33. package/.devcontainer/features/dprint/install.sh +131 -0
  34. package/.devcontainer/features/hadolint/README.md +35 -0
  35. package/.devcontainer/features/hadolint/devcontainer-feature.json +13 -0
  36. package/.devcontainer/features/hadolint/install.sh +86 -0
  37. package/.devcontainer/features/lsp-servers/devcontainer-feature.json +5 -0
  38. package/.devcontainer/features/lsp-servers/install.sh +7 -0
  39. package/.devcontainer/features/mcp-qdrant/devcontainer-feature.json +6 -1
  40. package/.devcontainer/features/mcp-qdrant/install.sh +13 -6
  41. package/.devcontainer/features/mcp-reasoner/devcontainer-feature.json +6 -1
  42. package/.devcontainer/features/mcp-reasoner/install.sh +8 -1
  43. package/.devcontainer/features/notify-hook/devcontainer-feature.json +5 -0
  44. package/.devcontainer/features/notify-hook/install.sh +7 -0
  45. package/.devcontainer/features/ruff/README.md +26 -0
  46. package/.devcontainer/features/ruff/devcontainer-feature.json +21 -0
  47. package/.devcontainer/features/ruff/install.sh +74 -0
  48. package/.devcontainer/features/shellcheck/README.md +38 -0
  49. package/.devcontainer/features/shellcheck/devcontainer-feature.json +13 -0
  50. package/.devcontainer/features/shellcheck/install.sh +24 -0
  51. package/.devcontainer/features/shfmt/README.md +37 -0
  52. package/.devcontainer/features/shfmt/devcontainer-feature.json +13 -0
  53. package/.devcontainer/features/shfmt/install.sh +85 -0
  54. package/.devcontainer/features/splitrail/devcontainer-feature.json +5 -0
  55. package/.devcontainer/features/splitrail/install.sh +7 -0
  56. package/.devcontainer/features/tmux/install.sh +8 -0
  57. package/.devcontainer/features/tree-sitter/install.sh +6 -0
  58. package/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json +3 -10
  59. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/.claude-plugin/plugin.json +1 -1
  60. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/scripts/__pycache__/format-on-stop.cpython-314.pyc +0 -0
  61. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/scripts/format-on-stop.py +133 -13
  62. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/.claude-plugin/plugin.json +1 -1
  63. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/hooks/hooks.json +4 -5
  64. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/scripts/__pycache__/lint-file.cpython-314.pyc +0 -0
  65. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/scripts/lint-file.py +477 -78
  66. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/.claude-plugin/plugin.json +1 -1
  67. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/AGENT-REDIRECTION.md +226 -0
  68. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/REVIEW-RUBRIC.md +440 -0
  69. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/architect.md +207 -0
  70. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/bash-exec.md +173 -0
  71. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/claude-guide.md +146 -0
  72. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/debug-logs.md +2 -0
  73. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/dependency-analyst.md +250 -0
  74. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/doc-writer.md +246 -0
  75. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/explorer.md +237 -0
  76. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/generalist.md +134 -0
  77. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/git-archaeologist.md +242 -0
  78. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/migrator.md +201 -0
  79. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/perf-profiler.md +265 -0
  80. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/refactorer.md +213 -0
  81. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/researcher.md +195 -0
  82. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/security-auditor.md +289 -0
  83. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/spec-writer.md +297 -0
  84. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/statusline-config.md +188 -0
  85. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/test-writer.md +248 -0
  86. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/hooks/hooks.json +51 -0
  87. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/advisory-test-runner.cpython-314.pyc +0 -0
  88. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/collect-edited-files.cpython-314.pyc +0 -0
  89. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/commit-reminder.cpython-314.pyc +0 -0
  90. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/git-state-injector.cpython-314.pyc +0 -0
  91. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/guard-readonly-bash.cpython-314.pyc +0 -0
  92. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/redirect-builtin-agents.cpython-314.pyc +0 -0
  93. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/skill-suggester.cpython-314.pyc +0 -0
  94. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/syntax-validator.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/__pycache__/verify-no-regression.cpython-314.pyc +0 -0
  98. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/verify-tests-pass.cpython-314.pyc +0 -0
  99. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/advisory-test-runner.py +174 -0
  100. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/collect-edited-files.py +8 -6
  101. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/commit-reminder.py +90 -0
  102. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/git-state-injector.py +114 -0
  103. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/guard-readonly-bash.py +611 -0
  104. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/redirect-builtin-agents.py +83 -0
  105. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/skill-suggester.py +146 -2
  106. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/syntax-validator.py +9 -4
  107. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/ticket-linker.py +137 -0
  108. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/todo-harvester.py +130 -0
  109. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/verify-no-regression.py +221 -0
  110. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/verify-tests-pass.py +176 -0
  111. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/api-design/SKILL.md +224 -0
  112. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/api-design/references/error-handling.md +166 -0
  113. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/api-design/references/rest-conventions.md +215 -0
  114. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/ast-grep-patterns/SKILL.md +211 -0
  115. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/ast-grep-patterns/references/language-patterns.md +327 -0
  116. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/claude-agent-sdk/SKILL.md +599 -0
  117. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/claude-agent-sdk/references/sdk-typescript-reference.md +954 -0
  118. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/dependency-management/SKILL.md +134 -0
  119. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/dependency-management/references/ecosystem-commands.md +264 -0
  120. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/dependency-management/references/license-compliance.md +80 -0
  121. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/documentation-patterns/SKILL.md +153 -0
  122. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/documentation-patterns/references/api-doc-templates.md +221 -0
  123. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/documentation-patterns/references/docstring-formats.md +296 -0
  124. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/git-forensics/SKILL.md +276 -0
  125. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/git-forensics/references/advanced-commands.md +332 -0
  126. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/git-forensics/references/investigation-playbooks.md +319 -0
  127. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/migration-patterns/SKILL.md +150 -0
  128. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/migration-patterns/references/javascript-migrations.md +179 -0
  129. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/migration-patterns/references/python-migrations.md +141 -0
  130. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/performance-profiling/SKILL.md +341 -0
  131. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/performance-profiling/references/interpreting-results.md +235 -0
  132. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/performance-profiling/references/tool-commands.md +395 -0
  133. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/refactoring-patterns/SKILL.md +344 -0
  134. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/refactoring-patterns/references/safe-transformations.md +247 -0
  135. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/refactoring-patterns/references/smell-catalog.md +332 -0
  136. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/security-checklist/SKILL.md +277 -0
  137. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/security-checklist/references/owasp-patterns.md +269 -0
  138. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/security-checklist/references/secrets-patterns.md +253 -0
  139. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/specification-writing/SKILL.md +320 -0
  140. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/specification-writing/references/criteria-patterns.md +245 -0
  141. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/specification-writing/references/ears-templates.md +239 -0
  142. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/__pycache__/block-dangerous.cpython-314.pyc +0 -0
  143. package/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/hooks/hooks.json +1 -1
  144. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/__pycache__/guard-protected.cpython-314.pyc +0 -0
  145. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py +40 -39
  146. package/.devcontainer/scripts/check-setup.sh +72 -0
  147. package/.devcontainer/scripts/setup-aliases.sh +51 -6
  148. package/.devcontainer/scripts/setup-auth.sh +74 -0
  149. package/.devcontainer/scripts/setup-config.sh +112 -20
  150. package/.devcontainer/scripts/setup-plugins.sh +38 -46
  151. package/.devcontainer/scripts/setup-projects.sh +175 -0
  152. package/.devcontainer/scripts/setup-symlink-claude.sh +36 -0
  153. package/.devcontainer/scripts/setup-update-claude.sh +19 -8
  154. package/.devcontainer/scripts/setup.sh +49 -14
  155. package/README.md +23 -190
  156. package/package.json +1 -1
  157. package/setup.js +245 -71
  158. package/.devcontainer/features/claude-code/README.md +0 -498
  159. package/.devcontainer/features/claude-code/config/settings.json +0 -36
  160. package/.devcontainer/features/claude-code/config/system-prompt.md +0 -118
  161. package/.devcontainer/features/claude-code/config/world-building-sp.md +0 -1432
  162. package/.devcontainer/features/claude-code/devcontainer-feature.json +0 -42
  163. package/.devcontainer/features/claude-code/install.sh +0 -466
  164. package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/.claude-plugin/plugin.json +0 -7
  165. package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/hooks/hooks.json +0 -17
  166. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/.claude-plugin/plugin.json +0 -6
  167. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/config/planning-instructions.md +0 -14
  168. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/functional-conjuring-map.md +0 -989
  169. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/hooks/hooks.json +0 -33
  170. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/__pycache__/post-enhance-task.cpython-314.pyc +0 -0
  171. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhance-planning.py +0 -71
  172. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhancers/enhance-plan.sh +0 -68
  173. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhancers/enhance-task.sh +0 -120
  174. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/post-enhance-plan.py +0 -133
  175. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/post-enhance-task.py +0 -253
  176. package/.devcontainer/scripts/setup-irie-claude.sh +0 -32
@@ -46,13 +46,23 @@ SKILLS = {
46
46
  "headless mode",
47
47
  "claude -p",
48
48
  "stream-json",
49
- "agent sdk",
50
49
  "claude code headless",
51
50
  "run claude in ci",
52
51
  "claude in pipeline",
53
52
  ],
54
53
  "terms": [],
55
54
  },
55
+ "claude-agent-sdk": {
56
+ "phrases": [
57
+ "agent sdk",
58
+ "claude agent sdk",
59
+ "build an agent",
60
+ "create an agent",
61
+ "canusetool",
62
+ "sdk permissions",
63
+ ],
64
+ "terms": ["claude-agent-sdk", "claude_agent_sdk"],
65
+ },
56
66
  "pydantic-ai": {
57
67
  "phrases": [
58
68
  "pydantic ai",
@@ -115,6 +125,7 @@ SKILLS = {
115
125
  "dockerfile",
116
126
  "docker compose",
117
127
  "docker-compose",
128
+ "compose file",
118
129
  "multi-stage build",
119
130
  "health check",
120
131
  "healthcheck",
@@ -122,7 +133,7 @@ SKILLS = {
122
133
  "docker volume",
123
134
  "docker image",
124
135
  ],
125
- "terms": ["dockerfile"],
136
+ "terms": ["dockerfile", "compose"],
126
137
  },
127
138
  "skill-building": {
128
139
  "phrases": [
@@ -152,6 +163,139 @@ SKILLS = {
152
163
  ],
153
164
  "terms": ["diagnose", "troubleshoot"],
154
165
  },
166
+ "refactoring-patterns": {
167
+ "phrases": [
168
+ "refactor this",
169
+ "clean up code",
170
+ "improve code structure",
171
+ "reduce complexity",
172
+ ],
173
+ "terms": [
174
+ "refactor",
175
+ "refactoring",
176
+ "code smell",
177
+ "extract function",
178
+ "dead code",
179
+ ],
180
+ },
181
+ "security-checklist": {
182
+ "phrases": [
183
+ "security review",
184
+ "check for vulnerabilities",
185
+ "audit security",
186
+ "find security issues",
187
+ ],
188
+ "terms": [
189
+ "security",
190
+ "vulnerability",
191
+ "owasp",
192
+ "injection",
193
+ "xss",
194
+ "secrets",
195
+ "cve",
196
+ ],
197
+ },
198
+ "git-forensics": {
199
+ "phrases": [
200
+ "git history",
201
+ "who changed this",
202
+ "when did this break",
203
+ "git blame",
204
+ ],
205
+ "terms": ["bisect", "blame", "archaeology", "git log", "pickaxe", "reflog"],
206
+ },
207
+ "specification-writing": {
208
+ "phrases": [
209
+ "write a spec",
210
+ "define requirements",
211
+ "acceptance criteria",
212
+ "user stories",
213
+ ],
214
+ "terms": [
215
+ "specification",
216
+ "requirements",
217
+ "ears",
218
+ "gherkin",
219
+ "given when then",
220
+ ],
221
+ },
222
+ "performance-profiling": {
223
+ "phrases": [
224
+ "profile performance",
225
+ "find bottleneck",
226
+ "benchmark this",
227
+ "why is this slow",
228
+ ],
229
+ "terms": [
230
+ "profiling",
231
+ "benchmark",
232
+ "flamegraph",
233
+ "bottleneck",
234
+ "latency",
235
+ "throughput",
236
+ ],
237
+ },
238
+ "api-design": {
239
+ "phrases": [
240
+ "api design",
241
+ "rest api design",
242
+ "design an api",
243
+ "design a rest",
244
+ "api convention",
245
+ "endpoint design",
246
+ "api versioning",
247
+ "pagination pattern",
248
+ "error response format",
249
+ ],
250
+ "terms": ["openapi", "swagger", "rfc7807"],
251
+ },
252
+ "ast-grep-patterns": {
253
+ "phrases": [
254
+ "ast-grep",
255
+ "ast grep",
256
+ "structural search",
257
+ "structural code search",
258
+ "syntax-aware search",
259
+ "tree-sitter",
260
+ ],
261
+ "terms": ["sg run", "ast-grep", "tree-sitter"],
262
+ },
263
+ "dependency-management": {
264
+ "phrases": [
265
+ "check dependencies",
266
+ "audit dependencies",
267
+ "outdated packages",
268
+ "dependency health",
269
+ "license check",
270
+ "unused dependencies",
271
+ "vulnerability scan",
272
+ ],
273
+ "terms": ["pip-audit", "npm audit", "cargo audit", "govulncheck"],
274
+ },
275
+ "documentation-patterns": {
276
+ "phrases": [
277
+ "write a readme",
278
+ "write documentation",
279
+ "add docstrings",
280
+ "add jsdoc",
281
+ "document the api",
282
+ "documentation template",
283
+ "update the docs",
284
+ ],
285
+ "terms": ["docstring", "jsdoc", "tsdoc", "godoc", "rustdoc"],
286
+ },
287
+ "migration-patterns": {
288
+ "phrases": [
289
+ "migrate from",
290
+ "upgrade to",
291
+ "version upgrade",
292
+ "framework migration",
293
+ "bump python",
294
+ "upgrade pydantic",
295
+ "migrate express",
296
+ ],
297
+ "terms": ["migrate", "migration", "upgrade"],
298
+ },
155
299
  }
156
300
 
157
301
  # Pre-compile term patterns for whole-word matching
@@ -19,11 +19,16 @@ EXTENSIONS = {".json", ".jsonc", ".yaml", ".yml", ".toml"}
19
19
 
20
20
 
21
21
  def strip_jsonc_comments(text: str) -> str:
22
- """Remove // and /* */ comments from JSONC text."""
23
- # Remove single-line comments (not inside strings)
24
- text = re.sub(r"(?<!:)//.*$", "", text, flags=re.MULTILINE)
25
- # Remove multi-line comments
22
+ """Remove // and /* */ comments from JSONC text.
23
+
24
+ Handles URLs (https://...) by only stripping // comments that are
25
+ preceded by whitespace or appear at line start, not those inside strings.
26
+ """
27
+ # Remove multi-line comments first
26
28
  text = re.sub(r"/\*.*?\*/", "", text, flags=re.DOTALL)
29
+ # Remove single-line comments: // preceded by start-of-line or whitespace
30
+ # This avoids stripping :// in URLs
31
+ text = re.sub(r"(^|[\s,\[\{])//.*$", r"\1", text, flags=re.MULTILINE)
27
32
  return text
28
33
 
29
34
 
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Ticket linker — UserPromptSubmit hook that auto-fetches GitHub issues/PRs.
4
+
5
+ When the user's prompt contains #123 or a full GitHub issue/PR URL,
6
+ fetches the ticket body via `gh` and injects it as additionalContext
7
+ so Claude has the full ticket context without the user copy-pasting.
8
+
9
+ Reads hook input from stdin (JSON). Returns JSON on stdout.
10
+ Always exits 0 (advisory, never blocking).
11
+ """
12
+
13
+ import json
14
+ import re
15
+ import subprocess
16
+ import sys
17
+
18
+ GH_CMD_TIMEOUT = 5
19
+ MAX_REFS = 3
20
+ BODY_CHAR_CAP = 1500
21
+ TOTAL_OUTPUT_CAP = 3000
22
+
23
+ # Short refs: #123 (but not inside URLs or hex colors)
24
+ SHORT_REF_RE = re.compile(r"(?<![/\w])#(\d+)\b")
25
+
26
+ # Full GitHub URLs: github.com/owner/repo/issues/123 or .../pull/123
27
+ URL_REF_RE = re.compile(r"github\.com/[^/\s]+/[^/\s]+/(?:issues|pull)/(\d+)")
28
+
29
+
30
+ def extract_refs(prompt: str) -> list[int]:
31
+ """Extract deduplicated issue/PR numbers from the prompt."""
32
+ seen: set[int] = set()
33
+ refs: list[int] = []
34
+
35
+ for match in SHORT_REF_RE.finditer(prompt):
36
+ num = int(match.group(1))
37
+ if num not in seen and num > 0:
38
+ seen.add(num)
39
+ refs.append(num)
40
+
41
+ for match in URL_REF_RE.finditer(prompt):
42
+ num = int(match.group(1))
43
+ if num not in seen and num > 0:
44
+ seen.add(num)
45
+ refs.append(num)
46
+
47
+ return refs[:MAX_REFS]
48
+
49
+
50
+ def fetch_ticket(number: int) -> str | None:
51
+ """Fetch a GitHub issue/PR via gh CLI. Returns formatted string or None."""
52
+ try:
53
+ result = subprocess.run(
54
+ [
55
+ "gh",
56
+ "issue",
57
+ "view",
58
+ str(number),
59
+ "--json",
60
+ "number,title,body,state,labels",
61
+ ],
62
+ capture_output=True,
63
+ text=True,
64
+ timeout=GH_CMD_TIMEOUT,
65
+ )
66
+ except (FileNotFoundError, OSError, subprocess.TimeoutExpired):
67
+ return None
68
+
69
+ if result.returncode != 0:
70
+ return None
71
+
72
+ try:
73
+ data = json.loads(result.stdout)
74
+ except (json.JSONDecodeError, ValueError):
75
+ return None
76
+
77
+ title = data.get("title", "(no title)")
78
+ state = data.get("state", "UNKNOWN")
79
+ body = data.get("body", "") or ""
80
+ labels = data.get("labels", [])
81
+
82
+ label_names = [lb.get("name", "") for lb in labels if lb.get("name")]
83
+ label_str = ", ".join(label_names) if label_names else "none"
84
+
85
+ # Truncate body
86
+ if len(body) > BODY_CHAR_CAP:
87
+ body = body[:BODY_CHAR_CAP] + "\n...(truncated)"
88
+
89
+ parts = [
90
+ f"[Ticket #{number}] {title}",
91
+ f"State: {state} | Labels: {label_str}",
92
+ ]
93
+ if body.strip():
94
+ parts.append(body.strip())
95
+
96
+ return "\n".join(parts)
97
+
98
+
99
+ def main():
100
+ raw = sys.stdin.read().strip()
101
+ if not raw:
102
+ sys.exit(0)
103
+
104
+ try:
105
+ data = json.loads(raw)
106
+ except (json.JSONDecodeError, ValueError):
107
+ sys.exit(0)
108
+
109
+ prompt = data.get("prompt", "")
110
+ if not prompt:
111
+ sys.exit(0)
112
+
113
+ refs = extract_refs(prompt)
114
+ if not refs:
115
+ sys.exit(0)
116
+
117
+ tickets: list[str] = []
118
+ for number in refs:
119
+ ticket = fetch_ticket(number)
120
+ if ticket:
121
+ tickets.append(ticket)
122
+
123
+ if not tickets:
124
+ sys.exit(0)
125
+
126
+ output = "\n\n---\n\n".join(tickets)
127
+
128
+ # Cap total output
129
+ if len(output) > TOTAL_OUTPUT_CAP:
130
+ output = output[:TOTAL_OUTPUT_CAP] + "\n...(truncated)"
131
+
132
+ json.dump({"additionalContext": output}, sys.stdout)
133
+ sys.exit(0)
134
+
135
+
136
+ if __name__ == "__main__":
137
+ main()
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ TODO/FIXME harvester — SessionStart hook that surfaces tech debt markers.
4
+
5
+ Greps the codebase for TODO/FIXME/HACK/XXX comments and injects a count
6
+ plus the top items as additionalContext so Claude can proactively mention
7
+ tech debt when relevant.
8
+
9
+ Reads hook input from stdin (JSON). Returns JSON on stdout.
10
+ Always exits 0 (advisory, never blocking).
11
+ """
12
+
13
+ import json
14
+ import os
15
+ import subprocess
16
+ import sys
17
+
18
+ GREP_TIMEOUT = 5
19
+ MAX_ITEMS = 10
20
+ TOTAL_OUTPUT_CAP = 800
21
+
22
+ SOURCE_INCLUDES = [
23
+ "--include=*.py",
24
+ "--include=*.ts",
25
+ "--include=*.tsx",
26
+ "--include=*.js",
27
+ "--include=*.jsx",
28
+ "--include=*.go",
29
+ "--include=*.rs",
30
+ "--include=*.sh",
31
+ "--include=*.svelte",
32
+ "--include=*.vue",
33
+ "--include=*.rb",
34
+ "--include=*.java",
35
+ "--include=*.kt",
36
+ ]
37
+
38
+ EXCLUDE_DIRS = [
39
+ "--exclude-dir=node_modules",
40
+ "--exclude-dir=.git",
41
+ "--exclude-dir=__pycache__",
42
+ "--exclude-dir=.venv",
43
+ "--exclude-dir=venv",
44
+ "--exclude-dir=dist",
45
+ "--exclude-dir=build",
46
+ "--exclude-dir=vendor",
47
+ "--exclude-dir=.next",
48
+ "--exclude-dir=.nuxt",
49
+ "--exclude-dir=target",
50
+ "--exclude-dir=.mypy_cache",
51
+ "--exclude-dir=.pytest_cache",
52
+ ]
53
+
54
+
55
+ def main():
56
+ try:
57
+ json.load(sys.stdin)
58
+ except (json.JSONDecodeError, ValueError):
59
+ pass
60
+
61
+ cwd = os.getcwd()
62
+
63
+ cmd = (
64
+ ["grep", "-rn", "-E", r"\b(TODO|FIXME|HACK|XXX)\b"]
65
+ + SOURCE_INCLUDES
66
+ + EXCLUDE_DIRS
67
+ + [cwd]
68
+ )
69
+
70
+ try:
71
+ result = subprocess.run(
72
+ cmd,
73
+ capture_output=True,
74
+ text=True,
75
+ timeout=GREP_TIMEOUT,
76
+ )
77
+ except (FileNotFoundError, OSError, subprocess.TimeoutExpired):
78
+ sys.exit(0)
79
+
80
+ # grep returns 1 for no matches — that's fine
81
+ output = result.stdout.strip()
82
+ if not output:
83
+ sys.exit(0)
84
+
85
+ lines = output.splitlines()
86
+ total_count = len(lines)
87
+
88
+ # Count unique files
89
+ files_seen: set[str] = set()
90
+ items: list[str] = []
91
+
92
+ for line in lines:
93
+ # Format: filepath:linenum:content
94
+ parts = line.split(":", 2)
95
+ if len(parts) >= 3:
96
+ filepath = parts[0]
97
+ files_seen.add(filepath)
98
+
99
+ if len(items) < MAX_ITEMS:
100
+ # Make path relative to cwd for readability
101
+ rel_path = os.path.relpath(filepath, cwd)
102
+ line_num = parts[1]
103
+ content = parts[2].strip()
104
+
105
+ # Trim long lines
106
+ if len(content) > 80:
107
+ content = content[:77] + "..."
108
+
109
+ items.append(f" {rel_path}:{line_num}: {content}")
110
+
111
+ file_count = len(files_seen)
112
+ header = (
113
+ f"[Tech Debt] {total_count} TODO/FIXME items found across {file_count} files"
114
+ )
115
+
116
+ if items:
117
+ body = header + "\nTop items:\n" + "\n".join(items)
118
+ else:
119
+ body = header
120
+
121
+ # Cap total output
122
+ if len(body) > TOTAL_OUTPUT_CAP:
123
+ body = body[:TOTAL_OUTPUT_CAP] + "\n...(truncated)"
124
+
125
+ json.dump({"additionalContext": body}, sys.stdout)
126
+ sys.exit(0)
127
+
128
+
129
+ if __name__ == "__main__":
130
+ main()
@@ -0,0 +1,221 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Verify no regression - PostToolUse hook for refactorer agent.
4
+
5
+ After each Edit operation, runs the project test suite to ensure
6
+ the refactoring didn't break anything. Includes debounce to avoid
7
+ running tests too frequently during rapid edits.
8
+
9
+ Reads hook input from stdin (JSON). Returns JSON on stdout.
10
+ Non-blocking on detection failures: always exits 0 if no framework found.
11
+
12
+ Exit 0: Tests pass, no framework found, debounced, or timeout
13
+ Exit 2: Tests fail (forces agent to fix regression before continuing)
14
+ """
15
+
16
+ import json
17
+ import os
18
+ import subprocess
19
+ import sys
20
+ import time
21
+
22
+ DEBOUNCE_SECONDS = 10
23
+
24
+
25
+ def detect_test_framework(cwd: str) -> tuple[str, list[str]]:
26
+ """Detect which test framework is available in the project.
27
+
28
+ Checks for: pytest, vitest, jest, mocha, go test, cargo test.
29
+ Falls back to npm test if a test script is defined.
30
+
31
+ Returns:
32
+ Tuple of (framework_name, command_list) or ("", []) if none found.
33
+ """
34
+ try:
35
+ entries = set(os.listdir(cwd))
36
+ except OSError:
37
+ return ("", [])
38
+
39
+ # --- Python: pytest ---
40
+ if "pytest.ini" in entries or "conftest.py" in entries:
41
+ return ("pytest", ["python3", "-m", "pytest", "--tb=short", "-q"])
42
+
43
+ for cfg_name in ("pyproject.toml", "setup.cfg", "tox.ini"):
44
+ cfg_path = os.path.join(cwd, cfg_name)
45
+ if os.path.isfile(cfg_path):
46
+ try:
47
+ with open(cfg_path, "r", encoding="utf-8") as f:
48
+ content = f.read()
49
+ if (
50
+ "[tool.pytest" in content
51
+ or "[pytest]" in content
52
+ or "[tool:pytest]" in content
53
+ ):
54
+ return ("pytest", ["python3", "-m", "pytest", "--tb=short", "-q"])
55
+ except OSError:
56
+ pass
57
+
58
+ if "tests" in entries and os.path.isdir(os.path.join(cwd, "tests")):
59
+ return ("pytest", ["python3", "-m", "pytest", "--tb=short", "-q"])
60
+
61
+ for entry in entries:
62
+ if entry.startswith("test_") and entry.endswith(".py"):
63
+ return ("pytest", ["python3", "-m", "pytest", "--tb=short", "-q"])
64
+
65
+ # --- JavaScript: vitest ---
66
+ for name in entries:
67
+ if name.startswith("vitest.config"):
68
+ return ("vitest", ["npx", "vitest", "run", "--reporter=verbose"])
69
+
70
+ for vite_cfg in ("vite.config.ts", "vite.config.js"):
71
+ cfg_path = os.path.join(cwd, vite_cfg)
72
+ if os.path.isfile(cfg_path):
73
+ try:
74
+ with open(cfg_path, "r", encoding="utf-8") as f:
75
+ if "test" in f.read():
76
+ return (
77
+ "vitest",
78
+ ["npx", "vitest", "run", "--reporter=verbose"],
79
+ )
80
+ except OSError:
81
+ pass
82
+
83
+ # --- JavaScript: jest ---
84
+ for name in entries:
85
+ if name.startswith("jest.config"):
86
+ return ("jest", ["npx", "jest", "--verbose"])
87
+
88
+ pkg_json = os.path.join(cwd, "package.json")
89
+ if os.path.isfile(pkg_json):
90
+ try:
91
+ with open(pkg_json, "r", encoding="utf-8") as f:
92
+ pkg = json.loads(f.read())
93
+
94
+ if "jest" in pkg:
95
+ return ("jest", ["npx", "jest", "--verbose"])
96
+
97
+ dev_deps = pkg.get("devDependencies", {})
98
+ deps = pkg.get("dependencies", {})
99
+
100
+ if "mocha" in dev_deps or "mocha" in deps:
101
+ return ("mocha", ["npx", "mocha", "--reporter", "spec"])
102
+
103
+ test_script = pkg.get("scripts", {}).get("test", "")
104
+ if test_script and "no test specified" not in test_script:
105
+ return ("npm-test", ["npm", "test"])
106
+ except (OSError, json.JSONDecodeError):
107
+ pass
108
+
109
+ # --- Go ---
110
+ if "go.mod" in entries:
111
+ return ("go", ["go", "test", "./...", "-count=1"])
112
+
113
+ # --- Rust ---
114
+ if "Cargo.toml" in entries:
115
+ return ("cargo", ["cargo", "test"])
116
+
117
+ return ("", [])
118
+
119
+
120
+ def should_debounce(session_id: str) -> bool:
121
+ """Check if we should skip this run due to recent execution.
122
+
123
+ Uses a timestamp file in /tmp to throttle test runs during rapid edits.
124
+ Returns True if the last run was less than DEBOUNCE_SECONDS ago.
125
+ """
126
+ stamp_path = f"/tmp/claude-regression-{session_id}"
127
+ now = time.time()
128
+
129
+ try:
130
+ with open(stamp_path, "r") as f:
131
+ last_run = float(f.read().strip())
132
+ if now - last_run < DEBOUNCE_SECONDS:
133
+ return True
134
+ except (OSError, ValueError):
135
+ pass
136
+
137
+ try:
138
+ with open(stamp_path, "w") as f:
139
+ f.write(str(now))
140
+ except OSError:
141
+ pass
142
+
143
+ return False
144
+
145
+
146
+ def main():
147
+ try:
148
+ input_data = json.load(sys.stdin)
149
+ except (json.JSONDecodeError, ValueError):
150
+ sys.exit(0)
151
+
152
+ tool_input = input_data.get("tool_input", {})
153
+ file_path = tool_input.get("file_path", "")
154
+
155
+ if not file_path:
156
+ sys.exit(0)
157
+
158
+ # Debounce: skip if tests ran recently in this session
159
+ session_id = input_data.get("session_id", "default")
160
+ if should_debounce(session_id):
161
+ sys.exit(0)
162
+
163
+ cwd = os.getcwd()
164
+ framework, cmd = detect_test_framework(cwd)
165
+
166
+ if not framework:
167
+ sys.exit(0)
168
+
169
+ try:
170
+ result = subprocess.run(
171
+ cmd,
172
+ cwd=cwd,
173
+ capture_output=True,
174
+ text=True,
175
+ timeout=60,
176
+ )
177
+ except subprocess.TimeoutExpired:
178
+ # Timeout is non-critical for PostToolUse — don't block the agent
179
+ json.dump(
180
+ {
181
+ "additionalContext": f"[Tests] {framework} timed out after 60s — skipping regression check."
182
+ },
183
+ sys.stdout,
184
+ )
185
+ sys.exit(0)
186
+ except FileNotFoundError:
187
+ sys.exit(0)
188
+ except OSError:
189
+ sys.exit(0)
190
+
191
+ output = (result.stdout + "\n" + result.stderr).strip()
192
+ if not output:
193
+ output = "(no test output)"
194
+
195
+ # Truncate to last 50 lines
196
+ lines = output.splitlines()
197
+ if len(lines) > 50:
198
+ output = "...(truncated)\n" + "\n".join(lines[-50:])
199
+
200
+ if result.returncode != 0:
201
+ edited = os.path.basename(file_path)
202
+ json.dump(
203
+ {
204
+ "error": (
205
+ f"Regression detected after editing {edited} "
206
+ f"({framework}). Fix the failing tests before continuing:\n{output}"
207
+ )
208
+ },
209
+ sys.stdout,
210
+ )
211
+ sys.exit(2)
212
+
213
+ json.dump(
214
+ {"additionalContext": f"[Tests] No regression ({framework}): all tests passed"},
215
+ sys.stdout,
216
+ )
217
+ sys.exit(0)
218
+
219
+
220
+ if __name__ == "__main__":
221
+ main()