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.
Files changed (75) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +14 -2
  3. package/dist/bin/cli.js +7 -0
  4. package/dist/marketplace/external_plugins/sequant/.claude-plugin/plugin.json +21 -0
  5. package/dist/marketplace/external_plugins/sequant/README.md +38 -0
  6. package/dist/marketplace/external_plugins/sequant/hooks/post-tool.sh +292 -0
  7. package/dist/marketplace/external_plugins/sequant/hooks/pre-tool.sh +463 -0
  8. package/dist/marketplace/external_plugins/sequant/skills/_shared/references/prompt-templates.md +350 -0
  9. package/dist/marketplace/external_plugins/sequant/skills/_shared/references/subagent-types.md +131 -0
  10. package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +474 -0
  11. package/dist/marketplace/external_plugins/sequant/skills/clean/SKILL.md +211 -0
  12. package/dist/marketplace/external_plugins/sequant/skills/docs/SKILL.md +337 -0
  13. package/dist/marketplace/external_plugins/sequant/skills/exec/SKILL.md +807 -0
  14. package/dist/marketplace/external_plugins/sequant/skills/fullsolve/SKILL.md +678 -0
  15. package/dist/marketplace/external_plugins/sequant/skills/improve/SKILL.md +668 -0
  16. package/dist/marketplace/external_plugins/sequant/skills/loop/SKILL.md +374 -0
  17. package/dist/marketplace/external_plugins/sequant/skills/qa/SKILL.md +570 -0
  18. package/dist/marketplace/external_plugins/sequant/skills/qa/references/code-quality-exemplars.md +107 -0
  19. package/dist/marketplace/external_plugins/sequant/skills/qa/references/code-review-checklist.md +65 -0
  20. package/dist/marketplace/external_plugins/sequant/skills/qa/references/quality-gates.md +179 -0
  21. package/dist/marketplace/external_plugins/sequant/skills/qa/references/semgrep-rules.md +207 -0
  22. package/dist/marketplace/external_plugins/sequant/skills/qa/references/testing-requirements.md +109 -0
  23. package/dist/marketplace/external_plugins/sequant/skills/qa/scripts/quality-checks.sh +622 -0
  24. package/dist/marketplace/external_plugins/sequant/skills/reflect/SKILL.md +175 -0
  25. package/dist/marketplace/external_plugins/sequant/skills/reflect/references/documentation-tiers.md +70 -0
  26. package/dist/marketplace/external_plugins/sequant/skills/reflect/references/phase-reflection.md +95 -0
  27. package/dist/marketplace/external_plugins/sequant/skills/security-review/SKILL.md +358 -0
  28. package/dist/marketplace/external_plugins/sequant/skills/security-review/references/security-checklists.md +432 -0
  29. package/dist/marketplace/external_plugins/sequant/skills/solve/SKILL.md +697 -0
  30. package/dist/marketplace/external_plugins/sequant/skills/spec/SKILL.md +754 -0
  31. package/dist/marketplace/external_plugins/sequant/skills/spec/references/parallel-groups.md +72 -0
  32. package/dist/marketplace/external_plugins/sequant/skills/spec/references/recommended-workflow.md +92 -0
  33. package/dist/marketplace/external_plugins/sequant/skills/spec/references/verification-criteria.md +104 -0
  34. package/dist/marketplace/external_plugins/sequant/skills/test/SKILL.md +600 -0
  35. package/dist/marketplace/external_plugins/sequant/skills/testgen/SKILL.md +576 -0
  36. package/dist/marketplace/external_plugins/sequant/skills/verify/SKILL.md +281 -0
  37. package/dist/src/commands/conventions.d.ts +9 -0
  38. package/dist/src/commands/conventions.js +61 -0
  39. package/dist/src/commands/init.js +12 -0
  40. package/dist/src/commands/run.d.ts +13 -280
  41. package/dist/src/commands/run.js +23 -1956
  42. package/dist/src/commands/sync.js +3 -0
  43. package/dist/src/commands/update.js +3 -0
  44. package/dist/src/lib/conventions-detector.d.ts +62 -0
  45. package/dist/src/lib/conventions-detector.js +510 -0
  46. package/dist/src/lib/plugin-version-sync.d.ts +2 -1
  47. package/dist/src/lib/plugin-version-sync.js +28 -7
  48. package/dist/src/lib/settings.d.ts +8 -0
  49. package/dist/src/lib/settings.js +1 -0
  50. package/dist/src/lib/solve-comment-parser.d.ts +26 -0
  51. package/dist/src/lib/solve-comment-parser.js +63 -7
  52. package/dist/src/lib/stacks.d.ts +4 -2
  53. package/dist/src/lib/stacks.js +43 -3
  54. package/dist/src/lib/workflow/batch-executor.d.ts +117 -0
  55. package/dist/src/lib/workflow/batch-executor.js +574 -0
  56. package/dist/src/lib/workflow/phase-executor.d.ts +40 -0
  57. package/dist/src/lib/workflow/phase-executor.js +381 -0
  58. package/dist/src/lib/workflow/phase-mapper.d.ts +65 -0
  59. package/dist/src/lib/workflow/phase-mapper.js +147 -0
  60. package/dist/src/lib/workflow/pr-operations.d.ts +86 -0
  61. package/dist/src/lib/workflow/pr-operations.js +326 -0
  62. package/dist/src/lib/workflow/pr-status.d.ts +9 -7
  63. package/dist/src/lib/workflow/pr-status.js +13 -11
  64. package/dist/src/lib/workflow/run-summary.d.ts +36 -0
  65. package/dist/src/lib/workflow/run-summary.js +142 -0
  66. package/dist/src/lib/workflow/state-schema.d.ts +5 -5
  67. package/dist/src/lib/workflow/worktree-manager.d.ts +205 -0
  68. package/dist/src/lib/workflow/worktree-manager.js +918 -0
  69. package/package.json +3 -1
  70. package/templates/skills/exec/SKILL.md +1086 -29
  71. package/templates/skills/fullsolve/SKILL.md +11 -1
  72. package/templates/skills/qa/SKILL.md +1758 -142
  73. package/templates/skills/solve/SKILL.md +86 -0
  74. package/templates/skills/spec/SKILL.md +53 -0
  75. 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.0",
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.16.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