@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.
- package/README.md +57 -0
- package/dist/agent/daemon.d.ts +27 -0
- package/dist/agent/daemon.js +254 -29
- package/dist/agent/health-check.d.ts +35 -0
- package/dist/agent/health-check.js +243 -0
- package/dist/agent/pty-runner.d.ts +1 -0
- package/dist/agent/pty-runner.js +6 -1
- package/dist/capture/eviction-client.d.ts +139 -0
- package/dist/capture/eviction-client.js +454 -0
- package/dist/capture/index.d.ts +2 -0
- package/dist/capture/index.js +2 -0
- package/dist/capture/jsonl-rewriter.d.ts +96 -0
- package/dist/capture/jsonl-rewriter.js +1369 -0
- package/dist/capture/transcript-repair.d.ts +51 -0
- package/dist/capture/transcript-repair.js +319 -0
- package/dist/commands/agent.d.ts +6 -0
- package/dist/commands/agent.js +244 -0
- package/dist/commands/dashboard.d.ts +25 -0
- package/dist/commands/dashboard.js +1175 -0
- package/dist/commands/doctor.js +23 -1
- package/dist/commands/run.d.ts +5 -0
- package/dist/commands/run.js +1605 -516
- package/dist/commands/setup-remote.js +146 -37
- package/dist/commands/swarm-dashboard.d.ts +20 -0
- package/dist/commands/swarm-dashboard.js +735 -0
- package/dist/commands/swarm-setup.d.ts +10 -0
- package/dist/commands/swarm-setup.js +956 -0
- package/dist/commands/swarm.d.ts +46 -0
- package/dist/commands/swarm.js +441 -0
- package/dist/commands/test-claude.d.ts +16 -0
- package/dist/commands/test-claude.js +156 -0
- package/dist/commands/usage/blocks.d.ts +8 -0
- package/dist/commands/usage/blocks.js +60 -0
- package/dist/commands/usage/daily.d.ts +9 -0
- package/dist/commands/usage/daily.js +96 -0
- package/dist/commands/usage/dashboard.d.ts +8 -0
- package/dist/commands/usage/dashboard.js +104 -0
- package/dist/commands/usage/formatters.d.ts +41 -0
- package/dist/commands/usage/formatters.js +147 -0
- package/dist/commands/usage/index.d.ts +13 -0
- package/dist/commands/usage/index.js +87 -0
- package/dist/commands/usage/monthly.d.ts +8 -0
- package/dist/commands/usage/monthly.js +66 -0
- package/dist/commands/usage/session.d.ts +11 -0
- package/dist/commands/usage/session.js +193 -0
- package/dist/commands/usage/weekly.d.ts +9 -0
- package/dist/commands/usage/weekly.js +61 -0
- package/dist/commands/usage.d.ts +7 -0
- package/dist/commands/usage.js +214 -0
- package/dist/cron/index.d.ts +7 -0
- package/dist/cron/index.js +13 -0
- package/dist/cron/promoter.d.ts +70 -0
- package/dist/cron/promoter.js +403 -0
- package/dist/deploy/instructions.d.ts +5 -2
- package/dist/deploy/instructions.js +11 -8
- package/dist/index.js +262 -5
- package/dist/lib/tmux-scrollbar.d.ts +14 -0
- package/dist/lib/tmux-scrollbar.js +296 -0
- package/dist/lib/usage-monitor.d.ts +47 -0
- package/dist/lib/usage-monitor.js +124 -0
- package/dist/lib/usage-parser.d.ts +162 -0
- package/dist/lib/usage-parser.js +583 -0
- package/dist/restore/RestoreOrchestrator.d.ts +4 -0
- package/dist/restore/RestoreOrchestrator.js +118 -30
- package/dist/utils/log-rotate.d.ts +18 -0
- package/dist/utils/log-rotate.js +74 -0
- package/dist/utils/platform.d.ts +2 -0
- package/dist/utils/platform.js +3 -1
- package/dist/utils/session-binding.d.ts +5 -0
- package/dist/utils/session-binding.js +46 -0
- package/dist/utils/state.js +4 -0
- package/dist/utils/verify-remote-terminal.d.ts +10 -0
- package/dist/utils/verify-remote-terminal.js +415 -0
- package/package.json +9 -2
- package/templates/CLAUDE.md +135 -23
- package/templates/ekkos-manifest.json +5 -5
- package/templates/hooks/lib/contract.sh +43 -31
- package/templates/hooks/lib/count-tokens.cjs +86 -0
- package/templates/hooks/lib/ekkos-reminders.sh +98 -0
- package/templates/hooks/lib/state.sh +53 -1
- package/templates/hooks/stop.sh +150 -388
- package/templates/hooks/user-prompt-submit.sh +353 -443
- package/templates/windsurf-hooks/README.md +212 -0
- package/templates/windsurf-hooks/hooks.json +9 -2
- package/templates/windsurf-hooks/install.sh +148 -0
- package/templates/windsurf-hooks/lib/contract.sh +2 -0
- package/templates/windsurf-hooks/post-cascade-response.sh +251 -0
- package/templates/windsurf-hooks/pre-user-prompt.sh +435 -0
- package/templates/windsurf-skills/ekkos-memory/SKILL.md +219 -0
- package/templates/agents/README.md +0 -182
- package/templates/agents/code-reviewer.md +0 -166
- package/templates/agents/debug-detective.md +0 -169
- package/templates/agents/ekkOS_Vercel.md +0 -99
- package/templates/agents/extension-manager.md +0 -229
- package/templates/agents/git-companion.md +0 -185
- package/templates/agents/github-test-agent.md +0 -321
- package/templates/agents/railway-manager.md +0 -215
- 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
|
-
"
|
|
4
|
+
"pre_user_prompt": [
|
|
5
5
|
{
|
|
6
|
-
"command": "./.windsurf/hooks/
|
|
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"
|
|
@@ -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
|