skillo 0.2.6 → 0.2.9

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 (68) hide show
  1. package/README.md +198 -198
  2. package/dist/api-client-BF6GDR7Q.js +12 -0
  3. package/dist/{chunk-WJKZWKER.js → chunk-63FVALWX.js} +1 -1
  4. package/dist/chunk-63FVALWX.js.map +1 -0
  5. package/dist/chunk-6GOJPFZ7.js +113 -0
  6. package/dist/chunk-6GOJPFZ7.js.map +1 -0
  7. package/dist/chunk-6UGTWBUW.js +89 -0
  8. package/dist/chunk-6UGTWBUW.js.map +1 -0
  9. package/dist/{chunk-2CVEPT6U.js → chunk-73NUWYUO.js} +2 -2
  10. package/dist/chunk-73NUWYUO.js.map +1 -0
  11. package/dist/chunk-QIV4VIXA.js +221 -0
  12. package/dist/chunk-QIV4VIXA.js.map +1 -0
  13. package/dist/{chunk-CPL3P2OF.js → chunk-QUXHHRRK.js} +2 -2
  14. package/dist/chunk-QUXHHRRK.js.map +1 -0
  15. package/dist/{chunk-ODOZM4QV.js → chunk-RQ2SC5HW.js} +72 -10
  16. package/dist/chunk-RQ2SC5HW.js.map +1 -0
  17. package/dist/chunk-U53QWUOR.js +727 -0
  18. package/dist/chunk-U53QWUOR.js.map +1 -0
  19. package/dist/chunk-VAQ73XPE.js +68 -0
  20. package/dist/chunk-VAQ73XPE.js.map +1 -0
  21. package/dist/chunk-XLJGCOVT.js +975 -0
  22. package/dist/chunk-XLJGCOVT.js.map +1 -0
  23. package/dist/{claude-watcher-N6GN6WHJ.js → claude-watcher-WKGBJYKN.js} +65 -3
  24. package/dist/claude-watcher-WKGBJYKN.js.map +1 -0
  25. package/dist/cli.js +495 -1846
  26. package/dist/cli.js.map +1 -1
  27. package/dist/{config-P5EM5L7N.js → config-ZOKAP2LJ.js} +3 -3
  28. package/dist/daemon-6DTCMOJB.js +28 -0
  29. package/dist/daemon-runner.js +338 -71
  30. package/dist/daemon-runner.js.map +1 -1
  31. package/dist/database-KQY5OSCS.js +9 -0
  32. package/dist/git-OGUSYBJS.js +16 -0
  33. package/dist/git-OGUSYBJS.js.map +1 -0
  34. package/dist/git-OUAHIOY2.js +110 -0
  35. package/dist/git-OUAHIOY2.js.map +1 -0
  36. package/dist/index.js.map +1 -1
  37. package/dist/{paths-INOKEM66.js → paths-MPOZBOKE.js} +2 -2
  38. package/dist/paths-MPOZBOKE.js.map +1 -0
  39. package/dist/project-OFU2W6MH.js +19 -0
  40. package/dist/project-OFU2W6MH.js.map +1 -0
  41. package/dist/shell-NZABRJLA.js +16 -0
  42. package/dist/shell-NZABRJLA.js.map +1 -0
  43. package/dist/skill-installer-F67OAOQN.js +121 -0
  44. package/dist/skill-installer-F67OAOQN.js.map +1 -0
  45. package/dist/{skill-usage-detector-EO26MRYV.js → skill-usage-detector-MSW5VWQZ.js} +2 -2
  46. package/dist/skill-usage-detector-MSW5VWQZ.js.map +1 -0
  47. package/dist/tray-WKFGUUTO.js +346 -0
  48. package/dist/tray-WKFGUUTO.js.map +1 -0
  49. package/package.json +62 -63
  50. package/scripts/postinstall.mjs +415 -364
  51. package/scripts/tray-helper-darwin +0 -0
  52. package/scripts/tray-helper-darwin.swift +180 -180
  53. package/scripts/tray-helper-linux.py +322 -0
  54. package/scripts/tray-helper-windows.cs +322 -0
  55. package/dist/api-client-KUQW7FSC.js +0 -12
  56. package/dist/chunk-2CVEPT6U.js.map +0 -1
  57. package/dist/chunk-CPL3P2OF.js.map +0 -1
  58. package/dist/chunk-ODOZM4QV.js.map +0 -1
  59. package/dist/chunk-WJKZWKER.js.map +0 -1
  60. package/dist/claude-watcher-N6GN6WHJ.js.map +0 -1
  61. package/dist/database-F3BFFZKG.js +0 -9
  62. package/dist/skill-usage-detector-EO26MRYV.js.map +0 -1
  63. package/dist/tray-UCAI2U2C.js +0 -408
  64. package/dist/tray-UCAI2U2C.js.map +0 -1
  65. /package/dist/{api-client-KUQW7FSC.js.map → api-client-BF6GDR7Q.js.map} +0 -0
  66. /package/dist/{config-P5EM5L7N.js.map → config-ZOKAP2LJ.js.map} +0 -0
  67. /package/dist/{database-F3BFFZKG.js.map → daemon-6DTCMOJB.js.map} +0 -0
  68. /package/dist/{paths-INOKEM66.js.map → database-KQY5OSCS.js.map} +0 -0
