@ekkos/cli 0.2.18 → 1.0.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 (98) hide show
  1. package/README.md +57 -0
  2. package/dist/agent/daemon.d.ts +27 -0
  3. package/dist/agent/daemon.js +254 -29
  4. package/dist/agent/health-check.d.ts +35 -0
  5. package/dist/agent/health-check.js +243 -0
  6. package/dist/agent/pty-runner.d.ts +1 -0
  7. package/dist/agent/pty-runner.js +6 -1
  8. package/dist/capture/eviction-client.d.ts +139 -0
  9. package/dist/capture/eviction-client.js +454 -0
  10. package/dist/capture/index.d.ts +2 -0
  11. package/dist/capture/index.js +2 -0
  12. package/dist/capture/jsonl-rewriter.d.ts +96 -0
  13. package/dist/capture/jsonl-rewriter.js +1369 -0
  14. package/dist/capture/transcript-repair.d.ts +51 -0
  15. package/dist/capture/transcript-repair.js +319 -0
  16. package/dist/commands/agent.d.ts +6 -0
  17. package/dist/commands/agent.js +244 -0
  18. package/dist/commands/dashboard.d.ts +25 -0
  19. package/dist/commands/dashboard.js +1175 -0
  20. package/dist/commands/doctor.js +23 -1
  21. package/dist/commands/run.d.ts +5 -0
  22. package/dist/commands/run.js +1605 -516
  23. package/dist/commands/setup-remote.js +146 -37
  24. package/dist/commands/swarm-dashboard.d.ts +20 -0
  25. package/dist/commands/swarm-dashboard.js +735 -0
  26. package/dist/commands/swarm-setup.d.ts +10 -0
  27. package/dist/commands/swarm-setup.js +956 -0
  28. package/dist/commands/swarm.d.ts +46 -0
  29. package/dist/commands/swarm.js +441 -0
  30. package/dist/commands/test-claude.d.ts +16 -0
  31. package/dist/commands/test-claude.js +156 -0
  32. package/dist/commands/usage/blocks.d.ts +8 -0
  33. package/dist/commands/usage/blocks.js +60 -0
  34. package/dist/commands/usage/daily.d.ts +9 -0
  35. package/dist/commands/usage/daily.js +96 -0
  36. package/dist/commands/usage/dashboard.d.ts +8 -0
  37. package/dist/commands/usage/dashboard.js +104 -0
  38. package/dist/commands/usage/formatters.d.ts +41 -0
  39. package/dist/commands/usage/formatters.js +147 -0
  40. package/dist/commands/usage/index.d.ts +13 -0
  41. package/dist/commands/usage/index.js +87 -0
  42. package/dist/commands/usage/monthly.d.ts +8 -0
  43. package/dist/commands/usage/monthly.js +66 -0
  44. package/dist/commands/usage/session.d.ts +11 -0
  45. package/dist/commands/usage/session.js +193 -0
  46. package/dist/commands/usage/weekly.d.ts +9 -0
  47. package/dist/commands/usage/weekly.js +61 -0
  48. package/dist/commands/usage.d.ts +7 -0
  49. package/dist/commands/usage.js +214 -0
  50. package/dist/cron/index.d.ts +7 -0
  51. package/dist/cron/index.js +13 -0
  52. package/dist/cron/promoter.d.ts +70 -0
  53. package/dist/cron/promoter.js +403 -0
  54. package/dist/deploy/instructions.d.ts +5 -2
  55. package/dist/deploy/instructions.js +11 -8
  56. package/dist/index.js +262 -5
  57. package/dist/lib/tmux-scrollbar.d.ts +14 -0
  58. package/dist/lib/tmux-scrollbar.js +296 -0
  59. package/dist/lib/usage-monitor.d.ts +47 -0
  60. package/dist/lib/usage-monitor.js +124 -0
  61. package/dist/lib/usage-parser.d.ts +162 -0
  62. package/dist/lib/usage-parser.js +583 -0
  63. package/dist/restore/RestoreOrchestrator.d.ts +4 -0
  64. package/dist/restore/RestoreOrchestrator.js +118 -30
  65. package/dist/utils/log-rotate.d.ts +18 -0
  66. package/dist/utils/log-rotate.js +74 -0
  67. package/dist/utils/platform.d.ts +2 -0
  68. package/dist/utils/platform.js +3 -1
  69. package/dist/utils/session-binding.d.ts +5 -0
  70. package/dist/utils/session-binding.js +46 -0
  71. package/dist/utils/state.js +4 -0
  72. package/dist/utils/verify-remote-terminal.d.ts +10 -0
  73. package/dist/utils/verify-remote-terminal.js +415 -0
  74. package/package.json +9 -2
  75. package/templates/CLAUDE.md +135 -23
  76. package/templates/ekkos-manifest.json +5 -5
  77. package/templates/hooks/lib/contract.sh +43 -31
  78. package/templates/hooks/lib/count-tokens.cjs +86 -0
  79. package/templates/hooks/lib/ekkos-reminders.sh +98 -0
  80. package/templates/hooks/lib/state.sh +53 -1
  81. package/templates/hooks/stop.sh +150 -388
  82. package/templates/hooks/user-prompt-submit.sh +353 -443
  83. package/templates/windsurf-hooks/README.md +212 -0
  84. package/templates/windsurf-hooks/hooks.json +9 -2
  85. package/templates/windsurf-hooks/install.sh +148 -0
  86. package/templates/windsurf-hooks/lib/contract.sh +2 -0
  87. package/templates/windsurf-hooks/post-cascade-response.sh +251 -0
  88. package/templates/windsurf-hooks/pre-user-prompt.sh +435 -0
  89. package/templates/windsurf-skills/ekkos-memory/SKILL.md +219 -0
  90. package/templates/agents/README.md +0 -182
  91. package/templates/agents/code-reviewer.md +0 -166
  92. package/templates/agents/debug-detective.md +0 -169
  93. package/templates/agents/ekkOS_Vercel.md +0 -99
  94. package/templates/agents/extension-manager.md +0 -229
  95. package/templates/agents/git-companion.md +0 -185
  96. package/templates/agents/github-test-agent.md +0 -321
  97. package/templates/agents/railway-manager.md +0 -215
  98. package/templates/windsurf-hooks/before-submit-prompt.sh +0 -238
