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.
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
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/database.ts"],"sourcesContent":["/**\r\n * Database operations for Skillo.\r\n * Uses sql.js for pure JavaScript SQLite (no native dependencies).\r\n */\r\n\r\nimport initSqlJs, { Database as SqlJsDatabase, SqlValue } from \"sql.js\";\r\nimport { readFileSync, writeFileSync, existsSync } from \"fs\";\r\nimport { randomUUID } from \"crypto\";\r\nimport { dirname } from \"path\";\r\nimport { ensureDirectory, getDbPath } from \"../utils/paths.js\";\r\n\r\nexport interface Command {\r\n id: string;\r\n timestamp: string;\r\n command: string;\r\n normalized: string;\r\n variables: string | null;\r\n cwd: string;\r\n exitCode: number | null;\r\n durationMs: number | null;\r\n sessionId: string;\r\n projectId: string | null;\r\n createdAt: string;\r\n}\r\n\r\nexport interface Session {\r\n id: string;\r\n startedAt: string;\r\n endedAt: string | null;\r\n shell: string;\r\n projectPath: string | null;\r\n commandCount: number;\r\n createdAt: string;\r\n}\r\n\r\nexport interface Pattern {\r\n id: string;\r\n sourceType: string;\r\n description: string;\r\n count: number;\r\n firstSeen: string;\r\n lastSeen: string;\r\n data: Record<string, unknown>;\r\n status: string;\r\n createdAt: string;\r\n updatedAt: string;\r\n}\r\n\r\nexport interface Skill {\r\n id: string;\r\n name: string;\r\n path: string;\r\n sourcePatterns: string | null;\r\n sourceType: string;\r\n triggerPhrase: string | null;\r\n createdAt: string;\r\n usageCount: number;\r\n lastUsedAt: string | null;\r\n}\r\n\r\nexport interface Stats {\r\n commandsToday: number;\r\n commandsTotal: number;\r\n patternsActive: number;\r\n skillsTotal: number;\r\n conversationsTotal: number;\r\n}\r\n\r\nlet SQL: Awaited<ReturnType<typeof initSqlJs>> | null = null;\r\n\r\nasync function getSqlJs() {\r\n if (!SQL) {\r\n SQL = await initSqlJs();\r\n }\r\n return SQL;\r\n}\r\n\r\nexport class SkilloDatabase {\r\n private db: SqlJsDatabase | null = null;\r\n private dbPath: string;\r\n private initialized = false;\r\n\r\n constructor(dbPath?: string) {\r\n this.dbPath = dbPath || getDbPath();\r\n ensureDirectory(dirname(this.dbPath));\r\n }\r\n\r\n private async getDb(): Promise<SqlJsDatabase> {\r\n if (!this.db) {\r\n const SQL = await getSqlJs();\r\n\r\n if (existsSync(this.dbPath)) {\r\n const buffer = readFileSync(this.dbPath);\r\n this.db = new SQL.Database(buffer);\r\n } else {\r\n this.db = new SQL.Database();\r\n }\r\n }\r\n return this.db;\r\n }\r\n\r\n private save(): void {\r\n if (this.db) {\r\n const data = this.db.export();\r\n const buffer = Buffer.from(data);\r\n writeFileSync(this.dbPath, buffer);\r\n }\r\n }\r\n\r\n async initialize(): Promise<void> {\r\n if (this.initialized) return;\r\n\r\n const db = await this.getDb();\r\n\r\n const schema = `\r\n -- Command history\r\n CREATE TABLE IF NOT EXISTS commands (\r\n id TEXT PRIMARY KEY,\r\n timestamp DATETIME NOT NULL,\r\n command TEXT NOT NULL,\r\n normalized TEXT NOT NULL,\r\n variables TEXT,\r\n cwd TEXT NOT NULL,\r\n exit_code INTEGER,\r\n duration_ms INTEGER,\r\n session_id TEXT NOT NULL,\r\n project_id TEXT,\r\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\r\n );\r\n\r\n CREATE INDEX IF NOT EXISTS idx_commands_session ON commands(session_id);\r\n CREATE INDEX IF NOT EXISTS idx_commands_timestamp ON commands(timestamp);\r\n CREATE INDEX IF NOT EXISTS idx_commands_normalized ON commands(normalized);\r\n\r\n -- Terminal sessions\r\n CREATE TABLE IF NOT EXISTS sessions (\r\n id TEXT PRIMARY KEY,\r\n started_at DATETIME NOT NULL,\r\n ended_at DATETIME,\r\n shell TEXT NOT NULL,\r\n project_path TEXT,\r\n command_count INTEGER DEFAULT 0,\r\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\r\n );\r\n\r\n -- Conversations (Claude Code)\r\n CREATE TABLE IF NOT EXISTS conversations (\r\n id TEXT PRIMARY KEY,\r\n project_path TEXT,\r\n started_at DATETIME,\r\n updated_at DATETIME,\r\n message_count INTEGER DEFAULT 0,\r\n tool_calls TEXT,\r\n files_touched TEXT,\r\n intent_summary TEXT,\r\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\r\n );\r\n\r\n CREATE INDEX IF NOT EXISTS idx_conversations_project ON conversations(project_path);\r\n\r\n -- Detected patterns\r\n CREATE TABLE IF NOT EXISTS patterns (\r\n id TEXT PRIMARY KEY,\r\n source_type TEXT NOT NULL,\r\n description TEXT NOT NULL,\r\n count INTEGER DEFAULT 1,\r\n first_seen DATETIME NOT NULL,\r\n last_seen DATETIME NOT NULL,\r\n data TEXT NOT NULL,\r\n status TEXT DEFAULT 'active',\r\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\r\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\r\n );\r\n\r\n CREATE INDEX IF NOT EXISTS idx_patterns_status ON patterns(status);\r\n CREATE INDEX IF NOT EXISTS idx_patterns_count ON patterns(count);\r\n\r\n -- Generated skills\r\n CREATE TABLE IF NOT EXISTS skills (\r\n id TEXT PRIMARY KEY,\r\n name TEXT UNIQUE NOT NULL,\r\n path TEXT NOT NULL,\r\n source_patterns TEXT,\r\n source_type TEXT NOT NULL,\r\n trigger_phrase TEXT,\r\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\r\n usage_count INTEGER DEFAULT 0,\r\n last_used_at DATETIME\r\n );\r\n\r\n CREATE INDEX IF NOT EXISTS idx_skills_name ON skills(name);\r\n\r\n -- Configuration\r\n CREATE TABLE IF NOT EXISTS config (\r\n key TEXT PRIMARY KEY,\r\n value TEXT NOT NULL,\r\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\r\n );\r\n\r\n -- Ignored patterns\r\n CREATE TABLE IF NOT EXISTS ignored_patterns (\r\n pattern_hash TEXT PRIMARY KEY,\r\n reason TEXT,\r\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\r\n );\r\n\r\n -- Notifications\r\n CREATE TABLE IF NOT EXISTS notifications (\r\n id TEXT PRIMARY KEY,\r\n type TEXT NOT NULL,\r\n title TEXT NOT NULL,\r\n message TEXT,\r\n data TEXT,\r\n is_read INTEGER DEFAULT 0,\r\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\r\n );\r\n `;\r\n\r\n db.run(schema);\r\n this.save();\r\n this.initialized = true;\r\n }\r\n\r\n close(): void {\r\n if (this.db) {\r\n this.save();\r\n this.db.close();\r\n this.db = null;\r\n }\r\n }\r\n\r\n // Command operations\r\n async addCommand(params: {\r\n command: string;\r\n normalized: string;\r\n cwd: string;\r\n sessionId: string;\r\n exitCode?: number | null;\r\n durationMs?: number | null;\r\n variables?: Record<string, unknown> | null;\r\n projectId?: string | null;\r\n }): Promise<string> {\r\n const db = await this.getDb();\r\n const id = randomUUID();\r\n const timestamp = new Date().toISOString();\r\n\r\n db.run(\r\n `INSERT INTO commands (id, timestamp, command, normalized, variables, cwd, exit_code, duration_ms, session_id, project_id)\r\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\r\n [\r\n id,\r\n timestamp,\r\n params.command,\r\n params.normalized,\r\n params.variables ? JSON.stringify(params.variables) : null,\r\n params.cwd,\r\n params.exitCode ?? null,\r\n params.durationMs ?? null,\r\n params.sessionId,\r\n params.projectId ?? null,\r\n ]\r\n );\r\n\r\n this.save();\r\n return id;\r\n }\r\n\r\n async getRecentCommands(options?: {\r\n limit?: number;\r\n sessionId?: string;\r\n days?: number;\r\n }): Promise<Command[]> {\r\n const db = await this.getDb();\r\n const limit = options?.limit ?? 100;\r\n let query = \"SELECT * FROM commands\";\r\n const params: SqlValue[] = [];\r\n const conditions: string[] = [];\r\n\r\n if (options?.sessionId) {\r\n conditions.push(\"session_id = ?\");\r\n params.push(options.sessionId);\r\n }\r\n\r\n if (options?.days) {\r\n const cutoff = new Date();\r\n cutoff.setDate(cutoff.getDate() - options.days);\r\n conditions.push(\"timestamp >= ?\");\r\n params.push(cutoff.toISOString());\r\n }\r\n\r\n if (conditions.length > 0) {\r\n query += \" WHERE \" + conditions.join(\" AND \");\r\n }\r\n\r\n query += \" ORDER BY timestamp DESC LIMIT ?\";\r\n params.push(limit);\r\n\r\n const results = db.exec(query, params);\r\n if (results.length === 0) return [];\r\n\r\n return results[0].values.map((row) => this.mapCommand(results[0].columns, row as SqlValue[]));\r\n }\r\n\r\n // Session operations\r\n async createSession(shell: string, projectPath?: string | null): Promise<string> {\r\n const db = await this.getDb();\r\n const id = randomUUID();\r\n const startedAt = new Date().toISOString();\r\n\r\n db.run(\r\n `INSERT INTO sessions (id, started_at, shell, project_path) VALUES (?, ?, ?, ?)`,\r\n [id, startedAt, shell, projectPath ?? null]\r\n );\r\n\r\n this.save();\r\n return id;\r\n }\r\n\r\n async endSession(sessionId: string): Promise<void> {\r\n const db = await this.getDb();\r\n const endedAt = new Date().toISOString();\r\n\r\n db.run(\r\n `UPDATE sessions\r\n SET ended_at = ?, command_count = (SELECT COUNT(*) FROM commands WHERE session_id = ?)\r\n WHERE id = ?`,\r\n [endedAt, sessionId, sessionId]\r\n );\r\n\r\n this.save();\r\n }\r\n\r\n async getSession(sessionId: string): Promise<Session | null> {\r\n const db = await this.getDb();\r\n const results = db.exec(\"SELECT * FROM sessions WHERE id = ?\", [sessionId]);\r\n if (results.length === 0 || results[0].values.length === 0) return null;\r\n return this.mapSession(results[0].columns, results[0].values[0]);\r\n }\r\n\r\n // Pattern operations\r\n async addPattern(params: {\r\n sourceType: string;\r\n description: string;\r\n data: Record<string, unknown>;\r\n patternHash?: string;\r\n }): Promise<string> {\r\n const db = await this.getDb();\r\n const patternId = params.patternHash || randomUUID();\r\n const now = new Date().toISOString();\r\n\r\n // Check if pattern exists\r\n const existing = db.exec(\"SELECT id, count FROM patterns WHERE id = ?\", [patternId]);\r\n\r\n if (existing.length > 0 && existing[0].values.length > 0) {\r\n // Update count\r\n db.run(`UPDATE patterns SET count = count + 1, last_seen = ?, updated_at = ? WHERE id = ?`, [\r\n now,\r\n now,\r\n patternId,\r\n ]);\r\n } else {\r\n // Insert new\r\n db.run(\r\n `INSERT INTO patterns (id, source_type, description, data, first_seen, last_seen)\r\n VALUES (?, ?, ?, ?, ?, ?)`,\r\n [patternId, params.sourceType, params.description, JSON.stringify(params.data), now, now]\r\n );\r\n }\r\n\r\n this.save();\r\n return patternId;\r\n }\r\n\r\n async getPatterns(options?: {\r\n type?: string;\r\n status?: string;\r\n minCount?: number;\r\n limit?: number;\r\n }): Promise<Pattern[]> {\r\n const db = await this.getDb();\r\n const minCount = options?.minCount ?? 1;\r\n const limit = options?.limit ?? 50;\r\n\r\n let query = \"SELECT * FROM patterns WHERE count >= ?\";\r\n const params: SqlValue[] = [minCount];\r\n\r\n if (options?.type) {\r\n query += \" AND source_type = ?\";\r\n params.push(options.type);\r\n }\r\n\r\n if (options?.status) {\r\n query += \" AND status = ?\";\r\n params.push(options.status);\r\n }\r\n\r\n query += \" ORDER BY count DESC, last_seen DESC LIMIT ?\";\r\n params.push(limit);\r\n\r\n const results = db.exec(query, params);\r\n if (results.length === 0) return [];\r\n\r\n return results[0].values.map((row) => this.mapPattern(results[0].columns, row as SqlValue[]));\r\n }\r\n\r\n async getPattern(patternId: string): Promise<Pattern | null> {\r\n const db = await this.getDb();\r\n\r\n // Try exact match first\r\n let results = db.exec(\"SELECT * FROM patterns WHERE id = ?\", [patternId]);\r\n\r\n if (results.length === 0 || results[0].values.length === 0) {\r\n // Try partial match\r\n results = db.exec(\"SELECT * FROM patterns WHERE id LIKE ?\", [`${patternId}%`]);\r\n }\r\n\r\n if (results.length === 0 || results[0].values.length === 0) return null;\r\n return this.mapPattern(results[0].columns, results[0].values[0]);\r\n }\r\n\r\n async updatePatternStatus(patternId: string, status: string): Promise<void> {\r\n const db = await this.getDb();\r\n const now = new Date().toISOString();\r\n\r\n db.run(`UPDATE patterns SET status = ?, updated_at = ? WHERE id = ? OR id LIKE ?`, [\r\n status,\r\n now,\r\n patternId,\r\n `${patternId}%`,\r\n ]);\r\n\r\n this.save();\r\n }\r\n\r\n async addIgnoredPattern(patternHash: string, reason?: string | null): Promise<void> {\r\n const db = await this.getDb();\r\n\r\n db.run(`INSERT OR REPLACE INTO ignored_patterns (pattern_hash, reason) VALUES (?, ?)`, [\r\n patternHash,\r\n reason ?? null,\r\n ]);\r\n\r\n this.save();\r\n }\r\n\r\n async isPatternIgnored(patternHash: string): Promise<boolean> {\r\n const db = await this.getDb();\r\n const results = db.exec(\"SELECT 1 FROM ignored_patterns WHERE pattern_hash = ?\", [patternHash]);\r\n return results.length > 0 && results[0].values.length > 0;\r\n }\r\n\r\n // Skill operations\r\n async registerSkill(params: {\r\n name: string;\r\n path: string;\r\n sourceType: string;\r\n triggerPhrase?: string | null;\r\n sourcePatterns?: string[] | null;\r\n }): Promise<string> {\r\n const db = await this.getDb();\r\n const id = randomUUID();\r\n\r\n db.run(\r\n `INSERT INTO skills (id, name, path, source_patterns, source_type, trigger_phrase)\r\n VALUES (?, ?, ?, ?, ?, ?)`,\r\n [\r\n id,\r\n params.name,\r\n params.path,\r\n params.sourcePatterns ? JSON.stringify(params.sourcePatterns) : null,\r\n params.sourceType,\r\n params.triggerPhrase ?? null,\r\n ]\r\n );\r\n\r\n this.save();\r\n return id;\r\n }\r\n\r\n async getSkills(options?: {\r\n source?: string;\r\n sortBy?: \"name\" | \"created\" | \"usage\";\r\n }): Promise<Skill[]> {\r\n const db = await this.getDb();\r\n const sortMap = {\r\n name: \"name\",\r\n created: \"created_at\",\r\n usage: \"usage_count DESC\",\r\n };\r\n const order = sortMap[options?.sortBy ?? \"name\"];\r\n\r\n let query = \"SELECT * FROM skills\";\r\n const params: SqlValue[] = [];\r\n\r\n if (options?.source) {\r\n query += \" WHERE source_type = ?\";\r\n params.push(options.source);\r\n }\r\n\r\n query += ` ORDER BY ${order}`;\r\n\r\n const results = db.exec(query, params);\r\n if (results.length === 0) return [];\r\n\r\n return results[0].values.map((row) => this.mapSkill(results[0].columns, row as SqlValue[]));\r\n }\r\n\r\n async getSkill(name: string): Promise<Skill | null> {\r\n const db = await this.getDb();\r\n const results = db.exec(\"SELECT * FROM skills WHERE name = ?\", [name]);\r\n if (results.length === 0 || results[0].values.length === 0) return null;\r\n return this.mapSkill(results[0].columns, results[0].values[0]);\r\n }\r\n\r\n async deleteSkill(name: string): Promise<void> {\r\n const db = await this.getDb();\r\n db.run(\"DELETE FROM skills WHERE name = ?\", [name]);\r\n this.save();\r\n }\r\n\r\n async recordSkillUsage(name: string): Promise<void> {\r\n const db = await this.getDb();\r\n const now = new Date().toISOString();\r\n\r\n db.run(`UPDATE skills SET usage_count = usage_count + 1, last_used_at = ? WHERE name = ?`, [\r\n now,\r\n name,\r\n ]);\r\n\r\n this.save();\r\n }\r\n\r\n // Statistics\r\n async getStats(): Promise<Stats> {\r\n const db = await this.getDb();\r\n const today = new Date();\r\n today.setHours(0, 0, 0, 0);\r\n const todayIso = today.toISOString();\r\n\r\n const getCount = (query: string, params: SqlValue[] = []): number => {\r\n const results = db.exec(query, params);\r\n if (results.length === 0 || results[0].values.length === 0) return 0;\r\n return results[0].values[0][0] as number;\r\n };\r\n\r\n return {\r\n commandsToday: getCount(\"SELECT COUNT(*) FROM commands WHERE timestamp >= ?\", [todayIso]),\r\n commandsTotal: getCount(\"SELECT COUNT(*) FROM commands\"),\r\n patternsActive: getCount(\"SELECT COUNT(*) FROM patterns WHERE status = 'active'\"),\r\n skillsTotal: getCount(\"SELECT COUNT(*) FROM skills\"),\r\n conversationsTotal: getCount(\"SELECT COUNT(*) FROM conversations\"),\r\n };\r\n }\r\n\r\n // Mapping helpers\r\n private mapCommand(columns: string[], values: SqlValue[]): Command {\r\n const row = this.toObject(columns, values);\r\n return {\r\n id: row.id as string,\r\n timestamp: row.timestamp as string,\r\n command: row.command as string,\r\n normalized: row.normalized as string,\r\n variables: row.variables as string | null,\r\n cwd: row.cwd as string,\r\n exitCode: row.exit_code as number | null,\r\n durationMs: row.duration_ms as number | null,\r\n sessionId: row.session_id as string,\r\n projectId: row.project_id as string | null,\r\n createdAt: row.created_at as string,\r\n };\r\n }\r\n\r\n private mapSession(columns: string[], values: SqlValue[]): Session {\r\n const row = this.toObject(columns, values);\r\n return {\r\n id: row.id as string,\r\n startedAt: row.started_at as string,\r\n endedAt: row.ended_at as string | null,\r\n shell: row.shell as string,\r\n projectPath: row.project_path as string | null,\r\n commandCount: row.command_count as number,\r\n createdAt: row.created_at as string,\r\n };\r\n }\r\n\r\n private mapPattern(columns: string[], values: SqlValue[]): Pattern {\r\n const row = this.toObject(columns, values);\r\n return {\r\n id: row.id as string,\r\n sourceType: row.source_type as string,\r\n description: row.description as string,\r\n count: row.count as number,\r\n firstSeen: row.first_seen as string,\r\n lastSeen: row.last_seen as string,\r\n data: JSON.parse(row.data as string),\r\n status: row.status as string,\r\n createdAt: row.created_at as string,\r\n updatedAt: row.updated_at as string,\r\n };\r\n }\r\n\r\n private mapSkill(columns: string[], values: SqlValue[]): Skill {\r\n const row = this.toObject(columns, values);\r\n return {\r\n id: row.id as string,\r\n name: row.name as string,\r\n path: row.path as string,\r\n sourcePatterns: row.source_patterns as string | null,\r\n sourceType: row.source_type as string,\r\n triggerPhrase: row.trigger_phrase as string | null,\r\n createdAt: row.created_at as string,\r\n usageCount: row.usage_count as number,\r\n lastUsedAt: row.last_used_at as string | null,\r\n };\r\n }\r\n\r\n private toObject(columns: string[], values: SqlValue[]): Record<string, SqlValue> {\r\n const obj: Record<string, SqlValue> = {};\r\n columns.forEach((col, i) => {\r\n obj[col] = values[i];\r\n });\r\n return obj;\r\n }\r\n}\r\n"],"mappings":"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","names":["SQL"]}
@@ -0,0 +1,221 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ isDaemonRunning,
4
+ startDaemonProcess
5
+ } from "./chunk-XLJGCOVT.js";
6
+ import {
7
+ logger_default
8
+ } from "./chunk-VAQ73XPE.js";
9
+ import {
10
+ getApiClient
11
+ } from "./chunk-RQ2SC5HW.js";
12
+ import {
13
+ getGitInfo
14
+ } from "./chunk-6GOJPFZ7.js";
15
+ import {
16
+ ensureDirectory,
17
+ getDataDir,
18
+ getShellIntegrationDir,
19
+ getTrackedProjectsCacheFile
20
+ } from "./chunk-63FVALWX.js";
21
+
22
+ // src/commands/project.ts
23
+ import { Command } from "commander";
24
+ import { existsSync, writeFileSync } from "fs";
25
+ import { resolve } from "path";
26
+ var projectCommand = new Command("project").description("Manage project tracking settings");
27
+ var trackCommand = new Command("track").description("Enable tracking for the current project").argument("[path]", "Project path (defaults to current directory)").option("-n, --name <name>", "Custom project name").action(async (pathArg, options) => {
28
+ const client = getApiClient();
29
+ logger_default.blank();
30
+ if (!client.hasApiKey()) {
31
+ logger_default.error("Not logged in. Run 'skillo login' first.");
32
+ return;
33
+ }
34
+ let projectPath = resolve(pathArg || process.cwd());
35
+ const gitInfo = getGitInfo(projectPath);
36
+ if (gitInfo.isGitRepo && gitInfo.rootPath) {
37
+ projectPath = gitInfo.rootPath;
38
+ }
39
+ logger_default.info(`Tracking project: ${projectPath}`);
40
+ logger_default.blank();
41
+ if (gitInfo.isGitRepo) {
42
+ logger_default.dim(`Git repository detected`);
43
+ if (gitInfo.remote) {
44
+ logger_default.dim(`Remote: ${gitInfo.remoteNormalized || gitInfo.remote}`);
45
+ }
46
+ if (gitInfo.branch) {
47
+ logger_default.dim(`Branch: ${gitInfo.branch}`);
48
+ }
49
+ logger_default.blank();
50
+ } else {
51
+ logger_default.warn("No git repository detected in this directory.");
52
+ logger_default.dim("Project will be tracked by path only (no team sharing).");
53
+ logger_default.blank();
54
+ }
55
+ const projectName = options?.name || gitInfo.projectName || projectPath.split("/").pop() || "Unknown";
56
+ try {
57
+ const result = await client.connectProject({
58
+ path: projectPath,
59
+ name: projectName,
60
+ gitRemote: gitInfo.remote ?? void 0,
61
+ gitRemoteNormalized: gitInfo.remoteNormalized ?? void 0
62
+ });
63
+ if (result.success) {
64
+ logger_default.success(`Project "${projectName}" is now being tracked!`);
65
+ logger_default.blank();
66
+ logger_default.dim("Your Claude Code prompts in this directory will now be synced.");
67
+ logger_default.dim("Run 'skillo untrack' to stop tracking.");
68
+ try {
69
+ const listResult = await client.listProjects(true);
70
+ if (listResult.success && listResult.data?.projects) {
71
+ const cacheData = {
72
+ updatedAt: Date.now(),
73
+ projects: listResult.data.projects.filter((p) => p.trackingEnabled).map((p) => ({ path: p.path, name: p.name, gitRemoteNormalized: p.gitRemoteNormalized }))
74
+ };
75
+ ensureDirectory(getDataDir());
76
+ writeFileSync(getTrackedProjectsCacheFile(), JSON.stringify(cacheData, null, 2));
77
+ }
78
+ } catch {
79
+ }
80
+ const { running } = isDaemonRunning();
81
+ if (!running) {
82
+ logger_default.blank();
83
+ const pid = startDaemonProcess();
84
+ if (pid) {
85
+ logger_default.success(`Background sync daemon started (PID: ${pid})`);
86
+ } else {
87
+ logger_default.dim("Tip: Run 'skillo start' to enable background sync.");
88
+ }
89
+ }
90
+ const shellIntegrationDir = getShellIntegrationDir();
91
+ if (!existsSync(shellIntegrationDir) || !existsSync(resolve(shellIntegrationDir, "skillo.zsh"))) {
92
+ logger_default.blank();
93
+ logger_default.dim("Tip: Run 'skillo setup-shell' to enable terminal auto-detection.");
94
+ }
95
+ } else {
96
+ logger_default.error("Failed to track project: " + (result.error || "Unknown error"));
97
+ }
98
+ } catch (error) {
99
+ logger_default.error("Failed to track project: " + (error instanceof Error ? error.message : "Unknown error"));
100
+ }
101
+ logger_default.blank();
102
+ });
103
+ var untrackCommand = new Command("untrack").description("Disable tracking for the current project").argument("[path]", "Project path (defaults to current directory)").action(async (pathArg) => {
104
+ const client = getApiClient();
105
+ logger_default.blank();
106
+ if (!client.hasApiKey()) {
107
+ logger_default.error("Not logged in. Run 'skillo login' first.");
108
+ return;
109
+ }
110
+ let projectPath = resolve(pathArg || process.cwd());
111
+ const gitInfo = getGitInfo(projectPath);
112
+ if (gitInfo.isGitRepo && gitInfo.rootPath) {
113
+ projectPath = gitInfo.rootPath;
114
+ }
115
+ logger_default.info(`Untracking project: ${projectPath}`);
116
+ try {
117
+ const result = await client.disconnectProject(projectPath, gitInfo.remoteNormalized ?? void 0);
118
+ if (result.success) {
119
+ logger_default.blank();
120
+ logger_default.success("Project is no longer being tracked.");
121
+ logger_default.blank();
122
+ logger_default.dim("Your Claude Code prompts in this directory will no longer be synced.");
123
+ logger_default.dim("Existing data remains in your account.");
124
+ } else {
125
+ logger_default.blank();
126
+ logger_default.error("Failed to untrack project: " + (result.error || "Unknown error"));
127
+ }
128
+ } catch (error) {
129
+ logger_default.blank();
130
+ logger_default.error("Failed to untrack project: " + (error instanceof Error ? error.message : "Unknown error"));
131
+ }
132
+ logger_default.blank();
133
+ });
134
+ projectCommand.command("status").description("Show tracking status for the current project").argument("[path]", "Project path (defaults to current directory)").action(async (pathArg) => {
135
+ const client = getApiClient();
136
+ logger_default.blank();
137
+ if (!client.hasApiKey()) {
138
+ logger_default.error("Not logged in. Run 'skillo login' first.");
139
+ return;
140
+ }
141
+ let projectPath = resolve(pathArg || process.cwd());
142
+ const gitInfo = getGitInfo(projectPath);
143
+ if (gitInfo.isGitRepo && gitInfo.rootPath) {
144
+ projectPath = gitInfo.rootPath;
145
+ }
146
+ logger_default.info(`Project: ${projectPath}`);
147
+ logger_default.blank();
148
+ if (gitInfo.isGitRepo) {
149
+ logger_default.dim(`Git: ${gitInfo.remoteNormalized || gitInfo.remote || "no remote"}`);
150
+ if (gitInfo.branch) {
151
+ logger_default.dim(`Branch: ${gitInfo.branch}`);
152
+ }
153
+ } else {
154
+ logger_default.dim("Git: not a repository");
155
+ }
156
+ logger_default.blank();
157
+ try {
158
+ const result = await client.getProjectStatus(projectPath, gitInfo.remoteNormalized ?? void 0);
159
+ if (result.success && result.data) {
160
+ const { connected, connectedAt } = result.data;
161
+ if (connected) {
162
+ logger_default.success("Tracking ENABLED");
163
+ if (connectedAt) {
164
+ logger_default.dim(`Since: ${new Date(connectedAt).toLocaleDateString()}`);
165
+ }
166
+ } else {
167
+ logger_default.warn("Tracking DISABLED");
168
+ logger_default.dim("Run 'skillo track' to enable tracking.");
169
+ }
170
+ } else {
171
+ logger_default.warn("Not tracked");
172
+ logger_default.dim("Run 'skillo track' to enable tracking for this project.");
173
+ }
174
+ } catch (error) {
175
+ logger_default.error("Failed to get project status: " + (error instanceof Error ? error.message : "Unknown error"));
176
+ }
177
+ logger_default.blank();
178
+ });
179
+ projectCommand.command("list").alias("ls").description("List all projects (tracked and untracked)").option("-a, --all", "Include untracked projects").action(async (options) => {
180
+ const client = getApiClient();
181
+ logger_default.blank();
182
+ if (!client.hasApiKey()) {
183
+ logger_default.error("Not logged in. Run 'skillo login' first.");
184
+ return;
185
+ }
186
+ try {
187
+ const result = await client.listProjects(options?.all || false);
188
+ if (result.success && result.data) {
189
+ const { projects, totalTracked, totalProjects } = result.data;
190
+ logger_default.info(`Projects (${totalTracked} tracked / ${totalProjects} total)`);
191
+ logger_default.blank();
192
+ if (projects.length === 0) {
193
+ logger_default.dim("No projects found.");
194
+ logger_default.dim("Run 'skillo track' in a project directory to start tracking.");
195
+ } else {
196
+ for (const project of projects) {
197
+ const status = project.trackingEnabled ? "\u25CF" : "\u25CB";
198
+ const statusColor = project.trackingEnabled ? "\x1B[32m" : "\x1B[90m";
199
+ const resetColor = "\x1B[0m";
200
+ console.log(` ${statusColor}${status}${resetColor} ${project.name}`);
201
+ logger_default.dim(` ${project.path}`);
202
+ if (project.gitRemoteNormalized) {
203
+ logger_default.dim(` ${project.gitRemoteNormalized}`);
204
+ }
205
+ }
206
+ }
207
+ } else {
208
+ logger_default.error("Failed to list projects: " + (result.error || "Unknown error"));
209
+ }
210
+ } catch (error) {
211
+ logger_default.error("Failed to list projects: " + (error instanceof Error ? error.message : "Unknown error"));
212
+ }
213
+ logger_default.blank();
214
+ });
215
+
216
+ export {
217
+ projectCommand,
218
+ trackCommand,
219
+ untrackCommand
220
+ };
221
+ //# sourceMappingURL=chunk-QIV4VIXA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/project.ts"],"sourcesContent":["/**\r\n * Project tracking commands - Track/untrack projects for privacy-first tracking.\r\n */\r\n\r\nimport { Command } from \"commander\";\r\nimport { existsSync, writeFileSync } from \"fs\";\r\nimport { getApiClient } from \"../core/api-client.js\";\r\nimport { getGitInfo } from \"../utils/git.js\";\r\nimport logger from \"../utils/logger.js\";\r\nimport { resolve } from \"path\";\r\nimport { getTrackedProjectsCacheFile, getShellIntegrationDir, ensureDirectory, getDataDir } from \"../utils/paths.js\";\r\nimport { isDaemonRunning, startDaemonProcess } from \"./daemon.js\";\r\n\r\nexport const projectCommand = new Command(\"project\")\r\n .description(\"Manage project tracking settings\");\r\n\r\n// skillo track - Enable tracking for current directory\r\nexport const trackCommand = new Command(\"track\")\r\n .description(\"Enable tracking for the current project\")\r\n .argument(\"[path]\", \"Project path (defaults to current directory)\")\r\n .option(\"-n, --name <name>\", \"Custom project name\")\r\n .action(async (pathArg?: string, options?: { name?: string }) => {\r\n const client = getApiClient();\r\n\r\n logger.blank();\r\n\r\n if (!client.hasApiKey()) {\r\n logger.error(\"Not logged in. Run 'skillo login' first.\");\r\n return;\r\n }\r\n\r\n let projectPath = resolve(pathArg || process.cwd());\r\n\r\n // Get git information\r\n const gitInfo = getGitInfo(projectPath);\r\n\r\n // Normalize to git root to prevent duplicates from subdirectories\r\n if (gitInfo.isGitRepo && gitInfo.rootPath) {\r\n projectPath = gitInfo.rootPath;\r\n }\r\n\r\n logger.info(`Tracking project: ${projectPath}`);\r\n logger.blank();\r\n\r\n if (gitInfo.isGitRepo) {\r\n logger.dim(`Git repository detected`);\r\n if (gitInfo.remote) {\r\n logger.dim(`Remote: ${gitInfo.remoteNormalized || gitInfo.remote}`);\r\n }\r\n if (gitInfo.branch) {\r\n logger.dim(`Branch: ${gitInfo.branch}`);\r\n }\r\n logger.blank();\r\n } else {\r\n logger.warn(\"No git repository detected in this directory.\");\r\n logger.dim(\"Project will be tracked by path only (no team sharing).\");\r\n logger.blank();\r\n }\r\n\r\n // Determine project name\r\n const projectName = options?.name || gitInfo.projectName || projectPath.split(\"/\").pop() || \"Unknown\";\r\n\r\n try {\r\n const result = await client.connectProject({\r\n path: projectPath,\r\n name: projectName,\r\n gitRemote: gitInfo.remote ?? undefined,\r\n gitRemoteNormalized: gitInfo.remoteNormalized ?? undefined,\r\n });\r\n\r\n if (result.success) {\r\n logger.success(`Project \"${projectName}\" is now being tracked!`);\r\n logger.blank();\r\n logger.dim(\"Your Claude Code prompts in this directory will now be synced.\");\r\n logger.dim(\"Run 'skillo untrack' to stop tracking.\");\r\n\r\n // Write tracked projects cache immediately\r\n try {\r\n const listResult = await client.listProjects(true);\r\n if (listResult.success && listResult.data?.projects) {\r\n const cacheData = {\r\n updatedAt: Date.now(),\r\n projects: listResult.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 };\r\n ensureDirectory(getDataDir());\r\n writeFileSync(getTrackedProjectsCacheFile(), JSON.stringify(cacheData, null, 2));\r\n }\r\n } catch {\r\n // Non-critical - daemon will update cache on next cycle\r\n }\r\n\r\n // Auto-start daemon if not running\r\n const { running } = isDaemonRunning();\r\n if (!running) {\r\n logger.blank();\r\n const pid = startDaemonProcess();\r\n if (pid) {\r\n logger.success(`Background sync daemon started (PID: ${pid})`);\r\n } else {\r\n logger.dim(\"Tip: Run 'skillo start' to enable background sync.\");\r\n }\r\n }\r\n\r\n // Suggest shell integration if not installed\r\n const shellIntegrationDir = getShellIntegrationDir();\r\n if (!existsSync(shellIntegrationDir) || !existsSync(resolve(shellIntegrationDir, \"skillo.zsh\"))) {\r\n logger.blank();\r\n logger.dim(\"Tip: Run 'skillo setup-shell' to enable terminal auto-detection.\");\r\n }\r\n } else {\r\n logger.error(\"Failed to track project: \" + (result.error || \"Unknown error\"));\r\n }\r\n } catch (error) {\r\n logger.error(\"Failed to track project: \" + (error instanceof Error ? error.message : \"Unknown error\"));\r\n }\r\n\r\n logger.blank();\r\n });\r\n\r\n// skillo untrack - Disable tracking for current directory\r\nexport const untrackCommand = new Command(\"untrack\")\r\n .description(\"Disable tracking for the current project\")\r\n .argument(\"[path]\", \"Project path (defaults to current directory)\")\r\n .action(async (pathArg?: string) => {\r\n const client = getApiClient();\r\n\r\n logger.blank();\r\n\r\n if (!client.hasApiKey()) {\r\n logger.error(\"Not logged in. Run 'skillo login' first.\");\r\n return;\r\n }\r\n\r\n let projectPath = resolve(pathArg || process.cwd());\r\n\r\n // Normalize to git root\r\n const gitInfo = getGitInfo(projectPath);\r\n if (gitInfo.isGitRepo && gitInfo.rootPath) {\r\n projectPath = gitInfo.rootPath;\r\n }\r\n\r\n logger.info(`Untracking project: ${projectPath}`);\r\n\r\n try {\r\n const result = await client.disconnectProject(projectPath, gitInfo.remoteNormalized ?? undefined);\r\n\r\n if (result.success) {\r\n logger.blank();\r\n logger.success(\"Project is no longer being tracked.\");\r\n logger.blank();\r\n logger.dim(\"Your Claude Code prompts in this directory will no longer be synced.\");\r\n logger.dim(\"Existing data remains in your account.\");\r\n } else {\r\n logger.blank();\r\n logger.error(\"Failed to untrack project: \" + (result.error || \"Unknown error\"));\r\n }\r\n } catch (error) {\r\n logger.blank();\r\n logger.error(\"Failed to untrack project: \" + (error instanceof Error ? error.message : \"Unknown error\"));\r\n }\r\n\r\n logger.blank();\r\n });\r\n\r\n// skillo project status - Show tracking status for current project\r\nprojectCommand\r\n .command(\"status\")\r\n .description(\"Show tracking status for the current project\")\r\n .argument(\"[path]\", \"Project path (defaults to current directory)\")\r\n .action(async (pathArg?: string) => {\r\n const client = getApiClient();\r\n\r\n logger.blank();\r\n\r\n if (!client.hasApiKey()) {\r\n logger.error(\"Not logged in. Run 'skillo login' first.\");\r\n return;\r\n }\r\n\r\n let projectPath = resolve(pathArg || process.cwd());\r\n\r\n // Get git information\r\n const gitInfo = getGitInfo(projectPath);\r\n\r\n // Normalize to git root\r\n if (gitInfo.isGitRepo && gitInfo.rootPath) {\r\n projectPath = gitInfo.rootPath;\r\n }\r\n\r\n logger.info(`Project: ${projectPath}`);\r\n logger.blank();\r\n\r\n if (gitInfo.isGitRepo) {\r\n logger.dim(`Git: ${gitInfo.remoteNormalized || gitInfo.remote || \"no remote\"}`);\r\n if (gitInfo.branch) {\r\n logger.dim(`Branch: ${gitInfo.branch}`);\r\n }\r\n } else {\r\n logger.dim(\"Git: not a repository\");\r\n }\r\n\r\n logger.blank();\r\n\r\n try {\r\n const result = await client.getProjectStatus(projectPath, gitInfo.remoteNormalized ?? undefined);\r\n\r\n if (result.success && result.data) {\r\n const { connected, connectedAt } = result.data;\r\n\r\n if (connected) {\r\n logger.success(\"Tracking ENABLED\");\r\n if (connectedAt) {\r\n logger.dim(`Since: ${new Date(connectedAt).toLocaleDateString()}`);\r\n }\r\n } else {\r\n logger.warn(\"Tracking DISABLED\");\r\n logger.dim(\"Run 'skillo track' to enable tracking.\");\r\n }\r\n } else {\r\n logger.warn(\"Not tracked\");\r\n logger.dim(\"Run 'skillo track' to enable tracking for this project.\");\r\n }\r\n } catch (error) {\r\n logger.error(\"Failed to get project status: \" + (error instanceof Error ? error.message : \"Unknown error\"));\r\n }\r\n\r\n logger.blank();\r\n });\r\n\r\n// skillo project list - List all tracked projects\r\nprojectCommand\r\n .command(\"list\")\r\n .alias(\"ls\")\r\n .description(\"List all projects (tracked and untracked)\")\r\n .option(\"-a, --all\", \"Include untracked projects\")\r\n .action(async (options?: { all?: boolean }) => {\r\n const client = getApiClient();\r\n\r\n logger.blank();\r\n\r\n if (!client.hasApiKey()) {\r\n logger.error(\"Not logged in. Run 'skillo login' first.\");\r\n return;\r\n }\r\n\r\n try {\r\n const result = await client.listProjects(options?.all || false);\r\n\r\n if (result.success && result.data) {\r\n const { projects, totalTracked, totalProjects } = result.data;\r\n\r\n logger.info(`Projects (${totalTracked} tracked / ${totalProjects} total)`);\r\n logger.blank();\r\n\r\n if (projects.length === 0) {\r\n logger.dim(\"No projects found.\");\r\n logger.dim(\"Run 'skillo track' in a project directory to start tracking.\");\r\n } else {\r\n for (const project of projects) {\r\n const status = project.trackingEnabled ? \"●\" : \"○\";\r\n const statusColor = project.trackingEnabled ? \"\\x1b[32m\" : \"\\x1b[90m\";\r\n const resetColor = \"\\x1b[0m\";\r\n\r\n console.log(` ${statusColor}${status}${resetColor} ${project.name}`);\r\n logger.dim(` ${project.path}`);\r\n if (project.gitRemoteNormalized) {\r\n logger.dim(` ${project.gitRemoteNormalized}`);\r\n }\r\n }\r\n }\r\n } else {\r\n logger.error(\"Failed to list projects: \" + (result.error || \"Unknown error\"));\r\n }\r\n } catch (error) {\r\n logger.error(\"Failed to list projects: \" + (error instanceof Error ? error.message : \"Unknown error\"));\r\n }\r\n\r\n logger.blank();\r\n });\r\n"],"mappings":"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","names":[]}
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  ensureDirectory,
4
4
  getConfigFile