@@ -1,364 +1,415 @@
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, chmodSync, readdirSync } 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
- // ── Fix systray2 binary permissions (npm strips +x) ─────────────────────────
323
-
324
- function fixSystrayBinaries() {
325
- if (isWin) return;
326
- try {
327
- // Fix binaries relative to this postinstall script (../node_modules/systray2/traybin/)
328
- const trayBinDir = join(new URL(".", import.meta.url).pathname, "..", "node_modules", "systray2", "traybin");
329
- if (!existsSync(trayBinDir)) return;
330
- for (const f of readdirSync(trayBinDir)) {
331
- if (f.startsWith("tray_")) {
332
- chmodSync(join(trayBinDir, f), 0o755);
333
- }
334
- }
335
- } catch {
336
- // best-effort
337
- }
338
- }
339
-
340
- // ── Main ─────────────────────────────────────────────────────────────────────
341
-
342
- try {
343
- fixSystrayBinaries();
344
- const shell = detectShell();
345
- const installed = install(shell);
346
-
347
- if (installed) {
348
- const dim = "\x1b[90m";
349
- const green = "\x1b[32m";
350
- const bold = "\x1b[1m";
351
- const reset = "\x1b[0m";
352
- console.log("");
353
- console.log(`${green}${bold}Skillo installed!${reset} Shell integration set up for ${shell}.`);
354
- console.log("");
355
- console.log(`${bold} Get started:${reset}`);
356
- console.log(`${green} skillo start${reset}`);
357
- console.log("");
358
- console.log(`${dim} That's it — opens browser to log in, starts the daemon,${reset}`);
359
- console.log(`${dim} installs auto-start, and adds a tray icon.${reset}`);
360
- console.log("");
361
- }
362
- } catch {
363
- // Never break npm install — silently skip on any error
364
- }
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, chmodSync } 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) {
28
+ // Git Bash / MSYS / MinGW detection
29
+ if (
30
+ process.env.MSYSTEM ||
31
+ (process.env.SHELL && process.env.SHELL.includes("bash")) ||
32
+ process.env.TERM === "mintty"
33
+ ) {
34
+ return "bash";
35
+ }
36
+ return "powershell";
37
+ }
38
+ const sh = process.env.SHELL || "";
39
+ if (sh.includes("zsh")) return "zsh";
40
+ if (sh.includes("fish")) return "fish";
41
+ return "bash";
42
+ }
43
+
44
+ // ── Shell scripts ────────────────────────────────────────────────────────────
45
+ //
46
+ // Timing: uses `date +%s` (seconds) everywhere for portability.
47
+ // macOS BSD date does NOT support %N or %3N — those are GNU extensions.
48
+ // Duration is reported in ms (seconds * 1000). Good enough for our purposes.
49
+
50
+ const bashScript = `# Skillo CLI Integration
51
+ # Records commands and auto-detects tracked projects
52
+
53
+ # Ensure daemon is running (< 1ms PID file check)
54
+ _skillo_ensure_daemon() {
55
+ local pidfile="$HOME/.skillo/daemon.pid"
56
+ if [ -f "$pidfile" ]; then
57
+ local pid
58
+ pid=$(cat "$pidfile" 2>/dev/null)
59
+ if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
60
+ return 0
61
+ fi
62
+ fi
63
+ (skillo start &>/dev/null &)
64
+ }
65
+ _skillo_ensure_daemon
66
+
67
+ _skillo_session="\${SKILLO_SESSION:-default}"
68
+ _skillo_last_cwd=
69
+
70
+ _skillo_preexec() {
71
+ _skillo_cmd="$1"
72
+ _skillo_start_time=$(date +%s)
73
+ }
74
+
75
+ _skillo_check_project() {
76
+ if [ "$PWD" = "$_skillo_last_cwd" ]; then
77
+ return
78
+ fi
79
+ _skillo_last_cwd="$PWD"
80
+ local result
81
+ result=$(skillo session-auto "$PWD" --pid $$ --shell bash 2>/dev/null)
82
+ if [ -n "$result" ]; then
83
+ export SKILLO_SESSION="$result"
84
+ _skillo_session="$result"
85
+ fi
86
+ }
87
+
88
+ _skillo_precmd() {
89
+ local exit_code=$?
90
+ _skillo_check_project
91
+ if [ -n "$_skillo_cmd" ]; then
92
+ local duration=0
93
+ if [ -n "$_skillo_start_time" ]; then
94
+ local end_time=$(date +%s)
95
+ duration=$(( (end_time - _skillo_start_time) * 1000 ))
96
+ fi
97
+ (skillo record "$_skillo_cmd" --cwd "$PWD" --exit-code "$exit_code" --duration "$duration" --session "$_skillo_session" &>/dev/null &)
98
+ _skillo_cmd=""
99
+ fi
100
+ }
101
+
102
+ _skillo_cleanup() {
103
+ if [ -n "$SKILLO_SESSION" ] && [ "$SKILLO_SESSION" != "default" ]; then
104
+ skillo session end --session "$SKILLO_SESSION" &>/dev/null
105
+ fi
106
+ }
107
+ trap _skillo_cleanup EXIT
108
+
109
+ trap '_skillo_preexec "$BASH_COMMAND"' DEBUG
110
+
111
+ if [[ ! "$PROMPT_COMMAND" =~ _skillo_precmd ]]; then
112
+ PROMPT_COMMAND="_skillo_precmd\${PROMPT_COMMAND:+;$PROMPT_COMMAND}"
113
+ fi
114
+
115
+ _skillo_check_project
116
+
117
+ # Show sync status on startup (respects SKILLO_CONFIG_DIR and XDG_CONFIG_HOME)
118
+ if [ -n "$SKILLO_CONFIG_DIR" ]; then
119
+ _skillo_config="$SKILLO_CONFIG_DIR/config.yaml"
120
+ elif [ -n "$XDG_CONFIG_HOME" ]; then
121
+ _skillo_config="$XDG_CONFIG_HOME/skillo/config.yaml"
122
+ else
123
+ _skillo_config="$HOME/.config/skillo/config.yaml"
124
+ fi
125
+ if [ -f "$_skillo_config" ] && grep -q "api_key:" "$_skillo_config" 2>/dev/null; then
126
+ echo -e "\\033[90mSkillo: Command tracking enabled (syncing to platform)\\033[0m"
127
+ else
128
+ echo -e "\\033[33mSkillo: Command tracking enabled (local only — run 'skillo login' to sync)\\033[0m"
129
+ fi
130
+ `;
131
+
132
+ const zshScript = `# Skillo CLI Integration
133
+ # Records commands and auto-detects tracked projects
134
+
135
+ # Ensure daemon is running (< 1ms PID file check)
136
+ _skillo_ensure_daemon() {
137
+ local pidfile="$HOME/.skillo/daemon.pid"
138
+ if [[ -f "$pidfile" ]]; then
139
+ local pid
140
+ pid=$(cat "$pidfile" 2>/dev/null)
141
+ if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
142
+ return 0
143
+ fi
144
+ fi
145
+ (skillo start &>/dev/null &)
146
+ }
147
+ _skillo_ensure_daemon
148
+
149
+ _skillo_session="\${SKILLO_SESSION:-default}"
150
+ _skillo_last_cwd=
151
+
152
+ _skillo_preexec() {
153
+ _skillo_cmd="$1"
154
+ _skillo_start_time=$(date +%s)
155
+ }
156
+
157
+ _skillo_check_project() {
158
+ if [[ "$PWD" == "$_skillo_last_cwd" ]]; then
159
+ return
160
+ fi
161
+ _skillo_last_cwd="$PWD"
162
+ local result
163
+ result=$(skillo session-auto "$PWD" --pid $$ --shell zsh 2>/dev/null)
164
+ if [[ -n "$result" ]]; then
165
+ export SKILLO_SESSION="$result"
166
+ _skillo_session="$result"
167
+ fi
168
+ }
169
+
170
+ _skillo_precmd() {
171
+ local exit_code=$?
172
+ _skillo_check_project
173
+ if [[ -n "$_skillo_cmd" ]]; then
174
+ local duration=0
175
+ if [[ -n "$_skillo_start_time" ]]; then
176
+ local end_time=$(date +%s)
177
+ duration=$(( (end_time - _skillo_start_time) * 1000 ))
178
+ fi
179
+ (skillo record "$_skillo_cmd" --cwd "$PWD" --exit-code "$exit_code" --duration "$duration" --session "$_skillo_session" &>/dev/null &)
180
+ _skillo_cmd=""
181
+ fi
182
+ }
183
+
184
+ _skillo_cleanup() {
185
+ if [[ -n "$SKILLO_SESSION" ]] && [[ "$SKILLO_SESSION" != "default" ]]; then
186
+ skillo session end --session "$SKILLO_SESSION" &>/dev/null
187
+ fi
188
+ }
189
+ trap _skillo_cleanup EXIT
190
+
191
+ autoload -Uz add-zsh-hook
192
+ add-zsh-hook preexec _skillo_preexec
193
+ add-zsh-hook precmd _skillo_precmd
194
+
195
+ _skillo_check_project
196
+
197
+ # Show sync status on startup (respects SKILLO_CONFIG_DIR and XDG_CONFIG_HOME)
198
+ if [ -n "$SKILLO_CONFIG_DIR" ]; then
199
+ _skillo_config="$SKILLO_CONFIG_DIR/config.yaml"
200
+ elif [ -n "$XDG_CONFIG_HOME" ]; then
201
+ _skillo_config="$XDG_CONFIG_HOME/skillo/config.yaml"
202
+ else
203
+ _skillo_config="$HOME/.config/skillo/config.yaml"
204
+ fi
205
+ if [ -f "$_skillo_config" ] && grep -q "api_key:" "$_skillo_config" 2>/dev/null; then
206
+ echo -e "\\033[90mSkillo: Command tracking enabled (syncing to platform)\\033[0m"
207
+ else
208
+ echo -e "\\033[33mSkillo: Command tracking enabled (local only — run 'skillo login' to sync)\\033[0m"
209
+ fi
210
+ `;
211
+
212
+ const fishScript = `# Skillo CLI Integration
213
+ # Records commands and auto-detects tracked projects
214
+
215
+ # Ensure daemon is running (< 1ms PID file check)
216
+ function _skillo_ensure_daemon
217
+ set -l pidfile "$HOME/.skillo/daemon.pid"
218
+ if test -f "$pidfile"
219
+ set -l pid (cat "$pidfile" 2>/dev/null)
220
+ if test -n "$pid"; and kill -0 "$pid" 2>/dev/null
221
+ return 0
222
+ end
223
+ end
224
+ skillo start &>/dev/null &
225
+ end
226
+ _skillo_ensure_daemon
227
+
228
+ set -q SKILLO_SESSION; or set -g SKILLO_SESSION "default"
229
+
230
+ function _skillo_postexec --on-event fish_postexec
231
+ set -l cmd $argv[1]
232
+ set -l exit_code $status
233
+ skillo record "$cmd" --cwd (pwd) --exit-code $exit_code --session $SKILLO_SESSION &>/dev/null &
234
+ end
235
+
236
+ # Show sync status on startup (respects SKILLO_CONFIG_DIR and XDG_CONFIG_HOME)
237
+ if set -q SKILLO_CONFIG_DIR
238
+ set _skillo_config "$SKILLO_CONFIG_DIR/config.yaml"
239
+ else if set -q XDG_CONFIG_HOME
240
+ set _skillo_config "$XDG_CONFIG_HOME/skillo/config.yaml"
241
+ else
242
+ set _skillo_config "$HOME/.config/skillo/config.yaml"
243
+ end
244
+ if test -f "$_skillo_config"; and grep -q "api_key:" "$_skillo_config" 2>/dev/null
245
+ echo -e "\\033[90mSkillo: Command tracking enabled (syncing to platform)\\033[0m"
246
+ else
247
+ echo -e "\\033[33mSkillo: Command tracking enabled (local only run 'skillo login' to sync)\\033[0m"
248
+ end
249
+ `;
250
+
251
+ const psScript = `# Skillo CLI Integration
252
+ # Records commands and auto-detects tracked projects
253
+
254
+ # Ensure daemon is running (< 1ms PID file check)
255
+ function _SkilloEnsureDaemon {
256
+ $pidfile = Join-Path $env:USERPROFILE ".skillo" "daemon.pid"
257
+ if (Test-Path $pidfile) {
258
+ $pid = Get-Content $pidfile -ErrorAction SilentlyContinue
259
+ if ($pid) {
260
+ try {
261
+ Get-Process -Id ([int]$pid) -ErrorAction Stop | Out-Null
262
+ return
263
+ } catch { }
264
+ }
265
+ }
266
+ Start-Job -ScriptBlock { & skillo start 2>$null } | Out-Null
267
+ }
268
+ _SkilloEnsureDaemon
269
+
270
+ $Global:SkilloSessionId = if ($env:SKILLO_SESSION) { $env:SKILLO_SESSION } else { 'default' }
271
+ $Global:SkilloLastHistoryId = 0
272
+ $Global:SkilloLastCwd = $null
273
+
274
+ function _SkilloCheckProject {
275
+ $cwd = (Get-Location).Path
276
+ if ($cwd -eq $Global:SkilloLastCwd) { return }
277
+ $Global:SkilloLastCwd = $cwd
278
+
279
+ try {
280
+ $result = & skillo session-auto $cwd --pid $PID --shell powershell 2>$null
281
+ if ($result -and $result.Trim()) {
282
+ $env:SKILLO_SESSION = $result.Trim()
283
+ $Global:SkilloSessionId = $result.Trim()
284
+ }
285
+ } catch { }
286
+ }
287
+
288
+ $Global:SkilloOriginalPrompt = $function:prompt
289
+ function Global:prompt {
290
+ $lastCmd = Get-History -Count 1 -ErrorAction SilentlyContinue
291
+
292
+ if ($lastCmd -and $lastCmd.Id -gt $Global:SkilloLastHistoryId) {
293
+ $Global:SkilloLastHistoryId = $lastCmd.Id
294
+
295
+ $duration = 0
296
+ if ($lastCmd.EndExecutionTime -and $lastCmd.StartExecutionTime) {
297
+ $duration = [int]($lastCmd.EndExecutionTime - $lastCmd.StartExecutionTime).TotalMilliseconds
298
+ }
299
+ $exit = if ($null -eq $LASTEXITCODE) { 0 } else { $LASTEXITCODE }
300
+ $cwd = (Get-Location).Path
301
+
302
+ Start-Job -ScriptBlock {
303
+ param($cmd, $cwd, $exit, $dur, $sessionId)
304
+ & skillo record $cmd --cwd $cwd --exit-code $exit --duration $dur --session $sessionId 2>$null
305
+ } -ArgumentList $lastCmd.CommandLine, $cwd, $exit, $duration, $Global:SkilloSessionId | Out-Null
306
+ }
307
+
308
+ _SkilloCheckProject
309
+ & $Global:SkilloOriginalPrompt
310
+ }
311
+
312
+ $null = Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action {
313
+ if ($Global:SkilloSessionId -and $Global:SkilloSessionId -ne 'default') {
314
+ try { & skillo session end --session $Global:SkilloSessionId 2>$null } catch { }
315
+ }
316
+ }
317
+
318
+ # Show sync status on startup (respects SKILLO_CONFIG_DIR and XDG_CONFIG_HOME)
319
+ if ($env:SKILLO_CONFIG_DIR) {
320
+ $skilloConfigPath = Join-Path $env:SKILLO_CONFIG_DIR "config.yaml"
321
+ } elseif ($env:XDG_CONFIG_HOME) {
322
+ $skilloConfigPath = Join-Path $env:XDG_CONFIG_HOME "skillo" "config.yaml"
323
+ } else {
324
+ $skilloConfigPath = Join-Path $HOME ".config" "skillo" "config.yaml"
325
+ }
326
+ $skilloLoggedIn = $false
327
+ if (Test-Path $skilloConfigPath) {
328
+ $skilloLoggedIn = (Select-String -Path $skilloConfigPath -Pattern "api_key:" -Quiet) -eq $true
329
+ }
330
+ if ($skilloLoggedIn) {
331
+ Write-Host "Skillo: Command tracking enabled (syncing to platform)" -ForegroundColor DarkGray
332
+ } else {
333
+ Write-Host "Skillo: Command tracking enabled (local only - run 'skillo login' to sync)" -ForegroundColor Yellow
334
+ }
335
+ `;
336
+
337
+ // ── Install logic ────────────────────────────────────────────────────────────
338
+
339
+ function install(shell) {
340
+ ensureDir(scriptPath);
341
+
342
+ let scriptFile, rcPath, sourceLine;
343
+
344
+ if (shell === "zsh") {
345
+ scriptFile = join(scriptPath, "skillo.zsh");
346
+ writeFileSync(scriptFile, zshScript, "utf-8");
347
+ rcPath = join(home, ".zshrc");
348
+ sourceLine = `\n# Skillo CLI Integration\n[ -f "${scriptFile}" ] && source "${scriptFile}"\n`;
349
+ } else if (shell === "bash") {
350
+ scriptFile = join(scriptPath, "skillo.bash");
351
+ writeFileSync(scriptFile, bashScript, "utf-8");
352
+ rcPath = join(home, ".bashrc");
353
+ sourceLine = `\n# Skillo CLI Integration\n[ -f "${scriptFile}" ] && source "${scriptFile}"\n`;
354
+ } else if (shell === "fish") {
355
+ scriptFile = join(scriptPath, "skillo.fish");
356
+ writeFileSync(scriptFile, fishScript, "utf-8");
357
+ const fishConfigDir = join(home, ".config", "fish");
358
+ ensureDir(fishConfigDir);
359
+ rcPath = join(fishConfigDir, "config.fish");
360
+ sourceLine = `\n# Skillo CLI Integration\nif test -f "${scriptFile}"\n source "${scriptFile}"\nend\n`;
361
+ } else if (shell === "powershell") {
362
+ scriptFile = join(scriptPath, "skillo.ps1");
363
+ writeFileSync(scriptFile, psScript, "utf-8");
364
+ // Try PowerShell Core profile first, then Windows PowerShell
365
+ const psCorePath = isWin
366
+ ? join(home, "Documents", "PowerShell", "Microsoft.PowerShell_profile.ps1")
367
+ : join(home, ".config", "powershell", "Microsoft.PowerShell_profile.ps1");
368
+ const psClassicPath = join(home, "Documents", "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1");
369
+ // Use whichever profile already exists, or default to Core
370
+ rcPath = existsSync(psClassicPath) ? psClassicPath : psCorePath;
371
+ ensureDir(join(rcPath, ".."));
372
+ sourceLine = `\n# Skillo CLI Integration\n. "${scriptFile}"\n`;
373
+ } else {
374
+ return false;
375
+ }
376
+
377
+ // Add source line to rc file (idempotent)
378
+ if (existsSync(rcPath)) {
379
+ const content = readFileSync(rcPath, "utf-8");
380
+ if (content.includes("Skillo CLI Integration")) {
381
+ // Already installed — just update the script file (already written above)
382
+ return true;
383
+ }
384
+ appendFileSync(rcPath, sourceLine);
385
+ } else {
386
+ writeFileSync(rcPath, sourceLine, "utf-8");
387
+ }
388
+
389
+ return true;
390
+ }
391
+
392
+ // ── Main ─────────────────────────────────────────────────────────────────────
393
+
394
+ try {
395
+ const shell = detectShell();
396
+ const installed = install(shell);
397
+
398
+ if (installed) {
399
+ const dim = "\x1b[90m";
400
+ const green = "\x1b[32m";
401
+ const bold = "\x1b[1m";
402
+ const reset = "\x1b[0m";
403
+ console.log("");
404
+ console.log(`${green}${bold}Skillo installed!${reset} Shell integration set up for ${shell}.`);
405
+ console.log("");
406
+ console.log(`${bold} Get started:${reset}`);
407
+ console.log(`${green} skillo start${reset}`);
408
+ console.log("");
409
+ console.log(`${dim} That's it — opens browser to log in, starts the daemon,${reset}`);
410
+ console.log(`${dim} installs auto-start, and adds a tray icon.${reset}`);
411
+ console.log("");
412
+ }
413
+ } catch {
414
+ // Never break npm install — silently skip on any error
415
+ }