@ekkos/cli 1.2.18 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/cache/capture.js +0 -0
  2. package/dist/commands/dashboard.js +121 -66
  3. package/dist/commands/hooks.d.ts +25 -36
  4. package/dist/commands/hooks.js +43 -615
  5. package/dist/commands/init.js +7 -23
  6. package/dist/commands/run.js +90 -3
  7. package/dist/commands/setup.js +10 -352
  8. package/dist/deploy/hooks.d.ts +8 -5
  9. package/dist/deploy/hooks.js +12 -105
  10. package/dist/deploy/settings.d.ts +8 -2
  11. package/dist/deploy/settings.js +22 -51
  12. package/dist/index.js +17 -39
  13. package/dist/utils/state.js +7 -2
  14. package/package.json +1 -1
  15. package/templates/CLAUDE.md +82 -292
  16. package/templates/cursor-rules/ekkos-memory.md +48 -108
  17. package/templates/windsurf-rules/ekkos-memory.md +62 -64
  18. package/templates/cursor-hooks/after-agent-response.sh +0 -117
  19. package/templates/cursor-hooks/before-submit-prompt.sh +0 -419
  20. package/templates/cursor-hooks/hooks.json +0 -20
  21. package/templates/cursor-hooks/lib/contract.sh +0 -320
  22. package/templates/cursor-hooks/stop.sh +0 -75
  23. package/templates/hooks/assistant-response.ps1 +0 -256
  24. package/templates/hooks/assistant-response.sh +0 -160
  25. package/templates/hooks/hooks.json +0 -40
  26. package/templates/hooks/lib/contract.sh +0 -332
  27. package/templates/hooks/lib/count-tokens.cjs +0 -86
  28. package/templates/hooks/lib/ekkos-reminders.sh +0 -98
  29. package/templates/hooks/lib/state.sh +0 -210
  30. package/templates/hooks/session-start.ps1 +0 -146
  31. package/templates/hooks/session-start.sh +0 -353
  32. package/templates/hooks/stop.ps1 +0 -349
  33. package/templates/hooks/stop.sh +0 -382
  34. package/templates/hooks/user-prompt-submit.ps1 +0 -419
  35. package/templates/hooks/user-prompt-submit.sh +0 -516
  36. package/templates/project-stubs/session-start.ps1 +0 -63
  37. package/templates/project-stubs/session-start.sh +0 -55
  38. package/templates/project-stubs/stop.ps1 +0 -63
  39. package/templates/project-stubs/stop.sh +0 -55
  40. package/templates/project-stubs/user-prompt-submit.ps1 +0 -63
  41. package/templates/project-stubs/user-prompt-submit.sh +0 -55
  42. package/templates/windsurf-hooks/README.md +0 -212
  43. package/templates/windsurf-hooks/hooks.json +0 -17
  44. package/templates/windsurf-hooks/install.sh +0 -148
  45. package/templates/windsurf-hooks/lib/contract.sh +0 -322
  46. package/templates/windsurf-hooks/post-cascade-response.sh +0 -251
  47. package/templates/windsurf-hooks/pre-user-prompt.sh +0 -435
