autoworkflow 3.0.1 → 3.1.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.
@@ -1,65 +1,241 @@
1
1
  #!/bin/bash
2
- # AutoWorkflow Pre-Commit Check Hook
3
- # Runs on: PreToolUse for Bash tool
4
- # Purpose: Check for issues before commands execute
2
+ # AutoWorkflow Pre-Commit Gate Check
3
+ # Runs on: PreToolUse for Bash tool (only git commit commands)
4
+ # Purpose: BLOCK commits that violate workflow gates
5
+ #
6
+ # This hook implements the full pre_commit_gate from system/gates.md
7
+ # All 7 checks must pass or the commit is BLOCKED
5
8
 
6
- # Check for TODO/FIXME in staged files
7
- check_staged_todos() {
8
- if git diff --cached --name-only 2>/dev/null | xargs grep -l "TODO\|FIXME\|XXX\|HACK" 2>/dev/null; then
9
- echo ""
10
- echo "=================================================="
11
- echo "AUTOWORKFLOW: TODO/FIXME DETECTED"
12
- echo "=================================================="
13
- echo ""
14
- echo "Found TODO/FIXME comments in staged files."
15
- echo "Remove all TODO/FIXME comments before committing."
16
- echo ""
17
- echo "=================================================="
18
- return 1
9
+ set -e
10
+
11
+ # Colors
12
+ RED='\033[0;31m'
13
+ GREEN='\033[0;32m'
14
+ YELLOW='\033[1;33m'
15
+ CYAN='\033[0;36m'
16
+ BOLD='\033[1m'
17
+ NC='\033[0m'
18
+
19
+ # State directory
20
+ STATE_DIR=".claude/.autoworkflow"
21
+ mkdir -p "$STATE_DIR"
22
+
23
+ # Track errors
24
+ ERRORS=0
25
+ WARNINGS=0
26
+
27
+ # Output formatting
28
+ print_header() {
29
+ echo ""
30
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
31
+ echo -e "${BOLD}AUTOWORKFLOW: PRE-COMMIT GATE${NC}"
32
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
33
+ echo ""
34
+ }
35
+
36
+ print_check() {
37
+ local num=$1
38
+ local name=$2
39
+ local status=$3
40
+ local details=$4
41
+
42
+ if [ "$status" = "pass" ]; then
43
+ echo -e "[${num}/7] ${name}: ${GREEN}✅ PASS${NC} ${details}"
44
+ elif [ "$status" = "fail" ]; then
45
+ echo -e "[${num}/7] ${name}: ${RED}⛔ FAIL${NC}"
46
+ echo -e " └── ${details}"
47
+ else
48
+ echo -e "[${num}/7] ${name}: ${YELLOW}⚠ SKIP${NC} ${details}"
49
+ fi
50
+ }
51
+
52
+ # Check 1: TypeScript errors
53
+ check_typescript() {
54
+ if [ -f "package.json" ] && grep -q "typecheck\|tsc" package.json 2>/dev/null; then
55
+ if npm run typecheck 2>&1 | grep -q "error"; then
56
+ TS_ERRORS=$(npm run typecheck 2>&1 | grep -c "error" || echo "0")
57
+ print_check "1" "TypeScript" "fail" "${TS_ERRORS} error(s) found"
58
+ ERRORS=$((ERRORS + 1))
59
+ return 1
60
+ else
61
+ print_check "1" "TypeScript" "pass" "No errors"
62
+ return 0
63
+ fi
64
+ else
65
+ print_check "1" "TypeScript" "skip" "(no typecheck script)"
66
+ return 0
67
+ fi
68
+ }
69
+
70
+ # Check 2: ESLint warnings
71
+ check_eslint() {
72
+ if [ -f "package.json" ] && grep -q '"lint"' package.json 2>/dev/null; then
73
+ LINT_OUTPUT=$(npm run lint 2>&1 || true)
74
+ if echo "$LINT_OUTPUT" | grep -qE "error|warning"; then
75
+ LINT_ERRORS=$(echo "$LINT_OUTPUT" | grep -c "error" || echo "0")
76
+ LINT_WARNINGS=$(echo "$LINT_OUTPUT" | grep -c "warning" || echo "0")
77
+ print_check "2" "ESLint" "fail" "${LINT_ERRORS} error(s), ${LINT_WARNINGS} warning(s)"
78
+ ERRORS=$((ERRORS + 1))
79
+ return 1
80
+ else
81
+ print_check "2" "ESLint" "pass" "No issues"
82
+ return 0
83
+ fi
84
+ else
85
+ print_check "2" "ESLint" "skip" "(no lint script)"
86
+ return 0
87
+ fi
88
+ }
89
+
90
+ # Check 3: TODO/FIXME in staged files
91
+ check_todos() {
92
+ if git diff --cached --name-only 2>/dev/null | head -1 | grep -q .; then
93
+ TODO_FILES=$(git diff --cached --name-only 2>/dev/null | xargs grep -l "TODO\|FIXME\|XXX\|HACK" 2>/dev/null || true)
94
+ if [ -n "$TODO_FILES" ]; then
95
+ TODO_COUNT=$(echo "$TODO_FILES" | wc -l | tr -d ' ')
96
+ print_check "3" "TODO/FIXME" "fail" "Found in ${TODO_COUNT} file(s)"
97
+ echo "$TODO_FILES" | while read -r file; do
98
+ echo -e " ${YELLOW}→${NC} $file"
99
+ done
100
+ ERRORS=$((ERRORS + 1))
101
+ return 1
102
+ fi
19
103
  fi
104
+ print_check "3" "TODO/FIXME" "pass" "None in staged files"
20
105
  return 0
21
106
  }
22
107
 
23
- # Check for console.log in staged files
24
- check_staged_logs() {
25
- if git diff --cached --name-only 2>/dev/null | grep -v "\.test\.\|\.spec\.\|__tests__" | xargs grep -l "console\.log\|console\.debug\|console\.info" 2>/dev/null; then
26
- echo ""
27
- echo "=================================================="
28
- echo "AUTOWORKFLOW: CONSOLE.LOG DETECTED"
29
- echo "=================================================="
30
- echo ""
31
- echo "Found console.log statements in staged files."
32
- echo "Remove debug logs before committing."
33
- echo ""
34
- echo "=================================================="
35
- return 1
108
+ # Check 4: console.log in staged files
109
+ check_console_logs() {
110
+ if git diff --cached --name-only 2>/dev/null | head -1 | grep -q .; then
111
+ # Exclude test files
112
+ LOG_FILES=$(git diff --cached --name-only 2>/dev/null | grep -v "\.test\.\|\.spec\.\|__tests__" | xargs grep -l "console\.log\|console\.debug\|console\.info" 2>/dev/null || true)
113
+ if [ -n "$LOG_FILES" ]; then
114
+ LOG_COUNT=$(echo "$LOG_FILES" | wc -l | tr -d ' ')
115
+ print_check "4" "console.log" "fail" "Found in ${LOG_COUNT} file(s)"
116
+ echo "$LOG_FILES" | while read -r file; do
117
+ echo -e " ${YELLOW}→${NC} $file"
118
+ done
119
+ ERRORS=$((ERRORS + 1))
120
+ return 1
121
+ fi
36
122
  fi
123
+ print_check "4" "console.log" "pass" "None in staged files"
37
124
  return 0
38
125
  }
39
126
 
40
- # Only run checks if there are staged changes
41
- if git diff --cached --quiet 2>/dev/null; then
42
- # No staged changes, skip checks
43
- exit 0
44
- fi
127
+ # Check 5: UI Enforcement (orphan features)
128
+ check_ui_enforcement() {
129
+ if [ -f "package.json" ] && grep -q '"audit:ui"' package.json 2>/dev/null; then
130
+ AUDIT_OUTPUT=$(npm run audit:ui 2>&1 || true)
131
+ if echo "$AUDIT_OUTPUT" | grep -qi "orphan\|missing ui\|no component"; then
132
+ print_check "5" "UI Enforcement" "fail" "Orphan features detected"
133
+ ERRORS=$((ERRORS + 1))
134
+ return 1
135
+ else
136
+ print_check "5" "UI Enforcement" "pass" "No orphan features"
137
+ return 0
138
+ fi
139
+ else
140
+ print_check "5" "UI Enforcement" "skip" "(no audit:ui script)"
141
+ return 0
142
+ fi
143
+ }
45
144
 
46
- # Run checks
47
- ERRORS=0
145
+ # Check 6: Circular Dependencies
146
+ check_circular_deps() {
147
+ if [ -f "package.json" ] && grep -q '"audit:cycles"' package.json 2>/dev/null; then
148
+ CYCLE_OUTPUT=$(npm run audit:cycles 2>&1 || true)
149
+ if echo "$CYCLE_OUTPUT" | grep -qi "circular\|cycle"; then
150
+ print_check "6" "Circular Deps" "fail" "Cycles detected"
151
+ ERRORS=$((ERRORS + 1))
152
+ return 1
153
+ else
154
+ print_check "6" "Circular Deps" "pass" "No cycles"
155
+ return 0
156
+ fi
157
+ else
158
+ print_check "6" "Circular Deps" "skip" "(no audit:cycles script)"
159
+ return 0
160
+ fi
161
+ }
48
162
 
49
- check_staged_todos
50
- if [ $? -ne 0 ]; then
51
- ERRORS=$((ERRORS + 1))
52
- fi
163
+ # Check 7: Commit message format (conventional commits)
164
+ check_commit_message() {
165
+ # Get the commit message from the staged commit or COMMIT_EDITMSG
166
+ local commit_msg=""
53
167
 
54
- check_staged_logs
55
- if [ $? -ne 0 ]; then
56
- ERRORS=$((ERRORS + 1))
57
- fi
168
+ if [ -f ".git/COMMIT_EDITMSG" ]; then
169
+ commit_msg=$(head -1 .git/COMMIT_EDITMSG)
170
+ fi
171
+
172
+ if [ -n "$commit_msg" ]; then
173
+ # Check conventional commit format: type(scope): description
174
+ if echo "$commit_msg" | grep -qE "^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?: .+"; then
175
+ print_check "7" "Commit Format" "pass" "Valid conventional commit"
176
+ return 0
177
+ else
178
+ print_check "7" "Commit Format" "fail" "Must be: type(scope): description"
179
+ echo -e " ${YELLOW}→${NC} Got: $commit_msg"
180
+ ERRORS=$((ERRORS + 1))
181
+ return 1
182
+ fi
183
+ else
184
+ print_check "7" "Commit Format" "skip" "(no message yet)"
185
+ return 0
186
+ fi
187
+ }
58
188
 
59
- if [ $ERRORS -gt 0 ]; then
189
+ # Main execution
190
+ main() {
191
+ print_header
192
+
193
+ echo "Checking requirements..."
60
194
  echo ""
61
- echo "Fix the above issues before committing."
195
+
196
+ # Run all checks
197
+ check_typescript || true
198
+ check_eslint || true
199
+ check_todos || true
200
+ check_console_logs || true
201
+ check_ui_enforcement || true
202
+ check_circular_deps || true
203
+ check_commit_message || true
204
+
62
205
  echo ""
206
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
207
+
208
+ if [ $ERRORS -gt 0 ]; then
209
+ echo -e "${RED}${BOLD}⛔ GATE BLOCKED${NC} - ${ERRORS} issue(s) must be fixed"
210
+ echo ""
211
+ echo "Fix the issues above before committing."
212
+ echo "The commit has been BLOCKED."
213
+ echo ""
214
+
215
+ # Write block status to state file
216
+ echo "BLOCKED" > "$STATE_DIR/gate-status"
217
+ echo "$ERRORS" > "$STATE_DIR/gate-errors"
218
+
219
+ # EXIT WITH ERROR TO BLOCK THE COMMIT
220
+ exit 1
221
+ else
222
+ echo -e "${GREEN}${BOLD}✅ ALL GATES PASSED${NC}"
223
+ echo ""
224
+ echo "Ready to commit."
225
+ echo ""
226
+
227
+ # Write pass status to state file
228
+ echo "PASSED" > "$STATE_DIR/gate-status"
229
+ echo "0" > "$STATE_DIR/gate-errors"
230
+
231
+ exit 0
232
+ fi
233
+ }
234
+
235
+ # Only run if there are staged changes
236
+ if git diff --cached --quiet 2>/dev/null; then
237
+ # No staged changes, skip all checks
238
+ exit 0
63
239
  fi
64
240
 
65
- exit 0
241
+ main
@@ -0,0 +1,67 @@
1
+ #!/bin/bash
2
+ # AutoWorkflow Pre-Tool Router
3
+ # Runs on: PreToolUse for Bash tool
4
+ # Purpose: Route commands to appropriate checks
5
+ #
6
+ # This router determines what checks to run based on the command being executed
7
+
8
+ TOOL_INPUT="$1"
9
+
10
+ # State directory
11
+ STATE_DIR=".claude/.autoworkflow"
12
+ mkdir -p "$STATE_DIR"
13
+
14
+ # Check if this is a git commit command
15
+ is_git_commit() {
16
+ echo "$TOOL_INPUT" | grep -qE "git\s+commit|git\s+.*commit"
17
+ }
18
+
19
+ # Check if this is a git push command
20
+ is_git_push() {
21
+ echo "$TOOL_INPUT" | grep -qE "git\s+push|git\s+.*push"
22
+ }
23
+
24
+ # Check if this is a destructive git command
25
+ is_destructive() {
26
+ echo "$TOOL_INPUT" | grep -qE "git\s+(reset\s+--hard|push\s+--force|push\s+-f|clean\s+-f|checkout\s+\.)"
27
+ }
28
+
29
+ # Route based on command type
30
+ if is_git_commit; then
31
+ # Run full pre-commit gate checks
32
+ exec ./.claude/hooks/pre-commit-check.sh
33
+
34
+ elif is_git_push; then
35
+ # Check if we're pushing to main/master without approval
36
+ if echo "$TOOL_INPUT" | grep -qE "(main|master)"; then
37
+ echo ""
38
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
39
+ echo "⚠️ AUTOWORKFLOW: PUSH TO MAIN DETECTED"
40
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
41
+ echo ""
42
+ echo "You're about to push directly to main/master."
43
+ echo "Consider creating a PR instead."
44
+ echo ""
45
+ # Don't block, just warn
46
+ exit 0
47
+ fi
48
+ exit 0
49
+
50
+ elif is_destructive; then
51
+ echo ""
52
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
53
+ echo "⚠️ AUTOWORKFLOW: DESTRUCTIVE COMMAND DETECTED"
54
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
55
+ echo ""
56
+ echo "Command: $TOOL_INPUT"
57
+ echo ""
58
+ echo "This is a destructive operation that may cause data loss."
59
+ echo "Make sure you have confirmed this with the user."
60
+ echo ""
61
+ # Don't block, just warn - user confirmation is handled by Claude
62
+ exit 0
63
+
64
+ else
65
+ # All other commands pass through
66
+ exit 0
67
+ fi