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.
- package/.devcontainer/.env +4 -5
- package/.devcontainer/.env.example +29 -0
- package/.devcontainer/.gitignore +8 -0
- package/.devcontainer/.secrets.example +12 -0
- package/.devcontainer/CHANGELOG.md +186 -0
- package/.devcontainer/CLAUDE.md +108 -21
- package/.devcontainer/README.md +173 -57
- package/.devcontainer/config/defaults/keybindings.json +5 -0
- package/.devcontainer/config/{main-system-prompt.md → defaults/main-system-prompt.md} +135 -2
- package/.devcontainer/config/{settings.json → defaults/settings.json} +25 -6
- package/.devcontainer/config/file-manifest.json +20 -0
- package/.devcontainer/devcontainer.json +38 -2
- package/.devcontainer/docs/configuration-reference.md +90 -0
- package/.devcontainer/docs/keybindings.md +100 -0
- package/.devcontainer/docs/optional-features.md +129 -0
- package/.devcontainer/docs/plugins.md +154 -0
- package/.devcontainer/docs/troubleshooting.md +128 -0
- package/.devcontainer/features/README.md +21 -7
- package/.devcontainer/features/agent-browser/install.sh +6 -0
- package/.devcontainer/features/ast-grep/install.sh +6 -0
- package/.devcontainer/features/biome/README.md +27 -0
- package/.devcontainer/features/biome/install.sh +6 -0
- package/.devcontainer/features/ccburn/README.md +60 -0
- package/.devcontainer/features/ccburn/devcontainer-feature.json +38 -0
- package/.devcontainer/features/ccburn/install.sh +180 -0
- package/.devcontainer/features/ccstatusline/README.md +22 -21
- package/.devcontainer/features/ccstatusline/devcontainer-feature.json +6 -1
- package/.devcontainer/features/ccstatusline/install.sh +55 -16
- package/.devcontainer/features/ccusage/install.sh +6 -0
- package/.devcontainer/features/claude-monitor/install.sh +6 -0
- package/.devcontainer/features/dprint/README.md +30 -0
- package/.devcontainer/features/dprint/devcontainer-feature.json +18 -0
- package/.devcontainer/features/dprint/install.sh +131 -0
- package/.devcontainer/features/hadolint/README.md +35 -0
- package/.devcontainer/features/hadolint/devcontainer-feature.json +13 -0
- package/.devcontainer/features/hadolint/install.sh +86 -0
- package/.devcontainer/features/lsp-servers/devcontainer-feature.json +5 -0
- package/.devcontainer/features/lsp-servers/install.sh +7 -0
- package/.devcontainer/features/mcp-qdrant/devcontainer-feature.json +6 -1
- package/.devcontainer/features/mcp-qdrant/install.sh +13 -6
- package/.devcontainer/features/mcp-reasoner/devcontainer-feature.json +6 -1
- package/.devcontainer/features/mcp-reasoner/install.sh +8 -1
- package/.devcontainer/features/notify-hook/devcontainer-feature.json +5 -0
- package/.devcontainer/features/notify-hook/install.sh +7 -0
- package/.devcontainer/features/ruff/README.md +26 -0
- package/.devcontainer/features/ruff/devcontainer-feature.json +21 -0
- package/.devcontainer/features/ruff/install.sh +74 -0
- package/.devcontainer/features/shellcheck/README.md +38 -0
- package/.devcontainer/features/shellcheck/devcontainer-feature.json +13 -0
- package/.devcontainer/features/shellcheck/install.sh +24 -0
- package/.devcontainer/features/shfmt/README.md +37 -0
- package/.devcontainer/features/shfmt/devcontainer-feature.json +13 -0
- package/.devcontainer/features/shfmt/install.sh +85 -0
- package/.devcontainer/features/splitrail/devcontainer-feature.json +5 -0
- package/.devcontainer/features/splitrail/install.sh +7 -0
- package/.devcontainer/features/tmux/install.sh +8 -0
- package/.devcontainer/features/tree-sitter/install.sh +6 -0
- package/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json +3 -10
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/.claude-plugin/plugin.json +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/scripts/__pycache__/format-on-stop.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/scripts/format-on-stop.py +133 -13
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/.claude-plugin/plugin.json +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/hooks/hooks.json +4 -5
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/scripts/__pycache__/lint-file.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/scripts/lint-file.py +477 -78
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/.claude-plugin/plugin.json +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/AGENT-REDIRECTION.md +226 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/REVIEW-RUBRIC.md +440 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/architect.md +207 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/bash-exec.md +173 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/claude-guide.md +146 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/debug-logs.md +2 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/dependency-analyst.md +250 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/doc-writer.md +246 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/explorer.md +237 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/generalist.md +134 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/git-archaeologist.md +242 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/migrator.md +201 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/perf-profiler.md +265 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/refactorer.md +213 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/researcher.md +195 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/security-auditor.md +289 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/spec-writer.md +297 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/statusline-config.md +188 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/test-writer.md +248 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/hooks/hooks.json +51 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/advisory-test-runner.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/collect-edited-files.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/commit-reminder.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/git-state-injector.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/guard-readonly-bash.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/redirect-builtin-agents.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/skill-suggester.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/syntax-validator.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/ticket-linker.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/todo-harvester.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/verify-no-regression.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/verify-tests-pass.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/advisory-test-runner.py +174 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/collect-edited-files.py +8 -6
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/commit-reminder.py +90 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/git-state-injector.py +114 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/guard-readonly-bash.py +611 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/redirect-builtin-agents.py +83 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/skill-suggester.py +146 -2
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/syntax-validator.py +9 -4
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/ticket-linker.py +137 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/todo-harvester.py +130 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/verify-no-regression.py +221 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/verify-tests-pass.py +176 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/api-design/SKILL.md +224 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/api-design/references/error-handling.md +166 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/api-design/references/rest-conventions.md +215 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/ast-grep-patterns/SKILL.md +211 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/ast-grep-patterns/references/language-patterns.md +327 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/claude-agent-sdk/SKILL.md +599 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/claude-agent-sdk/references/sdk-typescript-reference.md +954 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/dependency-management/SKILL.md +134 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/dependency-management/references/ecosystem-commands.md +264 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/dependency-management/references/license-compliance.md +80 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/documentation-patterns/SKILL.md +153 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/documentation-patterns/references/api-doc-templates.md +221 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/documentation-patterns/references/docstring-formats.md +296 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/git-forensics/SKILL.md +276 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/git-forensics/references/advanced-commands.md +332 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/git-forensics/references/investigation-playbooks.md +319 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/migration-patterns/SKILL.md +150 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/migration-patterns/references/javascript-migrations.md +179 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/migration-patterns/references/python-migrations.md +141 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/performance-profiling/SKILL.md +341 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/performance-profiling/references/interpreting-results.md +235 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/performance-profiling/references/tool-commands.md +395 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/refactoring-patterns/SKILL.md +344 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/refactoring-patterns/references/safe-transformations.md +247 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/refactoring-patterns/references/smell-catalog.md +332 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/security-checklist/SKILL.md +277 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/security-checklist/references/owasp-patterns.md +269 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/security-checklist/references/secrets-patterns.md +253 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/specification-writing/SKILL.md +320 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/specification-writing/references/criteria-patterns.md +245 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/specification-writing/references/ears-templates.md +239 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/__pycache__/block-dangerous.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/hooks/hooks.json +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/__pycache__/guard-protected.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py +40 -39
- package/.devcontainer/scripts/check-setup.sh +72 -0
- package/.devcontainer/scripts/setup-aliases.sh +51 -6
- package/.devcontainer/scripts/setup-auth.sh +74 -0
- package/.devcontainer/scripts/setup-config.sh +112 -20
- package/.devcontainer/scripts/setup-plugins.sh +38 -46
- package/.devcontainer/scripts/setup-projects.sh +175 -0
- package/.devcontainer/scripts/setup-symlink-claude.sh +36 -0
- package/.devcontainer/scripts/setup-update-claude.sh +19 -8
- package/.devcontainer/scripts/setup.sh +49 -14
- package/README.md +23 -190
- package/package.json +1 -1
- package/setup.js +245 -71
- package/.devcontainer/features/claude-code/README.md +0 -498
- package/.devcontainer/features/claude-code/config/settings.json +0 -36
- package/.devcontainer/features/claude-code/config/system-prompt.md +0 -118
- package/.devcontainer/features/claude-code/config/world-building-sp.md +0 -1432
- package/.devcontainer/features/claude-code/devcontainer-feature.json +0 -42
- package/.devcontainer/features/claude-code/install.sh +0 -466
- package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/.claude-plugin/plugin.json +0 -7
- package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/hooks/hooks.json +0 -17
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/.claude-plugin/plugin.json +0 -6
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/config/planning-instructions.md +0 -14
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/functional-conjuring-map.md +0 -989
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/hooks/hooks.json +0 -33
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/__pycache__/post-enhance-task.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhance-planning.py +0 -71
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhancers/enhance-plan.sh +0 -68
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhancers/enhance-task.sh +0 -120
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/post-enhance-plan.py +0 -133
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/post-enhance-task.py +0 -253
- package/.devcontainer/scripts/setup-irie-claude.sh +0 -32
package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/skill-suggester.py
CHANGED
|
@@ -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
|
package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/syntax-validator.py
CHANGED
|
@@ -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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/ticket-linker.py
ADDED
|
@@ -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()
|
package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/todo-harvester.py
ADDED
|
@@ -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()
|