@@ -1,322 +0,0 @@
1
- #!/bin/bash
2
- # ═══════════════════════════════════════════════════════════════════════════
3
- # ekkOS_ Turn Contract Library
4
- #
5
- # Shared functions for Golden Loop compliance enforcement.
6
- # Used by BOTH Claude Code (.claude/) and Cursor (.cursor/) hooks.
7
- #
8
- # TURN CONTRACT: Evidence that retrieval occurred before answering.
9
- # This is the SINGLE SOURCE OF TRUTH for compliance auditing.
10
- # ═══════════════════════════════════════════════════════════════════════════
11
-
12
- # Get contract directory based on environment
13
- get_contract_dir() {
14
- local source="${1:-claude-code}"
15
- local project_root="${2:-$PROJECT_ROOT}"
16
-
17
- if [ "$source" = "cursor" ]; then
18
- echo "$project_root/.cursor/state/ekkos"
19
- elif [ "$source" = "windsurf" ]; then
20
- echo "$project_root/.windsurf/state/ekkos"
21
- else
22
- echo "$project_root/.claude/state/ekkos"
23
- fi
24
- }
25
-
26
- # Generate stable hash of user prompt (for deduplication)
27
- generate_query_hash() {
28
- local query="$1"
29
- # Use md5 on macOS, md5sum on Linux
30
- if command -v md5 >/dev/null 2>&1; then
31
- echo -n "$query" | md5 | cut -c1-16
32
- elif command -v md5sum >/dev/null 2>&1; then
33
- echo -n "$query" | md5sum | cut -c1-16
34
- else
35
- # Fallback: simple hash using cksum
36
- echo -n "$query" | cksum | cut -d' ' -f1
37
- fi
38
- }
39
-
40
- # Write turn contract at RETRIEVAL time
41
- # This is the EVIDENCE that retrieval happened before answering
42
- write_turn_contract() {
43
- local session_id="$1"
44
- local retrieval_ok="$2"
45
- local retrieval_source="$3"
46
- local pattern_ids="$4" # Comma-separated list
47
- local directive_ids="$5" # Comma-separated list
48
- local query_hash="$6"
49
- local project_root="${7:-$PROJECT_ROOT}"
50
-
51
- local contract_dir
52
- contract_dir=$(get_contract_dir "$retrieval_source" "$project_root")
53
- mkdir -p "$contract_dir" 2>/dev/null || return 1
54
-
55
- local contract_file="$contract_dir/turn-contract-${session_id}.json"
56
- local timestamp
57
- timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)
58
-
59
- # Convert comma-separated IDs to JSON array
60
- local pattern_array
61
- local directive_array
62
- if [ -n "$pattern_ids" ]; then
63
- pattern_array=$(echo "$pattern_ids" | tr ',' '\n' | grep -v '^$' | jq -R . | jq -s .)
64
- else
65
- pattern_array="[]"
66
- fi
67
- if [ -n "$directive_ids" ]; then
68
- directive_array=$(echo "$directive_ids" | tr ',' '\n' | grep -v '^$' | jq -R . | jq -s .)
69
- else
70
- directive_array="[]"
71
- fi
72
-
73
- # Write contract
74
- cat > "$contract_file" << EOF
75
- {
76
- "session_id": "$session_id",
77
- "retrieval_ok": $retrieval_ok,
78
- "retrieval_source": "$retrieval_source",
79
- "retrieved_pattern_ids": $pattern_array,
80
- "retrieved_directive_ids": $directive_array,
81
- "timestamp": "$timestamp",
82
- "query_hash": "$query_hash",
83
- "ekkos_strict": ${EKKOS_STRICT:-0}
84
- }
85
- EOF
86
-
87
- return 0
88
- }
89
-
90
- # Read turn contract
91
- read_turn_contract() {
92
- local session_id="$1"
93
- local retrieval_source="$2"
94
- local project_root="${3:-$PROJECT_ROOT}"
95
-
96
- local contract_dir
97
- contract_dir=$(get_contract_dir "$retrieval_source" "$project_root")
98
- local contract_file="$contract_dir/turn-contract-${session_id}.json"
99
-
100
- if [ -f "$contract_file" ]; then
101
- cat "$contract_file"
102
- return 0
103
- else
104
- return 1
105
- fi
106
- }
107
-
108
- # Extract field from contract JSON
109
- get_contract_field() {
110
- local contract_json="$1"
111
- local field="$2"
112
-
113
- echo "$contract_json" | jq -r ".$field // \"\"" 2>/dev/null
114
- }
115
-
116
- # Get array field as comma-separated string
117
- get_contract_array() {
118
- local contract_json="$1"
119
- local field="$2"
120
-
121
- echo "$contract_json" | jq -r ".$field // [] | join(\",\")" 2>/dev/null
122
- }
123
-
124
- # Cleanup turn contract (only after successful capture)
125
- cleanup_turn_contract() {
126
- local session_id="$1"
127
- local retrieval_source="$2"
128
- local project_root="${3:-$PROJECT_ROOT}"
129
-
130
- local contract_dir
131
- contract_dir=$(get_contract_dir "$retrieval_source" "$project_root")
132
- local contract_file="$contract_dir/turn-contract-${session_id}.json"
133
-
134
- rm -f "$contract_file" 2>/dev/null
135
- }
136
-
137
- # Check if strict mode is enabled
138
- is_strict_mode() {
139
- [ "${EKKOS_STRICT:-0}" = "1" ]
140
- }
141
-
142
- # Generate strict mode blocker message for Claude Code
143
- get_strict_blocker_message() {
144
- cat << 'EOF'
145
- ════════════════════════════════════════════════════════════════════════════
146
- ⛔ EKKOS_STRICT MODE: RETRIEVAL FAILED — DO NOT ANSWER
147
-
148
- ekkOS memory retrieval failed or was not authenticated.
149
- In STRICT mode, you MUST NOT answer technical questions without memory context.
150
-
151
- ACTION REQUIRED:
152
- 1. Check if ekkOS is connected: Run `ekkOS: Connect` in VS Code
153
- 2. Verify auth token exists: ~/.ekkos/config.json
154
- 3. Retry the request after connecting
155
-
156
- DO NOT proceed with answering. The Golden Loop requires retrieval FIRST.
157
- ════════════════════════════════════════════════════════════════════════════
158
- EOF
159
- }
160
-
161
- # Validate PatternGuard coverage (returns 0-100)
162
- calculate_pattern_guard_coverage() {
163
- local assistant_response="$1"
164
- local pattern_ids="$2" # Comma-separated
165
-
166
- # Count total patterns
167
- local total_count
168
- total_count=$(echo "$pattern_ids" | tr ',' '\n' | grep -c '.' || echo 0)
169
-
170
- if [ "$total_count" -eq 0 ]; then
171
- echo "100" # No patterns = 100% coverage by definition
172
- return 0
173
- fi
174
-
175
- # Extract acknowledged IDs from [ekkOS_SELECT] and [ekkOS_SKIP] blocks
176
- local acknowledged_count=0
177
-
178
- # Check SELECT block
179
- local select_block
180
- select_block=$(echo "$assistant_response" | grep -ozP '\[ekkOS_SELECT\][\s\S]*?\[/ekkOS_SELECT\]' 2>/dev/null | tr '\0' '\n' || true)
181
- if [ -n "$select_block" ]; then
182
- local select_count
183
- select_count=$(echo "$select_block" | grep -oE 'id:\s*[a-f0-9-]+' | wc -l | tr -d ' ')
184
- acknowledged_count=$((acknowledged_count + select_count))
185
- fi
186
-
187
- # Check SKIP block
188
- local skip_block
189
- skip_block=$(echo "$assistant_response" | grep -ozP '\[ekkOS_SKIP\][\s\S]*?\[/ekkOS_SKIP\]' 2>/dev/null | tr '\0' '\n' || true)
190
- if [ -n "$skip_block" ]; then
191
- local skip_count
192
- skip_count=$(echo "$skip_block" | grep -oE 'id:\s*[a-f0-9-]+' | wc -l | tr -d ' ')
193
- acknowledged_count=$((acknowledged_count + skip_count))
194
- fi
195
-
196
- # Legacy: Check for [ekkOS_APPLY] markers (fallback)
197
- if [ "$acknowledged_count" -eq 0 ]; then
198
- local apply_count
199
- apply_count=$(echo "$assistant_response" | grep -c '\[ekkOS_APPLY\]' || echo 0)
200
- acknowledged_count=$apply_count
201
- fi
202
-
203
- # Calculate coverage percentage
204
- local coverage
205
- coverage=$((acknowledged_count * 100 / total_count))
206
-
207
- # Cap at 100%
208
- if [ "$coverage" -gt 100 ]; then
209
- coverage=100
210
- fi
211
-
212
- echo "$coverage"
213
- }
214
-
215
- # Check for ekkOS footer presence
216
- check_footer_present() {
217
- local assistant_response="$1"
218
-
219
- # Look for the mandatory footer format:
220
- # 🧠 **ekkOS_™** · 📅 YYYY-MM-DD
221
- # OR
222
- # {IDE} ({Model}) · 🧠 **ekkOS_™** · 📅 {Timestamp}
223
-
224
- if echo "$assistant_response" | grep -qE '🧠.*ekkOS.*📅.*[0-9]{4}-[0-9]{2}-[0-9]{2}'; then
225
- echo "true"
226
- return 0
227
- else
228
- echo "false"
229
- return 1
230
- fi
231
- }
232
-
233
- # Build compliance metadata for capture
234
- build_compliance_metadata() {
235
- local retrieval_ok="$1"
236
- local pattern_guard_coverage="$2"
237
- local footer_present="$3"
238
- local ekkos_strict="$4"
239
- local retrieved_count="$5"
240
-
241
- local pattern_guard_required="false"
242
- if [ "$retrieved_count" -gt 0 ]; then
243
- pattern_guard_required="true"
244
- fi
245
-
246
- cat << EOF
247
- {
248
- "retrieval_ok": $retrieval_ok,
249
- "pattern_guard_required": $pattern_guard_required,
250
- "pattern_guard_coverage_pct": $pattern_guard_coverage,
251
- "footer_present": $footer_present,
252
- "ekkos_strict": $ekkos_strict,
253
- "retrieved_count": $retrieved_count,
254
- "compliance_version": "1.0"
255
- }
256
- EOF
257
- }
258
-
259
- # Determine if turn is compliant
260
- is_turn_compliant() {
261
- local retrieval_ok="$1"
262
- local pattern_guard_coverage="$2"
263
- local footer_present="$3"
264
- local pattern_count="$4"
265
-
266
- # Retrieval must have succeeded
267
- if [ "$retrieval_ok" != "true" ]; then
268
- echo "false"
269
- return 1
270
- fi
271
-
272
- # If patterns were retrieved, PatternGuard must be 100%
273
- if [ "$pattern_count" -gt 0 ] && [ "$pattern_guard_coverage" -lt 100 ]; then
274
- echo "false"
275
- return 1
276
- fi
277
-
278
- # Footer must be present
279
- if [ "$footer_present" != "true" ]; then
280
- echo "false"
281
- return 1
282
- fi
283
-
284
- echo "true"
285
- return 0
286
- }
287
-
288
- # Generate violation reason
289
- get_violation_reason() {
290
- local retrieval_ok="$1"
291
- local pattern_guard_coverage="$2"
292
- local footer_present="$3"
293
- local pattern_count="$4"
294
-
295
- local reasons=""
296
-
297
- if [ "$retrieval_ok" != "true" ]; then
298
- reasons="retrieval_failed"
299
- fi
300
-
301
- if [ "$pattern_count" -gt 0 ] && [ "$pattern_guard_coverage" -lt 100 ]; then
302
- if [ -n "$reasons" ]; then
303
- reasons="$reasons,pattern_guard_incomplete"
304
- else
305
- reasons="pattern_guard_incomplete"
306
- fi
307
- fi
308
-
309
- if [ "$footer_present" != "true" ]; then
310
- if [ -n "$reasons" ]; then
311
- reasons="$reasons,footer_missing"
312
- else
313
- reasons="footer_missing"
314
- fi
315
- fi
316
-
317
- if [ -z "$reasons" ]; then
318
- reasons="none"
319
- fi
320
-
321
- echo "$reasons"
322
- }
@@ -1,251 +0,0 @@
1
- #!/bin/bash
2
- # ═══════════════════════════════════════════════════════════════════════════
3
- # ekkOS_ Hook: post_cascade_response (Windsurf Cascade) - CAPTURE + VALIDATE
4
- #
5
- # ARCHITECTURE: Dumb Hook, Smart Backend
6
- # ═══════════════════════════════════════════════════════════════════════════
7
- # This hook runs AFTER Cascade completes a response.
8
- # It is THE CANONICAL capture path for Windsurf Cascade.
9
- #
10
- # FEATURES:
11
- # - Turn capture to L2 episodic memory
12
- # - PatternGuard validation (footer + pattern acknowledgment)
13
- # - Outcome tracking for Golden Loop
14
- # - Compliance metadata generation
15
- #
16
- # Windsurf Hook Input:
17
- # {
18
- # "agent_action_name": "post_cascade_response",
19
- # "trajectory_id": "...",
20
- # "execution_id": "...",
21
- # "timestamp": "...",
22
- # "tool_info": {"response": "..."}
23
- # }
24
- #
25
- # Golden Loop Flow:
26
- # 1. pre_user_prompt: Retrieves patterns + writes turn contract
27
- # 2. post_cascade_response: Validates response + captures turn + tracks outcomes
28
- # ═══════════════════════════════════════════════════════════════════════════
29
-
30
- set +e # Don't exit on errors - be bulletproof
31
-
32
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
33
- PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
34
- STATE_DIR="$PROJECT_ROOT/.windsurf/state"
35
- mkdir -p "$STATE_DIR" 2>/dev/null || true
36
-
37
- # Load turn contract library
38
- if [ -f "$SCRIPT_DIR/lib/contract.sh" ]; then
39
- source "$SCRIPT_DIR/lib/contract.sh" 2>/dev/null || true
40
- fi
41
-
42
- # Fallback functions if library didn't load
43
- if ! command -v read_turn_contract >/dev/null 2>&1; then
44
- read_turn_contract() { return 1; }
45
- fi
46
- if ! command -v calculate_pattern_guard_coverage >/dev/null 2>&1; then
47
- calculate_pattern_guard_coverage() { echo "100"; }
48
- fi
49
- if ! command -v check_footer_present >/dev/null 2>&1; then
50
- check_footer_present() { echo "true"; }
51
- fi
52
- if ! command -v is_turn_compliant >/dev/null 2>&1; then
53
- is_turn_compliant() { echo "true"; }
54
- fi
55
- if ! command -v get_violation_reason >/dev/null 2>&1; then
56
- get_violation_reason() { echo "none"; }
57
- fi
58
- if ! command -v cleanup_turn_contract >/dev/null 2>&1; then
59
- cleanup_turn_contract() { return 0; }
60
- fi
61
-
62
- # ═══════════════════════════════════════════════════════════════════════════
63
- # Read JSON input from stdin (Windsurf format)
64
- # ═══════════════════════════════════════════════════════════════════════════
65
- INPUT=$(cat)
66
-
67
- # Extract response text from Windsurf format
68
- RESPONSE_TEXT=$(echo "$INPUT" | jq -r '.tool_info.response // ""' 2>/dev/null || echo "")
69
-
70
- # Extract trajectory/session info
71
- TRAJECTORY_ID=$(echo "$INPUT" | jq -r '.trajectory_id // ""' 2>/dev/null || echo "")
72
- EXECUTION_ID=$(echo "$INPUT" | jq -r '.execution_id // ""' 2>/dev/null || echo "")
73
-
74
- # Generate session ID if not provided
75
- if [ -z "$TRAJECTORY_ID" ] || [ "$TRAJECTORY_ID" = "null" ]; then
76
- TRAJECTORY_ID="windsurf-$(date +%s)-$$"
77
- fi
78
-
79
- SESSION_ID="$TRAJECTORY_ID"
80
-
81
- # Skip if empty response
82
- if [ -z "$RESPONSE_TEXT" ] || [ "$RESPONSE_TEXT" = "null" ]; then
83
- exit 0
84
- fi
85
-
86
- # ═══════════════════════════════════════════════════════════════════════════
87
- # Load auth
88
- # ═══════════════════════════════════════════════════════════════════════════
89
- EKKOS_CONFIG="$HOME/.ekkos/config.json"
90
- AUTH_TOKEN=""
91
- USER_ID=""
92
-
93
- if [ -f "$EKKOS_CONFIG" ]; then
94
- AUTH_TOKEN=$(jq -r '.hookApiKey // .apiKey // ""' "$EKKOS_CONFIG" 2>/dev/null || echo "")
95
- USER_ID=$(jq -r '.userId // ""' "$EKKOS_CONFIG" 2>/dev/null || echo "")
96
- fi
97
-
98
- if [ -z "$AUTH_TOKEN" ] && [ -f "$PROJECT_ROOT/.env.local" ]; then
99
- AUTH_TOKEN=$(grep -E "^SUPABASE_SECRET_KEY=" "$PROJECT_ROOT/.env.local" | cut -d'=' -f2- | tr -d '"' | tr -d "'" | tr -d '\r')
100
- fi
101
-
102
- [ -z "$AUTH_TOKEN" ] && exit 0
103
-
104
- MEMORY_API_URL="https://mcp.ekkos.dev"
105
-
106
- # ═══════════════════════════════════════════════════════════════════════════
107
- # Load session state from pre_user_prompt hook
108
- # ═══════════════════════════════════════════════════════════════════════════
109
- LAST_QUERY=""
110
- RETRIEVED_PATTERN_IDS=""
111
- CONTRACT_DATA=""
112
-
113
- # Load the query that triggered this response
114
- if [ -f "$STATE_DIR/last_query.txt" ]; then
115
- LAST_QUERY=$(cat "$STATE_DIR/last_query.txt" 2>/dev/null || echo "")
116
- fi
117
-
118
- # Read the turn contract written by pre_user_prompt
119
- CONTRACT_DATA=$(read_turn_contract "$SESSION_ID" "windsurf" "$PROJECT_ROOT" 2>/dev/null || echo "")
120
-
121
- if [ -n "$CONTRACT_DATA" ]; then
122
- RETRIEVAL_OK=$(echo "$CONTRACT_DATA" | jq -r '.retrieval_ok // "false"' 2>/dev/null || echo "false")
123
- RETRIEVED_PATTERN_IDS=$(echo "$CONTRACT_DATA" | jq -r '.retrieved_pattern_ids | join(",") // ""' 2>/dev/null || echo "")
124
- RETRIEVED_DIRECTIVE_IDS=$(echo "$CONTRACT_DATA" | jq -r '.retrieved_directive_ids | join(",") // ""' 2>/dev/null || echo "")
125
- EKKOS_STRICT=$(echo "$CONTRACT_DATA" | jq -r '.ekkos_strict // 0' 2>/dev/null || echo "0")
126
- else
127
- RETRIEVAL_OK="false"
128
- EKKOS_STRICT=0
129
- fi
130
-
131
- # Load patterns that were injected
132
- PATTERN_COUNT=0
133
- if [ -f "$STATE_DIR/patterns-${SESSION_ID}.json" ]; then
134
- PATTERN_COUNT=$(jq 'length' "$STATE_DIR/patterns-${SESSION_ID}.json" 2>/dev/null || echo "0")
135
- fi
136
-
137
- # ═══════════════════════════════════════════════════════════════════════════
138
- # [PATTERNGUARD] Validate response compliance
139
- # ═══════════════════════════════════════════════════════════════════════════
140
- PATTERN_GUARD_COVERAGE=$(calculate_pattern_guard_coverage "$RESPONSE_TEXT" "$RETRIEVED_PATTERN_IDS")
141
- FOOTER_PRESENT=$(check_footer_present "$RESPONSE_TEXT")
142
- IS_COMPLIANT=$(is_turn_compliant "$RETRIEVAL_OK" "$PATTERN_GUARD_COVERAGE" "$FOOTER_PRESENT" "$PATTERN_COUNT")
143
- VIOLATION_REASON=$(get_violation_reason "$RETRIEVAL_OK" "$PATTERN_GUARD_COVERAGE" "$FOOTER_PRESENT" "$PATTERN_COUNT")
144
-
145
- # ═══════════════════════════════════════════════════════════════════════════
146
- # Get turn number
147
- # ═══════════════════════════════════════════════════════════════════════════
148
- TURN_FILE="$STATE_DIR/turn_${SESSION_ID}.txt"
149
- TURN_NUMBER=1
150
- if [ -f "$TURN_FILE" ]; then
151
- TURN_NUMBER=$(cat "$TURN_FILE" 2>/dev/null || echo "0")
152
- TURN_NUMBER=$((TURN_NUMBER + 1))
153
- fi
154
- echo "$TURN_NUMBER" > "$TURN_FILE"
155
-
156
- # ═══════════════════════════════════════════════════════════════════════════
157
- # [ekkOS_CAPTURE] Capture turn to L2 episodic memory
158
- # ═══════════════════════════════════════════════════════════════════════════
159
- # Only capture if we have a query to pair with
160
- if [ -n "$LAST_QUERY" ]; then
161
- # Truncate for API limits
162
- QUERY_TRUNCATED="${LAST_QUERY:0:10000}"
163
- RESPONSE_TRUNCATED="${RESPONSE_TEXT:0:50000}"
164
-
165
- # Build compliance metadata
166
- COMPLIANCE_META=$(cat << EOF
167
- {
168
- "retrieval_ok": $RETRIEVAL_OK,
169
- "pattern_guard_required": $(if [ "$PATTERN_COUNT" -gt 0 ]; then echo "true"; else echo "false"; fi),
170
- "pattern_guard_coverage_pct": $PATTERN_GUARD_COVERAGE,
171
- "footer_present": $FOOTER_PRESENT,
172
- "ekkos_strict": $EKKOS_STRICT,
173
- "retrieved_count": $PATTERN_COUNT,
174
- "compliance_version": "1.0",
175
- "is_compliant": $IS_COMPLIANT,
176
- "violation_reason": "$VIOLATION_REASON"
177
- }
178
- EOF
179
- )
180
-
181
- CAPTURE_PAYLOAD=$(jq -n \
182
- --arg session_id "windsurf-$SESSION_ID" \
183
- --arg user_id "${USER_ID:-system}" \
184
- --arg user_query "$QUERY_TRUNCATED" \
185
- --arg assistant_response "$RESPONSE_TRUNCATED" \
186
- --argjson turn_number "$TURN_NUMBER" \
187
- --argjson compliance "$COMPLIANCE_META" \
188
- '{
189
- session_id: $session_id,
190
- user_id: $user_id,
191
- user_query: $user_query,
192
- assistant_response: $assistant_response,
193
- metadata: {
194
- source: "windsurf-cascade",
195
- turn_number: $turn_number,
196
- compliance: $compliance
197
- }
198
- }' 2>/dev/null || echo '{}')
199
-
200
- # Fire and forget - don't block Cascade
201
- curl -s -X POST "$MEMORY_API_URL/api/v1/capture" \
202
- -H "Authorization: Bearer $AUTH_TOKEN" \
203
- -H "Content-Type: application/json" \
204
- -d "$CAPTURE_PAYLOAD" \
205
- --connect-timeout 2 \
206
- --max-time 3 >/dev/null 2>&1 &
207
-
208
- # Track pattern outcomes if acknowledgment found
209
- if [ "$PATTERN_COUNT" -gt 0 ]; then
210
- # Check for [ekkOS_SELECT] markers and track outcomes
211
- SELECT_BLOCK=$(echo "$RESPONSE_TEXT" | grep -ozP '\[ekkOS_SELECT\][\s\S]*?\[/ekkOS_SELECT\]' 2>/dev/null | tr '\0' '\n' || true)
212
- if [ -n "$SELECT_BLOCK" ]; then
213
- # Extract pattern IDs and track them
214
- echo "$SELECT_BLOCK" | grep -oE 'id:\s*[a-f0-9-]+' | sed 's/id:\s*//' | while read -r pattern_id; do
215
- # Track successful application
216
- curl -s -X POST "$MEMORY_API_URL/api/v1/patterns/outcome" \
217
- -H "Authorization: Bearer $AUTH_TOKEN" \
218
- -H "Content-Type: application/json" \
219
- -d "{\"pattern_id\": \"$pattern_id\", \"success\": true, \"source\": \"windsurf-cascade\"}" \
220
- --connect-timeout 1 \
221
- --max-time 2 >/dev/null 2>&1 &
222
- done
223
- fi
224
- fi
225
-
226
- # Clear last query so we don't double-capture
227
- rm -f "$STATE_DIR/last_query.txt" 2>/dev/null || true
228
- fi
229
-
230
- # ═══════════════════════════════════════════════════════════════════════════
231
- # Cleanup turn contract after successful capture
232
- # ═══════════════════════════════════════════════════════════════════════════
233
- cleanup_turn_contract "$SESSION_ID" "windsurf" "$PROJECT_ROOT" 2>/dev/null || true
234
-
235
- # Clean up pattern file
236
- rm -f "$STATE_DIR/patterns-${SESSION_ID}.json" 2>/dev/null || true
237
-
238
- # ═══════════════════════════════════════════════════════════════════════════
239
- # [NON-COMPLIANCE WARNINGS] If Golden Loop violations detected
240
- # ═══════════════════════════════════════════════════════════════════════════
241
- if [ "$IS_COMPLIANT" != "true" ] && [ "${EKKOS_STRICT:-0}" != "1" ]; then
242
- # Log violation for analysis (fire and forget)
243
- curl -s -X POST "$MEMORY_API_URL/api/v1/capture/violation" \
244
- -H "Authorization: Bearer $AUTH_TOKEN" \
245
- -H "Content-Type: application/json" \
246
- -d "{\"session_id\": \"windsurf-$SESSION_ID\", \"user_id\": \"${USER_ID:-system}\", \"violation_reason\": \"$VIOLATION_REASON\", \"turn_number\": $TURN_NUMBER}" \
247
- --connect-timeout 1 \
248
- --max-time 2 >/dev/null 2>&1 &
249
- fi
250
-
251
- exit 0