fraim-framework 2.0.37 → 2.0.41

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 (38) hide show
  1. package/README.md +8 -0
  2. package/dist/src/ai-manager/ai-manager.js +162 -0
  3. package/dist/src/cli/commands/init-project.js +74 -0
  4. package/dist/src/cli/commands/setup.js +176 -0
  5. package/dist/src/cli/commands/test-mcp.js +135 -0
  6. package/dist/src/cli/fraim.js +6 -0
  7. package/dist/src/cli/setup/auto-mcp-setup.js +367 -0
  8. package/dist/src/cli/setup/ide-detector.js +165 -0
  9. package/dist/src/cli/setup/mcp-config-generator.js +144 -0
  10. package/dist/src/cli/setup/token-validator.js +49 -0
  11. package/dist/src/fraim-mcp-server.js +198 -0
  12. package/dist/tests/debug-tools.js +2 -2
  13. package/dist/tests/shared-server-utils.js +57 -0
  14. package/dist/tests/test-ai-manager.js +113 -0
  15. package/dist/tests/test-client-scripts-validation.js +27 -5
  16. package/dist/tests/test-complete-setup-flow.js +110 -0
  17. package/dist/tests/test-ide-detector.js +46 -0
  18. package/dist/tests/test-improved-setup.js +121 -0
  19. package/dist/tests/test-mcp-config-generator.js +99 -0
  20. package/dist/tests/test-mcp-connection.js +58 -117
  21. package/dist/tests/test-mcp-issue-integration.js +2 -2
  22. package/dist/tests/test-mcp-lifecycle-methods.js +34 -100
  23. package/dist/tests/test-mcp-shared-server.js +308 -0
  24. package/dist/tests/test-package-size.js +21 -8
  25. package/dist/tests/test-script-location-independence.js +39 -62
  26. package/dist/tests/test-server-utils.js +32 -0
  27. package/dist/tests/test-session-rehydration.js +2 -2
  28. package/dist/tests/test-setup-integration.js +98 -0
  29. package/dist/tests/test-standalone.js +2 -2
  30. package/dist/tests/test-stub-registry.js +23 -7
  31. package/dist/tests/test-telemetry.js +2 -2
  32. package/dist/tests/test-token-validator.js +30 -0
  33. package/dist/tests/test-user-journey.js +2 -1
  34. package/package.json +3 -2
  35. package/registry/scripts/code-quality-check.sh +566 -559
  36. package/registry/scripts/prep-issue.sh +7 -0
  37. package/registry/scripts/verify-pr-comments.sh +74 -70
  38. /package/registry/stubs/workflows/{convert-to-pdf.md → marketing/convert-to-pdf.md} +0 -0
