harveyz-skill 0.3.0 → 0.5.0

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 CHANGED
@@ -4,7 +4,7 @@ Harvey 的个人 Claude Code 技能仓库。
4
4
 
5
5
  ## 技能清单
6
6
 
7
- 按 bundle 分组,`npx harveyz-skill --bundle <bundle>` 安装对应分组。
7
+ 按 bundle 分组,`hskill install --bundle <bundle>` 安装对应分组。
8
8
 
9
9
  ### analysis — 分析工具
10
10
 
@@ -63,7 +63,7 @@ Harvey 的个人 Claude Code 技能仓库。
63
63
 
64
64
  ## 工具清单
65
65
 
66
- Shell 工具,通过 `npx harveyz-skill --bundle shell-tools` 安装。
66
+ Shell 工具,通过 `hskill install --tool p-launch` 安装。
67
67
 
68
68
  | 工具 | 说明 |
69
69
  |------|------|
@@ -74,50 +74,39 @@ Shell 工具,通过 `npx harveyz-skill --bundle shell-tools` 安装。
74
74
 
75
75
  ## 安装
76
76
 
77
- ### 推荐:npx 一键安装
78
-
79
77
  ```bash
80
- npx harveyz-skill
78
+ npm install -g harveyz-skill
81
79
  ```
82
80
 
83
- 交互式选择 bundle 和目标工具(Claude Code / Cursor / Codex)。
81
+ 安装后使用 `hskill` 命令管理 skills。
84
82
 
85
- **无交互模式:**
86
- ```bash
87
- npx harveyz-skill --bundle brainstorming --target claude
88
- npx harveyz-skill --bundle brainstorming,dev --target all
89
- ```
83
+ ### 常用命令
90
84
 
91
- **查看可用 bundle:**
92
85
  ```bash
93
- npx harveyz-skill list
86
+ hskill # 交互式选择安装
87
+ hskill install --bundle dev --target claude # 安装指定 bundle
88
+ hskill install --skill git-workflow-init # 安装指定 skill
89
+ hskill install --tool p-launch # 安装 shell 工具
90
+ hskill list # 查看可用 skill
91
+ hskill update # 更新到最新版
92
+ hskill --help # 查看帮助
94
93
  ```
95
94
 
96
- ### 本地源码安装
95
+ Skills 安装到 `~/.claude/skills/`,shell 工具安装到 `~/.local/bin/`。
97
96
 
98
- 克隆仓库后,用 CLI 安装:
97
+ ### 本地源码开发
99
98
 
100
99
  ```bash
101
- # 交互式选择 bundle 和目标工具
102
- node bin/cli.js
103
-
104
100
  # 安装指定 skill
105
- node bin/cli.js --skill mermaid-diagram --target claude
106
- node bin/cli.js --skill brainstorming,writing-plans --target claude
107
-
108
- # 安装整个 skill bundle
109
- node bin/cli.js --bundle dev --target claude
101
+ node bin/cli.js install --skill mermaid-diagram --target claude
110
102
 
111
- # 安装指定 shell 工具(固定安装到 ~/.local/bin)
112
- node bin/cli.js --tool p-launch
103
+ # 安装整个 bundle
104
+ node bin/cli.js install --bundle dev --target claude
113
105
 
114
106
  # 覆盖已有安装
115
- node bin/cli.js --skill mermaid-diagram --target claude --force
116
- node bin/cli.js --tool p-launch --force
107
+ node bin/cli.js install --skill mermaid-diagram --target claude --force
117
108
  ```
118
109
 
119
- Skills 安装到 `~/.claude/skills/`,shell 工具安装到 `~/.local/bin/`。
120
-
121
110
  ## Skill 开发指南
122
111
 
123
112
  ### 命名规范
package/bin/cli.js CHANGED
@@ -1,29 +1,75 @@
1
1
  #!/usr/bin/env node
2
- import { checkbox } from '@inquirer/prompts'
2
+ import { checkbox, select } from '@inquirer/prompts'
3
3
  import chalk from 'chalk'
4
+ import { execSync } from 'child_process'
5
+ import { createRequire } from 'module'
4
6
  import {
5
7
  buildAllChoices, getAllSkillItems, getAllToolItems,
6
8
  resolveSkills, resolveSkillsByName, resolveTools, resolveToolsByName,
7
9
  TOOL_BUNDLE_CHOICES,
8
10
  } from '../lib/bundles.js'
9
- import { TARGET_CHOICES, resolveTargets, TARGETS } from '../lib/targets.js'
11
+ import { buildTargetChoices, resolveTargets, TARGETS } from '../lib/targets.js'
10
12
  import { installSkills, installTools } from '../lib/installer.js'
11
13
 
14
+ const require = createRequire(import.meta.url)
15
+ const { version } = require('../package.json')
16
+
12
17
  const args = process.argv.slice(2)
