@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,435 +0,0 @@
1
- #!/bin/bash
2
- # ═══════════════════════════════════════════════════════════════════════════
3
- # ekkOS_ Hook: pre_user_prompt (Windsurf Cascade) - RETRIEVE + INJECT + CONTRACT
4
- #
5
- # ARCHITECTURE: Dumb Hook, Smart Backend
6
- # ═══════════════════════════════════════════════════════════════════════════
7
- # This hook runs BEFORE the user prompt is processed by Cascade.
8
- # It is THE CANONICAL retrieval path for Windsurf Cascade.
9
- #
10
- # FEATURES:
11
- # - Proactive pattern retrieval from all memory layers
12
- # - Endless Context automatic restoration
13
- # - Time Machine "Continue from here" support
14
- # - STRICT mode support (block on failed retrieval)
15
- # - Golden Loop compliance: Turn contract + PatternGuard + Footer
16
- #
17
- # Windsurf Hook Input:
18
- # {
19
- # "agent_action_name": "pre_user_prompt",
20
- # "trajectory_id": "...",
21
- # "execution_id": "...",
22
- # "timestamp": "...",
23
- # "tool_info": {"user_prompt": "..."}
24
- # }
25
- #
26
- # Output Format:
27
- # {"continue": true, "user_message": "augmented prompt"}
28
- # ═══════════════════════════════════════════════════════════════════════════
29
-
30
- set +e # Don't exit on errors - be bulletproof
31
-
32
- # Get project root
33
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
34
- PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
35
- STATE_DIR="$PROJECT_ROOT/.windsurf/state"
36
- mkdir -p "$STATE_DIR" 2>/dev/null || true
37
-
38
- # Load turn contract library
39
- if [ -f "$SCRIPT_DIR/lib/contract.sh" ]; then
40
- source "$SCRIPT_DIR/lib/contract.sh" 2>/dev/null || true
41
- fi
42
-
43
- # Fallback functions if library didn't load
44
- if ! command -v write_turn_contract >/dev/null 2>&1; then
45
- write_turn_contract() { return 0; }
46
- fi
47
- if ! command -v generate_query_hash >/dev/null 2>&1; then
48
- generate_query_hash() { echo "$(date +%s)"; }
49
- fi
50
- if ! command -v is_strict_mode >/dev/null 2>&1; then
51
- is_strict_mode() { [ "${EKKOS_STRICT:-0}" = "1" ]; }
52
- fi
53
- if ! command -v get_strict_blocker_message >/dev/null 2>&1; then
54
- get_strict_blocker_message() { echo "⛔ EKKOS_STRICT: Retrieval failed - DO NOT ANSWER"; }
55
- fi
56
-
57
- # ═══════════════════════════════════════════════════════════════════════════
58
- # Read JSON input from stdin (Windsurf format)
59
- # ═══════════════════════════════════════════════════════════════════════════
60
- INPUT=$(cat)
61
-
62
- # Extract prompt text from Windsurf format
63
- PROMPT_TEXT=$(echo "$INPUT" | jq -r '.tool_info.user_prompt // ""' 2>/dev/null || echo "")
64
-
65
- # Extract trajectory/session info
66
- TRAJECTORY_ID=$(echo "$INPUT" | jq -r '.trajectory_id // ""' 2>/dev/null || echo "")
67
- EXECUTION_ID=$(echo "$INPUT" | jq -r '.execution_id // ""' 2>/dev/null || echo "")
68
-
69
- # Generate session ID if not provided
70
- if [ -z "$TRAJECTORY_ID" ] || [ "$TRAJECTORY_ID" = "null" ]; then
71
- TRAJECTORY_ID="windsurf-$(date +%s)-$$"
72
- fi
73
-
74
- SESSION_ID="$TRAJECTORY_ID"
75
-
76
- # Skip if empty prompt
77
- if [ -z "$PROMPT_TEXT" ] || [ "$PROMPT_TEXT" = "null" ]; then
78
- echo '{"continue": true}'
79
- exit 0
80
- fi
81
-
82
- # Generate query hash for contract
83
- QUERY_HASH=$(generate_query_hash "$PROMPT_TEXT")
84
-
85
- # ═══════════════════════════════════════════════════════════════════════════
86
- # Load auth token - PORTABLE: Check 3 sources in priority order
87
- # ═══════════════════════════════════════════════════════════════════════════
88
- EKKOS_CONFIG="$HOME/.ekkos/config.json"
89
- AUTH_TOKEN=""
90
- USER_ID=""
91
-
92
- # 1. First try ~/.ekkos/config.json (set by VS Code extension - most portable)
93
- # Prefer hookApiKey (scoped key for hooks) over apiKey (legacy)
94
- if [ -f "$EKKOS_CONFIG" ]; then
95
- AUTH_TOKEN=$(jq -r '.hookApiKey // .apiKey // ""' "$EKKOS_CONFIG" 2>/dev/null || echo "")
96
- USER_ID=$(jq -r '.userId // ""' "$EKKOS_CONFIG" 2>/dev/null || echo "")
97
- fi
98
-
99
- # 2. Then try project .env.local (for developers)
100
- if [ -z "$AUTH_TOKEN" ] && [ -f "$PROJECT_ROOT/.env.local" ]; then
101
- AUTH_TOKEN=$(grep -E "^SUPABASE_SECRET_KEY=" "$PROJECT_ROOT/.env.local" | cut -d'=' -f2- | tr -d '"' | tr -d "'" | tr -d '\r')
102
- fi
103
-
104
- # 3. Finally try environment variable
105
- if [ -z "$AUTH_TOKEN" ]; then
106
- AUTH_TOKEN="${SUPABASE_SECRET_KEY:-}"
107
- fi
108
-
109
- # Track retrieval status
110
- RETRIEVAL_OK="false"
111
- RETRIEVED_PATTERN_IDS=""
112
- RETRIEVED_DIRECTIVE_IDS=""
113
-
114
- # Skip if no auth
115
- if [ -z "$AUTH_TOKEN" ]; then
116
- # STRICT MODE: Block turn if no auth
117
- if is_strict_mode; then
118
- BLOCKER_MSG=$(get_strict_blocker_message)
119
- write_turn_contract "$SESSION_ID" "false" "windsurf" "" "" "$QUERY_HASH" "$PROJECT_ROOT"
120
- echo "{\"continue\": false, \"user_message\": $(echo "$BLOCKER_MSG" | jq -R -s .)}"
121
- exit 0
122
- fi
123
-
124
- write_turn_contract "$SESSION_ID" "false" "windsurf" "" "" "$QUERY_HASH" "$PROJECT_ROOT"
125
- echo '{"continue": true, "user_message": "[ekkOS] No auth token. Run ekkOS: Connect in VS Code."}'
126
- exit 0
127
- fi
128
-
129
- # Cloud API
130
- MEMORY_API_URL="https://mcp.ekkos.dev"
131
-
132
- # ═══════════════════════════════════════════════════════════════════════════
133
- # [ENDLESS CONTEXT] Automatic context restoration (like Claude Code)
134
- # ═══════════════════════════════════════════════════════════════════════════
135
- # Cascade loses context silently - we detect and restore automatically.
136
- # Triggers: new session, time gap, or explicit request.
137
- # ═══════════════════════════════════════════════════════════════════════════
138
- ENDLESS_CONTEXT=""
139
- SESSION_FLAG="$STATE_DIR/session-active.flag"
140
- LAST_INTERACTION_FILE="$STATE_DIR/last-interaction.txt"
141
- TURN_COUNTER_FILE="$STATE_DIR/cascade-turn.txt"
142
-
143
- # Get current turn count
144
- CURRENT_TURN=1
145
- if [ -f "$TURN_COUNTER_FILE" ]; then
146
- CURRENT_TURN=$(cat "$TURN_COUNTER_FILE" 2>/dev/null || echo "0")
147
- CURRENT_TURN=$((CURRENT_TURN + 1))
148
- fi
149
- echo "$CURRENT_TURN" > "$TURN_COUNTER_FILE" 2>/dev/null || true
150
-
151
- # Determine if we should restore context
152
- SHOULD_RESTORE_CONTEXT=false
153
- RESTORE_REASON=""
154
-
155
- # 1. First turn of new session
156
- if [ ! -f "$SESSION_FLAG" ]; then
157
- SHOULD_RESTORE_CONTEXT=true
158
- RESTORE_REASON="new session"
159
- fi
160
-
161
- # 2. Time gap detection (context likely compacted)
162
- if [ -f "$LAST_INTERACTION_FILE" ]; then
163
- LAST_INTERACTION=$(cat "$LAST_INTERACTION_FILE" 2>/dev/null || echo "0")
164
- NOW=$(date +%s)
165
- SECONDS_AGO=$((NOW - LAST_INTERACTION))
166
-
167
- # 10 minutes = likely new conversation / compacted
168
- if [ "$SECONDS_AGO" -gt 600 ]; then
169
- SHOULD_RESTORE_CONTEXT=true
170
- RESTORE_REASON="10min gap"
171
- rm -f "$SESSION_FLAG" 2>/dev/null || true
172
- CURRENT_TURN=1
173
- echo "1" > "$TURN_COUNTER_FILE" 2>/dev/null || true
174
- fi
175
- fi
176
-
177
- # 3. User explicitly asks
178
- PROMPT_LOWER=$(echo "$PROMPT_TEXT" | tr '[:upper:]' '[:lower:]')
179
- if echo "$PROMPT_LOWER" | grep -qE "(recall|where were we|what were we|continue|restore context|load context|what did we)"; then
180
- SHOULD_RESTORE_CONTEXT=true
181
- RESTORE_REASON="user request"
182
- fi
183
-
184
- # Update last interaction
185
- date +%s > "$LAST_INTERACTION_FILE" 2>/dev/null || true
186
-
187
- # Auto-restore when triggered
188
- if [ "$SHOULD_RESTORE_CONTEXT" = true ] && [ -n "$USER_ID" ]; then
189
- # Fetch recent turns from L2
190
- RECENT_RESPONSE=$(curl -s -X POST "$MEMORY_API_URL/api/v1/turns/recall" \
191
- -H "Authorization: Bearer $AUTH_TOKEN" \
192
- -H "Content-Type: application/json" \
193
- -d "{\"user_id\": \"$USER_ID\", \"last_n\": 10, \"format\": \"summary\"}" \
194
- --connect-timeout 3 \
195
- --max-time 5 2>/dev/null || echo '{}')
196
-
197
- RECENT_COUNT=$(echo "$RECENT_RESPONSE" | jq '.turns // [] | length' 2>/dev/null || echo "0")
198
-
199
- if [ "$RECENT_COUNT" -gt 0 ]; then
200
- RECENT_CONTEXT=$(echo "$RECENT_RESPONSE" | jq -r '.formatted_context // ""' 2>/dev/null)
201
- TOTAL_TURNS=$(echo "$RECENT_RESPONSE" | jq -r '.total_turns_in_session // 0' 2>/dev/null)
202
-
203
- if [ -n "$RECENT_CONTEXT" ] && [ "$RECENT_CONTEXT" != "null" ]; then
204
- ENDLESS_CONTEXT="━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
205
- 🧠 ENDLESS CONTEXT · Auto-restored ($RESTORE_REASON)
206
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
207
-
208
- $RECENT_CONTEXT
209
-
210
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
211
- Turn $CURRENT_TURN · Session has $TOTAL_TURNS turns saved
212
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
213
-
214
- # Mark session as active
215
- echo "$(date +%s)" > "$SESSION_FLAG" 2>/dev/null || true
216
- fi
217
- fi
218
- fi
219
-
220
- # ═══════════════════════════════════════════════════════════════════════════
221
- # [TIME MACHINE] Check for pending "Continue from here" requests
222
- # ═══════════════════════════════════════════════════════════════════════════
223
- TIME_MACHINE_CONTEXT=""
224
- TIME_MACHINE_FLAG="$STATE_DIR/time-machine-consumed.flag"
225
-
226
- # Only check if we haven't already consumed a request this session
227
- if [ -n "$USER_ID" ] && [ ! -f "$TIME_MACHINE_FLAG" ]; then
228
- PENDING_RESPONSE=$(curl -s -X GET "$MEMORY_API_URL/api/v1/context/restore-request/pending?user_id=$USER_ID" \
229
- -H "Authorization: Bearer $AUTH_TOKEN" \
230
- --connect-timeout 2 \
231
- --max-time 3 2>/dev/null || echo '{}')
232
-
233
- IS_PENDING=$(echo "$PENDING_RESPONSE" | jq -r '.pending // false' 2>/dev/null)
234
-
235
- if [ "$IS_PENDING" = "true" ]; then
236
- TM_SESSION=$(echo "$PENDING_RESPONSE" | jq -r '.request.session_id // ""')
237
- TM_FROM_TURN=$(echo "$PENDING_RESPONSE" | jq -r '.request.from_turn // ""')
238
- TM_REQUEST_ID=$(echo "$PENDING_RESPONSE" | jq -r '.request.request_id // ""')
239
-
240
- if [ -n "$TM_SESSION" ]; then
241
- # Build recall request
242
- RECALL_BODY="{\"session_id\": \"$TM_SESSION\", \"last_n\": 15, \"format\": \"summary\"}"
243
- if [ -n "$TM_FROM_TURN" ] && [ "$TM_FROM_TURN" != "null" ]; then
244
- RECALL_BODY="{\"session_id\": \"$TM_SESSION\", \"from_turn\": $TM_FROM_TURN, \"format\": \"summary\"}"
245
- fi
246
-
247
- # Fetch turns
248
- TM_RESPONSE=$(curl -s -X POST "$MEMORY_API_URL/api/v1/turns/recall" \
249
- -H "Authorization: Bearer $AUTH_TOKEN" \
250
- -H "Content-Type: application/json" \
251
- -d "$RECALL_BODY" \
252
- --connect-timeout 3 \
253
- --max-time 5 2>/dev/null || echo '{}')
254
-
255
- TM_TURNS=$(echo "$TM_RESPONSE" | jq -r '.formatted_context // ""' 2>/dev/null)
256
-
257
- if [ -n "$TM_TURNS" ] && [ "$TM_TURNS" != "null" ]; then
258
- TIME_MACHINE_CONTEXT="━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
259
- ⏰ TIME MACHINE - Restored from past session
260
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
261
-
262
- $TM_TURNS
263
-
264
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
265
- Continue from where you left off!
266
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
267
-
268
- # Mark request as consumed
269
- curl -s -X POST "$MEMORY_API_URL/api/v1/context/restore-request/consume" \
270
- -H "Authorization: Bearer $AUTH_TOKEN" \
271
- -H "Content-Type: application/json" \
272
- -d "{\"request_id\": \"$TM_REQUEST_ID\"}" \
273
- --connect-timeout 2 \
274
- --max-time 3 >/dev/null 2>&1 || true
275
-
276
- # Set flag so we don't check again this session
277
- echo "$TM_REQUEST_ID" > "$TIME_MACHINE_FLAG" 2>/dev/null || true
278
- fi
279
- fi
280
- fi
281
- fi
282
-
283
- # ═══════════════════════════════════════════════════════════════════════════
284
- # [ekkOS_RETRIEVE] Search memory for patterns (all 8 queryable layers)
285
- # ═══════════════════════════════════════════════════════════════════════════
286
- JSON_PAYLOAD=$(jq -n \
287
- --arg query "$PROMPT_TEXT" \
288
- --arg user_id "${USER_ID:-system}" \
289
- --arg session "windsurf-$SESSION_ID" \
290
- '{
291
- query: $query,
292
- user_id: $user_id,
293
- session_id: $session,
294
- max_per_layer: 5,
295
- include_layers: ["working", "episodic", "semantic", "patterns", "procedural", "collective", "codebase", "directives"],
296
- metadata: { source: "windsurf-cascade-hook" }
297
- }' 2>/dev/null || echo '{}')
298
-
299
- API_RESPONSE=$(curl -s -X POST "$MEMORY_API_URL/api/v1/context/retrieve" \
300
- -H "Authorization: Bearer $AUTH_TOKEN" \
301
- -H "Content-Type: application/json" \
302
- -d "$JSON_PAYLOAD" \
303
- --connect-timeout 3 \
304
- --max-time 5 2>/dev/null || echo '{"error":"timeout"}')
305
-
306
- # Check if retrieval succeeded
307
- if echo "$API_RESPONSE" | jq -e '.layers' >/dev/null 2>&1; then
308
- RETRIEVAL_OK="true"
309
- else
310
- # STRICT MODE: Block turn if retrieval failed
311
- if is_strict_mode; then
312
- BLOCKER_MSG=$(get_strict_blocker_message)
313
- write_turn_contract "$SESSION_ID" "false" "windsurf" "" "" "$QUERY_HASH" "$PROJECT_ROOT"
314
- echo "{\"continue\": false, \"user_message\": $(echo "$BLOCKER_MSG" | jq -R -s .)}"
315
- exit 0
316
- fi
317
-
318
- API_RESPONSE='{"error":"timeout","formatted_context":"","layers":{"patterns":[],"directives":[]}}'
319
- fi
320
-
321
- # Extract counts
322
- PATTERN_COUNT=$(echo "$API_RESPONSE" | jq '.layers.patterns // [] | length' 2>/dev/null || echo "0")
323
- DIRECTIVE_COUNT=$(echo "$API_RESPONSE" | jq '.layers.directives // [] | length' 2>/dev/null || echo "0")
324
- TOTAL_COUNT=$((PATTERN_COUNT + DIRECTIVE_COUNT))
325
-
326
- # Extract pattern and directive IDs for turn contract
327
- RETRIEVED_PATTERN_IDS=$(echo "$API_RESPONSE" | jq -r '.layers.patterns // [] | map(.pattern_id // .id) | join(",")' 2>/dev/null || echo "")
328
- RETRIEVED_DIRECTIVE_IDS=$(echo "$API_RESPONSE" | jq -r '.layers.directives // [] | map(.directive_id // .id) | join(",")' 2>/dev/null || echo "")
329
-
330
- # ═══════════════════════════════════════════════════════════════════════════
331
- # [ekkOS_CONTRACT] Write turn contract as evidence of retrieval
332
- # ═══════════════════════════════════════════════════════════════════════════
333
- write_turn_contract "$SESSION_ID" "$RETRIEVAL_OK" "windsurf" "$RETRIEVED_PATTERN_IDS" "$RETRIEVED_DIRECTIVE_IDS" "$QUERY_HASH" "$PROJECT_ROOT"
334
-
335
- # Save session ID and query for post_cascade_response hook
336
- echo "$SESSION_ID" > "$STATE_DIR/current_session_id.txt" 2>/dev/null || true
337
- echo "$PROMPT_TEXT" > "$STATE_DIR/last_query.txt" 2>/dev/null || true
338
-
339
- # ═══════════════════════════════════════════════════════════════════════════
340
- # [ekkOS_INJECT] Build user message with patterns + footer requirement
341
- # ═══════════════════════════════════════════════════════════════════════════
342
- MESSAGE=""
343
-
344
- # Add Endless Context if present (auto-restored recent turns)
345
- if [ -n "$ENDLESS_CONTEXT" ]; then
346
- MESSAGE="$ENDLESS_CONTEXT
347
-
348
- "
349
- fi
350
-
351
- # Add Time Machine context if present (overrides Endless Context)
352
- if [ -n "$TIME_MACHINE_CONTEXT" ]; then
353
- MESSAGE="$TIME_MACHINE_CONTEXT
354
-
355
- "
356
- fi
357
-
358
- if [ "$PATTERN_COUNT" -gt 0 ]; then
359
- # Get formatted context
360
- FORMATTED=$(echo "$API_RESPONSE" | jq -r '.formatted_context // ""' 2>/dev/null)
361
-
362
- # Get pattern IDs for PatternGuard
363
- PATTERN_ID_LIST=$(echo "$API_RESPONSE" | jq -r '.layers.patterns[:5][] | .pattern_id // .id' 2>/dev/null || echo "")
364
-
365
- if [ -n "$FORMATTED" ] && [ "$FORMATTED" != "null" ]; then
366
- MESSAGE="${MESSAGE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
367
- 🧠 ekkOS™ Memory Substrate
368
- ✓ $PATTERN_COUNT patterns loaded from memory
369
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
370
-
371
- $FORMATTED
372
-
373
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
374
- ⚠️ PATTERNGUARD REQUIRED
375
-
376
- You MUST acknowledge ALL $PATTERN_COUNT pattern(s) above.
377
-
378
- Pattern IDs to acknowledge:
379
- $PATTERN_ID_LIST
380
-
381
- For patterns you USE:
382
- [ekkOS_SELECT]
383
- - id: <pattern-uuid>
384
- reason: <why using>
385
- confidence: <0.0-1.0>
386
- [/ekkOS_SELECT]
387
-
388
- For patterns you DO NOT use:
389
- [ekkOS_SKIP]
390
- - id: <pattern-uuid>
391
- reason: <why not relevant>
392
- [/ekkOS_SKIP]
393
-
394
- Coverage MUST be 100% (all IDs acknowledged).
395
-
396
- RESPONSE FORMAT: End with:
397
- 🧠 **ekkOS_™** · 📅 YYYY-MM-DD H:MM AM/PM TZ
398
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
399
- else
400
- # Fallback: minimal injection
401
- MESSAGE="${MESSAGE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
402
- 🧠 ekkOS™: $PATTERN_COUNT patterns found
403
-
404
- Pattern IDs to acknowledge with [ekkOS_SELECT] or [ekkOS_SKIP]:
405
- $PATTERN_ID_LIST
406
-
407
- End response with: 🧠 **ekkOS_™** · 📅 YYYY-MM-DD
408
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
409
- fi
410
-
411
- # Save patterns for post_cascade_response hook
412
- echo "$API_RESPONSE" | jq '.layers.patterns // []' > "$STATE_DIR/patterns-${SESSION_ID}.json" 2>/dev/null || true
413
-
414
- echo "{\"continue\": true, \"user_message\": $(echo "$MESSAGE" | jq -R -s .)}" | jq -c .
415
- elif [ -n "$TIME_MACHINE_CONTEXT" ] || [ -n "$ENDLESS_CONTEXT" ]; then
416
- # Time Machine or Endless Context only, no patterns - still require footer
417
- MESSAGE="${MESSAGE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
418
- 🧠 ekkOS™: Context restored, no new patterns
419
-
420
- End response with: 🧠 **ekkOS_™** · 📅 YYYY-MM-DD
421
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
422
-
423
- echo "{\"continue\": true, \"user_message\": $(echo "$MESSAGE" | jq -R -s .)}" | jq -c .
424
- else
425
- # No Time Machine, no patterns - still write contract and remind about footer
426
- MESSAGE="━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
427
- 🧠 ekkOS™: No patterns found (new territory)
428
-
429
- End response with: 🧠 **ekkOS_™** · 📅 YYYY-MM-DD
430
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
431
-
432
- echo "{\"continue\": true, \"user_message\": $(echo "$MESSAGE" | jq -R -s .)}" | jq -c .
433
- fi
434
-
435
- exit 0