harveyz-skill 0.6.0 → 0.6.1
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/bin/cli.js +157 -3
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -37,15 +37,20 @@ function printHelp() {
|
|
|
37
37
|
hskill install --scope <s> set scope: user (default) or project
|
|
38
38
|
hskill install --force overwrite existing installs
|
|
39
39
|
hskill list list available skills and bundles
|
|
40
|
+
hskill status show install status for all skills and tools
|
|
41
|
+
hskill outdated list skills and tools with available updates
|
|
42
|
+
hskill info <name> show install detail for a skill or tool
|
|
40
43
|
hskill update update hskill to the latest version
|
|
41
|
-
hskill
|
|
44
|
+
hskill version show version
|
|
42
45
|
hskill --help show this help
|
|
43
46
|
|
|
44
47
|
${chalk.cyan('Examples:')}
|
|
45
48
|
hskill install --bundle dev --target claude
|
|
46
49
|
hskill install --skill git-workflow-init --target claude --scope project
|
|
47
50
|
hskill install --tool p-launch
|
|
48
|
-
hskill
|
|
51
|
+
hskill status
|
|
52
|
+
hskill outdated
|
|
53
|
+
hskill info git-workflow-init
|
|
49
54
|
`)
|
|
50
55
|
}
|
|
51
56
|
|
|
@@ -54,7 +59,7 @@ if (args[0] === '--help' || args[0] === '-h') {
|
|
|
54
59
|
process.exit(0)
|
|
55
60
|
}
|
|
56
61
|
|
|
57
|
-
if (args[0] === '--version' || args[0] === '-v') {
|
|
62
|
+
if (args[0] === '--version' || args[0] === '-v' || subcommand === 'version') {
|
|
58
63
|
console.log(version)
|
|
59
64
|
process.exit(0)
|
|
60
65
|
}
|
|
@@ -92,6 +97,155 @@ if (subcommand === 'list') {
|
|
|
92
97
|
process.exit(0)
|
|
93
98
|
}
|
|
94
99
|
|
|
100
|
+
// ── Status / Outdated ─────────────────────────────────────────────────────────
|
|
101
|
+
if (subcommand === 'status' || subcommand === 'outdated') {
|
|
102
|
+
const outdatedOnly = subcommand === 'outdated'
|
|
103
|
+
const skillItems = getAllSkillItems()
|
|
104
|
+
const toolItems = getAllToolItems()
|
|
105
|
+
|
|
106
|
+
function icon(status) {
|
|
107
|
+
if (status === 'up-to-date') return chalk.green('✓')
|
|
108
|
+
if (status === 'update') return chalk.yellow('↑')
|
|
109
|
+
return chalk.dim('—')
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const skillRows = skillItems.map(s => {
|
|
113
|
+
const inst = checkInstalled(s.skillName, s.version ?? '—')
|
|
114
|
+
return {
|
|
115
|
+
name: s.skillName, version: s.version ?? '—',
|
|
116
|
+
userStatus: scopeSummary(inst.user), projectStatus: scopeSummary(inst.project),
|
|
117
|
+
userDetail: inst.user, projectDetail: inst.project,
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
const toolRows = toolItems.map(t => {
|
|
121
|
+
const inst = checkToolInstalled(t.toolName, t.srcPath)
|
|
122
|
+
return { name: t.toolName, version: t.version ?? '—', ...inst }
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
if (outdatedOnly) {
|
|
126
|
+
const outdatedSkills = skillRows.filter(r => r.userStatus === 'update' || r.projectStatus === 'update')
|
|
127
|
+
const outdatedTools = toolRows.filter(r => r.status === 'update')
|
|
128
|
+
|
|
129
|
+
if (!outdatedSkills.length && !outdatedTools.length) {
|
|
130
|
+
console.log(chalk.green.bold('\n ✓ All installed skills and tools are up to date\n'))
|
|
131
|
+
process.exit(0)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const targets = ['claude', 'cursor', 'codex']
|
|
135
|
+
|
|
136
|
+
if (outdatedSkills.length) {
|
|
137
|
+
console.log('\n ' + chalk.bold('SKILLS WITH UPDATES'))
|
|
138
|
+
const nw = Math.max(...outdatedSkills.map(r => r.name.length))
|
|
139
|
+
for (const r of outdatedSkills) {
|
|
140
|
+
console.log(' ' + r.name.padEnd(nw) + ' available: ' + chalk.yellow(r.version))
|
|
141
|
+
for (const scope of ['user', 'project']) {
|
|
142
|
+
const detail = scope === 'user' ? r.userDetail : r.projectDetail
|
|
143
|
+
for (const t of targets) {
|
|
144
|
+
if (detail[t].status === 'update') {
|
|
145
|
+
console.log(' ' + chalk.dim(scope.padEnd(8) + t.padEnd(8)) + detail[t].version + ' → ' + chalk.yellow(r.version))
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (outdatedTools.length) {
|
|
153
|
+
console.log('\n ' + chalk.bold('TOOLS WITH UPDATES'))
|
|
154
|
+
const nw = Math.max(...outdatedTools.map(r => r.name.length))
|
|
155
|
+
for (const r of outdatedTools) {
|
|
156
|
+
console.log(' ' + r.name.padEnd(nw) + ' ' + chalk.dim(r.version) + ' → ' + chalk.yellow(r.version))
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
console.log('')
|
|
160
|
+
process.exit(0)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Full status table
|
|
164
|
+
const allNames = [...skillRows, ...toolRows].map(r => r.name)
|
|
165
|
+
const allVers = [...skillRows, ...toolRows].map(r => r.version)
|
|
166
|
+
const nw = Math.max(...allNames.map(n => n.length), 4)
|
|
167
|
+
const vw = Math.max(...allVers.map(v => v.length), 7)
|
|
168
|
+
const sep = chalk.dim(' ' + '─'.repeat(nw + vw + 20))
|
|
169
|
+
|
|
170
|
+
console.log('')
|
|
171
|
+
console.log(' ' + chalk.bold('SKILLS') + chalk.dim(` — ${skillRows.length} available`))
|
|
172
|
+
console.log(sep)
|
|
173
|
+
console.log(' ' + ''.padEnd(nw + vw + 3) + chalk.dim('user project'))
|
|
174
|
+
for (const r of skillRows) {
|
|
175
|
+
const u = icon(r.userStatus), p = icon(r.projectStatus)
|
|
176
|
+
console.log(' ' + r.name.padEnd(nw) + ' ' + chalk.dim(r.version.padEnd(vw)) + ' ' + u + ' ' + p)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
console.log('')
|
|
180
|
+
console.log(' ' + chalk.bold('TOOLS') + chalk.dim(` — ${toolRows.length} available`))
|
|
181
|
+
console.log(sep)
|
|
182
|
+
for (const r of toolRows) {
|
|
183
|
+
console.log(' ' + r.name.padEnd(nw) + ' ' + chalk.dim(r.version.padEnd(vw)) + ' ' + icon(r.status))
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const installedSkills = skillRows.filter(r => r.userStatus !== 'none' || r.projectStatus !== 'none').length
|
|
187
|
+
const installedTools = toolRows.filter(r => r.status !== 'none').length
|
|
188
|
+
const outdatedCount = skillRows.filter(r => r.userStatus === 'update' || r.projectStatus === 'update').length
|
|
189
|
+
+ toolRows.filter(r => r.status === 'update').length
|
|
190
|
+
const outdatedNote = outdatedCount ? ' · ' + chalk.yellow(outdatedCount + ' outdated') : ''
|
|
191
|
+
console.log('')
|
|
192
|
+
console.log(chalk.dim(` ${installedSkills} of ${skillRows.length} skills installed · ${installedTools} of ${toolRows.length} tools installed${outdatedNote}`))
|
|
193
|
+
console.log('')
|
|
194
|
+
process.exit(0)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ── Info ──────────────────────────────────────────────────────────────────────
|
|
198
|
+
if (subcommand === 'info') {
|
|
199
|
+
const name = args[1]
|
|
200
|
+
if (!name) {
|
|
201
|
+
console.error(chalk.red(' ✗ Usage: hskill info <skill-or-tool-name>'))
|
|
202
|
+
process.exit(1)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const skillItems = getAllSkillItems()
|
|
206
|
+
const toolItems = getAllToolItems()
|
|
207
|
+
const skill = skillItems.find(s => s.skillName === name)
|
|
208
|
+
const tool = toolItems.find(t => t.toolName === name)
|
|
209
|
+
|
|
210
|
+
if (!skill && !tool) {
|
|
211
|
+
console.error(chalk.red(` ✗ Unknown: "${name}"`))
|
|
212
|
+
process.exit(1)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const targets = ['claude', 'cursor', 'codex']
|
|
216
|
+
|
|
217
|
+
function statusLabel(status) {
|
|
218
|
+
if (status === 'up-to-date') return chalk.green('✓ up to date')
|
|
219
|
+
if (status === 'update') return chalk.yellow('↑ update available')
|
|
220
|
+
return chalk.dim('— not installed')
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
console.log('')
|
|
224
|
+
if (skill) {
|
|
225
|
+
const inst = checkInstalled(skill.skillName, skill.version ?? '—')
|
|
226
|
+
console.log(' ' + chalk.bold(skill.skillName) + chalk.dim(' skill'))
|
|
227
|
+
console.log(' ' + chalk.dim('available: ') + (skill.version ?? '—'))
|
|
228
|
+
console.log('')
|
|
229
|
+
for (const [scopeLabel, detail] of [['USER LEVEL', inst.user], ['PROJECT LEVEL', inst.project]]) {
|
|
230
|
+
console.log(' ' + chalk.bold(scopeLabel))
|
|
231
|
+
for (const t of targets) {
|
|
232
|
+
const { version: v, status } = detail[t]
|
|
233
|
+
console.log(' ' + t.padEnd(8) + chalk.dim(v.padEnd(10)) + statusLabel(status))
|
|
234
|
+
}
|
|
235
|
+
console.log('')
|
|
236
|
+
}
|
|
237
|
+
} else {
|
|
238
|
+
const inst = checkToolInstalled(tool.toolName, tool.srcPath)
|
|
239
|
+
console.log(' ' + chalk.bold(tool.toolName) + chalk.dim(' shell tool'))
|
|
240
|
+
console.log(' ' + chalk.dim('available: ') + (tool.version ?? '—'))
|
|
241
|
+
console.log('')
|
|
242
|
+
console.log(' ' + chalk.bold('INSTALL STATUS'))
|
|
243
|
+
console.log(' ' + '~/.local/bin'.padEnd(14) + chalk.dim(inst.version.padEnd(10)) + statusLabel(inst.status))
|
|
244
|
+
console.log('')
|
|
245
|
+
}
|
|
246
|
+
process.exit(0)
|
|
247
|
+
}
|
|
248
|
+
|
|
95
249
|
// ── Install ───────────────────────────────────────────────────────────────────
|
|
96
250
|
// subcommand is 'install' or omitted (default behavior)
|
|
97
251
|
const installArgs = subcommand === 'install' ? args.slice(1) : args
|