@@ -0,0 +1,212 @@
1
+ # ekkOS Windsurf Cascade Hooks
2
+
3
+ Complete Golden Loop integration for Windsurf Cascade with proactive memory retrieval, PatternGuard enforcement, and automatic turn capture.
4
+
5
+ ## Features
6
+
7
+ - **Proactive Memory Retrieval**: Automatically retrieves patterns from all 8 memory layers before each prompt
8
+ - **Endless Context**: Auto-restores conversation context after context compaction (10+ min gaps)
9
+ - **Time Machine**: Supports "Continue from here" to restore past sessions
10
+ - **PatternGuard**: Enforces 100% pattern acknowledgment with SELECT/SKIP markers
11
+ - **Footer Enforcement**: Requires ekkOS™ branded timestamp footer on all responses
12
+ - **Golden Loop Compliance**: Turn contracts, validation, and outcome tracking
13
+
14
+ ## Installation
15
+
16
+ ### 1. Copy hooks to your project
17
+
18
+ ```bash
19
+ # From your project root
20
+ cp -r /path/to/ekkos/templates/windsurf-hooks .windsurf/
21
+ ```
22
+
23
+ ### 2. Configure Windsurf hooks
24
+
25
+ Create `.windsurf/hooks.json` in your project root:
26
+
27
+ ```json
28
+ {
29
+ "version": 1,
30
+ "hooks": {
31
+ "pre_user_prompt": [
32
+ {
33
+ "command": "./.windsurf/hooks/pre-user-prompt.sh",
34
+ "show_output": false
35
+ }
36
+ ],
37
+ "post_cascade_response": [
38
+ {
39
+ "command": "./.windsurf/hooks/post-cascade-response.sh",
40
+ "show_output": false
41
+ }
42
+ ]
43
+ }
44
+ }
45
+ ```
46
+
47
+ ### 3. Authenticate ekkOS
48
+
49
+ Ensure you have a valid ekkOS config:
50
+
51
+ ```bash
52
+ # Check your auth
53
+ cat ~/.ekkos/config.json
54
+ ```
55
+
56
+ If not configured, run `ekkOS: Connect` in VS Code to set up authentication.
57
+
58
+ ### 4. Test the integration
59
+
60
+ Open Cascade and ask a technical question. You should see:
61
+
62
+ ```
63
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
64
+ 🧠 ekkOS™ Memory Substrate
65
+ ✓ 3 patterns loaded from memory
66
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
67
+ ```
68
+
69
+ ## How It Works
70
+
71
+ ### Hook Flow
72
+
73
+ ```
74
+ User Prompt
75
+
76
+ pre_user_prompt hook
77
+ ├─ Retrieves patterns from ekkOS API
78
+ ├─ Checks for Time Machine requests
79
+ ├─ Auto-restores Endless Context (if gap detected)
80
+ ├─ Writes turn contract
81
+ └─ Injects patterns + footer requirement
82
+
83
+ Cascade processes augmented prompt
84
+
85
+ Cascade generates response
86
+
87
+ post_cascade_response hook
88
+ ├─ Validates PatternGuard (acknowledgments)
89
+ ├─ Checks footer presence
90
+ ├─ Captures turn to L2 episodic memory
91
+ ├─ Tracks pattern outcomes
92
+ └─ Reports compliance status
93
+ ```
94
+
95
+ ### Golden Loop Enforcement
96
+
97
+ **RETRIEVE** (pre_user_prompt):
98
+ - Searches all 8 memory layers
99
+ - Returns patterns with IDs
100
+ - Writes turn contract as evidence
101
+
102
+ **ACKNOWLEDGE** (Cascade response must include):
103
+ ```
104
+ [ekkOS_SELECT]
105
+ - id: <pattern-uuid>
106
+ reason: <why using>
107
+ confidence: <0.0-1.0>
108
+ [/ekkOS_SELECT]
109
+
110
+ [ekkOS_SKIP]
111
+ - id: <pattern-uuid>
112
+ reason: <why not relevant>
113
+ [/ekkOS_SKIP]
114
+ ```
115
+
116
+ **FOOTER** (required on all responses):
117
+ ```
118
+ 🧠 **ekkOS_™** · 📅 YYYY-MM-DD H:MM AM/PM TZ
119
+ ```
120
+
121
+ **CAPTURE** (post_cascade_response):
122
+ - Stores turn in episodic memory (L2)
123
+ - Validates compliance
124
+ - Tracks pattern usage outcomes
125
+
126
+ ## Configuration Options
127
+
128
+ ### Strict Mode
129
+
130
+ Set `EKKOS_STRICT=1` to block turns when retrieval fails:
131
+
132
+ ```bash
133
+ export EKKOS_STRICT=1
134
+ ```
135
+
136
+ ### Auth Sources (priority order)
137
+
138
+ 1. `~/.ekkos/config.json` (preferred - set by VS Code extension)
139
+ 2. Project `.env.local` (SUPABASE_SECRET_KEY)
140
+ 3. Environment variable `SUPABASE_SECRET_KEY`
141
+
142
+ ### Hook Locations
143
+
144
+ Cascade merges hooks from all locations (system → user → workspace):
145
+
146
+ - **System**: `/Library/Application Support/Windsurf/hooks.json` (macOS)
147
+ - **User**: `~/.codeium/windsurf/hooks.json`
148
+ - **Workspace**: `.windsurf/hooks.json` (project-specific)
149
+
150
+ ## File Structure
151
+
152
+ ```
153
+ .windsurf/
154
+ ├── hooks.json # Hook configuration
155
+ └── hooks/
156
+ ├── pre-user-prompt.sh # Retrieval + injection
157
+ ├── post-cascade-response.sh # Capture + validation
158
+ └── lib/
159
+ └── contract.sh # Turn contract library
160
+ ```
161
+
162
+ ## Troubleshooting
163
+
164
+ ### "No auth token" message
165
+
166
+ 1. Run `ekkOS: Connect` in VS Code
167
+ 2. Check `~/.ekkos/config.json` exists with `hookApiKey` or `apiKey`
168
+ 3. Verify the key is valid at https://platform.ekkos.dev
169
+
170
+ ### Hooks not firing
171
+
172
+ 1. Check file permissions: `chmod +x .windsurf/hooks/*.sh`
173
+ 2. Verify JSON syntax in `hooks.json`
174
+ 3. Check Windsurf logs: Cascade toolbar → Settings → Download diagnostics
175
+ 4. Ensure `jq` is installed: `which jq`
176
+
177
+ ### Patterns not loading
178
+
179
+ 1. Test API health: `curl https://mcp.ekkos.dev/api/v1/gateway/health`
180
+ 2. Check network connectivity
181
+ 3. Verify auth token has access to your user data
182
+
183
+ ### Footer enforcement not working
184
+
185
+ The footer check is in `post_cascade_response`. Non-compliance is logged but doesn't block the response (unless STRICT mode is on). Check your responses include:
186
+ - The 🧠 emoji
187
+ - `ekkOS` text
188
+ - 📅 date stamp
189
+
190
+ ## Comparison: Windsurf vs Cursor/Claude Code
191
+
192
+ | Feature | Windsurf Hooks | Cursor Hooks | Claude Code |
193
+ |---------|----------------|--------------|-------------|
194
+ | Hook names | `pre_user_prompt`, `post_cascade_response` | `beforeSubmitPrompt`, `afterAgentResponse` | `.claude/` scripts |
195
+ | Auto-inject | ✅ Yes | ✅ Yes | ✅ Yes |
196
+ | Endless Context | ✅ Yes | ✅ Yes | ✅ Yes |
197
+ | Time Machine | ✅ Yes | ✅ Yes | ✅ Yes |
198
+ | PatternGuard | ✅ Yes | ✅ Yes | ✅ Yes |
199
+ | Footer required | ✅ Yes | ✅ Yes | ✅ Yes |
200
+
201
+ ## API Endpoints Used
202
+
203
+ - `POST /api/v1/context/retrieve` - Pattern retrieval
204
+ - `POST /api/v1/capture` - Turn storage
205
+ - `POST /api/v1/turns/recall` - Endless Context restoration
206
+ - `GET /api/v1/context/restore-request/pending` - Time Machine check
207
+ - `POST /api/v1/context/restore-request/consume` - Time Machine consume
208
+ - `POST /api/v1/patterns/outcome` - Pattern outcome tracking
209
+
210
+ ## License
211
+
212
+ Part of the ekkOS memory system. See main repository for license details.
@@ -1,9 +1,16 @@
1
1
  {
2
2
  "version": 1,
3
3
  "hooks": {
4
- "beforeSubmitPrompt": [
4
+ "pre_user_prompt": [
5
5
  {
6
- "command": "./.windsurf/hooks/before-submit-prompt.sh"
6
+ "command": "./.windsurf/hooks/pre-user-prompt.sh",
7
+ "show_output": false
8
+ }
9
+ ],
10
+ "post_cascade_response": [
11
+ {
12
+ "command": "./.windsurf/hooks/post-cascade-response.sh",
13
+ "show_output": false
7
14
  }
8
15
  ]
9
16
  }
@@ -0,0 +1,148 @@
1
+ #!/bin/bash
2
+ # ═══════════════════════════════════════════════════════════════════════════
3
+ # ekkOS Windsurf Hooks Installer
4
+ # ═══════════════════════════════════════════════════════════════════════════
5
+ #
6
+ # Usage: ./install.sh [--global]
7
+ # --global: Install to ~/.windsurf/ (works in all projects)
8
+ # (default): Install to ./.windsurf/ (project-level only)
9
+ #
10
+ # ═══════════════════════════════════════════════════════════════════════════
11
+
12
+ set -e
13
+
14
+ # Colors
15
+ RED='\033[0;31m'
16
+ GREEN='\033[0;32m'
17
+ YELLOW='\033[1;33m'
18
+ BLUE='\033[0;34m'
19
+ NC='\033[0m' # No Color
20
+
21
+ # Parse arguments
22
+ GLOBAL_INSTALL=false
23
+ if [ "$1" = "--global" ]; then
24
+ GLOBAL_INSTALL=true
25
+ fi
26
+
27
+ # Get script directory
28
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
29
+
30
+ # Determine install location
31
+ if [ "$GLOBAL_INSTALL" = true ]; then
32
+ WINDSURF_DIR="$HOME/.windsurf"
33
+ echo -e "${BLUE}🧠 ekkOS Windsurf Hooks Installer (GLOBAL)${NC}"
34
+ echo -e "${BLUE}📍 Installing to: $WINDSURF_DIR/${NC}"
35
+ else
36
+ WINDSURF_DIR="./.windsurf"
37
+ echo -e "${BLUE}🧠 ekkOS Windsurf Hooks Installer (Project-level)${NC}"
38
+ fi
39
+
40
+ echo "═══════════════════════════════════════════════════════════════════"
41
+
42
+ # Check if running in a project (only for local install)
43
+ if [ "$GLOBAL_INSTALL" = false ]; then
44
+ if [ ! -d ".git" ] && [ ! -f "package.json" ] && [ ! -f "Cargo.toml" ] && [ ! -f "pyproject.toml" ]; then
45
+ echo -e "${YELLOW}⚠️ Warning: No project files detected. Make sure you're in a project root.${NC}"
46
+ read -p "Continue anyway? (y/N) " -n 1 -r
47
+ echo
48
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
49
+ exit 1
50
+ fi
51
+ fi
52
+ fi
53
+
54
+ # Check for jq
55
+ if ! command -v jq &> /dev/null; then
56
+ echo -e "${RED}❌ jq is required but not installed.${NC}"
57
+ echo " Install with: brew install jq (macOS) or apt-get install jq (Linux)"
58
+ exit 1
59
+ fi
60
+
61
+ # Check for curl
62
+ if ! command -v curl &> /dev/null; then
63
+ echo -e "${RED}❌ curl is required but not installed.${NC}"
64
+ exit 1
65
+ fi
66
+
67
+ # Create .windsurf directory
68
+ echo -e "${BLUE}📁 Creating .windsurf directory...${NC}"
69
+ mkdir -p "$WINDSURF_DIR/hooks/lib"
70
+
71
+ # Copy hook files
72
+ echo -e "${BLUE}📄 Copying hook files...${NC}"
73
+ cp "$SCRIPT_DIR/hooks.json" "$WINDSURF_DIR/hooks.json"
74
+ cp "$SCRIPT_DIR/pre-user-prompt.sh" "$WINDSURF_DIR/hooks/"
75
+ cp "$SCRIPT_DIR/post-cascade-response.sh" "$WINDSURF_DIR/hooks/"
76
+ cp "$SCRIPT_DIR/lib/contract.sh" "$WINDSURF_DIR/hooks/lib/"
77
+
78
+ # Make scripts executable
79
+ echo -e "${BLUE}🔧 Setting permissions...${NC}"
80
+ chmod +x "$WINDSURF_DIR/hooks/"*.sh
81
+ chmod +x "$WINDSURF_DIR/hooks/lib/"*.sh
82
+
83
+ # Check for ekkOS auth
84
+ echo -e "${BLUE}🔐 Checking ekkOS authentication...${NC}"
85
+ EKKOS_CONFIG="$HOME/.ekkos/config.json"
86
+
87
+ if [ -f "$EKKOS_CONFIG" ]; then
88
+ if jq -e '.hookApiKey // .apiKey' "$EKKOS_CONFIG" > /dev/null 2>&1; then
89
+ echo -e "${GREEN}✅ ekkOS auth found in ~/.ekkos/config.json${NC}"
90
+ else
91
+ echo -e "${YELLOW}⚠️ ~/.ekkos/config.json exists but no API key found${NC}"
92
+ echo " Run 'ekkOS: Connect' in VS Code to authenticate"
93
+ fi
94
+ else
95
+ echo -e "${YELLOW}⚠️ No ekkOS config found${NC}"
96
+ echo " Run 'ekkOS: Connect' in VS Code to authenticate"
97
+ fi
98
+
99
+ # Test API connectivity
100
+ echo -e "${BLUE}🌐 Testing ekkOS API connectivity...${NC}"
101
+ if curl -s --connect-timeout 3 https://mcp.ekkos.dev/api/v1/gateway/health > /dev/null 2>&1; then
102
+ echo -e "${GREEN}✅ ekkOS API is reachable${NC}"
103
+ else
104
+ echo -e "${YELLOW}⚠️ Could not reach ekkOS API (may be network issues)${NC}"
105
+ fi
106
+
107
+ # Summary
108
+ echo ""
109
+ echo -e "${GREEN}✅ Installation complete!${NC}"
110
+ echo "═══════════════════════════════════════════════════════════════════"
111
+ echo ""
112
+
113
+ if [ "$GLOBAL_INSTALL" = true ]; then
114
+ echo "📂 Installed files (GLOBAL - works in ALL projects):"
115
+ echo " ~/.windsurf/hooks.json"
116
+ echo " ~/.windsurf/hooks/pre-user-prompt.sh"
117
+ echo " ~/.windsurf/hooks/post-cascade-response.sh"
118
+ echo " ~/.windsurf/hooks/lib/contract.sh"
119
+ echo ""
120
+ echo "🚀 Next steps:"
121
+ echo " 1. Open any project in Windsurf Cascade"
122
+ echo " 2. Ask a technical question"
123
+ echo " 3. You should see: '🧠 ekkOS™ Memory Substrate' header"
124
+ echo ""
125
+ echo "� To use in a specific project, create a symlink:"
126
+ echo " ln -s ~/.windsurf ./.windsurf"
127
+ else
128
+ echo "📂 Installed files (Project-level):"
129
+ echo " .windsurf/hooks.json"
130
+ echo " .windsurf/hooks/pre-user-prompt.sh"
131
+ echo " .windsurf/hooks/post-cascade-response.sh"
132
+ echo " .windsurf/hooks/lib/contract.sh"
133
+ echo ""
134
+ echo "🚀 Next steps:"
135
+ echo " 1. Open Windsurf Cascade in this project"
136
+ echo " 2. Ask a technical question"
137
+ echo " 3. You should see: '🧠 ekkOS™ Memory Substrate' header"
138
+ echo ""
139
+ # Git ignore suggestion
140
+ if [ -d ".git" ]; then
141
+ if ! grep -q ".windsurf/state" .gitignore 2>/dev/null; then
142
+ echo -e "${YELLOW}💡 Tip: Add '.windsurf/state' to your .gitignore${NC}"
143
+ fi
144
+ fi
145
+ fi
146
+
147
+ echo ""
148
+ echo "📖 For more info: cat ~/.windsurf/hooks/README.md"
@@ -16,6 +16,8 @@ get_contract_dir() {
16
16
 
17
17
  if [ "$source" = "cursor" ]; then
18
18
  echo "$project_root/.cursor/state/ekkos"
19
+ elif [ "$source" = "windsurf" ]; then
20
+ echo "$project_root/.windsurf/state/ekkos"
19
21
  else
20
22
  echo "$project_root/.claude/state/ekkos"
21
23
  fi
@@ -0,0 +1,251 @@
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