@wang121ye/skillmanager 0.0.4 → 0.0.5
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 +15 -0
- package/package.json +1 -1
- package/src/cli.js +3 -0
- package/src/commands/bootstrap.js +1 -1
- package/src/commands/update.js +1 -1
- package/src/commands/webui.js +1 -1
- package/src/lib/git.js +69 -5
package/README.md
CHANGED
|
@@ -30,6 +30,21 @@ skillmanager webui
|
|
|
30
30
|
skillmanager webui --concurrency 1
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
### 缓存自动刷新(避免“只看到旧的 skills”)
|
|
34
|
+
|
|
35
|
+
为避免第三方用户忘记更新缓存,`skillmanager` 在扫描来源仓库时会检查本地缓存是否落后于远端:
|
|
36
|
+
|
|
37
|
+
- 如果检测到缓存落后,默认会**自动重新拉取**最新仓库。
|
|
38
|
+
- 如需手动控制,可使用 `--force-refresh` 强制刷新,或设置 `SKILLMANAGER_AUTO_REFRESH=0` 关闭自动刷新。
|
|
39
|
+
|
|
40
|
+
示例:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
skillmanager webui --force-refresh
|
|
44
|
+
skillmanager install --force-refresh
|
|
45
|
+
skillmanager update --force-refresh
|
|
46
|
+
```
|
|
47
|
+
|
|
33
48
|
## 安装与使用
|
|
34
49
|
|
|
35
50
|
### 全局安装(推荐)
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -30,6 +30,7 @@ async function main() {
|
|
|
30
30
|
.option('--dry-run', '仅打印将执行的内容,不实际安装', false)
|
|
31
31
|
.option('--concurrency <n>', '选择模式下的并发扫描数(默认:3)', '3')
|
|
32
32
|
.option('--profile <name>', '选择配置名(默认:来自 config 或 SKILLMANAGER_PROFILE 环境变量)')
|
|
33
|
+
.option('--force-refresh', '强制刷新来源仓库缓存(重新拉取)', false)
|
|
33
34
|
.action(async (opts) => {
|
|
34
35
|
await bootstrap(opts);
|
|
35
36
|
});
|
|
@@ -44,6 +45,7 @@ async function main() {
|
|
|
44
45
|
.option('--output <path>', '作用:sync 输出文件路径(默认:AGENTS.md)')
|
|
45
46
|
.option('--no-sync', '作用:不执行 openskills sync(仅安装/卸载,不更新 AGENTS.md)', false)
|
|
46
47
|
.option('--concurrency <n>', '作用:install 模式下并发拉取/扫描来源仓库,提高速度(默认:3)', '3')
|
|
48
|
+
.option('--force-refresh', '强制刷新来源仓库缓存(重新拉取)', false)
|
|
47
49
|
.action(async (opts) => {
|
|
48
50
|
await webui(opts);
|
|
49
51
|
});
|
|
@@ -64,6 +66,7 @@ async function main() {
|
|
|
64
66
|
.option('--no-sync', '跳过 openskills sync', false)
|
|
65
67
|
.option('--profile <name>', '按 profile 选择集更新(会重新安装选中的 skills)')
|
|
66
68
|
.option('--concurrency <n>', '选择模式下的并发扫描数(默认:3)', '3')
|
|
69
|
+
.option('--force-refresh', '强制刷新来源仓库缓存(重新拉取)', false)
|
|
67
70
|
.action(async (opts) => {
|
|
68
71
|
await update(opts);
|
|
69
72
|
});
|
|
@@ -77,7 +77,7 @@ async function bootstrap(opts) {
|
|
|
77
77
|
const skillsById = new Map();
|
|
78
78
|
const perSource = await mapWithConcurrency(enabledSources, concurrency, async (s) => {
|
|
79
79
|
try {
|
|
80
|
-
const repoDir = await ensureRepo({ reposDir: paths.reposDir, source: s });
|
|
80
|
+
const repoDir = await ensureRepo({ reposDir: paths.reposDir, source: s, forceRefresh: !!opts?.forceRefresh });
|
|
81
81
|
const skills = await scanSkillsInRepo({
|
|
82
82
|
sourceId: s.id,
|
|
83
83
|
sourceName: s.name || s.id,
|
package/src/commands/update.js
CHANGED
|
@@ -53,7 +53,7 @@ async function update(opts) {
|
|
|
53
53
|
const skillsById = new Map();
|
|
54
54
|
const perSource = await mapWithConcurrency(enabledSources, concurrency, async (s) => {
|
|
55
55
|
try {
|
|
56
|
-
const repoDir = await ensureRepo({ reposDir: paths.reposDir, source: s });
|
|
56
|
+
const repoDir = await ensureRepo({ reposDir: paths.reposDir, source: s, forceRefresh: !!opts?.forceRefresh });
|
|
57
57
|
const skills = await scanSkillsInRepo({
|
|
58
58
|
sourceId: s.id,
|
|
59
59
|
sourceName: s.name || s.id,
|
package/src/commands/webui.js
CHANGED
|
@@ -91,7 +91,7 @@ async function webui(opts) {
|
|
|
91
91
|
const skillsById = new Map();
|
|
92
92
|
const perSource = await mapWithConcurrency(enabledSources, concurrency, async (s) => {
|
|
93
93
|
try {
|
|
94
|
-
const repoDir = await ensureRepo({ reposDir: paths.reposDir, source: s });
|
|
94
|
+
const repoDir = await ensureRepo({ reposDir: paths.reposDir, source: s, forceRefresh: !!opts?.forceRefresh });
|
|
95
95
|
const skills = await scanSkillsInRepo({
|
|
96
96
|
sourceId: s.id,
|
|
97
97
|
sourceName: s.name || s.id,
|
package/src/lib/git.js
CHANGED
|
@@ -10,7 +10,53 @@ function sleep(ms) {
|
|
|
10
10
|
return new Promise((r) => setTimeout(r, ms));
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
function isTruthy(value) {
|
|
14
|
+
const v = String(value || '').trim().toLowerCase();
|
|
15
|
+
return v === '1' || v === 'true' || v === 'yes' || v === 'on';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function shouldAutoRefreshCache() {
|
|
19
|
+
// 默认开启自动刷新(发现远端更新时自动重新拉取)
|
|
20
|
+
// 如需关闭:SKILLMANAGER_AUTO_REFRESH=0
|
|
21
|
+
if (process.env.SKILLMANAGER_AUTO_REFRESH != null) {
|
|
22
|
+
return isTruthy(process.env.SKILLMANAGER_AUTO_REFRESH);
|
|
23
|
+
}
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function getLocalHead(repoGit) {
|
|
28
|
+
try {
|
|
29
|
+
const out = await repoGit.revparse(['HEAD']);
|
|
30
|
+
return String(out || '').trim() || null;
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function getRemoteHead(repoGit) {
|
|
37
|
+
try {
|
|
38
|
+
const out = await repoGit.raw(['ls-remote', 'origin', 'HEAD']);
|
|
39
|
+
const first = String(out || '').trim().split(/\s+/)[0];
|
|
40
|
+
return first || null;
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function isRepoStale(repoGit) {
|
|
47
|
+
const [localHead, remoteHead] = await Promise.all([getLocalHead(repoGit), getRemoteHead(repoGit)]);
|
|
48
|
+
if (!localHead || !remoteHead) return false;
|
|
49
|
+
return localHead !== remoteHead;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function cloneRepo({ git, repoUrl, repoDir, cloneArgs }) {
|
|
53
|
+
await rmDir(repoDir);
|
|
54
|
+
await ensureDir(repoDir);
|
|
55
|
+
await git.clone(repoUrl, repoDir, cloneArgs);
|
|
56
|
+
return repoDir;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function ensureRepo({ reposDir, source, forceRefresh = false }) {
|
|
14
60
|
if (!source?.repo) {
|
|
15
61
|
throw new Error(`Invalid source repo for ${source?.id || '<unknown>'}`);
|
|
16
62
|
}
|
|
@@ -20,14 +66,12 @@ async function ensureRepo({ reposDir, source }) {
|
|
|
20
66
|
|
|
21
67
|
const gitDir = path.join(repoDir, '.git');
|
|
22
68
|
const git = simpleGit();
|
|
69
|
+
const cloneArgs = ['--depth', '1', '--single-branch', '--filter=blob:none'];
|
|
23
70
|
|
|
24
71
|
if (!(await fileExists(gitDir))) {
|
|
25
|
-
const cloneArgs = ['--depth', '1', '--single-branch', '--filter=blob:none'];
|
|
26
72
|
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
27
73
|
try {
|
|
28
|
-
await
|
|
29
|
-
await ensureDir(repoDir);
|
|
30
|
-
await git.clone(source.repo, repoDir, cloneArgs);
|
|
74
|
+
await cloneRepo({ git, repoUrl: source.repo, repoDir, cloneArgs });
|
|
31
75
|
return repoDir;
|
|
32
76
|
} catch (err) {
|
|
33
77
|
await rmDir(repoDir);
|
|
@@ -50,6 +94,26 @@ async function ensureRepo({ reposDir, source }) {
|
|
|
50
94
|
} catch {
|
|
51
95
|
// ignore ff-only failures (e.g. detached HEAD) — user can clean manually
|
|
52
96
|
}
|
|
97
|
+
|
|
98
|
+
// 如果缓存仓库落后于远端,自动重新拉取(可用 env 关闭)
|
|
99
|
+
try {
|
|
100
|
+
const stale = await isRepoStale(repoGit);
|
|
101
|
+
if (stale) {
|
|
102
|
+
const shouldRefresh = forceRefresh || shouldAutoRefreshCache();
|
|
103
|
+
if (shouldRefresh) {
|
|
104
|
+
// eslint-disable-next-line no-console
|
|
105
|
+
console.warn(`检测到缓存仓库落后于远端:${source.id},正在重新拉取最新版本…`);
|
|
106
|
+
return await cloneRepo({ git, repoUrl: source.repo, repoDir, cloneArgs });
|
|
107
|
+
}
|
|
108
|
+
// eslint-disable-next-line no-console
|
|
109
|
+
console.warn(`检测到缓存仓库落后于远端:${source.id}。可使用 --force-refresh 或设置 SKILLMANAGER_AUTO_REFRESH=1。`);
|
|
110
|
+
}
|
|
111
|
+
} catch {
|
|
112
|
+
// eslint-disable-next-line no-console
|
|
113
|
+
console.warn(
|
|
114
|
+
`提示:无法检查远端版本(可能网络/权限问题),缓存可能过期。可使用 --force-refresh 强制刷新,或删除 ${repoDir} 后重试。`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
53
117
|
return repoDir;
|
|
54
118
|
}
|
|
55
119
|
|