@a-company/paradigm 3.44.0 → 5.3.3
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/{accept-orchestration-ZUWQUHSK.js → accept-orchestration-GX2YRWM4.js} +5 -5
- package/dist/{add-VSPZ6FM4.js → add-FZRKEGH4.js} +1 -1
- package/dist/agent-HYKC2LAK.js +387 -0
- package/dist/agent-loader-SJPJJS33.js +36 -0
- package/dist/{agents-suggest-65SER5IS.js → agents-suggest-DNSYJ6IA.js} +1 -1
- package/dist/{aggregate-SV3VGEIL.js → aggregate-H57K7PNV.js} +1 -1
- package/dist/{assess-UHBDYIK7.js → assess-4WVXZLZQ.js} +2 -2
- package/dist/{auto-24ICVUH4.js → auto-QFS5NHQU.js} +1 -1
- package/dist/{beacon-3SJV4DAP.js → beacon-KXZXYQHX.js} +1 -1
- package/dist/{calibration-WWHK73WU.js → calibration-V46G7JTY.js} +2 -2
- package/dist/{check-OLI6AUS6.js → check-OWAIWV23.js} +1 -1
- package/dist/{chunk-RP6TZYGE.js → chunk-2IO7JAG2.js} +1 -1
- package/dist/chunk-2T6BTYBN.js +712 -0
- package/dist/{chunk-CDMAMDSG.js → chunk-5VKJBNJL.js} +13 -5
- package/dist/{chunk-KB4XJWE3.js → chunk-6N3JTACN.js} +98 -437
- package/dist/chunk-7N7GSU6K.js +34 -0
- package/dist/chunk-A2L4TSLZ.js +526 -0
- package/dist/{chunk-P7XSBJE3.js → chunk-ABVQGRF7.js} +1 -1
- package/dist/{chunk-HIKKOCXY.js → chunk-EI32ZBE6.js} +1 -1
- package/dist/{chunk-QIOCFXDQ.js → chunk-EKGMAM62.js} +1 -1
- package/dist/chunk-EZ3GOCYC.js +132 -0
- package/dist/chunk-GGMI6C2L.js +1075 -0
- package/dist/{chunk-DS5QY37M.js → chunk-GTR2TBIJ.js} +247 -15
- package/dist/{chunk-QDXI2DHR.js → chunk-J2JEQRT3.js} +1 -1
- package/dist/{chunk-AKIMFN6I.js → chunk-JASGXLK3.js} +2 -2
- package/dist/chunk-KVDYJLTC.js +121 -0
- package/dist/{chunk-J4E6K5MG.js → chunk-LSRABQIY.js} +25 -1
- package/dist/chunk-MCMOGQMU.js +145 -0
- package/dist/{chunk-ZXMDA7VB.js → chunk-PDX44BCA.js} +1 -6
- package/dist/{chunk-SOBTKFSP.js → chunk-S2HO5MLR.js} +5 -0
- package/dist/{chunk-2SKXFXIT.js → chunk-S3ORKP3V.js} +10 -15
- package/dist/{chunk-ZMQA6SCO.js → chunk-S6MZ2IEX.js} +628 -228
- package/dist/chunk-TAIJOFOE.js +124 -0
- package/dist/{chunk-FS3WTUHY.js → chunk-TXESEO7Y.js} +6 -6
- package/dist/{chunk-7COU5S2Z.js → chunk-VL67H5IC.js} +1 -1
- package/dist/{chunk-QWA26UNO.js → chunk-WQITYKHM.js} +7 -7
- package/dist/{chunk-MW5DMGBB.js → chunk-YMDLDELF.js} +114 -55
- package/dist/{claude-63ISJAZK.js → claude-FRRWJSTJ.js} +1 -1
- package/dist/{claude-cli-ABML5RHX.js → claude-cli-XJLK2X4L.js} +1 -1
- package/dist/{claude-code-JRLMRPTO.js → claude-code-HTBA4XRB.js} +1 -1
- package/dist/{claude-code-teams-CAJBEFIZ.js → claude-code-teams-T4SP24MD.js} +1 -1
- package/dist/{conductor-HLWYWUVH.js → conductor-PGPDVIVE.js} +1 -1
- package/dist/{config-schema-3YNIFJCJ.js → config-schema-EA4XALGG.js} +4 -2
- package/dist/{constellation-FAGT45TU.js → constellation-A26CCGQS.js} +1 -1
- package/dist/{context-audit-557EO6PK.js → context-audit-RLO3ETRP.js} +8 -5
- package/dist/{cost-XEBADYFT.js → cost-BGM32XJU.js} +1 -1
- package/dist/{cost-UD3WPEKZ.js → cost-VI46A4XL.js} +1 -1
- package/dist/{cursor-cli-QUOOF2N4.js → cursor-cli-JVEZGHWQ.js} +1 -1
- package/dist/{cursorrules-3TKZ4E4R.js → cursorrules-HLIKJJZT.js} +1 -1
- package/dist/decision-loader-WWCLIQPJ.js +20 -0
- package/dist/{delete-RRK4RL6Y.js → delete-KBRPQLPC.js} +2 -2
- package/dist/{diff-IP5CIARP.js → diff-RQLLNAFI.js} +5 -5
- package/dist/{discipline-5F5OVTXB.js → discipline-FA4OZXIS.js} +1 -1
- package/dist/{dist-UXWV4OKX.js → dist-34NA5RS5.js} +1 -1
- package/dist/{dist-5QE2BB2B-X6DYVSUL.js → dist-5QE2BB2B-5S3T6Y3T.js} +1 -1
- package/dist/{dist-CM3MVWWW.js → dist-77JDTVAY.js} +1 -0
- package/dist/{dist-POMVY6WP.js → dist-QK4SQAK7.js} +1 -1
- package/dist/{dist-3RVKEJRT.js → dist-TA6LSC2Q.js} +1 -1
- package/dist/docs-LVLRPBAW.js +155 -0
- package/dist/docs-PBZB7LYP.js +89 -0
- package/dist/{doctor-GKZJU7QG.js → doctor-ULBOHEIC.js} +3 -3
- package/dist/{drift-YGT4LJ7Q.js → drift-R5NRKFHI.js} +1 -1
- package/dist/{echo-A6HD5UP7.js → echo-O2LY7CC2.js} +1 -1
- package/dist/{edit-4CLNN5JG.js → edit-R2HNLMOG.js} +2 -2
- package/dist/event-25OJKDCE.js +31 -0
- package/dist/{export-T7CMMJIB.js → export-IWVL7XLF.js} +1 -1
- package/dist/{flow-UFMPVOEM.js → flow-CRRVV3O3.js} +2 -2
- package/dist/{global-HHUJSBG5.js → global-3NG5JXUB.js} +1 -1
- package/dist/graduate-USAWGBJM.js +160 -0
- package/dist/{graph-YYUXI3F7.js → graph-VHUMAAS6.js} +2 -2
- package/dist/{graph-server-ZPXRSGCW.js → graph-server-YL22VBBN.js} +1 -1
- package/dist/{habits-RG5SVKXP.js → habits-OL5NGPXO.js} +3 -3
- package/dist/{history-CETCSUCP.js → history-WOWC573W.js} +1 -1
- package/dist/{hooks-TCUHQMPF.js → hooks-HFWSCGPV.js} +2 -2
- package/dist/index.js +307 -184
- package/dist/{integrity-MK2OP5TA.js → integrity-IHO4FZTS.js} +1 -1
- package/dist/{integrity-checker-J7YXRTBT.js → integrity-checker-PSKJA5SB.js} +1 -0
- package/dist/journal-loader-5EYSBFFY.js +18 -0
- package/dist/{lint-HYWGS3JJ.js → lint-K6CJGGPH.js} +1 -1
- package/dist/{list-IUCYPGMK.js → list-4YK7QKFF.js} +1 -1
- package/dist/{list-BTLFHSRC.js → list-ENR7Q4CR.js} +2 -2
- package/dist/{lore-loader-VTEEZDX3.js → lore-loader-7NO6N6FT.js} +4 -1
- package/dist/{lore-server-NOOAHKJX.js → lore-server-UNJY5KC3.js} +1 -1
- package/dist/{manual-AFJ2J2V3.js → manual-G6FISID5.js} +1 -1
- package/dist/mcp.js +3954 -479
- package/dist/{migrate-FQVGQNXZ.js → migrate-LS45DNEV.js} +2 -2
- package/dist/{migrate-assessments-JP6Q5KME.js → migrate-assessments-RGH4O6IX.js} +2 -2
- package/dist/nomination-engine-Q4XSXFKT.js +40 -0
- package/dist/notebook-YWIYGEHV.js +155 -0
- package/dist/{orchestrate-A226N6FC.js → orchestrate-XZA33TJC.js} +5 -5
- package/dist/peers-DEOUIZM6.js +82 -0
- package/dist/persona-UHAHIVST.js +390 -0
- package/dist/{pipeline-3G2FRAKM.js → pipeline-L4HCSBGN.js} +1 -1
- package/dist/{platform-server-KHL6ZPPN.js → platform-server-PMD57BEG.js} +264 -18
- package/dist/{plugin-update-checker-HMRPGY5Z.js → plugin-update-checker-ELOEEQYS.js} +1 -0
- package/dist/{portal-check-FF5EKZE5.js → portal-check-NPYGII2D.js} +2 -2
- package/dist/{portal-compliance-VU4NIFEN.js → portal-compliance-J7DGAPFX.js} +2 -2
- package/dist/{probe-7JK7IDNI.js → probe-MHL5HQZ2.js} +3 -3
- package/dist/{promote-XO63XMAN.js → promote-F6ZYZZAL.js} +2 -2
- package/dist/{providers-YNFSL6HK.js → providers-GK7PB2OL.js} +2 -2
- package/dist/{quiz-I75NU2QQ.js → quiz-M66SC7F7.js} +1 -1
- package/dist/{record-46CLR4OG.js → record-RA4WR2BO.js} +2 -2
- package/dist/{reindex-WIJMCJ4A.js → reindex-HRA2AUS6.js} +3 -2
- package/dist/{remember-4EUZKIIB.js → remember-HBWJ655S.js} +1 -1
- package/dist/{retag-KC4JVRLE.js → retag-3OLCVDEQ.js} +2 -2
- package/dist/{review-Q7M4CRB5.js → review-27ATYTD2.js} +2 -2
- package/dist/review-57QMURZV.js +334 -0
- package/dist/{ripple-RI3LOT6R.js → ripple-JPBXP5I3.js} +1 -1
- package/dist/{sentinel-UOIGJWHH.js → sentinel-4XIG4STA.js} +2 -2
- package/dist/{sentinel-bridge-APDXYAZS.js → sentinel-bridge-MDUXTQRL.js} +2 -2
- package/dist/{serve-JVXSRSUB.js → serve-FLTFTM3P.js} +2 -2
- package/dist/{serve-22A4XOIG.js → serve-INL7SNBK.js} +2 -2
- package/dist/{serve-2YJ6D2Y6.js → serve-KBSE36PL.js} +4 -4
- package/dist/{server-JV6UFGWZ.js → server-54SKYFFY.js} +2 -2
- package/dist/{server-RDLQ3DK7.js → server-XUOIO7E6.js} +1 -1
- package/dist/{setup-YNZJQLW7.js → setup-EDS27WUR.js} +1 -1
- package/dist/{setup-M2ZKLKNN.js → setup-KO5AFC4K.js} +2 -2
- package/dist/{shift-LNMKFYLR.js → shift-VFG23DLA.js} +16 -16
- package/dist/{show-P7GYO43X.js → show-5PV5KFJE.js} +2 -2
- package/dist/{show-PKZMYKRN.js → show-NQKYX6WQ.js} +1 -1
- package/dist/{snapshot-Y3COXK4T.js → snapshot-BK4RBPCG.js} +1 -1
- package/dist/{spawn-SSXZX45U.js → spawn-AW6GDECS.js} +3 -3
- package/dist/{status-KLHALGW4.js → status-WGIAQODY.js} +1 -1
- package/dist/{summary-5NQNOD3F.js → summary-NIRABMF5.js} +2 -2
- package/dist/{sweep-EZU3GU6S.js → sweep-QMHNSIY5.js} +2 -2
- package/dist/{switch-WYUMVNA5.js → switch-6EJPZDIA.js} +1 -1
- package/dist/{symphony-EYRGGVNE.js → symphony-4OCY36AI.js} +350 -29
- package/dist/{symphony-QWOEKZMC.js → symphony-B75X2MME.js} +20 -2
- package/dist/symphony-peers-2ZQYLRNI.js +34 -0
- package/dist/symphony-peers-OL7F6M5S.js +121 -0
- package/dist/symphony-relay-UJYUXN65.js +710 -0
- package/dist/{sync-ZM4Q3R4U.js → sync-VEHUH4OA.js} +3 -3
- package/dist/{sync-llms-JIPP3XX4.js → sync-llms-YHCFIE6X.js} +2 -2
- package/dist/{task-loader-7M2FCBX6.js → task-loader-LDYWQSLM.js} +1 -0
- package/dist/{team-HGLJXWQG.js → team-7HG7XK5C.js} +6 -6
- package/dist/{test-WTR5Q33E.js → test-566CP5KC.js} +1 -1
- package/dist/{thread-3WM7KKID.js → thread-N754I4D5.js} +1 -1
- package/dist/{timeline-ANC7LVDL.js → timeline-M3CICQFE.js} +2 -2
- package/dist/{triage-IZ4MDYNB.js → triage-HHYGT3HY.js} +1 -1
- package/dist/{tutorial-GC6QL4US.js → tutorial-KD22SUNO.js} +1 -1
- package/dist/university-content/courses/.purpose +66 -0
- package/dist/university-content/courses/para-401.json +146 -0
- package/dist/university-content/courses/para-501.json +151 -0
- package/dist/university-content/courses/para-601.json +608 -0
- package/dist/university-content/plsat/.purpose +6 -0
- package/dist/university-content/plsat/v2.0.json +2 -2
- package/dist/university-content/plsat/v3.0.json +563 -3
- package/dist/university-content/reference.json +91 -0
- package/dist/{upgrade-ANX3LVSA.js → upgrade-H5PF32BW.js} +2 -2
- package/dist/{validate-GD5XWILV.js → validate-CNKEKO6A.js} +1 -1
- package/dist/{validate-ITC5D6QG.js → validate-MB5ULIHS.js} +1 -1
- package/dist/{validate-ZVPNN4FL.js → validate-QH3LADM6.js} +1 -1
- package/dist/{watch-X64UK7K4.js → watch-2TKP5PVL.js} +3 -3
- package/dist/{watch-ERBEJUJW.js → watch-ZF4ML6CD.js} +2 -2
- package/dist/{wisdom-L2WC7J62.js → wisdom-AATMGNFA.js} +1 -1
- package/dist/work-log-loader-5L45XNYZ.js +14 -0
- package/dist/{workspace-UIUTHZTD.js → workspace-6E6OSRNU.js} +4 -4
- package/package.json +1 -1
- package/platform-ui/dist/assets/DocsSection-ByAgPzWV.js +1 -0
- package/platform-ui/dist/assets/DocsSection-CjdO6R-u.css +1 -0
- package/platform-ui/dist/assets/{GitSection-BD3Ze06e.js → GitSection-BLovj9yT.js} +1 -1
- package/platform-ui/dist/assets/{GraphSection-SglITfSs.js → GraphSection-C5PCPUFl.js} +1 -1
- package/platform-ui/dist/assets/{LoreSection-bR5Km4Fd.js → LoreSection-BftejTla.js} +1 -1
- package/platform-ui/dist/assets/{SentinelSection-QSpAZArG.js → SentinelSection-CnYcasN7.js} +1 -1
- package/platform-ui/dist/assets/{SymphonySection-CobYJgvg.js → SymphonySection-BpmqCHeK.js} +1 -1
- package/platform-ui/dist/assets/{index-DbxeSMkV.js → index-G9JnWEs_.js} +10 -10
- package/platform-ui/dist/index.html +1 -1
- package/dist/dist-PSF5CP4I.js +0 -7294
|
@@ -16,11 +16,16 @@ var COMMON_HOOK = `#!/bin/sh
|
|
|
16
16
|
# CWD \u2014 Project root directory (already cd'd into)
|
|
17
17
|
# MODIFIED \u2014 Output of \`git diff --name-only HEAD\`
|
|
18
18
|
#
|
|
19
|
+
# Caller may set:
|
|
20
|
+
# PARADIGM_AUTO_FIX \u2014 Set to "1" to auto-fix trivial violations (missing .purpose stubs, missing lore)
|
|
21
|
+
#
|
|
19
22
|
# Sets:
|
|
20
23
|
# VIOLATIONS \u2014 Newline-separated violation messages
|
|
21
24
|
# VIOLATION_COUNT \u2014 Number of violations
|
|
22
25
|
# ADVISORY \u2014 Non-blocking advisory text
|
|
23
26
|
# SOURCE_COUNT \u2014 Number of modified source files
|
|
27
|
+
# AUTO_FIXED \u2014 Newline-separated auto-fix actions taken
|
|
28
|
+
# AUTO_FIX_COUNT \u2014 Number of auto-fixes applied
|
|
24
29
|
#
|
|
25
30
|
# Checks:
|
|
26
31
|
# 1. Source files modified without .purpose updates (threshold: 2+)
|
|
@@ -37,6 +42,9 @@ var COMMON_HOOK = `#!/bin/sh
|
|
|
37
42
|
|
|
38
43
|
VIOLATIONS=""
|
|
39
44
|
VIOLATION_COUNT=0
|
|
45
|
+
AUTO_FIXED=""
|
|
46
|
+
AUTO_FIX_COUNT=0
|
|
47
|
+
PARADIGM_AUTO_FIX="\${PARADIGM_AUTO_FIX:-0}"
|
|
40
48
|
|
|
41
49
|
# --- Check 1: Source files modified without .purpose updates ---
|
|
42
50
|
SOURCE_COUNT=0
|
|
@@ -95,11 +103,28 @@ for file in $MODIFIED; do
|
|
|
95
103
|
done
|
|
96
104
|
|
|
97
105
|
if [ -n "$DIRS_WITHOUT_PURPOSE" ]; then
|
|
98
|
-
|
|
106
|
+
if [ "$PARADIGM_AUTO_FIX" = "1" ]; then
|
|
107
|
+
# Auto-fix: create stub .purpose files for directories missing them
|
|
108
|
+
for dir in $DIRS_WITHOUT_PURPOSE; do
|
|
109
|
+
dir_basename=$(basename "$dir")
|
|
110
|
+
cat > "$dir/.purpose" <<PURPOSEEOF
|
|
111
|
+
# Auto-generated .purpose stub \u2014 update with real descriptions
|
|
112
|
+
components:
|
|
113
|
+
$dir_basename:
|
|
114
|
+
description: "TODO: describe this component"
|
|
115
|
+
tags: []
|
|
116
|
+
PURPOSEEOF
|
|
117
|
+
AUTO_FIXED="$AUTO_FIXED
|
|
118
|
+
- Created stub .purpose in $dir (update descriptions)"
|
|
119
|
+
AUTO_FIX_COUNT=$((AUTO_FIX_COUNT + 1))
|
|
120
|
+
done
|
|
121
|
+
else
|
|
122
|
+
VIOLATIONS="$VIOLATIONS
|
|
99
123
|
- These directories have modified source files but no .purpose file anywhere in their path:
|
|
100
124
|
$DIRS_WITHOUT_PURPOSE
|
|
101
125
|
Create a .purpose file using paradigm_purpose_init + paradigm_purpose_add_component."
|
|
102
|
-
|
|
126
|
+
VIOLATION_COUNT=$((VIOLATION_COUNT + 1))
|
|
127
|
+
fi
|
|
103
128
|
fi
|
|
104
129
|
|
|
105
130
|
# --- Check 3: Route patterns added without portal.yaml ---
|
|
@@ -252,11 +277,46 @@ if [ "$SOURCE_COUNT" -ge 3 ] && [ -d ".paradigm/lore" ]; then
|
|
|
252
277
|
fi
|
|
253
278
|
|
|
254
279
|
if [ "$LORE_RECORDED" = false ]; then
|
|
255
|
-
|
|
280
|
+
if [ "$PARADIGM_AUTO_FIX" = "1" ]; then
|
|
281
|
+
# Auto-fix: create a stub lore entry with modified file info
|
|
282
|
+
TODAY=$(date -u +"%Y-%m-%d" 2>/dev/null || date +"%Y-%m-%d")
|
|
283
|
+
LORE_TIMESTAMP=$(date -u +"%H%M%S" 2>/dev/null || date +"%H%M%S")
|
|
284
|
+
LORE_DIR=".paradigm/lore/entries/$TODAY"
|
|
285
|
+
mkdir -p "$LORE_DIR" 2>/dev/null
|
|
286
|
+
LORE_ID="L-\${TODAY}-auto-\${LORE_TIMESTAMP}"
|
|
287
|
+
LORE_FILE="$LORE_DIR/\${LORE_ID}.yaml"
|
|
288
|
+
|
|
289
|
+
# Extract symbols from modified file paths (directory basenames as component names)
|
|
290
|
+
LORE_SYMBOLS=""
|
|
291
|
+
for file in $MODIFIED; do
|
|
292
|
+
case "$file" in
|
|
293
|
+
.paradigm/*|*.md|*.lock|*.log|.gitignore|.env*|*.json|*.purpose|portal.yaml) continue ;;
|
|
294
|
+
esac
|
|
295
|
+
sym_dir=$(basename "$(dirname "$file")")
|
|
296
|
+
case "$LORE_SYMBOLS" in
|
|
297
|
+
*"#$sym_dir"*) ;;
|
|
298
|
+
*) LORE_SYMBOLS="$LORE_SYMBOLS \\"#$sym_dir\\"" ;;
|
|
299
|
+
esac
|
|
300
|
+
done
|
|
301
|
+
|
|
302
|
+
cat > "$LORE_FILE" <<LOREEOF
|
|
303
|
+
id: "$LORE_ID"
|
|
304
|
+
type: agent-session
|
|
305
|
+
title: "Auto-recorded session \u2014 $SOURCE_COUNT files modified"
|
|
306
|
+
summary: "Session modified $SOURCE_COUNT source files. Auto-recorded by stop hook."
|
|
307
|
+
symbols_touched: [$(echo $LORE_SYMBOLS | sed 's/^ //')]
|
|
308
|
+
timestamp: "$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date +"%Y-%m-%dT%H:%M:%SZ")"
|
|
309
|
+
LOREEOF
|
|
310
|
+
AUTO_FIXED="$AUTO_FIXED
|
|
311
|
+
- Created stub lore entry $LORE_ID (update with real summary)"
|
|
312
|
+
AUTO_FIX_COUNT=$((AUTO_FIX_COUNT + 1))
|
|
313
|
+
else
|
|
314
|
+
VIOLATIONS="$VIOLATIONS
|
|
256
315
|
- You modified $SOURCE_COUNT source files but recorded no lore entry.
|
|
257
316
|
Record your session: paradigm_lore_record (MCP) or paradigm lore record (CLI).
|
|
258
317
|
Include: type, title, summary, and symbols_touched."
|
|
259
|
-
|
|
318
|
+
VIOLATION_COUNT=$((VIOLATION_COUNT + 1))
|
|
319
|
+
fi
|
|
260
320
|
fi
|
|
261
321
|
fi
|
|
262
322
|
|
|
@@ -350,11 +410,28 @@ if [ -f "$CONFIG_FILE" ]; then
|
|
|
350
410
|
fi
|
|
351
411
|
|
|
352
412
|
if [ -n "$MISSING_REQUIRED" ]; then
|
|
353
|
-
|
|
413
|
+
if [ "$PARADIGM_AUTO_FIX" = "1" ]; then
|
|
414
|
+
# Auto-fix: create stub .purpose files for required patterns
|
|
415
|
+
for dir in $MISSING_REQUIRED; do
|
|
416
|
+
dir_basename=$(basename "$dir")
|
|
417
|
+
cat > "$dir/.purpose" <<PURPOSEEOF
|
|
418
|
+
# Auto-generated .purpose stub (purpose-required) \u2014 update with real descriptions
|
|
419
|
+
components:
|
|
420
|
+
$dir_basename:
|
|
421
|
+
description: "TODO: describe this component"
|
|
422
|
+
tags: []
|
|
423
|
+
PURPOSEEOF
|
|
424
|
+
AUTO_FIXED="$AUTO_FIXED
|
|
425
|
+
- Created stub .purpose in $dir (purpose-required pattern)"
|
|
426
|
+
AUTO_FIX_COUNT=$((AUTO_FIX_COUNT + 1))
|
|
427
|
+
done
|
|
428
|
+
else
|
|
429
|
+
VIOLATIONS="$VIOLATIONS
|
|
354
430
|
- These directories match purpose-required patterns but have no .purpose file:
|
|
355
431
|
$MISSING_REQUIRED
|
|
356
432
|
Create .purpose files: paradigm_purpose_init + paradigm_purpose_add_component."
|
|
357
|
-
|
|
433
|
+
VIOLATION_COUNT=$((VIOLATION_COUNT + 1))
|
|
434
|
+
fi
|
|
358
435
|
fi
|
|
359
436
|
fi
|
|
360
437
|
|
|
@@ -406,6 +483,54 @@ if [ -f "portal.yaml" ]; then
|
|
|
406
483
|
fi
|
|
407
484
|
fi
|
|
408
485
|
fi
|
|
486
|
+
|
|
487
|
+
# --- Check 12: Graduation failure tracking ---
|
|
488
|
+
# When violations occur for graduated habits, record failures for auto-demotion.
|
|
489
|
+
if [ "$VIOLATION_COUNT" -gt 0 ] && [ -f ".paradigm/graduation.yaml" ]; then
|
|
490
|
+
GRAD_FAILURES_DIR=".paradigm/.graduation-failures"
|
|
491
|
+
mkdir -p "$GRAD_FAILURES_DIR" 2>/dev/null
|
|
492
|
+
|
|
493
|
+
# Map violations to graduated habit IDs
|
|
494
|
+
# Check 1/2/5 \u2192 purpose-coverage, Check 3/11 \u2192 gates-for-routes, Check 7 \u2192 record-lore-for-significant
|
|
495
|
+
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date +%Y-%m-%dT%H:%M:%SZ)
|
|
496
|
+
|
|
497
|
+
# Purpose coverage violations \u2192 purpose-coverage habit
|
|
498
|
+
if echo "$VIOLATIONS" | grep -q "source file.*without .purpose\\|missing .purpose\\|purpose.*stale" 2>/dev/null; then
|
|
499
|
+
if grep -q "purpose-coverage" ".paradigm/graduation.yaml" 2>/dev/null && grep -A1 "purpose-coverage" ".paradigm/graduation.yaml" | grep -q "tier: hook" 2>/dev/null; then
|
|
500
|
+
echo "$NOW" >> "$GRAD_FAILURES_DIR/purpose-coverage"
|
|
501
|
+
fi
|
|
502
|
+
fi
|
|
503
|
+
|
|
504
|
+
# Portal gate violations \u2192 gates-for-routes habit
|
|
505
|
+
if echo "$VIOLATIONS" | grep -q "portal.yaml\\|gate.*undeclared\\|gate.*not declared" 2>/dev/null; then
|
|
506
|
+
if grep -q "gates-for-routes" ".paradigm/graduation.yaml" 2>/dev/null && grep -A1 "gates-for-routes" ".paradigm/graduation.yaml" | grep -q "tier: hook" 2>/dev/null; then
|
|
507
|
+
echo "$NOW" >> "$GRAD_FAILURES_DIR/gates-for-routes"
|
|
508
|
+
fi
|
|
509
|
+
fi
|
|
510
|
+
|
|
511
|
+
# Lore entry violations \u2192 record-lore-for-significant habit
|
|
512
|
+
if echo "$VIOLATIONS" | grep -q "lore entry expected\\|no lore" 2>/dev/null; then
|
|
513
|
+
if grep -q "record-lore-for-significant" ".paradigm/graduation.yaml" 2>/dev/null && grep -A1 "record-lore-for-significant" ".paradigm/graduation.yaml" | grep -q "tier: hook" 2>/dev/null; then
|
|
514
|
+
echo "$NOW" >> "$GRAD_FAILURES_DIR/record-lore-for-significant"
|
|
515
|
+
fi
|
|
516
|
+
fi
|
|
517
|
+
|
|
518
|
+
# Count recent failures and emit advisory if approaching demotion threshold
|
|
519
|
+
for fail_file in "$GRAD_FAILURES_DIR"/*; do
|
|
520
|
+
[ -f "$fail_file" ] || continue
|
|
521
|
+
habit_id=$(basename "$fail_file")
|
|
522
|
+
fail_count=$(wc -l < "$fail_file" | tr -d ' ')
|
|
523
|
+
if [ "$fail_count" -ge 3 ]; then
|
|
524
|
+
ADVISORY="$ADVISORY
|
|
525
|
+
- Graduated habit '$habit_id' has $fail_count failures \u2014 auto-demotion triggered.
|
|
526
|
+
Run paradigm_graduate_status to review tier changes."
|
|
527
|
+
elif [ "$fail_count" -ge 2 ]; then
|
|
528
|
+
ADVISORY="$ADVISORY
|
|
529
|
+
- Graduated habit '$habit_id' has $fail_count failures (demotion at 3).
|
|
530
|
+
Fix the underlying issue or it will be demoted to habit tier."
|
|
531
|
+
fi
|
|
532
|
+
done
|
|
533
|
+
fi
|
|
409
534
|
`;
|
|
410
535
|
var CLAUDE_CODE_STOP_HOOK = `#!/bin/sh
|
|
411
536
|
# Paradigm Claude Code Stop Hook (v2)
|
|
@@ -441,8 +566,9 @@ cd "$CWD" || exit 0
|
|
|
441
566
|
# Get modified files (uncommitted changes)
|
|
442
567
|
MODIFIED=$(git diff --name-only HEAD 2>/dev/null)
|
|
443
568
|
if [ -z "$MODIFIED" ]; then
|
|
444
|
-
# Clean up
|
|
569
|
+
# Clean up session markers on pass (no modifications)
|
|
445
570
|
rm -f ".paradigm/.pending-review"
|
|
571
|
+
rm -f ".paradigm/.session-started"
|
|
446
572
|
exit 0
|
|
447
573
|
fi
|
|
448
574
|
|
|
@@ -450,8 +576,19 @@ fi
|
|
|
450
576
|
SCRIPT_DIR=$(dirname "$0")
|
|
451
577
|
. "$SCRIPT_DIR/paradigm-common.sh"
|
|
452
578
|
|
|
579
|
+
# --- Report auto-fixes if any ---
|
|
580
|
+
if [ "$AUTO_FIX_COUNT" -gt 0 ]; then
|
|
581
|
+
echo "" >&2
|
|
582
|
+
echo "[paradigm] Auto-fixed $AUTO_FIX_COUNT issue(s):" >&2
|
|
583
|
+
echo "$AUTO_FIXED" >&2
|
|
584
|
+
fi
|
|
585
|
+
|
|
453
586
|
# --- Final verdict ---
|
|
454
587
|
if [ "$VIOLATION_COUNT" -gt 0 ]; then
|
|
588
|
+
# Emit compliance-violation event (fire-and-forget)
|
|
589
|
+
if command -v paradigm >/dev/null 2>&1; then
|
|
590
|
+
paradigm event emit --type compliance-violation --source stop-hook --severity error --context "Stop hook: $VIOLATION_COUNT violation(s)" &
|
|
591
|
+
fi
|
|
455
592
|
echo "" >&2
|
|
456
593
|
echo "Paradigm compliance check failed ($VIOLATION_COUNT violation(s)):" >&2
|
|
457
594
|
echo "$VIOLATIONS" >&2
|
|
@@ -468,6 +605,8 @@ if [ "$VIOLATION_COUNT" -gt 0 ]; then
|
|
|
468
605
|
echo " 4. paradigm_reindex \u2014 rebuild indexes after updates" >&2
|
|
469
606
|
echo " 5. paradigm_lore_record \u2014 record session lore entry" >&2
|
|
470
607
|
echo " 6. paradigm_habits_check \u2014 evaluate habit compliance" >&2
|
|
608
|
+
echo "" >&2
|
|
609
|
+
echo "Tip: Set PARADIGM_AUTO_FIX=1 to auto-fix trivial violations (missing .purpose stubs, missing lore)." >&2
|
|
471
610
|
exit 2
|
|
472
611
|
fi
|
|
473
612
|
|
|
@@ -478,9 +617,26 @@ if [ -n "$ADVISORY" ]; then
|
|
|
478
617
|
echo "$ADVISORY" >&2
|
|
479
618
|
fi
|
|
480
619
|
|
|
481
|
-
#
|
|
620
|
+
# Auto-demote graduated habits with 3+ failures
|
|
621
|
+
if [ -d ".paradigm/.graduation-failures" ]; then
|
|
622
|
+
for fail_file in .paradigm/.graduation-failures/*; do
|
|
623
|
+
[ -f "$fail_file" ] || continue
|
|
624
|
+
habit_id=$(basename "$fail_file")
|
|
625
|
+
fail_count=$(wc -l < "$fail_file" | tr -d ' ')
|
|
626
|
+
if [ "$fail_count" -ge 3 ]; then
|
|
627
|
+
if command -v paradigm >/dev/null 2>&1; then
|
|
628
|
+
paradigm graduate demote "$habit_id" --cooldown 14 2>/dev/null || true
|
|
629
|
+
fi
|
|
630
|
+
rm -f "$fail_file"
|
|
631
|
+
echo "[paradigm] Auto-demoted '$habit_id' after $fail_count failures." >&2
|
|
632
|
+
fi
|
|
633
|
+
done
|
|
634
|
+
fi
|
|
635
|
+
|
|
636
|
+
# Clean up session markers on pass
|
|
482
637
|
rm -f ".paradigm/.pending-review"
|
|
483
638
|
rm -f ".paradigm/.habits-blocking"
|
|
639
|
+
rm -f ".paradigm/.session-started"
|
|
484
640
|
|
|
485
641
|
exit 0
|
|
486
642
|
`;
|
|
@@ -493,17 +649,23 @@ var CLAUDE_CODE_POSTWRITE_HOOK = `#!/bin/sh
|
|
|
493
649
|
#
|
|
494
650
|
# Hook type: PostToolUse (matcher: Edit,Write)
|
|
495
651
|
# Exit 0 always (never blocks \u2014 advisory only)
|
|
652
|
+
#
|
|
653
|
+
# NOTE: stdin JSON can be 8KB+ (tool_response includes full file contents).
|
|
654
|
+
# Using echo "$INPUT" | jq corrupts the JSON via shell string handling.
|
|
655
|
+
# Fix: write stdin to temp file, use jq < file for all extractions.
|
|
496
656
|
|
|
497
|
-
#
|
|
498
|
-
|
|
657
|
+
# Save stdin to temp file \u2014 avoids echo corruption on large JSON
|
|
658
|
+
TMPINPUT=$(mktemp)
|
|
659
|
+
trap 'rm -f "$TMPINPUT"' EXIT
|
|
660
|
+
cat > "$TMPINPUT"
|
|
499
661
|
|
|
500
662
|
# Extract the file path from tool_input
|
|
501
663
|
if command -v jq >/dev/null 2>&1; then
|
|
502
|
-
FILE_PATH=$(
|
|
664
|
+
FILE_PATH=$(jq -r '.tool_input.file_path // .tool_input.filePath // empty' < "$TMPINPUT" 2>/dev/null)
|
|
503
665
|
else
|
|
504
|
-
FILE_PATH=$(
|
|
666
|
+
FILE_PATH=$(grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' "$TMPINPUT" | head -1 | sed 's/.*"file_path"[[:space:]]*:[[:space:]]*"//' | sed 's/"$//')
|
|
505
667
|
if [ -z "$FILE_PATH" ]; then
|
|
506
|
-
FILE_PATH=$(
|
|
668
|
+
FILE_PATH=$(grep -o '"filePath"[[:space:]]*:[[:space:]]*"[^"]*"' "$TMPINPUT" | head -1 | sed 's/.*"filePath"[[:space:]]*:[[:space:]]*"//' | sed 's/"$//')
|
|
507
669
|
fi
|
|
508
670
|
fi
|
|
509
671
|
|
|
@@ -511,6 +673,16 @@ if [ -z "$FILE_PATH" ]; then
|
|
|
511
673
|
exit 0
|
|
512
674
|
fi
|
|
513
675
|
|
|
676
|
+
# Extract cwd from input
|
|
677
|
+
if command -v jq >/dev/null 2>&1; then
|
|
678
|
+
CWD=$(jq -r '.cwd // empty' < "$TMPINPUT" 2>/dev/null)
|
|
679
|
+
else
|
|
680
|
+
CWD=$(grep -o '"cwd"[[:space:]]*:[[:space:]]*"[^"]*"' "$TMPINPUT" | sed 's/.*"cwd"[[:space:]]*:[[:space:]]*"//' | sed 's/"$//')
|
|
681
|
+
fi
|
|
682
|
+
if [ -n "$CWD" ]; then
|
|
683
|
+
cd "$CWD" || exit 0
|
|
684
|
+
fi
|
|
685
|
+
|
|
514
686
|
# Skip non-source files
|
|
515
687
|
case "$FILE_PATH" in
|
|
516
688
|
*.purpose|portal.yaml|*.md|*.lock|*.log|*.json|*.yaml|*.yml|.gitignore|.env*) exit 0 ;;
|
|
@@ -526,6 +698,15 @@ if [ ! -d ".paradigm" ]; then
|
|
|
526
698
|
exit 0
|
|
527
699
|
fi
|
|
528
700
|
|
|
701
|
+
# Pseudo-session-start: first edit of session emits one-time guidance
|
|
702
|
+
if [ ! -f ".paradigm/.session-started" ]; then
|
|
703
|
+
PREV_PENDING=$(cat .paradigm/.pending-review 2>/dev/null | wc -l | tr -d ' ')
|
|
704
|
+
if [ "$PREV_PENDING" -gt 0 ] 2>/dev/null; then
|
|
705
|
+
echo "[paradigm] Session started. $PREV_PENDING uncovered edit(s) from last session." >&2
|
|
706
|
+
fi
|
|
707
|
+
touch ".paradigm/.session-started"
|
|
708
|
+
fi
|
|
709
|
+
|
|
529
710
|
# Convert to relative path (strip project root prefix)
|
|
530
711
|
PROJECT_ROOT="$(pwd)"
|
|
531
712
|
REL_PATH="$FILE_PATH"
|
|
@@ -538,6 +719,11 @@ case "$REL_PATH" in
|
|
|
538
719
|
/*) exit 0 ;;
|
|
539
720
|
esac
|
|
540
721
|
|
|
722
|
+
# Emit file-modified event (fire-and-forget)
|
|
723
|
+
if command -v paradigm >/dev/null 2>&1; then
|
|
724
|
+
paradigm event emit --type file-modified --source post-write-hook --path "$REL_PATH" &
|
|
725
|
+
fi
|
|
726
|
+
|
|
541
727
|
# Track: append to .paradigm/.pending-review (deduplicated)
|
|
542
728
|
PENDING_FILE=".paradigm/.pending-review"
|
|
543
729
|
if [ -f "$PENDING_FILE" ]; then
|
|
@@ -581,6 +767,11 @@ elif [ "$PENDING_COUNT" -gt 0 ] && [ "$((PENDING_COUNT % 3))" -eq 0 ]; then
|
|
|
581
767
|
echo " The stop hook WILL BLOCK if .purpose files aren't updated." >&2
|
|
582
768
|
fi
|
|
583
769
|
|
|
770
|
+
# Context budget heuristic: suggest handoff check at high edit counts
|
|
771
|
+
if [ "$PENDING_COUNT" -ge 30 ]; then
|
|
772
|
+
echo "[paradigm] ~$PENDING_COUNT edits this session. Consider preparing handoff." >&2
|
|
773
|
+
fi
|
|
774
|
+
|
|
584
775
|
exit 0
|
|
585
776
|
`;
|
|
586
777
|
var CLAUDE_CODE_PRECOMMIT_HOOK = `#!/bin/sh
|
|
@@ -784,8 +975,9 @@ fi
|
|
|
784
975
|
# Get modified files (uncommitted changes)
|
|
785
976
|
MODIFIED=$(git diff --name-only HEAD 2>/dev/null)
|
|
786
977
|
if [ -z "$MODIFIED" ]; then
|
|
787
|
-
# Clean up
|
|
978
|
+
# Clean up session markers on pass (no modifications)
|
|
788
979
|
rm -f ".paradigm/.pending-review"
|
|
980
|
+
rm -f ".paradigm/.session-started"
|
|
789
981
|
exit 0
|
|
790
982
|
fi
|
|
791
983
|
|
|
@@ -793,6 +985,13 @@ fi
|
|
|
793
985
|
SCRIPT_DIR=$(dirname "$0")
|
|
794
986
|
. "$SCRIPT_DIR/paradigm-common.sh"
|
|
795
987
|
|
|
988
|
+
# --- Report auto-fixes if any ---
|
|
989
|
+
if [ "$AUTO_FIX_COUNT" -gt 0 ]; then
|
|
990
|
+
echo "" >&2
|
|
991
|
+
echo "[paradigm] Auto-fixed $AUTO_FIX_COUNT issue(s):" >&2
|
|
992
|
+
echo "$AUTO_FIXED" >&2
|
|
993
|
+
fi
|
|
994
|
+
|
|
796
995
|
# --- Final verdict ---
|
|
797
996
|
if [ "$VIOLATION_COUNT" -gt 0 ]; then
|
|
798
997
|
echo "" >&2
|
|
@@ -811,6 +1010,8 @@ if [ "$VIOLATION_COUNT" -gt 0 ]; then
|
|
|
811
1010
|
echo " 4. paradigm_reindex \u2014 rebuild indexes after updates" >&2
|
|
812
1011
|
echo " 5. paradigm_lore_record \u2014 record session lore entry" >&2
|
|
813
1012
|
echo " 6. paradigm_habits_check \u2014 evaluate habit compliance" >&2
|
|
1013
|
+
echo "" >&2
|
|
1014
|
+
echo "Tip: Set PARADIGM_AUTO_FIX=1 to auto-fix trivial violations (missing .purpose stubs, missing lore)." >&2
|
|
814
1015
|
|
|
815
1016
|
# Output followup_message JSON to stdout for Cursor's compliance loop.
|
|
816
1017
|
# Cursor auto-submits this as the next user message, creating a retry loop.
|
|
@@ -828,10 +1029,27 @@ if [ -n "$ADVISORY" ]; then
|
|
|
828
1029
|
echo "$ADVISORY" >&2
|
|
829
1030
|
fi
|
|
830
1031
|
|
|
831
|
-
#
|
|
1032
|
+
# Auto-demote graduated habits with 3+ failures
|
|
1033
|
+
if [ -d ".paradigm/.graduation-failures" ]; then
|
|
1034
|
+
for fail_file in .paradigm/.graduation-failures/*; do
|
|
1035
|
+
[ -f "$fail_file" ] || continue
|
|
1036
|
+
habit_id=$(basename "$fail_file")
|
|
1037
|
+
fail_count=$(wc -l < "$fail_file" | tr -d ' ')
|
|
1038
|
+
if [ "$fail_count" -ge 3 ]; then
|
|
1039
|
+
if command -v paradigm >/dev/null 2>&1; then
|
|
1040
|
+
paradigm graduate demote "$habit_id" --cooldown 14 2>/dev/null || true
|
|
1041
|
+
fi
|
|
1042
|
+
rm -f "$fail_file"
|
|
1043
|
+
echo "[paradigm] Auto-demoted '$habit_id' after $fail_count failures." >&2
|
|
1044
|
+
fi
|
|
1045
|
+
done
|
|
1046
|
+
fi
|
|
1047
|
+
|
|
1048
|
+
# Clean up session markers and loop guard on pass
|
|
832
1049
|
rm -f ".paradigm/.pending-review"
|
|
833
1050
|
rm -f ".paradigm/.habits-blocking"
|
|
834
1051
|
rm -f ".paradigm/.stop-hook-active"
|
|
1052
|
+
rm -f ".paradigm/.session-started"
|
|
835
1053
|
|
|
836
1054
|
exit 0
|
|
837
1055
|
`;
|
|
@@ -1166,6 +1384,15 @@ fi
|
|
|
1166
1384
|
|
|
1167
1385
|
cd "$CWD" || exit 0
|
|
1168
1386
|
|
|
1387
|
+
# Pseudo-session-start: first edit of session emits one-time guidance
|
|
1388
|
+
if [ ! -f ".paradigm/.session-started" ]; then
|
|
1389
|
+
PREV_PENDING=$(cat .paradigm/.pending-review 2>/dev/null | wc -l | tr -d ' ')
|
|
1390
|
+
if [ "$PREV_PENDING" -gt 0 ] 2>/dev/null; then
|
|
1391
|
+
echo "[paradigm] Session started. $PREV_PENDING uncovered edit(s) from last session." >&2
|
|
1392
|
+
fi
|
|
1393
|
+
touch ".paradigm/.session-started"
|
|
1394
|
+
fi
|
|
1395
|
+
|
|
1169
1396
|
# Convert to relative path
|
|
1170
1397
|
REL_PATH="$FILE_PATH"
|
|
1171
1398
|
case "$FILE_PATH" in
|
|
@@ -1230,6 +1457,11 @@ elif [ "$PENDING_COUNT" -gt 0 ] && [ "$((PENDING_COUNT % 3))" -eq 0 ]; then
|
|
|
1230
1457
|
echo " The stop hook WILL BLOCK if .purpose files aren't updated." >&2
|
|
1231
1458
|
fi
|
|
1232
1459
|
|
|
1460
|
+
# Context budget heuristic: suggest handoff check at high edit counts
|
|
1461
|
+
if [ "$PENDING_COUNT" -ge 30 ]; then
|
|
1462
|
+
echo "[paradigm] ~$PENDING_COUNT edits this session. Consider preparing handoff." >&2
|
|
1463
|
+
fi
|
|
1464
|
+
|
|
1233
1465
|
exit 0
|
|
1234
1466
|
`;
|
|
1235
1467
|
|
|
@@ -10,7 +10,7 @@ async function graphCommand(path, options) {
|
|
|
10
10
|
const shouldOpen = options.open !== false;
|
|
11
11
|
console.log(chalk.cyan("\nStarting Symbol Graph...\n"));
|
|
12
12
|
try {
|
|
13
|
-
const { startGraphServer } = await import("./graph-server-
|
|
13
|
+
const { startGraphServer } = await import("./graph-server-YL22VBBN.js");
|
|
14
14
|
console.log(chalk.gray(`Project: ${projectDir}`));
|
|
15
15
|
console.log(chalk.gray(`Port: ${port}`));
|
|
16
16
|
console.log();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
indexCommand
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-VL67H5IC.js";
|
|
5
5
|
import {
|
|
6
6
|
getDefaultPremiseContent
|
|
7
7
|
} from "./chunk-ZGUAAVMA.js";
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
detectIDE,
|
|
20
20
|
loadParadigmFiles,
|
|
21
21
|
syncToIDE
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-6N3JTACN.js";
|
|
23
23
|
import {
|
|
24
24
|
log
|
|
25
25
|
} from "./chunk-4NCFWYGG.js";
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// ../paradigm-mcp/src/utils/symphony-peers.ts
|
|
4
|
+
import * as fs from "fs";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
import * as os from "os";
|
|
7
|
+
import * as crypto from "crypto";
|
|
8
|
+
var SCORE_DIR = path.join(os.homedir(), ".paradigm", "score");
|
|
9
|
+
var PEERS_FILE = path.join(SCORE_DIR, "peers.json");
|
|
10
|
+
var PAIRING_TTL_MS = 5 * 60 * 1e3;
|
|
11
|
+
function loadPeers() {
|
|
12
|
+
try {
|
|
13
|
+
if (!fs.existsSync(PEERS_FILE)) return [];
|
|
14
|
+
const content = fs.readFileSync(PEERS_FILE, "utf-8");
|
|
15
|
+
const parsed = JSON.parse(content);
|
|
16
|
+
if (!Array.isArray(parsed)) return [];
|
|
17
|
+
return parsed;
|
|
18
|
+
} catch {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function savePeers(peers) {
|
|
23
|
+
if (!fs.existsSync(SCORE_DIR)) {
|
|
24
|
+
fs.mkdirSync(SCORE_DIR, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
fs.writeFileSync(PEERS_FILE, JSON.stringify(peers, null, 2), { mode: 384 });
|
|
27
|
+
}
|
|
28
|
+
function findPeer(id) {
|
|
29
|
+
const peers = loadPeers();
|
|
30
|
+
return peers.find((p) => p.id === id) ?? null;
|
|
31
|
+
}
|
|
32
|
+
function addPeer(peer) {
|
|
33
|
+
const peers = loadPeers();
|
|
34
|
+
const idx = peers.findIndex((p) => p.id === peer.id);
|
|
35
|
+
if (idx >= 0) {
|
|
36
|
+
peers[idx] = peer;
|
|
37
|
+
} else {
|
|
38
|
+
peers.push(peer);
|
|
39
|
+
}
|
|
40
|
+
savePeers(peers);
|
|
41
|
+
}
|
|
42
|
+
function revokePeer(id) {
|
|
43
|
+
const peers = loadPeers();
|
|
44
|
+
const peer = peers.find((p) => p.id === id);
|
|
45
|
+
if (!peer) return false;
|
|
46
|
+
peer.revoked = true;
|
|
47
|
+
savePeers(peers);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
function forgetAllPeers() {
|
|
51
|
+
const peers = loadPeers();
|
|
52
|
+
const count = peers.length;
|
|
53
|
+
if (fs.existsSync(PEERS_FILE)) {
|
|
54
|
+
fs.unlinkSync(PEERS_FILE);
|
|
55
|
+
}
|
|
56
|
+
return count;
|
|
57
|
+
}
|
|
58
|
+
function updatePeerLastSeen(id) {
|
|
59
|
+
const peers = loadPeers();
|
|
60
|
+
const peer = peers.find((p) => p.id === id);
|
|
61
|
+
if (!peer) return;
|
|
62
|
+
peer.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
|
|
63
|
+
savePeers(peers);
|
|
64
|
+
}
|
|
65
|
+
function updatePeerAgents(id, agents) {
|
|
66
|
+
const peers = loadPeers();
|
|
67
|
+
const peer = peers.find((p) => p.id === id);
|
|
68
|
+
if (!peer) return;
|
|
69
|
+
peer.agents = agents;
|
|
70
|
+
savePeers(peers);
|
|
71
|
+
}
|
|
72
|
+
function generatePairing() {
|
|
73
|
+
const sharedSecret = crypto.randomBytes(32).toString("hex");
|
|
74
|
+
const code = (parseInt(crypto.randomBytes(3).toString("hex"), 16) % 1e6).toString().padStart(6, "0");
|
|
75
|
+
const codeHash = crypto.createHash("sha256").update(code).digest("hex");
|
|
76
|
+
return {
|
|
77
|
+
code,
|
|
78
|
+
codeHash,
|
|
79
|
+
sharedSecret,
|
|
80
|
+
createdAt: Date.now()
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function verifyPairingCode(state, code) {
|
|
84
|
+
if (Date.now() - state.createdAt > PAIRING_TTL_MS) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const hash = crypto.createHash("sha256").update(code).digest("hex");
|
|
88
|
+
return hash === state.codeHash;
|
|
89
|
+
}
|
|
90
|
+
function computeHmacProof(challenge, codeHash) {
|
|
91
|
+
return crypto.createHmac("sha256", codeHash).update(challenge).digest("hex");
|
|
92
|
+
}
|
|
93
|
+
function verifyHmacProof(challenge, codeHash, proof) {
|
|
94
|
+
const expected = computeHmacProof(challenge, codeHash);
|
|
95
|
+
if (expected.length !== proof.length) return false;
|
|
96
|
+
try {
|
|
97
|
+
return crypto.timingSafeEqual(
|
|
98
|
+
Buffer.from(expected, "hex"),
|
|
99
|
+
Buffer.from(proof, "hex")
|
|
100
|
+
);
|
|
101
|
+
} catch {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export {
|
|
107
|
+
PEERS_FILE,
|
|
108
|
+
PAIRING_TTL_MS,
|
|
109
|
+
loadPeers,
|
|
110
|
+
savePeers,
|
|
111
|
+
findPeer,
|
|
112
|
+
addPeer,
|
|
113
|
+
revokePeer,
|
|
114
|
+
forgetAllPeers,
|
|
115
|
+
updatePeerLastSeen,
|
|
116
|
+
updatePeerAgents,
|
|
117
|
+
generatePairing,
|
|
118
|
+
verifyPairingCode,
|
|
119
|
+
computeHmacProof,
|
|
120
|
+
verifyHmacProof
|
|
121
|
+
};
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
getProvider,
|
|
7
7
|
initializeProviders
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-TXESEO7Y.js";
|
|
9
9
|
import {
|
|
10
10
|
loadAgentsManifest
|
|
11
11
|
} from "./chunk-PMXRGPRQ.js";
|
|
@@ -614,6 +614,7 @@ var AgentSpawner = class {
|
|
|
614
614
|
agent: agentName,
|
|
615
615
|
task,
|
|
616
616
|
status: "success",
|
|
617
|
+
completionVerified: false,
|
|
617
618
|
outputs: {
|
|
618
619
|
artifacts: [],
|
|
619
620
|
symbols: context.symbols,
|
|
@@ -681,6 +682,29 @@ var AgentSpawner = class {
|
|
|
681
682
|
if (this.budgetTracker) {
|
|
682
683
|
this.budgetTracker.recordUsage(agentName, relay.metrics.tokens_used, model);
|
|
683
684
|
}
|
|
685
|
+
if (relay.status === "success") {
|
|
686
|
+
const hasArtifacts = relay.outputs.artifacts.length > 0;
|
|
687
|
+
const hasDecisions = relay.outputs.decisions.length > 0;
|
|
688
|
+
const hasHandoff = !!relay.handoff;
|
|
689
|
+
const didFileWork = relay.metrics.files_written > 0;
|
|
690
|
+
const isArchitect = agentName === "architect";
|
|
691
|
+
const isBuilder = agentName === "builder";
|
|
692
|
+
if (isBuilder && !didFileWork && !hasHandoff) {
|
|
693
|
+
relay.status = "partial";
|
|
694
|
+
} else if (!hasArtifacts && !hasDecisions && !hasHandoff && !didFileWork) {
|
|
695
|
+
relay.status = "partial";
|
|
696
|
+
}
|
|
697
|
+
relay.completionVerified = true;
|
|
698
|
+
if (relay.status === "partial" && !isArchitect) {
|
|
699
|
+
if (options.onMessage) {
|
|
700
|
+
options.onMessage({
|
|
701
|
+
type: "text",
|
|
702
|
+
content: `[completion-check] Agent "${agentName}" downgraded from success \u2192 partial: no meaningful output detected.`,
|
|
703
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
684
708
|
if (this.auditLogger) {
|
|
685
709
|
const agentLog = {
|
|
686
710
|
name: agentName,
|