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
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Advisory test runner — Stop hook that injects test results as context.
|
|
4
|
+
|
|
5
|
+
Detects the project's test framework and runs the test suite. Results are
|
|
6
|
+
returned as additionalContext so Claude sees pass/fail info without being
|
|
7
|
+
blocked. If tests fail, Claude's next response will naturally address them.
|
|
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
|
+
|
|
19
|
+
def detect_test_framework(cwd: str) -> tuple[str, list[str]]:
|
|
20
|
+
"""Detect which test framework is available in the project.
|
|
21
|
+
|
|
22
|
+
Checks for: pytest, vitest, jest, mocha, go test, cargo test.
|
|
23
|
+
Falls back to npm test if a test script is defined.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Tuple of (framework_name, command_list) or ("", []) if none found.
|
|
27
|
+
"""
|
|
28
|
+
try:
|
|
29
|
+
entries = set(os.listdir(cwd))
|
|
30
|
+
except OSError:
|
|
31
|
+
return ("", [])
|
|
32
|
+
|
|
33
|
+
# --- Python: pytest ---
|
|
34
|
+
if "pytest.ini" in entries or "conftest.py" in entries:
|
|
35
|
+
return ("pytest", ["python3", "-m", "pytest", "--tb=short", "-q"])
|
|
36
|
+
|
|
37
|
+
for cfg_name in ("pyproject.toml", "setup.cfg", "tox.ini"):
|
|
38
|
+
cfg_path = os.path.join(cwd, cfg_name)
|
|
39
|
+
if os.path.isfile(cfg_path):
|
|
40
|
+
try:
|
|
41
|
+
with open(cfg_path, "r", encoding="utf-8") as f:
|
|
42
|
+
content = f.read()
|
|
43
|
+
if (
|
|
44
|
+
"[tool.pytest" in content
|
|
45
|
+
or "[pytest]" in content
|
|
46
|
+
or "[tool:pytest]" in content
|
|
47
|
+
):
|
|
48
|
+
return ("pytest", ["python3", "-m", "pytest", "--tb=short", "-q"])
|
|
49
|
+
except OSError:
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
if "tests" in entries and os.path.isdir(os.path.join(cwd, "tests")):
|
|
53
|
+
return ("pytest", ["python3", "-m", "pytest", "--tb=short", "-q"])
|
|
54
|
+
|
|
55
|
+
for entry in entries:
|
|
56
|
+
if entry.startswith("test_") and entry.endswith(".py"):
|
|
57
|
+
return ("pytest", ["python3", "-m", "pytest", "--tb=short", "-q"])
|
|
58
|
+
|
|
59
|
+
# --- JavaScript: vitest ---
|
|
60
|
+
for name in entries:
|
|
61
|
+
if name.startswith("vitest.config"):
|
|
62
|
+
return ("vitest", ["npx", "vitest", "run", "--reporter=verbose"])
|
|
63
|
+
|
|
64
|
+
for vite_cfg in ("vite.config.ts", "vite.config.js"):
|
|
65
|
+
cfg_path = os.path.join(cwd, vite_cfg)
|
|
66
|
+
if os.path.isfile(cfg_path):
|
|
67
|
+
try:
|
|
68
|
+
with open(cfg_path, "r", encoding="utf-8") as f:
|
|
69
|
+
if "test" in f.read():
|
|
70
|
+
return (
|
|
71
|
+
"vitest",
|
|
72
|
+
["npx", "vitest", "run", "--reporter=verbose"],
|
|
73
|
+
)
|
|
74
|
+
except OSError:
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
# --- JavaScript: jest ---
|
|
78
|
+
for name in entries:
|
|
79
|
+
if name.startswith("jest.config"):
|
|
80
|
+
return ("jest", ["npx", "jest", "--verbose"])
|
|
81
|
+
|
|
82
|
+
pkg_json = os.path.join(cwd, "package.json")
|
|
83
|
+
if os.path.isfile(pkg_json):
|
|
84
|
+
try:
|
|
85
|
+
with open(pkg_json, "r", encoding="utf-8") as f:
|
|
86
|
+
pkg = json.loads(f.read())
|
|
87
|
+
|
|
88
|
+
if "jest" in pkg:
|
|
89
|
+
return ("jest", ["npx", "jest", "--verbose"])
|
|
90
|
+
|
|
91
|
+
dev_deps = pkg.get("devDependencies", {})
|
|
92
|
+
deps = pkg.get("dependencies", {})
|
|
93
|
+
|
|
94
|
+
if "mocha" in dev_deps or "mocha" in deps:
|
|
95
|
+
return ("mocha", ["npx", "mocha", "--reporter", "spec"])
|
|
96
|
+
|
|
97
|
+
test_script = pkg.get("scripts", {}).get("test", "")
|
|
98
|
+
if test_script and "no test specified" not in test_script:
|
|
99
|
+
return ("npm-test", ["npm", "test"])
|
|
100
|
+
except (OSError, json.JSONDecodeError):
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
# --- Go ---
|
|
104
|
+
if "go.mod" in entries:
|
|
105
|
+
return ("go", ["go", "test", "./...", "-count=1"])
|
|
106
|
+
|
|
107
|
+
# --- Rust ---
|
|
108
|
+
if "Cargo.toml" in entries:
|
|
109
|
+
return ("cargo", ["cargo", "test"])
|
|
110
|
+
|
|
111
|
+
return ("", [])
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def main():
|
|
115
|
+
try:
|
|
116
|
+
input_data = json.load(sys.stdin)
|
|
117
|
+
except (json.JSONDecodeError, ValueError):
|
|
118
|
+
sys.exit(0)
|
|
119
|
+
|
|
120
|
+
# Skip if another Stop hook is already blocking
|
|
121
|
+
if input_data.get("stop_hook_active"):
|
|
122
|
+
sys.exit(0)
|
|
123
|
+
|
|
124
|
+
cwd = os.getcwd()
|
|
125
|
+
framework, cmd = detect_test_framework(cwd)
|
|
126
|
+
|
|
127
|
+
if not framework:
|
|
128
|
+
sys.exit(0)
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
result = subprocess.run(
|
|
132
|
+
cmd,
|
|
133
|
+
cwd=cwd,
|
|
134
|
+
capture_output=True,
|
|
135
|
+
text=True,
|
|
136
|
+
timeout=60,
|
|
137
|
+
)
|
|
138
|
+
except subprocess.TimeoutExpired:
|
|
139
|
+
json.dump(
|
|
140
|
+
{"additionalContext": f"[Tests] {framework} timed out after 60s"},
|
|
141
|
+
sys.stdout,
|
|
142
|
+
)
|
|
143
|
+
sys.exit(0)
|
|
144
|
+
except (FileNotFoundError, OSError):
|
|
145
|
+
# Test runner not installed or not accessible
|
|
146
|
+
sys.exit(0)
|
|
147
|
+
|
|
148
|
+
output = (result.stdout + "\n" + result.stderr).strip()
|
|
149
|
+
|
|
150
|
+
if result.returncode == 0:
|
|
151
|
+
# Extract test count from output if possible
|
|
152
|
+
json.dump(
|
|
153
|
+
{"additionalContext": f"[Tests] All tests passed ({framework})"},
|
|
154
|
+
sys.stdout,
|
|
155
|
+
)
|
|
156
|
+
sys.exit(0)
|
|
157
|
+
|
|
158
|
+
# Tests failed — truncate to last 30 lines
|
|
159
|
+
if not output:
|
|
160
|
+
output = "(no test output)"
|
|
161
|
+
|
|
162
|
+
lines = output.splitlines()
|
|
163
|
+
if len(lines) > 30:
|
|
164
|
+
output = "...(truncated)\n" + "\n".join(lines[-30:])
|
|
165
|
+
|
|
166
|
+
json.dump(
|
|
167
|
+
{"additionalContext": (f"[Tests] Some tests FAILED ({framework}):\n{output}")},
|
|
168
|
+
sys.stdout,
|
|
169
|
+
)
|
|
170
|
+
sys.exit(0)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
if __name__ == "__main__":
|
|
174
|
+
main()
|
|
@@ -30,12 +30,14 @@ def main():
|
|
|
30
30
|
if not os.path.isfile(file_path):
|
|
31
31
|
sys.exit(0)
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
# Write to both formatter and linter temp files (independent pipelines)
|
|
34
|
+
for prefix in ("claude-edited-files", "claude-lint-files"):
|
|
35
|
+
tmp_path = f"/tmp/{prefix}-{session_id}"
|
|
36
|
+
try:
|
|
37
|
+
with open(tmp_path, "a") as f:
|
|
38
|
+
f.write(file_path + "\n")
|
|
39
|
+
except OSError:
|
|
40
|
+
pass # non-critical, don't block Claude
|
|
39
41
|
|
|
40
42
|
sys.exit(0)
|
|
41
43
|
|
package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/commit-reminder.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Commit reminder — Stop hook that advises about uncommitted changes.
|
|
4
|
+
|
|
5
|
+
On Stop, checks for uncommitted changes (staged + unstaged) and injects
|
|
6
|
+
an advisory reminder as additionalContext. Claude sees it and can
|
|
7
|
+
naturally ask the user if they want to commit.
|
|
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 subprocess
|
|
15
|
+
import sys
|
|
16
|
+
|
|
17
|
+
GIT_CMD_TIMEOUT = 5
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _run_git(args: list[str]) -> str | None:
|
|
21
|
+
"""Run a git command and return stdout, or None on any failure."""
|
|
22
|
+
try:
|
|
23
|
+
result = subprocess.run(
|
|
24
|
+
["git"] + args,
|
|
25
|
+
capture_output=True,
|
|
26
|
+
text=True,
|
|
27
|
+
timeout=GIT_CMD_TIMEOUT,
|
|
28
|
+
)
|
|
29
|
+
if result.returncode == 0:
|
|
30
|
+
return result.stdout.strip()
|
|
31
|
+
except (FileNotFoundError, OSError, subprocess.TimeoutExpired):
|
|
32
|
+
pass
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def main():
|
|
37
|
+
try:
|
|
38
|
+
input_data = json.load(sys.stdin)
|
|
39
|
+
except (json.JSONDecodeError, ValueError):
|
|
40
|
+
sys.exit(0)
|
|
41
|
+
|
|
42
|
+
# Skip if another Stop hook is already blocking
|
|
43
|
+
if input_data.get("stop_hook_active"):
|
|
44
|
+
sys.exit(0)
|
|
45
|
+
|
|
46
|
+
# Check if there are any changes at all
|
|
47
|
+
porcelain = _run_git(["status", "--porcelain"])
|
|
48
|
+
if porcelain is None:
|
|
49
|
+
# Not a git repo or git not available
|
|
50
|
+
sys.exit(0)
|
|
51
|
+
if not porcelain.strip():
|
|
52
|
+
# Working tree clean
|
|
53
|
+
sys.exit(0)
|
|
54
|
+
|
|
55
|
+
lines = porcelain.strip().splitlines()
|
|
56
|
+
total = len(lines)
|
|
57
|
+
|
|
58
|
+
# Count staged vs unstaged
|
|
59
|
+
staged = 0
|
|
60
|
+
unstaged = 0
|
|
61
|
+
for line in lines:
|
|
62
|
+
index_status = line[0:1] if len(line) > 0 else " "
|
|
63
|
+
worktree_status = line[1:2] if len(line) > 1 else " "
|
|
64
|
+
|
|
65
|
+
if index_status not in (" ", "?"):
|
|
66
|
+
staged += 1
|
|
67
|
+
if worktree_status not in (" ", "?"):
|
|
68
|
+
unstaged += 1
|
|
69
|
+
if line[0:2] == "??":
|
|
70
|
+
unstaged += 1
|
|
71
|
+
|
|
72
|
+
parts = []
|
|
73
|
+
if staged:
|
|
74
|
+
parts.append(f"{staged} staged")
|
|
75
|
+
if unstaged:
|
|
76
|
+
parts.append(f"{unstaged} unstaged")
|
|
77
|
+
|
|
78
|
+
summary = ", ".join(parts) if parts else f"{total} changed"
|
|
79
|
+
|
|
80
|
+
message = (
|
|
81
|
+
f"[Uncommitted Changes] {total} files with changes ({summary}).\n"
|
|
82
|
+
"Consider asking the user if they'd like to commit before finishing."
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
json.dump({"additionalContext": message}, sys.stdout)
|
|
86
|
+
sys.exit(0)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
if __name__ == "__main__":
|
|
90
|
+
main()
|
package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/git-state-injector.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Git state injector — SessionStart hook that injects repo state as context.
|
|
4
|
+
|
|
5
|
+
Runs git commands to gather branch, status, recent commits, and uncommitted
|
|
6
|
+
changes. Injects the results as additionalContext so Claude starts every
|
|
7
|
+
session knowing the current git state.
|
|
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
|
+
GIT_CMD_TIMEOUT = 5
|
|
19
|
+
STATUS_LINE_CAP = 20
|
|
20
|
+
DIFF_STAT_LINE_CAP = 15
|
|
21
|
+
TOTAL_OUTPUT_CAP = 2000
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _run_git(args: list[str], cwd: str) -> str | None:
|
|
25
|
+
"""Run a git command and return stdout, or None on any failure."""
|
|
26
|
+
try:
|
|
27
|
+
result = subprocess.run(
|
|
28
|
+
["git"] + args,
|
|
29
|
+
cwd=cwd,
|
|
30
|
+
capture_output=True,
|
|
31
|
+
text=True,
|
|
32
|
+
timeout=GIT_CMD_TIMEOUT,
|
|
33
|
+
)
|
|
34
|
+
if result.returncode == 0:
|
|
35
|
+
return result.stdout.strip()
|
|
36
|
+
except (FileNotFoundError, OSError, subprocess.TimeoutExpired):
|
|
37
|
+
pass
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _cap_lines(text: str, limit: int) -> str:
|
|
42
|
+
"""Truncate text to a maximum number of lines."""
|
|
43
|
+
lines = text.splitlines()
|
|
44
|
+
if len(lines) <= limit:
|
|
45
|
+
return text
|
|
46
|
+
return "\n".join(lines[:limit]) + f"\n...({len(lines) - limit} more lines)"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def main():
|
|
50
|
+
try:
|
|
51
|
+
json.load(sys.stdin)
|
|
52
|
+
except (json.JSONDecodeError, ValueError):
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
cwd = os.getcwd()
|
|
56
|
+
|
|
57
|
+
# Check if we're in a git repo at all
|
|
58
|
+
branch = _run_git(["branch", "--show-current"], cwd)
|
|
59
|
+
if branch is None:
|
|
60
|
+
# Not a git repo or git not available
|
|
61
|
+
sys.exit(0)
|
|
62
|
+
|
|
63
|
+
sections = []
|
|
64
|
+
sections.append(f"Branch: {branch or '(detached HEAD)'}")
|
|
65
|
+
|
|
66
|
+
# Git status
|
|
67
|
+
status = _run_git(["status", "--short"], cwd)
|
|
68
|
+
if status:
|
|
69
|
+
status_lines = status.splitlines()
|
|
70
|
+
modified = sum(1 for l in status_lines if l.strip() and l[0:1] == "M")
|
|
71
|
+
added = sum(1 for l in status_lines if l[0:1] == "A")
|
|
72
|
+
deleted = sum(1 for l in status_lines if l[0:1] == "D")
|
|
73
|
+
untracked = sum(1 for l in status_lines if l[0:2] == "??")
|
|
74
|
+
|
|
75
|
+
counts = []
|
|
76
|
+
if modified:
|
|
77
|
+
counts.append(f"{modified} modified")
|
|
78
|
+
if added:
|
|
79
|
+
counts.append(f"{added} added")
|
|
80
|
+
if deleted:
|
|
81
|
+
counts.append(f"{deleted} deleted")
|
|
82
|
+
if untracked:
|
|
83
|
+
counts.append(f"{untracked} untracked")
|
|
84
|
+
|
|
85
|
+
summary = ", ".join(counts) if counts else f"{len(status_lines)} changed"
|
|
86
|
+
sections.append(f"Status: {summary}")
|
|
87
|
+
sections.append(_cap_lines(status, STATUS_LINE_CAP))
|
|
88
|
+
else:
|
|
89
|
+
sections.append("Status: clean")
|
|
90
|
+
|
|
91
|
+
# Recent commits
|
|
92
|
+
log = _run_git(["log", "--oneline", "-5"], cwd)
|
|
93
|
+
if log:
|
|
94
|
+
sections.append(f"Recent commits:\n{log}")
|
|
95
|
+
|
|
96
|
+
# Uncommitted diff stats
|
|
97
|
+
diff_stat = _run_git(["diff", "--stat"], cwd)
|
|
98
|
+
if diff_stat:
|
|
99
|
+
sections.append(
|
|
100
|
+
f"Uncommitted changes:\n{_cap_lines(diff_stat, DIFF_STAT_LINE_CAP)}"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
output = "[Git State]\n" + "\n".join(sections)
|
|
104
|
+
|
|
105
|
+
# Cap total output to avoid context bloat
|
|
106
|
+
if len(output) > TOTAL_OUTPUT_CAP:
|
|
107
|
+
output = output[:TOTAL_OUTPUT_CAP] + "\n...(truncated)"
|
|
108
|
+
|
|
109
|
+
json.dump({"additionalContext": output}, sys.stdout)
|
|
110
|
+
sys.exit(0)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
if __name__ == "__main__":
|
|
114
|
+
main()
|