harveyz-skill 0.10.0 → 0.11.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/CHANGELOG.md +13 -0
- package/bin/cli.js +23 -20
- package/bin/preview.mjs +7 -5
- package/lib/bundles.js +4 -3
- package/lib/targets.js +1 -1
- package/package.json +1 -1
- package/skills/meta/npm-release/SKILL.md +37 -13
- package/skills/web-fetch/article-fetcher/references/__pycache__/article_utils.cpython-314.pyc +0 -0
- package/skills/web-fetch/url-extract/platforms/SKILL.claude.md +1 -2
- package/skills/web-fetch/url-extract/platforms/SKILL.codex.md +3 -1
- package/skills/web-fetch/url-extract/platforms/SKILL.hermes.md +3 -1
- package/skills/web-fetch/url-extract/vars.json +0 -5
- package/tools/p-launch/__pycache__/p_launch.cpython-314.pyc +0 -0
- package/tools/p-launch/tests/__pycache__/test_p_launch.cpython-314-pytest-9.0.2.pyc +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.11.0] - 2026-06-09
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `hskill list`:新增 Bundle 列并按 bundle 名称排序(替换旧的分组标题格式);`hskill status` skills 列表同步增加 Bundle 列
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- `hskill` 内部重构:平台列表从 `SKILL_TARGETS` 单一来源派生,消除 `bundles.js` / `preview.mjs` 中的硬编码数组
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- `url-extract`:移除 `SKILL_DIR` 用户配置变量
|
|
20
|
+
- `preview`:安装状态侧边栏补全 openclaw、hermes 平台(USER LEVEL)
|
|
21
|
+
- `npm-release`:中间步骤跳过 push 操作,最终统一给出推送 + 发布指令清单
|
|
22
|
+
|
|
10
23
|
## [0.10.0] - 2026-06-08
|
|
11
24
|
|
|
12
25
|
### Added
|
package/bin/cli.js
CHANGED
|
@@ -158,29 +158,31 @@ if (subcommand === 'update') {
|
|
|
158
158
|
|
|
159
159
|
// ── List ─────────────────────────────────────────────────────────────────────
|
|
160
160
|
if (subcommand === 'list') {
|
|
161
|
-
const {
|
|
162
|
-
const
|
|
163
|
-
for (const s of skills) {
|
|
164
|
-
if (!byBundle[s.bundle]) byBundle[s.bundle] = []
|
|
165
|
-
byBundle[s.bundle].push(s.path)
|
|
166
|
-
}
|
|
161
|
+
const { skills, tools = [] } = require('../skills-index.json')
|
|
162
|
+
const sorted = [...skills].sort((a, b) => a.bundle.localeCompare(b.bundle) || a.path.split('/').pop().localeCompare(b.path.split('/').pop()))
|
|
167
163
|
if (jsonFlag) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
console.log(JSON.stringify({ bundles, tools: tools.map(t => t.name) }, null, 2))
|
|
164
|
+
console.log(JSON.stringify({
|
|
165
|
+
skills: sorted.map(s => ({ name: s.path.split('/').pop(), path: s.path, bundle: s.bundle })),
|
|
166
|
+
tools: tools.map(t => t.name),
|
|
167
|
+
}, null, 2))
|
|
173
168
|
process.exit(0)
|
|
174
169
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
170
|
+
const nw = Math.max(...sorted.map(s => s.path.split('/').pop().length), 4)
|
|
171
|
+
const bw = Math.max(...sorted.map(s => s.bundle.length), 6)
|
|
172
|
+
const sep = chalk.dim(' ' + '─'.repeat(nw + bw + 4))
|
|
173
|
+
console.log('')
|
|
174
|
+
console.log(' ' + chalk.bold('NAME'.padEnd(nw)) + ' ' + chalk.bold('BUNDLE'))
|
|
175
|
+
console.log(sep)
|
|
176
|
+
for (const s of sorted) {
|
|
177
|
+
console.log(' ' + s.path.split('/').pop().padEnd(nw) + ' ' + chalk.dim(s.bundle))
|
|
178
178
|
}
|
|
179
179
|
if (tools.length > 0) {
|
|
180
180
|
console.log('')
|
|
181
|
-
console.log(chalk.bold('
|
|
181
|
+
console.log(' ' + chalk.bold('SHELL TOOLS'))
|
|
182
|
+
console.log(sep)
|
|
182
183
|
for (const t of tools) console.log(' ' + t.name)
|
|
183
184
|
}
|
|
185
|
+
console.log('')
|
|
184
186
|
process.exit(0)
|
|
185
187
|
}
|
|
186
188
|
|
|
@@ -208,11 +210,11 @@ if (subcommand === 'status' || subcommand === 'outdated') {
|
|
|
208
210
|
const skillRows = skillItems.map(s => {
|
|
209
211
|
const inst = checkInstalled(s.skillName, s.version ?? '—')
|
|
210
212
|
return {
|
|
211
|
-
name: s.skillName, version: s.version ?? '—',
|
|
213
|
+
name: s.skillName, bundle: s.bundle ?? '—', version: s.version ?? '—',
|
|
212
214
|
userStatus: scopeSummary(inst.user), projectStatus: scopeSummary(inst.project),
|
|
213
215
|
userDetail: inst.user, projectDetail: inst.project,
|
|
214
216
|
}
|
|
215
|
-
})
|
|
217
|
+
}).sort((a, b) => a.bundle.localeCompare(b.bundle) || a.name.localeCompare(b.name))
|
|
216
218
|
const toolRows = toolItems.map(t => {
|
|
217
219
|
const inst = checkToolInstalled(t.toolName, t.srcPath)
|
|
218
220
|
return { name: t.toolName, version: t.version ?? '—', ...inst }
|
|
@@ -285,15 +287,16 @@ if (subcommand === 'status' || subcommand === 'outdated') {
|
|
|
285
287
|
const allVers = [...skillRows, ...toolRows].map(r => r.version)
|
|
286
288
|
const nw = Math.max(...allNames.map(n => n.length), 4)
|
|
287
289
|
const vw = Math.max(...allVers.map(v => v.length), 7)
|
|
288
|
-
const
|
|
290
|
+
const bw = Math.max(...skillRows.map(r => r.bundle.length), 6)
|
|
291
|
+
const sep = chalk.dim(' ' + '─'.repeat(nw + vw + bw + 22))
|
|
289
292
|
|
|
290
293
|
console.log('')
|
|
291
294
|
console.log(' ' + chalk.bold('SKILLS') + chalk.dim(` — ${skillRows.length} available`))
|
|
292
295
|
console.log(sep)
|
|
293
|
-
console.log(' ' + ''.padEnd(nw + vw +
|
|
296
|
+
console.log(' ' + ''.padEnd(nw + vw + bw + 5) + chalk.dim('user project'))
|
|
294
297
|
for (const r of skillRows) {
|
|
295
298
|
const u = icon(r.userStatus), p = icon(r.projectStatus)
|
|
296
|
-
console.log(' ' + r.name.padEnd(nw) + ' ' + chalk.dim(r.version.padEnd(vw)) + ' ' + u + ' ' + p)
|
|
299
|
+
console.log(' ' + r.name.padEnd(nw) + ' ' + chalk.dim(r.version.padEnd(vw)) + ' ' + chalk.dim(r.bundle.padEnd(bw)) + ' ' + u + ' ' + p)
|
|
297
300
|
}
|
|
298
301
|
|
|
299
302
|
console.log('')
|
package/bin/preview.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import fs from 'fs'
|
|
|
3
3
|
import os from 'os'
|
|
4
4
|
import path from 'path'
|
|
5
5
|
import { fileURLToPath } from 'url'
|
|
6
|
+
import { SKILL_TARGETS, USER_ONLY_TARGETS } from '../lib/targets.js'
|
|
6
7
|
|
|
7
8
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
8
9
|
|
|
@@ -72,10 +73,11 @@ function statusLine(version, status) {
|
|
|
72
73
|
return ver + ' ' + D + '— not installed' + R
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
const
|
|
76
|
+
const userTargets = SKILL_TARGETS
|
|
77
|
+
const projectTargets = SKILL_TARGETS.filter(t => !USER_ONLY_TARGETS.has(t))
|
|
76
78
|
const home = os.homedir()
|
|
77
79
|
|
|
78
|
-
function checkScope(dirFn) {
|
|
80
|
+
function checkScope(targets, dirFn) {
|
|
79
81
|
return targets.map(t => {
|
|
80
82
|
const ver = readVersion(path.join(dirFn(t), skillName))
|
|
81
83
|
const status = ver === '—' ? 'none'
|
|
@@ -86,10 +88,10 @@ function checkScope(dirFn) {
|
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
const cwd = process.cwd()
|
|
89
|
-
const userDetails = checkScope(t => path.join(home, `.${t}`, 'skills'))
|
|
91
|
+
const userDetails = checkScope(userTargets, t => path.join(home, `.${t}`, 'skills'))
|
|
90
92
|
const projectDetails = cwd === home
|
|
91
|
-
?
|
|
92
|
-
: checkScope(t => path.join(cwd, `.${t}`, 'skills'))
|
|
93
|
+
? projectTargets.map(t => ({ tool: t, version: '—', status: 'none' }))
|
|
94
|
+
: checkScope(projectTargets, t => path.join(cwd, `.${t}`, 'skills'))
|
|
93
95
|
|
|
94
96
|
console.log(B + skillName + R)
|
|
95
97
|
console.log(D + 'available: ' + R + availableVersion)
|
package/lib/bundles.js
CHANGED
|
@@ -3,7 +3,7 @@ import os from 'os'
|
|
|
3
3
|
import { createRequire } from 'module'
|
|
4
4
|
import path from 'path'
|
|
5
5
|
import { fileURLToPath } from 'url'
|
|
6
|
-
import { USER_ONLY_TARGETS } from './targets.js'
|
|
6
|
+
import { SKILL_TARGETS, USER_ONLY_TARGETS } from './targets.js'
|
|
7
7
|
|
|
8
8
|
const require = createRequire(import.meta.url)
|
|
9
9
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
@@ -101,13 +101,14 @@ export function buildAllChoices() {
|
|
|
101
101
|
return choices
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
// 所有 skill 展开为 { kind:'skill', skillName, srcPath } 列表(对应 all 选项)
|
|
104
|
+
// 所有 skill 展开为 { kind:'skill', skillName, bundle, srcPath } 列表(对应 all 选项)
|
|
105
105
|
export function getAllSkillItems() {
|
|
106
106
|
return skillDefs.map(skill => {
|
|
107
107
|
const srcPath = path.join(skillsRoot, skill.path)
|
|
108
108
|
return {
|
|
109
109
|
kind: 'skill',
|
|
110
110
|
skillName: skill.path.split('/').pop(),
|
|
111
|
+
bundle: skill.bundle,
|
|
111
112
|
srcPath,
|
|
112
113
|
version: readVersion(srcPath),
|
|
113
114
|
}
|
|
@@ -132,7 +133,7 @@ export function getAllToolItems() {
|
|
|
132
133
|
// 每个工具: { version: string, status: 'up-to-date'|'update'|'none' }
|
|
133
134
|
export function checkInstalled(skillName, availableVersion) {
|
|
134
135
|
const home = os.homedir()
|
|
135
|
-
const targets =
|
|
136
|
+
const targets = SKILL_TARGETS
|
|
136
137
|
|
|
137
138
|
function checkScope(dirFn) {
|
|
138
139
|
const result = {}
|
package/lib/targets.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import os from 'os'
|
|
2
2
|
import path from 'path'
|
|
3
3
|
|
|
4
|
-
const SKILL_TARGETS = ['claude', 'cursor', 'codex', 'openclaw', 'hermes']
|
|
4
|
+
export const SKILL_TARGETS = ['claude', 'cursor', 'codex', 'openclaw', 'hermes']
|
|
5
5
|
|
|
6
6
|
export const USER_ONLY_TARGETS = new Set(['openclaw', 'hermes'])
|
|
7
7
|
|
package/package.json
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
name: npm-release
|
|
3
3
|
description: "Complete npm publish workflow for harveyz-skill: bump version, update CHANGELOG, create release branch, merge to staging then main, tag, and publish to npm. Use this skill whenever the user wants to release, publish, cut a version, bump version, ship to npm, or deploy a new package version."
|
|
4
4
|
user_invocable: true
|
|
5
|
-
version: "1.
|
|
5
|
+
version: "1.1.0"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# npm-release
|
|
9
9
|
|
|
10
|
-
harveyz-skill 发布到 npm 的完整流程:版本号升级 → CHANGELOG 更新 → 分支提交 →
|
|
10
|
+
harveyz-skill 发布到 npm 的完整流程:版本号升级 → CHANGELOG 更新 → 分支提交 → 本地合并到 staging 和 main → 打 tag → 给出推送 + 发布指令供用户执行。
|
|
11
|
+
|
|
12
|
+
**说明**:所有 `git push` 和 `npm publish` 操作由用户自行执行,Claude 只做本地操作,最后统一给出指令清单。
|
|
11
13
|
|
|
12
14
|
---
|
|
13
15
|
|
|
@@ -111,44 +113,66 @@ git commit -m "chore(release): bump version to <new-version>"
|
|
|
111
113
|
|
|
112
114
|
---
|
|
113
115
|
|
|
114
|
-
## Step 5 —
|
|
116
|
+
## Step 5 — 本地合并到 staging(不推送)
|
|
115
117
|
|
|
116
118
|
```bash
|
|
117
119
|
git checkout staging
|
|
118
120
|
git merge release/<new-version>
|
|
119
|
-
git push origin staging
|
|
120
121
|
```
|
|
121
122
|
|
|
123
|
+
只做本地合并,不执行 push。
|
|
124
|
+
|
|
122
125
|
---
|
|
123
126
|
|
|
124
|
-
## Step 6 —
|
|
127
|
+
## Step 6 — 本地合并到 main 并打 tag(不推送)
|
|
125
128
|
|
|
126
129
|
```bash
|
|
127
130
|
git checkout main
|
|
128
|
-
git pull origin main
|
|
131
|
+
git pull origin main # 只拉取,确保本地 main 是最新的
|
|
129
132
|
git merge staging
|
|
130
133
|
git tag -a v<new-version> -m "v<new-version>"
|
|
131
|
-
git push origin main
|
|
132
|
-
git push origin v<new-version>
|
|
133
134
|
```
|
|
134
135
|
|
|
135
136
|
tag 使用 annotated tag(`-a`),符合项目 workflow-config.yml 的 `require_annotated: true` 规则。
|
|
136
137
|
|
|
138
|
+
**不执行任何 push**,推送操作统一在最后由用户执行。
|
|
139
|
+
|
|
137
140
|
---
|
|
138
141
|
|
|
139
|
-
## Step 7 —
|
|
142
|
+
## Step 7 — 给出最终执行清单
|
|
140
143
|
|
|
141
|
-
|
|
144
|
+
本地准备工作已完成。向用户展示以下指令,请用户依次确认并手动执行:
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
== 待执行:推送 + 发布 ==
|
|
150
|
+
|
|
151
|
+
# 1. 推送 staging
|
|
152
|
+
git push origin staging
|
|
153
|
+
|
|
154
|
+
# 2. 推送 main 和 tag
|
|
155
|
+
git push origin main
|
|
156
|
+
git push origin v<new-version>
|
|
157
|
+
|
|
158
|
+
# 3. 发布到 npm(需要已登录:npm login)
|
|
142
159
|
npm publish
|
|
143
160
|
```
|
|
144
161
|
|
|
145
|
-
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
说明:
|
|
165
|
+
- 如果 `npm login` 状态已过期,先运行 `npm login` 再执行 `npm publish`
|
|
166
|
+
- 如果 `git push origin main` 被 pre-push hook 拒绝,检查 tag 格式是否为 `v<X>.<Y>.<Z>`,以及是否是 annotated tag
|
|
167
|
+
- 以上命令需在项目根目录执行
|
|
168
|
+
|
|
169
|
+
用户执行完毕后,告知 Claude,Claude 会输出最终摘要。
|
|
146
170
|
|
|
147
171
|
---
|
|
148
172
|
|
|
149
|
-
## Step 8 —
|
|
173
|
+
## Step 8 — 收尾(用户执行完毕后)
|
|
150
174
|
|
|
151
|
-
|
|
175
|
+
用户确认推送和发布成功后输出摘要:
|
|
152
176
|
|
|
153
177
|
```
|
|
154
178
|
✓ 版本 v<new-version> 已发布
|
package/skills/web-fetch/article-fetcher/references/__pycache__/article_utils.cpython-314.pyc
CHANGED
|
Binary file
|
|
@@ -25,7 +25,6 @@ sessions_spawn \
|
|
|
25
25
|
| 抽象变量 | 实际语法 |
|
|
26
26
|
|----------|----------|
|
|
27
27
|
| `VAULT_PATH` | `{{VAULT_PATH}}` |
|
|
28
|
-
| `SKILL_DIR` | `{{SKILL_DIR}}` |
|
|
29
28
|
| `CHROME_PROFILE` | `{{CHROME_PROFILE}}` |
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
`SKILL_DIR` 由平台自动提供,无需用户配置:值固定为 `$HOME/.claude/skills/url-extract`(Claude Code skill 的标准安装路径)。执行代码时直接将 `SKILL_DIR` 替换为该路径。
|
|
@@ -14,4 +14,6 @@
|
|
|
14
14
|
|
|
15
15
|
## ③ 变量注入
|
|
16
16
|
|
|
17
|
-
通过环境变量或 Codex 平台的变量系统注入,执行 SKILL.md 中的代码时直接使用实际值替换 `VAULT_PATH`、`
|
|
17
|
+
通过环境变量或 Codex 平台的变量系统注入,执行 SKILL.md 中的代码时直接使用实际值替换 `VAULT_PATH`、`CHROME_PROFILE` 占位。
|
|
18
|
+
|
|
19
|
+
`SKILL_DIR` 由平台自动提供,无需用户配置:值为 Codex 安装本 skill 的目录(即包含 `scripts/` 的那一级目录)。
|
|
@@ -14,4 +14,6 @@
|
|
|
14
14
|
|
|
15
15
|
## ③ 变量注入
|
|
16
16
|
|
|
17
|
-
通过 Hermes vars.json 或环境变量注入,执行 SKILL.md 中的代码时直接使用实际值替换 `VAULT_PATH`、`
|
|
17
|
+
通过 Hermes vars.json 或环境变量注入,执行 SKILL.md 中的代码时直接使用实际值替换 `VAULT_PATH`、`CHROME_PROFILE` 占位。
|
|
18
|
+
|
|
19
|
+
`SKILL_DIR` 由平台自动提供,无需用户配置:值为 Hermes 安装本 skill 的目录(即包含 `scripts/` 的那一级目录)。
|
|
@@ -4,11 +4,6 @@
|
|
|
4
4
|
"description": "Obsidian Reading 目录完整路径(例如 /Users/you/Vault/Product/Reading)",
|
|
5
5
|
"default": "{{HOME}}/Vault/Product/Reading"
|
|
6
6
|
},
|
|
7
|
-
{
|
|
8
|
-
"name": "SKILL_DIR",
|
|
9
|
-
"description": "url-extract skill 安装后的完整路径(用于定位辅助脚本和数据库文件)",
|
|
10
|
-
"default": "{{HOME}}/.claude/skills/url-extract"
|
|
11
|
-
},
|
|
12
7
|
{
|
|
13
8
|
"name": "CHROME_PROFILE",
|
|
14
9
|
"description": "Chrome 用户配置目录(X.com 登录态所需)。macOS 默认:~/Library/Application Support/Google/Chrome/Profile 2",
|
|
Binary file
|