sequant 1.17.0 → 1.19.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +14 -2
- package/dist/bin/cli.js +7 -0
- package/dist/marketplace/external_plugins/sequant/.claude-plugin/plugin.json +21 -0
- package/dist/marketplace/external_plugins/sequant/README.md +38 -0
- package/dist/marketplace/external_plugins/sequant/hooks/post-tool.sh +292 -0
- package/dist/marketplace/external_plugins/sequant/hooks/pre-tool.sh +463 -0
- package/dist/marketplace/external_plugins/sequant/skills/_shared/references/prompt-templates.md +350 -0
- package/dist/marketplace/external_plugins/sequant/skills/_shared/references/subagent-types.md +131 -0
- package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +474 -0
- package/dist/marketplace/external_plugins/sequant/skills/clean/SKILL.md +211 -0
- package/dist/marketplace/external_plugins/sequant/skills/docs/SKILL.md +337 -0
- package/dist/marketplace/external_plugins/sequant/skills/exec/SKILL.md +807 -0
- package/dist/marketplace/external_plugins/sequant/skills/fullsolve/SKILL.md +678 -0
- package/dist/marketplace/external_plugins/sequant/skills/improve/SKILL.md +668 -0
- package/dist/marketplace/external_plugins/sequant/skills/loop/SKILL.md +374 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/SKILL.md +570 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/code-quality-exemplars.md +107 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/code-review-checklist.md +65 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/quality-gates.md +179 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/semgrep-rules.md +207 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/testing-requirements.md +109 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/scripts/quality-checks.sh +622 -0
- package/dist/marketplace/external_plugins/sequant/skills/reflect/SKILL.md +175 -0
- package/dist/marketplace/external_plugins/sequant/skills/reflect/references/documentation-tiers.md +70 -0
- package/dist/marketplace/external_plugins/sequant/skills/reflect/references/phase-reflection.md +95 -0
- package/dist/marketplace/external_plugins/sequant/skills/security-review/SKILL.md +358 -0
- package/dist/marketplace/external_plugins/sequant/skills/security-review/references/security-checklists.md +432 -0
- package/dist/marketplace/external_plugins/sequant/skills/solve/SKILL.md +697 -0
- package/dist/marketplace/external_plugins/sequant/skills/spec/SKILL.md +754 -0
- package/dist/marketplace/external_plugins/sequant/skills/spec/references/parallel-groups.md +72 -0
- package/dist/marketplace/external_plugins/sequant/skills/spec/references/recommended-workflow.md +92 -0
- package/dist/marketplace/external_plugins/sequant/skills/spec/references/verification-criteria.md +104 -0
- package/dist/marketplace/external_plugins/sequant/skills/test/SKILL.md +600 -0
- package/dist/marketplace/external_plugins/sequant/skills/testgen/SKILL.md +576 -0
- package/dist/marketplace/external_plugins/sequant/skills/verify/SKILL.md +281 -0
- package/dist/src/commands/conventions.d.ts +9 -0
- package/dist/src/commands/conventions.js +61 -0
- package/dist/src/commands/init.js +12 -0
- package/dist/src/commands/run.d.ts +13 -280
- package/dist/src/commands/run.js +23 -1956
- package/dist/src/commands/sync.js +3 -0
- package/dist/src/commands/update.js +3 -0
- package/dist/src/lib/conventions-detector.d.ts +62 -0
- package/dist/src/lib/conventions-detector.js +510 -0
- package/dist/src/lib/plugin-version-sync.d.ts +2 -1
- package/dist/src/lib/plugin-version-sync.js +28 -7
- package/dist/src/lib/settings.d.ts +8 -0
- package/dist/src/lib/settings.js +1 -0
- package/dist/src/lib/solve-comment-parser.d.ts +26 -0
- package/dist/src/lib/solve-comment-parser.js +63 -7
- package/dist/src/lib/stacks.d.ts +4 -2
- package/dist/src/lib/stacks.js +43 -3
- package/dist/src/lib/workflow/batch-executor.d.ts +117 -0
- package/dist/src/lib/workflow/batch-executor.js +574 -0
- package/dist/src/lib/workflow/phase-executor.d.ts +40 -0
- package/dist/src/lib/workflow/phase-executor.js +381 -0
- package/dist/src/lib/workflow/phase-mapper.d.ts +65 -0
- package/dist/src/lib/workflow/phase-mapper.js +147 -0
- package/dist/src/lib/workflow/pr-operations.d.ts +86 -0
- package/dist/src/lib/workflow/pr-operations.js +326 -0
- package/dist/src/lib/workflow/pr-status.d.ts +9 -7
- package/dist/src/lib/workflow/pr-status.js +13 -11
- package/dist/src/lib/workflow/run-summary.d.ts +36 -0
- package/dist/src/lib/workflow/run-summary.js +142 -0
- package/dist/src/lib/workflow/state-schema.d.ts +5 -5
- package/dist/src/lib/workflow/worktree-manager.d.ts +205 -0
- package/dist/src/lib/workflow/worktree-manager.js +918 -0
- package/package.json +3 -1
- package/templates/skills/exec/SKILL.md +1086 -29
- package/templates/skills/fullsolve/SKILL.md +11 -1
- package/templates/skills/qa/SKILL.md +1758 -142
- package/templates/skills/solve/SKILL.md +86 -0
- package/templates/skills/spec/SKILL.md +53 -0
- package/templates/skills/test/SKILL.md +256 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
{
|
|
11
11
|
"name": "sequant",
|
|
12
12
|
"description": "Structured workflow system for Claude Code - GitHub issue resolution with spec, exec, test, and QA phases. Includes 16 skills for planning, implementation, testing, code review, and quality assurance.",
|
|
13
|
-
"version": "1.
|
|
13
|
+
"version": "1.17.0",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "sequant-io",
|
|
16
16
|
"email": "hello@sequant.io"
|
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sequant",
|
|
3
3
|
"description": "Structured workflow system for Claude Code - GitHub issue resolution with spec, exec, test, and QA phases",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.19.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "sequant-io",
|
|
7
7
|
"email": "hello@sequant.io"
|
|
8
|
-
}
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/sequant-io/sequant",
|
|
10
|
+
"repository": "https://github.com/sequant-io/sequant",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"workflow",
|
|
14
|
+
"github",
|
|
15
|
+
"issues",
|
|
16
|
+
"quality",
|
|
17
|
+
"testing",
|
|
18
|
+
"code-review",
|
|
19
|
+
"automation"
|
|
20
|
+
]
|
|
9
21
|
}
|
package/dist/bin/cli.js
CHANGED
|
@@ -45,6 +45,7 @@ import { dashboardCommand } from "../src/commands/dashboard.js";
|
|
|
45
45
|
import { stateInitCommand, stateRebuildCommand, stateCleanCommand, } from "../src/commands/state.js";
|
|
46
46
|
import { syncCommand, areSkillsOutdated } from "../src/commands/sync.js";
|
|
47
47
|
import { mergeCommand } from "../src/commands/merge.js";
|
|
48
|
+
import { conventionsCommand } from "../src/commands/conventions.js";
|
|
48
49
|
import { getManifest } from "../src/lib/manifest.js";
|
|
49
50
|
const program = new Command();
|
|
50
51
|
// Handle --no-color before parsing
|
|
@@ -159,6 +160,12 @@ program
|
|
|
159
160
|
.option("--json", "Output as JSON")
|
|
160
161
|
.option("-v, --verbose", "Enable verbose output")
|
|
161
162
|
.action(mergeCommand);
|
|
163
|
+
program
|
|
164
|
+
.command("conventions")
|
|
165
|
+
.description("View and manage codebase conventions")
|
|
166
|
+
.option("--detect", "Re-run convention detection")
|
|
167
|
+
.option("--reset", "Clear detected conventions (keep manual)")
|
|
168
|
+
.action(conventionsCommand);
|
|
162
169
|
program
|
|
163
170
|
.command("logs")
|
|
164
171
|
.description("View and analyze workflow run logs")
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sequant",
|
|
3
|
+
"description": "Structured workflow system for Claude Code - GitHub issue resolution with spec, exec, test, and QA phases",
|
|
4
|
+
"version": "1.17.0",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "sequant-io",
|
|
7
|
+
"email": "hello@sequant.io"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/sequant-io/sequant",
|
|
10
|
+
"repository": "https://github.com/sequant-io/sequant",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"workflow",
|
|
14
|
+
"github",
|
|
15
|
+
"issues",
|
|
16
|
+
"quality",
|
|
17
|
+
"testing",
|
|
18
|
+
"code-review",
|
|
19
|
+
"automation"
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Sequant
|
|
2
|
+
|
|
3
|
+
Structured workflow system for Claude Code — GitHub issue resolution with spec, exec, test, and QA phases.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
/plugin install sequant@claude-plugin-directory
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or browse in `/plugin > Discover`.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **16 workflow skills** for planning, implementation, testing, and code review
|
|
16
|
+
- **Automated quality gates** with test and QA loops
|
|
17
|
+
- **GitHub integration** for issue tracking and PR creation
|
|
18
|
+
- **Multi-stack support** (Next.js, Python, Go, Rust, and more)
|
|
19
|
+
|
|
20
|
+
## Skills
|
|
21
|
+
|
|
22
|
+
| Skill | Purpose |
|
|
23
|
+
|-------|---------|
|
|
24
|
+
| `/spec` | Plan implementation and extract acceptance criteria |
|
|
25
|
+
| `/exec` | Implement changes in a feature worktree |
|
|
26
|
+
| `/test` | Browser-based UI testing |
|
|
27
|
+
| `/qa` | Code review and AC validation |
|
|
28
|
+
| `/fullsolve` | End-to-end issue resolution |
|
|
29
|
+
| `/solve` | Generate recommended workflow for issues |
|
|
30
|
+
|
|
31
|
+
## Documentation
|
|
32
|
+
|
|
33
|
+
- [Getting Started](https://github.com/sequant-io/sequant/tree/main/docs/getting-started)
|
|
34
|
+
- [Configuration](https://github.com/sequant-io/sequant/tree/main/docs/reference)
|
|
35
|
+
|
|
36
|
+
## License
|
|
37
|
+
|
|
38
|
+
MIT
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Post-tool hook for Claude Code
|
|
3
|
+
# - Timing instrumentation (END timestamp to pair with pre-tool START)
|
|
4
|
+
# - Auto-formatting for code quality
|
|
5
|
+
# - Quality observability (test/build failures, SQL queries)
|
|
6
|
+
# - Smart test running (P3): Runs related tests after file edits (opt-in)
|
|
7
|
+
# - Webhook notifications (P3): Notifies on issue close (opt-in)
|
|
8
|
+
# Runs AFTER each tool completes
|
|
9
|
+
|
|
10
|
+
# === ROLLBACK MECHANISM ===
|
|
11
|
+
# Set CLAUDE_HOOKS_DISABLED=true to bypass all hook logic
|
|
12
|
+
if [[ "${CLAUDE_HOOKS_DISABLED:-}" == "true" ]]; then
|
|
13
|
+
exit 0
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
# === READ INPUT FROM STDIN ===
|
|
17
|
+
# Claude Code passes tool data as JSON via stdin, not environment variables
|
|
18
|
+
INPUT_JSON=$(cat)
|
|
19
|
+
|
|
20
|
+
# Parse JSON using jq (preferred) or fallback to grep
|
|
21
|
+
if command -v jq &>/dev/null; then
|
|
22
|
+
TOOL_NAME=$(echo "$INPUT_JSON" | jq -r '.tool_name // empty')
|
|
23
|
+
# For Bash tool, extract .command from tool_input; for others, stringify the whole object
|
|
24
|
+
if [[ "$(echo "$INPUT_JSON" | jq -r '.tool_name // empty')" == "Bash" ]]; then
|
|
25
|
+
TOOL_INPUT=$(echo "$INPUT_JSON" | jq -r '.tool_input.command // empty')
|
|
26
|
+
else
|
|
27
|
+
TOOL_INPUT=$(echo "$INPUT_JSON" | jq -r '.tool_input | tostring // empty')
|
|
28
|
+
fi
|
|
29
|
+
TOOL_OUTPUT=$(echo "$INPUT_JSON" | jq -r '.tool_response | tostring // empty')
|
|
30
|
+
else
|
|
31
|
+
TOOL_NAME=$(echo "$INPUT_JSON" | grep -oE '"tool_name"\s*:\s*"[^"]+"' | head -1 | cut -d'"' -f4)
|
|
32
|
+
# For Bash tool, extract command from tool_input; for others, extract the whole object
|
|
33
|
+
if [[ "$TOOL_NAME" == "Bash" ]]; then
|
|
34
|
+
TOOL_INPUT=$(echo "$INPUT_JSON" | grep -oE '"command"\s*:\s*"[^"]+"' | head -1 | cut -d'"' -f4)
|
|
35
|
+
else
|
|
36
|
+
TOOL_INPUT=$(echo "$INPUT_JSON" | grep -oE '"tool_input"\s*:\s*\{[^}]+\}' | head -1)
|
|
37
|
+
fi
|
|
38
|
+
TOOL_OUTPUT=$(echo "$INPUT_JSON" | grep -oE '"tool_response"\s*:\s*\{[^}]+\}' | head -1)
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
TIMING_LOG="/tmp/claude-timing.log"
|
|
42
|
+
QUALITY_LOG="/tmp/claude-quality.log"
|
|
43
|
+
TESTS_LOG="/tmp/claude-tests.log"
|
|
44
|
+
PARALLEL_MARKER_PREFIX="/tmp/claude-parallel-"
|
|
45
|
+
|
|
46
|
+
# === AGENT ID DETECTION ===
|
|
47
|
+
# For parallel agents, detect group ID from marker files
|
|
48
|
+
# Format: /tmp/claude-parallel-<group-id>.marker
|
|
49
|
+
AGENT_ID=""
|
|
50
|
+
IS_PARALLEL_AGENT="false"
|
|
51
|
+
for marker in "${PARALLEL_MARKER_PREFIX}"*.marker; do
|
|
52
|
+
if [[ -f "$marker" ]]; then
|
|
53
|
+
# Extract group ID from marker filename
|
|
54
|
+
AGENT_ID=$(basename "$marker" | sed 's/claude-parallel-//' | sed 's/\.marker//')
|
|
55
|
+
IS_PARALLEL_AGENT="true"
|
|
56
|
+
break
|
|
57
|
+
fi
|
|
58
|
+
done
|
|
59
|
+
|
|
60
|
+
# === TIMING END ===
|
|
61
|
+
# Include agent ID in log format if available (AC-4)
|
|
62
|
+
if [[ -n "$AGENT_ID" ]]; then
|
|
63
|
+
echo "$(date +%s.%N) [$AGENT_ID] END $TOOL_NAME" >> "$TIMING_LOG"
|
|
64
|
+
else
|
|
65
|
+
echo "$(date +%s.%N) END $TOOL_NAME" >> "$TIMING_LOG"
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# === LOG ROTATION FOR QUALITY LOG ===
|
|
69
|
+
# Rotate if over 1000 lines to prevent unbounded growth
|
|
70
|
+
if [[ -f "$QUALITY_LOG" ]]; then
|
|
71
|
+
LINE_COUNT=$(wc -l < "$QUALITY_LOG" 2>/dev/null || echo 0)
|
|
72
|
+
if [[ "$LINE_COUNT" -gt 1000 ]]; then
|
|
73
|
+
tail -500 "$QUALITY_LOG" > "${QUALITY_LOG}.tmp" && mv "${QUALITY_LOG}.tmp" "$QUALITY_LOG"
|
|
74
|
+
fi
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# === LOG ROTATION FOR TESTS LOG ===
|
|
78
|
+
if [[ -f "$TESTS_LOG" ]]; then
|
|
79
|
+
LINE_COUNT=$(wc -l < "$TESTS_LOG" 2>/dev/null || echo 0)
|
|
80
|
+
if [[ "$LINE_COUNT" -gt 1000 ]]; then
|
|
81
|
+
tail -500 "$TESTS_LOG" > "${TESTS_LOG}.tmp" && mv "${TESTS_LOG}.tmp" "$TESTS_LOG"
|
|
82
|
+
fi
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# === JSON PARSING HELPER ===
|
|
86
|
+
# Try jq first for reliable JSON parsing, fall back to grep for simpler systems
|
|
87
|
+
extract_file_path() {
|
|
88
|
+
local input="$1"
|
|
89
|
+
local path=""
|
|
90
|
+
|
|
91
|
+
if command -v jq &>/dev/null; then
|
|
92
|
+
path=$(echo "$input" | jq -r '.file_path // empty' 2>/dev/null)
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# Fallback to grep if jq fails or isn't available
|
|
96
|
+
if [[ -z "$path" ]]; then
|
|
97
|
+
path=$(echo "$input" | grep -oE '"file_path"\s*:\s*"[^"]+"' | head -1 | cut -d'"' -f4)
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
echo "$path"
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# === SECURITY WARNING LOGGING (AC-3 for Issue #492) ===
|
|
104
|
+
# Log warnings (don't block) for dangerous patterns in edited/written files
|
|
105
|
+
# These are not blocking because there may be legitimate uses, but should be reviewed
|
|
106
|
+
check_security_patterns() {
|
|
107
|
+
local content="$1"
|
|
108
|
+
local file_path="$2"
|
|
109
|
+
local warnings=()
|
|
110
|
+
|
|
111
|
+
# dangerouslyDisableSandbox usage (Bash tool security bypass)
|
|
112
|
+
if echo "$content" | grep -qE 'dangerouslyDisableSandbox.*true'; then
|
|
113
|
+
warnings+=("dangerouslyDisableSandbox=true (Bash security bypass)")
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
# eval() usage (dynamic code execution - XSS/injection risk)
|
|
117
|
+
if echo "$content" | grep -qE '\beval\s*\('; then
|
|
118
|
+
warnings+=("eval() usage (dynamic code execution)")
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
# innerHTML assignment (XSS vulnerability without sanitization)
|
|
122
|
+
if echo "$content" | grep -qE '\.innerHTML\s*='; then
|
|
123
|
+
warnings+=("innerHTML assignment (potential XSS)")
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
# SQL string concatenation (SQL injection risk)
|
|
127
|
+
# Look for patterns like: query + variable or `SELECT ... ${variable}`
|
|
128
|
+
if echo "$content" | grep -qE "(query|sql|SQL)\s*\+\s*|query\s*=.*\\\$\{"; then
|
|
129
|
+
warnings+=("SQL string concatenation (potential injection)")
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
# Log any warnings found
|
|
133
|
+
for warning in "${warnings[@]}"; do
|
|
134
|
+
echo "$(date +%H:%M:%S) SECURITY_WARNING: $warning in $file_path" >> "$QUALITY_LOG"
|
|
135
|
+
done
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if [[ "${CLAUDE_HOOKS_SECURITY:-true}" != "false" ]]; then
|
|
139
|
+
if [[ "$TOOL_NAME" == "Edit" || "$TOOL_NAME" == "Write" ]]; then
|
|
140
|
+
FILE_PATH=$(extract_file_path "$TOOL_INPUT")
|
|
141
|
+
|
|
142
|
+
if [[ -n "$FILE_PATH" && -f "$FILE_PATH" ]]; then
|
|
143
|
+
# Only check TypeScript/JavaScript files for security patterns
|
|
144
|
+
if [[ "$FILE_PATH" =~ \.(ts|tsx|js|jsx)$ ]]; then
|
|
145
|
+
FILE_CONTENT=$(cat "$FILE_PATH" 2>/dev/null || true)
|
|
146
|
+
if [[ -n "$FILE_CONTENT" ]]; then
|
|
147
|
+
check_security_patterns "$FILE_CONTENT" "$FILE_PATH"
|
|
148
|
+
fi
|
|
149
|
+
fi
|
|
150
|
+
fi
|
|
151
|
+
fi
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
# === AUTO-FORMAT ON FILE WRITE ===
|
|
155
|
+
# Skip auto-formatting for parallel agents (AC-5)
|
|
156
|
+
# Parent agent will format after the parallel group completes
|
|
157
|
+
if [[ "$IS_PARALLEL_AGENT" == "true" ]]; then
|
|
158
|
+
# Log that formatting was skipped for parallel agent
|
|
159
|
+
if [[ "$TOOL_NAME" == "Edit" || "$TOOL_NAME" == "Write" ]]; then
|
|
160
|
+
FILE_PATH=$(extract_file_path "$TOOL_INPUT")
|
|
161
|
+
if [[ -n "$FILE_PATH" ]]; then
|
|
162
|
+
echo "$(date +%H:%M:%S) SKIP_FORMAT (parallel): $FILE_PATH" >> "$QUALITY_LOG"
|
|
163
|
+
fi
|
|
164
|
+
fi
|
|
165
|
+
elif [[ "$TOOL_NAME" == "Edit" || "$TOOL_NAME" == "Write" ]]; then
|
|
166
|
+
FILE_PATH=$(extract_file_path "$TOOL_INPUT")
|
|
167
|
+
|
|
168
|
+
if [[ -n "$FILE_PATH" && -f "$FILE_PATH" ]]; then
|
|
169
|
+
# Auto-format TypeScript/JavaScript files (synchronous to avoid race conditions)
|
|
170
|
+
if [[ "$FILE_PATH" =~ \.(ts|tsx|js|jsx)$ ]]; then
|
|
171
|
+
if npx prettier --write "$FILE_PATH" 2>/dev/null; then
|
|
172
|
+
echo "$(date +%H:%M:%S) FORMATTED: $FILE_PATH" >> "$QUALITY_LOG"
|
|
173
|
+
fi
|
|
174
|
+
fi
|
|
175
|
+
|
|
176
|
+
# Auto-format JSON files (synchronous)
|
|
177
|
+
if [[ "$FILE_PATH" =~ \.json$ ]]; then
|
|
178
|
+
npx prettier --write "$FILE_PATH" 2>/dev/null
|
|
179
|
+
fi
|
|
180
|
+
fi
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
# === TRACK GIT OPERATIONS ===
|
|
184
|
+
if [[ "$TOOL_NAME" == "Bash" ]]; then
|
|
185
|
+
if echo "$TOOL_INPUT" | grep -qE 'git (commit|push|pr create)'; then
|
|
186
|
+
# Truncate long git commands for readability
|
|
187
|
+
GIT_CMD=$(echo "$TOOL_INPUT" | head -c 200)
|
|
188
|
+
echo "$(date +%H:%M:%S) GIT: $GIT_CMD" >> "$QUALITY_LOG"
|
|
189
|
+
fi
|
|
190
|
+
fi
|
|
191
|
+
|
|
192
|
+
# === DETECT TEST FAILURES ===
|
|
193
|
+
# Supports: npm, bun, yarn, pnpm
|
|
194
|
+
if [[ "$TOOL_NAME" == "Bash" ]] && echo "$TOOL_INPUT" | grep -qE '(npm (test|run test)|bun (test|run test)|yarn (test|run test)|pnpm (test|run test))'; then
|
|
195
|
+
if echo "$TOOL_OUTPUT" | grep -qE '(FAIL|failed|Error:)'; then
|
|
196
|
+
echo "$(date +%H:%M:%S) TEST_FAILURE detected" >> "$QUALITY_LOG"
|
|
197
|
+
fi
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
# === DETECT BUILD FAILURES ===
|
|
201
|
+
# Supports: npm, bun, yarn, pnpm
|
|
202
|
+
if [[ "$TOOL_NAME" == "Bash" ]] && echo "$TOOL_INPUT" | grep -qE '(npm run build|bun run build|yarn build|yarn run build|pnpm run build)'; then
|
|
203
|
+
if echo "$TOOL_OUTPUT" | grep -qE '(error TS|Build failed|Error:)'; then
|
|
204
|
+
echo "$(date +%H:%M:%S) BUILD_FAILURE detected" >> "$QUALITY_LOG"
|
|
205
|
+
fi
|
|
206
|
+
fi
|
|
207
|
+
|
|
208
|
+
# === SMART TEST RUNNING (P3) ===
|
|
209
|
+
# Opt-in: Set CLAUDE_HOOKS_SMART_TESTS=true to enable
|
|
210
|
+
# Runs related tests asynchronously after file edits
|
|
211
|
+
if [[ "${CLAUDE_HOOKS_SMART_TESTS:-}" == "true" ]]; then
|
|
212
|
+
if [[ "$TOOL_NAME" == "Edit" || "$TOOL_NAME" == "Write" ]]; then
|
|
213
|
+
FILE_PATH=$(extract_file_path "$TOOL_INPUT")
|
|
214
|
+
|
|
215
|
+
if [[ -n "$FILE_PATH" && "$FILE_PATH" =~ \.(ts|tsx)$ ]]; then
|
|
216
|
+
# Extract filename without extension (use -E for macOS sed compatibility)
|
|
217
|
+
FILENAME=$(basename "$FILE_PATH" | sed -E 's/\.(ts|tsx)$//')
|
|
218
|
+
|
|
219
|
+
# Find related test file in __tests__/ directory
|
|
220
|
+
# This project uses centralized tests, not co-located
|
|
221
|
+
PROJECT_ROOT="${FILE_PATH%%/lib/*}"
|
|
222
|
+
if [[ "$PROJECT_ROOT" == "$FILE_PATH" ]]; then
|
|
223
|
+
PROJECT_ROOT="${FILE_PATH%%/components/*}"
|
|
224
|
+
fi
|
|
225
|
+
if [[ "$PROJECT_ROOT" == "$FILE_PATH" ]]; then
|
|
226
|
+
PROJECT_ROOT="${FILE_PATH%%/app/*}"
|
|
227
|
+
fi
|
|
228
|
+
|
|
229
|
+
# Search for test files matching the source file name
|
|
230
|
+
TEST_FILE=""
|
|
231
|
+
if [[ -d "$PROJECT_ROOT/__tests__" ]]; then
|
|
232
|
+
# Try direct match first
|
|
233
|
+
if [[ -f "$PROJECT_ROOT/__tests__/${FILENAME}.test.ts" ]]; then
|
|
234
|
+
TEST_FILE="$PROJECT_ROOT/__tests__/${FILENAME}.test.ts"
|
|
235
|
+
elif [[ -f "$PROJECT_ROOT/__tests__/${FILENAME}.test.tsx" ]]; then
|
|
236
|
+
TEST_FILE="$PROJECT_ROOT/__tests__/${FILENAME}.test.tsx"
|
|
237
|
+
# Try integration tests
|
|
238
|
+
elif [[ -f "$PROJECT_ROOT/__tests__/integration/${FILENAME}.test.ts" ]]; then
|
|
239
|
+
TEST_FILE="$PROJECT_ROOT/__tests__/integration/${FILENAME}.test.ts"
|
|
240
|
+
elif [[ -f "$PROJECT_ROOT/__tests__/integration/${FILENAME}.test.tsx" ]]; then
|
|
241
|
+
TEST_FILE="$PROJECT_ROOT/__tests__/integration/${FILENAME}.test.tsx"
|
|
242
|
+
fi
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
if [[ -n "$TEST_FILE" && -f "$TEST_FILE" ]]; then
|
|
246
|
+
echo "$(date +%H:%M:%S) SMART_TEST: Running $TEST_FILE for $FILE_PATH" >> "$TESTS_LOG"
|
|
247
|
+
|
|
248
|
+
# Run test asynchronously to avoid blocking
|
|
249
|
+
# Use timeout/gtimeout if available, otherwise run without timeout
|
|
250
|
+
(
|
|
251
|
+
cd "$PROJECT_ROOT" 2>/dev/null || exit
|
|
252
|
+
TIMEOUT_CMD=""
|
|
253
|
+
if command -v timeout &>/dev/null; then
|
|
254
|
+
TIMEOUT_CMD="timeout 30"
|
|
255
|
+
elif command -v gtimeout &>/dev/null; then
|
|
256
|
+
TIMEOUT_CMD="gtimeout 30"
|
|
257
|
+
fi
|
|
258
|
+
$TIMEOUT_CMD npm test -- --testPathPatterns="$(basename "$TEST_FILE")" --silent 2>&1 | head -20 >> "$TESTS_LOG"
|
|
259
|
+
if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
|
|
260
|
+
echo "$(date +%H:%M:%S) SMART_TEST_RESULT: FAIL" >> "$TESTS_LOG"
|
|
261
|
+
else
|
|
262
|
+
echo "$(date +%H:%M:%S) SMART_TEST_RESULT: PASS" >> "$TESTS_LOG"
|
|
263
|
+
fi
|
|
264
|
+
) &
|
|
265
|
+
fi
|
|
266
|
+
fi
|
|
267
|
+
fi
|
|
268
|
+
fi
|
|
269
|
+
|
|
270
|
+
# === WEBHOOK NOTIFICATIONS (P3) ===
|
|
271
|
+
# Opt-in: Set CLAUDE_HOOKS_WEBHOOK_URL to enable
|
|
272
|
+
# Fires notification when issues are closed
|
|
273
|
+
if [[ -n "${CLAUDE_HOOKS_WEBHOOK_URL:-}" ]]; then
|
|
274
|
+
if [[ "$TOOL_NAME" == "Bash" ]] && echo "$TOOL_INPUT" | grep -qE 'gh issue close'; then
|
|
275
|
+
# Extract issue number
|
|
276
|
+
ISSUE_NUM=$(echo "$TOOL_INPUT" | grep -oE '#?[0-9]+' | head -1 | tr -d '#')
|
|
277
|
+
|
|
278
|
+
if [[ -n "$ISSUE_NUM" ]]; then
|
|
279
|
+
echo "$(date +%H:%M:%S) WEBHOOK: Notifying issue #$ISSUE_NUM closed" >> "$QUALITY_LOG"
|
|
280
|
+
|
|
281
|
+
# Fire-and-forget async curl (don't block on webhook failures)
|
|
282
|
+
(
|
|
283
|
+
curl -s -X POST "$CLAUDE_HOOKS_WEBHOOK_URL" \
|
|
284
|
+
-H 'Content-Type: application/json' \
|
|
285
|
+
-d "{\"text\":\"Issue #$ISSUE_NUM completed by Claude Code automation\"}" \
|
|
286
|
+
--max-time 5 2>/dev/null || true
|
|
287
|
+
) &
|
|
288
|
+
fi
|
|
289
|
+
fi
|
|
290
|
+
fi
|
|
291
|
+
|
|
292
|
+
exit 0
|