5
- } from "./chunk-WJKZWKER.js";
5
+ } from "./chunk-63FVALWX.js";
6
6
 
7
7
  // src/core/config.ts
8
8
  import { readFileSync, writeFileSync, existsSync } from "fs";
@@ -180,4 +180,4 @@ export {
180
180
  getConfigValue,
181
181
  setConfigValue
182
182
  };
183
- //# sourceMappingURL=chunk-CPL3P2OF.js.map
183
+ //# sourceMappingURL=chunk-QUXHHRRK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/config.ts"],"sourcesContent":["/**\r\n * Configuration management for Skillo.\r\n */\r\n\r\nimport { readFileSync, writeFileSync, existsSync } from \"fs\";\r\nimport { dirname } from \"path\";\r\nimport YAML from \"yaml\";\r\nimport { ensureDirectory, getConfigFile } from \"../utils/paths.js\";\r\n\r\nexport interface SkilloConfig {\r\n // Shell settings\r\n defaultShell: string | null;\r\n\r\n // Pattern detection\r\n patternDetection: {\r\n minCount: number;\r\n sessionTimeout: number;\r\n similarityThreshold: number;\r\n maxSequenceLength: number;\r\n };\r\n\r\n // Privacy settings\r\n privacy: {\r\n autoRedact: boolean;\r\n trackOutput: boolean;\r\n neverTrack: string[];\r\n redactionPatterns: string[];\r\n };\r\n\r\n // Notification settings\r\n notifications: {\r\n enabled: boolean;\r\n style: \"inline\" | \"desktop\" | \"both\";\r\n sound: boolean;\r\n };\r\n\r\n // Skill generation\r\n skillGeneration: {\r\n outputDir: string | null;\r\n includeScripts: boolean;\r\n includeExamples: boolean;\r\n autoGenerate: boolean;\r\n };\r\n\r\n // Claude Code integration\r\n claudeCode: {\r\n watchConversations: boolean;\r\n conversationDir: string | null;\r\n };\r\n\r\n // Daemon settings\r\n daemon: {\r\n logLevel: \"DEBUG\" | \"INFO\" | \"WARN\" | \"ERROR\";\r\n patternCheckInterval: number;\r\n conversationCheckInterval: number;\r\n };\r\n\r\n // Team settings\r\n team: {\r\n enabled: boolean;\r\n slug: string | null;\r\n autoSync: boolean;\r\n syncInterval: number;\r\n };\r\n\r\n // API settings\r\n api: {\r\n baseUrl: string | null;\r\n timeout: number;\r\n };\r\n\r\n // API key (stored separately, not in YAML for security)\r\n apiKey?: string;\r\n}\r\n\r\nexport function getDefaultConfig(): SkilloConfig {\r\n return {\r\n defaultShell: null,\r\n\r\n patternDetection: {\r\n minCount: 3,\r\n sessionTimeout: 30,\r\n similarityThreshold: 0.8,\r\n maxSequenceLength: 10,\r\n },\r\n\r\n privacy: {\r\n autoRedact: true,\r\n trackOutput: false,\r\n neverTrack: [\r\n \"*password*\",\r\n \"*secret*\",\r\n \"vault *\",\r\n \"1password *\",\r\n \"op *\",\r\n \"export *_KEY=*\",\r\n \"export *_SECRET=*\",\r\n \"export *_TOKEN=*\",\r\n ],\r\n redactionPatterns: [\r\n \"password[=:]\\\\s*\\\\S+\",\r\n \"token[=:]\\\\s*\\\\S+\",\r\n \"secret[=:]\\\\s*\\\\S+\",\r\n \"api[_-]?key[=:]\\\\s*\\\\S+\",\r\n \"auth[=:]\\\\s*\\\\S+\",\r\n \"bearer\\\\s+\\\\S+\",\r\n \"-----BEGIN.*PRIVATE KEY-----\",\r\n \"AKIA[0-9A-Z]{16}\",\r\n '[\"\\'][A-Za-z0-9+/]{40,}[\"\\']',\r\n ],\r\n },\r\n\r\n notifications: {\r\n enabled: true,\r\n style: \"inline\",\r\n sound: false,\r\n },\r\n\r\n skillGeneration: {\r\n outputDir: null,\r\n includeScripts: true,\r\n includeExamples: true,\r\n autoGenerate: false,\r\n },\r\n\r\n claudeCode: {\r\n watchConversations: true,\r\n conversationDir: null,\r\n },\r\n\r\n daemon: {\r\n logLevel: \"INFO\",\r\n patternCheckInterval: 60,\r\n conversationCheckInterval: 5,\r\n },\r\n\r\n team: {\r\n enabled: false,\r\n slug: null,\r\n autoSync: true,\r\n syncInterval: 300,\r\n },\r\n\r\n api: {\r\n baseUrl: null,\r\n timeout: 30000,\r\n },\r\n };\r\n}\r\n\r\nfunction deepMerge<T extends object>(base: T, override: Partial<T>): T {\r\n const result = { ...base };\r\n\r\n for (const key of Object.keys(override) as Array<keyof T>) {\r\n const value = override[key];\r\n if (\r\n value !== undefined &&\r\n typeof result[key] === \"object\" &&\r\n result[key] !== null &&\r\n typeof value === \"object\" &&\r\n value !== null &&\r\n !Array.isArray(value)\r\n ) {\r\n result[key] = deepMerge(\r\n result[key] as Record<string, unknown>,\r\n value as Record<string, unknown>\r\n ) as T[keyof T];\r\n } else if (value !== undefined) {\r\n result[key] = value as T[keyof T];\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\nexport function loadConfig(path?: string): SkilloConfig {\r\n const configPath = path || getConfigFile();\r\n\r\n if (!existsSync(configPath)) {\r\n return getDefaultConfig();\r\n }\r\n\r\n try {\r\n const content = readFileSync(configPath, \"utf-8\");\r\n const parsed = YAML.parse(content) || {};\r\n\r\n // Convert snake_case to camelCase for compatibility\r\n const converted = convertKeysToCamelCase(parsed);\r\n const defaultConfig = getDefaultConfig();\r\n\r\n // Deep merge with default config\r\n return deepMerge(defaultConfig, converted as Partial<SkilloConfig>);\r\n } catch {\r\n return getDefaultConfig();\r\n }\r\n}\r\n\r\nexport function saveConfig(config: SkilloConfig, path?: string): void {\r\n const configPath = path || getConfigFile();\r\n\r\n ensureDirectory(dirname(configPath));\r\n\r\n // Convert camelCase to snake_case for YAML file\r\n const converted = convertKeysToSnakeCase(config);\r\n\r\n const content = YAML.stringify(converted, {\r\n indent: 2,\r\n lineWidth: 0,\r\n });\r\n\r\n writeFileSync(configPath, content, \"utf-8\");\r\n}\r\n\r\nexport function getConfigValue(config: SkilloConfig, key: string): unknown {\r\n const keys = key.split(\".\");\r\n let current: unknown = config;\r\n\r\n for (const k of keys) {\r\n if (current && typeof current === \"object\" && k in current) {\r\n current = (current as Record<string, unknown>)[k];\r\n } else {\r\n return undefined;\r\n }\r\n }\r\n\r\n return current;\r\n}\r\n\r\nexport function setConfigValue(config: SkilloConfig, key: string, value: unknown): SkilloConfig {\r\n const keys = key.split(\".\");\r\n const result = JSON.parse(JSON.stringify(config)) as SkilloConfig;\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n let current: any = result;\r\n\r\n for (let i = 0; i < keys.length - 1; i++) {\r\n const k = keys[i];\r\n if (!(k in current)) {\r\n current[k] = {};\r\n }\r\n current = current[k] as Record<string, unknown>;\r\n }\r\n\r\n current[keys[keys.length - 1]] = value;\r\n return result;\r\n}\r\n\r\n// Helper functions for key conversion\r\nfunction toCamelCase(str: string): string {\r\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\r\n}\r\n\r\nfunction toSnakeCase(str: string): string {\r\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\r\n}\r\n\r\nfunction convertKeysToCamelCase(obj: unknown): unknown {\r\n if (Array.isArray(obj)) {\r\n return obj.map(convertKeysToCamelCase);\r\n }\r\n\r\n if (obj !== null && typeof obj === \"object\") {\r\n const result: Record<string, unknown> = {};\r\n for (const [key, value] of Object.entries(obj)) {\r\n result[toCamelCase(key)] = convertKeysToCamelCase(value);\r\n }\r\n return result;\r\n }\r\n\r\n return obj;\r\n}\r\n\r\nfunction convertKeysToSnakeCase(obj: unknown): unknown {\r\n if (Array.isArray(obj)) {\r\n return obj.map(convertKeysToSnakeCase);\r\n }\r\n\r\n if (obj !== null && typeof obj === \"object\") {\r\n const result: Record<string, unknown> = {};\r\n for (const [key, value] of Object.entries(obj)) {\r\n result[toSnakeCase(key)] = convertKeysToSnakeCase(value);\r\n }\r\n return result;\r\n }\r\n\r\n return obj;\r\n}\r\n"],"mappings":"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","names":[]}
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  loadConfig,
4
4
  saveConfig
