@ekkos/cli 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/dist/cache/LocalSessionStore.d.ts +129 -0
  2. package/dist/cache/LocalSessionStore.js +688 -0
  3. package/dist/cache/capture.d.ts +26 -0
  4. package/dist/cache/capture.js +461 -0
  5. package/dist/cache/index.d.ts +7 -0
  6. package/dist/cache/index.js +23 -0
  7. package/dist/cache/types.d.ts +147 -0
  8. package/dist/cache/types.js +40 -0
  9. package/dist/commands/init.d.ts +9 -0
  10. package/dist/commands/init.js +478 -0
  11. package/dist/commands/run.d.ts +12 -0
  12. package/dist/commands/run.js +829 -0
  13. package/dist/commands/setup.d.ts +6 -0
  14. package/dist/commands/setup.js +658 -0
  15. package/dist/commands/status.d.ts +1 -0
  16. package/dist/commands/status.js +109 -0
  17. package/dist/commands/test.d.ts +1 -0
  18. package/dist/commands/test.js +157 -0
  19. package/dist/deploy/agents.d.ts +15 -0
  20. package/dist/deploy/agents.js +72 -0
  21. package/dist/deploy/hooks.d.ts +16 -0
  22. package/dist/deploy/hooks.js +121 -0
  23. package/dist/deploy/index.d.ts +7 -0
  24. package/dist/deploy/index.js +24 -0
  25. package/dist/deploy/instructions.d.ts +12 -0
  26. package/dist/deploy/instructions.js +36 -0
  27. package/dist/deploy/mcp.d.ts +19 -0
  28. package/dist/deploy/mcp.js +109 -0
  29. package/dist/deploy/plugins.d.ts +19 -0
  30. package/dist/deploy/plugins.js +62 -0
  31. package/dist/deploy/settings.d.ts +8 -0
  32. package/dist/deploy/settings.js +84 -0
  33. package/dist/deploy/skills.d.ts +19 -0
  34. package/dist/deploy/skills.js +60 -0
  35. package/dist/index.d.ts +2 -0
  36. package/dist/index.js +71 -0
  37. package/dist/restore/RestoreOrchestrator.d.ts +48 -0
  38. package/dist/restore/RestoreOrchestrator.js +481 -0
  39. package/dist/restore/index.d.ts +4 -0
  40. package/dist/restore/index.js +20 -0
  41. package/dist/utils/platform.d.ts +29 -0
  42. package/dist/utils/platform.js +65 -0
  43. package/dist/utils/session-words.json +119 -0
  44. package/dist/utils/state.d.ts +57 -0
  45. package/dist/utils/state.js +186 -0
  46. package/dist/utils/templates.d.ts +24 -0
  47. package/dist/utils/templates.js +118 -0
  48. package/package.json +48 -0
  49. package/templates/CLAUDE.md +287 -0
  50. package/templates/README.md +378 -0
  51. package/templates/agents/README.md +182 -0
  52. package/templates/agents/code-reviewer.md +166 -0
  53. package/templates/agents/debug-detective.md +169 -0
  54. package/templates/agents/ekkOS_Vercel.md +99 -0
  55. package/templates/agents/extension-manager.md +229 -0
  56. package/templates/agents/git-companion.md +185 -0
  57. package/templates/agents/github-test-agent.md +321 -0
  58. package/templates/agents/railway-manager.md +179 -0
  59. package/templates/claude-plugins/PHASE2_COMPLETION.md +346 -0
  60. package/templates/claude-plugins/PLUGIN_PROPOSALS.md +1776 -0
  61. package/templates/claude-plugins/README.md +587 -0
  62. package/templates/claude-plugins/agents/code-reviewer.json +14 -0
  63. package/templates/claude-plugins/agents/debug-detective.json +15 -0
  64. package/templates/claude-plugins/agents/git-companion.json +14 -0
  65. package/templates/claude-plugins/blog-manager/.claude-plugin/plugin.json +8 -0
  66. package/templates/claude-plugins/blog-manager/commands/blog.md +691 -0
  67. package/templates/claude-plugins/golden-loop-monitor/.claude-plugin/plugin.json +8 -0
  68. package/templates/claude-plugins/golden-loop-monitor/commands/loop-status.md +434 -0
  69. package/templates/claude-plugins/learning-tracker/.claude-plugin/plugin.json +8 -0
  70. package/templates/claude-plugins/learning-tracker/commands/my-patterns.md +282 -0
  71. package/templates/claude-plugins/memory-lens/.claude-plugin/plugin.json +8 -0
  72. package/templates/claude-plugins/memory-lens/commands/memory-search.md +181 -0
  73. package/templates/claude-plugins/pattern-coach/.claude-plugin/plugin.json +8 -0
  74. package/templates/claude-plugins/pattern-coach/commands/forge.md +365 -0
  75. package/templates/claude-plugins/project-schema-validator/.claude-plugin/plugin.json +8 -0
  76. package/templates/claude-plugins/project-schema-validator/commands/validate-schema.md +582 -0
  77. package/templates/claude-plugins-admin/AGENT_TEAM_PROPOSALS.md +819 -0
  78. package/templates/claude-plugins-admin/README.md +446 -0
  79. package/templates/claude-plugins-admin/autonomous-admin-agent/.claude-plugin/plugin.json +8 -0
  80. package/templates/claude-plugins-admin/autonomous-admin-agent/commands/agent.md +595 -0
  81. package/templates/claude-plugins-admin/backend-agent/.claude-plugin/plugin.json +8 -0
  82. package/templates/claude-plugins-admin/backend-agent/commands/backend.md +798 -0
  83. package/templates/claude-plugins-admin/deploy-guardian/.claude-plugin/plugin.json +8 -0
  84. package/templates/claude-plugins-admin/deploy-guardian/commands/deploy.md +554 -0
  85. package/templates/claude-plugins-admin/frontend-agent/.claude-plugin/plugin.json +8 -0
  86. package/templates/claude-plugins-admin/frontend-agent/commands/frontend.md +881 -0
  87. package/templates/claude-plugins-admin/mcp-server-manager/.claude-plugin/plugin.json +8 -0
  88. package/templates/claude-plugins-admin/mcp-server-manager/commands/mcp.md +85 -0
  89. package/templates/claude-plugins-admin/memory-system-monitor/.claude-plugin/plugin.json +8 -0
  90. package/templates/claude-plugins-admin/memory-system-monitor/commands/memory-health.md +569 -0
  91. package/templates/claude-plugins-admin/qa-agent/.claude-plugin/plugin.json +8 -0
  92. package/templates/claude-plugins-admin/qa-agent/commands/qa.md +863 -0
  93. package/templates/claude-plugins-admin/tech-lead-agent/.claude-plugin/plugin.json +8 -0
  94. package/templates/claude-plugins-admin/tech-lead-agent/commands/lead.md +732 -0
  95. package/templates/commands/continue.md +47 -0
  96. package/templates/cursor-hooks/after-agent-response.sh +117 -0
  97. package/templates/cursor-hooks/before-submit-prompt.sh +419 -0
  98. package/templates/cursor-hooks/hooks.json +20 -0
  99. package/templates/cursor-hooks/lib/contract.sh +320 -0
  100. package/templates/cursor-hooks/stop.sh +75 -0
  101. package/templates/cursor-rules/ekkos-memory.md +187 -0
  102. package/templates/hooks/assistant-response.sh +96 -0
  103. package/templates/hooks/hooks.json +28 -0
  104. package/templates/hooks/lib/contract.sh +320 -0
  105. package/templates/hooks/lib/state.sh +158 -0
  106. package/templates/hooks/session-start.ps1 +41 -0
  107. package/templates/hooks/session-start.sh +318 -0
  108. package/templates/hooks/stop.ps1 +16 -0
  109. package/templates/hooks/stop.sh +989 -0
  110. package/templates/hooks/user-prompt-submit.ps1 +174 -0
  111. package/templates/hooks/user-prompt-submit.sh +587 -0
  112. package/templates/hooks-node/lib/state.js +187 -0
  113. package/templates/hooks-node/stop.js +416 -0
  114. package/templates/hooks-node/user-prompt-submit.js +337 -0
  115. package/templates/plan-template.md +306 -0
  116. package/templates/rules/00-hooks-contract.mdc +89 -0
  117. package/templates/rules/30-ekkos-core.mdc +188 -0
  118. package/templates/rules/31-ekkos-messages.mdc +78 -0
  119. package/templates/skills/continue/SKILL.md +169 -0
  120. package/templates/skills/ekkOS_Deep_Recall/Skill.md +282 -0
  121. package/templates/skills/ekkOS_Learn/Skill.md +265 -0
  122. package/templates/skills/ekkOS_Memory_First/Skill.md +206 -0
  123. package/templates/skills/ekkOS_Plan_Assist/Skill.md +302 -0
  124. package/templates/skills/ekkOS_Preferences/Skill.md +247 -0
  125. package/templates/skills/ekkOS_Reflect/Skill.md +257 -0
  126. package/templates/skills/ekkOS_Safety/Skill.md +265 -0
  127. package/templates/skills/ekkOS_Schema/Skill.md +251 -0
  128. package/templates/skills/ekkOS_Summary/Skill.md +257 -0
  129. package/templates/skills/ekkOS_Vault/Skill.md +287 -0
  130. package/templates/skills/permissions/Skill.md +322 -0
  131. package/templates/spec-template.md +159 -0
  132. package/templates/windsurf-hooks/before-submit-prompt.sh +238 -0
  133. package/templates/windsurf-hooks/hooks.json +10 -0
  134. package/templates/windsurf-hooks/lib/contract.sh +320 -0
  135. package/templates/windsurf-rules/ekkos-memory.md +129 -0