13
- const forceFlag = args.includes('--force')
14
- const bundleIdx = args.indexOf('--bundle')
15
- const targetIdx = args.indexOf('--target')
16
- const toolIdx = args.indexOf('--tool')
17
- const skillIdx = args.indexOf('--skill')
18
- const bundleArg = bundleIdx !== -1 ? args[bundleIdx + 1] : undefined
19
- const targetArg = targetIdx !== -1 ? args[targetIdx + 1] : undefined
20
- const toolArg = toolIdx !== -1 ? args[toolIdx + 1] : undefined
21
- const skillArg = skillIdx !== -1 ? args[skillIdx + 1] : undefined
22
-
23
- if (args[0] === 'list') {
24
- const { createRequire } = await import('module')
25
- const require = createRequire(import.meta.url)
26
- const { bundleMeta, skills, toolBundleMeta = {}, tools = [] } = require('../skills-index.json')
18
+ const subcommand = args[0]
19
+
20
+ // ── Help ─────────────────────────────────────────────────────────────────────
21
+ function printHelp() {
22
+ console.log(`
23
+ ${chalk.bold('hskill')} skill manager for Claude Code, Cursor, and Codex v${version}
24
+
25
+ ${chalk.cyan('Usage:')}
26
+ hskill interactive install
27
+ hskill install interactive install (explicit)
28
+ hskill install --bundle <b> install a skill bundle
29
+ hskill install --skill <s> install specific skill(s)
30
+ hskill install --tool <t> install shell tool(s)
31
+ hskill install --target <t> set target (claude/cursor/codex/all)
32
+ hskill install --scope <s> set scope: user (default) or project
33
+ hskill install --force overwrite existing installs
34
+ hskill list list available skills and bundles
35
+ hskill update update hskill to the latest version
36
+ hskill --version show version
37
+ hskill --help show this help
38
+
39
+ ${chalk.cyan('Examples:')}
40
+ hskill install --bundle dev --target claude
41
+ hskill install --skill git-workflow-init --target claude --scope project
42
+ hskill install --tool p-launch
43
+ hskill update
44
+ `)
45
+ }
46
+
47
+ if (args[0] === '--help' || args[0] === '-h') {
48
+ printHelp()
49
+ process.exit(0)
50
+ }
51
+
52
+ if (args[0] === '--version' || args[0] === '-v') {
53
+ console.log(version)
54
+ process.exit(0)
55
+ }
56
+
57
+ // ── Update ───────────────────────────────────────────────────────────────────
58
+ if (subcommand === 'update') {
59
+ console.log(chalk.dim(' · Updating hskill…'))
60
+ try {
61
+ execSync('npm update -g harveyz-skill', { stdio: 'inherit' })
62
+ console.log(chalk.green(' ✔ hskill updated'))
63
+ } catch {
64
+ console.error(chalk.red(' ✗ Update failed. Try: npm update -g harveyz-skill'))
65
+ process.exit(1)
66
+ }
67
+ process.exit(0)
68
+ }
69
+
70
+ // ── List ─────────────────────────────────────────────────────────────────────
71
+ if (subcommand === 'list') {
72
+ const { bundleMeta, skills, tools = [] } = require('../skills-index.json')
27
73
  const byBundle = {}
28
74
  for (const s of skills) {
29
75
  if (!byBundle[s.bundle]) byBundle[s.bundle] = []
@@ -41,6 +87,22 @@ if (args[0] === 'list') {
41
87
  process.exit(0)
42
88
  }
43
89
 
90
+ // ── Install ───────────────────────────────────────────────────────────────────
91
+ // subcommand is 'install' or omitted (default behavior)
92
+ const installArgs = subcommand === 'install' ? args.slice(1) : args
93
+
94
+ const forceFlag = installArgs.includes('--force')
95
+ const bundleIdx = installArgs.indexOf('--bundle')
96
+ const targetIdx = installArgs.indexOf('--target')
97
+ const toolIdx = installArgs.indexOf('--tool')
98
+ const skillIdx = installArgs.indexOf('--skill')
99
+ const scopeIdx = installArgs.indexOf('--scope')
100
+ const bundleArg = bundleIdx !== -1 ? installArgs[bundleIdx + 1] : undefined
101
+ const targetArg = targetIdx !== -1 ? installArgs[targetIdx + 1] : undefined
102
+ const toolArg = toolIdx !== -1 ? installArgs[toolIdx + 1] : undefined
103
+ const skillArg = skillIdx !== -1 ? installArgs[skillIdx + 1] : undefined
104
+ const scopeArg = scopeIdx !== -1 ? installArgs[scopeIdx + 1] : undefined
105
+
44
106
  const TOOL_BUNDLE_VALUES = new Set(TOOL_BUNDLE_CHOICES.map(c => c.value))
45
107
 
46
108
  function buildInteractiveChoices() {
@@ -103,23 +165,36 @@ try {
103
165
  process.exit(0)
104
166
  }
105
167
 
106
- // ── Install skills ───────────────────────────────────────────────────────────
168
+ // ── Install skills ──────────────────────────────────────────────────────────
107
169
  if (skillItems.length > 0) {
170
+ // Resolve scope
171
+ let scope = scopeArg ?? 'user'
172
+ if (!scopeArg && !targetArg) {
173
+ scope = await select({
174
+ message: 'Scope:',
175
+ choices: [
176
+ { name: `user — ~/.claude/skills/ (shared across all projects)`, value: 'user' },
177
+ { name: `project — .claude/skills/ (current project only)`, value: 'project' },
178
+ ],
179
+ })
180
+ }
181
+
182
+ // Resolve target
108
183
  let selectedTargets
109
184
  if (targetArg) {
110
- selectedTargets = targetArg === 'all' ? Object.keys(TARGETS) : [targetArg]
185
+ selectedTargets = targetArg === 'all' ? ['claude', 'cursor', 'codex'] : [targetArg]
111
186
  } else {
112
187
  selectedTargets = await checkbox({
113
188
  message: 'Install to which tools (space to select, enter to confirm):',
114
189
  choices: [
115
- ...TARGET_CHOICES,
190
+ ...buildTargetChoices(scope),
116
191
  { name: 'all — all tools', value: 'all' },
117
192
  ],
118
193
  })
119
194
  }
120
195
 
121
196
  if (selectedTargets.length > 0) {
122
- const targets = resolveTargets(selectedTargets)
197
+ const targets = resolveTargets(selectedTargets, scope)
123
198
  console.log('')
124
199
  const summary = await installSkills(skillItems, targets, forceFlag)
125
200
  console.log('')
@@ -134,7 +209,7 @@ try {
134
209
  }
135
210
  }
136
211
 
137
- // ── Install shell tools ──────────────────────────────────────────────────────
212
+ // ── Install shell tools ─────────────────────────────────────────────────────
138
213
  if (toolItems.length > 0) {
139
214
  console.log('')
140
215
  const installed = await installTools(
package/lib/targets.js CHANGED
@@ -1,6 +1,13 @@
1
1
  import os from 'os'
2
2
  import path from 'path'
3
3
 
4
+ const SKILL_TARGETS = ['claude', 'cursor', 'codex']
5
+
6
+ function skillDir(name, scope) {
7
+ if (scope === 'project') return path.join(process.cwd(), `.${name}`, 'skills')
8
+ return path.join(os.homedir(), `.${name}`, 'skills')
9
+ }
10
+
4
11
  export const TARGETS = {
5
12
  claude: path.join(os.homedir(), '.claude', 'skills'),
6
13
  cursor: path.join(os.homedir(), '.cursor', 'skills'),
@@ -8,17 +15,21 @@ export const TARGETS = {
8
15
  shell: path.join(os.homedir(), '.local', 'bin'),
9
16
  }
10
17
 
11
- export const TARGET_CHOICES = Object.entries(TARGETS).map(([name, dir]) => ({
12
- name: `${name.padEnd(8)} (${dir})`,
13
- value: name,
14
- }))
18
+ export function buildTargetChoices(scope = 'user') {
19
+ return SKILL_TARGETS.map(name => {
20
+ const dir = skillDir(name, scope)
21
+ return { name: `${name.padEnd(8)} (${dir})`, value: name }
22
+ })
23
+ }
24
+
25
+ export const TARGET_CHOICES = buildTargetChoices('user')
15
26
 
16
27
  // 返回选中 targets 的 { name, dir } 列表,all 展开为全部
17
- export function resolveTargets(selected) {
18
- if (selected.includes('all')) return Object.entries(TARGETS).map(([name, dir]) => ({ name, dir }))
28
+ export function resolveTargets(selected, scope = 'user') {
29
+ const allTargets = SKILL_TARGETS.map(name => ({ name, dir: skillDir(name, scope) }))
30
+ if (selected.includes('all')) return allTargets
19
31
  return selected.map(name => {
20
- const dir = TARGETS[name]
21
- if (!dir) throw new Error(`Unknown target: "${name}"`)
22
- return { name, dir }
32
+ if (!SKILL_TARGETS.includes(name)) throw new Error(`Unknown target: "${name}"`)
33
+ return { name, dir: skillDir(name, scope) }
23
34
  })
24
35
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "harveyz-skill",
3
- "version": "0.3.0",
4
- "description": "One-click skill installer for Claude Code, Cursor, and Codex",
3
+ "version": "0.5.0",
4
+ "description": "Skill manager for Claude Code, Cursor, and Codex",
5
5
  "type": "module",
6
6
  "bin": {
7
- "harveyz-skill": "bin/cli.js"
7
+ "hskill": "bin/cli.js"
8
8
  },
9
9
  "scripts": {
10
10
  "prepack": "node scripts/generate-npmignore.js"