5
- } from "./chunk-CPL3P2OF.js";
5
+ } from "./chunk-QUXHHRRK.js";
6
6
 
7
7
  // src/core/api-client.ts
8
8
  var DEFAULT_API_URL = "https://www.skillo.one/api/cli";
@@ -65,6 +65,20 @@ var ApiClient = class {
65
65
  saveConfig(config);
66
66
  this.baseUrl = url;
67
67
  }
68
+ /**
69
+ * Safely parse JSON from a fetch response, returning an error ApiResponse on failure.
70
+ */
71
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
72
+ async parseJsonResponse(response) {
73
+ try {
74
+ return { data: await response.json() };
75
+ } catch {
76
+ return {
77
+ success: false,
78
+ error: `Server returned non-JSON response (status ${response.status})`
79
+ };
80
+ }
81
+ }
68
82
  /**
69
83
  * Make an authenticated API request
70
84
  */
@@ -83,7 +97,9 @@ var ApiClient = class {
83
97
  ...options,
84
98
  headers
85
99
  });
86
- const data = await response.json();
100
+ const parsed = await this.parseJsonResponse(response);
101
+ if ("success" in parsed) return parsed;
102
+ const { data } = parsed;
87
103
  if (process.env.SKILLO_DEBUG) {
88
104
  console.log("[DEBUG] Request:", url);
89
105
  console.log("[DEBUG] Status:", response.status);
@@ -204,7 +220,9 @@ var ApiClient = class {
204
220
  headers: { "Content-Type": "application/json" },
205
221
  body: JSON.stringify({ device_name: deviceName })
206
222
  });
