@a-company/paradigm 5.21.2 → 5.22.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/dist/{ambient-HYZR42NX.js → ambient-WEPHBAJD.js} +1 -1
- package/dist/chunk-27FBCZX6.js +2 -0
- package/dist/{chunk-UONPO7SC.js → chunk-CSXVL2U7.js} +1 -1
- package/dist/{chunk-SWFC4HD7.js → chunk-QIGE36CB.js} +247 -125
- package/dist/{chunk-ET5XJTSX.js → chunk-RMOALQJN.js} +1 -1
- package/dist/{compliance-33HC4D5Z.js → compliance-TSXLXADI.js} +2 -2
- package/dist/{config-schema-HR7LDEQ6.js → config-schema-GUQY2QN7.js} +1 -1
- package/dist/{context-audit-RSSRX6XC.js → context-audit-XRPT3OU2.js} +1 -1
- package/dist/doctor-HMQBF2WK.js +2 -0
- package/dist/enforcement-MKFUH4TE.js +2 -0
- package/dist/enforcement-TJOXPSTJ.js +2 -0
- package/dist/{habits-4J2KRYUL.js → habits-LX5IWCZM.js} +1 -1
- package/dist/{hooks-JQKA3TUG.js → hooks-EFM2IK6E.js} +1 -1
- package/dist/index.js +7 -7
- package/dist/mcp.js +46 -46
- package/dist/{migrate-ZLITTMVW.js → migrate-OMR5G5AF.js} +1 -1
- package/dist/shift-ASDLMMK5.js +11 -0
- package/package.json +1 -1
- package/dist/doctor-PA7EAJO2.js +0 -2
- package/dist/shift-QZC77Y5O.js +0 -11
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import*as e from'fs';import*as r from'path';import*as E from'os';import {execSync}from'child_process';import o from'chalk';var
|
|
2
|
+
import*as e from'fs';import*as r from'path';import*as E from'os';import {execSync}from'child_process';import o from'chalk';var C=`#!/bin/sh
|
|
3
3
|
# paradigm-common.sh \u2014 Shared compliance checks for Paradigm stop hooks
|
|
4
4
|
# Sourced by claude-code-stop.sh and cursor-stop.sh
|
|
5
5
|
#
|
|
@@ -30,6 +30,8 @@ import*as e from'fs';import*as r from'path';import*as E from'os';import {execSyn
|
|
|
30
30
|
# 9. Purpose-required patterns from config.yaml
|
|
31
31
|
# 10. Aspect drift detection with auto-heal (from unified compliance-check)
|
|
32
32
|
# 11. Portal gate implementation compliance (from unified compliance-check)
|
|
33
|
+
# 12. Graduation failure tracking (auto-demotion)
|
|
34
|
+
# 13. Orchestration required for complex tasks
|
|
33
35
|
|
|
34
36
|
VIOLATIONS=""
|
|
35
37
|
VIOLATION_COUNT=0
|
|
@@ -37,6 +39,35 @@ AUTO_FIXED=""
|
|
|
37
39
|
AUTO_FIX_COUNT=0
|
|
38
40
|
PARADIGM_AUTO_FIX="\${PARADIGM_AUTO_FIX:-0}"
|
|
39
41
|
|
|
42
|
+
# --- Load enforcement config ---
|
|
43
|
+
ENFORCEMENT_MAP=""
|
|
44
|
+
if command -v paradigm >/dev/null 2>&1; then
|
|
45
|
+
ENFORCEMENT_MAP=$(paradigm enforcement resolve --json 2>/dev/null) || true
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# Helper: get severity for a check ID
|
|
49
|
+
_check_severity() {
|
|
50
|
+
_id="$1"
|
|
51
|
+
_default="$2"
|
|
52
|
+
if [ -z "$ENFORCEMENT_MAP" ]; then
|
|
53
|
+
echo "$_default"
|
|
54
|
+
return
|
|
55
|
+
fi
|
|
56
|
+
_val=$(echo "$ENFORCEMENT_MAP" | grep -o "\\"$_id\\":\\"[^\\"]*\\"" | sed 's/.*":\\"//' | sed 's/"//')
|
|
57
|
+
if [ -n "$_val" ]; then
|
|
58
|
+
echo "$_val"
|
|
59
|
+
else
|
|
60
|
+
echo "$_default"
|
|
61
|
+
fi
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# Read orchestration threshold
|
|
65
|
+
ORCH_THRESHOLD=3
|
|
66
|
+
if [ -n "$ENFORCEMENT_MAP" ]; then
|
|
67
|
+
_ot=$(echo "$ENFORCEMENT_MAP" | grep -o '"orchestrationThreshold":[0-9]*' | sed 's/.*://')
|
|
68
|
+
[ -n "$_ot" ] && ORCH_THRESHOLD="$_ot"
|
|
69
|
+
fi
|
|
70
|
+
|
|
40
71
|
# --- Cache .purpose file paths (avoid repeated find scans) ---
|
|
41
72
|
PURPOSE_CACHE=".paradigm/.purpose-paths"
|
|
42
73
|
if [ -f "$PURPOSE_CACHE" ]; then
|
|
@@ -103,17 +134,24 @@ for file in $MODIFIED; do
|
|
|
103
134
|
done
|
|
104
135
|
|
|
105
136
|
# Only flag if there are uncovered directories AND zero paradigm files were touched
|
|
106
|
-
|
|
137
|
+
_SEV=$(_check_severity "purpose-coverage" "block")
|
|
138
|
+
if [ "$_SEV" != "off" ] && [ -n "$UNCOVERED_SOURCE_DIRS" ] && [ "$PARADIGM_COUNT" -eq 0 ]; then
|
|
107
139
|
# Count uncovered dirs
|
|
108
140
|
UNCOVERED_DIR_COUNT=0
|
|
109
141
|
for _d in $UNCOVERED_SOURCE_DIRS; do
|
|
110
142
|
UNCOVERED_DIR_COUNT=$((UNCOVERED_DIR_COUNT + 1))
|
|
111
143
|
done
|
|
112
|
-
|
|
144
|
+
if [ "$_SEV" = "block" ]; then
|
|
145
|
+
VIOLATIONS="$VIOLATIONS
|
|
113
146
|
- You modified source files in $UNCOVERED_DIR_COUNT director(ies) without updating their .purpose files:
|
|
114
147
|
$UNCOVERED_SOURCE_DIRS
|
|
115
148
|
Update the nearest .purpose file for each modified code area."
|
|
116
|
-
|
|
149
|
+
VIOLATION_COUNT=$((VIOLATION_COUNT + 1))
|
|
150
|
+
else
|
|
151
|
+
ADVISORY="$ADVISORY
|
|
152
|
+
- (warn) Source files in $UNCOVERED_DIR_COUNT director(ies) modified without .purpose updates:
|
|
153
|
+
$UNCOVERED_SOURCE_DIRS"
|
|
154
|
+
fi
|
|
117
155
|
fi
|
|
118
156
|
|
|
119
157
|
# --- Check 2: Modified source directories missing .purpose files ---
|
|
@@ -149,7 +187,8 @@ for file in $MODIFIED; do
|
|
|
149
187
|
fi
|
|
150
188
|
done
|
|
151
189
|
|
|
152
|
-
|
|
190
|
+
_SEV=$(_check_severity "purpose-exists" "block")
|
|
191
|
+
if [ "$_SEV" != "off" ] && [ -n "$DIRS_WITHOUT_PURPOSE" ]; then
|
|
153
192
|
if [ "$PARADIGM_AUTO_FIX" = "1" ]; then
|
|
154
193
|
# Auto-fix: create stub .purpose files for directories missing them
|
|
155
194
|
for dir in $DIRS_WITHOUT_PURPOSE; do
|
|
@@ -165,86 +204,107 @@ PURPOSEEOF
|
|
|
165
204
|
- Created stub .purpose in $dir (update descriptions)"
|
|
166
205
|
AUTO_FIX_COUNT=$((AUTO_FIX_COUNT + 1))
|
|
167
206
|
done
|
|
168
|
-
|
|
207
|
+
elif [ "$_SEV" = "block" ]; then
|
|
169
208
|
VIOLATIONS="$VIOLATIONS
|
|
170
209
|
- These directories have modified source files but no .purpose file anywhere in their path:
|
|
171
210
|
$DIRS_WITHOUT_PURPOSE
|
|
172
211
|
Create a .purpose file using paradigm_purpose_init + paradigm_purpose_add_component."
|
|
173
212
|
VIOLATION_COUNT=$((VIOLATION_COUNT + 1))
|
|
213
|
+
else
|
|
214
|
+
ADVISORY="$ADVISORY
|
|
215
|
+
- (warn) Directories with modified source files but no .purpose file:
|
|
216
|
+
$DIRS_WITHOUT_PURPOSE"
|
|
174
217
|
fi
|
|
175
218
|
fi
|
|
176
219
|
|
|
177
220
|
# --- Check 3: Route patterns added without portal.yaml ---
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
221
|
+
_SEV=$(_check_severity "portal-gates" "block")
|
|
222
|
+
if [ "$_SEV" != "off" ]; then
|
|
223
|
+
if [ -f "portal.yaml" ] || echo "$MODIFIED" | grep -q "portal.yaml"; then
|
|
224
|
+
: # portal.yaml exists or was modified \u2014 OK
|
|
225
|
+
else
|
|
226
|
+
# Check if any modified files contain route-like patterns
|
|
227
|
+
# Skip: test/spec/fixture files, markdown, and comment-only matches
|
|
228
|
+
ROUTE_FILES=""
|
|
229
|
+
for file in $MODIFIED; do
|
|
230
|
+
# Skip test, spec, and fixture files entirely
|
|
231
|
+
case "$file" in
|
|
232
|
+
*test*|*spec*|*fixture*|*__tests__*|*__mocks__*|*.test.*|*.spec.*) continue ;;
|
|
233
|
+
esac
|
|
234
|
+
case "$file" in
|
|
235
|
+
*.ts|*.js|*.tsx|*.jsx|*.py|*.rs|*.go)
|
|
236
|
+
if [ -f "$file" ]; then
|
|
237
|
+
# Grep for route patterns, then filter out comment lines and description strings
|
|
238
|
+
ROUTE_MATCH=$(grep -nE '\\.(get|post|put|patch|delete)\\s*\\(|router\\.|app\\.(get|post|put|delete)|@(Get|Post|Put|Delete)|#\\[actix_web::(get|post)' "$file" 2>/dev/null \\
|
|
239
|
+
| grep -v '^\\s*[0-9]*:\\s*//' \\
|
|
240
|
+
| grep -v '^\\s*[0-9]*:\\s*\\*' \\
|
|
241
|
+
| grep -v '^\\s*[0-9]*:\\s*#' \\
|
|
242
|
+
| grep -v '^\\s*[0-9]*:\\s*<!--' \\
|
|
243
|
+
| grep -v 'description:' \\
|
|
244
|
+
| grep -v 'summary:' \\
|
|
245
|
+
| grep -v 'comment:' \\
|
|
246
|
+
| grep -v '@example' \\
|
|
247
|
+
| grep -v '@see' \\
|
|
248
|
+
|| true)
|
|
249
|
+
if [ -n "$ROUTE_MATCH" ]; then
|
|
250
|
+
ROUTE_FILES="$ROUTE_FILES $file"
|
|
251
|
+
fi
|
|
206
252
|
fi
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
done
|
|
253
|
+
;;
|
|
254
|
+
esac
|
|
255
|
+
done
|
|
211
256
|
|
|
212
|
-
|
|
213
|
-
|
|
257
|
+
if [ -n "$ROUTE_FILES" ]; then
|
|
258
|
+
if [ "$_SEV" = "block" ]; then
|
|
259
|
+
VIOLATIONS="$VIOLATIONS
|
|
214
260
|
- Route/endpoint patterns found in modified files but no portal.yaml exists:
|
|
215
261
|
$ROUTE_FILES
|
|
216
262
|
Create portal.yaml with gate definitions. Use paradigm_gates_for_route for suggestions."
|
|
217
|
-
|
|
263
|
+
VIOLATION_COUNT=$((VIOLATION_COUNT + 1))
|
|
264
|
+
else
|
|
265
|
+
ADVISORY="$ADVISORY
|
|
266
|
+
- (warn) Route/endpoint patterns found without portal.yaml:
|
|
267
|
+
$ROUTE_FILES"
|
|
268
|
+
fi
|
|
269
|
+
fi
|
|
218
270
|
fi
|
|
219
271
|
fi
|
|
220
272
|
|
|
221
273
|
# --- Check 4: Aspect anchor files that no longer exist ---
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if [
|
|
233
|
-
|
|
234
|
-
if [
|
|
235
|
-
|
|
274
|
+
_SEV=$(_check_severity "aspect-anchors" "block")
|
|
275
|
+
if [ "$_SEV" != "off" ]; then
|
|
276
|
+
for purpose_file in $PURPOSE_PATHS; do
|
|
277
|
+
if grep -q "anchors:" "$purpose_file" 2>/dev/null; then
|
|
278
|
+
purpose_dir=$(dirname "$purpose_file")
|
|
279
|
+
in_anchors=false
|
|
280
|
+
while IFS= read -r line; do
|
|
281
|
+
case "$line" in
|
|
282
|
+
*"anchors:"*) in_anchors=true; continue ;;
|
|
283
|
+
*"- "*)
|
|
284
|
+
if [ "$in_anchors" = true ]; then
|
|
285
|
+
anchor_path=$(echo "$line" | sed 's/.*- //' | sed 's/:.*//' | tr -d ' ')
|
|
286
|
+
if [ -n "$anchor_path" ]; then
|
|
287
|
+
# Try relative to .purpose dir first, then project root
|
|
288
|
+
if [ ! -f "$purpose_dir/$anchor_path" ] && [ ! -f "./$anchor_path" ]; then
|
|
289
|
+
if [ "$_SEV" = "block" ]; then
|
|
290
|
+
VIOLATIONS="$VIOLATIONS
|
|
236
291
|
- Aspect anchor '$anchor_path' in $purpose_file does not exist.
|
|
237
292
|
Update the anchor or remove the stale aspect."
|
|
238
|
-
|
|
293
|
+
VIOLATION_COUNT=$((VIOLATION_COUNT + 1))
|
|
294
|
+
else
|
|
295
|
+
ADVISORY="$ADVISORY
|
|
296
|
+
- (warn) Aspect anchor '$anchor_path' in $purpose_file does not exist."
|
|
297
|
+
fi
|
|
298
|
+
fi
|
|
239
299
|
fi
|
|
240
300
|
fi
|
|
241
|
-
|
|
242
|
-
;;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
301
|
+
;;
|
|
302
|
+
*) in_anchors=false ;;
|
|
303
|
+
esac
|
|
304
|
+
done < "$purpose_file"
|
|
305
|
+
fi
|
|
306
|
+
done
|
|
307
|
+
fi
|
|
248
308
|
|
|
249
309
|
# --- Check 5: Per-directory .purpose freshness ---
|
|
250
310
|
# Uses .pending-review if available, plus a git-based fallback that cross-references
|
|
@@ -328,51 +388,62 @@ for file in $MODIFIED; do
|
|
|
328
388
|
fi
|
|
329
389
|
done
|
|
330
390
|
|
|
331
|
-
|
|
332
|
-
|
|
391
|
+
_SEV=$(_check_severity "purpose-freshness" "warn")
|
|
392
|
+
if [ "$_SEV" != "off" ] && [ -n "$STALE_PURPOSES" ]; then
|
|
393
|
+
if [ "$_SEV" = "block" ]; then
|
|
394
|
+
VIOLATIONS="$VIOLATIONS
|
|
333
395
|
- These .purpose files cover modified source code but were NOT updated:
|
|
334
396
|
$STALE_PURPOSES
|
|
335
397
|
Update each with: #components, ~aspects (with anchors), !signals, \\$flows, ^gates."
|
|
336
|
-
|
|
398
|
+
VIOLATION_COUNT=$((VIOLATION_COUNT + 1))
|
|
399
|
+
else
|
|
400
|
+
ADVISORY="$ADVISORY
|
|
401
|
+
- (warn) These .purpose files cover modified source code but were NOT updated:
|
|
402
|
+
$STALE_PURPOSES"
|
|
403
|
+
fi
|
|
337
404
|
fi
|
|
338
405
|
|
|
339
406
|
# --- Check 6: Aspect coverage advisory ---
|
|
340
407
|
ADVISORY=""
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
if [ "$HAS_ASPECTS" = true ] && [ "$SOURCE_COUNT" -gt 0 ]; then
|
|
350
|
-
ASPECT_UPDATED=false
|
|
351
|
-
for file in $MODIFIED; do
|
|
352
|
-
case "$file" in
|
|
353
|
-
*.purpose)
|
|
354
|
-
if grep -qE '^\\s*~|anchors:|applies-to:' "$file" 2>/dev/null; then
|
|
355
|
-
ASPECT_UPDATED=true
|
|
356
|
-
break
|
|
357
|
-
fi
|
|
358
|
-
;;
|
|
359
|
-
esac
|
|
408
|
+
_SEV=$(_check_severity "aspect-advisory" "warn")
|
|
409
|
+
if [ "$_SEV" != "off" ]; then
|
|
410
|
+
HAS_ASPECTS=false
|
|
411
|
+
for purpose_file in $PURPOSE_PATHS; do
|
|
412
|
+
if grep -qE '^\\s*~' "$purpose_file" 2>/dev/null; then
|
|
413
|
+
HAS_ASPECTS=true
|
|
414
|
+
break
|
|
415
|
+
fi
|
|
360
416
|
done
|
|
361
417
|
|
|
362
|
-
if [ "$
|
|
363
|
-
|
|
418
|
+
if [ "$HAS_ASPECTS" = true ] && [ "$SOURCE_COUNT" -gt 0 ]; then
|
|
419
|
+
ASPECT_UPDATED=false
|
|
420
|
+
for file in $MODIFIED; do
|
|
421
|
+
case "$file" in
|
|
422
|
+
*.purpose)
|
|
423
|
+
if grep -qE '^\\s*~|anchors:|applies-to:' "$file" 2>/dev/null; then
|
|
424
|
+
ASPECT_UPDATED=true
|
|
425
|
+
break
|
|
426
|
+
fi
|
|
427
|
+
;;
|
|
428
|
+
esac
|
|
429
|
+
done
|
|
430
|
+
|
|
431
|
+
if [ "$ASPECT_UPDATED" = false ]; then
|
|
432
|
+
ADVISORY=" This project defines ~aspects with code anchors. Check if existing
|
|
364
433
|
~aspects need updated anchors or applies-to patterns."
|
|
434
|
+
fi
|
|
365
435
|
fi
|
|
366
436
|
fi
|
|
367
437
|
|
|
368
438
|
# --- Check 7: Lore entry expected for significant sessions ---
|
|
369
|
-
|
|
439
|
+
_SEV=$(_check_severity "lore-required" "block")
|
|
440
|
+
if [ "$_SEV" != "off" ] && [ "$SOURCE_COUNT" -ge 3 ] && [ -d ".paradigm/lore" ]; then
|
|
370
441
|
LORE_RECORDED=false
|
|
371
442
|
|
|
372
443
|
# Check git diff first (covers staged/committed lore)
|
|
373
444
|
for file in $MODIFIED; do
|
|
374
445
|
case "$file" in
|
|
375
|
-
.paradigm/lore/entries/*.yaml|.paradigm/lore/entries/*/*.yaml)
|
|
446
|
+
.paradigm/lore/entries/*.yaml|.paradigm/lore/entries/*/*.yaml|.paradigm/lore/entries/*.lore|.paradigm/lore/entries/*/*.lore)
|
|
376
447
|
LORE_RECORDED=true
|
|
377
448
|
break
|
|
378
449
|
;;
|
|
@@ -383,7 +454,7 @@ if [ "$SOURCE_COUNT" -ge 3 ] && [ -d ".paradigm/lore" ]; then
|
|
|
383
454
|
if [ "$LORE_RECORDED" = false ]; then
|
|
384
455
|
TODAY=$(date -u +"%Y-%m-%d")
|
|
385
456
|
if [ -d ".paradigm/lore/entries/$TODAY" ]; then
|
|
386
|
-
ENTRY_COUNT=$(find ".paradigm/lore/entries/$TODAY" -name "*.yaml" 2>/dev/null | head -1)
|
|
457
|
+
ENTRY_COUNT=$(find ".paradigm/lore/entries/$TODAY" \\( -name "*.yaml" -o -name "*.lore" \\) 2>/dev/null | head -1)
|
|
387
458
|
if [ -n "$ENTRY_COUNT" ]; then
|
|
388
459
|
LORE_RECORDED=true
|
|
389
460
|
fi
|
|
@@ -424,12 +495,15 @@ LOREEOF
|
|
|
424
495
|
AUTO_FIXED="$AUTO_FIXED
|
|
425
496
|
- Created stub lore entry $LORE_ID (update with real summary)"
|
|
426
497
|
AUTO_FIX_COUNT=$((AUTO_FIX_COUNT + 1))
|
|
427
|
-
|
|
498
|
+
elif [ "$_SEV" = "block" ]; then
|
|
428
499
|
VIOLATIONS="$VIOLATIONS
|
|
429
500
|
- You modified $SOURCE_COUNT source files but recorded no lore entry.
|
|
430
501
|
Record your session: paradigm_lore_record (MCP) or paradigm lore record (CLI).
|
|
431
502
|
Include: type, title, summary, and symbols_touched."
|
|
432
503
|
VIOLATION_COUNT=$((VIOLATION_COUNT + 1))
|
|
504
|
+
else
|
|
505
|
+
ADVISORY="$ADVISORY
|
|
506
|
+
- (warn) $SOURCE_COUNT source files modified without a lore entry."
|
|
433
507
|
fi
|
|
434
508
|
fi
|
|
435
509
|
fi
|
|
@@ -446,17 +520,23 @@ fi
|
|
|
446
520
|
|
|
447
521
|
if [ -n "$COMPLIANCE_RESULT" ]; then
|
|
448
522
|
# --- Check 8: Blocking habits (from unified result) ---
|
|
449
|
-
|
|
450
|
-
if [ -f ".paradigm/.habits-blocking" ]; then
|
|
523
|
+
_SEV=$(_check_severity "habits-blocking" "block")
|
|
524
|
+
if [ "$_SEV" != "off" ] && [ -f ".paradigm/.habits-blocking" ]; then
|
|
451
525
|
HABITS_BLOCKING=$(cat ".paradigm/.habits-blocking")
|
|
452
|
-
|
|
526
|
+
if [ "$_SEV" = "block" ]; then
|
|
527
|
+
VIOLATIONS="$VIOLATIONS
|
|
453
528
|
- Blocking habit(s) not satisfied:
|
|
454
529
|
$HABITS_BLOCKING
|
|
455
530
|
Fix: satisfy the habit(s), or change severity to 'warn' in .paradigm/habits.yaml to make advisory."
|
|
456
|
-
|
|
531
|
+
VIOLATION_COUNT=$((VIOLATION_COUNT + 1))
|
|
532
|
+
else
|
|
533
|
+
ADVISORY="$ADVISORY
|
|
534
|
+
- (warn) Blocking habit(s) not satisfied: $HABITS_BLOCKING"
|
|
535
|
+
fi
|
|
457
536
|
fi
|
|
458
537
|
|
|
459
538
|
# --- Check 10: Aspect drift (from unified result) ---
|
|
539
|
+
_SEV=$(_check_severity "drift-detection" "block")
|
|
460
540
|
DRIFTED_COUNT=$(echo "$COMPLIANCE_RESULT" | grep -o '"driftedCount":[0-9]*' | sed 's/.*://')
|
|
461
541
|
HEALED_COUNT=$(echo "$COMPLIANCE_RESULT" | grep -o '"healedCount":[0-9]*' | sed 's/.*://')
|
|
462
542
|
|
|
@@ -464,39 +544,57 @@ if [ -n "$COMPLIANCE_RESULT" ]; then
|
|
|
464
544
|
echo "[paradigm] Auto-healed $HEALED_COUNT shifted anchor(s)." >&2
|
|
465
545
|
fi
|
|
466
546
|
|
|
467
|
-
if [ -n "$DRIFTED_COUNT" ] && [ "$DRIFTED_COUNT" -gt 0 ] 2>/dev/null; then
|
|
468
|
-
|
|
547
|
+
if [ "$_SEV" != "off" ] && [ -n "$DRIFTED_COUNT" ] && [ "$DRIFTED_COUNT" -gt 0 ] 2>/dev/null; then
|
|
548
|
+
if [ "$_SEV" = "block" ]; then
|
|
549
|
+
VIOLATIONS="$VIOLATIONS
|
|
469
550
|
- $DRIFTED_COUNT aspect anchor(s) have drifted (content genuinely changed).
|
|
470
551
|
Run paradigm_aspect_check to review. Update anchors in .purpose files."
|
|
471
|
-
|
|
552
|
+
VIOLATION_COUNT=$((VIOLATION_COUNT + 1))
|
|
553
|
+
else
|
|
554
|
+
ADVISORY="$ADVISORY
|
|
555
|
+
- (warn) $DRIFTED_COUNT aspect anchor(s) have drifted."
|
|
556
|
+
fi
|
|
472
557
|
fi
|
|
473
558
|
|
|
474
559
|
# --- Check 11: Portal gate compliance (from unified result) ---
|
|
560
|
+
_SEV=$(_check_severity "portal-compliance" "block")
|
|
475
561
|
UNDECLARED=$(echo "$COMPLIANCE_RESULT" | grep -o '"usedButUndeclaredCount":[0-9]*' | sed 's/.*://')
|
|
476
562
|
|
|
477
|
-
if [ -n "$UNDECLARED" ] && [ "$UNDECLARED" -gt 0 ] 2>/dev/null; then
|
|
563
|
+
if [ "$_SEV" != "off" ] && [ -n "$UNDECLARED" ] && [ "$UNDECLARED" -gt 0 ] 2>/dev/null; then
|
|
478
564
|
UNDECLARED_LIST=$(echo "$COMPLIANCE_RESULT" | grep -o '"usedButUndeclared":\\[[^]]*\\]' | sed 's/.*\\[//;s/\\].*//;s/"//g')
|
|
479
|
-
|
|
565
|
+
if [ "$_SEV" = "block" ]; then
|
|
566
|
+
VIOLATIONS="$VIOLATIONS
|
|
480
567
|
- $UNDECLARED gate(s) used in code but not declared in portal.yaml:
|
|
481
568
|
$UNDECLARED_LIST
|
|
482
569
|
Add them to portal.yaml or use paradigm_portal_add_gate."
|
|
483
|
-
|
|
570
|
+
VIOLATION_COUNT=$((VIOLATION_COUNT + 1))
|
|
571
|
+
else
|
|
572
|
+
ADVISORY="$ADVISORY
|
|
573
|
+
- (warn) $UNDECLARED gate(s) used in code but not declared in portal.yaml: $UNDECLARED_LIST"
|
|
574
|
+
fi
|
|
484
575
|
fi
|
|
485
576
|
else
|
|
486
577
|
# Fallback: check habits blocking marker even if compliance-check unavailable
|
|
487
|
-
|
|
578
|
+
_SEV=$(_check_severity "habits-blocking" "block")
|
|
579
|
+
if [ "$_SEV" != "off" ] && [ -f ".paradigm/.habits-blocking" ]; then
|
|
488
580
|
HABITS_BLOCKING=$(cat ".paradigm/.habits-blocking")
|
|
489
|
-
|
|
581
|
+
if [ "$_SEV" = "block" ]; then
|
|
582
|
+
VIOLATIONS="$VIOLATIONS
|
|
490
583
|
- Blocking habit(s) not satisfied:
|
|
491
584
|
$HABITS_BLOCKING
|
|
492
585
|
Fix: satisfy the habit(s), or change severity to 'warn' in .paradigm/habits.yaml to make advisory."
|
|
493
|
-
|
|
586
|
+
VIOLATION_COUNT=$((VIOLATION_COUNT + 1))
|
|
587
|
+
else
|
|
588
|
+
ADVISORY="$ADVISORY
|
|
589
|
+
- (warn) Blocking habit(s) not satisfied: $HABITS_BLOCKING"
|
|
590
|
+
fi
|
|
494
591
|
fi
|
|
495
592
|
fi
|
|
496
593
|
|
|
497
594
|
# --- Check 9: Purpose-required patterns from config.yaml ---
|
|
595
|
+
_SEV=$(_check_severity "purpose-required-patterns" "block")
|
|
498
596
|
CONFIG_FILE=".paradigm/config.yaml"
|
|
499
|
-
if [ -f "$CONFIG_FILE" ]; then
|
|
597
|
+
if [ "$_SEV" != "off" ] && [ -f "$CONFIG_FILE" ]; then
|
|
500
598
|
MISSING_REQUIRED=""
|
|
501
599
|
in_section=false
|
|
502
600
|
current_pattern=""
|
|
@@ -582,19 +680,24 @@ PURPOSEEOF
|
|
|
582
680
|
- Created stub .purpose in $dir (purpose-required pattern)"
|
|
583
681
|
AUTO_FIX_COUNT=$((AUTO_FIX_COUNT + 1))
|
|
584
682
|
done
|
|
585
|
-
|
|
683
|
+
elif [ "$_SEV" = "block" ]; then
|
|
586
684
|
VIOLATIONS="$VIOLATIONS
|
|
587
685
|
- These directories match purpose-required patterns but have no .purpose file:
|
|
588
686
|
$MISSING_REQUIRED
|
|
589
687
|
Create .purpose files: paradigm_purpose_init + paradigm_purpose_add_component."
|
|
590
688
|
VIOLATION_COUNT=$((VIOLATION_COUNT + 1))
|
|
689
|
+
else
|
|
690
|
+
ADVISORY="$ADVISORY
|
|
691
|
+
- (warn) Directories matching purpose-required patterns without .purpose file:
|
|
692
|
+
$MISSING_REQUIRED"
|
|
591
693
|
fi
|
|
592
694
|
fi
|
|
593
695
|
fi
|
|
594
696
|
|
|
595
697
|
# --- Check 12: Graduation failure tracking ---
|
|
596
698
|
# When violations occur for graduated habits, record failures for auto-demotion.
|
|
597
|
-
|
|
699
|
+
_SEV=$(_check_severity "graduation-tracking" "warn")
|
|
700
|
+
if [ "$_SEV" != "off" ] && [ "$VIOLATION_COUNT" -gt 0 ] && [ -f ".paradigm/graduation.yaml" ]; then
|
|
598
701
|
GRAD_FAILURES_DIR=".paradigm/.graduation-failures"
|
|
599
702
|
mkdir -p "$GRAD_FAILURES_DIR" 2>/dev/null
|
|
600
703
|
|
|
@@ -639,6 +742,23 @@ if [ "$VIOLATION_COUNT" -gt 0 ] && [ -f ".paradigm/graduation.yaml" ]; then
|
|
|
639
742
|
fi
|
|
640
743
|
done
|
|
641
744
|
fi
|
|
745
|
+
|
|
746
|
+
# --- Check 13: Orchestration required for complex tasks ---
|
|
747
|
+
_SEV=$(_check_severity "orchestration-required" "warn")
|
|
748
|
+
if [ "$_SEV" != "off" ] && [ "$SOURCE_COUNT" -ge "$ORCH_THRESHOLD" ]; then
|
|
749
|
+
if [ ! -f ".paradigm/.orchestrated" ]; then
|
|
750
|
+
if [ "$_SEV" = "block" ]; then
|
|
751
|
+
VIOLATIONS="$VIOLATIONS
|
|
752
|
+
- You modified $SOURCE_COUNT source files without running orchestration.
|
|
753
|
+
Run paradigm_orchestrate_inline mode=\\"plan\\" first.
|
|
754
|
+
Override: paradigm enforcement override orchestration-required warn"
|
|
755
|
+
VIOLATION_COUNT=$((VIOLATION_COUNT + 1))
|
|
756
|
+
else
|
|
757
|
+
ADVISORY="$ADVISORY
|
|
758
|
+
- (orchestration) $SOURCE_COUNT files modified without team orchestration."
|
|
759
|
+
fi
|
|
760
|
+
fi
|
|
761
|
+
fi
|
|
642
762
|
`,R=`#!/bin/sh
|
|
643
763
|
# Paradigm Claude Code Stop Hook (v2)
|
|
644
764
|
# Validates paradigm compliance before allowing the agent to finish.
|
|
@@ -647,7 +767,7 @@ fi
|
|
|
647
767
|
# Hook type: Stop
|
|
648
768
|
# Exit 0 = allow, Exit 2 = block with message
|
|
649
769
|
#
|
|
650
|
-
# Checks 1\
|
|
770
|
+
# Checks 1\u201313 are defined in paradigm-common.sh (shared with Cursor hook).
|
|
651
771
|
|
|
652
772
|
# Read JSON from stdin (hook input)
|
|
653
773
|
INPUT=$(cat)
|
|
@@ -747,13 +867,14 @@ rm -f ".paradigm/.pending-review"
|
|
|
747
867
|
rm -f ".paradigm/.habits-blocking"
|
|
748
868
|
rm -f ".paradigm/.session-started"
|
|
749
869
|
rm -f ".paradigm/.purpose-paths"
|
|
870
|
+
rm -f ".paradigm/.orchestrated"
|
|
750
871
|
|
|
751
872
|
exit 0
|
|
752
|
-
`,
|
|
873
|
+
`,A=`#!/bin/sh
|
|
753
874
|
# Legacy afterFileEdit hook \u2014 replaced by paradigm-posttooluse.sh (postToolUse)
|
|
754
875
|
# Kept as a no-op because Claude Code expects the file to exist.
|
|
755
876
|
exit 0
|
|
756
|
-
`,
|
|
877
|
+
`,U=`#!/bin/sh
|
|
757
878
|
# Paradigm Claude Code Pre-Commit Hook
|
|
758
879
|
# Intercepts git commit Bash calls and auto-rebuilds the index.
|
|
759
880
|
# Installed by: paradigm hooks install --claude-code
|
|
@@ -798,7 +919,7 @@ done
|
|
|
798
919
|
|
|
799
920
|
# Never block \u2014 exit 0
|
|
800
921
|
exit 0
|
|
801
|
-
`,
|
|
922
|
+
`,D=`#!/bin/sh
|
|
802
923
|
# Paradigm Cursor Session Start Hook
|
|
803
924
|
# Fires before the agent does anything \u2014 injects additional_context
|
|
804
925
|
# that acts as a deterministic system prompt (not subject to context compaction).
|
|
@@ -906,7 +1027,7 @@ exit 0
|
|
|
906
1027
|
# Hook type: stop
|
|
907
1028
|
# Exit 0 = allow, Exit 2 = block with message
|
|
908
1029
|
#
|
|
909
|
-
# Checks 1\
|
|
1030
|
+
# Checks 1\u201313 are defined in paradigm-common.sh (shared with Claude Code hook).
|
|
910
1031
|
|
|
911
1032
|
# Read JSON from stdin (hook input)
|
|
912
1033
|
INPUT=$(cat)
|
|
@@ -1028,9 +1149,10 @@ rm -f ".paradigm/.habits-blocking"
|
|
|
1028
1149
|
rm -f ".paradigm/.stop-hook-active"
|
|
1029
1150
|
rm -f ".paradigm/.session-started"
|
|
1030
1151
|
rm -f ".paradigm/.purpose-paths"
|
|
1152
|
+
rm -f ".paradigm/.orchestrated"
|
|
1031
1153
|
|
|
1032
1154
|
exit 0
|
|
1033
|
-
`,
|
|
1155
|
+
`,P=`#!/bin/sh
|
|
1034
1156
|
# Legacy afterFileEdit hook \u2014 replaced by paradigm-posttooluse.sh (postToolUse)
|
|
1035
1157
|
# Kept as a no-op because Cursor expects the file to exist.
|
|
1036
1158
|
exit 0
|
|
@@ -1348,9 +1470,9 @@ if [ "$PENDING_COUNT" -ge 30 ]; then
|
|
|
1348
1470
|
fi
|
|
1349
1471
|
|
|
1350
1472
|
exit 0
|
|
1351
|
-
`;function x(){try{let i=r.join(E.homedir(),".claude","settings.json");if(!e.existsSync(i))return {active:!1};if(!JSON.parse(e.readFileSync(i,"utf8")).enabledPlugins?.["paradigm@a-paradigm"])return {active:!1};let d=r.join(E.homedir(),".claude","plugins","cache","a-paradigm","paradigm");if(!e.existsSync(d))return {active:!1};let a=e.readdirSync(d).filter(s=>e.statSync(r.join(d,s)).isDirectory()).sort().reverse();if(a.length===0)return {active:!1};let p=r.join(d,a[0]),l=r.join(p,"hooks","hooks.json");return e.existsSync(l)?{active:!0,cacheVersion:a[0]}:{active:!1}}catch{return {active:false}}}function
|
|
1352
|
-
`,"utf8"),h.push("settings.json hooks"));}}catch{}return {cleaned:h.length>0,removed:h}}function
|
|
1353
|
-
`)[0]||"unknown error"}`}finally{try{e.unlinkSync(c);}catch{}}}catch{return null}}var
|
|
1473
|
+
`;function x(){try{let i=r.join(E.homedir(),".claude","settings.json");if(!e.existsSync(i))return {active:!1};if(!JSON.parse(e.readFileSync(i,"utf8")).enabledPlugins?.["paradigm@a-paradigm"])return {active:!1};let d=r.join(E.homedir(),".claude","plugins","cache","a-paradigm","paradigm");if(!e.existsSync(d))return {active:!1};let a=e.readdirSync(d).filter(s=>e.statSync(r.join(d,s)).isDirectory()).sort().reverse();if(a.length===0)return {active:!1};let p=r.join(d,a[0]),l=r.join(p,"hooks","hooks.json");return e.existsSync(l)?{active:!0,cacheVersion:a[0]}:{active:!1}}catch{return {active:false}}}function Y(){try{let i=x();if(!i.active||!i.cacheVersion)return {compatible:!0};let h=r.join(E.homedir(),".claude","plugins","cache","a-paradigm","paradigm",i.cacheVersion,"hooks.json");if(!e.existsSync(h))return {compatible:!0};let d=JSON.parse(e.readFileSync(h,"utf8")).compatibleVersions;if(!d)return {compatible:!0};let a=X();if(!a)return {compatible:!0};let p=d.split(/\s+/);for(let l of p){let s=l.match(/^(>=?|<=?)\s*(\d+\.\d+\.\d+)/);if(!s)continue;let[,t,n]=s,f=W(a,n);if(t===">="&&f<0)return {compatible:!1,message:`Plugin requires paradigm ${d}, current: ${a}`};if(t===">"&&f<=0)return {compatible:!1,message:`Plugin requires paradigm ${d}, current: ${a}`};if(t==="<="&&f>0)return {compatible:!1,message:`Plugin requires paradigm ${d}, current: ${a}`};if(t==="<"&&f>=0)return {compatible:!1,message:`Plugin requires paradigm ${d}, current: ${a}`}}return {compatible:!0}}catch{return {compatible:true}}}function X(){try{let i=r.join(r.dirname(new URL(import.meta.url).pathname),"..","..","package.json");return JSON.parse(e.readFileSync(i,"utf8")).version||null}catch{return null}}function W(i,h){let c=i.split(".").map(Number),d=h.split(".").map(Number);for(let a=0;a<3;a++){if((c[a]||0)<(d[a]||0))return -1;if((c[a]||0)>(d[a]||0))return 1}return 0}function q(i){let h=[],c=r.join(i,".claude","hooks");if(e.existsSync(c)){for(let a of ["paradigm-common.sh","paradigm-stop.sh","paradigm-precommit.sh","paradigm-postwrite.sh"]){let p=r.join(c,a);e.existsSync(p)&&(e.unlinkSync(p),h.push(a));}try{e.readdirSync(c).length===0&&e.rmdirSync(c);}catch{}}let d=r.join(i,".claude","settings.json");if(e.existsSync(d))try{let a=JSON.parse(e.readFileSync(d,"utf8")),p=a.hooks;if(p){let l=!1;for(let[s,t]of Object.entries(p)){if(!Array.isArray(t))continue;let n=t.filter(f=>!JSON.stringify(f).includes("paradigm-"));n.length!==t.length&&(l=!0,n.length===0?delete p[s]:p[s]=n);}l&&(Object.keys(p).length===0?delete a.hooks:a.hooks=p,e.writeFileSync(d,JSON.stringify(a,null,2)+`
|
|
1474
|
+
`,"utf8"),h.push("settings.json hooks"));}}catch{}return {cleaned:h.length>0,removed:h}}function J(i,h){try{let c=r.join(E.tmpdir(),`paradigm-hook-validate-${Date.now()}.sh`);e.writeFileSync(c,i,"utf8");try{return execSync(`bash -n "${c}" 2>&1`,{encoding:"utf-8"}),null}catch(d){return `${h}: bash syntax error \u2014 ${d.message?.split(`
|
|
1475
|
+
`)[0]||"unknown error"}`}finally{try{e.unlinkSync(c);}catch{}}}catch{return null}}var F=`#!/bin/sh
|
|
1354
1476
|
# Paradigm post-commit hook - captures history from commits
|
|
1355
1477
|
# Installed by: paradigm hooks install
|
|
1356
1478
|
|
|
@@ -1434,7 +1556,7 @@ if [ -n "$SYMBOLS" ] && [ -d ".paradigm/history" ]; then
|
|
|
1434
1556
|
|
|
1435
1557
|
echo "[paradigm] History entry $ID recorded"
|
|
1436
1558
|
fi
|
|
1437
|
-
`,
|
|
1559
|
+
`,H=`#!/bin/sh
|
|
1438
1560
|
# Paradigm pre-push hook - reindex history before pushing
|
|
1439
1561
|
# Installed by: paradigm hooks install
|
|
1440
1562
|
|
|
@@ -1444,12 +1566,12 @@ if [ -d ".paradigm/history" ] && [ -f ".paradigm/history/log.jsonl" ]; then
|
|
|
1444
1566
|
fi
|
|
1445
1567
|
`;async function te(i={}){let h=process.cwd(),c=i.dryRun||false;c&&console.log(o.cyan(`
|
|
1446
1568
|
[dry-run] Showing what would be installed:
|
|
1447
|
-
`));let d=i.claudeCode&&!i.postCommit&&!i.prePush&&!i.cursor,a=i.cursor&&!i.postCommit&&!i.prePush&&!i.claudeCode;if(!c){let s=[{name:"post-commit",content:
|
|
1569
|
+
`));let d=i.claudeCode&&!i.postCommit&&!i.prePush&&!i.cursor,a=i.cursor&&!i.postCommit&&!i.prePush&&!i.claudeCode;if(!c){let s=[{name:"post-commit",content:F},{name:"pre-push",content:H},{name:"paradigm-common",content:C},{name:"claude-code-stop",content:R},{name:"claude-code-precommit",content:U},{name:"claude-code-postwrite",content:A},{name:"cursor-session-start",content:D},{name:"cursor-stop",content:v},{name:"cursor-precommit",content:b},{name:"cursor-postwrite",content:P},{name:"cursor-pretooluse",content:L},{name:"cursor-posttooluse",content:w}];for(let t of s){let n=J(t.content,t.name);if(n){console.log(o.red(`Hook syntax error: ${n}`)),console.log(o.gray("Aborting installation. Fix the hook script and try again."));return}}}let p=Y();if(p.compatible||(console.log(o.yellow(`
|
|
1448
1570
|
\u26A0 ${p.message}`)),console.log(o.gray(` Hook installation will continue, but behavior may differ from plugin expectations.
|
|
1449
|
-
`))),!d&&!a){let s=r.join(h,".git");if(!e.existsSync(s)){console.log(o.red("Not a git repository."));return}let t=r.join(s,"hooks"),n=!i.postCommit&&!i.prePush&&!i.claudeCode,f=[];if(n||i.postCommit){let u=r.join(t,"post-commit");if(c){let g=e.existsSync(u)&&!i.force?"skip (exists)":"install";console.log(o.gray(` post-commit: ${g} \u2192 ${u}`));}else e.existsSync(u)&&!i.force?e.readFileSync(u,"utf8").includes("paradigm")?console.log(o.gray("post-commit hook already installed by paradigm")):console.log(o.yellow("post-commit hook exists. Use --force to overwrite.")):(e.mkdirSync(t,{recursive:true}),e.writeFileSync(u,
|
|
1571
|
+
`))),!d&&!a){let s=r.join(h,".git");if(!e.existsSync(s)){console.log(o.red("Not a git repository."));return}let t=r.join(s,"hooks"),n=!i.postCommit&&!i.prePush&&!i.claudeCode,f=[];if(n||i.postCommit){let u=r.join(t,"post-commit");if(c){let g=e.existsSync(u)&&!i.force?"skip (exists)":"install";console.log(o.gray(` post-commit: ${g} \u2192 ${u}`));}else e.existsSync(u)&&!i.force?e.readFileSync(u,"utf8").includes("paradigm")?console.log(o.gray("post-commit hook already installed by paradigm")):console.log(o.yellow("post-commit hook exists. Use --force to overwrite.")):(e.mkdirSync(t,{recursive:true}),e.writeFileSync(u,F),e.chmodSync(u,"755"),f.push("post-commit"));}if(n||i.prePush){let u=r.join(t,"pre-push");if(c){let g=e.existsSync(u)&&!i.force?"skip (exists)":"install";console.log(o.gray(` pre-push: ${g} \u2192 ${u}`));}else e.existsSync(u)&&!i.force?e.readFileSync(u,"utf8").includes("paradigm")?console.log(o.gray("pre-push hook already installed by paradigm")):console.log(o.yellow("pre-push hook exists. Use --force to overwrite.")):(e.mkdirSync(t,{recursive:true}),e.writeFileSync(u,H),e.chmodSync(u,"755"),f.push("pre-push"));}!c&&f.length>0&&console.log(o.green(`Git hooks installed: ${f.join(", ")}`));let O=r.join(h,".paradigm/history");!e.existsSync(O)&&!c&&console.log(o.gray("Tip: Run `paradigm history init` to initialize history tracking"));}let l=!i.postCommit&&!i.prePush&&!i.claudeCode&&!i.cursor;(l||i.claudeCode)&&(c?(console.log(o.gray(" Claude Code hooks: would install paradigm-stop.sh, paradigm-precommit.sh, paradigm-postwrite.sh")),console.log(o.gray(` \u2192 ${r.join(h,".claude","hooks")}/`)),console.log(o.gray(" \u2192 Update .claude/settings.json with hook configuration"))):await B(h,i.force)),(l||i.cursor)&&(c?(console.log(o.gray(" Cursor hooks: would install paradigm-session-start.sh, paradigm-stop.sh, paradigm-precommit.sh, paradigm-postwrite.sh, paradigm-pretooluse.sh, paradigm-posttooluse.sh")),console.log(o.gray(` \u2192 ${r.join(h,".cursor","hooks")}/`)),console.log(o.gray(" \u2192 Update .cursor/hooks.json"))):await K(h,i.force)),c&&console.log(o.cyan(`
|
|
1450
1572
|
[dry-run] No changes made.
|
|
1451
|
-
`));}async function B(i,h){let c=x();if(c.active){console.log(o.cyan(` Paradigm plugin v${c.cacheVersion} is active \u2014 hooks are managed by the plugin.`));let{cleaned:
|
|
1452
|
-
`,"utf8"),a.length>0&&console.log(o.green(`Claude Code hooks installed: ${a.join(", ")}`)),console.log(o.green("Claude Code settings.json updated with hook configuration"));}async function K(i,h){let c=r.join(i,".cursor","hooks");e.mkdirSync(c,{recursive:true});let d=[],a=r.join(c,"paradigm-common.sh");e.writeFileSync(a,
|
|
1573
|
+
`));}async function B(i,h){let c=x();if(c.active){console.log(o.cyan(` Paradigm plugin v${c.cacheVersion} is active \u2014 hooks are managed by the plugin.`));let{cleaned:_,removed:$}=q(i);console.log(_?o.green(` Cleaned up stale project hooks: ${$.join(", ")}`):o.gray(" No stale project hooks to clean up.")),console.log(o.gray(" Plugin hooks auto-update with each session \u2014 no manual install needed."));return}let d=r.join(i,".claude","hooks");e.mkdirSync(d,{recursive:true});let a=[],p=r.join(d,"paradigm-common.sh");e.writeFileSync(p,C,"utf8"),e.chmodSync(p,"755");let l=[{name:"paradigm-stop.sh",content:R},{name:"paradigm-precommit.sh",content:U},{name:"paradigm-postwrite.sh",content:A}];for(let _ of l){let $=r.join(d,_.name);if(e.existsSync($)&&!h){console.log(o.gray(` ${_.name}: already installed`));continue}e.writeFileSync($,_.content,"utf8"),e.chmodSync($,"755"),a.push(_.name);}let s=r.join(i,".claude","settings.json"),t={};if(e.existsSync(s))try{t=JSON.parse(e.readFileSync(s,"utf8"));}catch{}let n=t.hooks||{},f={hooks:[{type:"command",command:'bash "$CLAUDE_PROJECT_DIR/.claude/hooks/paradigm-stop.sh"',timeout:10}]},O={matcher:"Bash",hooks:[{type:"command",command:'bash "$CLAUDE_PROJECT_DIR/.claude/hooks/paradigm-precommit.sh"',timeout:30}]},u=n.Stop||[];u.some(_=>JSON.stringify(_).includes("paradigm-stop.sh"))||u.push(f),n.Stop=u;let T=n.PreToolUse||[];T.some(_=>JSON.stringify(_).includes("paradigm-precommit.sh"))||T.push(O),n.PreToolUse=T;let V={matcher:"Edit,Write",hooks:[{type:"command",command:'bash "$CLAUDE_PROJECT_DIR/.claude/hooks/paradigm-postwrite.sh"',timeout:5}]},S=n.PostToolUse||[];S.some(_=>JSON.stringify(_).includes("paradigm-postwrite.sh"))||S.push(V),n.PostToolUse=S,t.hooks=n,e.writeFileSync(s,JSON.stringify(t,null,2)+`
|
|
1574
|
+
`,"utf8"),a.length>0&&console.log(o.green(`Claude Code hooks installed: ${a.join(", ")}`)),console.log(o.green("Claude Code settings.json updated with hook configuration"));}async function K(i,h){let c=r.join(i,".cursor","hooks");e.mkdirSync(c,{recursive:true});let d=[],a=r.join(c,"paradigm-common.sh");e.writeFileSync(a,C,"utf8"),e.chmodSync(a,"755");let p=[{name:"paradigm-session-start.sh",content:D},{name:"paradigm-stop.sh",content:v},{name:"paradigm-precommit.sh",content:b},{name:"paradigm-postwrite.sh",content:P},{name:"paradigm-pretooluse.sh",content:L},{name:"paradigm-posttooluse.sh",content:w}];for(let m of p){let N=r.join(c,m.name);if(e.existsSync(N)&&!h){console.log(o.gray(` ${m.name}: already installed (Cursor)`));continue}e.writeFileSync(N,m.content,"utf8"),e.chmodSync(N,"755"),d.push(m.name);}let l=r.join(i,".cursor","hooks.json"),s={};if(e.existsSync(l))try{s=JSON.parse(e.readFileSync(l,"utf8"));}catch{}s.version=1;let t=s.hooks||{},n={command:".cursor/hooks/paradigm-session-start.sh",timeout:5},f={command:".cursor/hooks/paradigm-stop.sh",timeout:10,loop_limit:3},O={command:".cursor/hooks/paradigm-postwrite.sh",timeout:5},u={command:".cursor/hooks/paradigm-precommit.sh",matcher:"git commit",timeout:30},g=t.sessionStart||[];g.some(m=>JSON.stringify(m).includes("paradigm-session-start.sh"))||g.push(n),t.sessionStart=g;let I=t.stop||[];I.some(m=>JSON.stringify(m).includes("paradigm-stop.sh"))||I.push(f),t.stop=I;let S=t.afterFileEdit||[];S.some(m=>JSON.stringify(m).includes("paradigm-postwrite.sh"))||S.push(O),t.afterFileEdit=S;let _={command:".cursor/hooks/paradigm-pretooluse.sh",matcher:"Edit|Write",timeout:5},$=t.preToolUse||[];$.some(m=>JSON.stringify(m).includes("paradigm-pretooluse.sh"))||$.push(_),t.preToolUse=$;let j={command:".cursor/hooks/paradigm-posttooluse.sh",matcher:"Edit|Write",timeout:5},y=t.postToolUse||[];y.some(m=>JSON.stringify(m).includes("paradigm-posttooluse.sh"))||y.push(j),t.postToolUse=y;let k=t.beforeShellExecution||[];k.some(m=>JSON.stringify(m).includes("paradigm-precommit.sh"))||k.push(u),t.beforeShellExecution=k,s.hooks=t,e.writeFileSync(l,JSON.stringify(s,null,2)+`
|
|
1453
1575
|
`,"utf8"),d.length>0&&console.log(o.green(`Cursor hooks installed: ${d.join(", ")}`)),console.log(o.green("Cursor hooks.json updated with hook configuration"));}async function re(i={}){let h=process.cwd(),c=i.dryRun||false;if(c&&console.log(o.cyan(`
|
|
1454
1576
|
[dry-run] Showing what would be removed:
|
|
1455
1577
|
`)),!i.cursor){let d=r.join(h,".git");if(!e.existsSync(d)){console.log(o.red("Not a git repository."));return}let a=r.join(d,"hooks"),p=[];for(let l of ["post-commit","pre-push"]){let s=r.join(a,l);e.existsSync(s)&&e.readFileSync(s,"utf8").includes("paradigm")&&(c?console.log(o.gray(` Would remove: ${s}`)):e.unlinkSync(s),p.push(l));}c?p.length===0&&console.log(o.gray(" No paradigm git hooks to remove")):p.length>0?console.log(o.green(`Git hooks removed: ${p.join(", ")}`)):console.log(o.gray("No paradigm git hooks found to remove"));}if(i.cursor){let d=r.join(h,".cursor","hooks"),a=[];for(let l of ["paradigm-session-start.sh","paradigm-stop.sh","paradigm-precommit.sh","paradigm-postwrite.sh","paradigm-pretooluse.sh","paradigm-posttooluse.sh"]){let s=r.join(d,l);e.existsSync(s)&&(c?console.log(o.gray(` Would remove: ${s}`)):e.unlinkSync(s),a.push(l));}let p=r.join(h,".cursor","hooks.json");if(e.existsSync(p))if(c)console.log(o.gray(` Would clean paradigm entries from: ${p}`));else try{let l=JSON.parse(e.readFileSync(p,"utf8")),s=l.hooks||{};for(let t of ["sessionStart","stop","afterFileEdit","beforeShellExecution","preToolUse","postToolUse"])Array.isArray(s[t])&&(s[t]=s[t].filter(n=>!JSON.stringify(n).includes("paradigm-")),s[t].length===0&&delete s[t]);l.hooks=s,e.writeFileSync(p,JSON.stringify(l,null,2)+`
|
|
@@ -1457,10 +1579,10 @@ fi
|
|
|
1457
1579
|
[dry-run] No changes made.
|
|
1458
1580
|
`));}async function ae(){let i=process.cwd(),h=r.join(i,".git");if(e.existsSync(h)){console.log(o.magenta(`
|
|
1459
1581
|
Git Hooks Status
|
|
1460
|
-
`));let l=r.join(h,"hooks"),s=["post-commit","pre-push"];for(let n of s){let f=r.join(l,n);e.existsSync(f)?e.readFileSync(f,"utf8").includes("paradigm")?console.log(o.green(` ${n}: installed (paradigm)`)):console.log(o.yellow(` ${n}: exists (other)`)):console.log(o.gray(` ${n}: not installed`));}console.log();let t=r.join(i,".paradigm/history");if(e.existsSync(t)){let n=r.join(t,"log.jsonl");if(e.existsSync(n)){let
|
|
1461
|
-
`).filter(u=>u.trim()).length;console.log(o.white(` History entries: ${
|
|
1582
|
+
`));let l=r.join(h,"hooks"),s=["post-commit","pre-push"];for(let n of s){let f=r.join(l,n);e.existsSync(f)?e.readFileSync(f,"utf8").includes("paradigm")?console.log(o.green(` ${n}: installed (paradigm)`)):console.log(o.yellow(` ${n}: exists (other)`)):console.log(o.gray(` ${n}: not installed`));}console.log();let t=r.join(i,".paradigm/history");if(e.existsSync(t)){let n=r.join(t,"log.jsonl");if(e.existsSync(n)){let O=e.readFileSync(n,"utf8").split(`
|
|
1583
|
+
`).filter(u=>u.trim()).length;console.log(o.white(` History entries: ${O}`));}}else console.log(o.gray(" History: not initialized")),console.log(o.gray(" Run `paradigm history init` to enable"));}else console.log(o.gray(`
|
|
1462
1584
|
Not a git repository (git hooks N/A)
|
|
1463
1585
|
`));console.log(o.magenta(` Claude Code Hooks Status
|
|
1464
|
-
`));let c=x();if(c.active){console.log(o.cyan(` Plugin: paradigm v${c.cacheVersion} (active)`)),console.log(o.green(" Hooks are managed by the plugin \u2014 auto-updates with each session."));let l=r.join(i,".claude","hooks"),s=[];for(let f of ["paradigm-common.sh","paradigm-stop.sh","paradigm-precommit.sh","paradigm-postwrite.sh"])e.existsSync(r.join(l,f))&&s.push(f);let t=r.join(i,".claude","settings.json"),n=false;if(e.existsSync(t))try{let f=JSON.parse(e.readFileSync(t,"utf8"));n=JSON.stringify(f.hooks||{}).includes("paradigm-");}catch{}(s.length>0||n)&&(console.log(o.yellow(` WARNING: Stale project hooks detected (${s.join(", ")}${n?", settings.json entries":""})`)),console.log(o.yellow(" These shadow the plugin hooks and may run outdated logic.")),console.log(o.gray(" Run `paradigm hooks install --claude-code` to clean them up.")));}else {console.log(o.gray(" Plugin: not active (using project-level hooks)"));let l=r.join(i,".claude","hooks"),s=["paradigm-stop.sh","paradigm-precommit.sh","paradigm-postwrite.sh"];for(let n of s){let f=r.join(l,n);e.existsSync(f)?console.log(o.green(` ${n}: installed`)):console.log(o.gray(` ${n}: not installed`));}let t=r.join(i,".claude","settings.json");if(e.existsSync(t))try{let f=JSON.parse(e.readFileSync(t,"utf8")).hooks||{},
|
|
1586
|
+
`));let c=x();if(c.active){console.log(o.cyan(` Plugin: paradigm v${c.cacheVersion} (active)`)),console.log(o.green(" Hooks are managed by the plugin \u2014 auto-updates with each session."));let l=r.join(i,".claude","hooks"),s=[];for(let f of ["paradigm-common.sh","paradigm-stop.sh","paradigm-precommit.sh","paradigm-postwrite.sh"])e.existsSync(r.join(l,f))&&s.push(f);let t=r.join(i,".claude","settings.json"),n=false;if(e.existsSync(t))try{let f=JSON.parse(e.readFileSync(t,"utf8"));n=JSON.stringify(f.hooks||{}).includes("paradigm-");}catch{}(s.length>0||n)&&(console.log(o.yellow(` WARNING: Stale project hooks detected (${s.join(", ")}${n?", settings.json entries":""})`)),console.log(o.yellow(" These shadow the plugin hooks and may run outdated logic.")),console.log(o.gray(" Run `paradigm hooks install --claude-code` to clean them up.")));}else {console.log(o.gray(" Plugin: not active (using project-level hooks)"));let l=r.join(i,".claude","hooks"),s=["paradigm-stop.sh","paradigm-precommit.sh","paradigm-postwrite.sh"];for(let n of s){let f=r.join(l,n);e.existsSync(f)?console.log(o.green(` ${n}: installed`)):console.log(o.gray(` ${n}: not installed`));}let t=r.join(i,".claude","settings.json");if(e.existsSync(t))try{let f=JSON.parse(e.readFileSync(t,"utf8")).hooks||{},O=JSON.stringify(f.Stop||[]).includes("paradigm-stop.sh"),u=JSON.stringify(f.PreToolUse||[]).includes("paradigm-precommit.sh"),g=JSON.stringify(f.PostToolUse||[]).includes("paradigm-postwrite.sh");console.log(o.gray(` settings.json Stop hook: ${O?"configured":"missing"}`)),console.log(o.gray(` settings.json PreToolUse hook: ${u?"configured":"missing"}`)),console.log(o.gray(` settings.json PostToolUse hook: ${g?"configured":"missing"}`));}catch{console.log(o.yellow(" settings.json: parse error"));}else console.log(o.gray(" settings.json: not found"));}console.log(o.magenta(`
|
|
1465
1587
|
Cursor Hooks Status
|
|
1466
|
-
`));let d=r.join(i,".cursor","hooks"),a=["paradigm-session-start.sh","paradigm-stop.sh","paradigm-precommit.sh","paradigm-postwrite.sh","paradigm-pretooluse.sh","paradigm-posttooluse.sh"];for(let l of a){let s=r.join(d,l);e.existsSync(s)?console.log(o.green(` ${l}: installed`)):console.log(o.gray(` ${l}: not installed`));}let p=r.join(i,".cursor","hooks.json");if(e.existsSync(p))try{let s=JSON.parse(e.readFileSync(p,"utf8")).hooks||{},t=JSON.stringify(s.sessionStart||[]).includes("paradigm-session-start.sh"),n=JSON.stringify(s.stop||[]).includes("paradigm-stop.sh"),f=JSON.stringify(s.afterFileEdit||[]).includes("paradigm-postwrite.sh"),
|
|
1588
|
+
`));let d=r.join(i,".cursor","hooks"),a=["paradigm-session-start.sh","paradigm-stop.sh","paradigm-precommit.sh","paradigm-postwrite.sh","paradigm-pretooluse.sh","paradigm-posttooluse.sh"];for(let l of a){let s=r.join(d,l);e.existsSync(s)?console.log(o.green(` ${l}: installed`)):console.log(o.gray(` ${l}: not installed`));}let p=r.join(i,".cursor","hooks.json");if(e.existsSync(p))try{let s=JSON.parse(e.readFileSync(p,"utf8")).hooks||{},t=JSON.stringify(s.sessionStart||[]).includes("paradigm-session-start.sh"),n=JSON.stringify(s.stop||[]).includes("paradigm-stop.sh"),f=JSON.stringify(s.afterFileEdit||[]).includes("paradigm-postwrite.sh"),O=JSON.stringify(s.beforeShellExecution||[]).includes("paradigm-precommit.sh"),u=JSON.stringify(s.preToolUse||[]).includes("paradigm-pretooluse.sh"),g=JSON.stringify(s.postToolUse||[]).includes("paradigm-posttooluse.sh");console.log(o.gray(` hooks.json sessionStart: ${t?"configured":"missing"}`)),console.log(o.gray(` hooks.json stop: ${n?"configured":"missing"}`)),console.log(o.gray(` hooks.json afterFileEdit: ${f?"configured":"missing"}`)),console.log(o.gray(` hooks.json preToolUse: ${u?"configured":"missing"}`)),console.log(o.gray(` hooks.json postToolUse: ${g?"configured":"missing"}`)),console.log(o.gray(` hooks.json beforeShellExecution: ${O?"configured":"missing"}`));}catch{console.log(o.yellow(" hooks.json: parse error"));}else console.log(o.gray(" hooks.json: not found"));console.log();}export{te as a,re as b,ae as c};
|