skillo 0.2.6 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +198 -198
  2. package/dist/api-client-BF6GDR7Q.js +12 -0
  3. package/dist/{chunk-WJKZWKER.js → chunk-63FVALWX.js} +1 -1
  4. package/dist/chunk-63FVALWX.js.map +1 -0
  5. package/dist/chunk-6GOJPFZ7.js +113 -0
  6. package/dist/chunk-6GOJPFZ7.js.map +1 -0
  7. package/dist/chunk-6UGTWBUW.js +89 -0
  8. package/dist/chunk-6UGTWBUW.js.map +1 -0
  9. package/dist/{chunk-2CVEPT6U.js → chunk-73NUWYUO.js} +2 -2
  10. package/dist/chunk-73NUWYUO.js.map +1 -0
  11. package/dist/chunk-QIV4VIXA.js +221 -0
  12. package/dist/chunk-QIV4VIXA.js.map +1 -0
  13. package/dist/{chunk-CPL3P2OF.js → chunk-QUXHHRRK.js} +2 -2
  14. package/dist/chunk-QUXHHRRK.js.map +1 -0
  15. package/dist/{chunk-ODOZM4QV.js → chunk-RQ2SC5HW.js} +72 -10
  16. package/dist/chunk-RQ2SC5HW.js.map +1 -0
  17. package/dist/chunk-U53QWUOR.js +727 -0
  18. package/dist/chunk-U53QWUOR.js.map +1 -0
  19. package/dist/chunk-VAQ73XPE.js +68 -0
  20. package/dist/chunk-VAQ73XPE.js.map +1 -0
  21. package/dist/chunk-XLJGCOVT.js +975 -0
  22. package/dist/chunk-XLJGCOVT.js.map +1 -0
  23. package/dist/{claude-watcher-N6GN6WHJ.js → claude-watcher-WKGBJYKN.js} +65 -3
  24. package/dist/claude-watcher-WKGBJYKN.js.map +1 -0
  25. package/dist/cli.js +495 -1846
  26. package/dist/cli.js.map +1 -1
  27. package/dist/{config-P5EM5L7N.js → config-ZOKAP2LJ.js} +3 -3
  28. package/dist/daemon-6DTCMOJB.js +28 -0
  29. package/dist/daemon-runner.js +338 -71
  30. package/dist/daemon-runner.js.map +1 -1
  31. package/dist/database-KQY5OSCS.js +9 -0
  32. package/dist/git-OGUSYBJS.js +16 -0
  33. package/dist/git-OGUSYBJS.js.map +1 -0
  34. package/dist/git-OUAHIOY2.js +110 -0
  35. package/dist/git-OUAHIOY2.js.map +1 -0
  36. package/dist/index.js.map +1 -1
  37. package/dist/{paths-INOKEM66.js → paths-MPOZBOKE.js} +2 -2
  38. package/dist/paths-MPOZBOKE.js.map +1 -0
  39. package/dist/project-OFU2W6MH.js +19 -0
  40. package/dist/project-OFU2W6MH.js.map +1 -0
  41. package/dist/shell-NZABRJLA.js +16 -0
  42. package/dist/shell-NZABRJLA.js.map +1 -0
  43. package/dist/skill-installer-F67OAOQN.js +121 -0
  44. package/dist/skill-installer-F67OAOQN.js.map +1 -0
  45. package/dist/{skill-usage-detector-EO26MRYV.js → skill-usage-detector-MSW5VWQZ.js} +2 -2
  46. package/dist/skill-usage-detector-MSW5VWQZ.js.map +1 -0
  47. package/dist/tray-WKFGUUTO.js +346 -0
  48. package/dist/tray-WKFGUUTO.js.map +1 -0
  49. package/package.json +62 -63
  50. package/scripts/postinstall.mjs +415 -364
  51. package/scripts/tray-helper-darwin +0 -0
  52. package/scripts/tray-helper-darwin.swift +180 -180
  53. package/scripts/tray-helper-linux.py +322 -0
  54. package/scripts/tray-helper-windows.cs +322 -0
  55. package/dist/api-client-KUQW7FSC.js +0 -12
  56. package/dist/chunk-2CVEPT6U.js.map +0 -1
  57. package/dist/chunk-CPL3P2OF.js.map +0 -1
  58. package/dist/chunk-ODOZM4QV.js.map +0 -1
  59. package/dist/chunk-WJKZWKER.js.map +0 -1
  60. package/dist/claude-watcher-N6GN6WHJ.js.map +0 -1
  61. package/dist/database-F3BFFZKG.js +0 -9
  62. package/dist/skill-usage-detector-EO26MRYV.js.map +0 -1
  63. package/dist/tray-UCAI2U2C.js +0 -408
  64. package/dist/tray-UCAI2U2C.js.map +0 -1
  65. /package/dist/{api-client-KUQW7FSC.js.map → api-client-BF6GDR7Q.js.map} +0 -0
  66. /package/dist/{config-P5EM5L7N.js.map → config-ZOKAP2LJ.js.map} +0 -0
  67. /package/dist/{database-F3BFFZKG.js.map → daemon-6DTCMOJB.js.map} +0 -0
  68. /package/dist/{paths-INOKEM66.js.map → database-KQY5OSCS.js.map} +0 -0