207
- const data = await response.json();
223
+ const parsed = await this.parseJsonResponse(response);
224
+ if ("success" in parsed) return parsed;
225
+ const { data } = parsed;
208
226
  if (!response.ok) {
209
227
  return {
210
228
  success: false,
@@ -232,7 +250,9 @@ var ApiClient = class {
232
250
  const url = `${this.baseUrl}/token?code=${encodeURIComponent(code)}`;
233
251
  try {
234
252
  const response = await fetch(url, { method: "GET" });
235
- const data = await response.json();
253
+ const parsed = await this.parseJsonResponse(response);
254
+ if ("success" in parsed) return parsed;
255
+ const { data } = parsed;
236
256
  if (!response.ok) {
237
257
  return {
238
258
  success: false,
@@ -258,7 +278,9 @@ var ApiClient = class {
258
278
  headers: { "Content-Type": "application/json" },
259
279
  body: JSON.stringify({ code, device_name: deviceName })
260
280
  });
261
- const data = await response.json();
281
+ const parsed = await this.parseJsonResponse(response);
282
+ if ("success" in parsed) return parsed;
283
+ const { data } = parsed;
262
284
  if (!response.ok) {
263
285
  return {
264
286
  success: false,
@@ -302,6 +324,42 @@ var ApiClient = class {
302
324
  });
303
325
  }
304
326
  // ============================================================================
327
+ // SKILL INSTALLATION
328
+ // ============================================================================
329
+ /**
330
+ * Get pending skill installs/uninstalls (daemon polling)
331
+ */
332
+ async getPendingInstalls() {
333
+ return this.request("/skills/pending-installs", { method: "GET" });
334
+ }
335
+ /**
336
+ * Report install/uninstall completion (daemon)
337
+ */
338
+ async reportInstallComplete(subscriptionId, action) {
339
+ return this.request("/skills/install-complete", {
340
+ method: "POST",
341
+ body: JSON.stringify({ subscriptionId, action })
342
+ });
343
+ }
344
+ /**
345
+ * Install a skill by slug (CLI direct install)
346
+ */
347
+ async installSkill(slug) {
348
+ return this.request("/skills/install", {
349
+ method: "POST",
350
+ body: JSON.stringify({ slug })
351
+ });
352
+ }
353
+ /**
354
+ * Uninstall a skill by slug (CLI)
355
+ */
356
+ async uninstallSkill(slug) {
357
+ return this.request("/skills/uninstall", {
358
+ method: "POST",
359
+ body: JSON.stringify({ slug })
360
+ });
361
+ }
362
+ // ============================================================================
305
363
  // PROJECT TRACKING
306
364
  // ============================================================================
307
365
  /**
@@ -316,16 +374,20 @@ var ApiClient = class {
316
374
  /**
317
375
  * Disconnect a project from tracking
318
376
  */
319
- async disconnectProject(path) {
320
- return this.request(`/projects/connect?path=${encodeURIComponent(path)}`, {
377
+ async disconnectProject(path, gitRemoteNormalized) {
378
+ const params = new URLSearchParams({ path });
379
+ if (gitRemoteNormalized) params.set("gitRemoteNormalized", gitRemoteNormalized);
380
+ return this.request(`/projects/connect?${params.toString()}`, {
321
381
  method: "DELETE"
322
382
  });
323
383
  }
324
384
  /**
325
385
  * Get tracking status for a project
326
386
  */
327
- async getProjectStatus(path) {
328
- return this.request(`/projects/connect?path=${encodeURIComponent(path)}`, {
387
+ async getProjectStatus(path, gitRemoteNormalized) {
388
+ const params = new URLSearchParams({ path });
389
+ if (gitRemoteNormalized) params.set("gitRemoteNormalized", gitRemoteNormalized);
390
+ return this.request(`/projects/connect?${params.toString()}`, {
329
391
  method: "GET"
330
392
  });
331
393
  }
@@ -368,4 +430,4 @@ export {
368
430
  ApiClient,
369
431
  getApiClient
370
432
  };
371
- //# sourceMappingURL=chunk-ODOZM4QV.js.map
433
+ //# sourceMappingURL=chunk-RQ2SC5HW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/api-client.ts"],"sourcesContent":["/**\r\n * Skillo Platform API Client\r\n *\r\n * Handles communication with the Skillo platform API.\r\n */\r\n\r\nimport { loadConfig, saveConfig } from \"./config.js\";\r\n\r\nconst DEFAULT_API_URL = \"https://www.skillo.one/api/cli\";\r\n\r\ninterface ApiResponse<T = unknown> {\r\n success: boolean;\r\n data?: T;\r\n error?: string;\r\n message?: string;\r\n}\r\n\r\ninterface AuthResponse {\r\n success: boolean;\r\n user: {\r\n id: string;\r\n email: string;\r\n name: string;\r\n };\r\n}\r\n\r\ninterface SyncResponse {\r\n success: boolean;\r\n skills?: Array<{\r\n id: string;\r\n name: string;\r\n slug: string;\r\n description: string;\r\n content: string;\r\n commands: string[];\r\n }>;\r\n patterns?: Array<{\r\n id: string;\r\n name: string;\r\n description: string;\r\n commands: string[];\r\n status: string;\r\n }>;\r\n}\r\n\r\ninterface GenerateResponse {\r\n success: boolean;\r\n skill: {\r\n id: string;\r\n name: string;\r\n slug: string;\r\n description: string;\r\n content: string;\r\n commands: string[];\r\n };\r\n}\r\n\r\ninterface DeviceAuthResponse {\r\n code: string;\r\n verification_url: string;\r\n expires_in: number;\r\n interval: number;\r\n}\r\n\r\ninterface TokenStatusResponse {\r\n status: \"pending\" | \"ready\" | \"expired\" | \"used\" | \"not_found\";\r\n}\r\n\r\ninterface TokenExchangeResponse {\r\n success: boolean;\r\n api_key: string;\r\n user: {\r\n id: string;\r\n };\r\n}\r\n\r\nexport class ApiClient {\r\n private baseUrl: string;\r\n private apiKey: string | null;\r\n\r\n constructor() {\r\n const config = loadConfig();\r\n this.baseUrl = config.api?.baseUrl || DEFAULT_API_URL;\r\n this.apiKey = this.loadApiKey();\r\n }\r\n\r\n /**\r\n * Load API key from config\r\n */\r\n private loadApiKey(): string | null {\r\n const config = loadConfig();\r\n return (config as { apiKey?: string }).apiKey || null;\r\n }\r\n\r\n /**\r\n * Save API key to config\r\n */\r\n saveApiKey(key: string): void {\r\n const config = loadConfig();\r\n (config as { apiKey?: string }).apiKey = key;\r\n saveConfig(config);\r\n this.apiKey = key;\r\n }\r\n\r\n /**\r\n * Clear API key from config\r\n */\r\n clearApiKey(): void {\r\n const config = loadConfig();\r\n delete (config as { apiKey?: string }).apiKey;\r\n saveConfig(config);\r\n this.apiKey = null;\r\n }\r\n\r\n /**\r\n * Check if API key is configured\r\n */\r\n hasApiKey(): boolean {\r\n return !!this.apiKey;\r\n }\r\n\r\n /**\r\n * Get the configured API key (masked)\r\n */\r\n getMaskedApiKey(): string | null {\r\n if (!this.apiKey) return null;\r\n return `${this.apiKey.substring(0, 12)}...${this.apiKey.substring(this.apiKey.length - 4)}`;\r\n }\r\n\r\n /**\r\n * Set the API base URL\r\n */\r\n setBaseUrl(url: string): void {\r\n const config = loadConfig();\r\n if (!config.api) {\r\n (config as { api?: { baseUrl: string } }).api = { baseUrl: url };\r\n } else {\r\n config.api.baseUrl = url;\r\n }\r\n saveConfig(config);\r\n this.baseUrl = url;\r\n }\r\n\r\n /**\r\n * Safely parse JSON from a fetch response, returning an error ApiResponse on failure.\r\n */\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n private async parseJsonResponse<T>(response: Response): Promise<{ data: any } | ApiResponse<T>> {\r\n try {\r\n return { data: await response.json() };\r\n } catch {\r\n return {\r\n success: false,\r\n error: `Server returned non-JSON response (status ${response.status})`,\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Make an authenticated API request\r\n */\r\n private async request<T>(\r\n endpoint: string,\r\n options: RequestInit = {}\r\n ): Promise<ApiResponse<T>> {\r\n if (!this.apiKey) {\r\n return { success: false, error: \"No API key configured. Run 'skillo login' first.\" };\r\n }\r\n\r\n const url = `${this.baseUrl}${endpoint}`;\r\n const headers: HeadersInit = {\r\n \"Content-Type\": \"application/json\",\r\n Authorization: `Bearer ${this.apiKey}`,\r\n ...options.headers,\r\n };\r\n\r\n try {\r\n const response = await fetch(url, {\r\n ...options,\r\n headers,\r\n });\r\n\r\n const parsed = await this.parseJsonResponse<T>(response);\r\n if (\"success\" in parsed) return parsed;\r\n const { data } = parsed;\r\n\r\n if (process.env.SKILLO_DEBUG) {\r\n console.log('[DEBUG] Request:', url);\r\n console.log('[DEBUG] Status:', response.status);\r\n console.log('[DEBUG] Response:', JSON.stringify(data));\r\n }\r\n\r\n if (!response.ok) {\r\n return {\r\n success: false,\r\n error: (data.error as string) || `Request failed with status ${response.status}`,\r\n };\r\n }\r\n\r\n // If the response already has success/data structure, return as-is\r\n // Otherwise wrap the data\r\n if (data.success !== undefined && data.data !== undefined) {\r\n return { success: data.success as boolean, data: data.data as T, message: data.message as string };\r\n }\r\n\r\n // If response just has success field (no data wrapper), return as-is\r\n if (data.success !== undefined) {\r\n return data as ApiResponse<T>;\r\n }\r\n\r\n // Legacy: wrap raw data\r\n return { success: true, data: data as T };\r\n } catch (error) {\r\n if (process.env.SKILLO_DEBUG) {\r\n console.log('[DEBUG] Error:', error);\r\n }\r\n if (error instanceof Error) {\r\n if (error.message.includes(\"ECONNREFUSED\")) {\r\n return {\r\n success: false,\r\n error: \"Cannot connect to Skillo platform. Is the server running?\",\r\n };\r\n }\r\n return { success: false, error: error.message };\r\n }\r\n return { success: false, error: \"Unknown error occurred\" };\r\n }\r\n }\r\n\r\n /**\r\n * Authenticate with the platform\r\n */\r\n async authenticate(): Promise<ApiResponse<AuthResponse>> {\r\n return this.request<AuthResponse>(\"/auth\", { method: \"POST\" });\r\n }\r\n\r\n /**\r\n * Sync commands to platform\r\n */\r\n async syncCommands(\r\n commands: Array<{\r\n timestamp: string;\r\n command: string;\r\n normalized: string;\r\n cwd: string;\r\n exitCode?: number | null;\r\n durationMs?: number | null;\r\n sessionId?: string;\r\n variables?: Record<string, string> | null;\r\n }>\r\n ): Promise<ApiResponse> {\r\n return this.request(\"/sync\", {\r\n method: \"POST\",\r\n body: JSON.stringify({\r\n type: \"commands\",\r\n data: commands,\r\n }),\r\n });\r\n }\r\n\r\n /**\r\n * Sync a pattern to platform\r\n */\r\n async syncPattern(pattern: {\r\n sourceType: string;\r\n name?: string;\r\n description?: string;\r\n commands: string[];\r\n category?: string;\r\n frequency?: number;\r\n score?: number;\r\n firstSeen?: string;\r\n lastSeen?: string;\r\n context?: Record<string, unknown>;\r\n }): Promise<ApiResponse<{ pattern: { id: string; name: string; status: string } }>> {\r\n return this.request(\"/sync\", {\r\n method: \"POST\",\r\n body: JSON.stringify({\r\n type: \"pattern\",\r\n data: pattern,\r\n }),\r\n });\r\n }\r\n\r\n /**\r\n * Create a session on the platform\r\n */\r\n async createSession(session: {\r\n startedAt: string;\r\n shell: string;\r\n endedAt?: string;\r\n commandCount?: number;\r\n }): Promise<ApiResponse<{ session: { id: string } }>> {\r\n return this.request(\"/sync\", {\r\n method: \"POST\",\r\n body: JSON.stringify({\r\n type: \"session\",\r\n data: session,\r\n }),\r\n });\r\n }\r\n\r\n /**\r\n * End a session on the platform\r\n */\r\n async endSession(sessionId: string): Promise<ApiResponse<{ success: boolean }>> {\r\n return this.request(\"/sessions\", {\r\n method: \"PATCH\",\r\n body: JSON.stringify({ sessionId }),\r\n });\r\n }\r\n\r\n /**\r\n * Start a new session on the platform (for standalone terminals)\r\n */\r\n async startSession(shell: string, projectPath?: string): Promise<ApiResponse<{ sessionId: string }>> {\r\n return this.request(\"/sessions\", {\r\n method: \"POST\",\r\n body: JSON.stringify({ shell, projectPath }),\r\n });\r\n }\r\n\r\n /**\r\n * Download skills and patterns from platform\r\n */\r\n async downloadData(type: \"all\" | \"skills\" | \"patterns\" = \"all\"): Promise<ApiResponse<SyncResponse>> {\r\n return this.request<SyncResponse>(`/sync?type=${type}`, { method: \"GET\" });\r\n }\r\n\r\n /**\r\n * Generate a skill from a pattern\r\n */\r\n async generateSkill(options: {\r\n patternId?: string;\r\n commands?: string[];\r\n name?: string;\r\n description?: string;\r\n category?: string;\r\n context?: Record<string, unknown>;\r\n }): Promise<ApiResponse<GenerateResponse>> {\r\n return this.request<GenerateResponse>(\"/generate\", {\r\n method: \"POST\",\r\n body: JSON.stringify(options),\r\n });\r\n }\r\n\r\n /**\r\n * Start device authorization flow (no auth required)\r\n */\r\n async startDeviceAuth(deviceName?: string): Promise<ApiResponse<DeviceAuthResponse>> {\r\n const url = `${this.baseUrl}/auth/device`;\r\n try {\r\n const response = await fetch(url, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({ device_name: deviceName }),\r\n });\r\n\r\n const parsed = await this.parseJsonResponse<DeviceAuthResponse>(response);\r\n if (\"success\" in parsed) return parsed;\r\n const { data } = parsed;\r\n\r\n if (!response.ok) {\r\n return {\r\n success: false,\r\n error: data.error || `Request failed with status ${response.status}`,\r\n };\r\n }\r\n\r\n return { success: true, data };\r\n } catch (error) {\r\n if (error instanceof Error) {\r\n if (error.message.includes(\"ECONNREFUSED\")) {\r\n return {\r\n success: false,\r\n error: \"Cannot connect to Skillo platform. Is the server running?\",\r\n };\r\n }\r\n return { success: false, error: error.message };\r\n }\r\n return { success: false, error: \"Unknown error occurred\" };\r\n }\r\n }\r\n\r\n /**\r\n * Check token status (polling)\r\n */\r\n async checkTokenStatus(code: string): Promise<ApiResponse<TokenStatusResponse>> {\r\n const url = `${this.baseUrl}/token?code=${encodeURIComponent(code)}`;\r\n try {\r\n const response = await fetch(url, { method: \"GET\" });\r\n\r\n const parsed = await this.parseJsonResponse<TokenStatusResponse>(response);\r\n if (\"success\" in parsed) return parsed;\r\n const { data } = parsed;\r\n\r\n if (!response.ok) {\r\n return {\r\n success: false,\r\n error: data.error || `Request failed with status ${response.status}`,\r\n };\r\n }\r\n\r\n return { success: true, data };\r\n } catch (error) {\r\n if (error instanceof Error) {\r\n return { success: false, error: error.message };\r\n }\r\n return { success: false, error: \"Unknown error occurred\" };\r\n }\r\n }\r\n\r\n /**\r\n * Exchange code for API key\r\n */\r\n async exchangeToken(code: string, deviceName?: string): Promise<ApiResponse<TokenExchangeResponse>> {\r\n const url = `${this.baseUrl}/token`;\r\n try {\r\n const response = await fetch(url, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({ code, device_name: deviceName }),\r\n });\r\n\r\n const parsed = await this.parseJsonResponse<TokenExchangeResponse>(response);\r\n if (\"success\" in parsed) return parsed;\r\n const { data } = parsed;\r\n\r\n if (!response.ok) {\r\n return {\r\n success: false,\r\n error: data.error || `Request failed with status ${response.status}`,\r\n };\r\n }\r\n\r\n return { success: true, data };\r\n } catch (error) {\r\n if (error instanceof Error) {\r\n return { success: false, error: error.message };\r\n }\r\n return { success: false, error: \"Unknown error occurred\" };\r\n }\r\n }\r\n\r\n /**\r\n * Sync Claude Code prompts to platform\r\n */\r\n async syncClaudePrompts(\r\n prompts: Array<{\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 options?: {\r\n terminalSessionId?: string;\r\n projectPath?: string;\r\n }\r\n ): Promise<ApiResponse<{\r\n sessionsCreated: number;\r\n promptsCreated: number;\r\n promptsSkipped: number;\r\n }>> {\r\n return this.request(\"/claude\", {\r\n method: \"POST\",\r\n body: JSON.stringify({\r\n prompts,\r\n terminalSessionId: options?.terminalSessionId,\r\n projectPath: options?.projectPath,\r\n }),\r\n });\r\n }\r\n\r\n /**\r\n * Get Claude Code sessions from platform\r\n */\r\n async getClaudeSessions(limit = 20): Promise<ApiResponse<{\r\n sessions: Array<{\r\n id: string;\r\n claudeSessionId: string;\r\n projectPath: string;\r\n projectName: string;\r\n startedAt: string;\r\n endedAt?: string;\r\n promptCount: number;\r\n prompts?: Array<{\r\n id: string;\r\n prompt: string;\r\n timestamp: string;\r\n category?: string;\r\n }>;\r\n }>;\r\n }>> {\r\n return this.request(`/claude?limit=${limit}`, { method: \"GET\" });\r\n }\r\n\r\n /**\r\n * Report skill usage detections to platform\r\n */\r\n async reportSkillUsage(\r\n usages: Array<{\r\n skillSlug: string;\r\n claudeSessionId: string;\r\n projectPath: string;\r\n timestamp: string;\r\n }>\r\n ): Promise<ApiResponse<{ logged: number }>> {\r\n return this.request(\"/skill-usage\", {\r\n method: \"POST\",\r\n body: JSON.stringify({ usages }),\r\n });\r\n }\r\n\r\n // ============================================================================\r\n // SKILL INSTALLATION\r\n // ============================================================================\r\n\r\n /**\r\n * Get pending skill installs/uninstalls (daemon polling)\r\n */\r\n async getPendingInstalls(): Promise<ApiResponse<{\r\n pendingInstalls: Array<{\r\n subscriptionId: string;\r\n slug: string;\r\n content: string;\r\n }>;\r\n pendingUninstalls: Array<{\r\n subscriptionId: string;\r\n slug: string;\r\n }>;\r\n }>> {\r\n return this.request(\"/skills/pending-installs\", { method: \"GET\" });\r\n }\r\n\r\n /**\r\n * Report install/uninstall completion (daemon)\r\n */\r\n async reportInstallComplete(\r\n subscriptionId: string,\r\n action: \"installed\" | \"uninstalled\"\r\n ): Promise<ApiResponse<{ ok: boolean }>> {\r\n return this.request(\"/skills/install-complete\", {\r\n method: \"POST\",\r\n body: JSON.stringify({ subscriptionId, action }),\r\n });\r\n }\r\n\r\n /**\r\n * Install a skill by slug (CLI direct install)\r\n */\r\n async installSkill(slug: string): Promise<ApiResponse<{\r\n subscription: { id: string };\r\n slug: string;\r\n name: string;\r\n content: string;\r\n }>> {\r\n return this.request(\"/skills/install\", {\r\n method: \"POST\",\r\n body: JSON.stringify({ slug }),\r\n });\r\n }\r\n\r\n /**\r\n * Uninstall a skill by slug (CLI)\r\n */\r\n async uninstallSkill(slug: string): Promise<ApiResponse<{ ok: boolean }>> {\r\n return this.request(\"/skills/uninstall\", {\r\n method: \"POST\",\r\n body: JSON.stringify({ slug }),\r\n });\r\n }\r\n\r\n // ============================================================================\r\n // PROJECT TRACKING\r\n // ============================================================================\r\n\r\n /**\r\n * Connect a project for tracking\r\n */\r\n async connectProject(params: {\r\n path: string;\r\n name?: string;\r\n gitRemote?: string;\r\n gitRemoteNormalized?: string;\r\n language?: string;\r\n framework?: string;\r\n }): Promise<ApiResponse<{\r\n id: string;\r\n name: string;\r\n path: string;\r\n trackingEnabled: boolean;\r\n connectedAt: string;\r\n }>> {\r\n return this.request(\"/projects/connect\", {\r\n method: \"POST\",\r\n body: JSON.stringify(params),\r\n });\r\n }\r\n\r\n /**\r\n * Disconnect a project from tracking\r\n */\r\n async disconnectProject(path: string, gitRemoteNormalized?: string): Promise<ApiResponse<{\r\n id: string;\r\n name: string;\r\n trackingEnabled: boolean;\r\n }>> {\r\n const params = new URLSearchParams({ path });\r\n if (gitRemoteNormalized) params.set(\"gitRemoteNormalized\", gitRemoteNormalized);\r\n return this.request(`/projects/connect?${params.toString()}`, {\r\n method: \"DELETE\",\r\n });\r\n }\r\n\r\n /**\r\n * Get tracking status for a project\r\n */\r\n async getProjectStatus(path: string, gitRemoteNormalized?: string): Promise<ApiResponse<{\r\n connected: boolean;\r\n tracked: boolean;\r\n connectedAt?: string;\r\n project?: {\r\n id: string;\r\n name: string;\r\n path: string;\r\n gitRemote?: string;\r\n gitRemoteNormalized?: string;\r\n trackingEnabled: boolean;\r\n };\r\n }>> {\r\n const params = new URLSearchParams({ path });\r\n if (gitRemoteNormalized) params.set(\"gitRemoteNormalized\", gitRemoteNormalized);\r\n return this.request(`/projects/connect?${params.toString()}`, {\r\n method: \"GET\",\r\n });\r\n }\r\n\r\n /**\r\n * List all tracked projects\r\n */\r\n async listProjects(includeDisabled = false): Promise<ApiResponse<{\r\n projects: Array<{\r\n id: string;\r\n name: string;\r\n path: string;\r\n gitRemote?: string;\r\n gitRemoteNormalized?: string;\r\n trackingEnabled: boolean;\r\n connectedAt?: string;\r\n }>;\r\n totalTracked: number;\r\n totalProjects: number;\r\n }>> {\r\n return this.request(`/projects/tracked?includeDisabled=${includeDisabled}`, {\r\n method: \"GET\",\r\n });\r\n }\r\n\r\n /**\r\n * Check if a path is in a tracked project\r\n * Returns the project if tracked, null if not\r\n */\r\n async isProjectTracked(path: string): Promise<{\r\n tracked: boolean;\r\n project?: {\r\n id: string;\r\n name: string;\r\n path: string;\r\n };\r\n }> {\r\n const result = await this.getProjectStatus(path);\r\n if (result.success && result.data?.connected) {\r\n return {\r\n tracked: true,\r\n project: result.data.project ? {\r\n id: result.data.project.id,\r\n name: result.data.project.name,\r\n path: result.data.project.path,\r\n } : undefined,\r\n };\r\n }\r\n return { tracked: false };\r\n }\r\n}\r\n\r\n// Singleton instance\r\nlet clientInstance: ApiClient | null = null;\r\n\r\nexport function getApiClient(): ApiClient {\r\n if (!clientInstance) {\r\n clientInstance = new ApiClient();\r\n }\r\n return clientInstance;\r\n}\r\n"],"mappings":";;;;;;;AAQA,IAAM,kBAAkB;AAoEjB,IAAM,YAAN,MAAgB;AAAA,EACb;AAAA,EACA;AAAA,EAER,cAAc;AACZ,UAAM,SAAS,WAAW;AAC1B,SAAK,UAAU,OAAO,KAAK,WAAW;AACtC,SAAK,SAAS,KAAK,WAAW;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKQ,aAA4B;AAClC,UAAM,SAAS,WAAW;AAC1B,WAAQ,OAA+B,UAAU;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,KAAmB;AAC5B,UAAM,SAAS,WAAW;AAC1B,IAAC,OAA+B,SAAS;AACzC,eAAW,MAAM;AACjB,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAKA,cAAoB;AAClB,UAAM,SAAS,WAAW;AAC1B,WAAQ,OAA+B;AACvC,eAAW,MAAM;AACjB,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAKA,YAAqB;AACnB,WAAO,CAAC,CAAC,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAKA,kBAAiC;AAC/B,QAAI,CAAC,KAAK,OAAQ,QAAO;AACzB,WAAO,GAAG,KAAK,OAAO,UAAU,GAAG,EAAE,CAAC,MAAM,KAAK,OAAO,UAAU,KAAK,OAAO,SAAS,CAAC,CAAC;AAAA,EAC3F;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,KAAmB;AAC5B,UAAM,SAAS,WAAW;AAC1B,QAAI,CAAC,OAAO,KAAK;AACf,MAAC,OAAyC,MAAM,EAAE,SAAS,IAAI;AAAA,IACjE,OAAO;AACL,aAAO,IAAI,UAAU;AAAA,IACvB;AACA,eAAW,MAAM;AACjB,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,kBAAqB,UAA6D;AAC9F,QAAI;AACF,aAAO,EAAE,MAAM,MAAM,SAAS,KAAK,EAAE;AAAA,IACvC,QAAQ;AACN,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,6CAA6C,SAAS,MAAM;AAAA,MACrE;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,QACZ,UACA,UAAuB,CAAC,GACC;AACzB,QAAI,CAAC,KAAK,QAAQ;AAChB,aAAO,EAAE,SAAS,OAAO,OAAO,mDAAmD;AAAA,IACrF;AAEA,UAAM,MAAM,GAAG,KAAK,OAAO,GAAG,QAAQ;AACtC,UAAM,UAAuB;AAAA,MAC3B,gBAAgB;AAAA,MAChB,eAAe,UAAU,KAAK,MAAM;AAAA,MACpC,GAAG,QAAQ;AAAA,IACb;AAEA,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC,GAAG;AAAA,QACH;AAAA,MACF,CAAC;AAED,YAAM,SAAS,MAAM,KAAK,kBAAqB,QAAQ;AACvD,UAAI,aAAa,OAAQ,QAAO;AAChC,YAAM,EAAE,KAAK,IAAI;AAEjB,UAAI,QAAQ,IAAI,cAAc;AAC5B,gBAAQ,IAAI,oBAAoB,GAAG;AACnC,gBAAQ,IAAI,mBAAmB,SAAS,MAAM;AAC9C,gBAAQ,IAAI,qBAAqB,KAAK,UAAU,IAAI,CAAC;AAAA,MACvD;AAEA,UAAI,CAAC,SAAS,IAAI;AAChB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,OAAQ,KAAK,SAAoB,8BAA8B,SAAS,MAAM;AAAA,QAChF;AAAA,MACF;AAIA,UAAI,KAAK,YAAY,UAAa,KAAK,SAAS,QAAW;AACzD,eAAO,EAAE,SAAS,KAAK,SAAoB,MAAM,KAAK,MAAW,SAAS,KAAK,QAAkB;AAAA,MACnG;AAGA,UAAI,KAAK,YAAY,QAAW;AAC9B,eAAO;AAAA,MACT;AAGA,aAAO,EAAE,SAAS,MAAM,KAAgB;AAAA,IAC1C,SAAS,OAAO;AACd,UAAI,QAAQ,IAAI,cAAc;AAC5B,gBAAQ,IAAI,kBAAkB,KAAK;AAAA,MACrC;AACA,UAAI,iBAAiB,OAAO;AAC1B,YAAI,MAAM,QAAQ,SAAS,cAAc,GAAG;AAC1C,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,OAAO;AAAA,UACT;AAAA,QACF;AACA,eAAO,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ;AAAA,MAChD;AACA,aAAO,EAAE,SAAS,OAAO,OAAO,yBAAyB;AAAA,IAC3D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAmD;AACvD,WAAO,KAAK,QAAsB,SAAS,EAAE,QAAQ,OAAO,CAAC;AAAA,EAC/D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aACJ,UAUsB;AACtB,WAAO,KAAK,QAAQ,SAAS;AAAA,MAC3B,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU;AAAA,QACnB,MAAM;AAAA,QACN,MAAM;AAAA,MACR,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,SAWkE;AAClF,WAAO,KAAK,QAAQ,SAAS;AAAA,MAC3B,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU;AAAA,QACnB,MAAM;AAAA,QACN,MAAM;AAAA,MACR,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,SAKkC;AACpD,WAAO,KAAK,QAAQ,SAAS;AAAA,MAC3B,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU;AAAA,QACnB,MAAM;AAAA,QACN,MAAM;AAAA,MACR,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAW,WAA+D;AAC9E,WAAO,KAAK,QAAQ,aAAa;AAAA,MAC/B,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,UAAU,CAAC;AAAA,IACpC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,OAAe,aAAmE;AACnG,WAAO,KAAK,QAAQ,aAAa;AAAA,MAC/B,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,OAAO,YAAY,CAAC;AAAA,IAC7C,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,OAAsC,OAA2C;AAClG,WAAO,KAAK,QAAsB,cAAc,IAAI,IAAI,EAAE,QAAQ,MAAM,CAAC;AAAA,EAC3E;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,SAOuB;AACzC,WAAO,KAAK,QAA0B,aAAa;AAAA,MACjD,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,OAAO;AAAA,IAC9B,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,YAA+D;AACnF,UAAM,MAAM,GAAG,KAAK,OAAO;AAC3B,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,aAAa,WAAW,CAAC;AAAA,MAClD,CAAC;AAED,YAAM,SAAS,MAAM,KAAK,kBAAsC,QAAQ;AACxE,UAAI,aAAa,OAAQ,QAAO;AAChC,YAAM,EAAE,KAAK,IAAI;AAEjB,UAAI,CAAC,SAAS,IAAI;AAChB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,OAAO,KAAK,SAAS,8BAA8B,SAAS,MAAM;AAAA,QACpE;AAAA,MACF;AAEA,aAAO,EAAE,SAAS,MAAM,KAAK;AAAA,IAC/B,SAAS,OAAO;AACd,UAAI,iBAAiB,OAAO;AAC1B,YAAI,MAAM,QAAQ,SAAS,cAAc,GAAG;AAC1C,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,OAAO;AAAA,UACT;AAAA,QACF;AACA,eAAO,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ;AAAA,MAChD;AACA,aAAO,EAAE,SAAS,OAAO,OAAO,yBAAyB;AAAA,IAC3D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAiB,MAAyD;AAC9E,UAAM,MAAM,GAAG,KAAK,OAAO,eAAe,mBAAmB,IAAI,CAAC;AAClE,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,KAAK,EAAE,QAAQ,MAAM,CAAC;AAEnD,YAAM,SAAS,MAAM,KAAK,kBAAuC,QAAQ;AACzE,UAAI,aAAa,OAAQ,QAAO;AAChC,YAAM,EAAE,KAAK,IAAI;AAEjB,UAAI,CAAC,SAAS,IAAI;AAChB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,OAAO,KAAK,SAAS,8BAA8B,SAAS,MAAM;AAAA,QACpE;AAAA,MACF;AAEA,aAAO,EAAE,SAAS,MAAM,KAAK;AAAA,IAC/B,SAAS,OAAO;AACd,UAAI,iBAAiB,OAAO;AAC1B,eAAO,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ;AAAA,MAChD;AACA,aAAO,EAAE,SAAS,OAAO,OAAO,yBAAyB;AAAA,IAC3D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,MAAc,YAAkE;AAClG,UAAM,MAAM,GAAG,KAAK,OAAO;AAC3B,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,MAAM,aAAa,WAAW,CAAC;AAAA,MACxD,CAAC;AAED,YAAM,SAAS,MAAM,KAAK,kBAAyC,QAAQ;AAC3E,UAAI,aAAa,OAAQ,QAAO;AAChC,YAAM,EAAE,KAAK,IAAI;AAEjB,UAAI,CAAC,SAAS,IAAI;AAChB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,OAAO,KAAK,SAAS,8BAA8B,SAAS,MAAM;AAAA,QACpE;AAAA,MACF;AAEA,aAAO,EAAE,SAAS,MAAM,KAAK;AAAA,IAC/B,SAAS,OAAO;AACd,UAAI,iBAAiB,OAAO;AAC1B,eAAO,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ;AAAA,MAChD;AACA,aAAO,EAAE,SAAS,OAAO,OAAO,yBAAyB;AAAA,IAC3D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,kBACJ,SASA,SAQE;AACF,WAAO,KAAK,QAAQ,WAAW;AAAA,MAC7B,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU;AAAA,QACnB;AAAA,QACA,mBAAmB,SAAS;AAAA,QAC5B,aAAa,SAAS;AAAA,MACxB,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,kBAAkB,QAAQ,IAgB5B;AACF,WAAO,KAAK,QAAQ,iBAAiB,KAAK,IAAI,EAAE,QAAQ,MAAM,CAAC;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBACJ,QAM0C;AAC1C,WAAO,KAAK,QAAQ,gBAAgB;AAAA,MAClC,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,OAAO,CAAC;AAAA,IACjC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,qBAUF;AACF,WAAO,KAAK,QAAQ,4BAA4B,EAAE,QAAQ,MAAM,CAAC;AAAA,EACnE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,sBACJ,gBACA,QACuC;AACvC,WAAO,KAAK,QAAQ,4BAA4B;AAAA,MAC9C,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,gBAAgB,OAAO,CAAC;AAAA,IACjD,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,MAKf;AACF,WAAO,KAAK,QAAQ,mBAAmB;AAAA,MACrC,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,KAAK,CAAC;AAAA,IAC/B,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAe,MAAqD;AACxE,WAAO,KAAK,QAAQ,qBAAqB;AAAA,MACvC,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,KAAK,CAAC;AAAA,IAC/B,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,eAAe,QAajB;AACF,WAAO,KAAK,QAAQ,qBAAqB;AAAA,MACvC,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,MAAM;AAAA,IAC7B,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,kBAAkB,MAAc,qBAIlC;AACF,UAAM,SAAS,IAAI,gBAAgB,EAAE,KAAK,CAAC;AAC3C,QAAI,oBAAqB,QAAO,IAAI,uBAAuB,mBAAmB;AAC9E,WAAO,KAAK,QAAQ,qBAAqB,OAAO,SAAS,CAAC,IAAI;AAAA,MAC5D,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAiB,MAAc,qBAYjC;AACF,UAAM,SAAS,IAAI,gBAAgB,EAAE,KAAK,CAAC;AAC3C,QAAI,oBAAqB,QAAO,IAAI,uBAAuB,mBAAmB;AAC9E,WAAO,KAAK,QAAQ,qBAAqB,OAAO,SAAS,CAAC,IAAI;AAAA,MAC5D,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,kBAAkB,OAYjC;AACF,WAAO,KAAK,QAAQ,qCAAqC,eAAe,IAAI;AAAA,MAC1E,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,iBAAiB,MAOpB;AACD,UAAM,SAAS,MAAM,KAAK,iBAAiB,IAAI;AAC/C,QAAI,OAAO,WAAW,OAAO,MAAM,WAAW;AAC5C,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS,OAAO,KAAK,UAAU;AAAA,UAC7B,IAAI,OAAO,KAAK,QAAQ;AAAA,UACxB,MAAM,OAAO,KAAK,QAAQ;AAAA,UAC1B,MAAM,OAAO,KAAK,QAAQ;AAAA,QAC5B,IAAI;AAAA,MACN;AAAA,IACF;AACA,WAAO,EAAE,SAAS,MAAM;AAAA,EAC1B;AACF;AAGA,IAAI,iBAAmC;AAEhC,SAAS,eAA0B;AACxC,MAAI,CAAC,gBAAgB;AACnB,qBAAiB,IAAI,UAAU;AAAA,EACjC;AACA,SAAO;AACT;","names":[]}