@ekkos/cli 1.2.18 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cache/capture.js +0 -0
- package/dist/commands/dashboard.js +121 -66
- package/dist/commands/hooks.d.ts +25 -36
- package/dist/commands/hooks.js +43 -615
- package/dist/commands/init.js +7 -23
- package/dist/commands/run.js +90 -3
- package/dist/commands/setup.js +10 -352
- package/dist/deploy/hooks.d.ts +8 -5
- package/dist/deploy/hooks.js +12 -105
- package/dist/deploy/settings.d.ts +8 -2
- package/dist/deploy/settings.js +22 -51
- package/dist/index.js +17 -39
- package/dist/utils/state.js +7 -2
- package/package.json +1 -1
- package/templates/CLAUDE.md +82 -292
- package/templates/cursor-rules/ekkos-memory.md +48 -108
- package/templates/windsurf-rules/ekkos-memory.md +62 -64
- package/templates/cursor-hooks/after-agent-response.sh +0 -117
- package/templates/cursor-hooks/before-submit-prompt.sh +0 -419
- package/templates/cursor-hooks/hooks.json +0 -20
- package/templates/cursor-hooks/lib/contract.sh +0 -320
- package/templates/cursor-hooks/stop.sh +0 -75
- package/templates/hooks/assistant-response.ps1 +0 -256
- package/templates/hooks/assistant-response.sh +0 -160
- package/templates/hooks/hooks.json +0 -40
- package/templates/hooks/lib/contract.sh +0 -332
- package/templates/hooks/lib/count-tokens.cjs +0 -86
- package/templates/hooks/lib/ekkos-reminders.sh +0 -98
- package/templates/hooks/lib/state.sh +0 -210
- package/templates/hooks/session-start.ps1 +0 -146
- package/templates/hooks/session-start.sh +0 -353
- package/templates/hooks/stop.ps1 +0 -349
- package/templates/hooks/stop.sh +0 -382
- package/templates/hooks/user-prompt-submit.ps1 +0 -419
- package/templates/hooks/user-prompt-submit.sh +0 -516
- package/templates/project-stubs/session-start.ps1 +0 -63
- package/templates/project-stubs/session-start.sh +0 -55
- package/templates/project-stubs/stop.ps1 +0 -63
- package/templates/project-stubs/stop.sh +0 -55
- package/templates/project-stubs/user-prompt-submit.ps1 +0 -63
- package/templates/project-stubs/user-prompt-submit.sh +0 -55
- package/templates/windsurf-hooks/README.md +0 -212
- package/templates/windsurf-hooks/hooks.json +0 -17
- package/templates/windsurf-hooks/install.sh +0 -148
- package/templates/windsurf-hooks/lib/contract.sh +0 -322
- package/templates/windsurf-hooks/post-cascade-response.sh +0 -251
- package/templates/windsurf-hooks/pre-user-prompt.sh +0 -435
|
@@ -1,419 +0,0 @@
|
|
|
1
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
2
|
-
# ekkOS_ Hook: UserPromptSubmit - SEAMLESS CONTEXT CONTINUITY (Windows)
|
|
3
|
-
# MANAGED BY ekkos-connect - DO NOT EDIT DIRECTLY
|
|
4
|
-
# EKKOS_MANAGED=1
|
|
5
|
-
# EKKOS_MANIFEST_SHA256=<computed-at-build>
|
|
6
|
-
# EKKOS_TEMPLATE_VERSION=1.0.0
|
|
7
|
-
#
|
|
8
|
-
# Per ekkOS Onboarding Spec v1.2 FINAL + ADDENDUM:
|
|
9
|
-
# - All persisted records MUST include: instanceId, sessionId, sessionName
|
|
10
|
-
# - Uses EKKOS_INSTANCE_ID env var for multi-session isolation
|
|
11
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
12
|
-
|
|
13
|
-
$ErrorActionPreference = "SilentlyContinue"
|
|
14
|
-
|
|
15
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
16
|
-
# PROXY MODE DETECTION
|
|
17
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
18
|
-
$ProxyMode = $false
|
|
19
|
-
if ($env:ANTHROPIC_BASE_URL -and $env:ANTHROPIC_BASE_URL -like "*proxy.ekkos.dev*") {
|
|
20
|
-
$ProxyMode = $true
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
24
|
-
# CONFIG PATHS - No hardcoded word arrays per spec v1.2 Addendum
|
|
25
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
26
|
-
$EkkosConfigDir = if ($env:EKKOS_CONFIG_DIR) { $env:EKKOS_CONFIG_DIR } else { "$env:USERPROFILE\.ekkos" }
|
|
27
|
-
$SessionWordsJson = "$EkkosConfigDir\session-words.json"
|
|
28
|
-
$SessionWordsDefault = "$EkkosConfigDir\.defaults\session-words.json"
|
|
29
|
-
|
|
30
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
31
|
-
# INSTANCE ID - Multi-session isolation per v1.2 ADDENDUM
|
|
32
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
33
|
-
$EkkosInstanceId = if ($env:EKKOS_INSTANCE_ID) { $env:EKKOS_INSTANCE_ID } else { "default" }
|
|
34
|
-
|
|
35
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
36
|
-
# Load session words from JSON file - NO HARDCODED ARRAYS
|
|
37
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
38
|
-
$script:SessionWords = $null
|
|
39
|
-
|
|
40
|
-
function Load-SessionWords {
|
|
41
|
-
$wordsFile = $SessionWordsJson
|
|
42
|
-
|
|
43
|
-
# Fallback to managed defaults if user file missing/invalid
|
|
44
|
-
if (-not (Test-Path $wordsFile)) {
|
|
45
|
-
$wordsFile = $SessionWordsDefault
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (-not (Test-Path $wordsFile)) {
|
|
49
|
-
return $null
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
try {
|
|
53
|
-
$script:SessionWords = Get-Content $wordsFile -Raw | ConvertFrom-Json
|
|
54
|
-
} catch {
|
|
55
|
-
return $null
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
# Read input from stdin
|
|
60
|
-
$inputJson = [Console]::In.ReadToEnd()
|
|
61
|
-
if (-not $inputJson) { exit 0 }
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
$input = $inputJson | ConvertFrom-Json
|
|
65
|
-
} catch {
|
|
66
|
-
exit 0
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
$userQuery = $input.query
|
|
70
|
-
if (-not $userQuery) { $userQuery = $input.message }
|
|
71
|
-
if (-not $userQuery) { $userQuery = $input.prompt }
|
|
72
|
-
if (-not $userQuery) { exit 0 }
|
|
73
|
-
|
|
74
|
-
$rawSessionId = $input.session_id
|
|
75
|
-
if (-not $rawSessionId -or $rawSessionId -eq "null") { $rawSessionId = "unknown" }
|
|
76
|
-
|
|
77
|
-
# Fallback: read session_id from saved state
|
|
78
|
-
if ($rawSessionId -eq "unknown") {
|
|
79
|
-
$stateFile = Join-Path $env:USERPROFILE ".claude\state\current-session.json"
|
|
80
|
-
if (Test-Path $stateFile) {
|
|
81
|
-
try {
|
|
82
|
-
$state = Get-Content $stateFile -Raw | ConvertFrom-Json
|
|
83
|
-
$rawSessionId = $state.session_id
|
|
84
|
-
} catch {}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
89
|
-
# PROXY MODE: Slim hook path (cosmetic output + local state only)
|
|
90
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
91
|
-
if ($ProxyMode) {
|
|
92
|
-
function Get-ProxySessionName {
|
|
93
|
-
param([string]$sessionId)
|
|
94
|
-
if (-not $sessionId -or $sessionId -eq "unknown") { return "unknown-session" }
|
|
95
|
-
if ($sessionId -notmatch '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$') {
|
|
96
|
-
return $sessionId
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (-not $script:SessionWords) { Load-SessionWords }
|
|
100
|
-
if (-not $script:SessionWords) { return $sessionId }
|
|
101
|
-
|
|
102
|
-
$adjectives = $script:SessionWords.adjectives
|
|
103
|
-
$nouns = $script:SessionWords.nouns
|
|
104
|
-
$verbs = $script:SessionWords.verbs
|
|
105
|
-
if (-not $adjectives -or -not $nouns -or -not $verbs) { return $sessionId }
|
|
106
|
-
|
|
107
|
-
$clean = $sessionId -replace "-", ""
|
|
108
|
-
if ($clean.Length -lt 12) { return $sessionId }
|
|
109
|
-
|
|
110
|
-
try {
|
|
111
|
-
$adj = [Convert]::ToInt32($clean.Substring(0, 4), 16) % $adjectives.Length
|
|
112
|
-
$noun = [Convert]::ToInt32($clean.Substring(4, 4), 16) % $nouns.Length
|
|
113
|
-
$verb = [Convert]::ToInt32($clean.Substring(8, 4), 16) % $verbs.Length
|
|
114
|
-
return "$($adjectives[$adj])-$($nouns[$noun])-$($verbs[$verb])"
|
|
115
|
-
} catch {
|
|
116
|
-
return $sessionId
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
$sessionName = Get-ProxySessionName $rawSessionId
|
|
121
|
-
$timestampUtc = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
|
|
122
|
-
$displayTime = (Get-Date).ToString("yyyy-MM-dd hh:mm:ss tt zzz")
|
|
123
|
-
$projectPath = if ($env:PWD) { $env:PWD } else { (Get-Location).Path }
|
|
124
|
-
|
|
125
|
-
New-Item -ItemType Directory -Path $EkkosConfigDir -Force | Out-Null
|
|
126
|
-
New-Item -ItemType Directory -Path (Join-Path $env:USERPROFILE ".claude\state") -Force | Out-Null
|
|
127
|
-
|
|
128
|
-
$globalState = @{
|
|
129
|
-
session_id = $rawSessionId
|
|
130
|
-
session_name = $sessionName
|
|
131
|
-
project = $projectPath
|
|
132
|
-
timestamp = $timestampUtc
|
|
133
|
-
} | ConvertTo-Json -Depth 10 -Compress
|
|
134
|
-
Set-Content -Path (Join-Path $EkkosConfigDir "current-session.json") -Value $globalState -Force
|
|
135
|
-
|
|
136
|
-
$localState = @{
|
|
137
|
-
session_id = $rawSessionId
|
|
138
|
-
session_name = $sessionName
|
|
139
|
-
timestamp = $timestampUtc
|
|
140
|
-
} | ConvertTo-Json -Depth 10 -Compress
|
|
141
|
-
Set-Content -Path (Join-Path $env:USERPROFILE ".claude\state\current-session.json") -Value $localState -Force
|
|
142
|
-
|
|
143
|
-
$activeSessionsPath = Join-Path $EkkosConfigDir "active-sessions.json"
|
|
144
|
-
$sessions = @()
|
|
145
|
-
if (Test-Path $activeSessionsPath) {
|
|
146
|
-
try { $sessions = @(Get-Content $activeSessionsPath -Raw | ConvertFrom-Json) } catch { $sessions = @() }
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
$entry = @{
|
|
150
|
-
sessionId = $rawSessionId
|
|
151
|
-
sessionName = $sessionName
|
|
152
|
-
projectPath = $projectPath
|
|
153
|
-
startedAt = $timestampUtc
|
|
154
|
-
lastHeartbeat = $timestampUtc
|
|
155
|
-
pid = 0
|
|
156
|
-
}
|
|
157
|
-
$idx = -1
|
|
158
|
-
for ($i = 0; $i -lt $sessions.Count; $i++) {
|
|
159
|
-
if ($sessions[$i].sessionId -eq $rawSessionId) { $idx = $i; break }
|
|
160
|
-
}
|
|
161
|
-
if ($idx -ge 0) {
|
|
162
|
-
$entry.startedAt = if ($sessions[$idx].startedAt) { $sessions[$idx].startedAt } else { $timestampUtc }
|
|
163
|
-
$sessions[$idx] = $entry
|
|
164
|
-
} else {
|
|
165
|
-
$sessions += $entry
|
|
166
|
-
}
|
|
167
|
-
Set-Content -Path $activeSessionsPath -Value ($sessions | ConvertTo-Json -Depth 10) -Force
|
|
168
|
-
|
|
169
|
-
$hint = @{
|
|
170
|
-
sessionName = $sessionName
|
|
171
|
-
sessionId = $rawSessionId
|
|
172
|
-
projectPath = $projectPath
|
|
173
|
-
ts = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()
|
|
174
|
-
} | ConvertTo-Json -Depth 10 -Compress
|
|
175
|
-
Set-Content -Path (Join-Path $EkkosConfigDir "hook-session-hint.json") -Value $hint -Force
|
|
176
|
-
|
|
177
|
-
$esc = [char]27
|
|
178
|
-
Write-Output "${esc}[0;36m${esc}[1m🧠 ekkOS Memory${esc}[0m ${esc}[2m| $sessionName | $displayTime${esc}[0m"
|
|
179
|
-
exit 0
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
183
|
-
# INTELLIGENT TOOL ROUTER: Multi-trigger skill detection
|
|
184
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
185
|
-
$skillReminders = @()
|
|
186
|
-
$queryLower = $userQuery.ToLower()
|
|
187
|
-
|
|
188
|
-
# Memory First - Debug/Error/Problem solving
|
|
189
|
-
if ($queryLower -match '(how do i|debug|error|bug|fix|not working|broken|fails|issue|problem|wrong|crash)') {
|
|
190
|
-
$skillReminders += "SKILL REQUIRED: Call Skill(skill: `"ekkOS_Memory_First`") FIRST before debugging"
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
# Recall Triggers - Time-based memory
|
|
194
|
-
if ($queryLower -match '(yesterday|last week|last month|remember when|what did we|where did we leave|before|earlier|previous|ago)') {
|
|
195
|
-
$skillReminders += "SKILL REQUIRED: Call Skill(skill: `"ekkOS_Deep_Recall`") for time-based memory"
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
# Directive Triggers - User preferences
|
|
199
|
-
if ($queryLower -match '(always |never |i prefer|i like |dont |don.t |avoid |remember that |from now on)') {
|
|
200
|
-
$skillReminders += "SKILL REQUIRED: Call Skill(skill: `"ekkOS_Preferences`") to capture directive"
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
# Safety Triggers - Destructive actions
|
|
204
|
-
if ($queryLower -match '(delete|drop |rm -rf|deploy|push.*main|push.*master|production|migrate|rollback)') {
|
|
205
|
-
$skillReminders += "SAFETY REQUIRED: Call ekkOS_Conflict before this destructive action"
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
# Schema Triggers - Database operations
|
|
209
|
-
if ($queryLower -match '(sql|query|supabase|prisma|database|table|column|select |insert |update |where )') {
|
|
210
|
-
$skillReminders += "SCHEMA REQUIRED: Call ekkOS_GetSchema for correct field names"
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
214
|
-
# SESSION NAME - Resolve early so it's available for all downstream use
|
|
215
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
216
|
-
function Convert-UuidToWords {
|
|
217
|
-
param([string]$uuid)
|
|
218
|
-
|
|
219
|
-
if (-not $script:SessionWords) { Load-SessionWords }
|
|
220
|
-
if (-not $script:SessionWords) { return "unknown-session" }
|
|
221
|
-
|
|
222
|
-
$adjectives = $script:SessionWords.adjectives
|
|
223
|
-
$nouns = $script:SessionWords.nouns
|
|
224
|
-
$verbs = $script:SessionWords.verbs
|
|
225
|
-
|
|
226
|
-
if (-not $adjectives -or -not $nouns -or -not $verbs) { return "unknown-session" }
|
|
227
|
-
if (-not $uuid -or $uuid -eq "unknown") { return "unknown-session" }
|
|
228
|
-
|
|
229
|
-
$clean = $uuid -replace "-", ""
|
|
230
|
-
if ($clean.Length -lt 12) { return "unknown-session" }
|
|
231
|
-
|
|
232
|
-
try {
|
|
233
|
-
$a = [Convert]::ToInt32($clean.Substring(0,4), 16) % $adjectives.Length
|
|
234
|
-
$n = [Convert]::ToInt32($clean.Substring(4,4), 16) % $nouns.Length
|
|
235
|
-
$an = [Convert]::ToInt32($clean.Substring(8,4), 16) % $verbs.Length
|
|
236
|
-
return "$($adjectives[$a])-$($nouns[$n])-$($verbs[$an])"
|
|
237
|
-
} catch {
|
|
238
|
-
return "unknown-session"
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
$sessionName = Convert-UuidToWords $rawSessionId
|
|
243
|
-
|
|
244
|
-
function Get-PendingSessionFromProxyBaseUrl {
|
|
245
|
-
param([string]$baseUrl)
|
|
246
|
-
if (-not $baseUrl) { return $null }
|
|
247
|
-
try {
|
|
248
|
-
$uri = [System.Uri]$baseUrl
|
|
249
|
-
$segments = $uri.AbsolutePath.Trim('/') -split '/'
|
|
250
|
-
if (-not $segments -or $segments.Count -lt 3) { return $null }
|
|
251
|
-
$proxyIndex = [Array]::IndexOf($segments, 'proxy')
|
|
252
|
-
if ($proxyIndex -lt 0 -or $segments.Count -le ($proxyIndex + 2)) { return $null }
|
|
253
|
-
$candidate = [System.Uri]::UnescapeDataString($segments[$proxyIndex + 2])
|
|
254
|
-
if ($candidate -eq '_pending' -or $candidate -eq 'pending' -or $candidate.StartsWith('_pending-')) {
|
|
255
|
-
return $candidate
|
|
256
|
-
}
|
|
257
|
-
} catch {}
|
|
258
|
-
return $null
|
|
259
|
-
}
|
|
260
|
-
# Session binding removed — proxy now self-resolves _pending → word-based
|
|
261
|
-
# names inline via uuidToSessionName(). No external bind call needed.
|
|
262
|
-
|
|
263
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
264
|
-
# SESSION CURRENT: Update Redis with current session name
|
|
265
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
266
|
-
if ($sessionName -ne "unknown-session" -and $rawSessionId -ne "unknown") {
|
|
267
|
-
$configFile2 = Join-Path $EkkosConfigDir "config.json"
|
|
268
|
-
if (Test-Path $configFile2) {
|
|
269
|
-
try {
|
|
270
|
-
$config2 = Get-Content $configFile2 -Raw | ConvertFrom-Json
|
|
271
|
-
$sessionToken = $config2.hookApiKey
|
|
272
|
-
if (-not $sessionToken) { $sessionToken = $config2.apiKey }
|
|
273
|
-
if ($sessionToken) {
|
|
274
|
-
$sessionBody = @{ session_name = $sessionName } | ConvertTo-Json -Depth 10
|
|
275
|
-
Start-Job -ScriptBlock {
|
|
276
|
-
param($body, $token)
|
|
277
|
-
Invoke-RestMethod -Uri "https://mcp.ekkos.dev/api/v1/working/session/current" `
|
|
278
|
-
-Method POST `
|
|
279
|
-
-Headers @{ Authorization = "Bearer $token"; "Content-Type" = "application/json" } `
|
|
280
|
-
-Body ([System.Text.Encoding]::UTF8.GetBytes($body)) -ErrorAction SilentlyContinue | Out-Null
|
|
281
|
-
} -ArgumentList $sessionBody, $sessionToken | Out-Null
|
|
282
|
-
}
|
|
283
|
-
} catch {}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
288
|
-
# TURN TRACKING & STATE MANAGEMENT
|
|
289
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
290
|
-
$stateDir = Join-Path $env:USERPROFILE ".claude\state"
|
|
291
|
-
if (-not (Test-Path $stateDir)) {
|
|
292
|
-
New-Item -ItemType Directory -Path $stateDir -Force | Out-Null
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
$stateFile = Join-Path $stateDir "hook-state.json"
|
|
296
|
-
$turn = 0
|
|
297
|
-
$contextPercent = ""
|
|
298
|
-
|
|
299
|
-
if (Test-Path $stateFile) {
|
|
300
|
-
try {
|
|
301
|
-
$hookState = Get-Content $stateFile -Raw | ConvertFrom-Json
|
|
302
|
-
# Only continue incrementing if this state belongs to the SAME session.
|
|
303
|
-
# If session changed, reset turn counter to 0.
|
|
304
|
-
if ($hookState.session_id -eq $rawSessionId) {
|
|
305
|
-
$turn = [int]$hookState.turn + 1
|
|
306
|
-
} else {
|
|
307
|
-
$turn = 0
|
|
308
|
-
}
|
|
309
|
-
} catch {
|
|
310
|
-
$turn = 0
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
# Save updated state
|
|
315
|
-
$newState = @{
|
|
316
|
-
turn = $turn
|
|
317
|
-
session_id = $rawSessionId
|
|
318
|
-
last_query = $userQuery.Substring(0, [Math]::Min(100, $userQuery.Length))
|
|
319
|
-
timestamp = (Get-Date).ToString("o")
|
|
320
|
-
} | ConvertTo-Json -Depth 10
|
|
321
|
-
|
|
322
|
-
Set-Content -Path $stateFile -Value $newState -Force
|
|
323
|
-
|
|
324
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
325
|
-
# LOCAL CACHE: Tier 0 capture (async, non-blocking)
|
|
326
|
-
# Per v1.2 ADDENDUM: Pass instanceId for namespacing
|
|
327
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
328
|
-
$captureCmd = Get-Command "ekkos-capture" -ErrorAction SilentlyContinue
|
|
329
|
-
if ($captureCmd -and $rawSessionId -ne "unknown") {
|
|
330
|
-
try {
|
|
331
|
-
# NEW format: ekkos-capture user <instance_id> <session_id> <session_name> <turn_id> <query> [project_path]
|
|
332
|
-
$queryBase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($userQuery))
|
|
333
|
-
$projectRoot = if ($env:PWD) { $env:PWD } else { (Get-Location).Path }
|
|
334
|
-
|
|
335
|
-
Start-Job -ScriptBlock {
|
|
336
|
-
param($instanceId, $sessionId, $sessionName, $turnNum, $queryB64, $projectPath)
|
|
337
|
-
try {
|
|
338
|
-
$decoded = [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($queryB64))
|
|
339
|
-
& ekkos-capture user $instanceId $sessionId $sessionName $turnNum $decoded $projectPath 2>&1 | Out-Null
|
|
340
|
-
} catch {}
|
|
341
|
-
} -ArgumentList $EkkosInstanceId, $rawSessionId, $sessionName, $turn, $queryBase64, $projectRoot | Out-Null
|
|
342
|
-
} catch {}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
346
|
-
# WORKING MEMORY: Fast capture to API (async, non-blocking)
|
|
347
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
348
|
-
$configFile = Join-Path $EkkosConfigDir "config.json"
|
|
349
|
-
if (Test-Path $configFile) {
|
|
350
|
-
try {
|
|
351
|
-
$config = Get-Content $configFile -Raw | ConvertFrom-Json
|
|
352
|
-
$captureToken = $config.hookApiKey
|
|
353
|
-
if (-not $captureToken) { $captureToken = $config.apiKey }
|
|
354
|
-
|
|
355
|
-
if ($captureToken) {
|
|
356
|
-
# Async capture using Start-Job (non-blocking)
|
|
357
|
-
Start-Job -ScriptBlock {
|
|
358
|
-
param($token, $instanceId, $sessionId, $sessionName, $turnNum, $query)
|
|
359
|
-
$body = @{
|
|
360
|
-
session_id = $sessionId
|
|
361
|
-
session_name = $sessionName
|
|
362
|
-
instance_id = $instanceId
|
|
363
|
-
turn = $turnNum
|
|
364
|
-
query = $query
|
|
365
|
-
} | ConvertTo-Json -Depth 10
|
|
366
|
-
|
|
367
|
-
Invoke-RestMethod -Uri "https://mcp.ekkos.dev/api/v1/working/fast-capture" `
|
|
368
|
-
-Method POST `
|
|
369
|
-
-Headers @{ Authorization = "Bearer $token" } `
|
|
370
|
-
-ContentType "application/json" `
|
|
371
|
-
-Body ([System.Text.Encoding]::UTF8.GetBytes($body)) -ErrorAction SilentlyContinue
|
|
372
|
-
} -ArgumentList $captureToken, $EkkosInstanceId, $rawSessionId, $sessionName, $turn, $userQuery | Out-Null
|
|
373
|
-
}
|
|
374
|
-
} catch {}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
$timestamp = (Get-Date).ToString("yyyy-MM-dd hh:mm:ss tt") + " EST"
|
|
378
|
-
|
|
379
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
380
|
-
# DASHBOARD HINT FILE: write session info for ekkos dashboard --wait-for-new
|
|
381
|
-
# On Windows, active-sessions.json is never populated (hook PIDs are dead).
|
|
382
|
-
# The dashboard reads this file instead to locate the JSONL path.
|
|
383
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
384
|
-
if ($sessionName -ne "unknown-session") {
|
|
385
|
-
try {
|
|
386
|
-
$projectPath = if ($env:PWD) { $env:PWD } else { (Get-Location).Path }
|
|
387
|
-
$hintFile = Join-Path $EkkosConfigDir "hook-session-hint.json"
|
|
388
|
-
$hint = @{
|
|
389
|
-
sessionName = $sessionName
|
|
390
|
-
sessionId = $rawSessionId
|
|
391
|
-
projectPath = $projectPath
|
|
392
|
-
ts = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()
|
|
393
|
-
} | ConvertTo-Json -Depth 10 -Compress
|
|
394
|
-
Set-Content -Path $hintFile -Value $hint -Force
|
|
395
|
-
} catch {}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
399
|
-
# OUTPUT SYSTEM REMINDER
|
|
400
|
-
# ═══════════════════════════════════════════════════════════════════════════
|
|
401
|
-
$esc = [char]27
|
|
402
|
-
$header = "${esc}[0;36m${esc}[1m🧠 ekkOS Memory${esc}[0m ${esc}[2m| $sessionName | $timestamp${esc}[0m"
|
|
403
|
-
|
|
404
|
-
$output = @"
|
|
405
|
-
$header
|
|
406
|
-
|
|
407
|
-
"@
|
|
408
|
-
|
|
409
|
-
if ($skillReminders.Count -gt 0) {
|
|
410
|
-
$output += "${esc}[0;35m${esc}[1m" + ($skillReminders -join "`n") + "${esc}[0m`n"
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
$output += @"
|
|
414
|
-
|
|
415
|
-
<footer-format>End responses with: Claude Code ({Model}) · 🧠 ekkOS_™ · $sessionName · $timestamp</footer-format>
|
|
416
|
-
<footer-note>Do not include a turn counter in the footer.</footer-note>
|
|
417
|
-
"@
|
|
418
|
-
|
|
419
|
-
Write-Output $output
|