@wangkh/skill-sync-cli 0.2.1 → 0.2.3
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 +26 -3
- package/README.zh-CN.md +17 -1
- package/bin/skill-sync.mjs +1 -1
- package/package.json +2 -1
- package/skills/skill-sync/SKILL.md +92 -0
- package/src/commands.mjs +70 -13
- package/src/mirror.mjs +1 -0
- package/src/revision.mjs +18 -0
- package/src/runner.mjs +8 -1
package/README.md
CHANGED
|
@@ -248,9 +248,12 @@ downloaded again. If the update check cannot reach the remote but the previous
|
|
|
248
248
|
install is still present, the current copy is kept. Use `--force` to reinstall
|
|
249
249
|
regardless.
|
|
250
250
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
through
|
|
251
|
+
`sync` prefers direct connections and falls back automatically: it probes
|
|
252
|
+
github.com with a 5-second timeout first, and when the probe fails, clones and
|
|
253
|
+
update checks run through `https://gh-proxy.com/` for that run. A direct
|
|
254
|
+
install that fails anyway gets one retry through the fallback mirror. Pass
|
|
255
|
+
`--github-mirror <url>` (or set `SKILL_SYNC_GITHUB_MIRROR`) to force a specific
|
|
256
|
+
mirror instead of the automatic fallback:
|
|
254
257
|
|
|
255
258
|
```bash
|
|
256
259
|
npx @wangkh/skill-sync-cli sync --github-mirror https://gh-proxy.com/
|
|
@@ -262,6 +265,11 @@ working include `https://gh-proxy.com/`, `https://ghproxy.net/`, and
|
|
|
262
265
|
`https://ghfast.top/`. To speed up the `skills` CLI download itself on a fresh
|
|
263
266
|
machine, configure an npm registry mirror such as `https://registry.npmmirror.com`.
|
|
264
267
|
|
|
268
|
+
Child `skills` commands run with `DO_NOT_TRACK=1`, so telemetry stays off. A
|
|
269
|
+
known Windows exit crash in the `skills` CLI after a completed install is
|
|
270
|
+
tolerated: when the files are in place, `sync` treats the group as installed
|
|
271
|
+
instead of failing.
|
|
272
|
+
|
|
265
273
|
For project-scoped skills, set:
|
|
266
274
|
|
|
267
275
|
```json
|
|
@@ -279,6 +287,21 @@ location. From the target application project, run:
|
|
|
279
287
|
npx @wangkh/skill-sync-cli sync --config /path/to/my-skills/skills.json
|
|
280
288
|
```
|
|
281
289
|
|
|
290
|
+
## Agent Skill
|
|
291
|
+
|
|
292
|
+
The repository ships an agent skill (`skills/skill-sync/SKILL.md`) that teaches
|
|
293
|
+
an agent how to use this CLI. Install it from the repository:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
npx skills add yudaiyan/skill-sync-cli
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Or declare it in the manifest and let `sync` install it:
|
|
300
|
+
|
|
301
|
+
```json
|
|
302
|
+
{ "name": "skill-sync", "source": "yudaiyan/skill-sync-cli" }
|
|
303
|
+
```
|
|
304
|
+
|
|
282
305
|
## Run Before the First Release
|
|
283
306
|
|
|
284
307
|
From a source checkout, the following commands work without installing project
|
package/README.zh-CN.md
CHANGED
|
@@ -124,7 +124,7 @@ npx --yes skills@1.5.22 add vercel-labs/skills --agent opencode --skill find-ski
|
|
|
124
124
|
|
|
125
125
|
`sync` 会把每个分组的已安装版本记录在 `~/.config/skill-sync/state.json`。之后的运行会用 `git ls-remote` 检查远端版本(固定为 commit SHA 的 `ref` 无需检查),版本和已安装文件都没有变化时跳过该组,不再重复下载。检查更新失败但本地文件仍然存在时,会保留现有安装。需要强制重装时使用 `--force`。
|
|
126
126
|
|
|
127
|
-
|
|
127
|
+
`sync` 默认优先直连 GitHub、失败自动回退:先用 5 秒超时探测 github.com,探测失败时本次运行的 clone 和版本检查自动走 `https://gh-proxy.com/`;直连安装失败也会再用镜像重试一次。需要强制指定镜像时用 `--github-mirror <url>`(或环境变量 `SKILL_SYNC_GITHUB_MIRROR`):
|
|
128
128
|
|
|
129
129
|
```bash
|
|
130
130
|
npx @wangkh/skill-sync-cli sync --github-mirror https://gh-proxy.com/
|
|
@@ -132,6 +132,8 @@ npx @wangkh/skill-sync-cli sync --github-mirror https://gh-proxy.com/
|
|
|
132
132
|
|
|
133
133
|
镜像通过 git 的 `insteadOf` 重写注入,只对本次命令生效,不会修改全局 git 配置。实测可用的免费公益镜像包括 `https://gh-proxy.com/`、`https://ghproxy.net/`、`https://ghfast.top/`。新机器上如果 `skills` CLI 下载慢,可以配置 npm 镜像源 `https://registry.npmmirror.com`。
|
|
134
134
|
|
|
135
|
+
wrapper 会以 `DO_NOT_TRACK=1` 运行 `skills` CLI(关闭遥测)。它在 Windows 上安装完成后偶发退出崩溃(0xC0000409);只要文件已就位,`sync` 会把该组视为安装成功,不再误判失败。
|
|
136
|
+
|
|
135
137
|
只预览,或者在单个 Skill 失败后继续处理其他条目:
|
|
136
138
|
|
|
137
139
|
```bash
|
|
@@ -186,6 +188,20 @@ Git 负责在机器之间传递清单,`sync` 根据本地清单安装 Skills
|
|
|
186
188
|
|
|
187
189
|
清单是这个脚本定义的格式;脚本读取后逐项调用官方 `skills` CLI。上游的 `.skill-lock.json` 继续由官方 CLI 维护。
|
|
188
190
|
|
|
191
|
+
## 给 agent 用的 skill
|
|
192
|
+
|
|
193
|
+
仓库自带一个 agent skill(`skills/skill-sync/SKILL.md`),用来告诉 agent 如何使用本 CLI。可以直接安装:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
npx skills add yudaiyan/skill-sync-cli
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
也可以加进清单,交给 `sync` 安装:
|
|
200
|
+
|
|
201
|
+
```json
|
|
202
|
+
{ "name": "skill-sync", "source": "yudaiyan/skill-sync-cli" }
|
|
203
|
+
```
|
|
204
|
+
|
|
189
205
|
## 发布前使用
|
|
190
206
|
|
|
191
207
|
获取项目源码后,可以在源码目录直接运行,无需安装项目依赖:
|
package/bin/skill-sync.mjs
CHANGED
|
@@ -29,7 +29,7 @@ Options:
|
|
|
29
29
|
--dry-run Print commands without downloading anything
|
|
30
30
|
--continue-on-error Continue after a skill installation fails
|
|
31
31
|
--retries <n> Retry each failed install up to n times (default 2)
|
|
32
|
-
--github-mirror <url>
|
|
32
|
+
--github-mirror <url> Force a GitHub mirror instead of the automatic fallback
|
|
33
33
|
--force Replace the selected manifest with init, or reinstall with sync
|
|
34
34
|
-h, --help Show this help
|
|
35
35
|
-v, --version Show the version
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wangkh/skill-sync-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Sync declared agent skills across machines through the skills CLI",
|
|
5
5
|
"author": "wkh",
|
|
6
6
|
"repository": {
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"bin",
|
|
28
28
|
"src",
|
|
29
29
|
"schemas",
|
|
30
|
+
"skills",
|
|
30
31
|
"README.md",
|
|
31
32
|
"README.zh-CN.md",
|
|
32
33
|
"RELEASING.md",
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: skill-sync
|
|
3
|
+
description: >-
|
|
4
|
+
Manage the user's portable agent-skill manifest with the skill-sync CLI
|
|
5
|
+
(@wangkh/skill-sync-cli). Use when the user wants to sync, back up, or
|
|
6
|
+
restore agent skills across machines ("同步我的 skills", "恢复 skill 环境",
|
|
7
|
+
"新电脑安装 skills"), add or remove a skill in their manifest
|
|
8
|
+
("把 skill 加到清单"), preview or validate the manifest, or install skills
|
|
9
|
+
when GitHub is unreachable. Also use when a skill installation fails and the
|
|
10
|
+
user asks why or how to retry.
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# skill-sync
|
|
14
|
+
|
|
15
|
+
`@wangkh/skill-sync-cli` keeps a portable JSON manifest of third-party agent
|
|
16
|
+
skills and installs them through the official `skills` CLI. The manifest is the
|
|
17
|
+
single source of truth; the tool only writes it during `init`.
|
|
18
|
+
|
|
19
|
+
## When to use
|
|
20
|
+
|
|
21
|
+
- Sync or restore skills on a machine: `sync`
|
|
22
|
+
- Add a skill to the manifest after finding it: edit the manifest, then `sync`
|
|
23
|
+
- Preview or validate before installing: `plan`, `validate`
|
|
24
|
+
- GitHub is blocked or slow: run `sync --github-mirror https://gh-proxy.com/`
|
|
25
|
+
- An install failed: check retries, the mirror, or `--continue-on-error`
|
|
26
|
+
|
|
27
|
+
## Commands
|
|
28
|
+
|
|
29
|
+
Run through npx so no global install is needed:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx @wangkh/skill-sync-cli init # create the manifest (keeps an existing one)
|
|
33
|
+
npx @wangkh/skill-sync-cli path # print the manifest path in use
|
|
34
|
+
npx @wangkh/skill-sync-cli validate # parse and validate without network
|
|
35
|
+
npx @wangkh/skill-sync-cli plan # print the exact skills commands
|
|
36
|
+
npx @wangkh/skill-sync-cli sync # install or update declared skills
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Useful options:
|
|
40
|
+
|
|
41
|
+
| Option | Meaning |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| `-c, --config <file>` | Use another manifest file (all commands) |
|
|
44
|
+
| `--from <url>` | `init` from a published manifest URL |
|
|
45
|
+
| `--dry-run` | Preview without installing |
|
|
46
|
+
| `--continue-on-error` | Keep going after a failure; exit code stays non-zero |
|
|
47
|
+
| `--retries <n>` | Retry failed installs (default 2) |
|
|
48
|
+
| `--github-mirror <url>` | Route GitHub clones and update checks through a mirror |
|
|
49
|
+
| `--force` | `init`: replace the file; `sync`: reinstall even if unchanged |
|
|
50
|
+
|
|
51
|
+
## Manifest
|
|
52
|
+
|
|
53
|
+
Default location:
|
|
54
|
+
|
|
55
|
+
- Windows: `%USERPROFILE%\.config\skill-sync\skills.json`
|
|
56
|
+
- macOS/Linux: `~/.config/skill-sync/skills.json`
|
|
57
|
+
|
|
58
|
+
Skill entries require `name` (the upstream skill name) and `source`
|
|
59
|
+
(`owner/repo`, Git URL, skill URL, or archive). Optional fields are
|
|
60
|
+
`description`, `ref`, `enabled`, `agents`, `scope`, and `copy`; shared
|
|
61
|
+
defaults live under `defaults`, and the `skills` CLI version is pinned under
|
|
62
|
+
`cli`.
|
|
63
|
+
|
|
64
|
+
To add a skill, append an entry and run `sync`. Confirm the upstream name
|
|
65
|
+
first:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npx --yes skills@1.5.22 add <owner>/<repo> --list
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Behavior worth knowing
|
|
72
|
+
|
|
73
|
+
- Entries sharing `source`, `ref`, `agents`, `scope`, and `copy` install with
|
|
74
|
+
one `skills add` call; a failed batch falls back to installing each skill.
|
|
75
|
+
- `sync` records revisions in `~/.config/skill-sync/state.json` and skips
|
|
76
|
+
groups whose revision and installed files are unchanged. `--force`
|
|
77
|
+
overrides the skip.
|
|
78
|
+
- When the update check cannot reach the remote but the installed copy still
|
|
79
|
+
exists, `sync` keeps it instead of failing.
|
|
80
|
+
- GitHub access: `sync` probes github.com with a 5-second timeout and falls
|
|
81
|
+
back to `https://gh-proxy.com/` automatically; `--github-mirror <url>` forces
|
|
82
|
+
a specific mirror (alternatives: `https://ghproxy.net/`,
|
|
83
|
+
`https://ghfast.top/`). For the `skills` CLI download itself, configure an
|
|
84
|
+
npm registry mirror such as `https://registry.npmmirror.com`.
|
|
85
|
+
|
|
86
|
+
## Security
|
|
87
|
+
|
|
88
|
+
A skill is agent instructions and may run scripts; review third-party skills
|
|
89
|
+
before adding them. Prefer trusted repositories and immutable commit refs.
|
|
90
|
+
|
|
91
|
+
Full documentation lives in the project README and
|
|
92
|
+
`schemas/skills-sync.schema.json`.
|
package/src/commands.mjs
CHANGED
|
@@ -15,8 +15,14 @@ import {
|
|
|
15
15
|
runCommand,
|
|
16
16
|
statusCode,
|
|
17
17
|
} from "./runner.mjs"
|
|
18
|
-
import { mirrorGitEnv } from "./mirror.mjs"
|
|
19
|
-
import {
|
|
18
|
+
import { DEFAULT_FALLBACK_MIRROR, mirrorGitEnv } from "./mirror.mjs"
|
|
19
|
+
import {
|
|
20
|
+
checkGithubReachable,
|
|
21
|
+
gitUrlForSource,
|
|
22
|
+
isCommitSha,
|
|
23
|
+
isGithubSource,
|
|
24
|
+
probeRevision,
|
|
25
|
+
} from "./revision.mjs"
|
|
20
26
|
import {
|
|
21
27
|
allInstalled,
|
|
22
28
|
readState,
|
|
@@ -30,6 +36,9 @@ import { downloadManifest } from "./remote.mjs"
|
|
|
30
36
|
|
|
31
37
|
export const DEFAULT_RETRIES = 2
|
|
32
38
|
const DEFAULT_RETRY_DELAY_MS = 2000
|
|
39
|
+
// The skills CLI occasionally aborts on exit after a completed install on
|
|
40
|
+
// Windows (libuv teardown assertion). These are the observed exit codes.
|
|
41
|
+
const POST_INSTALL_CRASH_CODES = new Set([3221226505, -1073740791])
|
|
33
42
|
|
|
34
43
|
const SAMPLE_MANIFEST = {
|
|
35
44
|
version: 1,
|
|
@@ -96,17 +105,26 @@ function resolveGroupRevision(skills, probe, probeOptions) {
|
|
|
96
105
|
return probe(url, ref, probeOptions)
|
|
97
106
|
}
|
|
98
107
|
|
|
99
|
-
async function runWithRetries(execute, command, { cwd, env, retries, retryDelayMs, label }) {
|
|
108
|
+
async function runWithRetries(execute, command, { cwd, env, fallbackEnv, retries, retryDelayMs, label }) {
|
|
109
|
+
let result
|
|
100
110
|
for (let attempt = 0; ; attempt += 1) {
|
|
101
|
-
|
|
111
|
+
result = execute(command, { cwd, env })
|
|
102
112
|
const code = statusCode(result)
|
|
103
|
-
if (code === 0 || result.signal || attempt >= retries)
|
|
113
|
+
if (code === 0 || result.signal || attempt >= retries) break
|
|
104
114
|
|
|
105
115
|
const delayMs = retryDelayMs * (attempt + 1)
|
|
106
116
|
const delayText = delayMs > 0 ? ` in ${Math.round(delayMs / 1000)}s` : ""
|
|
107
117
|
console.error(`[${label}] attempt ${attempt + 1}/${retries + 1} failed with exit code ${code}; retrying${delayText}`)
|
|
108
118
|
if (delayMs > 0) await sleep(delayMs)
|
|
109
119
|
}
|
|
120
|
+
|
|
121
|
+
// Direct connections can work for the reachability probe and still time out
|
|
122
|
+
// later; one mirror attempt covers that case.
|
|
123
|
+
if (statusCode(result) !== 0 && !result.signal && fallbackEnv) {
|
|
124
|
+
console.error(`[${label}] retrying via fallback mirror`)
|
|
125
|
+
result = execute(command, { cwd, env: fallbackEnv })
|
|
126
|
+
}
|
|
127
|
+
return result
|
|
110
128
|
}
|
|
111
129
|
|
|
112
130
|
export async function planCommand(manifestFilename) {
|
|
@@ -137,7 +155,6 @@ export async function syncCommand(manifestFilename, options = {}) {
|
|
|
137
155
|
const home = options.home ?? os.homedir()
|
|
138
156
|
const cwd = options.cwd ?? process.cwd()
|
|
139
157
|
const probe = options.probe ?? probeRevision
|
|
140
|
-
const gitEnv = mirrorGitEnv(options.githubMirror)
|
|
141
158
|
|
|
142
159
|
if (skills.length === 0) {
|
|
143
160
|
console.log("No enabled skills.")
|
|
@@ -151,6 +168,30 @@ export async function syncCommand(manifestFilename, options = {}) {
|
|
|
151
168
|
command: commandForGroup(group, manifest.cli),
|
|
152
169
|
}))
|
|
153
170
|
|
|
171
|
+
// Route GitHub traffic for this run: an explicit mirror always wins;
|
|
172
|
+
// otherwise probe github.com briefly and fall back to the default mirror
|
|
173
|
+
// when it does not answer.
|
|
174
|
+
let gitEnv = mirrorGitEnv(options.githubMirror)
|
|
175
|
+
let fallbackEnv = null
|
|
176
|
+
if (!options.githubMirror && !options.dryRun && groups.some((group) => isGithubSource(group.skills[0].source))) {
|
|
177
|
+
const check = options.checkGithub ?? checkGithubReachable
|
|
178
|
+
if (check()) {
|
|
179
|
+
console.log("# GitHub reachable; using direct connections")
|
|
180
|
+
fallbackEnv = mirrorGitEnv(DEFAULT_FALLBACK_MIRROR)
|
|
181
|
+
} else {
|
|
182
|
+
gitEnv = mirrorGitEnv(DEFAULT_FALLBACK_MIRROR)
|
|
183
|
+
console.log(`# GitHub unreachable; using fallback mirror ${DEFAULT_FALLBACK_MIRROR}`)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const resolveRevision = (group) => {
|
|
188
|
+
let revision = resolveGroupRevision(group.skills, probe, { env: gitEnv })
|
|
189
|
+
if (revision.status === "error" && fallbackEnv) {
|
|
190
|
+
revision = resolveGroupRevision(group.skills, probe, { env: fallbackEnv })
|
|
191
|
+
}
|
|
192
|
+
return revision
|
|
193
|
+
}
|
|
194
|
+
|
|
154
195
|
const stateFilename = options.stateFile ?? statePath(home)
|
|
155
196
|
const state = await readState(stateFilename)
|
|
156
197
|
|
|
@@ -171,18 +212,28 @@ export async function syncCommand(manifestFilename, options = {}) {
|
|
|
171
212
|
|
|
172
213
|
const key = stateKey(group.skills)
|
|
173
214
|
const entry = stateEntry(state, filename, key)
|
|
174
|
-
const
|
|
215
|
+
const installScope = {
|
|
175
216
|
global: group.skills[0].scope === "global",
|
|
176
217
|
cwd,
|
|
177
218
|
home,
|
|
178
|
-
}
|
|
219
|
+
}
|
|
220
|
+
const installedHere = allInstalled(group.skills, installScope)
|
|
221
|
+
// A completed install followed by the known exit crash counts as success.
|
|
222
|
+
const executeTolerant = (command, runOptions) => {
|
|
223
|
+
const result = execute(command, runOptions)
|
|
224
|
+
if (POST_INSTALL_CRASH_CODES.has(statusCode(result)) && allInstalled(group.skills, installScope)) {
|
|
225
|
+
console.error(`[${sourceLabel(group.skills)}] the skills CLI crashed after installing (known Windows bug); treating the install as complete`)
|
|
226
|
+
return { status: 0 }
|
|
227
|
+
}
|
|
228
|
+
return result
|
|
229
|
+
}
|
|
179
230
|
// Only a previous install can be skipped, so check the remote before
|
|
180
231
|
// installing only when there is a record and the files are still present.
|
|
181
232
|
// A first install records the revision afterwards.
|
|
182
233
|
let revision = null
|
|
183
234
|
|
|
184
235
|
if (!options.force && entry && installedHere) {
|
|
185
|
-
revision =
|
|
236
|
+
revision = resolveRevision(group)
|
|
186
237
|
if (revision.status === "known" && entry.revision === revision.revision) {
|
|
187
238
|
console.log(`# unchanged (${revision.revision.slice(0, 7)}); skipping, use --force to reinstall`)
|
|
188
239
|
skipped += 1
|
|
@@ -202,9 +253,10 @@ export async function syncCommand(manifestFilename, options = {}) {
|
|
|
202
253
|
if (group.skills.length === 1) {
|
|
203
254
|
const skill = group.skills[0]
|
|
204
255
|
printDescriptions(group.skills)
|
|
205
|
-
const result = await runWithRetries(
|
|
256
|
+
const result = await runWithRetries(executeTolerant, group.command, {
|
|
206
257
|
cwd: options.cwd,
|
|
207
258
|
env: gitEnv,
|
|
259
|
+
fallbackEnv,
|
|
208
260
|
retries,
|
|
209
261
|
retryDelayMs,
|
|
210
262
|
label: skill.name,
|
|
@@ -219,7 +271,11 @@ export async function syncCommand(manifestFilename, options = {}) {
|
|
|
219
271
|
// A batched command shares one fetch. Since a failure could come from a
|
|
220
272
|
// single bad name or a transient error, retry each entry individually
|
|
221
273
|
// when the batch does not succeed.
|
|
222
|
-
|
|
274
|
+
let batch = executeTolerant(group.command, { cwd: options.cwd, env: gitEnv })
|
|
275
|
+
if (statusCode(batch) !== 0 && !batch.signal && fallbackEnv) {
|
|
276
|
+
console.error(`[${sourceLabel(group.skills)}] batch install failed; retrying via fallback mirror`)
|
|
277
|
+
batch = executeTolerant(group.command, { cwd: options.cwd, env: fallbackEnv })
|
|
278
|
+
}
|
|
223
279
|
if (statusCode(batch) !== 0) {
|
|
224
280
|
if (batch.signal) {
|
|
225
281
|
failures += group.skills.length
|
|
@@ -229,9 +285,10 @@ export async function syncCommand(manifestFilename, options = {}) {
|
|
|
229
285
|
} else {
|
|
230
286
|
console.error(`[${sourceLabel(group.skills)}] batch install failed; retrying each skill individually`)
|
|
231
287
|
for (const skill of group.skills) {
|
|
232
|
-
const result = await runWithRetries(
|
|
288
|
+
const result = await runWithRetries(executeTolerant, commandFor(skill, manifest.cli), {
|
|
233
289
|
cwd: options.cwd,
|
|
234
290
|
env: gitEnv,
|
|
291
|
+
fallbackEnv,
|
|
235
292
|
retries,
|
|
236
293
|
retryDelayMs,
|
|
237
294
|
label: skill.name,
|
|
@@ -251,7 +308,7 @@ export async function syncCommand(manifestFilename, options = {}) {
|
|
|
251
308
|
}
|
|
252
309
|
|
|
253
310
|
if (!groupFailed) {
|
|
254
|
-
if (!revision) revision =
|
|
311
|
+
if (!revision) revision = resolveRevision(group)
|
|
255
312
|
if (revision.status === "known") {
|
|
256
313
|
setStateEntry(state, filename, key, {
|
|
257
314
|
revision: revision.revision,
|
package/src/mirror.mjs
CHANGED
package/src/revision.mjs
CHANGED
|
@@ -53,3 +53,21 @@ export function probeRevision(url, ref, { timeoutMs = 10000, env = process.env }
|
|
|
53
53
|
if (!COMMIT_SHA.test(revision)) return { status: "unknown" }
|
|
54
54
|
return { status: "known", revision: revision.toLowerCase() }
|
|
55
55
|
}
|
|
56
|
+
|
|
57
|
+
const GITHUB_CHECK_URL = "https://github.com/octocat/Hello-World.git"
|
|
58
|
+
|
|
59
|
+
export function isGithubSource(source) {
|
|
60
|
+
if (SHORTHAND.test(source) && !source.split("/").some((part) => part.startsWith("."))) return true
|
|
61
|
+
if (/^git@github\.com:/i.test(source)) return true
|
|
62
|
+
try {
|
|
63
|
+
return /(^|\.)github\.com$/i.test(new URL(source).hostname)
|
|
64
|
+
} catch {
|
|
65
|
+
return false
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// A short direct probe decides whether sync runs against github.com or the
|
|
70
|
+
// fallback mirror for this run.
|
|
71
|
+
export function checkGithubReachable({ timeoutMs = 5000 } = {}) {
|
|
72
|
+
return probeRevision(GITHUB_CHECK_URL, "HEAD", { timeoutMs }).status === "known"
|
|
73
|
+
}
|
package/src/runner.mjs
CHANGED
|
@@ -89,6 +89,13 @@ function windowsNpxCli(env) {
|
|
|
89
89
|
return filename
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
// The skills CLI can crash on exit on some Windows setups (a libuv teardown
|
|
93
|
+
// assertion) while its telemetry flush is pending. DO_NOT_TRACK disables that
|
|
94
|
+
// flush so the process exits cleanly; it is also a privacy default.
|
|
95
|
+
export function childEnv(env = process.env) {
|
|
96
|
+
return { ...env, DO_NOT_TRACK: "1" }
|
|
97
|
+
}
|
|
98
|
+
|
|
92
99
|
export function runCommand(command, { cwd, env = process.env, timeout } = {}) {
|
|
93
100
|
try {
|
|
94
101
|
// Windows cannot spawn .cmd files directly. Run npx's JS entry point so
|
|
@@ -97,7 +104,7 @@ export function runCommand(command, { cwd, env = process.env, timeout } = {}) {
|
|
|
97
104
|
return spawnSync(useNode ? process.execPath : command.executable,
|
|
98
105
|
useNode ? [windowsNpxCli(env), ...command.args] : command.args, {
|
|
99
106
|
cwd,
|
|
100
|
-
env,
|
|
107
|
+
env: childEnv(env),
|
|
101
108
|
timeout,
|
|
102
109
|
stdio: "inherit",
|
|
103
110
|
windowsHide: true,
|