@@ -0,0 +1,47 @@
1
+ # /continue
2
+
3
+ Restore your last 5 turns after running `/clear`.
4
+
5
+ ## Usage
6
+
7
+ ```
8
+ /clear # First: free up context
9
+ /continue # Then: restore last 5 turns
10
+ ```
11
+
12
+ ## What Happens
13
+
14
+ 1. Hook detects `/continue`
15
+ 2. Fetches last 5 turns from ekkOS API
16
+ 3. Injects them as context
17
+ 4. Claude continues seamlessly
18
+
19
+ ## Why This Exists
20
+
21
+ When context gets full (90%+), you need to `/clear` but don't want to lose your work. This command restores just enough context (5 turns) to continue working without re-explaining everything.
22
+
23
+ ## The Flow
24
+
25
+ ```
26
+ Work normally until context ~90%
27
+
28
+ Run: /clear (frees context)
29
+
30
+ Run: /continue (restores 5 turns)
31
+
32
+ Keep working
33
+ ```
34
+
35
+ ## Example
36
+
37
+ ```
38
+ [Context at 92%]
39
+
40
+ You: /clear
41
+ Claude: Context cleared.
42
+
43
+ You: /continue
44
+ Hook: ✓ Session continued (5 turns restored)
45
+
46
+ Claude: ✓ **Continuing** - We were working on the /continue command...
47
+ ```
@@ -0,0 +1,117 @@
1
+ #!/bin/bash
2
+ # ═══════════════════════════════════════════════════════════════════════════
3
+ # ekkOS_ Hook: AfterAgentResponse (Cursor Agent Mode)
4
+ #
5
+ # Captures agent responses for turn storage in ekkOS L2 (episodic memory)
6
+ # This enables Time Machine to work with Cursor Agent conversations
7
+ # ═══════════════════════════════════════════════════════════════════════════
8
+
9
+ set +e
10
+
11
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
+ PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
13
+ STATE_DIR="$PROJECT_ROOT/.cursor/state"
14
+ mkdir -p "$STATE_DIR" 2>/dev/null || true
15
+
16
+ # Read JSON input
17
+ INPUT=$(cat)
18
+
19
+ # Extract response text
20
+ RESPONSE_TEXT=$(echo "$INPUT" | jq -r '.text // ""' 2>/dev/null || echo "")
21
+
22
+ # Skip if empty
23
+ if [ -z "$RESPONSE_TEXT" ] || [ "$RESPONSE_TEXT" = "null" ]; then
24
+ exit 0
25
+ fi
26
+
27
+ # ═══════════════════════════════════════════════════════════════════════════
28
+ # Load auth
29
+ # ═══════════════════════════════════════════════════════════════════════════
30
+ EKKOS_CONFIG="$HOME/.ekkos/config.json"
31
+ AUTH_TOKEN=""
32
+ USER_ID=""
33
+
34
+ if [ -f "$EKKOS_CONFIG" ]; then
35
+ AUTH_TOKEN=$(jq -r '.hookApiKey // .apiKey // ""' "$EKKOS_CONFIG" 2>/dev/null || echo "")
36
+ USER_ID=$(jq -r '.userId // ""' "$EKKOS_CONFIG" 2>/dev/null || echo "")
37
+ fi
38
+
39
+ if [ -z "$AUTH_TOKEN" ] && [ -f "$PROJECT_ROOT/.env.local" ]; then
40
+ AUTH_TOKEN=$(grep -E "^SUPABASE_SECRET_KEY=" "$PROJECT_ROOT/.env.local" | cut -d'=' -f2- | tr -d '"' | tr -d "'" | tr -d '\r')
41
+ fi
42
+
43
+ [ -z "$AUTH_TOKEN" ] && exit 0
44
+
45
+ MEMORY_API_URL="https://mcp.ekkos.dev"
46
+
47
+ # ═══════════════════════════════════════════════════════════════════════════
48
+ # Load session state
49
+ # ═══════════════════════════════════════════════════════════════════════════
50
+ SESSION_ID=""
51
+ LAST_QUERY=""
52
+
53
+ if [ -f "$STATE_DIR/current_session_id.txt" ]; then
54
+ SESSION_ID=$(cat "$STATE_DIR/current_session_id.txt" 2>/dev/null || echo "")
55
+ fi
56
+
57
+ if [ -f "$STATE_DIR/last_query.txt" ]; then
58
+ LAST_QUERY=$(cat "$STATE_DIR/last_query.txt" 2>/dev/null || echo "")
59
+ fi
60
+
61
+ # Generate session ID if missing
62
+ if [ -z "$SESSION_ID" ]; then
63
+ SESSION_ID="cursor-agent-$(date +%s)-$$"
64
+ echo "$SESSION_ID" > "$STATE_DIR/current_session_id.txt"
65
+ fi
66
+
67
+ # ═══════════════════════════════════════════════════════════════════════════
68
+ # Get turn number
69
+ # ═══════════════════════════════════════════════════════════════════════════
70
+ TURN_FILE="$STATE_DIR/turn_${SESSION_ID}.txt"
71
+ TURN_NUMBER=1
72
+ if [ -f "$TURN_FILE" ]; then
73
+ TURN_NUMBER=$(cat "$TURN_FILE" 2>/dev/null || echo "0")
74
+ TURN_NUMBER=$((TURN_NUMBER + 1))
75
+ fi
76
+ echo "$TURN_NUMBER" > "$TURN_FILE"
77
+
78
+ # ═══════════════════════════════════════════════════════════════════════════
79
+ # Capture turn to ekkOS L2
80
+ # ═══════════════════════════════════════════════════════════════════════════
81
+ # Only capture if we have a query to pair with
82
+ if [ -n "$LAST_QUERY" ]; then
83
+ # Truncate for API limits
84
+ QUERY_TRUNCATED="${LAST_QUERY:0:10000}"
85
+ RESPONSE_TRUNCATED="${RESPONSE_TEXT:0:50000}"
86
+
87
+ CAPTURE_PAYLOAD=$(jq -n \
88
+ --arg session_id "cursor-agent-$SESSION_ID" \
89
+ --arg user_id "${USER_ID:-system}" \
90
+ --arg user_query "$QUERY_TRUNCATED" \
91
+ --arg assistant_response "$RESPONSE_TRUNCATED" \
92
+ --argjson turn_number "$TURN_NUMBER" \
93
+ '{
94
+ session_id: $session_id,
95
+ user_id: $user_id,
96
+ user_query: $user_query,
97
+ assistant_response: $assistant_response,
98
+ metadata: {
99
+ source: "cursor-agent",
100
+ turn_number: $turn_number,
101
+ agent_mode: true
102
+ }
103
+ }' 2>/dev/null || echo '{}')
104
+
105
+ # Fire and forget - don't block agent
106
+ curl -s -X POST "$MEMORY_API_URL/api/v1/capture" \
107
+ -H "Authorization: Bearer $AUTH_TOKEN" \
108
+ -H "Content-Type: application/json" \
109
+ -d "$CAPTURE_PAYLOAD" \
110
+ --connect-timeout 2 \
111
+ --max-time 3 >/dev/null 2>&1 &
112
+
113
+ # Clear last query
114
+ rm -f "$STATE_DIR/last_query.txt" 2>/dev/null || true
115
+ fi
116
+
117
+ exit 0
@@ -0,0 +1,419 @@
1
+ #!/bin/bash
2
+ # ═══════════════════════════════════════════════════════════════════════════
3
+ # ekkOS_ Hook: BeforeSubmitPrompt (Cursor) - RETRIEVE + INJECT + TIME MACHINE
4
+ #
5
+ # ARCHITECTURE: Dumb Hook, Smart Backend
6
+ # ═══════════════════════════════════════════════════════════════════════════
7
+ # This hook runs BEFORE the prompt is sent to the AI.
8
+ # It is THE CANONICAL retrieval path for Cursor.
9
+ #
10
+ # FEATURES:
11
+ # - Pattern retrieval from all memory layers
12
+ # - Time Machine "Continue from here" support
13
+ # - Turn contract for PatternGuard validation
14
+ # - STRICT mode support (block on failed retrieval)
15
+ # ═══════════════════════════════════════════════════════════════════════════
16
+
17
+ set +e # Don't exit on errors - be bulletproof
18
+
19
+ # Get project root
20
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
21
+ PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
22
+ STATE_DIR="$PROJECT_ROOT/.cursor/state"
23
+ mkdir -p "$STATE_DIR" 2>/dev/null || true
24
+
25
+ # Load turn contract library
26
+ if [ -f "$SCRIPT_DIR/lib/contract.sh" ]; then
27
+ source "$SCRIPT_DIR/lib/contract.sh" 2>/dev/null || true
28
+ fi
29
+
30
+ # Fallback functions if library didn't load
31
+ if ! command -v write_turn_contract >/dev/null 2>&1; then
32
+ write_turn_contract() { return 0; }
33
+ fi
34
+ if ! command -v generate_query_hash >/dev/null 2>&1; then
35
+ generate_query_hash() { echo "$(date +%s)"; }
36
+ fi
37
+ if ! command -v is_strict_mode >/dev/null 2>&1; then
38
+ is_strict_mode() { [ "${EKKOS_STRICT:-0}" = "1" ]; }
39
+ fi
40
+ if ! command -v get_strict_blocker_message >/dev/null 2>&1; then
41
+ get_strict_blocker_message() { echo "⛔ EKKOS_STRICT: Retrieval failed - DO NOT ANSWER"; }
42
+ fi
43
+
44
+ # Read JSON input from stdin
45
+ INPUT=$(cat)
46
+
47
+ # Extract prompt text and session info
48
+ PROMPT_TEXT=$(echo "$INPUT" | jq -r '.prompt // .text // ""' 2>/dev/null || echo "")
49
+ SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // .conversation_id // ""' 2>/dev/null || echo "")
50
+ MODEL_INFO=$(echo "$INPUT" | jq -r '.model // ""' 2>/dev/null || echo "")
51
+
52
+ # Generate session ID if not provided
53
+ if [ -z "$SESSION_ID" ] || [ "$SESSION_ID" = "null" ]; then
54
+ SESSION_ID="cursor-$(date +%s)-$$"
55
+ fi
56
+
57
+ # Skip if empty
58
+ if [ -z "$PROMPT_TEXT" ] || [ "$PROMPT_TEXT" = "null" ]; then
59
+ echo '{"continue": true}'
60
+ exit 0
61
+ fi
62
+
63
+ # Generate query hash for contract
64
+ QUERY_HASH=$(generate_query_hash "$PROMPT_TEXT")
65
+
66
+ # ═══════════════════════════════════════════════════════════════════════════
67
+ # Load auth token - PORTABLE: Check 3 sources in priority order
68
+ # ═══════════════════════════════════════════════════════════════════════════
69
+ EKKOS_CONFIG="$HOME/.ekkos/config.json"
70
+ AUTH_TOKEN=""
71
+ USER_ID=""
72
+
73
+ # 1. First try ~/.ekkos/config.json (set by VS Code extension - most portable)
74
+ # Prefer hookApiKey (scoped key for hooks) over apiKey (legacy)
75
+ if [ -f "$EKKOS_CONFIG" ]; then
76
+ AUTH_TOKEN=$(jq -r '.hookApiKey // .apiKey // ""' "$EKKOS_CONFIG" 2>/dev/null || echo "")
77
+ USER_ID=$(jq -r '.userId // ""' "$EKKOS_CONFIG" 2>/dev/null || echo "")
78
+ fi
79
+
80
+ # 2. Then try project .env.local (for developers)
81
+ if [ -z "$AUTH_TOKEN" ] && [ -f "$PROJECT_ROOT/.env.local" ]; then
82
+ AUTH_TOKEN=$(grep -E "^SUPABASE_SECRET_KEY=" "$PROJECT_ROOT/.env.local" | cut -d'=' -f2- | tr -d '"' | tr -d "'" | tr -d '\r')
83
+ fi
84
+
85
+ # 3. Finally try environment variable
86
+ if [ -z "$AUTH_TOKEN" ]; then
87
+ AUTH_TOKEN="${SUPABASE_SECRET_KEY:-}"
88
+ fi
89
+
90
+ # Track retrieval status
91
+ RETRIEVAL_OK="false"
92
+ RETRIEVED_PATTERN_IDS=""
93
+ RETRIEVED_DIRECTIVE_IDS=""
94
+
95
+ # Skip if no auth
96
+ if [ -z "$AUTH_TOKEN" ]; then
97
+ # STRICT MODE: Block turn if no auth
98
+ if is_strict_mode; then
99
+ BLOCKER_MSG=$(get_strict_blocker_message)
100
+ write_turn_contract "$SESSION_ID" "false" "cursor" "" "" "$QUERY_HASH" "$PROJECT_ROOT"
101
+ echo "{\"continue\": false, \"user_message\": $(echo "$BLOCKER_MSG" | jq -R -s .)}"
102
+ exit 0
103
+ fi
104
+
105
+ write_turn_contract "$SESSION_ID" "false" "cursor" "" "" "$QUERY_HASH" "$PROJECT_ROOT"
106
+ echo '{"continue": true, "user_message": "[ekkOS] No auth token. Run ekkOS: Connect in VS Code."}'
107
+ exit 0
108
+ fi
109
+
110
+ # Cloud API
111
+ MEMORY_API_URL="https://mcp.ekkos.dev"
112
+
113
+ # ═══════════════════════════════════════════════════════════════════════════
114
+ # [ENDLESS CONTEXT] Automatic context restoration (like Claude Code)
115
+ # ═══════════════════════════════════════════════════════════════════════════
116
+ # Cursor loses context silently - we detect and restore automatically.
117
+ # Triggers: new session, time gap, or explicit request.
118
+ # ═══════════════════════════════════════════════════════════════════════════
119
+ ENDLESS_CONTEXT=""
120
+ SESSION_FLAG="$STATE_DIR/session-active.flag"
121
+ LAST_INTERACTION_FILE="$STATE_DIR/last-interaction.txt"
122
+ TURN_COUNTER_FILE="$STATE_DIR/cursor-turn.txt"
123
+
124
+ # Get current turn count
125
+ CURRENT_TURN=1
126
+ if [ -f "$TURN_COUNTER_FILE" ]; then
127
+ CURRENT_TURN=$(cat "$TURN_COUNTER_FILE" 2>/dev/null || echo "0")
128
+ CURRENT_TURN=$((CURRENT_TURN + 1))
129
+ fi
130
+ echo "$CURRENT_TURN" > "$TURN_COUNTER_FILE" 2>/dev/null || true
131
+
132
+ # Determine if we should restore context
133
+ SHOULD_RESTORE_CONTEXT=false
134
+ RESTORE_REASON=""
135
+
136
+ # 1. First turn of new session
137
+ if [ ! -f "$SESSION_FLAG" ]; then
138
+ SHOULD_RESTORE_CONTEXT=true
139
+ RESTORE_REASON="new session"
140
+ fi
141
+
142
+ # 2. Time gap detection (context likely compacted)
143
+ if [ -f "$LAST_INTERACTION_FILE" ]; then
144
+ LAST_INTERACTION=$(cat "$LAST_INTERACTION_FILE" 2>/dev/null || echo "0")
145
+ NOW=$(date +%s)
146
+ SECONDS_AGO=$((NOW - LAST_INTERACTION))
147
+
148
+ # 10 minutes = likely new conversation / compacted
149
+ if [ "$SECONDS_AGO" -gt 600 ]; then
150
+ SHOULD_RESTORE_CONTEXT=true
151
+ RESTORE_REASON="10min gap"
152
+ rm -f "$SESSION_FLAG" 2>/dev/null || true
153
+ CURRENT_TURN=1
154
+ echo "1" > "$TURN_COUNTER_FILE" 2>/dev/null || true
155
+ fi
156
+ fi
157
+
158
+ # 3. User explicitly asks
159
+ PROMPT_LOWER=$(echo "$PROMPT_TEXT" | tr '[:upper:]' '[:lower:]')
160
+ if echo "$PROMPT_LOWER" | grep -qE "(recall|where were we|what were we|continue|restore context|load context|what did we)"; then
161
+ SHOULD_RESTORE_CONTEXT=true
162
+ RESTORE_REASON="user request"
163
+ fi
164
+
165
+ # Update last interaction
166
+ date +%s > "$LAST_INTERACTION_FILE" 2>/dev/null || true
167
+
168
+ # Auto-restore when triggered
169
+ if [ "$SHOULD_RESTORE_CONTEXT" = true ] && [ -n "$USER_ID" ]; then
170
+ # Fetch recent turns from L2
171
+ RECENT_RESPONSE=$(curl -s -X POST "$MEMORY_API_URL/api/v1/turns/recall" \
172
+ -H "Authorization: Bearer $AUTH_TOKEN" \
173
+ -H "Content-Type: application/json" \
174
+ -d "{\"user_id\": \"$USER_ID\", \"last_n\": 10, \"format\": \"summary\"}" \
175
+ --connect-timeout 3 \
176
+ --max-time 5 2>/dev/null || echo '{}')
177
+
178
+ RECENT_COUNT=$(echo "$RECENT_RESPONSE" | jq '.turns // [] | length' 2>/dev/null || echo "0")
179
+
180
+ if [ "$RECENT_COUNT" -gt 0 ]; then
181
+ RECENT_CONTEXT=$(echo "$RECENT_RESPONSE" | jq -r '.formatted_context // ""' 2>/dev/null)
182
+ TOTAL_TURNS=$(echo "$RECENT_RESPONSE" | jq -r '.total_turns_in_session // 0' 2>/dev/null)
183
+
184
+ if [ -n "$RECENT_CONTEXT" ] && [ "$RECENT_CONTEXT" != "null" ]; then
185
+ ENDLESS_CONTEXT="━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
186
+ 🧠 ENDLESS CONTEXT · Auto-restored ($RESTORE_REASON)
187
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
188
+
189
+ $RECENT_CONTEXT
190
+
191
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
192
+ Turn $CURRENT_TURN · Session has $TOTAL_TURNS turns saved
193
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
194
+
195
+ # Mark session as active
196
+ echo "$(date +%s)" > "$SESSION_FLAG" 2>/dev/null || true
197
+ fi
198
+ fi
199
+ fi
200
+
201
+ # ═══════════════════════════════════════════════════════════════════════════
202
+ # [TIME MACHINE] Check for pending "Continue from here" requests
203
+ # ═══════════════════════════════════════════════════════════════════════════
204
+ TIME_MACHINE_CONTEXT=""
205
+ TIME_MACHINE_FLAG="$STATE_DIR/time-machine-consumed.flag"
206
+
207
+ # Only check if we haven't already consumed a request this session
208
+ if [ -n "$USER_ID" ] && [ ! -f "$TIME_MACHINE_FLAG" ]; then
209
+ PENDING_RESPONSE=$(curl -s -X GET "$MEMORY_API_URL/api/v1/context/restore-request/pending?user_id=$USER_ID" \
210
+ -H "Authorization: Bearer $AUTH_TOKEN" \
211
+ --connect-timeout 2 \
212
+ --max-time 3 2>/dev/null || echo '{}')
213
+
214
+ IS_PENDING=$(echo "$PENDING_RESPONSE" | jq -r '.pending // false' 2>/dev/null)
215
+
216
+ if [ "$IS_PENDING" = "true" ]; then
217
+ TM_SESSION=$(echo "$PENDING_RESPONSE" | jq -r '.request.session_id // ""')
218
+ TM_FROM_TURN=$(echo "$PENDING_RESPONSE" | jq -r '.request.from_turn // ""')
219
+ TM_REQUEST_ID=$(echo "$PENDING_RESPONSE" | jq -r '.request.request_id // ""')
220
+
221
+ if [ -n "$TM_SESSION" ]; then
222
+ # Build recall request
223
+ RECALL_BODY="{\"session_id\": \"$TM_SESSION\", \"last_n\": 15, \"format\": \"summary\"}"
224
+ if [ -n "$TM_FROM_TURN" ] && [ "$TM_FROM_TURN" != "null" ]; then
225
+ RECALL_BODY="{\"session_id\": \"$TM_SESSION\", \"from_turn\": $TM_FROM_TURN, \"format\": \"summary\"}"
226
+ fi
227
+
228
+ # Fetch turns
229
+ TM_RESPONSE=$(curl -s -X POST "$MEMORY_API_URL/api/v1/turns/recall" \
230
+ -H "Authorization: Bearer $AUTH_TOKEN" \
231
+ -H "Content-Type: application/json" \
232
+ -d "$RECALL_BODY" \
233
+ --connect-timeout 3 \
234
+ --max-time 5 2>/dev/null || echo '{}')
235
+
236
+ TM_TURNS=$(echo "$TM_RESPONSE" | jq -r '.formatted_context // ""' 2>/dev/null)
237
+
238
+ if [ -n "$TM_TURNS" ] && [ "$TM_TURNS" != "null" ]; then
239
+ TIME_MACHINE_CONTEXT="━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
240
+ ⏰ TIME MACHINE - Restored from past session
241
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
242
+
243
+ $TM_TURNS
244
+
245
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
246
+ Continue from where you left off!
247
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
248
+
249
+ # Mark request as consumed
250
+ curl -s -X POST "$MEMORY_API_URL/api/v1/context/restore-request/consume" \
251
+ -H "Authorization: Bearer $AUTH_TOKEN" \
252
+ -H "Content-Type: application/json" \
253
+ -d "{\"request_id\": \"$TM_REQUEST_ID\"}" \
254
+ --connect-timeout 2 \
255
+ --max-time 3 >/dev/null 2>&1 || true
256
+
257
+ # Set flag so we don't check again this session
258
+ echo "$TM_REQUEST_ID" > "$TIME_MACHINE_FLAG" 2>/dev/null || true
259
+ fi
260
+ fi
261
+ fi
262
+ fi
263
+
264
+ # ═══════════════════════════════════════════════════════════════════════════
265
+ # [ekkOS_RETRIEVE] Search memory for patterns (all 8 queryable layers)
266
+ # ═══════════════════════════════════════════════════════════════════════════
267
+ JSON_PAYLOAD=$(jq -n \
268
+ --arg query "$PROMPT_TEXT" \
269
+ --arg user_id "${USER_ID:-system}" \
270
+ --arg session "cursor-$SESSION_ID" \
271
+ '{
272
+ query: $query,
273
+ user_id: $user_id,
274
+ session_id: $session,
275
+ max_per_layer: 5,
276
+ include_layers: ["working", "episodic", "semantic", "patterns", "procedural", "collective", "codebase", "directives"],
277
+ metadata: { source: "cursor-hook" }
278
+ }' 2>/dev/null || echo '{}')
279
+
280
+ API_RESPONSE=$(curl -s -X POST "$MEMORY_API_URL/api/v1/context/retrieve" \
281
+ -H "Authorization: Bearer $AUTH_TOKEN" \
282
+ -H "Content-Type: application/json" \
283
+ -d "$JSON_PAYLOAD" \
284
+ --connect-timeout 3 \
285
+ --max-time 5 2>/dev/null || echo '{"error":"timeout"}')
286
+
287
+ # Check if retrieval succeeded
288
+ if echo "$API_RESPONSE" | jq -e '.layers' >/dev/null 2>&1; then
289
+ RETRIEVAL_OK="true"
290
+ else
291
+ # STRICT MODE: Block turn if retrieval failed
292
+ if is_strict_mode; then
293
+ BLOCKER_MSG=$(get_strict_blocker_message)
294
+ write_turn_contract "$SESSION_ID" "false" "cursor" "" "" "$QUERY_HASH" "$PROJECT_ROOT"
295
+ echo "{\"continue\": false, \"user_message\": $(echo "$BLOCKER_MSG" | jq -R -s .)}"
296
+ exit 0
297
+ fi
298
+
299
+ API_RESPONSE='{"error":"timeout","formatted_context":"","layers":{"patterns":[],"directives":[]}}'
300
+ fi
301
+
302
+ # Extract counts
303
+ PATTERN_COUNT=$(echo "$API_RESPONSE" | jq '.layers.patterns // [] | length' 2>/dev/null || echo "0")
304
+ DIRECTIVE_COUNT=$(echo "$API_RESPONSE" | jq '.layers.directives // [] | length' 2>/dev/null || echo "0")
305
+ TOTAL_COUNT=$((PATTERN_COUNT + DIRECTIVE_COUNT))
306
+
307
+ # Extract pattern and directive IDs for turn contract
308
+ RETRIEVED_PATTERN_IDS=$(echo "$API_RESPONSE" | jq -r '.layers.patterns // [] | map(.pattern_id // .id) | join(",")' 2>/dev/null || echo "")
309
+ RETRIEVED_DIRECTIVE_IDS=$(echo "$API_RESPONSE" | jq -r '.layers.directives // [] | map(.directive_id // .id) | join(",")' 2>/dev/null || echo "")
310
+
311
+ # ═══════════════════════════════════════════════════════════════════════════
312
+ # [ekkOS_CONTRACT] Write turn contract as evidence of retrieval
313
+ # ═══════════════════════════════════════════════════════════════════════════
314
+ write_turn_contract "$SESSION_ID" "$RETRIEVAL_OK" "cursor" "$RETRIEVED_PATTERN_IDS" "$RETRIEVED_DIRECTIVE_IDS" "$QUERY_HASH" "$PROJECT_ROOT"
315
+
316
+ # Save session ID and query for Agent mode hooks
317
+ echo "$SESSION_ID" > "$STATE_DIR/current_session_id.txt" 2>/dev/null || true
318
+ echo "$PROMPT_TEXT" > "$STATE_DIR/last_query.txt" 2>/dev/null || true
319
+
320
+ # ═══════════════════════════════════════════════════════════════════════════
321
+ # [ekkOS_INJECT] Build user message with patterns
322
+ # ═══════════════════════════════════════════════════════════════════════════
323
+ # ═══════════════════════════════════════════════════════════════════════════
324
+ # Build final message with Time Machine + Patterns
325
+ # ═══════════════════════════════════════════════════════════════════════════
326
+ MESSAGE=""
327
+
328
+ # Add Endless Context if present (auto-restored recent turns)
329
+ if [ -n "$ENDLESS_CONTEXT" ]; then
330
+ MESSAGE="$ENDLESS_CONTEXT
331
+
332
+ "
333
+ fi
334
+
335
+ # Add Time Machine context if present (overrides Endless Context)
336
+ if [ -n "$TIME_MACHINE_CONTEXT" ]; then
337
+ MESSAGE="$TIME_MACHINE_CONTEXT
338
+
339
+ "
340
+ fi
341
+
342
+ if [ "$PATTERN_COUNT" -gt 0 ]; then
343
+ # Get formatted context
344
+ FORMATTED=$(echo "$API_RESPONSE" | jq -r '.formatted_context // ""' 2>/dev/null)
345
+
346
+ # Get pattern IDs for PatternGuard
347
+ PATTERN_ID_LIST=$(echo "$API_RESPONSE" | jq -r '.layers.patterns[:5][] | .pattern_id // .id' 2>/dev/null || echo "")
348
+
349
+ if [ -n "$FORMATTED" ] && [ "$FORMATTED" != "null" ]; then
350
+ MESSAGE="${MESSAGE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
351
+ 🧠 ekkOS™ Memory Substrate
352
+ ✓ $PATTERN_COUNT patterns loaded from memory
353
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
354
+
355
+ $FORMATTED
356
+
357
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
358
+ ⚠️ PATTERNGUARD REQUIRED
359
+
360
+ You MUST acknowledge ALL $PATTERN_COUNT pattern(s) above.
361
+
362
+ Pattern IDs to acknowledge:
363
+ $PATTERN_ID_LIST
364
+
365
+ For patterns you USE:
366
+ [ekkOS_SELECT]
367
+ - id: <pattern-uuid>
368
+ reason: <why using>
369
+ confidence: <0.0-1.0>
370
+ [/ekkOS_SELECT]
371
+
372
+ For patterns you DO NOT use:
373
+ [ekkOS_SKIP]
374
+ - id: <pattern-uuid>
375
+ reason: <why not relevant>
376
+ [/ekkOS_SKIP]
377
+
378
+ Coverage MUST be 100% (all IDs acknowledged).
379
+
380
+ RESPONSE FORMAT: End with:
381
+ 🧠 **ekkOS_™** · 📅 YYYY-MM-DD H:MM AM/PM TZ
382
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
383
+ else
384
+ # Fallback: minimal injection
385
+ MESSAGE="${MESSAGE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
386
+ 🧠 ekkOS™: $PATTERN_COUNT patterns found
387
+
388
+ Pattern IDs to acknowledge with [ekkOS_SELECT] or [ekkOS_SKIP]:
389
+ $PATTERN_ID_LIST
390
+
391
+ End response with: 🧠 **ekkOS_™** · 📅 YYYY-MM-DD
392
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
393
+ fi
394
+
395
+ # Save patterns for stop.sh
396
+ echo "$API_RESPONSE" | jq '.layers.patterns // []' > "$STATE_DIR/patterns-${SESSION_ID}.json" 2>/dev/null || true
397
+
398
+ echo "{\"continue\": true, \"user_message\": $(echo "$MESSAGE" | jq -R -s .)}" | jq -c .
399
+ elif [ -n "$TIME_MACHINE_CONTEXT" ] || [ -n "$ENDLESS_CONTEXT" ]; then
400
+ # Time Machine or Endless Context only, no patterns
401
+ MESSAGE="${MESSAGE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
402
+ 🧠 ekkOS™: Context restored, no new patterns
403
+
404
+ End response with: 🧠 **ekkOS_™** · 📅 YYYY-MM-DD
405
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
406
+
407
+ echo "{\"continue\": true, \"user_message\": $(echo "$MESSAGE" | jq -R -s .)}" | jq -c .
408
+ else
409
+ # No Time Machine, no patterns - still write contract and remind about footer
410
+ MESSAGE="━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
411
+ 🧠 ekkOS™: No patterns found (new territory)
412
+
413
+ End response with: 🧠 **ekkOS_™** · 📅 YYYY-MM-DD
414
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
415
+
416
+ echo "{\"continue\": true, \"user_message\": $(echo "$MESSAGE" | jq -R -s .)}" | jq -c .
417
+ fi
418
+
419
+ exit 0
@@ -0,0 +1,20 @@
1
+ {
2
+ "version": 1,
3
+ "hooks": {
4
+ "beforeSubmitPrompt": [
5
+ {
6
+ "command": "./.cursor/hooks/before-submit-prompt.sh"
7
+ }
8
+ ],
9
+ "afterAgentResponse": [
10
+ {
11
+ "command": "./.cursor/hooks/after-agent-response.sh"
12
+ }
13
+ ],
14
+ "stop": [
15
+ {
16
+ "command": "./.cursor/hooks/stop.sh"
17
+ }
18
+ ]
19
+ }
20
+ }