skillo 0.2.6 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +198 -198
- package/dist/api-client-BF6GDR7Q.js +12 -0
- package/dist/{chunk-WJKZWKER.js → chunk-63FVALWX.js} +1 -1
- package/dist/chunk-63FVALWX.js.map +1 -0
- package/dist/chunk-6GOJPFZ7.js +113 -0
- package/dist/chunk-6GOJPFZ7.js.map +1 -0
- package/dist/chunk-6UGTWBUW.js +89 -0
- package/dist/chunk-6UGTWBUW.js.map +1 -0
- package/dist/{chunk-2CVEPT6U.js → chunk-73NUWYUO.js} +2 -2
- package/dist/chunk-73NUWYUO.js.map +1 -0
- package/dist/chunk-QIV4VIXA.js +221 -0
- package/dist/chunk-QIV4VIXA.js.map +1 -0
- package/dist/{chunk-CPL3P2OF.js → chunk-QUXHHRRK.js} +2 -2
- package/dist/chunk-QUXHHRRK.js.map +1 -0
- package/dist/{chunk-ODOZM4QV.js → chunk-RQ2SC5HW.js} +72 -10
- package/dist/chunk-RQ2SC5HW.js.map +1 -0
- package/dist/chunk-U53QWUOR.js +727 -0
- package/dist/chunk-U53QWUOR.js.map +1 -0
- package/dist/chunk-VAQ73XPE.js +68 -0
- package/dist/chunk-VAQ73XPE.js.map +1 -0
- package/dist/chunk-XLJGCOVT.js +975 -0
- package/dist/chunk-XLJGCOVT.js.map +1 -0
- package/dist/{claude-watcher-N6GN6WHJ.js → claude-watcher-WKGBJYKN.js} +65 -3
- package/dist/claude-watcher-WKGBJYKN.js.map +1 -0
- package/dist/cli.js +495 -1846
- package/dist/cli.js.map +1 -1
- package/dist/{config-P5EM5L7N.js → config-ZOKAP2LJ.js} +3 -3
- package/dist/daemon-6DTCMOJB.js +28 -0
- package/dist/daemon-runner.js +338 -71
- package/dist/daemon-runner.js.map +1 -1
- package/dist/database-KQY5OSCS.js +9 -0
- package/dist/git-OGUSYBJS.js +16 -0
- package/dist/git-OGUSYBJS.js.map +1 -0
- package/dist/git-OUAHIOY2.js +110 -0
- package/dist/git-OUAHIOY2.js.map +1 -0
- package/dist/index.js.map +1 -1
- package/dist/{paths-INOKEM66.js → paths-MPOZBOKE.js} +2 -2
- package/dist/paths-MPOZBOKE.js.map +1 -0
- package/dist/project-OFU2W6MH.js +19 -0
- package/dist/project-OFU2W6MH.js.map +1 -0
- package/dist/shell-NZABRJLA.js +16 -0
- package/dist/shell-NZABRJLA.js.map +1 -0
- package/dist/skill-installer-F67OAOQN.js +121 -0
- package/dist/skill-installer-F67OAOQN.js.map +1 -0
- package/dist/{skill-usage-detector-EO26MRYV.js → skill-usage-detector-MSW5VWQZ.js} +2 -2
- package/dist/skill-usage-detector-MSW5VWQZ.js.map +1 -0
- package/dist/tray-WKFGUUTO.js +346 -0
- package/dist/tray-WKFGUUTO.js.map +1 -0
- package/package.json +62 -63
- package/scripts/postinstall.mjs +415 -364
- package/scripts/tray-helper-darwin +0 -0
- package/scripts/tray-helper-darwin.swift +180 -180
- package/scripts/tray-helper-linux.py +322 -0
- package/scripts/tray-helper-windows.cs +322 -0
- package/dist/api-client-KUQW7FSC.js +0 -12
- package/dist/chunk-2CVEPT6U.js.map +0 -1
- package/dist/chunk-CPL3P2OF.js.map +0 -1
- package/dist/chunk-ODOZM4QV.js.map +0 -1
- package/dist/chunk-WJKZWKER.js.map +0 -1
- package/dist/claude-watcher-N6GN6WHJ.js.map +0 -1
- package/dist/database-F3BFFZKG.js +0 -9
- package/dist/skill-usage-detector-EO26MRYV.js.map +0 -1
- package/dist/tray-UCAI2U2C.js +0 -408
- package/dist/tray-UCAI2U2C.js.map +0 -1
- /package/dist/{api-client-KUQW7FSC.js.map → api-client-BF6GDR7Q.js.map} +0 -0
- /package/dist/{config-P5EM5L7N.js.map → config-ZOKAP2LJ.js.map} +0 -0
- /package/dist/{database-F3BFFZKG.js.map → daemon-6DTCMOJB.js.map} +0 -0
- /package/dist/{paths-INOKEM66.js.map → database-KQY5OSCS.js.map} +0 -0
package/README.md
CHANGED
|
@@ -1,198 +1,198 @@
|
|
|
1
|
-
# Skillo CLI
|
|
2
|
-
|
|
3
|
-
Autonomous workflow learning & skill generation system.
|
|
4
|
-
|
|
5
|
-
**Learn workflows by observation, not explanation.**
|
|
6
|
-
|
|
7
|
-
**Website:** [skillo.one](https://skillo.one)
|
|
8
|
-
|
|
9
|
-
## Installation
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
# Install globally
|
|
13
|
-
npm install -g skillo
|
|
14
|
-
|
|
15
|
-
# Or use with npx
|
|
16
|
-
npx skillo --help
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Quick Start
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
# Login to skillo.one (opens browser for OAuth)
|
|
23
|
-
skillo login
|
|
24
|
-
|
|
25
|
-
# Enable tracking for your project (privacy-first: opt-in only)
|
|
26
|
-
cd /path/to/your/project
|
|
27
|
-
skillo track
|
|
28
|
-
|
|
29
|
-
# Sync Claude Code history
|
|
30
|
-
skillo claude sync
|
|
31
|
-
|
|
32
|
-
# Check tracking status
|
|
33
|
-
skillo project status
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## Commands
|
|
37
|
-
|
|
38
|
-
### Authentication
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
# Login to skillo.one (opens browser)
|
|
42
|
-
skillo login
|
|
43
|
-
|
|
44
|
-
# Check login status
|
|
45
|
-
skillo whoami
|
|
46
|
-
|
|
47
|
-
# Logout
|
|
48
|
-
skillo logout
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Project Tracking (Privacy-First)
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
# Enable tracking for current project
|
|
55
|
-
skillo track
|
|
56
|
-
|
|
57
|
-
# Disable tracking
|
|
58
|
-
skillo untrack
|
|
59
|
-
|
|
60
|
-
# Check tracking status
|
|
61
|
-
skillo project status
|
|
62
|
-
|
|
63
|
-
# List all tracked projects
|
|
64
|
-
skillo project list
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### Claude Code Sync
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
# Sync Claude Code conversation history
|
|
71
|
-
skillo claude sync
|
|
72
|
-
|
|
73
|
-
# Sync specific project only
|
|
74
|
-
skillo claude sync --project /path/to/project
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### Sync
|
|
78
|
-
|
|
79
|
-
```bash
|
|
80
|
-
# Pull skills and patterns from platform
|
|
81
|
-
skillo sync --pull
|
|
82
|
-
|
|
83
|
-
# Push local patterns to platform
|
|
84
|
-
skillo sync --push
|
|
85
|
-
|
|
86
|
-
# Sync everything
|
|
87
|
-
skillo sync --push --pull
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
### Patterns
|
|
91
|
-
|
|
92
|
-
```bash
|
|
93
|
-
# List detected patterns
|
|
94
|
-
skillo patterns list
|
|
95
|
-
|
|
96
|
-
# Show pattern details
|
|
97
|
-
skillo patterns show <id>
|
|
98
|
-
|
|
99
|
-
# Generate a skill from a pattern
|
|
100
|
-
skillo patterns generate <id>
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### Skills
|
|
104
|
-
|
|
105
|
-
```bash
|
|
106
|
-
# List generated skills
|
|
107
|
-
skillo skills list
|
|
108
|
-
|
|
109
|
-
# Show skill details
|
|
110
|
-
skillo skills show <name>
|
|
111
|
-
|
|
112
|
-
# Open skills directory
|
|
113
|
-
skillo skills open
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
### Status
|
|
117
|
-
|
|
118
|
-
```bash
|
|
119
|
-
# Check overall status
|
|
120
|
-
skillo status
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
## Configuration
|
|
124
|
-
|
|
125
|
-
Configuration is stored in `~/.config/skillo/config.yaml`.
|
|
126
|
-
|
|
127
|
-
Key settings:
|
|
128
|
-
|
|
129
|
-
```yaml
|
|
130
|
-
# Pattern detection
|
|
131
|
-
pattern_detection:
|
|
132
|
-
min_count: 3 # Occurrences before detection
|
|
133
|
-
session_timeout: 30 # Minutes of inactivity
|
|
134
|
-
|
|
135
|
-
# Privacy
|
|
136
|
-
privacy:
|
|
137
|
-
auto_redact: true # Redact sensitive data
|
|
138
|
-
track_output: false # Don't track command output
|
|
139
|
-
|
|
140
|
-
# Notifications
|
|
141
|
-
notifications:
|
|
142
|
-
enabled: true
|
|
143
|
-
style: inline # inline, desktop, or both
|
|
144
|
-
|
|
145
|
-
# Skill generation
|
|
146
|
-
skill_generation:
|
|
147
|
-
include_scripts: true
|
|
148
|
-
include_examples: true
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
## How It Works
|
|
152
|
-
|
|
153
|
-
1. **Login**: Run `skillo login` to authenticate with [skillo.one](https://skillo.one). Opens your browser for secure OAuth.
|
|
154
|
-
|
|
155
|
-
2. **Track Projects**: Run `skillo track` in any project directory to enable tracking. Nothing is tracked by default (privacy-first).
|
|
156
|
-
|
|
157
|
-
3. **Pattern Detection**: Skillo analyzes your Claude Code conversations to identify repeated workflows and patterns.
|
|
158
|
-
|
|
159
|
-
4. **Skill Generation**: Convert detected patterns into reusable AI skills through the dashboard at [skillo.one](https://skillo.one).
|
|
160
|
-
|
|
161
|
-
5. **Team Sharing**: Skills are automatically matched to team members via git remote detection—no GitHub OAuth required.
|
|
162
|
-
|
|
163
|
-
## Directory Structure
|
|
164
|
-
|
|
165
|
-
```
|
|
166
|
-
~/.config/skillo/ # Config directory
|
|
167
|
-
└── config.yaml # Configuration + API key
|
|
168
|
-
|
|
169
|
-
~/.claude/skills/ # Skills directory (Claude Code)
|
|
170
|
-
├── my-workflow/
|
|
171
|
-
│ ├── SKILL.md
|
|
172
|
-
│ └── scripts/
|
|
173
|
-
└── another-skill/
|
|
174
|
-
└── SKILL.md
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
## Privacy
|
|
178
|
-
|
|
179
|
-
Skillo is built with privacy as a core principle:
|
|
180
|
-
|
|
181
|
-
- **Explicit Opt-In** — Run `skillo track` to enable tracking per project
|
|
182
|
-
- **Local Reading** — Claude Code history is read locally, never scraped
|
|
183
|
-
- **No GitHub OAuth** — Git remote detection works locally
|
|
184
|
-
- **Secure Sync** — All data encrypted in transit
|
|
185
|
-
|
|
186
|
-
## Requirements
|
|
187
|
-
|
|
188
|
-
- Node.js 18+
|
|
189
|
-
- npm or yarn
|
|
190
|
-
- Claude Code (for conversation sync)
|
|
191
|
-
|
|
192
|
-
## Support
|
|
193
|
-
|
|
194
|
-
- **Website:** [skillo.one](https://skillo.one)
|
|
195
|
-
|
|
196
|
-
## License
|
|
197
|
-
|
|
198
|
-
Proprietary - All rights reserved.
|
|
1
|
+
# Skillo CLI
|
|
2
|
+
|
|
3
|
+
Autonomous workflow learning & skill generation system.
|
|
4
|
+
|
|
5
|
+
**Learn workflows by observation, not explanation.**
|
|
6
|
+
|
|
7
|
+
**Website:** [skillo.one](https://skillo.one)
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Install globally
|
|
13
|
+
npm install -g skillo
|
|
14
|
+
|
|
15
|
+
# Or use with npx
|
|
16
|
+
npx skillo --help
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Login to skillo.one (opens browser for OAuth)
|
|
23
|
+
skillo login
|
|
24
|
+
|
|
25
|
+
# Enable tracking for your project (privacy-first: opt-in only)
|
|
26
|
+
cd /path/to/your/project
|
|
27
|
+
skillo track
|
|
28
|
+
|
|
29
|
+
# Sync Claude Code history
|
|
30
|
+
skillo claude sync
|
|
31
|
+
|
|
32
|
+
# Check tracking status
|
|
33
|
+
skillo project status
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Commands
|
|
37
|
+
|
|
38
|
+
### Authentication
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Login to skillo.one (opens browser)
|
|
42
|
+
skillo login
|
|
43
|
+
|
|
44
|
+
# Check login status
|
|
45
|
+
skillo whoami
|
|
46
|
+
|
|
47
|
+
# Logout
|
|
48
|
+
skillo logout
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Project Tracking (Privacy-First)
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Enable tracking for current project
|
|
55
|
+
skillo track
|
|
56
|
+
|
|
57
|
+
# Disable tracking
|
|
58
|
+
skillo untrack
|
|
59
|
+
|
|
60
|
+
# Check tracking status
|
|
61
|
+
skillo project status
|
|
62
|
+
|
|
63
|
+
# List all tracked projects
|
|
64
|
+
skillo project list
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Claude Code Sync
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Sync Claude Code conversation history
|
|
71
|
+
skillo claude sync
|
|
72
|
+
|
|
73
|
+
# Sync specific project only
|
|
74
|
+
skillo claude sync --project /path/to/project
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Sync
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# Pull skills and patterns from platform
|
|
81
|
+
skillo sync --pull
|
|
82
|
+
|
|
83
|
+
# Push local patterns to platform
|
|
84
|
+
skillo sync --push
|
|
85
|
+
|
|
86
|
+
# Sync everything
|
|
87
|
+
skillo sync --push --pull
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Patterns
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# List detected patterns
|
|
94
|
+
skillo patterns list
|
|
95
|
+
|
|
96
|
+
# Show pattern details
|
|
97
|
+
skillo patterns show <id>
|
|
98
|
+
|
|
99
|
+
# Generate a skill from a pattern
|
|
100
|
+
skillo patterns generate <id>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Skills
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# List generated skills
|
|
107
|
+
skillo skills list
|
|
108
|
+
|
|
109
|
+
# Show skill details
|
|
110
|
+
skillo skills show <name>
|
|
111
|
+
|
|
112
|
+
# Open skills directory
|
|
113
|
+
skillo skills open
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Status
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# Check overall status
|
|
120
|
+
skillo status
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Configuration
|
|
124
|
+
|
|
125
|
+
Configuration is stored in `~/.config/skillo/config.yaml`.
|
|
126
|
+
|
|
127
|
+
Key settings:
|
|
128
|
+
|
|
129
|
+
```yaml
|
|
130
|
+
# Pattern detection
|
|
131
|
+
pattern_detection:
|
|
132
|
+
min_count: 3 # Occurrences before detection
|
|
133
|
+
session_timeout: 30 # Minutes of inactivity
|
|
134
|
+
|
|
135
|
+
# Privacy
|
|
136
|
+
privacy:
|
|
137
|
+
auto_redact: true # Redact sensitive data
|
|
138
|
+
track_output: false # Don't track command output
|
|
139
|
+
|
|
140
|
+
# Notifications
|
|
141
|
+
notifications:
|
|
142
|
+
enabled: true
|
|
143
|
+
style: inline # inline, desktop, or both
|
|
144
|
+
|
|
145
|
+
# Skill generation
|
|
146
|
+
skill_generation:
|
|
147
|
+
include_scripts: true
|
|
148
|
+
include_examples: true
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## How It Works
|
|
152
|
+
|
|
153
|
+
1. **Login**: Run `skillo login` to authenticate with [skillo.one](https://skillo.one). Opens your browser for secure OAuth.
|
|
154
|
+
|
|
155
|
+
2. **Track Projects**: Run `skillo track` in any project directory to enable tracking. Nothing is tracked by default (privacy-first).
|
|
156
|
+
|
|
157
|
+
3. **Pattern Detection**: Skillo analyzes your Claude Code conversations to identify repeated workflows and patterns.
|
|
158
|
+
|
|
159
|
+
4. **Skill Generation**: Convert detected patterns into reusable AI skills through the dashboard at [skillo.one](https://skillo.one).
|
|
160
|
+
|
|
161
|
+
5. **Team Sharing**: Skills are automatically matched to team members via git remote detection—no GitHub OAuth required.
|
|
162
|
+
|
|
163
|
+
## Directory Structure
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
~/.config/skillo/ # Config directory
|
|
167
|
+
└── config.yaml # Configuration + API key
|
|
168
|
+
|
|
169
|
+
~/.claude/skills/ # Skills directory (Claude Code)
|
|
170
|
+
├── my-workflow/
|
|
171
|
+
│ ├── SKILL.md
|
|
172
|
+
│ └── scripts/
|
|
173
|
+
└── another-skill/
|
|
174
|
+
└── SKILL.md
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Privacy
|
|
178
|
+
|
|
179
|
+
Skillo is built with privacy as a core principle:
|
|
180
|
+
|
|
181
|
+
- **Explicit Opt-In** — Run `skillo track` to enable tracking per project
|
|
182
|
+
- **Local Reading** — Claude Code history is read locally, never scraped
|
|
183
|
+
- **No GitHub OAuth** — Git remote detection works locally
|
|
184
|
+
- **Secure Sync** — All data encrypted in transit
|
|
185
|
+
|
|
186
|
+
## Requirements
|
|
187
|
+
|
|
188
|
+
- Node.js 18+
|
|
189
|
+
- npm or yarn
|
|
190
|
+
- Claude Code (for conversation sync)
|
|
191
|
+
|
|
192
|
+
## Support
|
|
193
|
+
|
|
194
|
+
- **Website:** [skillo.one](https://skillo.one)
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
Proprietary - All rights reserved.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/paths.ts"],"sourcesContent":["/**\r\n * Path utilities for Skillo.\r\n *\r\n * Handles platform-specific path resolution for data directories,\r\n * configuration files, and skill storage locations.\r\n */\r\n\r\nimport { homedir } from \"os\";\r\nimport { join } from \"path\";\r\nimport { mkdirSync, existsSync } from \"fs\";\r\n\r\nexport function getHomeDir(): string {\r\n return homedir();\r\n}\r\n\r\n/**\r\n * Get Skillo data directory.\r\n * This is where the database and other data files are stored.\r\n *\r\n * - Linux/macOS: ~/.skillo/\r\n * - Windows: %USERPROFILE%/.skillo/\r\n */\r\nexport function getDataDir(): string {\r\n const envPath = process.env.SKILLO_DATA_DIR;\r\n if (envPath) return envPath;\r\n\r\n return join(getHomeDir(), \".skillo\");\r\n}\r\n\r\n/**\r\n * Get Skillo configuration directory.\r\n * This is where config.yaml and other configuration files are stored.\r\n *\r\n * - Linux: ~/.config/skillo/\r\n * - macOS: ~/.config/skillo/\r\n * - Windows: %USERPROFILE%/.config/skillo/\r\n */\r\nexport function getConfigDir(): string {\r\n const envPath = process.env.SKILLO_CONFIG_DIR;\r\n if (envPath) return envPath;\r\n\r\n const xdgConfig = process.env.XDG_CONFIG_HOME;\r\n if (xdgConfig) return join(xdgConfig, \"skillo\");\r\n\r\n return join(getHomeDir(), \".config\", \"skillo\");\r\n}\r\n\r\n/**\r\n * Get personal skills directory.\r\n * This is where generated skills are stored and where Claude Code\r\n * reads personal skills from.\r\n *\r\n * - All platforms: ~/.claude/skills/\r\n */\r\nexport function getSkillsDir(): string {\r\n const envPath = process.env.SKILLO_SKILLS_DIR;\r\n if (envPath) return envPath;\r\n\r\n return join(getClaudeDir(), \"skills\");\r\n}\r\n\r\n/**\r\n * Get Claude Code directory.\r\n * This is where Claude Code stores its data, including conversations.\r\n *\r\n * - All platforms: ~/.claude/\r\n */\r\nexport function getClaudeDir(): string {\r\n return join(getHomeDir(), \".claude\");\r\n}\r\n\r\n/**\r\n * Get project-specific skills directory.\r\n * This is where project-specific skills are stored.\r\n *\r\n * - Path: <project>/.claude/skills/\r\n */\r\nexport function getProjectSkillsDir(projectPath?: string): string {\r\n const project = projectPath || process.cwd();\r\n return join(project, \".claude\", \"skills\");\r\n}\r\n\r\n/**\r\n * Get team skills directory.\r\n * This is where team skills are synced to.\r\n *\r\n * - Path: ~/.claude/skills/team/<team-slug>/\r\n */\r\nexport function getTeamSkillsDir(teamSlug?: string): string {\r\n const base = join(getSkillsDir(), \"team\");\r\n if (teamSlug) return join(base, teamSlug);\r\n return base;\r\n}\r\n\r\n/**\r\n * Ensure a directory exists, creating it if necessary.\r\n *\r\n * @returns True if directory was created, False if it already existed.\r\n */\r\nexport function ensureDirectory(path: string): boolean {\r\n if (existsSync(path)) {\r\n return false;\r\n }\r\n\r\n mkdirSync(path, { recursive: true });\r\n return true;\r\n}\r\n\r\n/**\r\n * Get path to daemon log file.\r\n */\r\nexport function getLogFile(): string {\r\n return join(getDataDir(), \"daemon.log\");\r\n}\r\n\r\n/**\r\n * Get path to daemon PID file.\r\n */\r\nexport function getPidFile(): string {\r\n return join(getDataDir(), \"daemon.pid\");\r\n}\r\n\r\n/**\r\n * Get path to SQLite database file.\r\n */\r\nexport function getDbPath(): string {\r\n return join(getDataDir(), \"skillo.db\");\r\n}\r\n\r\n/**\r\n * Get path to config file.\r\n */\r\nexport function getConfigFile(): string {\r\n return join(getConfigDir(), \"config.yaml\");\r\n}\r\n\r\n/**\r\n * Get path to tracked projects cache file.\r\n * Written by daemon, read by shell hooks for fast project detection.\r\n */\r\nexport function getTrackedProjectsCacheFile(): string {\r\n return join(getDataDir(), \"tracked-projects.json\");\r\n}\r\n\r\n/**\r\n * Get path to active sessions directory.\r\n * Each terminal gets a file named by its PID.\r\n */\r\nexport function getActiveSessionsDir(): string {\r\n return join(getDataDir(), \"active-sessions\");\r\n}\r\n\r\n/**\r\n * Get path to shell integration directory.\r\n */\r\nexport function getShellIntegrationDir(): string {\r\n return join(getDataDir(), \"shell-integration\");\r\n}\r\n"],"mappings":";;;AAOA,SAAS,eAAe;AACxB,SAAS,YAAY;AACrB,SAAS,WAAW,kBAAkB;AAE/B,SAAS,aAAqB;AACnC,SAAO,QAAQ;AACjB;AASO,SAAS,aAAqB;AACnC,QAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,QAAS,QAAO;AAEpB,SAAO,KAAK,WAAW,GAAG,SAAS;AACrC;AAUO,SAAS,eAAuB;AACrC,QAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,QAAS,QAAO;AAEpB,QAAM,YAAY,QAAQ,IAAI;AAC9B,MAAI,UAAW,QAAO,KAAK,WAAW,QAAQ;AAE9C,SAAO,KAAK,WAAW,GAAG,WAAW,QAAQ;AAC/C;AASO,SAAS,eAAuB;AACrC,QAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,QAAS,QAAO;AAEpB,SAAO,KAAK,aAAa,GAAG,QAAQ;AACtC;AAQO,SAAS,eAAuB;AACrC,SAAO,KAAK,WAAW,GAAG,SAAS;AACrC;AAQO,SAAS,oBAAoB,aAA8B;AAChE,QAAM,UAAU,eAAe,QAAQ,IAAI;AAC3C,SAAO,KAAK,SAAS,WAAW,QAAQ;AAC1C;AAQO,SAAS,iBAAiB,UAA2B;AAC1D,QAAM,OAAO,KAAK,aAAa,GAAG,MAAM;AACxC,MAAI,SAAU,QAAO,KAAK,MAAM,QAAQ;AACxC,SAAO;AACT;AAOO,SAAS,gBAAgB,MAAuB;AACrD,MAAI,WAAW,IAAI,GAAG;AACpB,WAAO;AAAA,EACT;AAEA,YAAU,MAAM,EAAE,WAAW,KAAK,CAAC;AACnC,SAAO;AACT;AAKO,SAAS,aAAqB;AACnC,SAAO,KAAK,WAAW,GAAG,YAAY;AACxC;AAKO,SAAS,aAAqB;AACnC,SAAO,KAAK,WAAW,GAAG,YAAY;AACxC;AAKO,SAAS,YAAoB;AAClC,SAAO,KAAK,WAAW,GAAG,WAAW;AACvC;AAKO,SAAS,gBAAwB;AACtC,SAAO,KAAK,aAAa,GAAG,aAAa;AAC3C;AAMO,SAAS,8BAAsC;AACpD,SAAO,KAAK,WAAW,GAAG,uBAAuB;AACnD;AAMO,SAAS,uBAA+B;AAC7C,SAAO,KAAK,WAAW,GAAG,iBAAiB;AAC7C;AAKO,SAAS,yBAAiC;AAC/C,SAAO,KAAK,WAAW,GAAG,mBAAmB;AAC/C;","names":[]}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/utils/git.ts
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
import { existsSync } from "fs";
|
|
6
|
+
import { join, dirname, basename } from "path";
|
|
7
|
+
function getGitInfo(projectPath) {
|
|
8
|
+
const result = {
|
|
9
|
+
isGitRepo: false,
|
|
10
|
+
rootPath: null,
|
|
11
|
+
remote: null,
|
|
12
|
+
remoteNormalized: null,
|
|
13
|
+
branch: null,
|
|
14
|
+
projectName: null
|
|
15
|
+
};
|
|
16
|
+
try {
|
|
17
|
+
const rootPath = execSync("git rev-parse --show-toplevel", {
|
|
18
|
+
cwd: projectPath,
|
|
19
|
+
encoding: "utf-8",
|
|
20
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
21
|
+
}).trim();
|
|
22
|
+
if (!rootPath) return result;
|
|
23
|
+
result.isGitRepo = true;
|
|
24
|
+
result.rootPath = rootPath;
|
|
25
|
+
result.projectName = basename(rootPath);
|
|
26
|
+
try {
|
|
27
|
+
result.branch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
28
|
+
cwd: projectPath,
|
|
29
|
+
encoding: "utf-8",
|
|
30
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
31
|
+
}).trim();
|
|
32
|
+
} catch {
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
result.remote = execSync("git remote get-url origin", {
|
|
36
|
+
cwd: projectPath,
|
|
37
|
+
encoding: "utf-8",
|
|
38
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
39
|
+
}).trim();
|
|
40
|
+
} catch {
|
|
41
|
+
try {
|
|
42
|
+
const remotes = execSync("git remote", {
|
|
43
|
+
cwd: projectPath,
|
|
44
|
+
encoding: "utf-8",
|
|
45
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
46
|
+
}).trim().split("\n");
|
|
47
|
+
if (remotes.length > 0 && remotes[0]) {
|
|
48
|
+
result.remote = execSync(`git remote get-url ${remotes[0]}`, {
|
|
49
|
+
cwd: projectPath,
|
|
50
|
+
encoding: "utf-8",
|
|
51
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
52
|
+
}).trim();
|
|
53
|
+
}
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (result.remote) {
|
|
58
|
+
result.remoteNormalized = normalizeGitRemote(result.remote);
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
}
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
function normalizeGitRemote(remoteUrl) {
|
|
65
|
+
let url = remoteUrl.trim();
|
|
66
|
+
url = url.replace(/\.git$/, "");
|
|
67
|
+
const sshMatch = url.match(/^git@([^:]+):(.+)$/);
|
|
68
|
+
if (sshMatch) {
|
|
69
|
+
return `${sshMatch[1]}/${sshMatch[2]}`;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
let normalized = url.replace(/^(https?|git|ssh):\/\//, "").replace(/^git@/, "");
|
|
73
|
+
normalized = normalized.replace(/^[^@]+@/, "");
|
|
74
|
+
normalized = normalized.replace(/:\d+\//, "/");
|
|
75
|
+
normalized = normalized.replace(/\/+/g, "/");
|
|
76
|
+
normalized = normalized.replace(/^\/|\/$/g, "");
|
|
77
|
+
return normalized.toLowerCase();
|
|
78
|
+
} catch {
|
|
79
|
+
return url.toLowerCase();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function findGitRoot(startPath) {
|
|
83
|
+
let current = startPath;
|
|
84
|
+
while (current !== dirname(current)) {
|
|
85
|
+
if (existsSync(join(current, ".git"))) {
|
|
86
|
+
return current;
|
|
87
|
+
}
|
|
88
|
+
current = dirname(current);
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
function isInsideGitRepo(path) {
|
|
93
|
+
return findGitRoot(path) !== null;
|
|
94
|
+
}
|
|
95
|
+
function parseGitRemote(normalizedRemote) {
|
|
96
|
+
const parts = normalizedRemote.split("/");
|
|
97
|
+
if (parts.length < 3) return null;
|
|
98
|
+
return {
|
|
99
|
+
host: parts[0],
|
|
100
|
+
owner: parts[1],
|
|
101
|
+
repo: parts.slice(2).join("/")
|
|
102
|
+
// Handle nested paths like gitlab subgroups
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export {
|
|
107
|
+
getGitInfo,
|
|
108
|
+
normalizeGitRemote,
|
|
109
|
+
findGitRoot,
|
|
110
|
+
isInsideGitRepo,
|
|
111
|
+
parseGitRemote
|
|
112
|
+
};
|
|
113
|
+
//# sourceMappingURL=chunk-6GOJPFZ7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/git.ts"],"sourcesContent":["/**\r\n * Git utilities for project detection and identification.\r\n */\r\n\r\nimport { execSync } from \"child_process\";\r\nimport { existsSync } from \"fs\";\r\nimport { join, dirname, basename } from \"path\";\r\n\r\nexport interface GitInfo {\r\n isGitRepo: boolean;\r\n rootPath: string | null;\r\n remote: string | null;\r\n remoteNormalized: string | null;\r\n branch: string | null;\r\n projectName: string | null;\r\n}\r\n\r\n/**\r\n * Get git information for a given path.\r\n */\r\nexport function getGitInfo(projectPath: string): GitInfo {\r\n const result: GitInfo = {\r\n isGitRepo: false,\r\n rootPath: null,\r\n remote: null,\r\n remoteNormalized: null,\r\n branch: null,\r\n projectName: null,\r\n };\r\n\r\n try {\r\n // Check if it's a git repo and get root\r\n const rootPath = execSync(\"git rev-parse --show-toplevel\", {\r\n cwd: projectPath,\r\n encoding: \"utf-8\",\r\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\r\n }).trim();\r\n\r\n if (!rootPath) return result;\r\n\r\n result.isGitRepo = true;\r\n result.rootPath = rootPath;\r\n result.projectName = basename(rootPath);\r\n\r\n // Get current branch\r\n try {\r\n result.branch = execSync(\"git rev-parse --abbrev-ref HEAD\", {\r\n cwd: projectPath,\r\n encoding: \"utf-8\",\r\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\r\n }).trim();\r\n } catch {\r\n // Might be in detached HEAD state\r\n }\r\n\r\n // Get remote URL (prefer origin)\r\n try {\r\n result.remote = execSync(\"git remote get-url origin\", {\r\n cwd: projectPath,\r\n encoding: \"utf-8\",\r\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\r\n }).trim();\r\n } catch {\r\n // Try to get any remote\r\n try {\r\n const remotes = execSync(\"git remote\", {\r\n cwd: projectPath,\r\n encoding: \"utf-8\",\r\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\r\n }).trim().split(\"\\n\");\r\n\r\n if (remotes.length > 0 && remotes[0]) {\r\n result.remote = execSync(`git remote get-url ${remotes[0]}`, {\r\n cwd: projectPath,\r\n encoding: \"utf-8\",\r\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\r\n }).trim();\r\n }\r\n } catch {\r\n // No remotes configured\r\n }\r\n }\r\n\r\n // Normalize the remote URL for team matching\r\n if (result.remote) {\r\n result.remoteNormalized = normalizeGitRemote(result.remote);\r\n }\r\n } catch {\r\n // Not a git repository\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Normalize a git remote URL for consistent matching across team members.\r\n * Converts various formats to: \"host/owner/repo\"\r\n *\r\n * Examples:\r\n * - git@github.com:acme/webapp.git -> github.com/acme/webapp\r\n * - https://github.com/acme/webapp.git -> github.com/acme/webapp\r\n * - ssh://git@github.com/acme/webapp -> github.com/acme/webapp\r\n * - git://github.com/acme/webapp.git -> github.com/acme/webapp\r\n */\r\nexport function normalizeGitRemote(remoteUrl: string): string {\r\n let url = remoteUrl.trim();\r\n\r\n // Remove .git suffix\r\n url = url.replace(/\\.git$/, \"\");\r\n\r\n // Handle SSH format: git@github.com:owner/repo\r\n const sshMatch = url.match(/^git@([^:]+):(.+)$/);\r\n if (sshMatch) {\r\n return `${sshMatch[1]}/${sshMatch[2]}`;\r\n }\r\n\r\n // Handle various URL formats\r\n try {\r\n // Remove protocol prefix for parsing\r\n let normalized = url\r\n .replace(/^(https?|git|ssh):\\/\\//, \"\")\r\n .replace(/^git@/, \"\");\r\n\r\n // Remove username if present (e.g., git@host or user@host)\r\n normalized = normalized.replace(/^[^@]+@/, \"\");\r\n\r\n // Remove port if present\r\n normalized = normalized.replace(/:\\d+\\//, \"/\");\r\n\r\n // Clean up any double slashes\r\n normalized = normalized.replace(/\\/+/g, \"/\");\r\n\r\n // Remove leading/trailing slashes\r\n normalized = normalized.replace(/^\\/|\\/$/g, \"\");\r\n\r\n return normalized.toLowerCase();\r\n } catch {\r\n // Fallback: return as-is but lowercase\r\n return url.toLowerCase();\r\n }\r\n}\r\n\r\n/**\r\n * Find git root by walking up the directory tree.\r\n */\r\nexport function findGitRoot(startPath: string): string | null {\r\n let current = startPath;\r\n\r\n while (current !== dirname(current)) {\r\n if (existsSync(join(current, \".git\"))) {\r\n return current;\r\n }\r\n current = dirname(current);\r\n }\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * Check if a path is inside a git repository.\r\n */\r\nexport function isInsideGitRepo(path: string): boolean {\r\n return findGitRoot(path) !== null;\r\n}\r\n\r\n/**\r\n * Extract owner and repo name from a normalized git remote.\r\n */\r\nexport function parseGitRemote(normalizedRemote: string): { host: string; owner: string; repo: string } | null {\r\n const parts = normalizedRemote.split(\"/\");\r\n\r\n if (parts.length < 3) return null;\r\n\r\n return {\r\n host: parts[0],\r\n owner: parts[1],\r\n repo: parts.slice(2).join(\"/\"), // Handle nested paths like gitlab subgroups\r\n };\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ensureDirectory,
|
|
4
|
+
getDataDir
|
|
5
|
+
} from "./chunk-63FVALWX.js";
|
|
6
|
+
|
|
7
|
+
// src/utils/status-writer.ts
|
|
8
|
+
import { writeFileSync, readFileSync, existsSync } from "fs";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
var STATUS_FILE = "daemon-status.json";
|
|
11
|
+
var WRITE_INTERVAL_MS = 1e4;
|
|
12
|
+
var StatusWriter = class _StatusWriter {
|
|
13
|
+
intervalId = null;
|
|
14
|
+
status;
|
|
15
|
+
filePath;
|
|
16
|
+
constructor() {
|
|
17
|
+
this.filePath = join(getDataDir(), STATUS_FILE);
|
|
18
|
+
this.status = {
|
|
19
|
+
running: true,
|
|
20
|
+
pid: process.pid,
|
|
21
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
23
|
+
claudeWatcher: { lastSync: null, promptsSynced: 0, lastError: null },
|
|
24
|
+
skillDetector: { deployedSkills: 0, usagesDetected: 0, lastDetection: null, lastError: null },
|
|
25
|
+
trackedProjects: [],
|
|
26
|
+
activeSessions: 0
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/** Start periodic writes */
|
|
30
|
+
start() {
|
|
31
|
+
ensureDirectory(getDataDir());
|
|
32
|
+
this.write();
|
|
33
|
+
this.intervalId = setInterval(() => this.write(), WRITE_INTERVAL_MS);
|
|
34
|
+
}
|
|
35
|
+
/** Stop writing and mark as not running */
|
|
36
|
+
stop() {
|
|
37
|
+
if (this.intervalId) {
|
|
38
|
+
clearInterval(this.intervalId);
|
|
39
|
+
this.intervalId = null;
|
|
40
|
+
}
|
|
41
|
+
this.status.running = false;
|
|
42
|
+
this.status.pid = null;
|
|
43
|
+
this.status.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
44
|
+
this.write();
|
|
45
|
+
}
|
|
46
|
+
/** Merge partial status updates */
|
|
47
|
+
update(partial) {
|
|
48
|
+
if (partial.claudeWatcher) {
|
|
49
|
+
this.status.claudeWatcher = { ...this.status.claudeWatcher, ...partial.claudeWatcher };
|
|
50
|
+
delete partial.claudeWatcher;
|
|
51
|
+
}
|
|
52
|
+
if (partial.skillDetector) {
|
|
53
|
+
this.status.skillDetector = { ...this.status.skillDetector, ...partial.skillDetector };
|
|
54
|
+
delete partial.skillDetector;
|
|
55
|
+
}
|
|
56
|
+
if (partial.skillInstaller) {
|
|
57
|
+
this.status.skillInstaller = { ...this.status.skillInstaller, ...partial.skillInstaller };
|
|
58
|
+
delete partial.skillInstaller;
|
|
59
|
+
}
|
|
60
|
+
Object.assign(this.status, partial);
|
|
61
|
+
}
|
|
62
|
+
/** Get the status file path */
|
|
63
|
+
static getStatusFilePath() {
|
|
64
|
+
return join(getDataDir(), STATUS_FILE);
|
|
65
|
+
}
|
|
66
|
+
/** Read current status from disk (static, for tray/status commands) */
|
|
67
|
+
static read() {
|
|
68
|
+
const filePath = _StatusWriter.getStatusFilePath();
|
|
69
|
+
try {
|
|
70
|
+
if (existsSync(filePath)) {
|
|
71
|
+
return JSON.parse(readFileSync(filePath, "utf-8"));
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
write() {
|
|
78
|
+
this.status.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
79
|
+
try {
|
|
80
|
+
writeFileSync(this.filePath, JSON.stringify(this.status, null, 2), "utf-8");
|
|
81
|
+
} catch {
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export {
|
|
87
|
+
StatusWriter
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=chunk-6UGTWBUW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/status-writer.ts"],"sourcesContent":["/**\r\n * StatusWriter — writes daemon status JSON for tray icon and diagnostics.\r\n *\r\n * Writes ~/.skillo/daemon-status.json every 10s and on key events.\r\n * Tray icon and `skillo status` read this file.\r\n */\r\n\r\nimport { writeFileSync, readFileSync, existsSync } from \"fs\";\r\nimport { join } from \"path\";\r\nimport { getDataDir, ensureDirectory } from \"./paths.js\";\r\n\r\nexport interface DaemonStatus {\r\n running: boolean;\r\n pid: number | null;\r\n startedAt: string | null;\r\n updatedAt: string;\r\n user?: string;\r\n claudeWatcher?: {\r\n lastSync?: string | null;\r\n promptsSynced?: number;\r\n lastError?: string | null;\r\n };\r\n skillDetector?: {\r\n deployedSkills?: number;\r\n usagesDetected?: number;\r\n lastDetection?: string | null;\r\n lastError?: string | null;\r\n };\r\n skillInstaller?: {\r\n lastInstall?: string | null;\r\n lastUninstall?: string | null;\r\n lastAction?: string | null;\r\n lastError?: string | null;\r\n };\r\n trackedProjects?: Array<{ name: string; path: string }>;\r\n activeSessions?: number;\r\n}\r\n\r\nconst STATUS_FILE = \"daemon-status.json\";\r\nconst WRITE_INTERVAL_MS = 10000;\r\n\r\nexport class StatusWriter {\r\n private intervalId: ReturnType<typeof setInterval> | null = null;\r\n private status: DaemonStatus;\r\n private filePath: string;\r\n\r\n constructor() {\r\n this.filePath = join(getDataDir(), STATUS_FILE);\r\n this.status = {\r\n running: true,\r\n pid: process.pid,\r\n startedAt: new Date().toISOString(),\r\n updatedAt: new Date().toISOString(),\r\n claudeWatcher: { lastSync: null, promptsSynced: 0, lastError: null },\r\n skillDetector: { deployedSkills: 0, usagesDetected: 0, lastDetection: null, lastError: null },\r\n trackedProjects: [],\r\n activeSessions: 0,\r\n };\r\n }\r\n\r\n /** Start periodic writes */\r\n start() {\r\n ensureDirectory(getDataDir());\r\n this.write();\r\n this.intervalId = setInterval(() => this.write(), WRITE_INTERVAL_MS);\r\n }\r\n\r\n /** Stop writing and mark as not running */\r\n stop() {\r\n if (this.intervalId) {\r\n clearInterval(this.intervalId);\r\n this.intervalId = null;\r\n }\r\n this.status.running = false;\r\n this.status.pid = null;\r\n this.status.updatedAt = new Date().toISOString();\r\n this.write();\r\n }\r\n\r\n /** Merge partial status updates */\r\n update(partial: Partial<DaemonStatus>) {\r\n // Deep merge for nested objects\r\n if (partial.claudeWatcher) {\r\n this.status.claudeWatcher = { ...this.status.claudeWatcher, ...partial.claudeWatcher };\r\n delete partial.claudeWatcher;\r\n }\r\n if (partial.skillDetector) {\r\n this.status.skillDetector = { ...this.status.skillDetector, ...partial.skillDetector };\r\n delete partial.skillDetector;\r\n }\r\n if (partial.skillInstaller) {\r\n this.status.skillInstaller = { ...this.status.skillInstaller, ...partial.skillInstaller };\r\n delete partial.skillInstaller;\r\n }\r\n Object.assign(this.status, partial);\r\n }\r\n\r\n /** Get the status file path */\r\n static getStatusFilePath(): string {\r\n return join(getDataDir(), STATUS_FILE);\r\n }\r\n\r\n /** Read current status from disk (static, for tray/status commands) */\r\n static read(): DaemonStatus | null {\r\n const filePath = StatusWriter.getStatusFilePath();\r\n try {\r\n if (existsSync(filePath)) {\r\n return JSON.parse(readFileSync(filePath, \"utf-8\"));\r\n }\r\n } catch {\r\n // ignore\r\n }\r\n return null;\r\n }\r\n\r\n private write() {\r\n this.status.updatedAt = new Date().toISOString();\r\n try {\r\n writeFileSync(this.filePath, JSON.stringify(this.status, null, 2), \"utf-8\");\r\n } catch {\r\n // Can't write status, ignore\r\n }\r\n }\r\n}\r\n"],"mappings":";;;;;;;AAOA,SAAS,eAAe,cAAc,kBAAkB;AACxD,SAAS,YAAY;AA8BrB,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,QAAI,QAAQ,gBAAgB;AAC1B,WAAK,OAAO,iBAAiB,EAAE,GAAG,KAAK,OAAO,gBAAgB,GAAG,QAAQ,eAAe;AACxF,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;","names":[]}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
ensureDirectory,
|
|
4
4
|
getDbPath
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-63FVALWX.js";
|
|
6
6
|
|
|
7
7
|
// src/core/database.ts
|
|
8
8
|
import initSqlJs from "sql.js";
|
|
@@ -460,4 +460,4 @@ var SkilloDatabase = class {
|
|
|
460
460
|
export {
|
|
461
461
|
SkilloDatabase
|
|
462
462
|
};
|
|
463
|
-
//# sourceMappingURL=chunk-
|
|
463
|
+
//# sourceMappingURL=chunk-73NUWYUO.js.map
|