@@ -1,408 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- ensureDirectory,
4
- getDataDir
5
- } from "./chunk-WJKZWKER.js";
6
-
7
- // src/tray/tray.ts
8
- import SysTrayModule from "systray2";
9
- import { readFileSync as readFileSync2, existsSync as existsSync2, writeFileSync as writeFileSync2, unlinkSync, chmodSync, readdirSync } from "fs";
10
- import { exec, execSync, spawn } from "child_process";
11
- import { join as join2, dirname } from "path";
12
- import { platform, homedir } from "os";
13
- import { fileURLToPath } from "url";
14
-
15
- // src/utils/status-writer.ts
16
- import { writeFileSync, readFileSync, existsSync } from "fs";
17
- import { join } from "path";
18
- var STATUS_FILE = "daemon-status.json";
19
- var WRITE_INTERVAL_MS = 1e4;
20
- var StatusWriter = class _StatusWriter {
21
- intervalId = null;
22
- status;
23
- filePath;
24
- constructor() {
25
- this.filePath = join(getDataDir(), STATUS_FILE);
26
- this.status = {
27
- running: true,
28
- pid: process.pid,
29
- startedAt: (/* @__PURE__ */ new Date()).toISOString(),
30
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
31
- claudeWatcher: { lastSync: null, promptsSynced: 0, lastError: null },
32
- skillDetector: { deployedSkills: 0, usagesDetected: 0, lastDetection: null, lastError: null },
33
- trackedProjects: [],
34
- activeSessions: 0
35
- };
36
- }
37
- /** Start periodic writes */
38
- start() {
39
- ensureDirectory(getDataDir());
40
- this.write();
41
- this.intervalId = setInterval(() => this.write(), WRITE_INTERVAL_MS);
42
- }
43
- /** Stop writing and mark as not running */
44
- stop() {
45
- if (this.intervalId) {
46
- clearInterval(this.intervalId);
47
- this.intervalId = null;
48
- }
49
- this.status.running = false;
50
- this.status.pid = null;
51
- this.status.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
52
- this.write();
53
- }
54
- /** Merge partial status updates */
55
- update(partial) {
56
- if (partial.claudeWatcher) {
57
- this.status.claudeWatcher = { ...this.status.claudeWatcher, ...partial.claudeWatcher };
58
- delete partial.claudeWatcher;
59
- }
60
- if (partial.skillDetector) {
61
- this.status.skillDetector = { ...this.status.skillDetector, ...partial.skillDetector };
62
- delete partial.skillDetector;
63
- }
64
- Object.assign(this.status, partial);
65
- }
66
- /** Get the status file path */
67
- static getStatusFilePath() {
68
- return join(getDataDir(), STATUS_FILE);
69
- }
70
- /** Read current status from disk (static, for tray/status commands) */
71
- static read() {
72
- const filePath = _StatusWriter.getStatusFilePath();
73
- try {
74
- if (existsSync(filePath)) {
75
- return JSON.parse(readFileSync(filePath, "utf-8"));
76
- }
77
- } catch {
78
- }
79
- return null;
80
- }
81
- write() {
82
- this.status.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
83
- try {
84
- writeFileSync(this.filePath, JSON.stringify(this.status, null, 2), "utf-8");
85
- } catch {
86
- }
87
- }
88
- };
89
-
90
- // src/tray/tray.ts
91
- var SysTray = SysTrayModule.default || SysTrayModule;
92
- var SEPARATOR = { title: "<SEPARATOR>", tooltip: "", enabled: true };
93
- var ICON_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAAXNSR0IArs4c6QAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAFqADAAQAAAABAAAAFgAAAAA/6RFgAAABm2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyI+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj4yMjwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj4yMjwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgolQXt+AAABg0lEQVQ4EaWUPyhFURzHH4/UK38KA5OBlGKQUWQRmQ1vMSmb2WKwSqKkUBaLUhabTfmTjMhiV0qKouTf59tz6nTeuefc637r07nn/H7f7zvvnntvsZBPndiXoQHu8kVV3G0MK/AGPzANudSMewleQIGG1MHDmFy1snAMJsweq4JrXTfzQZjwrD+xNg79sAufkFo1dF7BLZQiLv2AerXzqh273sm/RjVfQK/b4MzrmW9CNHiNJvvefTDX6TdCSE2homrbYAeb6wfWZ0C36l8q4zJhvvGcug43s/SUHIIv1Kx9Ud+AFsikIt0L8A4mzDc+Up8D9WdSF90H4Au11/TI9UFmjeG4BjvMvX6l3pM5GUMdzMMzuKFmvkUtUbETb8e5AzpAE2jGk8RUCmewDnqjQhqieAkmVONeyKBgNd3ASKiRmp6GRfgGeaYgUXoB7F0cMR9N7K4UVhlO3R4dSkjdFDtAL4525pO+hMHbIJPZ8T3X+jakefgHZIxpn4ZZiP2TWE7hF4oBfI7ofv98AAAAAElFTkSuQmCC";
94
- var TRAY_PID_FILE = "tray.pid";
95
- var STATUS_POLL_MS = 3e3;
96
- function formatTimeAgo(isoString) {
97
- if (!isoString) return "Never";
98
- const diff = Date.now() - new Date(isoString).getTime();
99
- const seconds = Math.floor(diff / 1e3);
100
- if (seconds < 60) return "Just now";
101
- const minutes = Math.floor(seconds / 60);
102
- if (minutes < 60) return `${minutes} min ago`;
103
- const hours = Math.floor(minutes / 60);
104
- if (hours < 24) return `${hours}h ago`;
105
- const days = Math.floor(hours / 24);
106
- return `${days}d ago`;
107
- }
108
- function readStatus() {
109
- return StatusWriter.read();
110
- }
111
- function buildMenu(status) {
112
- const running = status?.running ?? false;
113
- const statusIcon = running ? "\u25CF" : "\u25CB";
114
- const statusText = running ? "Running" : "Stopped";
115
- const projects = status?.trackedProjects ?? [];
116
- const projectItems = projects.length > 0 ? projects.map((p) => ({
117
- title: ` ${p.name}`,
118
- tooltip: p.path,
119
- enabled: false,
120
- checked: false
121
- })) : [{
122
- title: " (none)",
123
- tooltip: "No tracked projects",
124
- enabled: false,
125
- checked: false
126
- }];
127
- const items = [
128
- {
129
- title: `${statusIcon} Skillo \u2014 ${statusText}`,
130
- tooltip: "Daemon status",
131
- enabled: false,
132
- checked: false
133
- },
134
- SEPARATOR,
135
- {
136
- title: status?.user ? `Logged in as: ${status.user}` : "Not logged in",
137
- tooltip: "User info",
138
- enabled: false,
139
- checked: false
140
- },
141
- SEPARATOR,
142
- {
143
- title: `Tracked Projects (${projects.length})`,
144
- tooltip: "Projects being tracked",
145
- enabled: false,
146
- checked: false
147
- },
148
- ...projectItems,
149
- SEPARATOR,
150
- {
151
- title: `Last sync: ${formatTimeAgo(status?.claudeWatcher?.lastSync)}`,
152
- tooltip: "Last Claude conversation sync",
153
- enabled: false,
154
- checked: false
155
- },
156
- {
157
- title: `Prompts synced: ${status?.claudeWatcher?.promptsSynced ?? 0}`,
158
- tooltip: "Total prompts synced",
159
- enabled: false,
160
- checked: false
161
- },
162
- {
163
- title: `Skills detected: ${status?.skillDetector?.usagesDetected ?? 0}`,
164
- tooltip: "Skill usages detected",
165
- enabled: false,
166
- checked: false
167
- },
168
- SEPARATOR,
169
- {
170
- title: running ? "Stop Daemon" : "Start Daemon",
171
- tooltip: running ? "Stop the Skillo daemon" : "Start the Skillo daemon",
172
- enabled: true,
173
- checked: false
174
- },
175
- {
176
- title: "Open Dashboard",
177
- tooltip: "Open Skillo dashboard in browser",
178
- enabled: true,
179
- checked: false
180
- },
181
- SEPARATOR,
182
- {
183
- title: "Quit Tray",
184
- tooltip: "Hide tray icon (daemon keeps running)",
185
- enabled: true,
186
- checked: false
187
- }
188
- ];
189
- return {
190
- icon: ICON_BASE64,
191
- title: "",
192
- tooltip: `Skillo \u2014 ${statusText}`,
193
- items
194
- };
195
- }
196
- function openBrowser(url) {
197
- const os = platform();
198
- let cmd;
199
- if (os === "darwin") {
200
- cmd = `open "${url}"`;
201
- } else if (os === "win32") {
202
- cmd = `start "" "${url}"`;
203
- } else {
204
- cmd = `xdg-open "${url}"`;
205
- }
206
- exec(cmd, () => {
207
- });
208
- }
209
- function execSkillo(args) {
210
- exec(`skillo ${args}`, () => {
211
- });
212
- }
213
- function handleClick(title, cleanup) {
214
- if (title === "Stop Daemon" || title === "Start Daemon") {
215
- execSkillo(title === "Stop Daemon" ? "stop" : "start");
216
- } else if (title === "Open Dashboard") {
217
- openBrowser("https://www.skillo.one/dashboard");
218
- } else if (title === "Quit Tray") {
219
- cleanup();
220
- }
221
- }
222
- function findDarwinHelper() {
223
- const candidates = [
224
- join2(getDataDir(), "tray-helper-darwin"),
225
- join2(dirname(fileURLToPath(import.meta.url)), "..", "scripts", "tray-helper-darwin"),
226
- join2(dirname(fileURLToPath(import.meta.url)), "scripts", "tray-helper-darwin")
227
- ];
228
- for (const p of candidates) {
229
- if (existsSync2(p)) return p;
230
- }
231
- return null;
232
- }
233
- function compileDarwinHelper() {
234
- const srcCandidates = [
235
- join2(dirname(fileURLToPath(import.meta.url)), "..", "scripts", "tray-helper-darwin.swift"),
236
- join2(dirname(fileURLToPath(import.meta.url)), "scripts", "tray-helper-darwin.swift")
237
- ];
238
- let src = null;
239
- for (const p of srcCandidates) {
240
- if (existsSync2(p)) {
241
- src = p;
242
- break;
243
- }
244
- }
245
- if (!src) return null;
246
- const out = join2(getDataDir(), "tray-helper-darwin");
247
- try {
248
- execSync(`swiftc "${src}" -o "${out}" -O 2>/dev/null`, { timeout: 6e4 });
249
- chmodSync(out, 493);
250
- return out;
251
- } catch {
252
- return null;
253
- }
254
- }
255
- async function startDarwinTray(pidFile) {
256
- let helperPath = findDarwinHelper();
257
- if (!helperPath) {
258
- helperPath = compileDarwinHelper();
259
- }
260
- if (!helperPath) {
261
- await startSystray2Tray(pidFile);
262
- return;
263
- }
264
- const initialStatus = readStatus();
265
- const menu = buildMenu(initialStatus);
266
- const helper = spawn(helperPath, [], {
267
- stdio: ["pipe", "pipe", "ignore"]
268
- });
269
- const cleanup = () => {
270
- try {
271
- unlinkSync(pidFile);
272
- } catch {
273
- }
274
- helper.kill();
275
- process.exit(0);
276
- };
277
- process.on("SIGINT", cleanup);
278
- process.on("SIGTERM", cleanup);
279
- helper.on("exit", () => {
280
- try {
281
- unlinkSync(pidFile);
282
- } catch {
283
- }
284
- process.exit(0);
285
- });
286
- await new Promise((resolve, reject) => {
287
- let buf = "";
288
- const onData = (data) => {
289
- buf += data.toString();
290
- const lines = buf.split("\n");
291
- buf = lines.pop() || "";
292
- for (const line of lines) {
293
- if (!line.trim()) continue;
294
- try {
295
- const msg = JSON.parse(line);
296
- if (msg.type === "ready") {
297
- helper.stdout.removeListener("data", onData);
298
- resolve();
299
- }
300
- } catch {
301
- }
302
- }
303
- };
304
- helper.stdout.on("data", onData);
305
- setTimeout(() => reject(new Error("Tray helper did not become ready")), 1e4);
306
- });
307
- helper.stdin.write(JSON.stringify(menu) + "\n");
308
- let clickBuf = "";
309
- helper.stdout.on("data", (data) => {
310
- clickBuf += data.toString();
311
- const lines = clickBuf.split("\n");
312
- clickBuf = lines.pop() || "";
313
- for (const line of lines) {
314
- if (!line.trim()) continue;
315
- try {
316
- const msg = JSON.parse(line);
317
- if (msg.type === "clicked" && msg.item) {
318
- handleClick(msg.item.title, cleanup);
319
- }
320
- } catch {
321
- }
322
- }
323
- });
324
- setInterval(() => {
325
- try {
326
- const status = readStatus();
327
- const updatedMenu = buildMenu(status);
328
- const action = { type: "update-menu", menu: updatedMenu };
329
- helper.stdin.write(JSON.stringify(action) + "\n");
330
- } catch {
331
- }
332
- }, STATUS_POLL_MS);
333
- await new Promise(() => {
334
- });
335
- }
336
- function fixSystrayBinaryPermissions() {
337
- if (platform() === "win32") return;
338
- try {
339
- const cacheDir = join2(homedir(), ".cache", "node-systray");
340
- if (!existsSync2(cacheDir)) return;
341
- for (const ver of readdirSync(cacheDir)) {
342
- const dir = join2(cacheDir, ver);
343
- for (const file of readdirSync(dir)) {
344
- if (file.startsWith("tray_")) {
345
- chmodSync(join2(dir, file), 493);
346
- }
347
- }
348
- }
349
- } catch {
350
- }
351
- }
352
- async function startSystray2Tray(pidFile) {
353
- fixSystrayBinaryPermissions();
354
- const initialStatus = readStatus();
355
- const menu = buildMenu(initialStatus);
356
- const systray = new SysTray({
357
- menu,
358
- debug: false,
359
- copyDir: true
360
- });
361
- const cleanup = () => {
362
- try {
363
- unlinkSync(pidFile);
364
- } catch {
365
- }
366
- systray.kill(false);
367
- process.exit(0);
368
- };
369
- process.on("SIGINT", cleanup);
370
- process.on("SIGTERM", cleanup);
371
- await systray.ready();
372
- systray.onClick(async (action) => {
373
- handleClick(action.item.title, cleanup);
374
- });
375
- setInterval(async () => {
376
- try {
377
- const status = readStatus();
378
- const updatedMenu = buildMenu(status);
379
- await systray.sendAction({ type: "update-menu", menu: updatedMenu });
380
- } catch {
381
- }
382
- }, STATUS_POLL_MS);
383
- await new Promise(() => {
384
- });
385
- }
386
- async function startTray() {
387
- const pidFile = join2(getDataDir(), TRAY_PID_FILE);
388
- ensureDirectory(getDataDir());
389
- if (existsSync2(pidFile)) {
390
- try {
391
- const pid = parseInt(readFileSync2(pidFile, "utf-8").trim(), 10);
392
- process.kill(pid, 0);
393
- console.error("Tray icon is already running (PID: " + pid + ")");
394
- process.exit(1);
395
- } catch {
396
- }
397
- }
398
- writeFileSync2(pidFile, String(process.pid));
399
- if (platform() === "darwin") {
400
- await startDarwinTray(pidFile);
401
- } else {
402
- await startSystray2Tray(pidFile);
403
- }
404
- }
405
- export {
406
- startTray
407
- };
408
- //# sourceMappingURL=tray-UCAI2U2C.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/tray/tray.ts","../src/utils/status-writer.ts"],"sourcesContent":["/**\n * Skillo System Tray Icon\n *\n * Shows daemon status, tracked projects, sync info in the system tray.\n * Reads ~/.skillo/daemon-status.json (written by StatusWriter in daemon).\n *\n * macOS: native Swift helper (scripts/tray-helper-darwin) — arm64+x86_64.\n * Linux/Windows: systray2 (Go-based binary).\n */\n\nimport SysTrayModule from \"systray2\";\nimport { readFileSync, existsSync, writeFileSync, unlinkSync, chmodSync, readdirSync } from \"fs\";\nimport { exec, execSync, spawn } from \"child_process\";\nimport { join, dirname } from \"path\";\nimport { platform, homedir } from \"os\";\nimport { fileURLToPath } from \"url\";\nimport { StatusWriter, type DaemonStatus } from \"../utils/status-writer.js\";\nimport { getDataDir, ensureDirectory } from \"../utils/paths.js\";\n\n// Handle CJS/ESM interop — systray2 is CJS, so default import may be wrapped\nconst SysTray = (SysTrayModule as any).default || SysTrayModule;\n\ntype MenuItem = {\n title: string;\n tooltip: string;\n checked?: boolean;\n enabled?: boolean;\n hidden?: boolean;\n items?: MenuItem[];\n};\n\ntype Menu = {\n icon: string;\n title: string;\n tooltip: string;\n items: MenuItem[];\n};\n\nconst SEPARATOR: MenuItem = { title: \"<SEPARATOR>\", tooltip: \"\", enabled: true };\n\n// Skillo logo as 22x22 black template PNG (auto-inverts for dark mode on macOS)\nconst ICON_BASE64 =\n \"iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAAXNSR0IArs4c6QAAADhlWElm\" +\n \"TU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAFqADAAQAAAABAAAAFgAAAAA/\" +\n \"6RFgAAABm2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJh\" +\n \"ZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4\" +\n \"bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMi\" +\n \"PgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxu\" +\n \"czpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyI+CiAgICAgICAgIDxleGlm\" +\n \"OlBpeGVsWERpbWVuc2lvbj4yMjwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAgICAgIDxl\" +\n \"eGlmOlBpeGVsWURpbWVuc2lvbj4yMjwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgIDwv\" +\n \"cmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgolQXt+AAABg0lE\" +\n \"QVQ4EaWUPyhFURzHH4/UK38KA5OBlGKQUWQRmQ1vMSmb2WKwSqKkUBaLUhabTfmTjMhiV0qK\" +\n \"ouTf59tz6nTeuefc637r07nn/H7f7zvvnntvsZBPndiXoQHu8kVV3G0MK/AGPzANudSMewle\" +\n \"QIGG1MHDmFy1snAMJsweq4JrXTfzQZjwrD+xNg79sAufkFo1dF7BLZQiLv2AerXzqh273sm/\" +\n \"RjVfQK/b4MzrmW9CNHiNJvvefTDX6TdCSE2homrbYAeb6wfWZ0C36l8q4zJhvvGcug43s/SU\" +\n \"HIIv1Kx9Ud+AFsikIt0L8A4mzDc+Up8D9WdSF90H4Au11/TI9UFmjeG4BjvMvX6l3pM5GUMd\" +\n \"zMMzuKFmvkUtUbETb8e5AzpAE2jGk8RUCmewDnqjQhqieAkmVONeyKBgNd3ASKiRmp6GRfgG\" +\n \"eaYgUXoB7F0cMR9N7K4UVhlO3R4dSkjdFDtAL4525pO+hMHbIJPZ8T3X+jakefgHZIxpn4ZZ\" +\n \"iP2TWE7hF4oBfI7ofv98AAAAAElFTkSuQmCC\";\n\nconst TRAY_PID_FILE = \"tray.pid\";\nconst STATUS_POLL_MS = 3000;\n\nfunction formatTimeAgo(isoString: string | null | undefined): string {\n if (!isoString) return \"Never\";\n const diff = Date.now() - new Date(isoString).getTime();\n const seconds = Math.floor(diff / 1000);\n if (seconds < 60) return \"Just now\";\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes} min ago`;\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.floor(hours / 24);\n return `${days}d ago`;\n}\n\nfunction readStatus(): DaemonStatus | null {\n return StatusWriter.read();\n}\n\nfunction buildMenu(status: DaemonStatus | null): Menu {\n const running = status?.running ?? false;\n const statusIcon = running ? \"\\u25CF\" : \"\\u25CB\"; // ● or ○\n const statusText = running ? \"Running\" : \"Stopped\";\n\n const projects = status?.trackedProjects ?? [];\n const projectItems: MenuItem[] = projects.length > 0\n ? projects.map((p) => ({\n title: ` ${p.name}`,\n tooltip: p.path,\n enabled: false,\n checked: false,\n }))\n : [{\n title: \" (none)\",\n tooltip: \"No tracked projects\",\n enabled: false,\n checked: false,\n }];\n\n const items: MenuItem[] = [\n {\n title: `${statusIcon} Skillo \\u2014 ${statusText}`,\n tooltip: \"Daemon status\",\n enabled: false,\n checked: false,\n },\n SEPARATOR,\n {\n title: status?.user ? `Logged in as: ${status.user}` : \"Not logged in\",\n tooltip: \"User info\",\n enabled: false,\n checked: false,\n },\n SEPARATOR,\n {\n title: `Tracked Projects (${projects.length})`,\n tooltip: \"Projects being tracked\",\n enabled: false,\n checked: false,\n },\n ...projectItems,\n SEPARATOR,\n {\n title: `Last sync: ${formatTimeAgo(status?.claudeWatcher?.lastSync)}`,\n tooltip: \"Last Claude conversation sync\",\n enabled: false,\n checked: false,\n },\n {\n title: `Prompts synced: ${status?.claudeWatcher?.promptsSynced ?? 0}`,\n tooltip: \"Total prompts synced\",\n enabled: false,\n checked: false,\n },\n {\n title: `Skills detected: ${status?.skillDetector?.usagesDetected ?? 0}`,\n tooltip: \"Skill usages detected\",\n enabled: false,\n checked: false,\n },\n SEPARATOR,\n {\n title: running ? \"Stop Daemon\" : \"Start Daemon\",\n tooltip: running ? \"Stop the Skillo daemon\" : \"Start the Skillo daemon\",\n enabled: true,\n checked: false,\n },\n {\n title: \"Open Dashboard\",\n tooltip: \"Open Skillo dashboard in browser\",\n enabled: true,\n checked: false,\n },\n SEPARATOR,\n {\n title: \"Quit Tray\",\n tooltip: \"Hide tray icon (daemon keeps running)\",\n enabled: true,\n checked: false,\n },\n ];\n\n return {\n icon: ICON_BASE64,\n title: \"\",\n tooltip: `Skillo \\u2014 ${statusText}`,\n items,\n };\n}\n\nfunction openBrowser(url: string) {\n const os = platform();\n let cmd: string;\n if (os === \"darwin\") {\n cmd = `open \"${url}\"`;\n } else if (os === \"win32\") {\n cmd = `start \"\" \"${url}\"`;\n } else {\n cmd = `xdg-open \"${url}\"`;\n }\n exec(cmd, () => {});\n}\n\nfunction execSkillo(args: string) {\n exec(`skillo ${args}`, () => {});\n}\n\nfunction handleClick(title: string, cleanup: () => void) {\n if (title === \"Stop Daemon\" || title === \"Start Daemon\") {\n execSkillo(title === \"Stop Daemon\" ? \"stop\" : \"start\");\n } else if (title === \"Open Dashboard\") {\n openBrowser(\"https://www.skillo.one/dashboard\");\n } else if (title === \"Quit Tray\") {\n cleanup();\n }\n}\n\n// ── macOS native tray (Swift helper) ─────────────────────────────────────────\n\nfunction findDarwinHelper(): string | null {\n // Look for the compiled helper next to the running script, or in scripts/\n const candidates = [\n join(getDataDir(), \"tray-helper-darwin\"),\n join(dirname(fileURLToPath(import.meta.url)), \"..\", \"scripts\", \"tray-helper-darwin\"),\n join(dirname(fileURLToPath(import.meta.url)), \"scripts\", \"tray-helper-darwin\"),\n ];\n for (const p of candidates) {\n if (existsSync(p)) return p;\n }\n return null;\n}\n\nfunction compileDarwinHelper(): string | null {\n // Compile from source if Swift is available\n const srcCandidates = [\n join(dirname(fileURLToPath(import.meta.url)), \"..\", \"scripts\", \"tray-helper-darwin.swift\"),\n join(dirname(fileURLToPath(import.meta.url)), \"scripts\", \"tray-helper-darwin.swift\"),\n ];\n let src: string | null = null;\n for (const p of srcCandidates) {\n if (existsSync(p)) { src = p; break; }\n }\n if (!src) return null;\n\n const out = join(getDataDir(), \"tray-helper-darwin\");\n try {\n execSync(`swiftc \"${src}\" -o \"${out}\" -O 2>/dev/null`, { timeout: 60000 });\n chmodSync(out, 0o755);\n return out;\n } catch {\n return null;\n }\n}\n\nasync function startDarwinTray(pidFile: string): Promise<void> {\n let helperPath = findDarwinHelper();\n if (!helperPath) {\n helperPath = compileDarwinHelper();\n }\n if (!helperPath) {\n // Fall back to systray2\n await startSystray2Tray(pidFile);\n return;\n }\n\n const initialStatus = readStatus();\n const menu = buildMenu(initialStatus);\n\n const helper = spawn(helperPath, [], {\n stdio: [\"pipe\", \"pipe\", \"ignore\"],\n });\n\n const cleanup = () => {\n try { unlinkSync(pidFile); } catch { /* ignore */ }\n helper.kill();\n process.exit(0);\n };\n process.on(\"SIGINT\", cleanup);\n process.on(\"SIGTERM\", cleanup);\n helper.on(\"exit\", () => {\n try { unlinkSync(pidFile); } catch { /* ignore */ }\n process.exit(0);\n });\n\n // Wait for ready, then send menu\n await new Promise<void>((resolve, reject) => {\n let buf = \"\";\n const onData = (data: Buffer) => {\n buf += data.toString();\n const lines = buf.split(\"\\n\");\n buf = lines.pop() || \"\";\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const msg = JSON.parse(line);\n if (msg.type === \"ready\") {\n helper.stdout!.removeListener(\"data\", onData);\n resolve();\n }\n } catch { /* ignore parse errors */ }\n }\n };\n helper.stdout!.on(\"data\", onData);\n setTimeout(() => reject(new Error(\"Tray helper did not become ready\")), 10000);\n });\n\n // Send initial menu\n helper.stdin!.write(JSON.stringify(menu) + \"\\n\");\n\n // Listen for clicks\n let clickBuf = \"\";\n helper.stdout!.on(\"data\", (data: Buffer) => {\n clickBuf += data.toString();\n const lines = clickBuf.split(\"\\n\");\n clickBuf = lines.pop() || \"\";\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const msg = JSON.parse(line);\n if (msg.type === \"clicked\" && msg.item) {\n handleClick(msg.item.title, cleanup);\n }\n } catch { /* ignore */ }\n }\n });\n\n // Poll and update\n setInterval(() => {\n try {\n const status = readStatus();\n const updatedMenu = buildMenu(status);\n const action = { type: \"update-menu\", menu: updatedMenu };\n helper.stdin!.write(JSON.stringify(action) + \"\\n\");\n } catch { /* ignore */ }\n }, STATUS_POLL_MS);\n\n await new Promise(() => {});\n}\n\n// ── systray2 tray (Linux/Windows) ────────────────────────────────────────────\n\nfunction fixSystrayBinaryPermissions() {\n if (platform() === \"win32\") return;\n try {\n const cacheDir = join(homedir(), \".cache\", \"node-systray\");\n if (!existsSync(cacheDir)) return;\n for (const ver of readdirSync(cacheDir)) {\n const dir = join(cacheDir, ver);\n for (const file of readdirSync(dir)) {\n if (file.startsWith(\"tray_\")) {\n chmodSync(join(dir, file), 0o755);\n }\n }\n }\n } catch { /* best-effort */ }\n}\n\nasync function startSystray2Tray(pidFile: string): Promise<void> {\n fixSystrayBinaryPermissions();\n\n const initialStatus = readStatus();\n const menu = buildMenu(initialStatus);\n\n const systray = new SysTray({\n menu,\n debug: false,\n copyDir: true,\n });\n\n const cleanup = () => {\n try { unlinkSync(pidFile); } catch { /* ignore */ }\n systray.kill(false);\n process.exit(0);\n };\n process.on(\"SIGINT\", cleanup);\n process.on(\"SIGTERM\", cleanup);\n\n await systray.ready();\n\n systray.onClick(async (action: { item: MenuItem }) => {\n handleClick(action.item.title, cleanup);\n });\n\n setInterval(async () => {\n try {\n const status = readStatus();\n const updatedMenu = buildMenu(status);\n await systray.sendAction({ type: \"update-menu\", menu: updatedMenu });\n } catch { /* ignore */ }\n }, STATUS_POLL_MS);\n\n await new Promise(() => {});\n}\n\n// ── Entry point ──────────────────────────────────────────────────────────────\n\nexport async function startTray(): Promise<void> {\n const pidFile = join(getDataDir(), TRAY_PID_FILE);\n ensureDirectory(getDataDir());\n\n // Check if another tray is already running\n if (existsSync(pidFile)) {\n try {\n const pid = parseInt(readFileSync(pidFile, \"utf-8\").trim(), 10);\n process.kill(pid, 0);\n console.error(\"Tray icon is already running (PID: \" + pid + \")\");\n process.exit(1);\n } catch {\n // Stale PID file, continue\n }\n }\n\n writeFileSync(pidFile, String(process.pid));\n\n if (platform() === \"darwin\") {\n await startDarwinTray(pidFile);\n } else {\n await startSystray2Tray(pidFile);\n }\n}\n","/**\n * StatusWriter — writes daemon status JSON for tray icon and diagnostics.\n *\n * Writes ~/.skillo/daemon-status.json every 10s and on key events.\n * Tray icon and `skillo status` read this file.\n */\n\nimport { writeFileSync, readFileSync, existsSync } from \"fs\";\nimport { join } from \"path\";\nimport { getDataDir, ensureDirectory } from \"./paths.js\";\n\nexport interface DaemonStatus {\n running: boolean;\n pid: number | null;\n startedAt: string | null;\n updatedAt: string;\n user?: string;\n claudeWatcher?: {\n lastSync?: string | null;\n promptsSynced?: number;\n lastError?: string | null;\n };\n skillDetector?: {\n deployedSkills?: number;\n usagesDetected?: number;\n lastDetection?: string | null;\n lastError?: string | null;\n };\n trackedProjects?: Array<{ name: string; path: string }>;\n activeSessions?: number;\n}\n\nconst STATUS_FILE = \"daemon-status.json\";\nconst WRITE_INTERVAL_MS = 10000;\n\nexport class StatusWriter {\n private intervalId: ReturnType<typeof setInterval> | null = null;\n private status: DaemonStatus;\n private filePath: string;\n\n constructor() {\n this.filePath = join(getDataDir(), STATUS_FILE);\n this.status = {\n running: true,\n pid: process.pid,\n startedAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n claudeWatcher: { lastSync: null, promptsSynced: 0, lastError: null },\n skillDetector: { deployedSkills: 0, usagesDetected: 0, lastDetection: null, lastError: null },\n trackedProjects: [],\n activeSessions: 0,\n };\n }\n\n /** Start periodic writes */\n start() {\n ensureDirectory(getDataDir());\n this.write();\n this.intervalId = setInterval(() => this.write(), WRITE_INTERVAL_MS);\n }\n\n /** Stop writing and mark as not running */\n stop() {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n this.intervalId = null;\n }\n this.status.running = false;\n this.status.pid = null;\n this.status.updatedAt = new Date().toISOString();\n this.write();\n }\n\n /** Merge partial status updates */\n update(partial: Partial<DaemonStatus>) {\n // Deep merge for nested objects\n if (partial.claudeWatcher) {\n this.status.claudeWatcher = { ...this.status.claudeWatcher, ...partial.claudeWatcher };\n delete partial.claudeWatcher;\n }\n if (partial.skillDetector) {\n this.status.skillDetector = { ...this.status.skillDetector, ...partial.skillDetector };\n delete partial.skillDetector;\n }\n Object.assign(this.status, partial);\n }\n\n /** Get the status file path */\n static getStatusFilePath(): string {\n return join(getDataDir(), STATUS_FILE);\n }\n\n /** Read current status from disk (static, for tray/status commands) */\n static read(): DaemonStatus | null {\n const filePath = StatusWriter.getStatusFilePath();\n try {\n if (existsSync(filePath)) {\n return JSON.parse(readFileSync(filePath, \"utf-8\"));\n }\n } catch {\n // ignore\n }\n return null;\n }\n\n private write() {\n this.status.updatedAt = new Date().toISOString();\n try {\n writeFileSync(this.filePath, JSON.stringify(this.status, null, 2), \"utf-8\");\n } catch {\n // Can't write status, ignore\n }\n }\n}\n"],"mappings":";;;;;;;AAUA,OAAO,mBAAmB;AAC1B,SAAS,gBAAAA,eAAc,cAAAC,aAAY,iBAAAC,gBAAe,YAAY,WAAW,mBAAmB;AAC5F,SAAS,MAAM,UAAU,aAAa;AACtC,SAAS,QAAAC,OAAM,eAAe;AAC9B,SAAS,UAAU,eAAe;AAClC,SAAS,qBAAqB;;;ACR9B,SAAS,eAAe,cAAc,kBAAkB;AACxD,SAAS,YAAY;AAwBrB,IAAM,cAAc;AACpB,IAAM,oBAAoB;AAEnB,IAAM,eAAN,MAAM,cAAa;AAAA,EAChB,aAAoD;AAAA,EACpD;AAAA,EACA;AAAA,EAER,cAAc;AACZ,SAAK,WAAW,KAAK,WAAW,GAAG,WAAW;AAC9C,SAAK,SAAS;AAAA,MACZ,SAAS;AAAA,MACT,KAAK,QAAQ;AAAA,MACb,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,eAAe,EAAE,UAAU,MAAM,eAAe,GAAG,WAAW,KAAK;AAAA,MACnE,eAAe,EAAE,gBAAgB,GAAG,gBAAgB,GAAG,eAAe,MAAM,WAAW,KAAK;AAAA,MAC5F,iBAAiB,CAAC;AAAA,MAClB,gBAAgB;AAAA,IAClB;AAAA,EACF;AAAA;AAAA,EAGA,QAAQ;AACN,oBAAgB,WAAW,CAAC;AAC5B,SAAK,MAAM;AACX,SAAK,aAAa,YAAY,MAAM,KAAK,MAAM,GAAG,iBAAiB;AAAA,EACrE;AAAA;AAAA,EAGA,OAAO;AACL,QAAI,KAAK,YAAY;AACnB,oBAAc,KAAK,UAAU;AAC7B,WAAK,aAAa;AAAA,IACpB;AACA,SAAK,OAAO,UAAU;AACtB,SAAK,OAAO,MAAM;AAClB,SAAK,OAAO,aAAY,oBAAI,KAAK,GAAE,YAAY;AAC/C,SAAK,MAAM;AAAA,EACb;AAAA;AAAA,EAGA,OAAO,SAAgC;AAErC,QAAI,QAAQ,eAAe;AACzB,WAAK,OAAO,gBAAgB,EAAE,GAAG,KAAK,OAAO,eAAe,GAAG,QAAQ,cAAc;AACrF,aAAO,QAAQ;AAAA,IACjB;AACA,QAAI,QAAQ,eAAe;AACzB,WAAK,OAAO,gBAAgB,EAAE,GAAG,KAAK,OAAO,eAAe,GAAG,QAAQ,cAAc;AACrF,aAAO,QAAQ;AAAA,IACjB;AACA,WAAO,OAAO,KAAK,QAAQ,OAAO;AAAA,EACpC;AAAA;AAAA,EAGA,OAAO,oBAA4B;AACjC,WAAO,KAAK,WAAW,GAAG,WAAW;AAAA,EACvC;AAAA;AAAA,EAGA,OAAO,OAA4B;AACjC,UAAM,WAAW,cAAa,kBAAkB;AAChD,QAAI;AACF,UAAI,WAAW,QAAQ,GAAG;AACxB,eAAO,KAAK,MAAM,aAAa,UAAU,OAAO,CAAC;AAAA,MACnD;AAAA,IACF,QAAQ;AAAA,IAER;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,QAAQ;AACd,SAAK,OAAO,aAAY,oBAAI,KAAK,GAAE,YAAY;AAC/C,QAAI;AACF,oBAAc,KAAK,UAAU,KAAK,UAAU,KAAK,QAAQ,MAAM,CAAC,GAAG,OAAO;AAAA,IAC5E,QAAQ;AAAA,IAER;AAAA,EACF;AACF;;;AD7FA,IAAM,UAAW,cAAsB,WAAW;AAkBlD,IAAM,YAAsB,EAAE,OAAO,eAAe,SAAS,IAAI,SAAS,KAAK;AAG/E,IAAM,cACJ;AAmBF,IAAM,gBAAgB;AACtB,IAAM,iBAAiB;AAEvB,SAAS,cAAc,WAA8C;AACnE,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,OAAO,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS,EAAE,QAAQ;AACtD,QAAM,UAAU,KAAK,MAAM,OAAO,GAAI;AACtC,MAAI,UAAU,GAAI,QAAO;AACzB,QAAM,UAAU,KAAK,MAAM,UAAU,EAAE;AACvC,MAAI,UAAU,GAAI,QAAO,GAAG,OAAO;AACnC,QAAM,QAAQ,KAAK,MAAM,UAAU,EAAE;AACrC,MAAI,QAAQ,GAAI,QAAO,GAAG,KAAK;AAC/B,QAAM,OAAO,KAAK,MAAM,QAAQ,EAAE;AAClC,SAAO,GAAG,IAAI;AAChB;AAEA,SAAS,aAAkC;AACzC,SAAO,aAAa,KAAK;AAC3B;AAEA,SAAS,UAAU,QAAmC;AACpD,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,aAAa,UAAU,WAAW;AACxC,QAAM,aAAa,UAAU,YAAY;AAEzC,QAAM,WAAW,QAAQ,mBAAmB,CAAC;AAC7C,QAAM,eAA2B,SAAS,SAAS,IAC/C,SAAS,IAAI,CAAC,OAAO;AAAA,IACnB,OAAO,KAAK,EAAE,IAAI;AAAA,IAClB,SAAS,EAAE;AAAA,IACX,SAAS;AAAA,IACT,SAAS;AAAA,EACX,EAAE,IACF,CAAC;AAAA,IACC,OAAO;AAAA,IACP,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS;AAAA,EACX,CAAC;AAEL,QAAM,QAAoB;AAAA,IACxB;AAAA,MACE,OAAO,GAAG,UAAU,kBAAkB,UAAU;AAAA,MAChD,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,MACE,OAAO,QAAQ,OAAO,iBAAiB,OAAO,IAAI,KAAK;AAAA,MACvD,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,MACE,OAAO,qBAAqB,SAAS,MAAM;AAAA,MAC3C,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,IACX;AAAA,IACA,GAAG;AAAA,IACH;AAAA,IACA;AAAA,MACE,OAAO,cAAc,cAAc,QAAQ,eAAe,QAAQ,CAAC;AAAA,MACnE,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,OAAO,mBAAmB,QAAQ,eAAe,iBAAiB,CAAC;AAAA,MACnE,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,OAAO,oBAAoB,QAAQ,eAAe,kBAAkB,CAAC;AAAA,MACrE,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,MACE,OAAO,UAAU,gBAAgB;AAAA,MACjC,SAAS,UAAU,2BAA2B;AAAA,MAC9C,SAAS;AAAA,MACT,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,IACX;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS,iBAAiB,UAAU;AAAA,IACpC;AAAA,EACF;AACF;AAEA,SAAS,YAAY,KAAa;AAChC,QAAM,KAAK,SAAS;AACpB,MAAI;AACJ,MAAI,OAAO,UAAU;AACnB,UAAM,SAAS,GAAG;AAAA,EACpB,WAAW,OAAO,SAAS;AACzB,UAAM,aAAa,GAAG;AAAA,EACxB,OAAO;AACL,UAAM,aAAa,GAAG;AAAA,EACxB;AACA,OAAK,KAAK,MAAM;AAAA,EAAC,CAAC;AACpB;AAEA,SAAS,WAAW,MAAc;AAChC,OAAK,UAAU,IAAI,IAAI,MAAM;AAAA,EAAC,CAAC;AACjC;AAEA,SAAS,YAAY,OAAe,SAAqB;AACvD,MAAI,UAAU,iBAAiB,UAAU,gBAAgB;AACvD,eAAW,UAAU,gBAAgB,SAAS,OAAO;AAAA,EACvD,WAAW,UAAU,kBAAkB;AACrC,gBAAY,kCAAkC;AAAA,EAChD,WAAW,UAAU,aAAa;AAChC,YAAQ;AAAA,EACV;AACF;AAIA,SAAS,mBAAkC;AAEzC,QAAM,aAAa;AAAA,IACjBC,MAAK,WAAW,GAAG,oBAAoB;AAAA,IACvCA,MAAK,QAAQ,cAAc,YAAY,GAAG,CAAC,GAAG,MAAM,WAAW,oBAAoB;AAAA,IACnFA,MAAK,QAAQ,cAAc,YAAY,GAAG,CAAC,GAAG,WAAW,oBAAoB;AAAA,EAC/E;AACA,aAAW,KAAK,YAAY;AAC1B,QAAIC,YAAW,CAAC,EAAG,QAAO;AAAA,EAC5B;AACA,SAAO;AACT;AAEA,SAAS,sBAAqC;AAE5C,QAAM,gBAAgB;AAAA,IACpBD,MAAK,QAAQ,cAAc,YAAY,GAAG,CAAC,GAAG,MAAM,WAAW,0BAA0B;AAAA,IACzFA,MAAK,QAAQ,cAAc,YAAY,GAAG,CAAC,GAAG,WAAW,0BAA0B;AAAA,EACrF;AACA,MAAI,MAAqB;AACzB,aAAW,KAAK,eAAe;AAC7B,QAAIC,YAAW,CAAC,GAAG;AAAE,YAAM;AAAG;AAAA,IAAO;AAAA,EACvC;AACA,MAAI,CAAC,IAAK,QAAO;AAEjB,QAAM,MAAMD,MAAK,WAAW,GAAG,oBAAoB;AACnD,MAAI;AACF,aAAS,WAAW,GAAG,SAAS,GAAG,oBAAoB,EAAE,SAAS,IAAM,CAAC;AACzE,cAAU,KAAK,GAAK;AACpB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,gBAAgB,SAAgC;AAC7D,MAAI,aAAa,iBAAiB;AAClC,MAAI,CAAC,YAAY;AACf,iBAAa,oBAAoB;AAAA,EACnC;AACA,MAAI,CAAC,YAAY;AAEf,UAAM,kBAAkB,OAAO;AAC/B;AAAA,EACF;AAEA,QAAM,gBAAgB,WAAW;AACjC,QAAM,OAAO,UAAU,aAAa;AAEpC,QAAM,SAAS,MAAM,YAAY,CAAC,GAAG;AAAA,IACnC,OAAO,CAAC,QAAQ,QAAQ,QAAQ;AAAA,EAClC,CAAC;AAED,QAAM,UAAU,MAAM;AACpB,QAAI;AAAE,iBAAW,OAAO;AAAA,IAAG,QAAQ;AAAA,IAAe;AAClD,WAAO,KAAK;AACZ,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,UAAQ,GAAG,UAAU,OAAO;AAC5B,UAAQ,GAAG,WAAW,OAAO;AAC7B,SAAO,GAAG,QAAQ,MAAM;AACtB,QAAI;AAAE,iBAAW,OAAO;AAAA,IAAG,QAAQ;AAAA,IAAe;AAClD,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AAGD,QAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,QAAI,MAAM;AACV,UAAM,SAAS,CAAC,SAAiB;AAC/B,aAAO,KAAK,SAAS;AACrB,YAAM,QAAQ,IAAI,MAAM,IAAI;AAC5B,YAAM,MAAM,IAAI,KAAK;AACrB,iBAAW,QAAQ,OAAO;AACxB,YAAI,CAAC,KAAK,KAAK,EAAG;AAClB,YAAI;AACF,gBAAM,MAAM,KAAK,MAAM,IAAI;AAC3B,cAAI,IAAI,SAAS,SAAS;AACxB,mBAAO,OAAQ,eAAe,QAAQ,MAAM;AAC5C,oBAAQ;AAAA,UACV;AAAA,QACF,QAAQ;AAAA,QAA4B;AAAA,MACtC;AAAA,IACF;AACA,WAAO,OAAQ,GAAG,QAAQ,MAAM;AAChC,eAAW,MAAM,OAAO,IAAI,MAAM,kCAAkC,CAAC,GAAG,GAAK;AAAA,EAC/E,CAAC;AAGD,SAAO,MAAO,MAAM,KAAK,UAAU,IAAI,IAAI,IAAI;AAG/C,MAAI,WAAW;AACf,SAAO,OAAQ,GAAG,QAAQ,CAAC,SAAiB;AAC1C,gBAAY,KAAK,SAAS;AAC1B,UAAM,QAAQ,SAAS,MAAM,IAAI;AACjC,eAAW,MAAM,IAAI,KAAK;AAC1B,eAAW,QAAQ,OAAO;AACxB,UAAI,CAAC,KAAK,KAAK,EAAG;AAClB,UAAI;AACF,cAAM,MAAM,KAAK,MAAM,IAAI;AAC3B,YAAI,IAAI,SAAS,aAAa,IAAI,MAAM;AACtC,sBAAY,IAAI,KAAK,OAAO,OAAO;AAAA,QACrC;AAAA,MACF,QAAQ;AAAA,MAAe;AAAA,IACzB;AAAA,EACF,CAAC;AAGD,cAAY,MAAM;AAChB,QAAI;AACF,YAAM,SAAS,WAAW;AAC1B,YAAM,cAAc,UAAU,MAAM;AACpC,YAAM,SAAS,EAAE,MAAM,eAAe,MAAM,YAAY;AACxD,aAAO,MAAO,MAAM,KAAK,UAAU,MAAM,IAAI,IAAI;AAAA,IACnD,QAAQ;AAAA,IAAe;AAAA,EACzB,GAAG,cAAc;AAEjB,QAAM,IAAI,QAAQ,MAAM;AAAA,EAAC,CAAC;AAC5B;AAIA,SAAS,8BAA8B;AACrC,MAAI,SAAS,MAAM,QAAS;AAC5B,MAAI;AACF,UAAM,WAAWA,MAAK,QAAQ,GAAG,UAAU,cAAc;AACzD,QAAI,CAACC,YAAW,QAAQ,EAAG;AAC3B,eAAW,OAAO,YAAY,QAAQ,GAAG;AACvC,YAAM,MAAMD,MAAK,UAAU,GAAG;AAC9B,iBAAW,QAAQ,YAAY,GAAG,GAAG;AACnC,YAAI,KAAK,WAAW,OAAO,GAAG;AAC5B,oBAAUA,MAAK,KAAK,IAAI,GAAG,GAAK;AAAA,QAClC;AAAA,MACF;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAAoB;AAC9B;AAEA,eAAe,kBAAkB,SAAgC;AAC/D,8BAA4B;AAE5B,QAAM,gBAAgB,WAAW;AACjC,QAAM,OAAO,UAAU,aAAa;AAEpC,QAAM,UAAU,IAAI,QAAQ;AAAA,IAC1B;AAAA,IACA,OAAO;AAAA,IACP,SAAS;AAAA,EACX,CAAC;AAED,QAAM,UAAU,MAAM;AACpB,QAAI;AAAE,iBAAW,OAAO;AAAA,IAAG,QAAQ;AAAA,IAAe;AAClD,YAAQ,KAAK,KAAK;AAClB,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,UAAQ,GAAG,UAAU,OAAO;AAC5B,UAAQ,GAAG,WAAW,OAAO;AAE7B,QAAM,QAAQ,MAAM;AAEpB,UAAQ,QAAQ,OAAO,WAA+B;AACpD,gBAAY,OAAO,KAAK,OAAO,OAAO;AAAA,EACxC,CAAC;AAED,cAAY,YAAY;AACtB,QAAI;AACF,YAAM,SAAS,WAAW;AAC1B,YAAM,cAAc,UAAU,MAAM;AACpC,YAAM,QAAQ,WAAW,EAAE,MAAM,eAAe,MAAM,YAAY,CAAC;AAAA,IACrE,QAAQ;AAAA,IAAe;AAAA,EACzB,GAAG,cAAc;AAEjB,QAAM,IAAI,QAAQ,MAAM;AAAA,EAAC,CAAC;AAC5B;AAIA,eAAsB,YAA2B;AAC/C,QAAM,UAAUA,MAAK,WAAW,GAAG,aAAa;AAChD,kBAAgB,WAAW,CAAC;AAG5B,MAAIC,YAAW,OAAO,GAAG;AACvB,QAAI;AACF,YAAM,MAAM,SAASC,cAAa,SAAS,OAAO,EAAE,KAAK,GAAG,EAAE;AAC9D,cAAQ,KAAK,KAAK,CAAC;AACnB,cAAQ,MAAM,wCAAwC,MAAM,GAAG;AAC/D,cAAQ,KAAK,CAAC;AAAA,IAChB,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,EAAAC,eAAc,SAAS,OAAO,QAAQ,GAAG,CAAC;AAE1C,MAAI,SAAS,MAAM,UAAU;AAC3B,UAAM,gBAAgB,OAAO;AAAA,EAC/B,OAAO;AACL,UAAM,kBAAkB,OAAO;AAAA,EACjC;AACF;","names":["readFileSync","existsSync","writeFileSync","join","join","existsSync","readFileSync","writeFileSync"]}