skillo 0.2.6 → 0.2.8
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 +198 -198
- package/dist/api-client-BF6GDR7Q.js +12 -0
- package/dist/{chunk-WJKZWKER.js → chunk-63FVALWX.js} +1 -1
- package/dist/chunk-63FVALWX.js.map +1 -0
- package/dist/chunk-6GOJPFZ7.js +113 -0
- package/dist/chunk-6GOJPFZ7.js.map +1 -0
- package/dist/chunk-6UGTWBUW.js +89 -0
- package/dist/chunk-6UGTWBUW.js.map +1 -0
- package/dist/{chunk-2CVEPT6U.js → chunk-73NUWYUO.js} +2 -2
- package/dist/chunk-73NUWYUO.js.map +1 -0
- package/dist/chunk-QIV4VIXA.js +221 -0
- package/dist/chunk-QIV4VIXA.js.map +1 -0
- package/dist/{chunk-CPL3P2OF.js → chunk-QUXHHRRK.js} +2 -2
- package/dist/chunk-QUXHHRRK.js.map +1 -0
- package/dist/{chunk-ODOZM4QV.js → chunk-RQ2SC5HW.js} +72 -10
- package/dist/chunk-RQ2SC5HW.js.map +1 -0
- package/dist/chunk-U53QWUOR.js +727 -0
- package/dist/chunk-U53QWUOR.js.map +1 -0
- package/dist/chunk-VAQ73XPE.js +68 -0
- package/dist/chunk-VAQ73XPE.js.map +1 -0
- package/dist/chunk-XLJGCOVT.js +975 -0
- package/dist/chunk-XLJGCOVT.js.map +1 -0
- package/dist/{claude-watcher-N6GN6WHJ.js → claude-watcher-WKGBJYKN.js} +65 -3
- package/dist/claude-watcher-WKGBJYKN.js.map +1 -0
- package/dist/cli.js +495 -1846
- package/dist/cli.js.map +1 -1
- package/dist/{config-P5EM5L7N.js → config-ZOKAP2LJ.js} +3 -3
- package/dist/daemon-6DTCMOJB.js +28 -0
- package/dist/daemon-runner.js +338 -71
- package/dist/daemon-runner.js.map +1 -1
- package/dist/database-KQY5OSCS.js +9 -0
- package/dist/git-OGUSYBJS.js +16 -0
- package/dist/git-OGUSYBJS.js.map +1 -0
- package/dist/git-OUAHIOY2.js +110 -0
- package/dist/git-OUAHIOY2.js.map +1 -0
- package/dist/index.js.map +1 -1
- package/dist/{paths-INOKEM66.js → paths-MPOZBOKE.js} +2 -2
- package/dist/paths-MPOZBOKE.js.map +1 -0
- package/dist/project-OFU2W6MH.js +19 -0
- package/dist/project-OFU2W6MH.js.map +1 -0
- package/dist/shell-NZABRJLA.js +16 -0
- package/dist/shell-NZABRJLA.js.map +1 -0
- package/dist/skill-installer-F67OAOQN.js +121 -0
- package/dist/skill-installer-F67OAOQN.js.map +1 -0
- package/dist/{skill-usage-detector-EO26MRYV.js → skill-usage-detector-MSW5VWQZ.js} +2 -2
- package/dist/skill-usage-detector-MSW5VWQZ.js.map +1 -0
- package/dist/tray-WKFGUUTO.js +346 -0
- package/dist/tray-WKFGUUTO.js.map +1 -0
- package/package.json +62 -63
- package/scripts/postinstall.mjs +415 -364
- package/scripts/tray-helper-darwin +0 -0
- package/scripts/tray-helper-darwin.swift +180 -180
- package/scripts/tray-helper-linux.py +322 -0
- package/scripts/tray-helper-windows.cs +322 -0
- package/dist/api-client-KUQW7FSC.js +0 -12
- package/dist/chunk-2CVEPT6U.js.map +0 -1
- package/dist/chunk-CPL3P2OF.js.map +0 -1
- package/dist/chunk-ODOZM4QV.js.map +0 -1
- package/dist/chunk-WJKZWKER.js.map +0 -1
- package/dist/claude-watcher-N6GN6WHJ.js.map +0 -1
- package/dist/database-F3BFFZKG.js +0 -9
- package/dist/skill-usage-detector-EO26MRYV.js.map +0 -1
- package/dist/tray-UCAI2U2C.js +0 -408
- package/dist/tray-UCAI2U2C.js.map +0 -1
- /package/dist/{api-client-KUQW7FSC.js.map → api-client-BF6GDR7Q.js.map} +0 -0
- /package/dist/{config-P5EM5L7N.js.map → config-ZOKAP2LJ.js.map} +0 -0
- /package/dist/{database-F3BFFZKG.js.map → daemon-6DTCMOJB.js.map} +0 -0
- /package/dist/{paths-INOKEM66.js.map → database-KQY5OSCS.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/daemon.ts","../src/utils/os-service.ts","../src/utils/daemon-tasks.ts"],"sourcesContent":["/**\r\n * Daemon management commands.\r\n */\r\n\r\nimport { existsSync, readFileSync, writeFileSync, unlinkSync } from \"fs\";\r\nimport { fork, spawn } from \"child_process\";\r\nimport { join } from \"path\";\r\nimport { Command } from \"commander\";\r\nimport { loadConfig } from \"../core/config.js\";\r\nimport { getPidFile, getLogFile, getConfigFile, getDataDir, getActiveSessionsDir, ensureDirectory } from \"../utils/paths.js\";\r\nimport logger from \"../utils/logger.js\";\r\nimport { installService, uninstallService, getServiceStatus } from \"../utils/os-service.js\";\r\nimport { StatusWriter } from \"../utils/status-writer.js\";\r\nimport { updateTrackedProjectsCache, cleanupStaleSessions } from \"../utils/daemon-tasks.js\";\r\n\r\nexport function isDaemonRunning(): { running: boolean; pid: number | null } {\r\n const pidFile = getPidFile();\r\n\r\n if (!existsSync(pidFile)) {\r\n return { running: false, pid: null };\r\n }\r\n\r\n try {\r\n const pidStr = readFileSync(pidFile, \"utf-8\").trim();\r\n const pid = parseInt(pidStr, 10);\r\n\r\n if (isNaN(pid)) {\r\n return { running: false, pid: null };\r\n }\r\n\r\n // Check if process is running\r\n try {\r\n process.kill(pid, 0);\r\n return { running: true, pid };\r\n } catch {\r\n // Process doesn't exist, clean up stale PID file\r\n try {\r\n unlinkSync(pidFile);\r\n } catch {\r\n // Ignore cleanup errors\r\n }\r\n return { running: false, pid: null };\r\n }\r\n } catch {\r\n return { running: false, pid: null };\r\n }\r\n}\r\n\r\nexport function startDaemonProcess(): number | null {\r\n const daemonScript = new URL(\"./daemon-runner.js\", import.meta.url).pathname;\r\n\r\n if (process.platform === \"win32\") {\r\n // On Windows, fork() doesn't produce a usable PID — use spawn instead\r\n const scriptPath = daemonScript.replace(/^\\/([A-Za-z]:)/, \"$1\");\r\n const child = spawn(process.execPath, [scriptPath], {\r\n detached: true,\r\n stdio: \"ignore\",\r\n env: { ...process.env, SKILLO_DAEMON: \"1\" },\r\n });\r\n child.unref();\r\n if (child.pid) {\r\n writeFileSync(getPidFile(), String(child.pid));\r\n return child.pid;\r\n }\r\n return null;\r\n }\r\n\r\n // Unix: fork works correctly\r\n const child = fork(daemonScript, [], {\r\n detached: true,\r\n stdio: \"ignore\",\r\n env: {\r\n ...process.env,\r\n SKILLO_DAEMON: \"1\",\r\n },\r\n });\r\n\r\n child.unref();\r\n\r\n if (child.pid) {\r\n writeFileSync(getPidFile(), String(child.pid));\r\n return child.pid;\r\n }\r\n\r\n return null;\r\n}\r\n\r\nexport function isTrayRunning(): boolean {\r\n const trayPidFile = join(getDataDir(), \"tray.pid\");\r\n if (!existsSync(trayPidFile)) return false;\r\n try {\r\n const pid = parseInt(readFileSync(trayPidFile, \"utf-8\").trim(), 10);\r\n if (isNaN(pid)) return false;\r\n process.kill(pid, 0);\r\n return true;\r\n } catch {\r\n // Stale PID, clean up\r\n try { unlinkSync(trayPidFile); } catch { /* ignore */ }\r\n return false;\r\n }\r\n}\r\n\r\nexport function startTrayProcess(): void {\r\n if (isTrayRunning()) return;\r\n\r\n // Use process.argv[1] (the actual CLI entry point that's running right now)\r\n // This works for both local dev (dist/cli.js) and global install (skillo bin)\r\n const cliEntry = process.argv[1];\r\n\r\n const child = spawn(process.execPath, [cliEntry, \"tray\"], {\r\n detached: true,\r\n stdio: \"ignore\",\r\n env: { ...process.env },\r\n });\r\n child.unref();\r\n}\r\n\r\n// ── Auto-init helper ──────────────────────────────────────────────────────\r\n\r\nasync function ensureInitialized(): Promise<void> {\r\n if (existsSync(getConfigFile())) return;\r\n\r\n // Auto-initialize silently\r\n const { getDefaultConfig, saveConfig } = await import(\"../core/config.js\");\r\n const { SkilloDatabase } = await import(\"../core/database.js\");\r\n const { getConfigDir, getSkillsDir } = await import(\"../utils/paths.js\");\r\n\r\n ensureDirectory(getDataDir());\r\n ensureDirectory(getConfigDir());\r\n ensureDirectory(getSkillsDir());\r\n\r\n saveConfig(getDefaultConfig());\r\n\r\n const db = new SkilloDatabase();\r\n await db.initialize();\r\n db.close();\r\n\r\n logger.dim(\"Skillo initialized.\");\r\n}\r\n\r\n// ── Inline login helper ──────────────────────────────────────────────────\r\n\r\nexport async function ensureLoggedIn(): Promise<boolean> {\r\n const { getApiClient } = await import(\"../core/api-client.js\");\r\n const client = getApiClient();\r\n\r\n if (client.hasApiKey()) {\r\n // Verify the key still works\r\n const result = await client.authenticate();\r\n if (result.success) return true;\r\n\r\n const err = ((result as { error?: string }).error || \"\").toLowerCase();\r\n const isAuthFailure =\r\n err.includes(\"unauthorized\") ||\r\n err.includes(\"invalid\") ||\r\n err.includes(\"expired\") ||\r\n err.includes(\"revoked\");\r\n\r\n if (isAuthFailure) {\r\n // Key is stale — clear it and re-login\r\n client.clearApiKey();\r\n logger.warn(\"Session expired. Logging in again...\");\r\n } else {\r\n // Network/server error — keep the key, continue offline\r\n logger.warn(\"Could not verify login (server unreachable). Continuing...\");\r\n return true;\r\n }\r\n }\r\n\r\n // Start device auth flow inline\r\n logger.blank();\r\n logger.info(\"Login required. Opening browser...\");\r\n logger.blank();\r\n\r\n const { hostname } = await import(\"os\");\r\n const deviceName = `CLI on ${hostname()}`;\r\n const deviceAuthResult = await client.startDeviceAuth(deviceName);\r\n\r\n if (!deviceAuthResult.success || !deviceAuthResult.data) {\r\n logger.error(\"Failed to start authentication: \" + (deviceAuthResult.error || \"Unknown error\"));\r\n logger.dim(\"You can also login manually: skillo login <api-key>\");\r\n return false;\r\n }\r\n\r\n const { code, verification_url, interval } = deviceAuthResult.data;\r\n\r\n logger.highlight(` ${verification_url}`);\r\n logger.blank();\r\n\r\n // Open browser automatically\r\n try {\r\n const { exec: execCb } = await import(\"child_process\");\r\n const { promisify } = await import(\"util\");\r\n const execAsync = promisify(execCb);\r\n const openCmd = process.platform === \"darwin\" ? \"open\" : process.platform === \"win32\" ? \"start \\\"\\\"\" : \"xdg-open\";\r\n await execAsync(`${openCmd} \"${verification_url}\"`);\r\n logger.dim(\"Browser opened. Waiting for authorization...\");\r\n } catch {\r\n logger.dim(\"Open the URL above in your browser to authorize.\");\r\n }\r\n\r\n logger.dim(\"(Press Ctrl+C to cancel)\");\r\n\r\n // Poll for authorization\r\n const maxAttempts = 120;\r\n for (let i = 0; i < maxAttempts; i++) {\r\n await new Promise((r) => setTimeout(r, interval * 1000));\r\n\r\n const statusResult = await client.checkTokenStatus(code);\r\n if (!statusResult.success) continue;\r\n\r\n const status = statusResult.data?.status;\r\n\r\n if (status === \"ready\") {\r\n const tokenResult = await client.exchangeToken(code, deviceName);\r\n if (tokenResult.success && tokenResult.data) {\r\n client.saveApiKey(tokenResult.data.api_key);\r\n\r\n const authResult = await client.authenticate();\r\n logger.blank();\r\n logger.success(\"Login successful!\");\r\n if (authResult.success && authResult.data) {\r\n logger.info(`Welcome, ${authResult.data.user.name}!`);\r\n }\r\n logger.blank();\r\n return true;\r\n }\r\n logger.error(\"Failed to complete login: \" + (tokenResult.error || \"Unknown error\"));\r\n return false;\r\n }\r\n\r\n if (status === \"expired\" || status === \"used\" || status === \"not_found\") {\r\n logger.error(`Authorization ${status}. Please try again.`);\r\n return false;\r\n }\r\n }\r\n\r\n logger.error(\"Authorization timed out.\");\r\n return false;\r\n}\r\n\r\n// skillo start\r\nexport const startCommand = new Command(\"start\")\r\n .description(\"Start the Skillo daemon\")\r\n .option(\"-f, --foreground\", \"Run in foreground (don't daemonize)\")\r\n .action(async (options: { foreground?: boolean }) => {\r\n // Step 1: Auto-initialize if needed\r\n await ensureInitialized();\r\n\r\n // Step 2: Ensure logged in (opens browser if needed)\r\n if (!options.foreground) {\r\n const loggedIn = await ensureLoggedIn();\r\n if (!loggedIn) {\r\n process.exit(1);\r\n }\r\n }\r\n\r\n // Step 3: Check if already running\r\n const { running, pid } = isDaemonRunning();\r\n if (running) {\r\n logger.success(`Daemon is already running (PID: ${pid})`);\r\n\r\n // Still ensure services are installed\r\n const serviceStatus = getServiceStatus();\r\n if (!serviceStatus.daemon.installed) {\r\n const serviceResult = await installService({ includeTray: true });\r\n if (serviceResult.success) {\r\n logger.dim(\"Auto-start service installed.\");\r\n }\r\n }\r\n\r\n // Ensure tray is running\r\n if (!isTrayRunning()) {\r\n startTrayProcess();\r\n }\r\n process.exit(0);\r\n }\r\n\r\n // Step 4: Start daemon\r\n if (options.foreground) {\r\n logger.info(\"Starting Skillo daemon in foreground...\");\r\n logger.dim(\"Press Ctrl+C to stop\");\r\n logger.blank();\r\n await runDaemonForeground();\r\n } else {\r\n logger.info(\"Starting Skillo daemon...\");\r\n\r\n const daemonPid = startDaemonProcess();\r\n if (daemonPid) {\r\n logger.success(`Daemon started (PID: ${daemonPid})`);\r\n\r\n // Step 5: Install OS services (daemon + tray)\r\n const serviceResult = await installService({ includeTray: true });\r\n if (serviceResult.success) {\r\n logger.dim(\"Auto-start & tray service installed. Daemon will survive reboots.\");\r\n }\r\n\r\n // Step 6: Start tray icon\r\n startTrayProcess();\r\n\r\n logger.blank();\r\n logger.dim(\"Use 'skillo status' to check daemon status\");\r\n logger.dim(\"Use 'skillo stop' to stop the daemon\");\r\n } else {\r\n logger.error(\"Failed to start daemon\");\r\n process.exit(1);\r\n }\r\n\r\n // Explicitly exit — detached child processes are unref'd\r\n // but dynamic imports can keep the event loop alive\r\n process.exit(0);\r\n }\r\n });\r\n\r\n// skillo stop\r\nexport const stopCommand = new Command(\"stop\")\r\n .description(\"Stop the Skillo daemon\")\r\n .action(async () => {\r\n const { running, pid } = isDaemonRunning();\r\n\r\n if (!running) {\r\n logger.dim(\"Daemon is not running.\");\r\n return;\r\n }\r\n\r\n logger.info(`Stopping daemon (PID: ${pid})...`);\r\n\r\n // Stop tray icon if running\r\n const trayPidFile = join(getDataDir(), \"tray.pid\");\r\n if (existsSync(trayPidFile)) {\r\n try {\r\n const trayPid = parseInt(readFileSync(trayPidFile, \"utf-8\").trim(), 10);\r\n process.kill(trayPid, \"SIGTERM\");\r\n logger.dim(\"Tray icon stopped.\");\r\n } catch { /* ignore */ }\r\n try { unlinkSync(trayPidFile); } catch { /* ignore */ }\r\n }\r\n\r\n try {\r\n process.kill(pid!, \"SIGTERM\");\r\n logger.success(\"Daemon stopped.\");\r\n logger.dim(\"Auto-start services are still installed. Daemon will restart on login.\");\r\n logger.dim(\"To remove auto-start: skillo service uninstall\");\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code === \"ESRCH\") {\r\n logger.dim(\"Daemon already stopped\");\r\n } else if ((error as NodeJS.ErrnoException).code === \"EPERM\") {\r\n logger.error(\"Permission denied. Try with sudo.\");\r\n process.exit(1);\r\n } else {\r\n throw error;\r\n }\r\n }\r\n\r\n // Clean up PID file\r\n const pidFile = getPidFile();\r\n if (existsSync(pidFile)) {\r\n try {\r\n unlinkSync(pidFile);\r\n } catch {\r\n // Ignore cleanup errors\r\n }\r\n }\r\n });\r\n\r\n// skillo logs\r\nexport const logsCommand = new Command(\"logs\")\r\n .description(\"Show daemon logs\")\r\n .option(\"-n, --lines <number>\", \"Number of lines to show\", \"50\")\r\n .option(\"-f, --follow\", \"Follow log output\")\r\n .action(async (options: { lines: string; follow?: boolean }) => {\r\n const logFile = getLogFile();\r\n\r\n if (!existsSync(logFile)) {\r\n logger.dim(\"No logs found.\");\r\n return;\r\n }\r\n\r\n const numLines = parseInt(options.lines, 10);\r\n\r\n if (options.follow) {\r\n // Tail -f functionality\r\n const { spawn } = await import(\"child_process\");\r\n const tail = spawn(\"tail\", [\"-f\", \"-n\", String(numLines), logFile], {\r\n stdio: \"inherit\",\r\n });\r\n\r\n tail.on(\"error\", () => {\r\n // Fallback for Windows\r\n logger.warn(\"Follow mode not supported on this platform.\");\r\n showLogs(logFile, numLines);\r\n });\r\n } else {\r\n showLogs(logFile, numLines);\r\n }\r\n });\r\n\r\nfunction showLogs(logFile: string, numLines: number): void {\r\n const content = readFileSync(logFile, \"utf-8\");\r\n const lines = content.split(\"\\n\").filter(Boolean);\r\n const lastLines = lines.slice(-numLines);\r\n lastLines.forEach((line) => console.log(line));\r\n}\r\n\r\n// skillo service\r\nexport const serviceCommand = new Command(\"service\")\r\n .description(\"Manage auto-start service\")\r\n .addCommand(\r\n new Command(\"install\")\r\n .description(\"Install auto-start service (LaunchAgent on macOS, systemd on Linux)\")\r\n .option(\"--no-tray\", \"Skip installing tray icon service\")\r\n .action(async (options: { tray?: boolean }) => {\r\n logger.info(\"Installing auto-start service...\");\r\n const result = await installService({ includeTray: options.tray !== false });\r\n if (result.success) {\r\n logger.success(\"Auto-start service installed.\");\r\n logger.dim(\"Daemon will start automatically on login and restart on crash.\");\r\n if (options.tray !== false) {\r\n logger.dim(\"Tray icon will appear in GUI sessions.\");\r\n }\r\n } else {\r\n logger.error(`Failed to install service: ${result.error}`);\r\n }\r\n })\r\n )\r\n .addCommand(\r\n new Command(\"uninstall\")\r\n .description(\"Remove auto-start service\")\r\n .action(async () => {\r\n logger.info(\"Removing auto-start service...\");\r\n const result = await uninstallService();\r\n if (result.success) {\r\n logger.success(\"Auto-start service removed.\");\r\n } else {\r\n logger.error(`Failed to remove service: ${result.error}`);\r\n }\r\n })\r\n )\r\n .addCommand(\r\n new Command(\"status\")\r\n .description(\"Show auto-start service status\")\r\n .action(async () => {\r\n const status = getServiceStatus();\r\n\r\n logger.blank();\r\n logger.info(`Platform: ${status.platform}`);\r\n logger.blank();\r\n\r\n if (status.daemon.installed) {\r\n logger.running(`Daemon service: installed${status.daemon.loaded ? \" & loaded\" : \" (not loaded)\"}`);\r\n } else {\r\n logger.stopped(\"Daemon service: not installed\");\r\n }\r\n\r\n if (status.tray.installed) {\r\n logger.running(`Tray service: installed${status.tray.loaded ? \" & loaded\" : \" (not loaded)\"}`);\r\n } else {\r\n logger.stopped(\"Tray service: not installed\");\r\n }\r\n\r\n logger.blank();\r\n if (!status.daemon.installed) {\r\n logger.dim(\"Run 'skillo service install' to enable auto-start.\");\r\n }\r\n logger.blank();\r\n })\r\n );\r\n\r\n// Foreground daemon runner (mirrors daemon-runner.ts logic for Windows)\r\nasync function runDaemonForeground(): Promise<void> {\r\n const pidFile = getPidFile();\r\n writeFileSync(pidFile, String(process.pid));\r\n\r\n logger.dim(`Daemon PID: ${process.pid}`);\r\n logger.dim(`Log file: ${getLogFile()}`);\r\n logger.blank();\r\n\r\n // Status writer for tray icon communication\r\n const statusWriter = new StatusWriter();\r\n\r\n // Handle shutdown signals\r\n const cleanup = () => {\r\n logger.blank();\r\n logger.info(\"Shutting down daemon...\");\r\n statusWriter.stop();\r\n if (existsSync(pidFile)) {\r\n try {\r\n unlinkSync(pidFile);\r\n } catch {\r\n // Ignore\r\n }\r\n }\r\n process.exit(0);\r\n };\r\n\r\n process.on(\"SIGINT\", cleanup);\r\n process.on(\"SIGTERM\", cleanup);\r\n\r\n // Load config and client\r\n const config = loadConfig();\r\n const { getApiClient } = await import(\"../core/api-client.js\");\r\n const client = getApiClient();\r\n\r\n if (!client.hasApiKey()) {\r\n logger.error(\"Not logged in. Run 'skillo login' first.\");\r\n process.exit(1);\r\n }\r\n\r\n // Fetch user info for tray display\r\n try {\r\n const authResult = await client.authenticate() as any;\r\n const user = authResult.data?.user || authResult.user;\r\n if (authResult.success && user) {\r\n statusWriter.update({ user: user.name });\r\n logger.dim(`Authenticated as: ${user.name}`);\r\n }\r\n } catch {\r\n // Non-fatal: tray will show without user info\r\n }\r\n\r\n ensureDirectory(getDataDir());\r\n ensureDirectory(getActiveSessionsDir());\r\n\r\n // Log adapter for shared daemon tasks\r\n const log = (level: string, msg: string) => {\r\n if (level === \"ERROR\") logger.error(msg);\r\n else if (level === \"WARN\") logger.warn(msg);\r\n else logger.dim(msg);\r\n };\r\n\r\n // Initial tracked projects cache update\r\n const projects = await updateTrackedProjectsCache(client, log);\r\n if (projects) statusWriter.update({ trackedProjects: projects });\r\n\r\n // Clean up stale sessions from previous crashes\r\n await cleanupStaleSessions(client, log);\r\n\r\n // Import and start Claude watcher\r\n const { ClaudeWatcher } = await import(\"../core/claude-watcher.js\");\r\n\r\n const watchInterval = ((config as { daemon?: { conversationCheckInterval?: number } }).daemon?.conversationCheckInterval || 5) * 1000;\r\n const watcher = new ClaudeWatcher(client, {\r\n intervalMs: watchInterval,\r\n callbacks: {\r\n onSync: (count) => {\r\n logger.success(`Synced ${count} Claude prompt(s)`);\r\n statusWriter.update({ claudeWatcher: { promptsSynced: count, lastSync: new Date().toISOString(), lastError: null } });\r\n },\r\n onError: (err) => {\r\n logger.error(`Watcher error: ${err.message}`);\r\n statusWriter.update({ claudeWatcher: { lastError: err.message } });\r\n },\r\n log: (level, msg) => {\r\n if (level === \"ERROR\") logger.error(msg);\r\n else if (level === \"WARN\") logger.warn(msg);\r\n else logger.dim(msg);\r\n },\r\n },\r\n });\r\n\r\n await watcher.start();\r\n\r\n // Import and start skill usage detector\r\n const { SkillUsageDetector } = await import(\"../core/skill-usage-detector.js\");\r\n\r\n const skillDetector = new SkillUsageDetector(client, {\r\n intervalMs: 30000,\r\n callbacks: {\r\n onDetection: (count) => {\r\n logger.success(`Detected ${count} skill usage(s)`);\r\n statusWriter.update({ skillDetector: { usagesDetected: count, lastDetection: new Date().toISOString(), lastError: null } });\r\n },\r\n onError: (err) => {\r\n logger.error(`Skill detection error: ${err.message}`);\r\n statusWriter.update({ skillDetector: { lastError: err.message } });\r\n },\r\n log: (level, msg) => {\r\n if (level === \"ERROR\") logger.error(msg);\r\n else if (level === \"WARN\") logger.warn(msg);\r\n else logger.dim(msg);\r\n },\r\n },\r\n });\r\n\r\n await skillDetector.start();\r\n\r\n // Import and start skill installer\r\n const { SkillInstaller } = await import(\"../core/skill-installer.js\");\r\n\r\n const skillInstaller = new SkillInstaller(client, {\r\n intervalMs: 30000,\r\n callbacks: {\r\n onInstall: (slug) => {\r\n logger.success(`Installed skill: ${slug}`);\r\n statusWriter.update({ skillInstaller: { lastInstall: slug, lastAction: new Date().toISOString(), lastError: null } });\r\n },\r\n onUninstall: (slug) => {\r\n logger.success(`Uninstalled skill: ${slug}`);\r\n statusWriter.update({ skillInstaller: { lastUninstall: slug, lastAction: new Date().toISOString(), lastError: null } });\r\n },\r\n onError: (err) => {\r\n logger.error(`Skill installer error: ${err.message}`);\r\n statusWriter.update({ skillInstaller: { lastError: err.message } });\r\n },\r\n log: (level, msg) => {\r\n if (level === \"ERROR\") logger.error(msg);\r\n else if (level === \"WARN\") logger.warn(msg);\r\n else logger.dim(msg);\r\n },\r\n },\r\n });\r\n\r\n await skillInstaller.start();\r\n\r\n // Start status writer (tray reads this file every 3s)\r\n statusWriter.start();\r\n\r\n // Refresh tracked projects cache every 60s\r\n setInterval(async () => {\r\n const updated = await updateTrackedProjectsCache(client, log);\r\n if (updated) statusWriter.update({ trackedProjects: updated });\r\n }, 60000);\r\n\r\n // Clean up stale sessions every 5 minutes\r\n setInterval(() => cleanupStaleSessions(client, log), 300000);\r\n\r\n logger.success(\"Daemon is running. Watching Claude conversations and skill usage...\");\r\n\r\n // Keep the process alive\r\n await new Promise(() => {});\r\n}\r\n","/**\r\n * OS Service Manager — install/uninstall auto-start services.\r\n *\r\n * macOS: LaunchAgent plist (user-level, no sudo)\r\n * Linux: systemd user service (no sudo)\r\n * Windows: Registry Run key + VBScript hidden launcher (no admin)\r\n *\r\n * Creates two services:\r\n * 1. Daemon service — headless background daemon\r\n * 2. Tray service — GUI tray icon (only in graphical sessions)\r\n */\r\n\r\nimport { existsSync, writeFileSync, unlinkSync, readFileSync, mkdirSync, readdirSync } from \"fs\";\r\nimport { join, dirname } from \"path\";\r\nimport { homedir, platform } from \"os\";\r\nimport { execSync } from \"child_process\";\r\n\r\nconst DAEMON_LABEL = \"one.skillo.daemon\";\r\nconst TRAY_LABEL = \"one.skillo.tray\";\r\n\r\n// Windows registry key and value names\r\nconst WIN_REG_KEY = \"HKCU\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run\";\r\nconst WIN_DAEMON_VALUE = \"SkilloDaemon\";\r\nconst WIN_TRAY_VALUE = \"SkilloTray\";\r\n\r\nconst isWin = platform() === \"win32\";\r\nconst pathSep = isWin ? \";\" : \":\";\r\n\r\n// ── Path helpers ────────────────────────────────────────────────────────────\r\n\r\nfunction getLaunchAgentsDir(): string {\r\n return join(homedir(), \"Library\", \"LaunchAgents\");\r\n}\r\n\r\nfunction getSystemdUserDir(): string {\r\n return join(homedir(), \".config\", \"systemd\", \"user\");\r\n}\r\n\r\nfunction getDaemonPlistPath(): string {\r\n return join(getLaunchAgentsDir(), `${DAEMON_LABEL}.plist`);\r\n}\r\n\r\nfunction getTrayPlistPath(): string {\r\n return join(getLaunchAgentsDir(), `${TRAY_LABEL}.plist`);\r\n}\r\n\r\nfunction getDaemonServicePath(): string {\r\n return join(getSystemdUserDir(), \"skillo-daemon.service\");\r\n}\r\n\r\nfunction getTrayServicePath(): string {\r\n return join(getSystemdUserDir(), \"skillo-tray.service\");\r\n}\r\n\r\n/** Get the ~/.skillo/ directory for placing Windows launcher scripts */\r\nfunction getSkilloDataDir(): string {\r\n return join(homedir(), \".skillo\");\r\n}\r\n\r\nfunction getWinDaemonVbsPath(): string {\r\n return join(getSkilloDataDir(), \"skillo-daemon.vbs\");\r\n}\r\n\r\n/** Find the skillo binary path */\r\nfunction findSkilloBin(): string {\r\n try {\r\n const whichCmd = isWin ? \"where skillo\" : \"which skillo\";\r\n const result = execSync(whichCmd, { encoding: \"utf-8\" }).trim();\r\n // `where` on Windows can return multiple lines; take the first\r\n const firstLine = result.split(/\\r?\\n/)[0].trim();\r\n if (firstLine) return firstLine;\r\n } catch {\r\n // fallback\r\n }\r\n\r\n if (isWin) {\r\n // Common Windows locations\r\n const appData = process.env.APPDATA || join(homedir(), \"AppData\", \"Roaming\");\r\n const candidates = [\r\n join(appData, \"npm\", \"skillo.cmd\"),\r\n join(homedir(), \"AppData\", \"Local\", \"fnm_multishells\"),\r\n ];\r\n for (const c of candidates) {\r\n if (existsSync(c)) return c;\r\n }\r\n } else {\r\n const candidates = [\r\n \"/usr/local/bin/skillo\",\r\n \"/usr/bin/skillo\",\r\n ];\r\n for (const c of candidates) {\r\n if (existsSync(c)) return c;\r\n }\r\n }\r\n\r\n return \"skillo\"; // Rely on PATH\r\n}\r\n\r\n/** Build a broad PATH that includes common Node.js install locations */\r\nfunction buildPath(): string {\r\n const paths = new Set<string>();\r\n const home = homedir();\r\n\r\n // Current PATH\r\n (process.env.PATH || \"\").split(pathSep).forEach((p) => paths.add(p));\r\n\r\n if (isWin) {\r\n // Windows-specific Node.js locations\r\n const appData = process.env.APPDATA || join(home, \"AppData\", \"Roaming\");\r\n paths.add(join(appData, \"npm\"));\r\n paths.add(join(home, \"AppData\", \"Local\", \"Programs\", \"nodejs\"));\r\n\r\n // nvm-windows\r\n const nvmHome = process.env.NVM_HOME;\r\n if (nvmHome) paths.add(nvmHome);\r\n const nvmSymlink = process.env.NVM_SYMLINK;\r\n if (nvmSymlink) paths.add(nvmSymlink);\r\n\r\n // fnm on Windows\r\n const fnmDir = join(home, \".fnm\");\r\n if (existsSync(fnmDir)) paths.add(fnmDir);\r\n } else {\r\n // nvm (macOS/Linux)\r\n const nvmDir = process.env.NVM_DIR || join(home, \".nvm\");\r\n if (existsSync(nvmDir)) {\r\n try {\r\n const defaultAlias = join(nvmDir, \"alias\", \"default\");\r\n if (existsSync(defaultAlias)) {\r\n const versionsDir = join(nvmDir, \"versions\", \"node\");\r\n if (existsSync(versionsDir)) {\r\n const versions = readdirSync(versionsDir) as string[];\r\n for (const v of versions) {\r\n paths.add(join(versionsDir, v, \"bin\"));\r\n }\r\n }\r\n }\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n\r\n // fnm\r\n paths.add(join(home, \".fnm\", \"current\", \"bin\"));\r\n\r\n // Homebrew\r\n paths.add(\"/opt/homebrew/bin\");\r\n paths.add(\"/usr/local/bin\");\r\n\r\n // Standard\r\n paths.add(\"/usr/bin\");\r\n paths.add(\"/bin\");\r\n\r\n // npm global\r\n paths.add(join(home, \".npm-global\", \"bin\"));\r\n paths.add(join(home, \".local\", \"bin\"));\r\n }\r\n\r\n return [...paths].filter(Boolean).join(pathSep);\r\n}\r\n\r\n// ── macOS LaunchAgent ───────────────────────────────────────────────────────\r\n\r\nfunction generateDaemonPlist(skilloBin: string, envPath: string): string {\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\r\n<plist version=\"1.0\">\r\n<dict>\r\n <key>Label</key>\r\n <string>${DAEMON_LABEL}</string>\r\n <key>ProgramArguments</key>\r\n <array>\r\n <string>${skilloBin}</string>\r\n <string>start</string>\r\n <string>--foreground</string>\r\n </array>\r\n <key>RunAtLoad</key>\r\n <true/>\r\n <key>KeepAlive</key>\r\n <dict>\r\n <key>SuccessfulExit</key>\r\n <false/>\r\n </dict>\r\n <key>ThrottleInterval</key>\r\n <integer>10</integer>\r\n <key>EnvironmentVariables</key>\r\n <dict>\r\n <key>PATH</key>\r\n <string>${envPath}</string>\r\n <key>HOME</key>\r\n <string>${homedir()}</string>\r\n </dict>\r\n <key>StandardOutPath</key>\r\n <string>${join(homedir(), \".skillo\", \"launchd-stdout.log\")}</string>\r\n <key>StandardErrorPath</key>\r\n <string>${join(homedir(), \".skillo\", \"launchd-stderr.log\")}</string>\r\n</dict>\r\n</plist>`;\r\n}\r\n\r\nfunction generateTrayPlist(skilloBin: string, envPath: string): string {\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\r\n<plist version=\"1.0\">\r\n<dict>\r\n <key>Label</key>\r\n <string>${TRAY_LABEL}</string>\r\n <key>ProgramArguments</key>\r\n <array>\r\n <string>${skilloBin}</string>\r\n <string>tray</string>\r\n </array>\r\n <key>RunAtLoad</key>\r\n <true/>\r\n <key>LimitLoadToSessionType</key>\r\n <string>Aqua</string>\r\n <key>KeepAlive</key>\r\n <dict>\r\n <key>SuccessfulExit</key>\r\n <false/>\r\n </dict>\r\n <key>ThrottleInterval</key>\r\n <integer>10</integer>\r\n <key>EnvironmentVariables</key>\r\n <dict>\r\n <key>PATH</key>\r\n <string>${envPath}</string>\r\n <key>HOME</key>\r\n <string>${homedir()}</string>\r\n </dict>\r\n <key>StandardOutPath</key>\r\n <string>${join(homedir(), \".skillo\", \"launchd-tray-stdout.log\")}</string>\r\n <key>StandardErrorPath</key>\r\n <string>${join(homedir(), \".skillo\", \"launchd-tray-stderr.log\")}</string>\r\n</dict>\r\n</plist>`;\r\n}\r\n\r\n// ── Linux systemd ───────────────────────────────────────────────────────────\r\n\r\nfunction generateDaemonUnit(skilloBin: string, envPath: string): string {\r\n return `[Unit]\r\nDescription=Skillo Daemon\r\nAfter=network.target\r\n\r\n[Service]\r\nType=simple\r\nExecStart=${skilloBin} start --foreground\r\nRestart=on-failure\r\nRestartSec=10\r\nEnvironment=PATH=${envPath}\r\nEnvironment=HOME=${homedir()}\r\n\r\n[Install]\r\nWantedBy=default.target\r\n`;\r\n}\r\n\r\nfunction generateTrayUnit(skilloBin: string, envPath: string): string {\r\n return `[Unit]\r\nDescription=Skillo Tray Icon\r\nAfter=graphical-session.target\r\nPartOf=graphical-session.target\r\n\r\n[Service]\r\nType=simple\r\nExecStart=${skilloBin} tray\r\nRestart=on-failure\r\nRestartSec=10\r\nEnvironment=PATH=${envPath}\r\nEnvironment=HOME=${homedir()}\r\nEnvironment=DISPLAY=:0\r\n\r\n[Install]\r\nWantedBy=graphical-session.target\r\n`;\r\n}\r\n\r\n// ── Windows Registry + VBScript ─────────────────────────────────────────────\r\n\r\n/**\r\n * Generate a VBScript that runs the daemon hidden (no console window).\r\n * WScript.Shell Run(..., 0, False) → windowStyle=hidden, waitOnReturn=false.\r\n */\r\nfunction generateDaemonVbs(skilloBin: string): string {\r\n // Escape backslashes for VBS string literal\r\n const escaped = skilloBin.replace(/\\\\/g, \"\\\\\\\\\");\r\n return `' Skillo Daemon — hidden launcher\\r\\nSet WshShell = CreateObject(\"WScript.Shell\")\\r\\nWshShell.Run \"\"\"${escaped}\"\" start --foreground\", 0, False\\r\\n`;\r\n}\r\n\r\nfunction winRegAdd(valueName: string, data: string) {\r\n // /f = force overwrite without prompt\r\n execSync(\r\n `reg add \"${WIN_REG_KEY}\" /v \"${valueName}\" /t REG_SZ /d \"${data}\" /f`,\r\n { stdio: \"ignore\" }\r\n );\r\n}\r\n\r\nfunction winRegDelete(valueName: string) {\r\n try {\r\n execSync(\r\n `reg delete \"${WIN_REG_KEY}\" /v \"${valueName}\" /f`,\r\n { stdio: \"ignore\" }\r\n );\r\n } catch {\r\n // Value may not exist\r\n }\r\n}\r\n\r\nfunction winRegExists(valueName: string): boolean {\r\n try {\r\n execSync(`reg query \"${WIN_REG_KEY}\" /v \"${valueName}\"`, { stdio: \"ignore\" });\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n// ── Public API ──────────────────────────────────────────────────────────────\r\n\r\nexport interface ServiceStatus {\r\n platform: \"macos\" | \"linux\" | \"windows\" | \"unsupported\";\r\n daemon: { installed: boolean; loaded: boolean };\r\n tray: { installed: boolean; loaded: boolean };\r\n}\r\n\r\nexport async function installService(options?: { includeTray?: boolean }): Promise<{ success: boolean; error?: string }> {\r\n const os = platform();\r\n const skilloBin = findSkilloBin();\r\n const envPath = buildPath();\r\n const includeTray = options?.includeTray ?? true;\r\n\r\n try {\r\n if (os === \"darwin\") {\r\n // ── macOS LaunchAgent ──\r\n const agentsDir = getLaunchAgentsDir();\r\n if (!existsSync(agentsDir)) mkdirSync(agentsDir, { recursive: true });\r\n\r\n // Daemon\r\n const daemonPlist = getDaemonPlistPath();\r\n writeFileSync(daemonPlist, generateDaemonPlist(skilloBin, envPath), \"utf-8\");\r\n try { execSync(`launchctl unload \"${daemonPlist}\" 2>/dev/null`); } catch { /* ignore */ }\r\n execSync(`launchctl load \"${daemonPlist}\"`);\r\n\r\n // Tray\r\n if (includeTray) {\r\n const trayPlist = getTrayPlistPath();\r\n writeFileSync(trayPlist, generateTrayPlist(skilloBin, envPath), \"utf-8\");\r\n try { execSync(`launchctl unload \"${trayPlist}\" 2>/dev/null`); } catch { /* ignore */ }\r\n execSync(`launchctl load \"${trayPlist}\"`);\r\n }\r\n\r\n return { success: true };\r\n\r\n } else if (os === \"linux\") {\r\n // ── Linux systemd ──\r\n const systemdDir = getSystemdUserDir();\r\n if (!existsSync(systemdDir)) mkdirSync(systemdDir, { recursive: true });\r\n\r\n // Daemon\r\n writeFileSync(getDaemonServicePath(), generateDaemonUnit(skilloBin, envPath), \"utf-8\");\r\n execSync(\"systemctl --user daemon-reload\");\r\n execSync(\"systemctl --user enable skillo-daemon.service\");\r\n execSync(\"systemctl --user start skillo-daemon.service\");\r\n\r\n // Tray\r\n if (includeTray) {\r\n writeFileSync(getTrayServicePath(), generateTrayUnit(skilloBin, envPath), \"utf-8\");\r\n execSync(\"systemctl --user daemon-reload\");\r\n execSync(\"systemctl --user enable skillo-tray.service\");\r\n try { execSync(\"systemctl --user start skillo-tray.service\"); } catch { /* may not have graphical session */ }\r\n }\r\n\r\n return { success: true };\r\n\r\n } else if (os === \"win32\") {\r\n // ── Windows Registry Run + VBScript ──\r\n const dataDir = getSkilloDataDir();\r\n if (!existsSync(dataDir)) mkdirSync(dataDir, { recursive: true });\r\n\r\n // Daemon: write a VBScript launcher that hides the console window,\r\n // then point the registry Run key at the VBS file.\r\n const vbsPath = getWinDaemonVbsPath();\r\n writeFileSync(vbsPath, generateDaemonVbs(skilloBin), \"utf-8\");\r\n winRegAdd(WIN_DAEMON_VALUE, `wscript.exe \"${vbsPath}\"`);\r\n\r\n // Tray: runs directly (it has its own window)\r\n if (includeTray) {\r\n winRegAdd(WIN_TRAY_VALUE, `\"${skilloBin}\" tray`);\r\n }\r\n\r\n return { success: true };\r\n\r\n } else {\r\n return { success: false, error: `Unsupported platform: ${os}. Auto-start is available on macOS, Linux, and Windows.` };\r\n }\r\n } catch (error) {\r\n return { success: false, error: error instanceof Error ? error.message : String(error) };\r\n }\r\n}\r\n\r\nexport async function uninstallService(): Promise<{ success: boolean; error?: string }> {\r\n const os = platform();\r\n\r\n try {\r\n if (os === \"darwin\") {\r\n // Unload and remove daemon\r\n const daemonPlist = getDaemonPlistPath();\r\n if (existsSync(daemonPlist)) {\r\n try { execSync(`launchctl unload \"${daemonPlist}\" 2>/dev/null`); } catch { /* ignore */ }\r\n unlinkSync(daemonPlist);\r\n }\r\n\r\n // Unload and remove tray\r\n const trayPlist = getTrayPlistPath();\r\n if (existsSync(trayPlist)) {\r\n try { execSync(`launchctl unload \"${trayPlist}\" 2>/dev/null`); } catch { /* ignore */ }\r\n unlinkSync(trayPlist);\r\n }\r\n\r\n return { success: true };\r\n\r\n } else if (os === \"linux\") {\r\n // Stop, disable, remove daemon\r\n const daemonService = getDaemonServicePath();\r\n if (existsSync(daemonService)) {\r\n try { execSync(\"systemctl --user stop skillo-daemon.service 2>/dev/null\"); } catch { /* ignore */ }\r\n try { execSync(\"systemctl --user disable skillo-daemon.service 2>/dev/null\"); } catch { /* ignore */ }\r\n unlinkSync(daemonService);\r\n }\r\n\r\n // Stop, disable, remove tray\r\n const trayService = getTrayServicePath();\r\n if (existsSync(trayService)) {\r\n try { execSync(\"systemctl --user stop skillo-tray.service 2>/dev/null\"); } catch { /* ignore */ }\r\n try { execSync(\"systemctl --user disable skillo-tray.service 2>/dev/null\"); } catch { /* ignore */ }\r\n unlinkSync(trayService);\r\n }\r\n\r\n try { execSync(\"systemctl --user daemon-reload\"); } catch { /* ignore */ }\r\n\r\n return { success: true };\r\n\r\n } else if (os === \"win32\") {\r\n // Remove registry entries\r\n winRegDelete(WIN_DAEMON_VALUE);\r\n winRegDelete(WIN_TRAY_VALUE);\r\n\r\n // Remove VBS launcher\r\n const vbsPath = getWinDaemonVbsPath();\r\n if (existsSync(vbsPath)) {\r\n try { unlinkSync(vbsPath); } catch { /* ignore */ }\r\n }\r\n\r\n return { success: true };\r\n\r\n } else {\r\n return { success: false, error: `Unsupported platform: ${os}` };\r\n }\r\n } catch (error) {\r\n return { success: false, error: error instanceof Error ? error.message : String(error) };\r\n }\r\n}\r\n\r\nexport function getServiceStatus(): ServiceStatus {\r\n const os = platform();\r\n\r\n if (os === \"darwin\") {\r\n const daemonInstalled = existsSync(getDaemonPlistPath());\r\n const trayInstalled = existsSync(getTrayPlistPath());\r\n\r\n let daemonLoaded = false;\r\n let trayLoaded = false;\r\n\r\n try {\r\n const output = execSync(\"launchctl list\", { encoding: \"utf-8\" });\r\n daemonLoaded = output.includes(DAEMON_LABEL);\r\n trayLoaded = output.includes(TRAY_LABEL);\r\n } catch {\r\n // ignore\r\n }\r\n\r\n return {\r\n platform: \"macos\",\r\n daemon: { installed: daemonInstalled, loaded: daemonLoaded },\r\n tray: { installed: trayInstalled, loaded: trayLoaded },\r\n };\r\n\r\n } else if (os === \"linux\") {\r\n const daemonInstalled = existsSync(getDaemonServicePath());\r\n const trayInstalled = existsSync(getTrayServicePath());\r\n\r\n let daemonLoaded = false;\r\n let trayLoaded = false;\r\n\r\n try {\r\n execSync(\"systemctl --user is-active skillo-daemon.service\", { encoding: \"utf-8\" });\r\n daemonLoaded = true;\r\n } catch { /* not active */ }\r\n\r\n try {\r\n execSync(\"systemctl --user is-active skillo-tray.service\", { encoding: \"utf-8\" });\r\n trayLoaded = true;\r\n } catch { /* not active */ }\r\n\r\n return {\r\n platform: \"linux\",\r\n daemon: { installed: daemonInstalled, loaded: daemonLoaded },\r\n tray: { installed: trayInstalled, loaded: trayLoaded },\r\n };\r\n\r\n } else if (os === \"win32\") {\r\n const daemonInstalled = winRegExists(WIN_DAEMON_VALUE);\r\n const trayInstalled = winRegExists(WIN_TRAY_VALUE);\r\n\r\n // On Windows, \"loaded\" = the Run key exists (it will run at next login).\r\n // We can't easily check if the process is currently running from the registry\r\n // alone, but the key existing means it *will* auto-start.\r\n return {\r\n platform: \"windows\",\r\n daemon: { installed: daemonInstalled, loaded: daemonInstalled },\r\n tray: { installed: trayInstalled, loaded: trayInstalled },\r\n };\r\n }\r\n\r\n return {\r\n platform: \"unsupported\",\r\n daemon: { installed: false, loaded: false },\r\n tray: { installed: false, loaded: false },\r\n };\r\n}\r\n","/**\r\n * Shared daemon tasks used by both daemon-runner.ts (background)\r\n * and daemon.ts foreground mode.\r\n */\r\n\r\nimport { existsSync, readFileSync, writeFileSync, unlinkSync, readdirSync } from \"fs\";\r\nimport { getTrackedProjectsCacheFile, getActiveSessionsDir } from \"./paths.js\";\r\n\r\ntype LogFn = (level: string, msg: string) => void;\r\ntype ApiClient = { listProjects: (trackingOnly: boolean) => Promise<any>; endSession: (id: string) => Promise<any> };\r\n\r\nexport async function updateTrackedProjectsCache(client: ApiClient, log: LogFn) {\r\n try {\r\n const result = await client.listProjects(true);\r\n if (result.success && result.data?.projects) {\r\n const projects = result.data.projects\r\n .filter((p: { trackingEnabled: boolean }) => p.trackingEnabled)\r\n .map((p: { path: string; name: string; gitRemoteNormalized?: string }) => ({ path: p.path, name: p.name, gitRemoteNormalized: p.gitRemoteNormalized }));\r\n const cacheData = { updatedAt: Date.now(), projects };\r\n writeFileSync(getTrackedProjectsCacheFile(), JSON.stringify(cacheData, null, 2));\r\n log(\"DEBUG\", `Updated tracked projects cache: ${projects.length} project(s)`);\r\n return projects;\r\n }\r\n } catch (error) {\r\n log(\"ERROR\", `Failed to update tracked projects cache: ${error}`);\r\n }\r\n return null;\r\n}\r\n\r\nexport async function cleanupStaleSessions(client: ApiClient, log: LogFn) {\r\n const sessionsDir = getActiveSessionsDir();\r\n if (!existsSync(sessionsDir)) return;\r\n\r\n try {\r\n const files = readdirSync(sessionsDir);\r\n for (const file of files) {\r\n if (!file.endsWith(\".json\")) continue;\r\n const pid = parseInt(file.replace(\".json\", \"\"), 10);\r\n if (isNaN(pid)) continue;\r\n\r\n let isRunning = false;\r\n try { process.kill(pid, 0); isRunning = true; } catch { /* process dead */ }\r\n\r\n if (!isRunning) {\r\n try {\r\n const sessionData = JSON.parse(readFileSync(`${sessionsDir}/${file}`, \"utf-8\"));\r\n if (sessionData.sessionId) {\r\n await client.endSession(sessionData.sessionId);\r\n log(\"INFO\", `Ended stale session ${sessionData.sessionId.slice(0, 8)} (PID ${pid} dead)`);\r\n }\r\n unlinkSync(`${sessionsDir}/${file}`);\r\n } catch {\r\n try { unlinkSync(`${sessionsDir}/${file}`); } catch { /* ignore */ }\r\n }\r\n }\r\n }\r\n } catch (error) {\r\n log(\"ERROR\", `Stale session cleanup error: ${error}`);\r\n }\r\n}\r\n\r\nexport function readTrackedProjectsFromCache(): Array<{ path: string; name: string; gitRemoteNormalized?: string }> | null {\r\n try {\r\n const cacheData = JSON.parse(readFileSync(getTrackedProjectsCacheFile(), \"utf-8\"));\r\n return cacheData.projects || [];\r\n } catch {\r\n return null;\r\n }\r\n}\r\n"],"mappings":"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","names":["existsSync","readFileSync","writeFileSync","unlinkSync","join","existsSync","readFileSync","writeFileSync","unlinkSync","readdirSync","writeFileSync","existsSync","readdirSync","readFileSync","unlinkSync","existsSync","readFileSync","unlinkSync","child","writeFileSync","join","spawn"]}
|
|
@@ -18,6 +18,7 @@ function matchesProjectPath(claudeProjectPath, filterPath) {
|
|
|
18
18
|
}
|
|
19
19
|
var projectCache = {
|
|
20
20
|
projects: /* @__PURE__ */ new Map(),
|
|
21
|
+
gitRemoteIndex: /* @__PURE__ */ new Map(),
|
|
21
22
|
lastFetched: 0
|
|
22
23
|
};
|
|
23
24
|
var CACHE_TTL_MS = 6e4;
|
|
@@ -30,12 +31,17 @@ async function loadTrackedProjects(client) {
|
|
|
30
31
|
const result = await client.listProjects(true);
|
|
31
32
|
if (result.success && result.data?.projects) {
|
|
32
33
|
projectCache.projects.clear();
|
|
34
|
+
projectCache.gitRemoteIndex.clear();
|
|
33
35
|
for (const project of result.data.projects) {
|
|
34
36
|
const normalizedPath = normalizePath(project.path);
|
|
35
|
-
|
|
37
|
+
const info = {
|
|
36
38
|
tracked: project.trackingEnabled,
|
|
37
39
|
name: project.name
|
|
38
|
-
}
|
|
40
|
+
};
|
|
41
|
+
projectCache.projects.set(normalizedPath, info);
|
|
42
|
+
if (project.gitRemoteNormalized) {
|
|
43
|
+
projectCache.gitRemoteIndex.set(project.gitRemoteNormalized, info);
|
|
44
|
+
}
|
|
39
45
|
}
|
|
40
46
|
projectCache.lastFetched = now;
|
|
41
47
|
}
|
|
@@ -56,6 +62,20 @@ async function isProjectTracked(projectPath) {
|
|
|
56
62
|
return info;
|
|
57
63
|
}
|
|
58
64
|
}
|
|
65
|
+
if (projectCache.gitRemoteIndex.size > 0) {
|
|
66
|
+
try {
|
|
67
|
+
const { getGitInfo } = await import("./git-OGUSYBJS.js");
|
|
68
|
+
const gitInfo = getGitInfo(projectPath);
|
|
69
|
+
if (gitInfo.remoteNormalized) {
|
|
70
|
+
const remoteMatch = projectCache.gitRemoteIndex.get(gitInfo.remoteNormalized);
|
|
71
|
+
if (remoteMatch) return remoteMatch;
|
|
72
|
+
}
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (process.env.DEBUG) {
|
|
75
|
+
console.error("[skillo] git detection failed:", error);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
59
79
|
return { tracked: false };
|
|
60
80
|
}
|
|
61
81
|
var ClaudeWatcher = class {
|
|
@@ -141,6 +161,8 @@ var ClaudeWatcher = class {
|
|
|
141
161
|
if (!trackingStatus.tracked) {
|
|
142
162
|
continue;
|
|
143
163
|
}
|
|
164
|
+
entry.toolsUsed = extractToolsUsed(entry.display);
|
|
165
|
+
entry.filesReferenced = extractFilesReferenced(entry.display);
|
|
144
166
|
newPrompts.push(entry);
|
|
145
167
|
} catch {
|
|
146
168
|
}
|
|
@@ -168,9 +190,49 @@ var ClaudeWatcher = class {
|
|
|
168
190
|
}
|
|
169
191
|
}
|
|
170
192
|
};
|
|
193
|
+
var TOOL_PATTERNS = [
|
|
194
|
+
/\b(grep|rg|ripgrep)\b/i,
|
|
195
|
+
/\b(git)\s+(commit|push|pull|merge|rebase|checkout|branch|stash|log|diff|add|reset)\b/i,
|
|
196
|
+
/\b(npm|yarn|pnpm|bun)\s+(install|run|build|test|dev|start|publish)\b/i,
|
|
197
|
+
/\b(docker|docker-compose|podman)\s+\w+/i,
|
|
198
|
+
/\b(curl|wget|fetch)\b/i,
|
|
199
|
+
/\b(vim|nvim|nano|code|emacs)\b/i,
|
|
200
|
+
/\b(make|cmake|cargo|go\s+build|rustc|gcc|clang)\b/i,
|
|
201
|
+
/\b(pytest|jest|vitest|mocha|cypress|playwright)\b/i,
|
|
202
|
+
/\b(prisma|drizzle|sequelize|typeorm)\s+\w+/i,
|
|
203
|
+
/\b(kubectl|helm|terraform|ansible)\s+\w+/i
|
|
204
|
+
];
|
|
205
|
+
function extractToolsUsed(text) {
|
|
206
|
+
const tools = /* @__PURE__ */ new Set();
|
|
207
|
+
for (const pattern of TOOL_PATTERNS) {
|
|
208
|
+
const match = text.match(pattern);
|
|
209
|
+
if (match) {
|
|
210
|
+
tools.add(match[0].trim().toLowerCase());
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return Array.from(tools);
|
|
214
|
+
}
|
|
215
|
+
var FILE_PATTERN = /(?:^|\s|['"`])((?:\.{0,2}\/)?[\w-]+\/[\w./-]+\.(?:ts|tsx|js|jsx|mjs|cjs|json|yaml|yml|toml|md|css|scss|html|sql|py|rs|go|java|rb|sh|prisma|graphql))\b/gi;
|
|
216
|
+
var CONFIG_FILE_PATTERN = /(?:^|\s|['"`])((?:package\.json|tsconfig\.json|\.env|Dockerfile|Makefile|docker-compose\.ya?ml|\.gitignore|prisma\/schema\.prisma))\b/gi;
|
|
217
|
+
function extractFilesReferenced(text) {
|
|
218
|
+
const files = /* @__PURE__ */ new Set();
|
|
219
|
+
let match;
|
|
220
|
+
while ((match = FILE_PATTERN.exec(text)) !== null) {
|
|
221
|
+
const filePath = match[1];
|
|
222
|
+
if (filePath.length < 200 && !filePath.includes("..")) {
|
|
223
|
+
files.add(filePath);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
FILE_PATTERN.lastIndex = 0;
|
|
227
|
+
while ((match = CONFIG_FILE_PATTERN.exec(text)) !== null) {
|
|
228
|
+
files.add(match[1]);
|
|
229
|
+
}
|
|
230
|
+
CONFIG_FILE_PATTERN.lastIndex = 0;
|
|
231
|
+
return Array.from(files);
|
|
232
|
+
}
|
|
171
233
|
export {
|
|
172
234
|
ClaudeWatcher,
|
|
173
235
|
getTrackedProjectCount,
|
|
174
236
|
loadTrackedProjects
|
|
175
237
|
};
|
|
176
|
-
//# sourceMappingURL=claude-watcher-
|
|
238
|
+
//# sourceMappingURL=claude-watcher-WKGBJYKN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/claude-watcher.ts"],"sourcesContent":["/**\r\n * Claude Watcher - Reusable module for watching Claude Code history.\r\n *\r\n * Polls ~/.claude/history.jsonl for new entries and syncs them to the platform.\r\n * Used by both the daemon (background) and the `claude watch` command (foreground).\r\n */\r\n\r\nimport * as fs from \"fs\";\r\nimport * as path from \"path\";\r\nimport * as os from \"os\";\r\nimport * as readline from \"readline\";\r\n\r\n// Types\r\nexport interface ClaudeHistoryEntry {\r\n display: string;\r\n timestamp: number;\r\n project: string;\r\n sessionId: string;\r\n pastedContents?: Record<string, unknown>;\r\n toolsUsed?: string[];\r\n filesReferenced?: string[];\r\n}\r\n\r\ninterface TrackedProjectInfo {\r\n tracked: boolean;\r\n name?: string;\r\n}\r\n\r\nexport interface ClaudeWatcherCallbacks {\r\n onSync?: (promptsCreated: number) => void;\r\n onError?: (error: Error) => void;\r\n log?: (level: string, msg: string) => void;\r\n}\r\n\r\n// ── Path helpers ──\r\n\r\nfunction getClaudeHistoryPath(): string {\r\n return path.join(os.homedir(), \".claude\", \"history.jsonl\");\r\n}\r\n\r\nfunction normalizePath(p: string): string {\r\n return p.toLowerCase().replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\");\r\n}\r\n\r\nfunction matchesProjectPath(claudeProjectPath: string, filterPath: string): boolean {\r\n const normalizedClaude = normalizePath(claudeProjectPath);\r\n const normalizedFilter = normalizePath(filterPath);\r\n return normalizedClaude === normalizedFilter ||\r\n normalizedClaude.startsWith(normalizedFilter + \"/\");\r\n}\r\n\r\n// ── Project tracking cache ──\r\n\r\nconst projectCache = {\r\n projects: new Map<string, TrackedProjectInfo>(),\r\n gitRemoteIndex: new Map<string, TrackedProjectInfo>(),\r\n lastFetched: 0,\r\n};\r\n\r\nconst CACHE_TTL_MS = 60000;\r\n\r\nexport async function loadTrackedProjects(\r\n client: { listProjects: (includeDisabled: boolean) => Promise<{ success: boolean; data?: { projects: Array<{ path: string; name: string; trackingEnabled: boolean; gitRemoteNormalized?: string }> } }> }\r\n): Promise<Map<string, TrackedProjectInfo>> {\r\n const now = Date.now();\r\n\r\n if (projectCache.lastFetched > 0 && now - projectCache.lastFetched < CACHE_TTL_MS) {\r\n return projectCache.projects;\r\n }\r\n\r\n try {\r\n const result = await client.listProjects(true);\r\n if (result.success && result.data?.projects) {\r\n projectCache.projects.clear();\r\n projectCache.gitRemoteIndex.clear();\r\n for (const project of result.data.projects) {\r\n const normalizedPath = normalizePath(project.path);\r\n const info: TrackedProjectInfo = {\r\n tracked: project.trackingEnabled,\r\n name: project.name,\r\n };\r\n projectCache.projects.set(normalizedPath, info);\r\n // Also index by gitRemoteNormalized for cross-machine matching\r\n if (project.gitRemoteNormalized) {\r\n projectCache.gitRemoteIndex.set(project.gitRemoteNormalized, info);\r\n }\r\n }\r\n projectCache.lastFetched = now;\r\n }\r\n } catch {\r\n // Use stale cache on failure\r\n }\r\n\r\n return projectCache.projects;\r\n}\r\n\r\nexport function getTrackedProjectCount(): number {\r\n return Array.from(projectCache.projects.values()).filter(p => p.tracked).length;\r\n}\r\n\r\nasync function isProjectTracked(projectPath: string): Promise<TrackedProjectInfo> {\r\n const normalizedPath = normalizePath(projectPath);\r\n\r\n // Check by path (exact or parent match)\r\n if (projectCache.projects.has(normalizedPath)) {\r\n return projectCache.projects.get(normalizedPath)!;\r\n }\r\n\r\n for (const [trackedPath, info] of projectCache.projects.entries()) {\r\n if (normalizedPath.startsWith(trackedPath + \"/\")) {\r\n return info;\r\n }\r\n }\r\n\r\n // Fall back to matching by git remote (handles different paths on different machines)\r\n if (projectCache.gitRemoteIndex.size > 0) {\r\n try {\r\n const { getGitInfo } = await import(\"../utils/git.js\");\r\n const gitInfo = getGitInfo(projectPath);\r\n if (gitInfo.remoteNormalized) {\r\n const remoteMatch = projectCache.gitRemoteIndex.get(gitInfo.remoteNormalized);\r\n if (remoteMatch) return remoteMatch;\r\n }\r\n } catch (error) {\r\n if (process.env.DEBUG) {\r\n console.error(\"[skillo] git detection failed:\", error);\r\n }\r\n }\r\n }\r\n\r\n return { tracked: false };\r\n}\r\n\r\n// ── Watcher ──\r\n\r\nexport class ClaudeWatcher {\r\n private lastSize = 0;\r\n private lastMtime = 0;\r\n private intervalId: ReturnType<typeof setInterval> | null = null;\r\n private historyPath: string;\r\n private intervalMs: number;\r\n private projectFilter?: string;\r\n private sessionFilter?: string;\r\n private callbacks: ClaudeWatcherCallbacks;\r\n private client: {\r\n syncClaudePrompts: (prompts: ClaudeHistoryEntry[], options?: { terminalSessionId?: string; projectPath?: string }) => Promise<{ success: boolean; data?: { promptsCreated: number; sessionsCreated: number; promptsSkipped: number }; error?: string }>;\r\n listProjects: (includeDisabled: boolean) => Promise<{ success: boolean; data?: { projects: Array<{ path: string; name: string; trackingEnabled: boolean }> } }>;\r\n };\r\n\r\n constructor(\r\n client: ClaudeWatcher[\"client\"],\r\n options: {\r\n intervalMs?: number;\r\n projectFilter?: string;\r\n sessionFilter?: string;\r\n callbacks?: ClaudeWatcherCallbacks;\r\n } = {}\r\n ) {\r\n this.client = client;\r\n this.historyPath = getClaudeHistoryPath();\r\n this.intervalMs = options.intervalMs || 5000;\r\n this.projectFilter = options.projectFilter;\r\n this.sessionFilter = options.sessionFilter;\r\n this.callbacks = options.callbacks || {};\r\n }\r\n\r\n private log(level: string, msg: string) {\r\n if (this.callbacks.log) {\r\n this.callbacks.log(level, msg);\r\n }\r\n }\r\n\r\n /** Initialize file position to current end (don't sync old entries). */\r\n initPosition() {\r\n try {\r\n if (fs.existsSync(this.historyPath)) {\r\n const stats = fs.statSync(this.historyPath);\r\n this.lastSize = stats.size;\r\n this.lastMtime = stats.mtimeMs;\r\n }\r\n } catch {\r\n // File doesn't exist yet\r\n }\r\n }\r\n\r\n /** Start watching. Runs an initial check then polls on interval. */\r\n async start() {\r\n this.initPosition();\r\n\r\n // Load tracked projects\r\n await loadTrackedProjects(this.client);\r\n const trackedCount = getTrackedProjectCount();\r\n this.log(\"INFO\", `Watching ${trackedCount} tracked project(s)`);\r\n\r\n // Initial check\r\n await this.checkAndSync();\r\n\r\n // Poll\r\n this.intervalId = setInterval(() => this.checkAndSync(), this.intervalMs);\r\n }\r\n\r\n /** Stop watching. */\r\n stop() {\r\n if (this.intervalId) {\r\n clearInterval(this.intervalId);\r\n this.intervalId = null;\r\n }\r\n }\r\n\r\n /** Check for new entries and sync them. */\r\n async checkAndSync() {\r\n try {\r\n if (!fs.existsSync(this.historyPath)) return;\r\n\r\n const stats = fs.statSync(this.historyPath);\r\n\r\n // Skip if no changes\r\n if (stats.size === this.lastSize && stats.mtimeMs === this.lastMtime) {\r\n return;\r\n }\r\n\r\n // Refresh tracked projects cache\r\n await loadTrackedProjects(this.client);\r\n\r\n // Read new content\r\n const newPrompts: ClaudeHistoryEntry[] = [];\r\n\r\n if (stats.size > this.lastSize) {\r\n const stream = fs.createReadStream(this.historyPath, {\r\n start: this.lastSize,\r\n encoding: \"utf-8\",\r\n });\r\n\r\n const rl = readline.createInterface({\r\n input: stream,\r\n crlfDelay: Infinity,\r\n });\r\n\r\n for await (const line of rl) {\r\n if (!line.trim()) continue;\r\n\r\n try {\r\n const entry = JSON.parse(line) as ClaudeHistoryEntry;\r\n if (!entry.display || !entry.timestamp || !entry.project || !entry.sessionId) {\r\n continue;\r\n }\r\n\r\n // Project path filter\r\n if (this.projectFilter && !matchesProjectPath(entry.project, this.projectFilter)) {\r\n continue;\r\n }\r\n\r\n // Privacy: only tracked projects\r\n const trackingStatus = await isProjectTracked(entry.project);\r\n if (!trackingStatus.tracked) {\r\n continue;\r\n }\r\n\r\n // Extract tool and file metadata from prompt text\r\n entry.toolsUsed = extractToolsUsed(entry.display);\r\n entry.filesReferenced = extractFilesReferenced(entry.display);\r\n\r\n newPrompts.push(entry);\r\n } catch {\r\n // Skip invalid JSON\r\n }\r\n }\r\n }\r\n\r\n this.lastSize = stats.size;\r\n this.lastMtime = stats.mtimeMs;\r\n\r\n if (newPrompts.length > 0) {\r\n this.log(\"INFO\", `Detected ${newPrompts.length} new prompt(s), syncing...`);\r\n\r\n const response = await this.client.syncClaudePrompts(newPrompts, {\r\n terminalSessionId: this.sessionFilter,\r\n projectPath: this.projectFilter,\r\n });\r\n\r\n if (response.success && response.data) {\r\n this.log(\"INFO\", `Synced ${response.data.promptsCreated} prompt(s)`);\r\n this.callbacks.onSync?.(response.data.promptsCreated);\r\n } else {\r\n this.log(\"ERROR\", `Sync failed: ${response.error}`);\r\n }\r\n }\r\n } catch (error) {\r\n const err = error instanceof Error ? error : new Error(String(error));\r\n this.log(\"ERROR\", `Watch error: ${err.message}`);\r\n this.callbacks.onError?.(err);\r\n }\r\n }\r\n}\r\n\r\n// ── Metadata extraction helpers ──\r\n\r\nconst TOOL_PATTERNS = [\r\n /\\b(grep|rg|ripgrep)\\b/i,\r\n /\\b(git)\\s+(commit|push|pull|merge|rebase|checkout|branch|stash|log|diff|add|reset)\\b/i,\r\n /\\b(npm|yarn|pnpm|bun)\\s+(install|run|build|test|dev|start|publish)\\b/i,\r\n /\\b(docker|docker-compose|podman)\\s+\\w+/i,\r\n /\\b(curl|wget|fetch)\\b/i,\r\n /\\b(vim|nvim|nano|code|emacs)\\b/i,\r\n /\\b(make|cmake|cargo|go\\s+build|rustc|gcc|clang)\\b/i,\r\n /\\b(pytest|jest|vitest|mocha|cypress|playwright)\\b/i,\r\n /\\b(prisma|drizzle|sequelize|typeorm)\\s+\\w+/i,\r\n /\\b(kubectl|helm|terraform|ansible)\\s+\\w+/i,\r\n];\r\n\r\nfunction extractToolsUsed(text: string): string[] {\r\n const tools = new Set<string>();\r\n for (const pattern of TOOL_PATTERNS) {\r\n const match = text.match(pattern);\r\n if (match) {\r\n tools.add(match[0].trim().toLowerCase());\r\n }\r\n }\r\n return Array.from(tools);\r\n}\r\n\r\n// Match file paths that look intentional: must contain a slash OR a known config filename\r\nconst FILE_PATTERN = /(?:^|\\s|['\"`])((?:\\.{0,2}\\/)?[\\w-]+\\/[\\w./-]+\\.(?:ts|tsx|js|jsx|mjs|cjs|json|yaml|yml|toml|md|css|scss|html|sql|py|rs|go|java|rb|sh|prisma|graphql))\\b/gi;\r\nconst CONFIG_FILE_PATTERN = /(?:^|\\s|['\"`])((?:package\\.json|tsconfig\\.json|\\.env|Dockerfile|Makefile|docker-compose\\.ya?ml|\\.gitignore|prisma\\/schema\\.prisma))\\b/gi;\r\n\r\nfunction extractFilesReferenced(text: string): string[] {\r\n const files = new Set<string>();\r\n let match;\r\n while ((match = FILE_PATTERN.exec(text)) !== null) {\r\n const filePath = match[1];\r\n if (filePath.length < 200 && !filePath.includes(\"..\")) {\r\n files.add(filePath);\r\n }\r\n }\r\n FILE_PATTERN.lastIndex = 0;\r\n while ((match = CONFIG_FILE_PATTERN.exec(text)) !== null) {\r\n files.add(match[1]);\r\n }\r\n CONFIG_FILE_PATTERN.lastIndex = 0;\r\n return Array.from(files);\r\n}\r\n"],"mappings":"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","names":[]}
|