@boyingliu01/xp-gate 0.14.13 → 0.14.15
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/build-integrity/__tests__/gate-10.test.ts +1 -1
- package/hooks/gate-12-file-hygiene.sh +230 -0
- package/hooks/gate-8.sh +1 -1
- package/hooks/pre-commit +467 -279
- package/lib/doctor.js +32 -4
- package/mock-policy/AGENTS.md +3 -3
- package/mutation/AGENTS.md +3 -3
- package/package.json +2 -2
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/claude-code/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/qoder/plugin.json +1 -1
- package/plugins/qoder/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/qoder/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +3 -3
- package/principles/AGENTS.md +3 -3
- package/principles/__tests__/config.test.ts +1 -1
- package/principles/config.ts +1 -1
- package/skills/delphi-review/AGENTS.md +3 -3
- package/skills/sprint-flow/AGENTS.md +3 -3
- package/skills/test-specification-alignment/AGENTS.md +3 -3
|
@@ -71,7 +71,7 @@ describe('runTscCheck', () => {
|
|
|
71
71
|
process.cwd = originalCwd;
|
|
72
72
|
}
|
|
73
73
|
} finally {
|
|
74
|
-
await fs.rm(isolatedDir, { recursive: true, force: true }).catch(() => {});
|
|
74
|
+
await fs.rm(isolatedDir, { recursive: true, force: true }).catch(() => { /* ignore cleanup errors */ });
|
|
75
75
|
}
|
|
76
76
|
});
|
|
77
77
|
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# ============================================================================
|
|
2
|
+
# GATE 12: File Hygiene Check
|
|
3
|
+
# Detects common file quality issues in staged files:
|
|
4
|
+
# - Trailing whitespace
|
|
5
|
+
# - Missing EOF newline
|
|
6
|
+
# - Merge conflict markers (<<<<<<< , =======, >>>>>>>)
|
|
7
|
+
# - Oversized files (>1MB default, configurable)
|
|
8
|
+
# ============================================================================
|
|
9
|
+
|
|
10
|
+
2>&1 echo ""
|
|
11
|
+
2>&1 echo "→ Gate 12: File hygiene check..."
|
|
12
|
+
GATE_12_START=$(gate_start_ms)
|
|
13
|
+
|
|
14
|
+
# Configuration (can be overridden via environment)
|
|
15
|
+
MAX_FILE_SIZE_BYTES="${XP_GATE_MAX_FILE_SIZE:-1048576}" # 1MB default
|
|
16
|
+
|
|
17
|
+
# Get list of staged files (excluding deleted files)
|
|
18
|
+
STAGED_FILES=$(git diff --cached --name-only --diff-filter=d 2>/dev/null)
|
|
19
|
+
|
|
20
|
+
if [ -z "$STAGED_FILES" ]; then
|
|
21
|
+
echo " ℹ️ No staged files — skipping"
|
|
22
|
+
echo " ✅ PASSED - File Hygiene (no files to check)"
|
|
23
|
+
GATE_12_STATUS="SKIP"
|
|
24
|
+
record_gate_audit "gate-12" "file-hygiene" "$GATE_12_STATUS" "0" "$GATE_12_START"
|
|
25
|
+
return 0 2>/dev/null || true
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
HYGIENE_ISSUES=0
|
|
29
|
+
HYGIENE_FILES=""
|
|
30
|
+
|
|
31
|
+
# --- Check 1: Trailing whitespace ---
|
|
32
|
+
TRAILING_WS_FILES=""
|
|
33
|
+
while IFS= read -r file; do
|
|
34
|
+
[ -z "$file" ] && continue
|
|
35
|
+
# Skip binary files by checking if file contains null bytes
|
|
36
|
+
if git diff --cached --no-color -- "$file" 2>/dev/null | grep -q $'\x00'; then
|
|
37
|
+
continue
|
|
38
|
+
fi
|
|
39
|
+
# Check staged content for trailing whitespace (spaces/tabs at end of lines)
|
|
40
|
+
if git show ":$file" 2>/dev/null | grep -qE '[[:space:]]$'; then
|
|
41
|
+
TRAILING_WS_FILES="$TRAILING_WS_FILES $file"
|
|
42
|
+
fi
|
|
43
|
+
done <<EOF
|
|
44
|
+
$STAGED_FILES
|
|
45
|
+
EOF
|
|
46
|
+
|
|
47
|
+
if [ -n "$TRAILING_WS_FILES" ]; then
|
|
48
|
+
echo " ⚠️ Trailing whitespace detected in:"
|
|
49
|
+
for f in $TRAILING_WS_FILES; do
|
|
50
|
+
echo " - $f"
|
|
51
|
+
done
|
|
52
|
+
HYGIENE_ISSUES=$((HYGIENE_ISSUES + 1))
|
|
53
|
+
HYGIENE_FILES="$HYGIENE_FILES trailing-whitespace"
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# --- Check 2: Missing EOF newline ---
|
|
57
|
+
NO_EOF_NEWLINE_FILES=""
|
|
58
|
+
while IFS= read -r file; do
|
|
59
|
+
[ -z "$file" ] && continue
|
|
60
|
+
# Skip binary files
|
|
61
|
+
if git diff --cached --no-color -- "$file" 2>/dev/null | grep -q $'\x00'; then
|
|
62
|
+
continue
|
|
63
|
+
fi
|
|
64
|
+
# Check if file content ends with newline
|
|
65
|
+
FILE_CONTENT=$(git show ":$file" 2>/dev/null)
|
|
66
|
+
if [ -n "$FILE_CONTENT" ]; then
|
|
67
|
+
LAST_CHAR=$(printf '%s' "$FILE_CONTENT" | tail -c 1)
|
|
68
|
+
if [ "$LAST_CHAR" != "" ]; then
|
|
69
|
+
NO_EOF_NEWLINE_FILES="$NO_EOF_NEWLINE_FILES $file"
|
|
70
|
+
fi
|
|
71
|
+
fi
|
|
72
|
+
done <<EOF
|
|
73
|
+
$STAGED_FILES
|
|
74
|
+
EOF
|
|
75
|
+
|
|
76
|
+
if [ -n "$NO_EOF_NEWLINE_FILES" ]; then
|
|
77
|
+
echo " ⚠️ Missing EOF newline in:"
|
|
78
|
+
for f in $NO_EOF_NEWLINE_FILES; do
|
|
79
|
+
echo " - $f"
|
|
80
|
+
done
|
|
81
|
+
HYGIENE_ISSUES=$((HYGIENE_ISSUES + 1))
|
|
82
|
+
HYGIENE_FILES="$HYGIENE_FILES no-eof-newline"
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# --- Check 3: Merge conflict markers ---
|
|
86
|
+
CONFLICT_MARKER_FILES=""
|
|
87
|
+
while IFS= read -r file; do
|
|
88
|
+
[ -z "$file" ] && continue
|
|
89
|
+
# Skip binary files
|
|
90
|
+
if git diff --cached --no-color -- "$file" 2>/dev/null | grep -q $'\x00'; then
|
|
91
|
+
continue
|
|
92
|
+
fi
|
|
93
|
+
# Check for conflict markers in staged content
|
|
94
|
+
if git show ":$file" 2>/dev/null | grep -qE '^(<<<<<<<|=======|>>>>>>>)'; then
|
|
95
|
+
CONFLICT_MARKER_FILES="$CONFLICT_MARKER_FILES $file"
|
|
96
|
+
fi
|
|
97
|
+
done <<EOF
|
|
98
|
+
$STAGED_FILES
|
|
99
|
+
EOF
|
|
100
|
+
|
|
101
|
+
if [ -n "$CONFLICT_MARKER_FILES" ]; then
|
|
102
|
+
echo " ❌ Merge conflict markers detected in:"
|
|
103
|
+
for f in $CONFLICT_MARKER_FILES; do
|
|
104
|
+
echo " - $f"
|
|
105
|
+
done
|
|
106
|
+
HYGIENE_ISSUES=$((HYGIENE_ISSUES + 1))
|
|
107
|
+
HYGIENE_FILES="$HYGIENE_FILES conflict-markers"
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
# --- Check 4: Oversized files ---
|
|
111
|
+
OVERSIZED_FILES=""
|
|
112
|
+
while IFS= read -r file; do
|
|
113
|
+
[ -z "$file" ] && continue
|
|
114
|
+
[ ! -f "$file" ] && continue
|
|
115
|
+
FILE_SIZE=$(wc -c < "$file" 2>/dev/null | tr -d ' ')
|
|
116
|
+
if [ -n "$FILE_SIZE" ] && [ "$FILE_SIZE" -gt "$MAX_FILE_SIZE_BYTES" ]; then
|
|
117
|
+
SIZE_MB=$((FILE_SIZE / 1048576))
|
|
118
|
+
OVERSIZED_FILES="$OVERSIZED_FILES $file(${SIZE_MB}MB)"
|
|
119
|
+
fi
|
|
120
|
+
done <<EOF
|
|
121
|
+
$STAGED_FILES
|
|
122
|
+
EOF
|
|
123
|
+
|
|
124
|
+
if [ -n "$OVERSIZED_FILES" ]; then
|
|
125
|
+
echo " ⚠️ Oversized files (>${MAX_FILE_SIZE_BYTES} bytes):"
|
|
126
|
+
for f in $OVERSIZED_FILES; do
|
|
127
|
+
echo " - $f"
|
|
128
|
+
done
|
|
129
|
+
HYGIENE_ISSUES=$((HYGIENE_ISSUES + 1))
|
|
130
|
+
HYGIENE_FILES="$HYGIENE_FILES oversized-files"
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
# --- Check 5: YAML/JSON syntax validation (Issue #351) ---
|
|
134
|
+
# Only check staged YAML/JSON files that have changes
|
|
135
|
+
YAML_JSON_ERRORS=""
|
|
136
|
+
YAML_JSON_CHECK_COUNT=0
|
|
137
|
+
|
|
138
|
+
# Detect available validators
|
|
139
|
+
YAML_CMD=""
|
|
140
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
141
|
+
YAML_CMD="python3"
|
|
142
|
+
elif command -v python >/dev/null 2>&1; then
|
|
143
|
+
YAML_CMD="python"
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
JSON_CMD=""
|
|
147
|
+
if command -v node >/dev/null 2>&1; then
|
|
148
|
+
JSON_CMD="node"
|
|
149
|
+
elif command -v jq >/dev/null 2>&1; then
|
|
150
|
+
JSON_CMD="jq"
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
while IFS= read -r file; do
|
|
154
|
+
[ -z "$file" ] && continue
|
|
155
|
+
[ ! -f "$file" ] && continue
|
|
156
|
+
|
|
157
|
+
case "$file" in
|
|
158
|
+
*.yml|*.yaml)
|
|
159
|
+
if [ -n "$YAML_CMD" ]; then
|
|
160
|
+
YAML_JSON_CHECK_COUNT=$((YAML_JSON_CHECK_COUNT + 1))
|
|
161
|
+
# Validate YAML syntax using Python's yaml module
|
|
162
|
+
YAML_ERROR=$(git show ":$file" 2>/dev/null | $YAML_CMD -c "
|
|
163
|
+
import sys, yaml
|
|
164
|
+
try:
|
|
165
|
+
yaml.safe_load(sys.stdin.read())
|
|
166
|
+
except yaml.YAMLError as e:
|
|
167
|
+
print(str(e).split(chr(10))[0])
|
|
168
|
+
sys.exit(1)
|
|
169
|
+
except Exception:
|
|
170
|
+
pass
|
|
171
|
+
" 2>&1)
|
|
172
|
+
if [ $? -ne 0 ] && [ -n "$YAML_ERROR" ]; then
|
|
173
|
+
YAML_JSON_ERRORS="$YAML_JSON_ERRORS $file(yaml:$YAML_ERROR)"
|
|
174
|
+
fi
|
|
175
|
+
fi
|
|
176
|
+
;;
|
|
177
|
+
*.json)
|
|
178
|
+
YAML_JSON_CHECK_COUNT=$((YAML_JSON_CHECK_COUNT + 1))
|
|
179
|
+
if [ "$JSON_CMD" = "node" ]; then
|
|
180
|
+
JSON_ERROR=$(git show ":$file" 2>/dev/null | node -e "
|
|
181
|
+
let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{JSON.parse(d)}catch(e){console.log(e.message.split('\\n')[0]);process.exit(1)}})
|
|
182
|
+
" 2>&1)
|
|
183
|
+
if [ $? -ne 0 ] && [ -n "$JSON_ERROR" ]; then
|
|
184
|
+
YAML_JSON_ERRORS="$YAML_JSON_ERRORS $file(json:$JSON_ERROR)"
|
|
185
|
+
fi
|
|
186
|
+
elif [ "$JSON_CMD" = "jq" ]; then
|
|
187
|
+
JSON_ERROR=$(git show ":$file" 2>/dev/null | jq empty 2>&1)
|
|
188
|
+
if [ $? -ne 0 ] && [ -n "$JSON_ERROR" ]; then
|
|
189
|
+
YAML_JSON_ERRORS="$YAML_JSON_ERRORS $file(json:syntax error)"
|
|
190
|
+
fi
|
|
191
|
+
fi
|
|
192
|
+
;;
|
|
193
|
+
esac
|
|
194
|
+
done <<EOF
|
|
195
|
+
$STAGED_FILES
|
|
196
|
+
EOF
|
|
197
|
+
|
|
198
|
+
if [ -n "$YAML_JSON_ERRORS" ]; then
|
|
199
|
+
echo " ❌ YAML/JSON syntax errors:"
|
|
200
|
+
for f in $YAML_JSON_ERRORS; do
|
|
201
|
+
echo " - $f"
|
|
202
|
+
done
|
|
203
|
+
HYGIENE_ISSUES=$((HYGIENE_ISSUES + 1))
|
|
204
|
+
HYGIENE_FILES="$HYGIENE_FILES yaml-json-syntax"
|
|
205
|
+
elif [ "$YAML_JSON_CHECK_COUNT" -gt 0 ]; then
|
|
206
|
+
echo " ✅ YAML/JSON syntax OK ($YAML_JSON_CHECK_COUNT file(s) checked)"
|
|
207
|
+
fi
|
|
208
|
+
|
|
209
|
+
# --- Result ---
|
|
210
|
+
if [ "$HYGIENE_ISSUES" -eq 0 ]; then
|
|
211
|
+
echo " ✅ PASSED - No file hygiene issues detected."
|
|
212
|
+
GATE_12_STATUS="PASS"
|
|
213
|
+
else
|
|
214
|
+
# Hard blocks: conflict markers + YAML/JSON syntax errors
|
|
215
|
+
if [ -n "$CONFLICT_MARKER_FILES" ] || [ -n "$YAML_JSON_ERRORS" ]; then
|
|
216
|
+
echo ""
|
|
217
|
+
echo "❌ BLOCKED - File hygiene violations must be fixed before commit."
|
|
218
|
+
GATE_12_STATUS="BLOCK"
|
|
219
|
+
record_gate_audit "gate-12" "file-hygiene" "$GATE_12_STATUS" "$HYGIENE_ISSUES" "$GATE_12_START"
|
|
220
|
+
exit 1
|
|
221
|
+
else
|
|
222
|
+
echo ""
|
|
223
|
+
echo " ⚠️ WARNINGS - $HYGIENE_ISSUES file hygiene issue(s) found."
|
|
224
|
+
echo " Fix recommended but not blocking (no conflict markers or syntax errors)."
|
|
225
|
+
echo " Issues:$HYGIENE_FILES"
|
|
226
|
+
GATE_12_STATUS="WARN"
|
|
227
|
+
fi
|
|
228
|
+
fi
|
|
229
|
+
|
|
230
|
+
record_gate_audit "gate-12" "file-hygiene" "$GATE_12_STATUS" "$HYGIENE_ISSUES" "$GATE_12_START"
|
package/hooks/gate-8.sh
CHANGED
|
@@ -13,7 +13,7 @@ GITLEAKS_CMD=""
|
|
|
13
13
|
if command -v gitleaks >/dev/null 2>&1; then
|
|
14
14
|
GITLEAKS_CMD="gitleaks"
|
|
15
15
|
elif [ -f "$HOME/.local/bin/gitleaks" ]; then
|
|
16
|
-
|
|
16
|
+
GITLEAKS_CMD="$HOME/.local/bin/gitleaks"
|
|
17
17
|
fi
|
|
18
18
|
|
|
19
19
|
if [ -n "$GITLEAKS_CMD" ]; then
|