skillo 0.1.4 → 0.2.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.
@@ -0,0 +1,348 @@
1
+ /**
2
+ * Postinstall script — auto-setup shell integration on `npm i -g skillo`.
3
+ *
4
+ * Standalone (zero dependencies, plain Node.js).
5
+ * Detects current shell, writes integration script, adds source line to rc file.
6
+ * Silent on failure — never breaks `npm install`.
7
+ *
8
+ * Cross-platform: macOS, Linux, Windows (PowerShell).
9
+ */
10
+
11
+ import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync } from "fs";
12
+ import { join } from "path";
13
+ import { homedir, platform } from "os";
14
+
15
+ const home = homedir();
16
+ const isWin = platform() === "win32";
17
+ const dataDir = join(home, ".skillo");
18
+ const scriptPath = join(dataDir, "shell-integration");
19
+
20
+ function ensureDir(dir) {
21
+ if (!existsSync(dir)) {
22
+ mkdirSync(dir, { recursive: true });
23
+ }
24
+ }
25
+
26
+ function detectShell() {
27
+ if (isWin) return "powershell";
28
+ const sh = process.env.SHELL || "";
29
+ if (sh.includes("zsh")) return "zsh";
30
+ if (sh.includes("fish")) return "fish";
31
+ return "bash";
32
+ }
33
+
34
+ // ── Shell scripts ────────────────────────────────────────────────────────────
35
+ //
36
+ // Timing: uses `date +%s` (seconds) everywhere for portability.
37
+ // macOS BSD date does NOT support %N or %3N — those are GNU extensions.
38
+ // Duration is reported in ms (seconds * 1000). Good enough for our purposes.
39
+
40
+ const bashScript = `# Skillo CLI Integration
41
+ # Records commands and auto-detects tracked projects
42
+
43
+ # Ensure daemon is running (< 1ms PID file check)
44
+ _skillo_ensure_daemon() {
45
+ local pidfile="$HOME/.skillo/daemon.pid"
46
+ if [ -f "$pidfile" ]; then
47
+ local pid
48
+ pid=$(cat "$pidfile" 2>/dev/null)
49
+ if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
50
+ return 0
51
+ fi
52
+ fi
53
+ (skillo start &>/dev/null &)
54
+ }
55
+ _skillo_ensure_daemon
56
+
57
+ _skillo_session="\${SKILLO_SESSION:-default}"
58
+ _skillo_last_cwd=
59
+
60
+ _skillo_preexec() {
61
+ _skillo_cmd="$1"
62
+ _skillo_start_time=$(date +%s)
63
+ }
64
+
65
+ _skillo_check_project() {
66
+ if [ "$PWD" = "$_skillo_last_cwd" ]; then
67
+ return
68
+ fi
69
+ _skillo_last_cwd="$PWD"
70
+ local result
71
+ result=$(skillo session-auto "$PWD" --pid $$ --shell bash 2>/dev/null)
72
+ if [ -n "$result" ]; then
73
+ export SKILLO_SESSION="$result"
74
+ _skillo_session="$result"
75
+ fi
76
+ }
77
+
78
+ _skillo_precmd() {
79
+ local exit_code=$?
80
+ _skillo_check_project
81
+ if [ -n "$_skillo_cmd" ]; then
82
+ local duration=0
83
+ if [ -n "$_skillo_start_time" ]; then
84
+ local end_time=$(date +%s)
85
+ duration=$(( (end_time - _skillo_start_time) * 1000 ))
86
+ fi
87
+ (skillo record "$_skillo_cmd" --cwd "$PWD" --exit-code "$exit_code" --duration "$duration" --session "$_skillo_session" &>/dev/null &)
88
+ _skillo_cmd=""
89
+ fi
90
+ }
91
+
92
+ _skillo_cleanup() {
93
+ if [ -n "$SKILLO_SESSION" ] && [ "$SKILLO_SESSION" != "default" ]; then
94
+ skillo session end --session "$SKILLO_SESSION" &>/dev/null
95
+ fi
96
+ }
97
+ trap _skillo_cleanup EXIT
98
+
99
+ trap '_skillo_preexec "$BASH_COMMAND"' DEBUG
100
+
101
+ if [[ ! "$PROMPT_COMMAND" =~ _skillo_precmd ]]; then
102
+ PROMPT_COMMAND="_skillo_precmd\${PROMPT_COMMAND:+;$PROMPT_COMMAND}"
103
+ fi
104
+
105
+ _skillo_check_project
106
+ `;
107
+
108
+ const zshScript = `# Skillo CLI Integration
109
+ # Records commands and auto-detects tracked projects
110
+
111
+ # Ensure daemon is running (< 1ms PID file check)
112
+ _skillo_ensure_daemon() {
113
+ local pidfile="$HOME/.skillo/daemon.pid"
114
+ if [[ -f "$pidfile" ]]; then
115
+ local pid
116
+ pid=$(cat "$pidfile" 2>/dev/null)
117
+ if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
118
+ return 0
119
+ fi
120
+ fi
121
+ (skillo start &>/dev/null &)
122
+ }
123
+ _skillo_ensure_daemon
124
+
125
+ _skillo_session="\${SKILLO_SESSION:-default}"
126
+ _skillo_last_cwd=
127
+
128
+ _skillo_preexec() {
129
+ _skillo_cmd="$1"
130
+ _skillo_start_time=$(date +%s)
131
+ }
132
+
133
+ _skillo_check_project() {
134
+ if [[ "$PWD" == "$_skillo_last_cwd" ]]; then
135
+ return
136
+ fi
137
+ _skillo_last_cwd="$PWD"
138
+ local result
139
+ result=$(skillo session-auto "$PWD" --pid $$ --shell zsh 2>/dev/null)
140
+ if [[ -n "$result" ]]; then
141
+ export SKILLO_SESSION="$result"
142
+ _skillo_session="$result"
143
+ fi
144
+ }
145
+
146
+ _skillo_precmd() {
147
+ local exit_code=$?
148
+ _skillo_check_project
149
+ if [[ -n "$_skillo_cmd" ]]; then
150
+ local duration=0
151
+ if [[ -n "$_skillo_start_time" ]]; then
152
+ local end_time=$(date +%s)
153
+ duration=$(( (end_time - _skillo_start_time) * 1000 ))
154
+ fi
155
+ (skillo record "$_skillo_cmd" --cwd "$PWD" --exit-code "$exit_code" --duration "$duration" --session "$_skillo_session" &>/dev/null &)
156
+ _skillo_cmd=""
157
+ fi
158
+ }
159
+
160
+ _skillo_cleanup() {
161
+ if [[ -n "$SKILLO_SESSION" ]] && [[ "$SKILLO_SESSION" != "default" ]]; then
162
+ skillo session end --session "$SKILLO_SESSION" &>/dev/null
163
+ fi
164
+ }
165
+ trap _skillo_cleanup EXIT
166
+
167
+ autoload -Uz add-zsh-hook
168
+ add-zsh-hook preexec _skillo_preexec
169
+ add-zsh-hook precmd _skillo_precmd
170
+
171
+ _skillo_check_project
172
+ `;
173
+
174
+ const fishScript = `# Skillo CLI Integration
175
+ # Records commands and auto-detects tracked projects
176
+
177
+ # Ensure daemon is running (< 1ms PID file check)
178
+ function _skillo_ensure_daemon
179
+ set -l pidfile "$HOME/.skillo/daemon.pid"
180
+ if test -f "$pidfile"
181
+ set -l pid (cat "$pidfile" 2>/dev/null)
182
+ if test -n "$pid"; and kill -0 "$pid" 2>/dev/null
183
+ return 0
184
+ end
185
+ end
186
+ skillo start &>/dev/null &
187
+ end
188
+ _skillo_ensure_daemon
189
+
190
+ set -q SKILLO_SESSION; or set -g SKILLO_SESSION "default"
191
+
192
+ function _skillo_postexec --on-event fish_postexec
193
+ set -l cmd $argv[1]
194
+ set -l exit_code $status
195
+ skillo record "$cmd" --cwd (pwd) --exit-code $exit_code --session $SKILLO_SESSION &>/dev/null &
196
+ end
197
+ `;
198
+
199
+ const psScript = `# Skillo CLI Integration
200
+ # Records commands and auto-detects tracked projects
201
+
202
+ # Ensure daemon is running (< 1ms PID file check)
203
+ function _SkilloEnsureDaemon {
204
+ $pidfile = Join-Path $env:USERPROFILE ".skillo" "daemon.pid"
205
+ if (Test-Path $pidfile) {
206
+ $pid = Get-Content $pidfile -ErrorAction SilentlyContinue
207
+ if ($pid) {
208
+ try {
209
+ Get-Process -Id ([int]$pid) -ErrorAction Stop | Out-Null
210
+ return
211
+ } catch { }
212
+ }
213
+ }
214
+ Start-Job -ScriptBlock { & skillo start 2>$null } | Out-Null
215
+ }
216
+ _SkilloEnsureDaemon
217
+
218
+ $Global:SkilloSessionId = if ($env:SKILLO_SESSION) { $env:SKILLO_SESSION } else { 'default' }
219
+ $Global:SkilloLastHistoryId = 0
220
+ $Global:SkilloLastCwd = $null
221
+
222
+ function _SkilloCheckProject {
223
+ $cwd = (Get-Location).Path
224
+ if ($cwd -eq $Global:SkilloLastCwd) { return }
225
+ $Global:SkilloLastCwd = $cwd
226
+
227
+ try {
228
+ $result = & skillo session-auto $cwd --pid $PID --shell powershell 2>$null
229
+ if ($result -and $result.Trim()) {
230
+ $env:SKILLO_SESSION = $result.Trim()
231
+ $Global:SkilloSessionId = $result.Trim()
232
+ }
233
+ } catch { }
234
+ }
235
+
236
+ $Global:SkilloOriginalPrompt = $function:prompt
237
+ function Global:prompt {
238
+ $lastCmd = Get-History -Count 1 -ErrorAction SilentlyContinue
239
+
240
+ if ($lastCmd -and $lastCmd.Id -gt $Global:SkilloLastHistoryId) {
241
+ $Global:SkilloLastHistoryId = $lastCmd.Id
242
+
243
+ $duration = 0
244
+ if ($lastCmd.EndExecutionTime -and $lastCmd.StartExecutionTime) {
245
+ $duration = [int]($lastCmd.EndExecutionTime - $lastCmd.StartExecutionTime).TotalMilliseconds
246
+ }
247
+ $exit = if ($null -eq $LASTEXITCODE) { 0 } else { $LASTEXITCODE }
248
+ $cwd = (Get-Location).Path
249
+
250
+ Start-Job -ScriptBlock {
251
+ param($cmd, $cwd, $exit, $dur, $sessionId)
252
+ & skillo record $cmd --cwd $cwd --exit-code $exit --duration $dur --session $sessionId 2>$null
253
+ } -ArgumentList $lastCmd.CommandLine, $cwd, $exit, $duration, $Global:SkilloSessionId | Out-Null
254
+ }
255
+
256
+ _SkilloCheckProject
257
+ & $Global:SkilloOriginalPrompt
258
+ }
259
+
260
+ $null = Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action {
261
+ if ($Global:SkilloSessionId -and $Global:SkilloSessionId -ne 'default') {
262
+ try { & skillo session end --session $Global:SkilloSessionId 2>$null } catch { }
263
+ }
264
+ }
265
+ `;
266
+
267
+ // ── Install logic ────────────────────────────────────────────────────────────
268
+
269
+ function install(shell) {
270
+ ensureDir(scriptPath);
271
+
272
+ let scriptFile, rcPath, sourceLine;
273
+
274
+ if (shell === "zsh") {
275
+ scriptFile = join(scriptPath, "skillo.zsh");
276
+ writeFileSync(scriptFile, zshScript, "utf-8");
277
+ rcPath = join(home, ".zshrc");
278
+ sourceLine = `\n# Skillo CLI Integration\n[ -f "${scriptFile}" ] && source "${scriptFile}"\n`;
279
+ } else if (shell === "bash") {
280
+ scriptFile = join(scriptPath, "skillo.bash");
281
+ writeFileSync(scriptFile, bashScript, "utf-8");
282
+ rcPath = join(home, ".bashrc");
283
+ sourceLine = `\n# Skillo CLI Integration\n[ -f "${scriptFile}" ] && source "${scriptFile}"\n`;
284
+ } else if (shell === "fish") {
285
+ scriptFile = join(scriptPath, "skillo.fish");
286
+ writeFileSync(scriptFile, fishScript, "utf-8");
287
+ const fishConfigDir = join(home, ".config", "fish");
288
+ ensureDir(fishConfigDir);
289
+ rcPath = join(fishConfigDir, "config.fish");
290
+ sourceLine = `\n# Skillo CLI Integration\nif test -f "${scriptFile}"\n source "${scriptFile}"\nend\n`;
291
+ } else if (shell === "powershell") {
292
+ scriptFile = join(scriptPath, "skillo.ps1");
293
+ writeFileSync(scriptFile, psScript, "utf-8");
294
+ // Try PowerShell Core profile first, then Windows PowerShell
295
+ const psCorePath = isWin
296
+ ? join(home, "Documents", "PowerShell", "Microsoft.PowerShell_profile.ps1")
297
+ : join(home, ".config", "powershell", "Microsoft.PowerShell_profile.ps1");
298
+ const psClassicPath = join(home, "Documents", "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1");
299
+ // Use whichever profile already exists, or default to Core
300
+ rcPath = existsSync(psClassicPath) ? psClassicPath : psCorePath;
301
+ ensureDir(join(rcPath, ".."));
302
+ sourceLine = `\n# Skillo CLI Integration\n. "${scriptFile}"\n`;
303
+ } else {
304
+ return false;
305
+ }
306
+
307
+ // Add source line to rc file (idempotent)
308
+ if (existsSync(rcPath)) {
309
+ const content = readFileSync(rcPath, "utf-8");
310
+ if (content.includes("Skillo CLI Integration")) {
311
+ // Already installed — just update the script file (already written above)
312
+ return true;
313
+ }
314
+ appendFileSync(rcPath, sourceLine);
315
+ } else {
316
+ writeFileSync(rcPath, sourceLine, "utf-8");
317
+ }
318
+
319
+ return true;
320
+ }
321
+
322
+ // ── Main ─────────────────────────────────────────────────────────────────────
323
+
324
+ try {
325
+ const shell = detectShell();
326
+ const installed = install(shell);
327
+
328
+ if (installed) {
329
+ const dim = "\x1b[90m";
330
+ const green = "\x1b[32m";
331
+ const reset = "\x1b[0m";
332
+ const rcHint = {
333
+ zsh: "source ~/.zshrc",
334
+ bash: "source ~/.bashrc",
335
+ fish: "source ~/.config/fish/config.fish",
336
+ powershell: ". $PROFILE",
337
+ }[shell] || "";
338
+ console.log("");
339
+ console.log(`${green}Skillo${reset}: Shell integration installed for ${shell}.`);
340
+ if (rcHint) {
341
+ console.log(`${dim}Restart your terminal or run: ${rcHint}${reset}`);
342
+ }
343
+ console.log(`${dim}Run 'skillo setup-shell --uninstall' to remove.${reset}`);
344
+ console.log("");
345
+ }
346
+ } catch {
347
+ // Never break npm install — silently skip on any error
348
+ }
@@ -1,11 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- ApiClient,
4
- getApiClient
5
- } from "./chunk-QNRWQL6M.js";
6
- import "./chunk-SWPZL2RI.js";
7
- export {
8
- ApiClient,
9
- getApiClient
10
- };
11
- //# sourceMappingURL=api-client-HTWGTHC2.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/config.ts","../src/core/api-client.ts"],"sourcesContent":["/**\n * Configuration management for Skillo.\n */\n\nimport { readFileSync, writeFileSync, existsSync } from \"fs\";\nimport { dirname } from \"path\";\nimport YAML from \"yaml\";\nimport { ensureDirectory, getConfigFile } from \"../utils/paths.js\";\n\nexport interface SkilloConfig {\n // Shell settings\n defaultShell: string | null;\n\n // Pattern detection\n patternDetection: {\n minCount: number;\n sessionTimeout: number;\n similarityThreshold: number;\n maxSequenceLength: number;\n };\n\n // Privacy settings\n privacy: {\n autoRedact: boolean;\n trackOutput: boolean;\n neverTrack: string[];\n redactionPatterns: string[];\n };\n\n // Notification settings\n notifications: {\n enabled: boolean;\n style: \"inline\" | \"desktop\" | \"both\";\n sound: boolean;\n };\n\n // Skill generation\n skillGeneration: {\n outputDir: string | null;\n includeScripts: boolean;\n includeExamples: boolean;\n autoGenerate: boolean;\n };\n\n // Claude Code integration\n claudeCode: {\n watchConversations: boolean;\n conversationDir: string | null;\n };\n\n // Daemon settings\n daemon: {\n logLevel: \"DEBUG\" | \"INFO\" | \"WARN\" | \"ERROR\";\n patternCheckInterval: number;\n conversationCheckInterval: number;\n };\n\n // Team settings\n team: {\n enabled: boolean;\n slug: string | null;\n autoSync: boolean;\n syncInterval: number;\n };\n\n // API settings\n api: {\n baseUrl: string | null;\n timeout: number;\n };\n\n // API key (stored separately, not in YAML for security)\n apiKey?: string;\n}\n\nexport function getDefaultConfig(): SkilloConfig {\n return {\n defaultShell: null,\n\n patternDetection: {\n minCount: 3,\n sessionTimeout: 30,\n similarityThreshold: 0.8,\n maxSequenceLength: 10,\n },\n\n privacy: {\n autoRedact: true,\n trackOutput: false,\n neverTrack: [\n \"*password*\",\n \"*secret*\",\n \"vault *\",\n \"1password *\",\n \"op *\",\n \"export *_KEY=*\",\n \"export *_SECRET=*\",\n \"export *_TOKEN=*\",\n ],\n redactionPatterns: [\n \"password[=:]\\\\s*\\\\S+\",\n \"token[=:]\\\\s*\\\\S+\",\n \"secret[=:]\\\\s*\\\\S+\",\n \"api[_-]?key[=:]\\\\s*\\\\S+\",\n \"auth[=:]\\\\s*\\\\S+\",\n \"bearer\\\\s+\\\\S+\",\n \"-----BEGIN.*PRIVATE KEY-----\",\n \"AKIA[0-9A-Z]{16}\",\n '[\"\\'][A-Za-z0-9+/]{40,}[\"\\']',\n ],\n },\n\n notifications: {\n enabled: true,\n style: \"inline\",\n sound: false,\n },\n\n skillGeneration: {\n outputDir: null,\n includeScripts: true,\n includeExamples: true,\n autoGenerate: false,\n },\n\n claudeCode: {\n watchConversations: true,\n conversationDir: null,\n },\n\n daemon: {\n logLevel: \"INFO\",\n patternCheckInterval: 60,\n conversationCheckInterval: 5,\n },\n\n team: {\n enabled: false,\n slug: null,\n autoSync: true,\n syncInterval: 300,\n },\n\n api: {\n baseUrl: null,\n timeout: 30000,\n },\n };\n}\n\nfunction deepMerge<T extends object>(base: T, override: Partial<T>): T {\n const result = { ...base };\n\n for (const key of Object.keys(override) as Array<keyof T>) {\n const value = override[key];\n if (\n value !== undefined &&\n typeof result[key] === \"object\" &&\n result[key] !== null &&\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value)\n ) {\n result[key] = deepMerge(\n result[key] as Record<string, unknown>,\n value as Record<string, unknown>\n ) as T[keyof T];\n } else if (value !== undefined) {\n result[key] = value as T[keyof T];\n }\n }\n\n return result;\n}\n\nexport function loadConfig(path?: string): SkilloConfig {\n const configPath = path || getConfigFile();\n\n if (!existsSync(configPath)) {\n return getDefaultConfig();\n }\n\n try {\n const content = readFileSync(configPath, \"utf-8\");\n const parsed = YAML.parse(content) || {};\n\n // Convert snake_case to camelCase for compatibility\n const converted = convertKeysToCamelCase(parsed);\n const defaultConfig = getDefaultConfig();\n\n // Deep merge with default config\n return deepMerge(defaultConfig, converted as Partial<SkilloConfig>);\n } catch {\n return getDefaultConfig();\n }\n}\n\nexport function saveConfig(config: SkilloConfig, path?: string): void {\n const configPath = path || getConfigFile();\n\n ensureDirectory(dirname(configPath));\n\n // Convert camelCase to snake_case for YAML file\n const converted = convertKeysToSnakeCase(config);\n\n const content = YAML.stringify(converted, {\n indent: 2,\n lineWidth: 0,\n });\n\n writeFileSync(configPath, content, \"utf-8\");\n}\n\nexport function getConfigValue(config: SkilloConfig, key: string): unknown {\n const keys = key.split(\".\");\n let current: unknown = config;\n\n for (const k of keys) {\n if (current && typeof current === \"object\" && k in current) {\n current = (current as Record<string, unknown>)[k];\n } else {\n return undefined;\n }\n }\n\n return current;\n}\n\nexport function setConfigValue(config: SkilloConfig, key: string, value: unknown): SkilloConfig {\n const keys = key.split(\".\");\n const result = JSON.parse(JSON.stringify(config)) as SkilloConfig;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let current: any = result;\n\n for (let i = 0; i < keys.length - 1; i++) {\n const k = keys[i];\n if (!(k in current)) {\n current[k] = {};\n }\n current = current[k] as Record<string, unknown>;\n }\n\n current[keys[keys.length - 1]] = value;\n return result;\n}\n\n// Helper functions for key conversion\nfunction toCamelCase(str: string): string {\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\n}\n\nfunction toSnakeCase(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\n}\n\nfunction convertKeysToCamelCase(obj: unknown): unknown {\n if (Array.isArray(obj)) {\n return obj.map(convertKeysToCamelCase);\n }\n\n if (obj !== null && typeof obj === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[toCamelCase(key)] = convertKeysToCamelCase(value);\n }\n return result;\n }\n\n return obj;\n}\n\nfunction convertKeysToSnakeCase(obj: unknown): unknown {\n if (Array.isArray(obj)) {\n return obj.map(convertKeysToSnakeCase);\n }\n\n if (obj !== null && typeof obj === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[toSnakeCase(key)] = convertKeysToSnakeCase(value);\n }\n return result;\n }\n\n return obj;\n}\n","/**\n * Skillo Platform API Client\n *\n * Handles communication with the Skillo platform API.\n */\n\nimport { loadConfig, saveConfig } from \"./config.js\";\n\nconst DEFAULT_API_URL = \"https://www.skillo.one/api/cli\";\n\ninterface ApiResponse<T = unknown> {\n success: boolean;\n data?: T;\n error?: string;\n message?: string;\n}\n\ninterface AuthResponse {\n success: boolean;\n user: {\n id: string;\n email: string;\n name: string;\n };\n}\n\ninterface SyncResponse {\n success: boolean;\n skills?: Array<{\n id: string;\n name: string;\n slug: string;\n description: string;\n content: string;\n commands: string[];\n }>;\n patterns?: Array<{\n id: string;\n name: string;\n description: string;\n commands: string[];\n status: string;\n }>;\n}\n\ninterface GenerateResponse {\n success: boolean;\n skill: {\n id: string;\n name: string;\n slug: string;\n description: string;\n content: string;\n commands: string[];\n };\n}\n\ninterface DeviceAuthResponse {\n code: string;\n verification_url: string;\n expires_in: number;\n interval: number;\n}\n\ninterface TokenStatusResponse {\n status: \"pending\" | \"ready\" | \"expired\" | \"used\" | \"not_found\";\n}\n\ninterface TokenExchangeResponse {\n success: boolean;\n api_key: string;\n user: {\n id: string;\n };\n}\n\nexport class ApiClient {\n private baseUrl: string;\n private apiKey: string | null;\n\n constructor() {\n const config = loadConfig();\n this.baseUrl = config.api?.baseUrl || DEFAULT_API_URL;\n this.apiKey = this.loadApiKey();\n }\n\n /**\n * Load API key from config\n */\n private loadApiKey(): string | null {\n const config = loadConfig();\n return (config as { apiKey?: string }).apiKey || null;\n }\n\n /**\n * Save API key to config\n */\n saveApiKey(key: string): void {\n const config = loadConfig();\n (config as { apiKey?: string }).apiKey = key;\n saveConfig(config);\n this.apiKey = key;\n }\n\n /**\n * Clear API key from config\n */\n clearApiKey(): void {\n const config = loadConfig();\n delete (config as { apiKey?: string }).apiKey;\n saveConfig(config);\n this.apiKey = null;\n }\n\n /**\n * Check if API key is configured\n */\n hasApiKey(): boolean {\n return !!this.apiKey;\n }\n\n /**\n * Get the configured API key (masked)\n */\n getMaskedApiKey(): string | null {\n if (!this.apiKey) return null;\n return `${this.apiKey.substring(0, 12)}...${this.apiKey.substring(this.apiKey.length - 4)}`;\n }\n\n /**\n * Set the API base URL\n */\n setBaseUrl(url: string): void {\n const config = loadConfig();\n if (!config.api) {\n (config as { api?: { baseUrl: string } }).api = { baseUrl: url };\n } else {\n config.api.baseUrl = url;\n }\n saveConfig(config);\n this.baseUrl = url;\n }\n\n /**\n * Make an authenticated API request\n */\n private async request<T>(\n endpoint: string,\n options: RequestInit = {}\n ): Promise<ApiResponse<T>> {\n if (!this.apiKey) {\n return { success: false, error: \"No API key configured. Run 'skillo login' first.\" };\n }\n\n const url = `${this.baseUrl}${endpoint}`;\n const headers: HeadersInit = {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n ...options.headers,\n };\n\n try {\n const response = await fetch(url, {\n ...options,\n headers,\n });\n\n const data = await response.json();\n\n if (process.env.SKILLO_DEBUG) {\n console.log('[DEBUG] Request:', url);\n console.log('[DEBUG] Status:', response.status);\n console.log('[DEBUG] Response:', JSON.stringify(data));\n }\n\n if (!response.ok) {\n return {\n success: false,\n error: data.error || `Request failed with status ${response.status}`,\n };\n }\n\n // If the response already has success/data structure, return as-is\n // Otherwise wrap the data\n if (data.success !== undefined && data.data !== undefined) {\n return { success: data.success, data: data.data as T, message: data.message };\n }\n\n // If response just has success field (no data wrapper), return as-is\n if (data.success !== undefined) {\n return data;\n }\n\n // Legacy: wrap raw data\n return { success: true, data: data as T };\n } catch (error) {\n if (process.env.SKILLO_DEBUG) {\n console.log('[DEBUG] Error:', error);\n }\n if (error instanceof Error) {\n if (error.message.includes(\"ECONNREFUSED\")) {\n return {\n success: false,\n error: \"Cannot connect to Skillo platform. Is the server running?\",\n };\n }\n return { success: false, error: error.message };\n }\n return { success: false, error: \"Unknown error occurred\" };\n }\n }\n\n /**\n * Authenticate with the platform\n */\n async authenticate(): Promise<ApiResponse<AuthResponse>> {\n return this.request<AuthResponse>(\"/auth\", { method: \"POST\" });\n }\n\n /**\n * Sync commands to platform\n */\n async syncCommands(\n commands: Array<{\n timestamp: string;\n command: string;\n normalized: string;\n cwd: string;\n exitCode?: number | null;\n durationMs?: number | null;\n sessionId?: string;\n variables?: Record<string, string> | null;\n }>\n ): Promise<ApiResponse> {\n return this.request(\"/sync\", {\n method: \"POST\",\n body: JSON.stringify({\n type: \"commands\",\n data: commands,\n }),\n });\n }\n\n /**\n * Sync a pattern to platform\n */\n async syncPattern(pattern: {\n sourceType: string;\n name?: string;\n description?: string;\n commands: string[];\n category?: string;\n frequency?: number;\n score?: number;\n firstSeen?: string;\n lastSeen?: string;\n context?: Record<string, unknown>;\n }): Promise<ApiResponse<{ pattern: { id: string; name: string; status: string } }>> {\n return this.request(\"/sync\", {\n method: \"POST\",\n body: JSON.stringify({\n type: \"pattern\",\n data: pattern,\n }),\n });\n }\n\n /**\n * Create a session on the platform\n */\n async createSession(session: {\n startedAt: string;\n shell: string;\n endedAt?: string;\n commandCount?: number;\n }): Promise<ApiResponse<{ session: { id: string } }>> {\n return this.request(\"/sync\", {\n method: \"POST\",\n body: JSON.stringify({\n type: \"session\",\n data: session,\n }),\n });\n }\n\n /**\n * End a session on the platform\n */\n async endSession(sessionId: string): Promise<ApiResponse<{ success: boolean }>> {\n return this.request(\"/sessions\", {\n method: \"PATCH\",\n body: JSON.stringify({ sessionId }),\n });\n }\n\n /**\n * Start a new session on the platform (for standalone terminals)\n */\n async startSession(shell: string): Promise<ApiResponse<{ sessionId: string }>> {\n return this.request(\"/sessions\", {\n method: \"POST\",\n body: JSON.stringify({ shell }),\n });\n }\n\n /**\n * Download skills and patterns from platform\n */\n async downloadData(type: \"all\" | \"skills\" | \"patterns\" = \"all\"): Promise<ApiResponse<SyncResponse>> {\n return this.request<SyncResponse>(`/sync?type=${type}`, { method: \"GET\" });\n }\n\n /**\n * Generate a skill from a pattern\n */\n async generateSkill(options: {\n patternId?: string;\n commands?: string[];\n name?: string;\n description?: string;\n category?: string;\n context?: Record<string, unknown>;\n }): Promise<ApiResponse<GenerateResponse>> {\n return this.request<GenerateResponse>(\"/generate\", {\n method: \"POST\",\n body: JSON.stringify(options),\n });\n }\n\n /**\n * Start device authorization flow (no auth required)\n */\n async startDeviceAuth(deviceName?: string): Promise<ApiResponse<DeviceAuthResponse>> {\n const url = `${this.baseUrl}/auth/device`;\n try {\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ device_name: deviceName }),\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n return {\n success: false,\n error: data.error || `Request failed with status ${response.status}`,\n };\n }\n\n return { success: true, data };\n } catch (error) {\n if (error instanceof Error) {\n if (error.message.includes(\"ECONNREFUSED\")) {\n return {\n success: false,\n error: \"Cannot connect to Skillo platform. Is the server running?\",\n };\n }\n return { success: false, error: error.message };\n }\n return { success: false, error: \"Unknown error occurred\" };\n }\n }\n\n /**\n * Check token status (polling)\n */\n async checkTokenStatus(code: string): Promise<ApiResponse<TokenStatusResponse>> {\n const url = `${this.baseUrl}/token?code=${encodeURIComponent(code)}`;\n try {\n const response = await fetch(url, { method: \"GET\" });\n const data = await response.json();\n\n if (!response.ok) {\n return {\n success: false,\n error: data.error || `Request failed with status ${response.status}`,\n };\n }\n\n return { success: true, data };\n } catch (error) {\n if (error instanceof Error) {\n return { success: false, error: error.message };\n }\n return { success: false, error: \"Unknown error occurred\" };\n }\n }\n\n /**\n * Exchange code for API key\n */\n async exchangeToken(code: string, deviceName?: string): Promise<ApiResponse<TokenExchangeResponse>> {\n const url = `${this.baseUrl}/token`;\n try {\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ code, device_name: deviceName }),\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n return {\n success: false,\n error: data.error || `Request failed with status ${response.status}`,\n };\n }\n\n return { success: true, data };\n } catch (error) {\n if (error instanceof Error) {\n return { success: false, error: error.message };\n }\n return { success: false, error: \"Unknown error occurred\" };\n }\n }\n\n /**\n * Sync Claude Code prompts to platform\n */\n async syncClaudePrompts(\n prompts: Array<{\n display: string;\n timestamp: number;\n project: string;\n sessionId: string;\n pastedContents?: Record<string, unknown>;\n }>,\n options?: {\n terminalSessionId?: string;\n projectPath?: string;\n }\n ): Promise<ApiResponse<{\n sessionsCreated: number;\n promptsCreated: number;\n promptsSkipped: number;\n }>> {\n return this.request(\"/claude\", {\n method: \"POST\",\n body: JSON.stringify({\n prompts,\n terminalSessionId: options?.terminalSessionId,\n projectPath: options?.projectPath,\n }),\n });\n }\n\n /**\n * Get Claude Code sessions from platform\n */\n async getClaudeSessions(limit = 20): Promise<ApiResponse<{\n sessions: Array<{\n id: string;\n claudeSessionId: string;\n projectPath: string;\n projectName: string;\n startedAt: string;\n endedAt?: string;\n promptCount: number;\n prompts?: Array<{\n id: string;\n prompt: string;\n timestamp: string;\n category?: string;\n }>;\n }>;\n }>> {\n return this.request(`/claude?limit=${limit}`, { method: \"GET\" });\n }\n\n // ============================================================================\n // PROJECT TRACKING\n // ============================================================================\n\n /**\n * Connect a project for tracking\n */\n async connectProject(params: {\n path: string;\n name?: string;\n gitRemote?: string;\n gitRemoteNormalized?: string;\n language?: string;\n framework?: string;\n }): Promise<ApiResponse<{\n id: string;\n name: string;\n path: string;\n trackingEnabled: boolean;\n connectedAt: string;\n }>> {\n return this.request(\"/projects/connect\", {\n method: \"POST\",\n body: JSON.stringify(params),\n });\n }\n\n /**\n * Disconnect a project from tracking\n */\n async disconnectProject(path: string): Promise<ApiResponse<{\n id: string;\n name: string;\n trackingEnabled: boolean;\n }>> {\n return this.request(`/projects/connect?path=${encodeURIComponent(path)}`, {\n method: \"DELETE\",\n });\n }\n\n /**\n * Get tracking status for a project\n */\n async getProjectStatus(path: string): Promise<ApiResponse<{\n connected: boolean;\n tracked: boolean;\n connectedAt?: string;\n project?: {\n id: string;\n name: string;\n path: string;\n gitRemote?: string;\n gitRemoteNormalized?: string;\n trackingEnabled: boolean;\n };\n }>> {\n return this.request(`/projects/connect?path=${encodeURIComponent(path)}`, {\n method: \"GET\",\n });\n }\n\n /**\n * List all tracked projects\n */\n async listProjects(includeDisabled = false): Promise<ApiResponse<{\n projects: Array<{\n id: string;\n name: string;\n path: string;\n gitRemote?: string;\n gitRemoteNormalized?: string;\n trackingEnabled: boolean;\n connectedAt?: string;\n }>;\n totalTracked: number;\n totalProjects: number;\n }>> {\n return this.request(`/projects/tracked?includeDisabled=${includeDisabled}`, {\n method: \"GET\",\n });\n }\n\n /**\n * Check if a path is in a tracked project\n * Returns the project if tracked, null if not\n */\n async isProjectTracked(path: string): Promise<{\n tracked: boolean;\n project?: {\n id: string;\n name: string;\n path: string;\n };\n }> {\n const result = await this.getProjectStatus(path);\n if (result.success && result.data?.connected) {\n return {\n tracked: true,\n project: result.data.project ? {\n id: result.data.project.id,\n name: result.data.project.name,\n path: result.data.project.path,\n } : undefined,\n };\n }\n return { tracked: false };\n }\n}\n\n// Singleton instance\nlet clientInstance: ApiClient | null = null;\n\nexport function getApiClient(): ApiClient {\n if (!clientInstance) {\n clientInstance = new ApiClient();\n }\n return clientInstance;\n}\n"],"mappings":"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","names":[]}