@ekkos/cli 1.0.33 → 1.0.35
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/capture/jsonl-rewriter.js +72 -7
- package/dist/commands/dashboard.js +186 -557
- package/dist/commands/init.js +3 -15
- package/dist/commands/run.js +221 -259
- package/dist/commands/setup.js +0 -47
- package/dist/commands/swarm-dashboard.js +4 -13
- package/dist/deploy/instructions.d.ts +2 -5
- package/dist/deploy/instructions.js +8 -11
- package/dist/deploy/settings.js +21 -15
- package/dist/deploy/skills.d.ts +0 -8
- package/dist/deploy/skills.js +0 -26
- package/dist/index.js +2 -2
- package/dist/lib/usage-parser.js +1 -2
- package/dist/utils/platform.d.ts +0 -3
- package/dist/utils/platform.js +1 -4
- package/dist/utils/session-binding.d.ts +1 -1
- package/dist/utils/session-binding.js +2 -3
- package/package.json +4 -2
- package/templates/CLAUDE.md +23 -135
- package/templates/agents/README.md +182 -0
- package/templates/agents/code-reviewer.md +166 -0
- package/templates/agents/debug-detective.md +169 -0
- package/templates/agents/ekkOS_Vercel.md +99 -0
- package/templates/agents/extension-manager.md +229 -0
- package/templates/agents/git-companion.md +185 -0
- package/templates/agents/github-test-agent.md +321 -0
- package/templates/agents/railway-manager.md +179 -0
- package/templates/ekkos-manifest.json +8 -8
- package/templates/hooks/assistant-response.ps1 +160 -256
- package/templates/hooks/assistant-response.sh +66 -130
- package/templates/hooks/hooks.json +0 -6
- package/templates/hooks/lib/contract.sh +31 -43
- package/templates/hooks/lib/count-tokens.cjs +0 -0
- package/templates/hooks/lib/ekkos-reminders.sh +0 -0
- package/templates/hooks/lib/state.sh +1 -53
- package/templates/hooks/session-start.ps1 +391 -91
- package/templates/hooks/session-start.sh +166 -201
- package/templates/hooks/stop.ps1 +341 -202
- package/templates/hooks/stop.sh +948 -275
- package/templates/hooks/user-prompt-submit.ps1 +548 -224
- package/templates/hooks/user-prompt-submit.sh +456 -382
- package/templates/plan-template.md +0 -0
- package/templates/spec-template.md +0 -0
- package/templates/windsurf-hooks/before-submit-prompt.sh +238 -0
- package/templates/windsurf-hooks/hooks.json +2 -9
- package/templates/windsurf-hooks/install.sh +0 -0
- package/templates/windsurf-hooks/lib/contract.sh +0 -2
- package/templates/windsurf-hooks/post-cascade-response.sh +0 -0
- package/templates/windsurf-hooks/pre-user-prompt.sh +0 -0
- package/templates/windsurf-skills/ekkos-memory/SKILL.md +219 -0
- package/README.md +0 -57
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
#
|
|
3
|
-
# ekkOS_ Hook: AssistantResponse - Validates and enforces footer format
|
|
4
|
-
# MANAGED BY ekkos-connect - DO NOT EDIT DIRECTLY
|
|
5
|
-
# EKKOS_MANAGED=1
|
|
6
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
2
|
+
# Post-response hook: Validates and enforces ekkOS footer format
|
|
7
3
|
# Runs AFTER assistant response, checks footer compliance
|
|
8
|
-
# Per spec v1.2 Addendum: NO jq, NO hardcoded arrays
|
|
9
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
10
|
-
|
|
11
|
-
set +e
|
|
12
4
|
|
|
13
5
|
RESPONSE_FILE="$1"
|
|
14
6
|
HOOK_ENV="$2"
|
|
@@ -18,143 +10,87 @@ if [[ ! -f "$RESPONSE_FILE" ]]; then
|
|
|
18
10
|
exit 0
|
|
19
11
|
fi
|
|
20
12
|
|
|
21
|
-
#
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
SESSION_WORDS_DEFAULT="$EKKOS_CONFIG_DIR/.defaults/session-words.json"
|
|
27
|
-
JSON_PARSE_HELPER="$EKKOS_CONFIG_DIR/.helpers/json-parse.cjs"
|
|
28
|
-
|
|
29
|
-
# Parse metadata from hook environment using Node (no jq)
|
|
30
|
-
parse_hook_env() {
|
|
31
|
-
local json="$1"
|
|
32
|
-
local path="$2"
|
|
33
|
-
echo "$json" | node -e "
|
|
34
|
-
const data = JSON.parse(require('fs').readFileSync('/dev/stdin', 'utf8') || '{}');
|
|
35
|
-
const path = '$path'.replace(/^\./,'').split('.').filter(Boolean);
|
|
36
|
-
let result = data;
|
|
37
|
-
for (const p of path) {
|
|
38
|
-
if (result === undefined || result === null) { result = undefined; break; }
|
|
39
|
-
result = result[p];
|
|
40
|
-
}
|
|
41
|
-
if (result !== undefined && result !== null) console.log(result);
|
|
42
|
-
" 2>/dev/null || echo ""
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
SESSION_ID=$(parse_hook_env "$HOOK_ENV" '.sessionId')
|
|
46
|
-
[ -z "$SESSION_ID" ] && SESSION_ID="unknown"
|
|
47
|
-
|
|
48
|
-
MODEL=$(parse_hook_env "$HOOK_ENV" '.model')
|
|
49
|
-
[ -z "$MODEL" ] && MODEL="Claude Code (Opus 4.5)"
|
|
50
|
-
|
|
51
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
52
|
-
# Session name conversion - Uses external session-words.json (NO hardcoded arrays)
|
|
53
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
54
|
-
declare -a ADJECTIVES
|
|
55
|
-
declare -a NOUNS
|
|
56
|
-
declare -a VERBS
|
|
57
|
-
SESSION_WORDS_LOADED=false
|
|
58
|
-
|
|
59
|
-
load_session_words() {
|
|
60
|
-
if [ "$SESSION_WORDS_LOADED" = "true" ]; then
|
|
61
|
-
return 0
|
|
62
|
-
fi
|
|
63
|
-
|
|
64
|
-
local words_file="$SESSION_WORDS_JSON"
|
|
65
|
-
if [ ! -f "$words_file" ]; then
|
|
66
|
-
words_file="$SESSION_WORDS_DEFAULT"
|
|
67
|
-
fi
|
|
68
|
-
|
|
69
|
-
if [ ! -f "$words_file" ] || [ ! -f "$JSON_PARSE_HELPER" ]; then
|
|
70
|
-
return 1
|
|
71
|
-
fi
|
|
72
|
-
|
|
73
|
-
if command -v node &>/dev/null; then
|
|
74
|
-
if [ "${BASH_VERSINFO[0]}" -ge 4 ]; then
|
|
75
|
-
readarray -t ADJECTIVES < <(node "$JSON_PARSE_HELPER" "$words_file" '.adjectives' 2>/dev/null)
|
|
76
|
-
readarray -t NOUNS < <(node "$JSON_PARSE_HELPER" "$words_file" '.nouns' 2>/dev/null)
|
|
77
|
-
readarray -t VERBS < <(node "$JSON_PARSE_HELPER" "$words_file" '.verbs' 2>/dev/null)
|
|
78
|
-
else
|
|
79
|
-
local i=0
|
|
80
|
-
while IFS= read -r line; do
|
|
81
|
-
ADJECTIVES[i]="$line"
|
|
82
|
-
((i++))
|
|
83
|
-
done < <(node "$JSON_PARSE_HELPER" "$words_file" '.adjectives' 2>/dev/null)
|
|
84
|
-
i=0
|
|
85
|
-
while IFS= read -r line; do
|
|
86
|
-
NOUNS[i]="$line"
|
|
87
|
-
((i++))
|
|
88
|
-
done < <(node "$JSON_PARSE_HELPER" "$words_file" '.nouns' 2>/dev/null)
|
|
89
|
-
i=0
|
|
90
|
-
while IFS= read -r line; do
|
|
91
|
-
VERBS[i]="$line"
|
|
92
|
-
((i++))
|
|
93
|
-
done < <(node "$JSON_PARSE_HELPER" "$words_file" '.verbs' 2>/dev/null)
|
|
94
|
-
fi
|
|
95
|
-
|
|
96
|
-
if [ ${#ADJECTIVES[@]} -gt 0 ] && [ ${#NOUNS[@]} -gt 0 ] && [ ${#VERBS[@]} -gt 0 ]; then
|
|
97
|
-
SESSION_WORDS_LOADED=true
|
|
98
|
-
return 0
|
|
99
|
-
fi
|
|
100
|
-
fi
|
|
101
|
-
return 1
|
|
102
|
-
}
|
|
13
|
+
# Parse metadata from hook environment
|
|
14
|
+
SESSION_ID=$(echo "$HOOK_ENV" | jq -r '.sessionId // "unknown"')
|
|
15
|
+
TURN=$(echo "$HOOK_ENV" | jq -r '.turn // 0')
|
|
16
|
+
CONTEXT_PERCENT=$(echo "$HOOK_ENV" | jq -r '.contextUsagePercent // 0')
|
|
17
|
+
MODEL=$(echo "$HOOK_ENV" | jq -r '.model // "Claude Code (Opus 4.5)"')
|
|
103
18
|
|
|
19
|
+
# Convert session UUID to word-based name
|
|
104
20
|
convert_uuid_to_name() {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
21
|
+
local uuid="$1"
|
|
22
|
+
|
|
23
|
+
# Word lists (same as MCP)
|
|
24
|
+
local ADJECTIVES=("cosmic" "turbo" "mega" "hyper" "quantum" "atomic" "stellar" "epic"
|
|
25
|
+
"mighty" "groovy" "zippy" "snappy" "jazzy" "funky" "zesty" "peppy"
|
|
26
|
+
"spicy" "crispy" "fluffy" "sparkly" "chunky" "bouncy" "bubbly" "sassy"
|
|
27
|
+
"slick" "sleek" "bold" "nifty" "perky" "plucky" "witty" "nimble"
|
|
28
|
+
"dapper" "fancy" "quirky" "punchy" "swift" "brave" "clever" "dandy"
|
|
29
|
+
"eager" "fiery" "golden" "hasty" "icy" "jolly" "keen" "lively"
|
|
30
|
+
"merry" "noble" "odd" "plush" "quick" "royal" "silly" "tidy"
|
|
31
|
+
"ultra" "vivid" "wacky" "zany" "alpha" "beta" "cyber" "delta"
|
|
32
|
+
"electric" "foggy" "giga" "hazy" "ionic" "jumpy" "kinky" "lunar"
|
|
33
|
+
"magic" "nerdy" "omega" "pixel" "quaint" "retro" "solar" "techno"
|
|
34
|
+
"unified" "viral" "wonky" "xerox" "yappy" "zen" "agile" "binary"
|
|
35
|
+
"chrome" "disco" "elastic" "fizzy" "glossy" "humble" "itchy" "jiffy"
|
|
36
|
+
"kooky" "loopy" "moody" "noisy")
|
|
37
|
+
|
|
38
|
+
local NOUNS=("penguin" "panda" "otter" "narwhal" "alpaca" "llama" "badger" "walrus"
|
|
39
|
+
"waffle" "pickle" "noodle" "pretzel" "muffin" "taco" "nugget" "biscuit"
|
|
40
|
+
"rocket" "comet" "nebula" "quasar" "meteor" "photon" "pulsar" "nova"
|
|
41
|
+
"ninja" "pirate" "wizard" "robot" "yeti" "phoenix" "sphinx" "kraken"
|
|
42
|
+
"thunder" "blizzard" "tornado" "avalanche" "mango" "kiwi" "banana" "coconut"
|
|
43
|
+
"donut" "espresso" "falafel" "gyro" "hummus" "icecream" "jambon" "kebab"
|
|
44
|
+
"latte" "mocha" "nachos" "olive" "pasta" "quinoa" "ramen" "sushi"
|
|
45
|
+
"tamale" "udon" "velvet" "wasabi" "xmas" "yogurt" "ziti" "anchor"
|
|
46
|
+
"beacon" "canyon" "drifter" "echo" "falcon" "glacier" "harbor" "island"
|
|
47
|
+
"jetpack" "kayak" "lagoon" "meadow" "orbit" "parrot" "quest"
|
|
48
|
+
"rapids" "summit" "tunnel" "umbrella" "volcano" "whisper" "xylophone" "yacht"
|
|
49
|
+
"zephyr" "acorn" "bobcat" "cactus" "dolphin" "eagle" "ferret" "gopher"
|
|
50
|
+
"hedgehog" "iguana" "jackal" "koala")
|
|
51
|
+
|
|
52
|
+
# Extract first 8 hex chars
|
|
53
|
+
local hex="${uuid:0:8}"
|
|
54
|
+
hex="${hex//-/}"
|
|
55
|
+
|
|
56
|
+
# Convert to number
|
|
57
|
+
local num=$((16#$hex))
|
|
58
|
+
|
|
59
|
+
# Calculate indices
|
|
60
|
+
local adj_idx=$((num % 100))
|
|
61
|
+
local noun_idx=$(((num / 100) % 100))
|
|
62
|
+
|
|
63
|
+
echo "${ADJECTIVES[$adj_idx]}-${NOUNS[$noun_idx]}"
|
|
128
64
|
}
|
|
129
65
|
|
|
130
66
|
SESSION_NAME=$(convert_uuid_to_name "$SESSION_ID")
|
|
131
|
-
TIMESTAMP=$(date "+%Y-%m-%d %I:%M
|
|
67
|
+
TIMESTAMP=$(date "+%Y-%m-%d %I:%M %p %Z")
|
|
132
68
|
|
|
133
69
|
# Required footer format
|
|
134
70
|
REQUIRED_FOOTER="---
|
|
135
|
-
$MODEL ·
|
|
71
|
+
$MODEL · $SESSION_NAME · Turn $TURN · ${CONTEXT_PERCENT}% · 🧠 **ekkOS_™** · 📅 $TIMESTAMP"
|
|
136
72
|
|
|
137
73
|
# Check if response has correct footer
|
|
138
74
|
RESPONSE_CONTENT=$(cat "$RESPONSE_FILE")
|
|
139
75
|
LAST_LINE=$(echo "$RESPONSE_CONTENT" | tail -1)
|
|
140
76
|
|
|
141
77
|
# Check if footer exists and is correct
|
|
142
|
-
if [[ "$LAST_LINE" == *"ekkOS"* ]] && [[ "$LAST_LINE" == *"
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
echo "" >> "$RESPONSE_FILE"
|
|
152
|
-
echo "$REQUIRED_FOOTER" >> "$RESPONSE_FILE"
|
|
153
|
-
fi
|
|
154
|
-
else
|
|
155
|
-
# Footer missing - append it
|
|
78
|
+
if [[ "$LAST_LINE" == *"ekkOS"* ]] && [[ "$LAST_LINE" == *"Turn"* ]]; then
|
|
79
|
+
# Footer exists - validate format
|
|
80
|
+
if [[ "$LAST_LINE" == *"Turn $TURN"* ]] && [[ "$LAST_LINE" == *"${CONTEXT_PERCENT}%"* ]] && [[ "$LAST_LINE" == *"$SESSION_NAME"* ]]; then
|
|
81
|
+
# Footer is correct
|
|
82
|
+
exit 0
|
|
83
|
+
else
|
|
84
|
+
# Footer exists but is malformed - replace it
|
|
85
|
+
RESPONSE_WITHOUT_FOOTER=$(echo "$RESPONSE_CONTENT" | head -n -2) # Remove last 2 lines (--- and footer)
|
|
86
|
+
echo "$RESPONSE_WITHOUT_FOOTER" > "$RESPONSE_FILE"
|
|
156
87
|
echo "" >> "$RESPONSE_FILE"
|
|
157
88
|
echo "$REQUIRED_FOOTER" >> "$RESPONSE_FILE"
|
|
89
|
+
fi
|
|
90
|
+
else
|
|
91
|
+
# Footer missing - append it
|
|
92
|
+
echo "" >> "$RESPONSE_FILE"
|
|
93
|
+
echo "$REQUIRED_FOOTER" >> "$RESPONSE_FILE"
|
|
158
94
|
fi
|
|
159
95
|
|
|
160
96
|
exit 0
|
|
@@ -78,7 +78,7 @@ write_turn_contract() {
|
|
|
78
78
|
"retrieved_directive_ids": $directive_array,
|
|
79
79
|
"timestamp": "$timestamp",
|
|
80
80
|
"query_hash": "$query_hash",
|
|
81
|
-
"ekkos_strict": ${EKKOS_STRICT:-
|
|
81
|
+
"ekkos_strict": ${EKKOS_STRICT:-0}
|
|
82
82
|
}
|
|
83
83
|
EOF
|
|
84
84
|
|
|
@@ -134,7 +134,7 @@ cleanup_turn_contract() {
|
|
|
134
134
|
|
|
135
135
|
# Check if strict mode is enabled
|
|
136
136
|
is_strict_mode() {
|
|
137
|
-
[ "${EKKOS_STRICT:-
|
|
137
|
+
[ "${EKKOS_STRICT:-0}" = "1" ]
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
# Generate strict mode blocker message for Claude Code
|
|
@@ -157,57 +157,50 @@ EOF
|
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
# Validate PatternGuard coverage (returns 0-100)
|
|
160
|
-
# PORTABLE: Works on macOS without Perl regex (grep -P)
|
|
161
160
|
calculate_pattern_guard_coverage() {
|
|
162
161
|
local assistant_response="$1"
|
|
163
162
|
local pattern_ids="$2" # Comma-separated
|
|
164
163
|
|
|
165
|
-
# Count total patterns
|
|
166
|
-
local total_count
|
|
167
|
-
|
|
168
|
-
total_count=$(echo "$pattern_ids" | tr ',' '\n' | grep -v '^$' | wc -l | tr -d ' ')
|
|
169
|
-
fi
|
|
164
|
+
# Count total patterns
|
|
165
|
+
local total_count
|
|
166
|
+
total_count=$(echo "$pattern_ids" | tr ',' '\n' | grep -c '.' || echo 0)
|
|
170
167
|
|
|
171
168
|
if [ "$total_count" -eq 0 ]; then
|
|
172
169
|
echo "100" # No patterns = 100% coverage by definition
|
|
173
170
|
return 0
|
|
174
171
|
fi
|
|
175
172
|
|
|
176
|
-
# Extract acknowledged IDs
|
|
173
|
+
# Extract acknowledged IDs from [ekkOS_SELECT] and [ekkOS_SKIP] blocks
|
|
177
174
|
local acknowledged_count=0
|
|
178
175
|
|
|
179
|
-
#
|
|
180
|
-
local
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
')
|
|
197
|
-
acknowledged_count=$((acknowledged_count + skip_count))
|
|
176
|
+
# Check SELECT block
|
|
177
|
+
local select_block
|
|
178
|
+
select_block=$(echo "$assistant_response" | grep -ozP '\[ekkOS_SELECT\][\s\S]*?\[/ekkOS_SELECT\]' 2>/dev/null | tr '\0' '\n' || true)
|
|
179
|
+
if [ -n "$select_block" ]; then
|
|
180
|
+
local select_count
|
|
181
|
+
select_count=$(echo "$select_block" | grep -oE 'id:\s*[a-f0-9-]+' | wc -l | tr -d ' ')
|
|
182
|
+
acknowledged_count=$((acknowledged_count + select_count))
|
|
183
|
+
fi
|
|
184
|
+
|
|
185
|
+
# Check SKIP block
|
|
186
|
+
local skip_block
|
|
187
|
+
skip_block=$(echo "$assistant_response" | grep -ozP '\[ekkOS_SKIP\][\s\S]*?\[/ekkOS_SKIP\]' 2>/dev/null | tr '\0' '\n' || true)
|
|
188
|
+
if [ -n "$skip_block" ]; then
|
|
189
|
+
local skip_count
|
|
190
|
+
skip_count=$(echo "$skip_block" | grep -oE 'id:\s*[a-f0-9-]+' | wc -l | tr -d ' ')
|
|
191
|
+
acknowledged_count=$((acknowledged_count + skip_count))
|
|
192
|
+
fi
|
|
198
193
|
|
|
199
194
|
# Legacy: Check for [ekkOS_APPLY] markers (fallback)
|
|
200
195
|
if [ "$acknowledged_count" -eq 0 ]; then
|
|
201
196
|
local apply_count
|
|
202
|
-
apply_count=$(echo "$assistant_response" | grep -c '\[ekkOS_APPLY\]'
|
|
203
|
-
acknowledged_count=$
|
|
197
|
+
apply_count=$(echo "$assistant_response" | grep -c '\[ekkOS_APPLY\]' || echo 0)
|
|
198
|
+
acknowledged_count=$apply_count
|
|
204
199
|
fi
|
|
205
200
|
|
|
206
201
|
# Calculate coverage percentage
|
|
207
|
-
local coverage
|
|
208
|
-
|
|
209
|
-
coverage=$((acknowledged_count * 100 / total_count))
|
|
210
|
-
fi
|
|
202
|
+
local coverage
|
|
203
|
+
coverage=$((acknowledged_count * 100 / total_count))
|
|
211
204
|
|
|
212
205
|
# Cap at 100%
|
|
213
206
|
if [ "$coverage" -gt 100 ]; then
|
|
@@ -262,16 +255,11 @@ EOF
|
|
|
262
255
|
}
|
|
263
256
|
|
|
264
257
|
# Determine if turn is compliant
|
|
265
|
-
# ROBUST: Handles edge cases with non-numeric or empty values
|
|
266
258
|
is_turn_compliant() {
|
|
267
|
-
local retrieval_ok="$
|
|
268
|
-
local pattern_guard_coverage="$
|
|
269
|
-
local footer_present="$
|
|
270
|
-
local pattern_count="$
|
|
271
|
-
|
|
272
|
-
# Sanitize numeric values (default to safe values)
|
|
273
|
-
pattern_guard_coverage=$(echo "$pattern_guard_coverage" | grep -oE '^[0-9]+$' || echo "100")
|
|
274
|
-
pattern_count=$(echo "$pattern_count" | grep -oE '^[0-9]+$' || echo "0")
|
|
259
|
+
local retrieval_ok="$1"
|
|
260
|
+
local pattern_guard_coverage="$2"
|
|
261
|
+
local footer_present="$3"
|
|
262
|
+
local pattern_count="$4"
|
|
275
263
|
|
|
276
264
|
# Retrieval must have succeeded
|
|
277
265
|
if [ "$retrieval_ok" != "true" ]; then
|
|
File without changes
|
|
File without changes
|
|
@@ -20,40 +20,25 @@ mkdir -p "$STATE_DIR"
|
|
|
20
20
|
# State File Management
|
|
21
21
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
22
22
|
|
|
23
|
-
# Save patterns for session
|
|
23
|
+
# Save patterns for session
|
|
24
24
|
save_patterns() {
|
|
25
25
|
local session_id="$1"
|
|
26
26
|
local patterns="$2"
|
|
27
27
|
local model_used="$3"
|
|
28
|
-
local retrieval_token="${4:-}" # Optional retrieval_token for verified applications
|
|
29
28
|
|
|
30
29
|
local state_file="$STATE_DIR/patterns-${session_id}.json"
|
|
31
30
|
|
|
32
31
|
jq -n \
|
|
33
32
|
--argjson patterns "$patterns" \
|
|
34
33
|
--arg model "$model_used" \
|
|
35
|
-
--arg token "$retrieval_token" \
|
|
36
34
|
--arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
37
35
|
'{
|
|
38
36
|
patterns: $patterns,
|
|
39
37
|
model_used: $model,
|
|
40
|
-
retrieval_token: $token,
|
|
41
38
|
saved_at: $timestamp
|
|
42
39
|
}' > "$state_file"
|
|
43
40
|
}
|
|
44
41
|
|
|
45
|
-
# Get retrieval token from saved patterns
|
|
46
|
-
get_retrieval_token() {
|
|
47
|
-
local session_id="$1"
|
|
48
|
-
local state_file="$STATE_DIR/patterns-${session_id}.json"
|
|
49
|
-
|
|
50
|
-
if [ -f "$state_file" ]; then
|
|
51
|
-
jq -r '.retrieval_token // ""' "$state_file" 2>/dev/null
|
|
52
|
-
else
|
|
53
|
-
echo ""
|
|
54
|
-
fi
|
|
55
|
-
}
|
|
56
|
-
|
|
57
42
|
# Load patterns for session
|
|
58
43
|
load_patterns() {
|
|
59
44
|
local session_id="$1"
|
|
@@ -72,43 +57,6 @@ clear_patterns() {
|
|
|
72
57
|
rm -f "$STATE_DIR/patterns-${session_id}.json"
|
|
73
58
|
}
|
|
74
59
|
|
|
75
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
76
|
-
# Conversation Context (for Golden Loop MEASURE phase)
|
|
77
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
78
|
-
|
|
79
|
-
# Save current conversation context for pattern tracking
|
|
80
|
-
# Called by user-prompt-submit.sh when new query is submitted
|
|
81
|
-
save_conversation_context() {
|
|
82
|
-
local session_id="$1"
|
|
83
|
-
local user_query="$2"
|
|
84
|
-
local prev_response="$3"
|
|
85
|
-
|
|
86
|
-
local context_file="$STATE_DIR/conversation-${session_id}.json"
|
|
87
|
-
|
|
88
|
-
jq -n \
|
|
89
|
-
--arg query "$user_query" \
|
|
90
|
-
--arg response "$prev_response" \
|
|
91
|
-
--arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
92
|
-
'{
|
|
93
|
-
query: $query,
|
|
94
|
-
response: $response,
|
|
95
|
-
saved_at: $timestamp
|
|
96
|
-
}' > "$context_file" 2>/dev/null || true
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
# Load current conversation context
|
|
100
|
-
# Called by post-tool-use.sh when tracking pattern applications
|
|
101
|
-
load_conversation_context() {
|
|
102
|
-
local session_id="$1"
|
|
103
|
-
local context_file="$STATE_DIR/conversation-${session_id}.json"
|
|
104
|
-
|
|
105
|
-
if [ -f "$context_file" ]; then
|
|
106
|
-
cat "$context_file"
|
|
107
|
-
else
|
|
108
|
-
echo '{"query":"","response":""}'
|
|
109
|
-
fi
|
|
110
|
-
}
|
|
111
|
-
|
|
112
60
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
113
61
|
# Capture Deduplication
|
|
114
62
|
# ═══════════════════════════════════════════════════════════════════════════
|