@@ -1,559 +1,566 @@
1
- #!/bin/bash
2
- # Unified code quality check script
3
- # Can be run at different stages: pre-commit, pre-pr, or ci
4
- # Usage: bash code-quality-check.sh [pre-commit|pre-pr|ci]
5
-
6
- MODE="${1:-pre-pr}" # Default to pre-pr if not specified
7
-
8
- set -e
9
-
10
- echo "🔍 Running code quality checks (mode: $MODE)..."
11
- echo ""
12
-
13
- FAILED=0
14
- WARNINGS=0
15
-
16
- # Determine what files to check based on mode
17
- if [ "$MODE" = "pre-commit" ]; then
18
- # Pre-commit: only check staged files
19
- TS_FILES=$(git diff --cached --name-only | grep -E '\.(ts|tsx)$' || true)
20
- CHECK_SCOPE="staged files"
21
- elif [ "$MODE" = "ci" ]; then
22
- # CI: check only files changed in this PR
23
- # Compare against the base branch (e.g., master or feature/*)
24
- BASE_BRANCH="${GITHUB_BASE_REF:-origin/master}"
25
- echo "â„šī¸ Comparing against base: $BASE_BRANCH"
26
-
27
- # Get all changed TypeScript files in the PR
28
- TS_FILES=$(git diff --name-only $BASE_BRANCH...HEAD | grep -E '\.(ts|tsx)$' || true)
29
-
30
- if [ -z "$TS_FILES" ]; then
31
- echo "â„šī¸ No TypeScript files changed in this PR"
32
- CHECK_SCOPE="no files changed"
33
- else
34
- FILE_COUNT=$(echo "$TS_FILES" | wc -l | tr -d ' ')
35
- CHECK_SCOPE="$FILE_COUNT changed file(s) in PR"
36
- fi
37
- elif [ "$MODE" = "pre-pr" ]; then
38
- # Pre-PR: check entire src directory (local development)
39
- TS_FILES=$(find src -name "*.ts" -o -name "*.tsx" 2>/dev/null || true)
40
- CHECK_SCOPE="entire codebase"
41
- fi
42
- # Load configuration from config.json
43
- CONFIG_FILE=".fraim/config.json"
44
- if [ -f "$CONFIG_FILE" ]; then
45
- # Extract OpenAPI path using node
46
- OPENAPI_PATH=$(node -e "const fs = require('fs'); try { const config = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf-8')); console.log(config.paths?.openapi || 'src/openapi/OpenAPI.json'); } catch(e) { console.log('src/openapi/OpenAPI.json'); }")
47
- ARCH_DOC=$(node -e "const fs = require('fs'); try { const config = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf-8')); console.log('rules/' + (config.persona?.name?.toLowerCase() || 'persona') + '-architecture.md'); } catch(e) { console.log('rules/persona-architecture.md'); }")
48
- else
49
- OPENAPI_PATH="src/openapi/OpenAPI.json"
50
- ARCH_DOC="rules/persona-architecture.md"
51
- fi
52
-
53
- echo "Checking: $CHECK_SCOPE"
54
- echo ""
55
-
56
- # GATE 1: Check for 'as any' type bypassing
57
- echo "================================================"
58
- echo "📋 GATE 1: No 'as any' Type Bypassing"
59
- echo "================================================"
60
-
61
- if [ -z "$TS_FILES" ]; then
62
- echo "â„šī¸ No TypeScript files to check"
63
- else
64
- if echo "$TS_FILES" | xargs grep -n ' as any' 2>/dev/null; then
65
- echo ""
66
- echo "❌ FAILED: Found 'as any' type assertions"
67
- echo " Rule: Use proper TypeScript interfaces, not 'as any'"
68
- echo " See: rules/code-quality-and-debugging-patterns.md#rule-1"
69
- FAILED=1
70
- else
71
- echo "✅ PASSED: No 'as any' type bypassing found"
72
- fi
73
- fi
74
- echo ""
75
-
76
- # GATE 1.5: Tautological Tests Check
77
- echo "================================================"
78
- echo "📋 GATE 1.5: No Tautological Test Patterns"
79
- echo "================================================"
80
-
81
- if bash "$(dirname "$0")/detect-tautological-tests.sh"; then
82
- echo "✅ PASSED: No tautological tests found"
83
- else
84
- echo "❌ FAILED: Tautological test patterns detected"
85
- echo " Rule: Tests must fail if product code is removed. Don't test types/constants."
86
- echo " See: rules/integrity-and-test-ethics.md#critical-invalid-test-patterns-tautology-type-testing"
87
- FAILED=1
88
- fi
89
- echo ""
90
-
91
-
92
- # GATE 2: TypeScript Compilation
93
- echo "================================================"
94
- echo "📋 GATE 2: TypeScript Compilation"
95
- echo "================================================"
96
-
97
- if command -v npx &> /dev/null; then
98
- if npx tsc --noEmit --skipLibCheck 2>&1 | head -50; then
99
- echo "✅ PASSED: TypeScript compilation successful"
100
- else
101
- echo "❌ FAILED: TypeScript compilation errors"
102
- echo " Fix all TypeScript errors before proceeding"
103
- FAILED=1
104
- fi
105
- else
106
- echo "â„šī¸ npx not found - skipping TypeScript check"
107
- fi
108
- echo ""
109
-
110
- # GATE 3: Linter
111
- echo "================================================"
112
- echo "📋 GATE 3: Linter (ESLint)"
113
- echo "================================================"
114
-
115
- if command -v npm &> /dev/null; then
116
- if npm run lint 2>&1 | head -50; then
117
- echo "✅ PASSED: Linter checks passed"
118
- else
119
- echo "âš ī¸ WARNING: Linter has issues"
120
- WARNINGS=1
121
- fi
122
- else
123
- echo "â„šī¸ npm not found - skipping lint check"
124
- fi
125
- echo ""
126
-
127
- # GATE 4: Git Status (pre-pr and ci only)
128
- if [ "$MODE" != "pre-commit" ]; then
129
- echo "================================================"
130
- echo "📋 GATE 4: Git Status"
131
- echo "================================================"
132
-
133
- if [ -n "$(git status --porcelain)" ]; then
134
- echo "âš ī¸ WARNING: Uncommitted changes detected"
135
- git status --short
136
- WARNINGS=1
137
- else
138
- echo "✅ PASSED: Working directory is clean"
139
- fi
140
- echo ""
141
- fi
142
-
143
- # GATE 5: Commits Pushed (pre-pr only)
144
- if [ "$MODE" = "pre-pr" ]; then
145
- echo "================================================"
146
- echo "📋 GATE 5: Commits Pushed to Remote"
147
- echo "================================================"
148
-
149
- CURRENT_BRANCH=$(git branch --show-current)
150
- if git log origin/$CURRENT_BRANCH..$CURRENT_BRANCH --oneline 2>/dev/null | grep . > /dev/null; then
151
- echo "âš ī¸ WARNING: Unpushed commits detected"
152
- git log origin/$CURRENT_BRANCH..$CURRENT_BRANCH --oneline
153
- WARNINGS=1
154
- else
155
- echo "✅ PASSED: All commits pushed to remote"
156
- fi
157
- echo ""
158
- fi
159
-
160
- # GATE 6: PR Comments Addressed (pre-pr only)
161
- if [ "$MODE" = "pre-pr" ]; then
162
- echo "================================================"
163
- echo "📋 GATE 6: PR Comments Addressed"
164
- echo "================================================"
165
-
166
- # Check if gh CLI is available
167
- if ! command -v gh &> /dev/null; then
168
- echo "â„šī¸ gh CLI not found - skipping PR comment check"
169
- echo ""
170
- else
171
- # Try to get PR number for current branch
172
- PR_NUMBER=$(gh pr view --json number --jq '.number' 2>/dev/null || echo "")
173
-
174
- if [ -z "$PR_NUMBER" ]; then
175
- echo "â„šī¸ No PR found for current branch - skipping comment check"
176
- else
177
- echo "Found PR #$PR_NUMBER"
178
-
179
- # Get PR comments count
180
- COMMENT_COUNT=$(gh api repos/:owner/:repo/pulls/$PR_NUMBER/comments --jq 'length' 2>/dev/null || echo "0")
181
-
182
- if [ "$COMMENT_COUNT" -gt 0 ]; then
183
- echo "âš ī¸ WARNING: PR has $COMMENT_COUNT review comments"
184
- echo " Please ensure all comments are addressed before marking ready for review"
185
- echo ""
186
- echo " To view comments:"
187
- echo " gh pr view $PR_NUMBER --comments"
188
- echo ""
189
- echo " To reply to a comment:"
190
- echo " gh api repos/:owner/:repo/pulls/comments/{comment_id}/replies -X POST -f body='your response'"
191
- WARNINGS=1
192
- else
193
- echo "✅ PASSED: No outstanding PR comments"
194
- fi
195
- fi
196
- fi
197
- echo ""
198
- fi
199
-
200
- # GATE 7: Build Evidence Verification (pre-pr only)
201
- if [ "$MODE" = "pre-pr" ]; then
202
- echo "================================================"
203
- echo "📋 GATE 7: Build Evidence Verification"
204
- echo "================================================"
205
-
206
- # Check if build has been run (look for build output or recent dist/ directory)
207
- BUILD_EVIDENCE_FOUND=0
208
-
209
- # Method 1: Check if dist/ directory exists and has recent files
210
- if [ -d "dist" ]; then
211
- # Check if dist has files modified in last 2 hours
212
- if find dist -type f -newermt "2 hours ago" 2>/dev/null | grep -q .; then
213
- BUILD_EVIDENCE_FOUND=1
214
- echo "✅ Found: Recent files in dist/ directory"
215
- fi
216
- fi
217
-
218
- # Method 2: Check for build logs
219
- if [ -f "server.log" ] && tail -50 server.log 2>/dev/null | grep -qiE "(Build.*success|Compiled successfully|build.*complete)"; then
220
- BUILD_EVIDENCE_FOUND=1
221
- echo "✅ Found: Build success in server.log"
222
- fi
223
-
224
- # Method 3: Check if TypeScript compilation succeeds now
225
- if command -v npx &> /dev/null; then
226
- if npx tsc --noEmit --skipLibCheck > /dev/null 2>&1; then
227
- BUILD_EVIDENCE_FOUND=1
228
- echo "✅ Found: TypeScript compilation succeeds (runtime check)"
229
- fi
230
- fi
231
-
232
- if [ $BUILD_EVIDENCE_FOUND -eq 0 ]; then
233
- echo "❌ FAILED: No build evidence found"
234
- echo " Required: Run 'npm run build' and verify it succeeds before marking PR ready"
235
- echo " Evidence can be: dist/ files, build logs, or successful compilation"
236
- FAILED=1
237
- else
238
- echo "✅ PASSED: Build evidence verified"
239
- fi
240
- echo ""
241
- fi
242
-
243
- # GATE 8: Test Execution Verification (pre-pr only)
244
- if [ "$MODE" = "pre-pr" ]; then
245
- echo "================================================"
246
- echo "📋 GATE 8: Test Execution Verification"
247
- echo "================================================"
248
-
249
- # Check if test files were created in this PR/branch
250
- NEW_TEST_FILES=$(find . -name "test-*.ts" -newer .git/HEAD 2>/dev/null | grep -v node_modules | head -10 || true)
251
-
252
- if [ -n "$NEW_TEST_FILES" ]; then
253
- echo "âš ī¸ WARNING: New test files detected:"
254
- echo "$NEW_TEST_FILES" | sed 's/^/ - /'
255
- echo " Rule: All new test files must be executed before submitting evidence"
256
- echo " Check: Verify test execution results are included in PR evidence"
257
- WARNINGS=1
258
- else
259
- echo "✅ PASSED: No new test files detected (or tests were executed)"
260
- fi
261
-
262
- # Check evidence files for "pending" automated validations
263
- EVIDENCE_FILES=$(find . -name "implementation-evidence-*.md" -o -name "*evidence*.md" 2>/dev/null | head -5 || true)
264
- if [ -n "$EVIDENCE_FILES" ]; then
265
- PENDING_AUTO=$(grep -l "pending.*automated\|pending.*test\|manual validation.*pending" $EVIDENCE_FILES 2>/dev/null | head -3 || true)
266
- if [ -n "$PENDING_AUTO" ]; then
267
- echo "âš ī¸ WARNING: Evidence files mention 'pending' automated validations:"
268
- echo "$PENDING_AUTO" | sed 's/^/ - /'
269
- echo " Rule: Cannot mark automated validations as 'pending' without attempting them first"
270
- echo " Check: Verify all test files have been executed before submitting evidence"
271
- WARNINGS=1
272
- fi
273
- fi
274
- echo ""
275
- fi
276
-
277
- # GATE 9: Test Evidence Verification (pre-pr only)
278
- if [ "$MODE" = "pre-pr" ]; then
279
- echo "================================================"
280
- echo "📋 GATE 9: Test Evidence Verification"
281
- echo "================================================"
282
-
283
- TEST_EVIDENCE_FOUND=0
284
-
285
- # Method 1: Check PR comments for test evidence
286
- if command -v gh &> /dev/null; then
287
- PR_NUMBER=$(gh pr view --json number --jq '.number' 2>/dev/null || echo "")
288
- if [ -n "$PR_NUMBER" ]; then
289
- # Search PR comments for test evidence keywords
290
- if gh api repos/:owner/:repo/pulls/$PR_NUMBER/comments --jq '.[].body' 2>/dev/null | grep -qiE "(test.*pass|test.*evidence|test.*result|exit code.*0|✅.*test)" 2>/dev/null; then
291
- TEST_EVIDENCE_FOUND=1
292
- echo "✅ Found: Test evidence in PR comments"
293
- fi
294
- fi
295
- fi
296
-
297
- # Method 2: Check for test log files with pass indicators
298
- if [ -f "test.log" ] && grep -qE "(pass|PASS|✅|exit code.*0)" test.log 2>/dev/null; then
299
- TEST_EVIDENCE_FOUND=1
300
- echo "✅ Found: Test evidence in test.log"
301
- fi
302
-
303
- # Method 3: Check for npm test exit codes (if test.log exists and shows recent runs)
304
- # Note: We don't run tests here (too slow), just check for evidence of recent test runs
305
- if [ -f "test.log" ] && [ -n "$(find test.log -mmin -60 2>/dev/null)" ]; then
306
- # If test.log was modified in last hour, assume tests were run
307
- if grep -qE "(pass|PASS|✅)" test.log 2>/dev/null; then
308
- TEST_EVIDENCE_FOUND=1
309
- echo "✅ Found: Recent test run in test.log"
310
- fi
311
- fi
312
-
313
- if [ $TEST_EVIDENCE_FOUND -eq 0 ]; then
314
- echo "❌ FAILED: No test evidence found"
315
- echo " Required: Provide test evidence before marking PR ready"
316
- echo " Evidence can be: PR comment with test results, test.log file, or passing test run"
317
- echo " Evidence must include exit codes and test names"
318
- FAILED=1
319
- else
320
- echo "✅ PASSED: Test evidence verified"
321
- fi
322
- echo ""
323
- fi
324
-
325
- # GATE 11: Issue Label Verification (pre-pr only)
326
- if [ "$MODE" = "pre-pr" ]; then
327
- echo "================================================"
328
- echo "📋 GATE 10: Issue Label Verification"
329
- echo "================================================"
330
-
331
- if ! command -v gh &> /dev/null; then
332
- echo "â„šī¸ gh CLI not found - skipping issue label check"
333
- echo ""
334
- else
335
- # Extract issue number from branch name
336
- CURRENT_BRANCH=$(git branch --show-current)
337
- ISSUE_NUMBER=$(echo "$CURRENT_BRANCH" | sed -n 's/feature\/\([0-9]\+\).*/\1/p')
338
-
339
- if [ -z "$ISSUE_NUMBER" ]; then
340
- echo "âš ī¸ WARNING: Could not extract issue number from branch: $CURRENT_BRANCH"
341
- echo " Expected format: feature/533-slug"
342
- echo " Skipping label verification"
343
- else
344
- echo "Checking labels for issue #$ISSUE_NUMBER"
345
-
346
- # Get issue labels
347
- LABELS=$(gh api repos/:owner/:repo/issues/$ISSUE_NUMBER --jq '.labels[].name' 2>/dev/null || echo "")
348
-
349
- if [ -z "$LABELS" ]; then
350
- echo "❌ FAILED: Could not fetch issue labels"
351
- echo " Check that issue #$ISSUE_NUMBER exists and is accessible"
352
- FAILED=1
353
- else
354
- # Check for phase label
355
- HAS_PHASE_LABEL=false
356
- PHASE_LABEL=""
357
- while IFS= read -r label; do
358
- case "$label" in
359
- phase:spec|phase:design|phase:impl|phase:tests)
360
- HAS_PHASE_LABEL=true
361
- PHASE_LABEL="$label"
362
- break
363
- ;;
364
- esac
365
- done <<< "$LABELS"
366
-
367
- # Check status labels
368
- HAS_STATUS_WIP=false
369
- HAS_STATUS_NEEDS_REVIEW=false
370
- while IFS= read -r label; do
371
- case "$label" in
372
- status:wip)
373
- HAS_STATUS_WIP=true
374
- ;;
375
- status:needs-review)
376
- HAS_STATUS_NEEDS_REVIEW=true
377
- ;;
378
- esac
379
- done <<< "$LABELS"
380
-
381
- # Validation
382
- if [ "$HAS_PHASE_LABEL" = false ]; then
383
- echo "❌ FAILED: Issue missing phase label"
384
- echo " Required: phase:spec, phase:design, phase:impl, or phase:tests"
385
- echo " Current labels: $LABELS"
386
- FAILED=1
387
- elif [ "$HAS_STATUS_NEEDS_REVIEW" = true ] && [ "$HAS_STATUS_WIP" = true ]; then
388
- echo "❌ FAILED: Issue has both status:wip and status:needs-review"
389
- echo " Remove status:wip before marking ready for review"
390
- echo " Current labels: $LABELS"
391
- FAILED=1
392
- else
393
- echo "✅ PASSED: Issue labels correct"
394
- echo " Phase: $PHASE_LABEL"
395
- if [ "$HAS_STATUS_NEEDS_REVIEW" = true ]; then
396
- echo " Status: status:needs-review"
397
- elif [ "$HAS_STATUS_WIP" = true ]; then
398
- echo " Status: status:wip"
399
- else
400
- echo " Status: (none)"
401
- fi
402
- fi
403
- fi
404
- fi
405
- fi
406
- echo ""
407
- fi
408
-
409
- # GATE 10: Validation Plan Coverage (pre-pr only)
410
- if [ "$MODE" = "pre-pr" ]; then
411
- echo "================================================"
412
- echo "📋 GATE 9: Test Coverage Alignment"
413
- echo "================================================"
414
-
415
- # Extract issue number from branch
416
- CURRENT_BRANCH=$(git branch --show-current)
417
- ISSUE_NUMBER=$(echo "$CURRENT_BRANCH" | sed -n 's/feature\/\([0-9]\+\).*/\1/p')
418
-
419
- if [ -z "$ISSUE_NUMBER" ]; then
420
- echo "â„šī¸ Could not extract issue number from branch - skipping validation plan check"
421
- elif command -v npx &> /dev/null; then
422
- # Run validation coverage checker
423
- COVERAGE_OUTPUT=$(GIT_BRANCH="$CURRENT_BRANCH" npx tsx "$(dirname "$0")/validate-test-coverage.ts" "$ISSUE_NUMBER" 2>&1)
424
- COVERAGE_EXIT=$?
425
-
426
- echo "$COVERAGE_OUTPUT"
427
-
428
- if [ $COVERAGE_EXIT -eq 1 ]; then
429
- # Coverage incomplete
430
- FAILED=1
431
- elif echo "$COVERAGE_OUTPUT" | grep -qi "skipped\|no.*validation plan\|no spec"; then
432
- # No spec/design or no validation plan - Gate 9 warns but doesn't block
433
- echo "âš ī¸ WARNING: Validation plan check skipped (no spec/design or no validation plan)"
434
- echo " This is acceptable, but consider adding validation plan to spec/design for future issues"
435
- WARNINGS=1
436
- fi
437
- else
438
- echo "â„šī¸ npx not available - skipping validation plan coverage check"
439
- echo " Install: npm (Node.js package manager)"
440
- fi
441
- echo ""
442
- fi
443
-
444
- # GATE 12: ChatGPT Length Limits (pre-pr only)
445
- if [ "$MODE" = "pre-pr" ]; then
446
- echo "================================================"
447
- echo "📋 GATE 11: ChatGPT Length Limits"
448
- echo "================================================"
449
-
450
- CHATGPT_LIMITS_FAILED=0
451
-
452
- # Check instructions.txt length (8,000 char limit)
453
- if [ -f "src/openapi/instructions.txt" ]; then
454
- if command -v python &> /dev/null || command -v python3 &> /dev/null; then
455
- PYTHON_CMD=$(command -v python3 2>/dev/null || command -v python 2>/dev/null)
456
- INST_LENGTH=$($PYTHON_CMD -c "f = open('src/openapi/instructions.txt', 'r', encoding='utf-8'); content = f.read(); f.close(); print(len(content))" 2>/dev/null || echo "0")
457
-
458
- if [ "$INST_LENGTH" -gt 8000 ]; then
459
- echo "❌ FAILED: instructions.txt exceeds 8,000 character limit"
460
- echo " Current length: $INST_LENGTH characters"
461
- echo " Limit: 8,000 characters"
462
- echo " Solution: Move detailed content to KB files (KB-*.txt) and reference them"
463
- echo " See: $ARCH_DOC#chatgpt-length-limitations"
464
- CHATGPT_LIMITS_FAILED=1
465
- else
466
- echo "✅ PASSED: instructions.txt length ($INST_LENGTH chars) within limit (8,000)"
467
- fi
468
- else
469
- echo "â„šī¸ python not found - skipping instructions.txt length check"
470
- fi
471
- fi
472
-
473
- # Check OpenAPI operation description lengths (300 char limit)
474
- if [ -f "$OPENAPI_PATH" ]; then
475
- if command -v python &> /dev/null || command -v python3 &> /dev/null; then
476
- PYTHON_CMD=$(command -v python3 2>/dev/null || command -v python 2>/dev/null)
477
- LONG_DESCS=$($PYTHON_CMD -c "
478
- import json
479
- try:
480
- f = open('$OPENAPI_PATH', 'r', encoding='utf-8')
481
- spec = json.load(f)
482
- f.close()
483
- violations = []
484
- for path, methods in spec.get('paths', {}).items():
485
- for method, details in methods.items():
486
- if isinstance(details, dict) and 'description' in details:
487
- desc = details['description']
488
- if len(desc) > 300:
489
- violations.append(f'{path} {method.upper()}: {len(desc)} chars')
490
- if violations:
491
- print('\n'.join(violations))
492
- else:
493
- print('OK')
494
- except Exception as e:
495
- print(f'ERROR: {e}')
496
- " 2>/dev/null || echo "ERROR")
497
-
498
- if [ "$LONG_DESCS" != "OK" ] && [ -n "$LONG_DESCS" ] && [ "$LONG_DESCS" != "ERROR" ]; then
499
- echo "❌ FAILED: OpenAPI operation descriptions exceed 300 character limit"
500
- echo "$LONG_DESCS" | while IFS= read -r line; do
501
- echo " $line"
502
- done
503
- echo " Solution: Shorten descriptions, move details to KB files, reference KB files"
504
- echo " See: $ARCH_DOC#chatgpt-length-limitations"
505
- CHATGPT_LIMITS_FAILED=1
506
- elif [ "$LONG_DESCS" = "ERROR" ]; then
507
- echo "âš ī¸ WARNING: Could not validate OpenAPI description lengths (JSON parsing error)"
508
- WARNINGS=1
509
- else
510
- echo "✅ PASSED: All OpenAPI operation descriptions within limit (300 chars)"
511
- fi
512
- else
513
- echo "â„šī¸ python not found - skipping OpenAPI description length check"
514
- fi
515
- fi
516
-
517
- if [ $CHATGPT_LIMITS_FAILED -eq 1 ]; then
518
- FAILED=1
519
- fi
520
- echo ""
521
- fi
522
-
523
- # Summary
524
- echo "================================================"
525
- echo "📊 QUALITY CHECK SUMMARY ($MODE mode)"
526
- echo "================================================"
527
- echo ""
528
-
529
- if [ $FAILED -eq 1 ]; then
530
- echo "❌ CHECKS FAILED"
531
- echo ""
532
- echo "Critical issues found. Cannot proceed."
533
- echo "See rules/code-quality-and-debugging-patterns.md"
534
- exit 1
535
- elif [ $WARNINGS -gt 0 ]; then
536
- echo "âš ī¸ CHECKS PASSED WITH WARNINGS"
537
- echo ""
538
- echo "Some warnings found. Consider addressing before proceeding."
539
-
540
- if [ "$MODE" = "pre-commit" ]; then
541
- echo "You can still commit, but consider fixing warnings."
542
- exit 0
543
- else
544
- exit 0
545
- fi
546
- else
547
- echo "✅ ALL CHECKS PASSED"
548
- echo ""
549
-
550
- if [ "$MODE" = "pre-commit" ]; then
551
- echo "Ready to commit!"
552
- elif [ "$MODE" = "pre-pr" ]; then
553
- echo "Ready to create PR!"
554
- echo "Run: gh pr create --title \"your title\" --body \"your description\""
555
- else
556
- echo "CI checks complete!"
557
- fi
558
- exit 0
559
- fi
1
+ #!/bin/bash
2
+ # Unified code quality check script
3
+ # Can be run at different stages: pre-commit, pre-pr, or ci
4
+ # Usage: bash code-quality-check.sh [pre-commit|pre-pr|ci]
5
+
6
+ MODE="${1:-pre-pr}" # Default to pre-pr if not specified
7
+
8
+ set -e
9
+
10
+ echo "🔍 Running code quality checks (mode: $MODE)..."
11
+ echo ""
12
+
13
+ FAILED=0
14
+ WARNINGS=0
15
+
16
+ # Determine what files to check based on mode
17
+ if [ "$MODE" = "pre-commit" ]; then
18
+ # Pre-commit: only check staged files
19
+ TS_FILES=$(git diff --cached --name-only | grep -E '\.(ts|tsx)$' || true)
20
+ CHECK_SCOPE="staged files"
21
+ elif [ "$MODE" = "ci" ]; then
22
+ # CI: check only files changed in this PR
23
+ # Compare against the base branch (e.g., master or feature/*)
24
+ BASE_BRANCH="${GITHUB_BASE_REF:-origin/master}"
25
+ echo "â„šī¸ Comparing against base: $BASE_BRANCH"
26
+
27
+ # Get all changed TypeScript files in the PR
28
+ TS_FILES=$(git diff --name-only $BASE_BRANCH...HEAD | grep -E '\.(ts|tsx)$' || true)
29
+
30
+ if [ -z "$TS_FILES" ]; then
31
+ echo "â„šī¸ No TypeScript files changed in this PR"
32
+ CHECK_SCOPE="no files changed"
33
+ else
34
+ FILE_COUNT=$(echo "$TS_FILES" | wc -l | tr -d ' ')
35
+ CHECK_SCOPE="$FILE_COUNT changed file(s) in PR"
36
+ fi
37
+ elif [ "$MODE" = "pre-pr" ]; then
38
+ # Pre-PR: check entire src directory (local development)
39
+ TS_FILES=$(find src -name "*.ts" -o -name "*.tsx" 2>/dev/null || true)
40
+ CHECK_SCOPE="entire codebase"
41
+ fi
42
+ # Load configuration from config.json
43
+ CONFIG_FILE=".fraim/config.json"
44
+ if [ ! -f "$CONFIG_FILE" ]; then
45
+ echo "Error: Config file not found at $CONFIG_FILE" >&2
46
+ echo "Make sure you're running this script from the project root directory" >&2
47
+ echo "Current directory: $(pwd)" >&2
48
+ exit 1
49
+ fi
50
+
51
+ echo "Using config file: $CONFIG_FILE"
52
+ # Extract OpenAPI path using node
53
+ OPENAPI_PATH=$(node -e "const fs = require('fs'); try { const config = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf-8')); console.log(config.paths?.openapi || 'src/openapi/OpenAPI.json'); } catch(e) { console.log('src/openapi/OpenAPI.json'); }")
54
+ ARCH_DOC=$(node -e "const fs = require('fs'); try { const config = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf-8')); console.log('rules/' + (config.persona?.name?.toLowerCase() || 'persona') + '-architecture.md'); } catch(e) { console.log('rules/persona-architecture.md'); }")
55
+ else
56
+ OPENAPI_PATH="src/openapi/OpenAPI.json"
57
+ ARCH_DOC="rules/persona-architecture.md"
58
+ fi
59
+
60
+ echo "Checking: $CHECK_SCOPE"
61
+ echo ""
62
+
63
+ # GATE 1: Check for 'as any' type bypassing
64
+ echo "================================================"
65
+ echo "📋 GATE 1: No 'as any' Type Bypassing"
66
+ echo "================================================"
67
+
68
+ if [ -z "$TS_FILES" ]; then
69
+ echo "â„šī¸ No TypeScript files to check"
70
+ else
71
+ if echo "$TS_FILES" | xargs grep -n ' as any' 2>/dev/null; then
72
+ echo ""
73
+ echo "❌ FAILED: Found 'as any' type assertions"
74
+ echo " Rule: Use proper TypeScript interfaces, not 'as any'"
75
+ echo " See: rules/code-quality-and-debugging-patterns.md#rule-1"
76
+ FAILED=1
77
+ else
78
+ echo "✅ PASSED: No 'as any' type bypassing found"
79
+ fi
80
+ fi
81
+ echo ""
82
+
83
+ # GATE 1.5: Tautological Tests Check
84
+ echo "================================================"
85
+ echo "📋 GATE 1.5: No Tautological Test Patterns"
86
+ echo "================================================"
87
+
88
+ if bash "$(dirname "$0")/detect-tautological-tests.sh"; then
89
+ echo "✅ PASSED: No tautological tests found"
90
+ else
91
+ echo "❌ FAILED: Tautological test patterns detected"
92
+ echo " Rule: Tests must fail if product code is removed. Don't test types/constants."
93
+ echo " See: rules/integrity-and-test-ethics.md#critical-invalid-test-patterns-tautology-type-testing"
94
+ FAILED=1
95
+ fi
96
+ echo ""
97
+
98
+
99
+ # GATE 2: TypeScript Compilation
100
+ echo "================================================"
101
+ echo "📋 GATE 2: TypeScript Compilation"
102
+ echo "================================================"
103
+
104
+ if command -v npx &> /dev/null; then
105
+ if npx tsc --noEmit --skipLibCheck 2>&1 | head -50; then
106
+ echo "✅ PASSED: TypeScript compilation successful"
107
+ else
108
+ echo "❌ FAILED: TypeScript compilation errors"
109
+ echo " Fix all TypeScript errors before proceeding"
110
+ FAILED=1
111
+ fi
112
+ else
113
+ echo "â„šī¸ npx not found - skipping TypeScript check"
114
+ fi
115
+ echo ""
116
+
117
+ # GATE 3: Linter
118
+ echo "================================================"
119
+ echo "📋 GATE 3: Linter (ESLint)"
120
+ echo "================================================"
121
+
122
+ if command -v npm &> /dev/null; then
123
+ if npm run lint 2>&1 | head -50; then
124
+ echo "✅ PASSED: Linter checks passed"
125
+ else
126
+ echo "âš ī¸ WARNING: Linter has issues"
127
+ WARNINGS=1
128
+ fi
129
+ else
130
+ echo "â„šī¸ npm not found - skipping lint check"
131
+ fi
132
+ echo ""
133
+
134
+ # GATE 4: Git Status (pre-pr and ci only)
135
+ if [ "$MODE" != "pre-commit" ]; then
136
+ echo "================================================"
137
+ echo "📋 GATE 4: Git Status"
138
+ echo "================================================"
139
+
140
+ if [ -n "$(git status --porcelain)" ]; then
141
+ echo "âš ī¸ WARNING: Uncommitted changes detected"
142
+ git status --short
143
+ WARNINGS=1
144
+ else
145
+ echo "✅ PASSED: Working directory is clean"
146
+ fi
147
+ echo ""
148
+ fi
149
+
150
+ # GATE 5: Commits Pushed (pre-pr only)
151
+ if [ "$MODE" = "pre-pr" ]; then
152
+ echo "================================================"
153
+ echo "📋 GATE 5: Commits Pushed to Remote"
154
+ echo "================================================"
155
+
156
+ CURRENT_BRANCH=$(git branch --show-current)
157
+ if git log origin/$CURRENT_BRANCH..$CURRENT_BRANCH --oneline 2>/dev/null | grep . > /dev/null; then
158
+ echo "âš ī¸ WARNING: Unpushed commits detected"
159
+ git log origin/$CURRENT_BRANCH..$CURRENT_BRANCH --oneline
160
+ WARNINGS=1
161
+ else
162
+ echo "✅ PASSED: All commits pushed to remote"
163
+ fi
164
+ echo ""
165
+ fi
166
+
167
+ # GATE 6: PR Comments Addressed (pre-pr only)
168
+ if [ "$MODE" = "pre-pr" ]; then
169
+ echo "================================================"
170
+ echo "📋 GATE 6: PR Comments Addressed"
171
+ echo "================================================"
172
+
173
+ # Check if gh CLI is available
174
+ if ! command -v gh &> /dev/null; then
175
+ echo "â„šī¸ gh CLI not found - skipping PR comment check"
176
+ echo ""
177
+ else
178
+ # Try to get PR number for current branch
179
+ PR_NUMBER=$(gh pr view --json number --jq '.number' 2>/dev/null || echo "")
180
+
181
+ if [ -z "$PR_NUMBER" ]; then
182
+ echo "â„šī¸ No PR found for current branch - skipping comment check"
183
+ else
184
+ echo "Found PR #$PR_NUMBER"
185
+
186
+ # Get PR comments count
187
+ COMMENT_COUNT=$(gh api repos/:owner/:repo/pulls/$PR_NUMBER/comments --jq 'length' 2>/dev/null || echo "0")
188
+
189
+ if [ "$COMMENT_COUNT" -gt 0 ]; then
190
+ echo "âš ī¸ WARNING: PR has $COMMENT_COUNT review comments"
191
+ echo " Please ensure all comments are addressed before marking ready for review"
192
+ echo ""
193
+ echo " To view comments:"
194
+ echo " gh pr view $PR_NUMBER --comments"
195
+ echo ""
196
+ echo " To reply to a comment:"
197
+ echo " gh api repos/:owner/:repo/pulls/comments/{comment_id}/replies -X POST -f body='your response'"
198
+ WARNINGS=1
199
+ else
200
+ echo "✅ PASSED: No outstanding PR comments"
201
+ fi
202
+ fi
203
+ fi
204
+ echo ""
205
+ fi
206
+
207
+ # GATE 7: Build Evidence Verification (pre-pr only)
208
+ if [ "$MODE" = "pre-pr" ]; then
209
+ echo "================================================"
210
+ echo "📋 GATE 7: Build Evidence Verification"
211
+ echo "================================================"
212
+
213
+ # Check if build has been run (look for build output or recent dist/ directory)
214
+ BUILD_EVIDENCE_FOUND=0
215
+
216
+ # Method 1: Check if dist/ directory exists and has recent files
217
+ if [ -d "dist" ]; then
218
+ # Check if dist has files modified in last 2 hours
219
+ if find dist -type f -newermt "2 hours ago" 2>/dev/null | grep -q .; then
220
+ BUILD_EVIDENCE_FOUND=1
221
+ echo "✅ Found: Recent files in dist/ directory"
222
+ fi
223
+ fi
224
+
225
+ # Method 2: Check for build logs
226
+ if [ -f "server.log" ] && tail -50 server.log 2>/dev/null | grep -qiE "(Build.*success|Compiled successfully|build.*complete)"; then
227
+ BUILD_EVIDENCE_FOUND=1
228
+ echo "✅ Found: Build success in server.log"
229
+ fi
230
+
231
+ # Method 3: Check if TypeScript compilation succeeds now
232
+ if command -v npx &> /dev/null; then
233
+ if npx tsc --noEmit --skipLibCheck > /dev/null 2>&1; then
234
+ BUILD_EVIDENCE_FOUND=1
235
+ echo "✅ Found: TypeScript compilation succeeds (runtime check)"
236
+ fi
237
+ fi
238
+
239
+ if [ $BUILD_EVIDENCE_FOUND -eq 0 ]; then
240
+ echo "❌ FAILED: No build evidence found"
241
+ echo " Required: Run 'npm run build' and verify it succeeds before marking PR ready"
242
+ echo " Evidence can be: dist/ files, build logs, or successful compilation"
243
+ FAILED=1
244
+ else
245
+ echo "✅ PASSED: Build evidence verified"
246
+ fi
247
+ echo ""
248
+ fi
249
+
250
+ # GATE 8: Test Execution Verification (pre-pr only)
251
+ if [ "$MODE" = "pre-pr" ]; then
252
+ echo "================================================"
253
+ echo "📋 GATE 8: Test Execution Verification"
254
+ echo "================================================"
255
+
256
+ # Check if test files were created in this PR/branch
257
+ NEW_TEST_FILES=$(find . -name "test-*.ts" -newer .git/HEAD 2>/dev/null | grep -v node_modules | head -10 || true)
258
+
259
+ if [ -n "$NEW_TEST_FILES" ]; then
260
+ echo "âš ī¸ WARNING: New test files detected:"
261
+ echo "$NEW_TEST_FILES" | sed 's/^/ - /'
262
+ echo " Rule: All new test files must be executed before submitting evidence"
263
+ echo " Check: Verify test execution results are included in PR evidence"
264
+ WARNINGS=1
265
+ else
266
+ echo "✅ PASSED: No new test files detected (or tests were executed)"
267
+ fi
268
+
269
+ # Check evidence files for "pending" automated validations
270
+ EVIDENCE_FILES=$(find . -name "implementation-evidence-*.md" -o -name "*evidence*.md" 2>/dev/null | head -5 || true)
271
+ if [ -n "$EVIDENCE_FILES" ]; then
272
+ PENDING_AUTO=$(grep -l "pending.*automated\|pending.*test\|manual validation.*pending" $EVIDENCE_FILES 2>/dev/null | head -3 || true)
273
+ if [ -n "$PENDING_AUTO" ]; then
274
+ echo "âš ī¸ WARNING: Evidence files mention 'pending' automated validations:"
275
+ echo "$PENDING_AUTO" | sed 's/^/ - /'
276
+ echo " Rule: Cannot mark automated validations as 'pending' without attempting them first"
277
+ echo " Check: Verify all test files have been executed before submitting evidence"
278
+ WARNINGS=1
279
+ fi
280
+ fi
281
+ echo ""
282
+ fi
283
+
284
+ # GATE 9: Test Evidence Verification (pre-pr only)
285
+ if [ "$MODE" = "pre-pr" ]; then
286
+ echo "================================================"
287
+ echo "📋 GATE 9: Test Evidence Verification"
288
+ echo "================================================"
289
+
290
+ TEST_EVIDENCE_FOUND=0
291
+
292
+ # Method 1: Check PR comments for test evidence
293
+ if command -v gh &> /dev/null; then
294
+ PR_NUMBER=$(gh pr view --json number --jq '.number' 2>/dev/null || echo "")
295
+ if [ -n "$PR_NUMBER" ]; then
296
+ # Search PR comments for test evidence keywords
297
+ if gh api repos/:owner/:repo/pulls/$PR_NUMBER/comments --jq '.[].body' 2>/dev/null | grep -qiE "(test.*pass|test.*evidence|test.*result|exit code.*0|✅.*test)" 2>/dev/null; then
298
+ TEST_EVIDENCE_FOUND=1
299
+ echo "✅ Found: Test evidence in PR comments"
300
+ fi
301
+ fi
302
+ fi
303
+
304
+ # Method 2: Check for test log files with pass indicators
305
+ if [ -f "test.log" ] && grep -qE "(pass|PASS|✅|exit code.*0)" test.log 2>/dev/null; then
306
+ TEST_EVIDENCE_FOUND=1
307
+ echo "✅ Found: Test evidence in test.log"
308
+ fi
309
+
310
+ # Method 3: Check for npm test exit codes (if test.log exists and shows recent runs)
311
+ # Note: We don't run tests here (too slow), just check for evidence of recent test runs
312
+ if [ -f "test.log" ] && [ -n "$(find test.log -mmin -60 2>/dev/null)" ]; then
313
+ # If test.log was modified in last hour, assume tests were run
314
+ if grep -qE "(pass|PASS|✅)" test.log 2>/dev/null; then
315
+ TEST_EVIDENCE_FOUND=1
316
+ echo "✅ Found: Recent test run in test.log"
317
+ fi
318
+ fi
319
+
320
+ if [ $TEST_EVIDENCE_FOUND -eq 0 ]; then
321
+ echo "❌ FAILED: No test evidence found"
322
+ echo " Required: Provide test evidence before marking PR ready"
323
+ echo " Evidence can be: PR comment with test results, test.log file, or passing test run"
324
+ echo " Evidence must include exit codes and test names"
325
+ FAILED=1
326
+ else
327
+ echo "✅ PASSED: Test evidence verified"
328
+ fi
329
+ echo ""
330
+ fi
331
+
332
+ # GATE 11: Issue Label Verification (pre-pr only)
333
+ if [ "$MODE" = "pre-pr" ]; then
334
+ echo "================================================"
335
+ echo "📋 GATE 10: Issue Label Verification"
336
+ echo "================================================"
337
+
338
+ if ! command -v gh &> /dev/null; then
339
+ echo "â„šī¸ gh CLI not found - skipping issue label check"
340
+ echo ""
341
+ else
342
+ # Extract issue number from branch name
343
+ CURRENT_BRANCH=$(git branch --show-current)
344
+ ISSUE_NUMBER=$(echo "$CURRENT_BRANCH" | sed -n 's/feature\/\([0-9]\+\).*/\1/p')
345
+
346
+ if [ -z "$ISSUE_NUMBER" ]; then
347
+ echo "âš ī¸ WARNING: Could not extract issue number from branch: $CURRENT_BRANCH"
348
+ echo " Expected format: feature/533-slug"
349
+ echo " Skipping label verification"
350
+ else
351
+ echo "Checking labels for issue #$ISSUE_NUMBER"
352
+
353
+ # Get issue labels
354
+ LABELS=$(gh api repos/:owner/:repo/issues/$ISSUE_NUMBER --jq '.labels[].name' 2>/dev/null || echo "")
355
+
356
+ if [ -z "$LABELS" ]; then
357
+ echo "❌ FAILED: Could not fetch issue labels"
358
+ echo " Check that issue #$ISSUE_NUMBER exists and is accessible"
359
+ FAILED=1
360
+ else
361
+ # Check for phase label
362
+ HAS_PHASE_LABEL=false
363
+ PHASE_LABEL=""
364
+ while IFS= read -r label; do
365
+ case "$label" in
366
+ phase:spec|phase:design|phase:impl|phase:tests)
367
+ HAS_PHASE_LABEL=true
368
+ PHASE_LABEL="$label"
369
+ break
370
+ ;;
371
+ esac
372
+ done <<< "$LABELS"
373
+
374
+ # Check status labels
375
+ HAS_STATUS_WIP=false
376
+ HAS_STATUS_NEEDS_REVIEW=false
377
+ while IFS= read -r label; do
378
+ case "$label" in
379
+ status:wip)
380
+ HAS_STATUS_WIP=true
381
+ ;;
382
+ status:needs-review)
383
+ HAS_STATUS_NEEDS_REVIEW=true
384
+ ;;
385
+ esac
386
+ done <<< "$LABELS"
387
+
388
+ # Validation
389
+ if [ "$HAS_PHASE_LABEL" = false ]; then
390
+ echo "❌ FAILED: Issue missing phase label"
391
+ echo " Required: phase:spec, phase:design, phase:impl, or phase:tests"
392
+ echo " Current labels: $LABELS"
393
+ FAILED=1
394
+ elif [ "$HAS_STATUS_NEEDS_REVIEW" = true ] && [ "$HAS_STATUS_WIP" = true ]; then
395
+ echo "❌ FAILED: Issue has both status:wip and status:needs-review"
396
+ echo " Remove status:wip before marking ready for review"
397
+ echo " Current labels: $LABELS"
398
+ FAILED=1
399
+ else
400
+ echo "✅ PASSED: Issue labels correct"
401
+ echo " Phase: $PHASE_LABEL"
402
+ if [ "$HAS_STATUS_NEEDS_REVIEW" = true ]; then
403
+ echo " Status: status:needs-review"
404
+ elif [ "$HAS_STATUS_WIP" = true ]; then
405
+ echo " Status: status:wip"
406
+ else
407
+ echo " Status: (none)"
408
+ fi
409
+ fi
410
+ fi
411
+ fi
412
+ fi
413
+ echo ""
414
+ fi
415
+
416
+ # GATE 10: Validation Plan Coverage (pre-pr only)
417
+ if [ "$MODE" = "pre-pr" ]; then
418
+ echo "================================================"
419
+ echo "📋 GATE 9: Test Coverage Alignment"
420
+ echo "================================================"
421
+
422
+ # Extract issue number from branch
423
+ CURRENT_BRANCH=$(git branch --show-current)
424
+ ISSUE_NUMBER=$(echo "$CURRENT_BRANCH" | sed -n 's/feature\/\([0-9]\+\).*/\1/p')
425
+
426
+ if [ -z "$ISSUE_NUMBER" ]; then
427
+ echo "â„šī¸ Could not extract issue number from branch - skipping validation plan check"
428
+ elif command -v npx &> /dev/null; then
429
+ # Run validation coverage checker
430
+ COVERAGE_OUTPUT=$(GIT_BRANCH="$CURRENT_BRANCH" npx tsx "$(dirname "$0")/validate-test-coverage.ts" "$ISSUE_NUMBER" 2>&1)
431
+ COVERAGE_EXIT=$?
432
+
433
+ echo "$COVERAGE_OUTPUT"
434
+
435
+ if [ $COVERAGE_EXIT -eq 1 ]; then
436
+ # Coverage incomplete
437
+ FAILED=1
438
+ elif echo "$COVERAGE_OUTPUT" | grep -qi "skipped\|no.*validation plan\|no spec"; then
439
+ # No spec/design or no validation plan - Gate 9 warns but doesn't block
440
+ echo "âš ī¸ WARNING: Validation plan check skipped (no spec/design or no validation plan)"
441
+ echo " This is acceptable, but consider adding validation plan to spec/design for future issues"
442
+ WARNINGS=1
443
+ fi
444
+ else
445
+ echo "â„šī¸ npx not available - skipping validation plan coverage check"
446
+ echo " Install: npm (Node.js package manager)"
447
+ fi
448
+ echo ""
449
+ fi
450
+
451
+ # GATE 12: ChatGPT Length Limits (pre-pr only)
452
+ if [ "$MODE" = "pre-pr" ]; then
453
+ echo "================================================"
454
+ echo "📋 GATE 11: ChatGPT Length Limits"
455
+ echo "================================================"
456
+
457
+ CHATGPT_LIMITS_FAILED=0
458
+
459
+ # Check instructions.txt length (8,000 char limit)
460
+ if [ -f "src/openapi/instructions.txt" ]; then
461
+ if command -v python &> /dev/null || command -v python3 &> /dev/null; then
462
+ PYTHON_CMD=$(command -v python3 2>/dev/null || command -v python 2>/dev/null)
463
+ INST_LENGTH=$($PYTHON_CMD -c "f = open('src/openapi/instructions.txt', 'r', encoding='utf-8'); content = f.read(); f.close(); print(len(content))" 2>/dev/null || echo "0")
464
+
465
+ if [ "$INST_LENGTH" -gt 8000 ]; then
466
+ echo "❌ FAILED: instructions.txt exceeds 8,000 character limit"
467
+ echo " Current length: $INST_LENGTH characters"
468
+ echo " Limit: 8,000 characters"
469
+ echo " Solution: Move detailed content to KB files (KB-*.txt) and reference them"
470
+ echo " See: $ARCH_DOC#chatgpt-length-limitations"
471
+ CHATGPT_LIMITS_FAILED=1
472
+ else
473
+ echo "✅ PASSED: instructions.txt length ($INST_LENGTH chars) within limit (8,000)"
474
+ fi
475
+ else
476
+ echo "â„šī¸ python not found - skipping instructions.txt length check"
477
+ fi
478
+ fi
479
+
480
+ # Check OpenAPI operation description lengths (300 char limit)
481
+ if [ -f "$OPENAPI_PATH" ]; then
482
+ if command -v python &> /dev/null || command -v python3 &> /dev/null; then
483
+ PYTHON_CMD=$(command -v python3 2>/dev/null || command -v python 2>/dev/null)
484
+ LONG_DESCS=$($PYTHON_CMD -c "
485
+ import json
486
+ try:
487
+ f = open('$OPENAPI_PATH', 'r', encoding='utf-8')
488
+ spec = json.load(f)
489
+ f.close()
490
+ violations = []
491
+ for path, methods in spec.get('paths', {}).items():
492
+ for method, details in methods.items():
493
+ if isinstance(details, dict) and 'description' in details:
494
+ desc = details['description']
495
+ if len(desc) > 300:
496
+ violations.append(f'{path} {method.upper()}: {len(desc)} chars')
497
+ if violations:
498
+ print('\n'.join(violations))
499
+ else:
500
+ print('OK')
501
+ except Exception as e:
502
+ print(f'ERROR: {e}')
503
+ " 2>/dev/null || echo "ERROR")
504
+
505
+ if [ "$LONG_DESCS" != "OK" ] && [ -n "$LONG_DESCS" ] && [ "$LONG_DESCS" != "ERROR" ]; then
506
+ echo "❌ FAILED: OpenAPI operation descriptions exceed 300 character limit"
507
+ echo "$LONG_DESCS" | while IFS= read -r line; do
508
+ echo " $line"
509
+ done
510
+ echo " Solution: Shorten descriptions, move details to KB files, reference KB files"
511
+ echo " See: $ARCH_DOC#chatgpt-length-limitations"
512
+ CHATGPT_LIMITS_FAILED=1
513
+ elif [ "$LONG_DESCS" = "ERROR" ]; then
514
+ echo "âš ī¸ WARNING: Could not validate OpenAPI description lengths (JSON parsing error)"
515
+ WARNINGS=1
516
+ else
517
+ echo "✅ PASSED: All OpenAPI operation descriptions within limit (300 chars)"
518
+ fi
519
+ else
520
+ echo "â„šī¸ python not found - skipping OpenAPI description length check"
521
+ fi
522
+ fi
523
+
524
+ if [ $CHATGPT_LIMITS_FAILED -eq 1 ]; then
525
+ FAILED=1
526
+ fi
527
+ echo ""
528
+ fi
529
+
530
+ # Summary
531
+ echo "================================================"
532
+ echo "📊 QUALITY CHECK SUMMARY ($MODE mode)"
533
+ echo "================================================"
534
+ echo ""
535
+
536
+ if [ $FAILED -eq 1 ]; then
537
+ echo "❌ CHECKS FAILED"
538
+ echo ""
539
+ echo "Critical issues found. Cannot proceed."
540
+ echo "See rules/code-quality-and-debugging-patterns.md"
541
+ exit 1
542
+ elif [ $WARNINGS -gt 0 ]; then
543
+ echo "âš ī¸ CHECKS PASSED WITH WARNINGS"
544
+ echo ""
545
+ echo "Some warnings found. Consider addressing before proceeding."
546
+
547
+ if [ "$MODE" = "pre-commit" ]; then
548
+ echo "You can still commit, but consider fixing warnings."
549
+ exit 0
550
+ else
551
+ exit 0
552
+ fi
553
+ else
554
+ echo "✅ ALL CHECKS PASSED"
555
+ echo ""
556
+
557
+ if [ "$MODE" = "pre-commit" ]; then
558
+ echo "Ready to commit!"
559
+ elif [ "$MODE" = "pre-pr" ]; then
560
+ echo "Ready to create PR!"
561
+ echo "Run: gh pr create --title \"your title\" --body \"your description\""
562
+ else
563
+ echo "CI checks complete!"
564
+ fi
565
+ exit 0
566
+ fi