opencode-skills-doctor 0.5.2 → 0.6.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 +15 -15
- package/docs/README.zh-CN.md +15 -15
- package/package.json +2 -2
- package/src/cli.mjs +55 -2
- package/src/dsh.mjs +185 -0
- package/src/i18n.mjs +20 -2
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Local scanner, linter, fixer, updater and **OpenCode plugin** for agent skills
|
|
|
23
23
|
| **Plan** | exact token savings: trim descriptions and/or hide skills, projected over N steps |
|
|
24
24
|
| **Cost** | CLI: 0 tokens. Plugin: ~191 tokens/step for the tool definition, plus a one-time warning per session |
|
|
25
25
|
| **Requirements** | Node ≥ 20, zero runtime dependencies |
|
|
26
|
-
| **Quality** |
|
|
26
|
+
| **Quality** | 57 tests, CI on ubuntu/macos/windows × Node 20/22/24 |
|
|
27
27
|
|
|
28
28
|
## Why
|
|
29
29
|
|
|
@@ -342,7 +342,19 @@ It scans `.opencode/skills`, `.claude/skills` and `.agents/skills` (project and
|
|
|
342
342
|
|
|
343
343
|
### DeepSeek Harness (DSH)
|
|
344
344
|
|
|
345
|
-
|
|
345
|
+
One command installs both the MCP tool and a skill that tells the model when to audit:
|
|
346
|
+
|
|
347
|
+
```powershell
|
|
348
|
+
npx -y opencode-skills-doctor install --client dsh
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
It locates `~/.dsh/profiles/<profile>/cordis.patch.yml` (default `desktop`; otherwise `--profile <name>`), appends the `mcp-skills-doctor` entry idempotently, and copies the `skills-doctor-audit` skill into `~/.dsh/skills/`. Restart DSH to load the MCP tool; the skill is watched and appears without a restart.
|
|
352
|
+
|
|
353
|
+
- tool name `mcp__skills_doctor__skills_doctor`, arguments `directory`, `budget`, `lang`, `global`, `html`;
|
|
354
|
+
- `uninstall --client dsh` removes both the patch entry and the skill;
|
|
355
|
+
- `--dsh-home <dir>` for a non-standard DSH home.
|
|
356
|
+
|
|
357
|
+
Manual fallback — add this to the profile patch yourself:
|
|
346
358
|
|
|
347
359
|
```yaml
|
|
348
360
|
- id: mcp-skills-doctor
|
|
@@ -354,20 +366,8 @@ DSH ships an MCP client bundle (`@deepseek-ai/dsh-mcp-client`). Add one entry to
|
|
|
354
366
|
args: ["-y", "opencode-skills-doctor", "mcp"]
|
|
355
367
|
```
|
|
356
368
|
|
|
357
|
-
Restart DSH. The tool shows up as `mcp__skills_doctor__skills_doctor` and accepts `directory`, `budget`, `lang`, `global` and `html`.
|
|
358
|
-
|
|
359
369
|
On Windows, if the stdio spawn cannot resolve `npx`, use `command: cmd` and `args: ["/c", "npx", "-y", "opencode-skills-doctor", "mcp"]`.
|
|
360
370
|
|
|
361
|
-
#### Optional skill (tells the model when to audit)
|
|
362
|
-
|
|
363
|
-
The repo ships a DSH skill at `skills/dsh-skills-doctor/`:
|
|
364
|
-
|
|
365
|
-
```powershell
|
|
366
|
-
Copy-Item -Recurse skills\dsh-skills-doctor "$env:USERPROFILE\.dsh\skills\"
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
DSH watches skill roots, so `skills-doctor-audit` appears in the catalog without a restart. The skill runs `check`/`plan`, explains the token impact, and only proposes fixes — it never edits skills without confirmation.
|
|
370
|
-
|
|
371
371
|
## How it works
|
|
372
372
|
|
|
373
373
|
**Audit pipeline**
|
|
@@ -517,7 +517,7 @@ Executing code is free, but the tool definition is part of the model's context.
|
|
|
517
517
|
## Development and contributing
|
|
518
518
|
|
|
519
519
|
```powershell
|
|
520
|
-
npm test #
|
|
520
|
+
npm test # 57 tests (node test/doctor.test.mjs)
|
|
521
521
|
npm run bench # lint timing for 100/500/1000 skills
|
|
522
522
|
```
|
|
523
523
|
|
package/docs/README.zh-CN.md
CHANGED
|
@@ -23,7 +23,7 @@ OpenCode skill 的**本地检测器 + 更新器 + 修复器**,并附带 **Open
|
|
|
23
23
|
| **规划** | 精确 token 节省:精简描述 / 隐藏 skill,按 N 步折算 |
|
|
24
24
|
| **成本** | CLI 0 token;插件工具定义约 191/步,另有每会话一次预警 |
|
|
25
25
|
| **要求** | Node ≥ 20,零运行时依赖 |
|
|
26
|
-
| **质量** |
|
|
26
|
+
| **质量** | 57 个测试,ubuntu/macos/windows × Node 20/22/24 CI |
|
|
27
27
|
|
|
28
28
|
## 为什么需要它
|
|
29
29
|
|
|
@@ -342,7 +342,19 @@ args = ["-y", "opencode-skills-doctor", "mcp"]
|
|
|
342
342
|
|
|
343
343
|
### DeepSeek Harness(DSH)
|
|
344
344
|
|
|
345
|
-
|
|
345
|
+
一条命令同时装好 MCP 工具和「告诉模型什么时候该体检」的 skill:
|
|
346
|
+
|
|
347
|
+
```powershell
|
|
348
|
+
npx -y opencode-skills-doctor install --client dsh
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
它会找到 `~/.dsh/profiles/<profile>/cordis.patch.yml`(默认 `desktop`,否则用 `--profile <name>`),幂等地追加 `mcp-skills-doctor` 条目,并把 `skills-doctor-audit` 技能复制到 `~/.dsh/skills/`。重启 DSH 加载 MCP 工具;skill 目录有监听,无需重启。
|
|
352
|
+
|
|
353
|
+
- 工具名 `mcp__skills_doctor__skills_doctor`,参数 `directory`、`budget`、`lang`、`global`、`html`;
|
|
354
|
+
- `uninstall --client dsh` 同时移除配置条目和 skill;
|
|
355
|
+
- `--dsh-home <dir>` 用于非默认的 DSH 主目录。
|
|
356
|
+
|
|
357
|
+
手动兜底 —— 自己往 profile patch 里加:
|
|
346
358
|
|
|
347
359
|
```yaml
|
|
348
360
|
- id: mcp-skills-doctor
|
|
@@ -354,20 +366,8 @@ DSH 自带 MCP 客户端 bundle(`@deepseek-ai/dsh-mcp-client`)。在 profile
|
|
|
354
366
|
args: ["-y", "opencode-skills-doctor", "mcp"]
|
|
355
367
|
```
|
|
356
368
|
|
|
357
|
-
重启 DSH。工具会以 `mcp__skills_doctor__skills_doctor` 出现,参数支持 `directory`、`budget`、`lang`、`global`、`html`。
|
|
358
|
-
|
|
359
369
|
Windows 上如果 stdio 起不来 `npx`,改成 `command: cmd`、`args: ["/c", "npx", "-y", "opencode-skills-doctor", "mcp"]`。
|
|
360
370
|
|
|
361
|
-
#### 可选技能包(告诉模型什么时候该体检)
|
|
362
|
-
|
|
363
|
-
仓库自带一个 DSH skill:`skills/dsh-skills-doctor/`:
|
|
364
|
-
|
|
365
|
-
```powershell
|
|
366
|
-
Copy-Item -Recurse skills\dsh-skills-doctor "$env:USERPROFILE\.dsh\skills\"
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
DSH 会监听 skill 目录,`skills-doctor-audit` 无需重启就会出现在目录里。它会跑 `check`/`plan`、解释 token 影响,并且只提出修复建议 —— 不经确认不会改 skill 文件。
|
|
370
|
-
|
|
371
371
|
## 工作原理
|
|
372
372
|
|
|
373
373
|
**审计流程**
|
|
@@ -517,7 +517,7 @@ token 是启发式估算,1200 是建议预算。可以用 `--budget` 或 `.ski
|
|
|
517
517
|
## 开发与贡献
|
|
518
518
|
|
|
519
519
|
```powershell
|
|
520
|
-
npm test #
|
|
520
|
+
npm test # 57 个测试(node test/doctor.test.mjs)
|
|
521
521
|
npm run bench # 100/500/1000 个 skill 的 lint 耗时
|
|
522
522
|
```
|
|
523
523
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-skills-doctor",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Audit, fix and update agent skills: token cost, broken references, shadowed duplicates, HTML reports, catalog sync. CLI, OpenCode plugin, MCP server; works with OpenCode, Claude Code, Codex, Grok CLI and
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Audit, fix and update agent skills: token cost, broken references, shadowed duplicates, HTML reports, catalog sync. CLI, OpenCode plugin, MCP server; works with OpenCode, Claude Code, Codex, Grok CLI, Cursor and DeepSeek Harness.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": "./src/plugin.mjs",
|
package/src/cli.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import { auditSkills } from "./audit.mjs"
|
|
|
9
9
|
import { loadConfig } from "./config.mjs"
|
|
10
10
|
import { startMcpServer } from "./mcp.mjs"
|
|
11
11
|
import { installOpenCode, uninstallOpenCode } from "./install.mjs"
|
|
12
|
+
import { installDsh, uninstallDsh } from "./dsh.mjs"
|
|
12
13
|
import { writeHtmlReport, openReport } from "./html.mjs"
|
|
13
14
|
import { planBudget } from "./plan.mjs"
|
|
14
15
|
import {
|
|
@@ -72,6 +73,8 @@ function parseArgs(argv) {
|
|
|
72
73
|
checkOnly: false,
|
|
73
74
|
prune: false,
|
|
74
75
|
client: "auto",
|
|
76
|
+
profile: null,
|
|
77
|
+
dshHome: null,
|
|
75
78
|
configDir: null,
|
|
76
79
|
noNpm: false,
|
|
77
80
|
html: null,
|
|
@@ -153,6 +156,12 @@ function parseArgs(argv) {
|
|
|
153
156
|
options.client = String(args[++i] ?? "").toLowerCase()
|
|
154
157
|
break
|
|
155
158
|
}
|
|
159
|
+
case "--profile":
|
|
160
|
+
options.profile = args[++i] ?? null
|
|
161
|
+
break
|
|
162
|
+
case "--dsh-home":
|
|
163
|
+
options.dshHome = path.resolve(args[++i] ?? ".")
|
|
164
|
+
break
|
|
156
165
|
case "--no-npm":
|
|
157
166
|
options.noNpm = true
|
|
158
167
|
break
|
|
@@ -591,8 +600,8 @@ export async function main(argv) {
|
|
|
591
600
|
process.exit(2)
|
|
592
601
|
}
|
|
593
602
|
|
|
594
|
-
if (!["auto", "opencode", "desktop"].includes(options.client)) {
|
|
595
|
-
console.error("unsupported --client: expected auto, opencode or
|
|
603
|
+
if (!["auto", "opencode", "desktop", "dsh"].includes(options.client)) {
|
|
604
|
+
console.error("unsupported --client: expected auto, opencode, desktop or dsh")
|
|
596
605
|
process.exitCode = 2
|
|
597
606
|
return
|
|
598
607
|
}
|
|
@@ -616,6 +625,29 @@ export async function main(argv) {
|
|
|
616
625
|
return
|
|
617
626
|
}
|
|
618
627
|
|
|
628
|
+
if (options.command === "install" && options.client === "dsh") {
|
|
629
|
+
const result = installDsh({
|
|
630
|
+
home: options.dshHome ?? undefined,
|
|
631
|
+
profile: options.profile ?? undefined,
|
|
632
|
+
dryRun: options.dryRun,
|
|
633
|
+
})
|
|
634
|
+
if (options.json) {
|
|
635
|
+
console.log(JSON.stringify(result, null, 2))
|
|
636
|
+
} else {
|
|
637
|
+
console.log(bold(t("dsh.title")))
|
|
638
|
+
if (result.profile) console.log(dim(t("dsh.profile", { profile: result.profile })))
|
|
639
|
+
if (result.patchFile) console.log(dim(t("dsh.patch", { file: result.patchFile })))
|
|
640
|
+
if (result.skillDir) console.log(dim(t("dsh.skill", { dir: result.skillDir })))
|
|
641
|
+
if (result.dryRun) console.log(dim(t("install.dryRun")))
|
|
642
|
+
for (const action of result.actions) console.log(` · ${action}`)
|
|
643
|
+
for (const error of result.errors) console.log(red(t("install.error", { error })))
|
|
644
|
+
console.log("")
|
|
645
|
+
console.log(result.errors.length ? red(t("dsh.failed")) : dim(t("dsh.restart")))
|
|
646
|
+
}
|
|
647
|
+
process.exitCode = result.errors.length ? 1 : 0
|
|
648
|
+
return
|
|
649
|
+
}
|
|
650
|
+
|
|
619
651
|
if (options.command === "install") {
|
|
620
652
|
const result = installOpenCode({
|
|
621
653
|
configDir: options.configDir ?? undefined,
|
|
@@ -645,6 +677,27 @@ export async function main(argv) {
|
|
|
645
677
|
return
|
|
646
678
|
}
|
|
647
679
|
|
|
680
|
+
if (options.command === "uninstall" && options.client === "dsh") {
|
|
681
|
+
const result = uninstallDsh({
|
|
682
|
+
home: options.dshHome ?? undefined,
|
|
683
|
+
profile: options.profile ?? undefined,
|
|
684
|
+
dryRun: options.dryRun,
|
|
685
|
+
})
|
|
686
|
+
if (options.json) {
|
|
687
|
+
console.log(JSON.stringify(result, null, 2))
|
|
688
|
+
} else {
|
|
689
|
+
console.log(bold(t("dsh.uninstallTitle")))
|
|
690
|
+
if (result.profile) console.log(dim(t("dsh.profile", { profile: result.profile })))
|
|
691
|
+
if (result.dryRun) console.log(dim(t("install.dryRun")))
|
|
692
|
+
for (const action of result.actions) console.log(` · ${action}`)
|
|
693
|
+
for (const error of result.errors) console.log(red(t("install.error", { error })))
|
|
694
|
+
console.log("")
|
|
695
|
+
console.log(result.errors.length ? red(t("dsh.failed")) : dim(t("dsh.restart")))
|
|
696
|
+
}
|
|
697
|
+
process.exitCode = result.errors.length ? 1 : 0
|
|
698
|
+
return
|
|
699
|
+
}
|
|
700
|
+
|
|
648
701
|
if (options.command === "uninstall") {
|
|
649
702
|
const result = uninstallOpenCode({
|
|
650
703
|
configDir: options.configDir ?? undefined,
|
package/src/dsh.mjs
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import fs from "node:fs"
|
|
2
|
+
import path from "node:path"
|
|
3
|
+
import os from "node:os"
|
|
4
|
+
import { fileURLToPath } from "node:url"
|
|
5
|
+
|
|
6
|
+
export const ENTRY_ID = "mcp-skills-doctor"
|
|
7
|
+
export const SERVER_NAME = "skills_doctor"
|
|
8
|
+
export const SKILL_NAME = "skills-doctor-audit"
|
|
9
|
+
|
|
10
|
+
const ENTRIES = `- id: ${ENTRY_ID}
|
|
11
|
+
name: "@deepseek-ai/dsh-mcp-client"
|
|
12
|
+
config:
|
|
13
|
+
serverName: ${SERVER_NAME}
|
|
14
|
+
transport: stdio
|
|
15
|
+
command: npx
|
|
16
|
+
args: ["-y", "opencode-skills-doctor", "mcp"]
|
|
17
|
+
`
|
|
18
|
+
|
|
19
|
+
export function dshHome(env = process.env) {
|
|
20
|
+
return env.DSH_HOME || path.join(os.homedir(), ".dsh")
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function profileDir(home, profile) {
|
|
24
|
+
return path.join(home, "profiles", profile)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** DSH profile 目录(含 package.json 的子目录)。 */
|
|
28
|
+
export function listProfiles(home) {
|
|
29
|
+
const dir = path.join(home, "profiles")
|
|
30
|
+
try {
|
|
31
|
+
return fs
|
|
32
|
+
.readdirSync(dir, { withFileTypes: true })
|
|
33
|
+
.filter((entry) => entry.isDirectory() && fs.existsSync(path.join(dir, entry.name, "package.json")))
|
|
34
|
+
.map((entry) => entry.name)
|
|
35
|
+
.sort()
|
|
36
|
+
} catch {
|
|
37
|
+
return []
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function pickProfile({ home, profile }) {
|
|
42
|
+
const profiles = listProfiles(home)
|
|
43
|
+
if (profile) {
|
|
44
|
+
if (!profiles.includes(profile)) {
|
|
45
|
+
return { error: `DSH profile "${profile}" not found under ${path.join(home, "profiles")}` }
|
|
46
|
+
}
|
|
47
|
+
return { profile, profiles }
|
|
48
|
+
}
|
|
49
|
+
if (profiles.includes("desktop")) return { profile: "desktop", profiles }
|
|
50
|
+
if (profiles.length === 1) return { profile: profiles[0], profiles }
|
|
51
|
+
if (!profiles.length) return { error: `no DSH profile found under ${path.join(home, "profiles")}` }
|
|
52
|
+
return { error: `multiple DSH profiles found (${profiles.join(", ")}); pass --profile <name>` }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function skillSourceDir() {
|
|
56
|
+
return fileURLToPath(new URL("../skills/dsh-skills-doctor", import.meta.url))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function copySkill(source, target, dryRun, act, dry) {
|
|
60
|
+
if (!fs.existsSync(source)) return false
|
|
61
|
+
const file = path.join(source, "SKILL.md")
|
|
62
|
+
if (!fs.existsSync(file)) return false
|
|
63
|
+
const targetFile = path.join(target, "SKILL.md")
|
|
64
|
+
if (fs.existsSync(targetFile) && fs.readFileSync(targetFile, "utf8") === fs.readFileSync(file, "utf8")) {
|
|
65
|
+
act(`skill already up to date ${target}`)
|
|
66
|
+
return true
|
|
67
|
+
}
|
|
68
|
+
if (dryRun) {
|
|
69
|
+
dry(`install skill ${SKILL_NAME} into ${target}`)
|
|
70
|
+
return true
|
|
71
|
+
}
|
|
72
|
+
fs.mkdirSync(target, { recursive: true })
|
|
73
|
+
fs.copyFileSync(file, targetFile)
|
|
74
|
+
act(`wrote skill ${targetFile}`)
|
|
75
|
+
return true
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** 在 cordis.patch.yml 里追加 MCP 条目(幂等)。 */
|
|
79
|
+
function appendEntry(patchFile, dryRun, act, dry) {
|
|
80
|
+
const existing = fs.existsSync(patchFile) ? fs.readFileSync(patchFile, "utf8") : ""
|
|
81
|
+
if (existing.includes(ENTRY_ID) || existing.includes(`serverName: ${SERVER_NAME}`)) {
|
|
82
|
+
act(`MCP entry already present in ${patchFile}`)
|
|
83
|
+
return false
|
|
84
|
+
}
|
|
85
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, "-")
|
|
86
|
+
if (dryRun) {
|
|
87
|
+
dry(`append ${ENTRY_ID} to ${patchFile}`)
|
|
88
|
+
return true
|
|
89
|
+
}
|
|
90
|
+
if (existing) fs.copyFileSync(patchFile, `${patchFile}.bak-${stamp}`)
|
|
91
|
+
const header = existing
|
|
92
|
+
? (existing.endsWith("\n") ? "" : "\n")
|
|
93
|
+
: "# DSH profile patch layer\n"
|
|
94
|
+
const next = `${existing}${header}${existing.trim() ? "\n" : ""}${ENTRIES}`
|
|
95
|
+
fs.writeFileSync(patchFile, next, "utf8")
|
|
96
|
+
act(`appended ${ENTRY_ID} to ${patchFile}`)
|
|
97
|
+
return true
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function removeEntry(patchFile, dryRun, act, dry) {
|
|
101
|
+
if (!fs.existsSync(patchFile)) return false
|
|
102
|
+
const text = fs.readFileSync(patchFile, "utf8")
|
|
103
|
+
const lines = text.split(/\r?\n/)
|
|
104
|
+
const index = lines.findIndex((line) => line.trim() === `- id: ${ENTRY_ID}`)
|
|
105
|
+
if (index < 0) return false
|
|
106
|
+
|
|
107
|
+
let end = index + 1
|
|
108
|
+
while (end < lines.length) {
|
|
109
|
+
const line = lines[end]
|
|
110
|
+
if (line.trim() === "" || /^[ \t]/.test(line)) end++
|
|
111
|
+
else break
|
|
112
|
+
}
|
|
113
|
+
if (dryRun) {
|
|
114
|
+
dry(`remove ${ENTRY_ID} from ${patchFile}`)
|
|
115
|
+
return true
|
|
116
|
+
}
|
|
117
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, "-")
|
|
118
|
+
fs.copyFileSync(patchFile, `${patchFile}.bak-${stamp}`)
|
|
119
|
+
fs.writeFileSync(patchFile, lines.slice(0, index).concat(lines.slice(end)).join("\n"), "utf8")
|
|
120
|
+
act(`removed ${ENTRY_ID} from ${patchFile}`)
|
|
121
|
+
return true
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function removeSkill(target, dryRun, act, dry) {
|
|
125
|
+
const file = path.join(target, "SKILL.md")
|
|
126
|
+
if (!fs.existsSync(file)) return false
|
|
127
|
+
const text = fs.readFileSync(file, "utf8")
|
|
128
|
+
if (!text.includes("Skill audit workflow")) {
|
|
129
|
+
act(`kept ${target} (not the generated skill)`)
|
|
130
|
+
return false
|
|
131
|
+
}
|
|
132
|
+
if (dryRun) {
|
|
133
|
+
dry(`delete skill ${target}`)
|
|
134
|
+
return true
|
|
135
|
+
}
|
|
136
|
+
fs.rmSync(target, { recursive: true, force: true })
|
|
137
|
+
act(`deleted skill ${target}`)
|
|
138
|
+
return true
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function installDsh({ home = dshHome(), profile = null, dryRun = false } = {}) {
|
|
142
|
+
const actions = []
|
|
143
|
+
const errors = []
|
|
144
|
+
const dry = (message) => actions.push(`[dry-run] ${message}`)
|
|
145
|
+
const act = (message) => actions.push(message)
|
|
146
|
+
|
|
147
|
+
const picked = pickProfile({ home, profile })
|
|
148
|
+
if (picked.error) {
|
|
149
|
+
errors.push(picked.error)
|
|
150
|
+
return { ok: false, home, actions, errors, dryRun }
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const dir = profileDir(home, picked.profile)
|
|
154
|
+
const patchFile = path.join(dir, "cordis.patch.yml")
|
|
155
|
+
const skillDir = path.join(home, "skills", SKILL_NAME)
|
|
156
|
+
|
|
157
|
+
if (dryRun) dry(`profile: ${picked.profile} (${dir})`)
|
|
158
|
+
else act(`profile: ${picked.profile}`)
|
|
159
|
+
|
|
160
|
+
appendEntry(patchFile, dryRun, act, dry)
|
|
161
|
+
copySkill(skillSourceDir(), skillDir, dryRun, act, dry)
|
|
162
|
+
|
|
163
|
+
return { ok: errors.length === 0, home, profile: picked.profile, patchFile, skillDir, actions, errors, dryRun }
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function uninstallDsh({ home = dshHome(), profile = null, dryRun = false } = {}) {
|
|
167
|
+
const actions = []
|
|
168
|
+
const errors = []
|
|
169
|
+
const dry = (message) => actions.push(`[dry-run] ${message}`)
|
|
170
|
+
const act = (message) => actions.push(message)
|
|
171
|
+
|
|
172
|
+
const picked = pickProfile({ home, profile })
|
|
173
|
+
if (picked.error) {
|
|
174
|
+
errors.push(picked.error)
|
|
175
|
+
return { ok: false, home, actions, errors, dryRun }
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const dir = profileDir(home, picked.profile)
|
|
179
|
+
const patchFile = path.join(dir, "cordis.patch.yml")
|
|
180
|
+
const skillDir = path.join(home, "skills", SKILL_NAME)
|
|
181
|
+
|
|
182
|
+
removeEntry(patchFile, dryRun, act, dry)
|
|
183
|
+
removeSkill(skillDir, dryRun, act, dry)
|
|
184
|
+
return { ok: errors.length === 0, home, profile: picked.profile, patchFile, skillDir, actions, errors, dryRun }
|
|
185
|
+
}
|
package/src/i18n.mjs
CHANGED
|
@@ -36,7 +36,9 @@ Options:
|
|
|
36
36
|
--html [file] check: write a self-contained HTML report (default: skills-report.html)
|
|
37
37
|
--open check: open the HTML report after writing it
|
|
38
38
|
--config-dir <dir> install/uninstall: OpenCode config directory (default: ~/.config/opencode)
|
|
39
|
-
--client <name> install/uninstall: auto | opencode | desktop (default auto)
|
|
39
|
+
--client <name> install/uninstall: auto | opencode | desktop | dsh (default auto)
|
|
40
|
+
--profile <name> dsh: profile to install into (default: desktop, or the only one)
|
|
41
|
+
--dsh-home <dir> dsh: DSH home directory (default: ~/.dsh)
|
|
40
42
|
--no-npm install/uninstall: do not run npm (1.x)
|
|
41
43
|
--apply upgrade: run npm install -g opencode-skills-doctor@latest
|
|
42
44
|
--no-update-check skip the automatic update check for this run
|
|
@@ -120,6 +122,13 @@ Exit codes: 0 ok; 1 errors (and warnings with --strict); 2 bad arguments or runt
|
|
|
120
122
|
"install.next": "Restart OpenCode to activate.",
|
|
121
123
|
"uninstall.title": "Remove OpenCode integration",
|
|
122
124
|
"uninstall.next": "Restart OpenCode to finish removal.",
|
|
125
|
+
"dsh.title": "DSH integration",
|
|
126
|
+
"dsh.uninstallTitle": "Remove DSH integration",
|
|
127
|
+
"dsh.profile": " profile: {profile}",
|
|
128
|
+
"dsh.patch": " patch: {file}",
|
|
129
|
+
"dsh.skill": " skill: {dir}",
|
|
130
|
+
"dsh.restart": "Restart DSH to load the MCP tool (the skill updates live).",
|
|
131
|
+
"dsh.failed": "DSH integration failed.",
|
|
123
132
|
"update.toolAvailable":
|
|
124
133
|
" ! skills-doctor {latest} is available (you have {current}); update with: {cmd} · {url}",
|
|
125
134
|
"upgrade.title": "skills-doctor upgrade",
|
|
@@ -248,7 +257,9 @@ Exit codes: 0 ok; 1 errors (and warnings with --strict); 2 bad arguments or runt
|
|
|
248
257
|
--html [file] check: 生成自包含 HTML 报告(默认 skills-report.html)
|
|
249
258
|
--open check: 写完报告后用浏览器打开
|
|
250
259
|
--config-dir <dir> install/uninstall: OpenCode 配置目录(默认 ~/.config/opencode)
|
|
251
|
-
--client <name> install/uninstall: auto | opencode | desktop(默认 auto)
|
|
260
|
+
--client <name> install/uninstall: auto | opencode | desktop | dsh(默认 auto)
|
|
261
|
+
--profile <name> dsh: 安装到哪个 profile(默认 desktop,或唯一的一个)
|
|
262
|
+
--dsh-home <dir> dsh: DSH 主目录(默认 ~/.dsh)
|
|
252
263
|
--no-npm install/uninstall: 不执行 npm(1.x)
|
|
253
264
|
--apply upgrade: 执行 npm install -g opencode-skills-doctor@latest
|
|
254
265
|
--no-update-check 本次运行跳过自动更新检查
|
|
@@ -332,6 +343,13 @@ Exit codes: 0 ok; 1 errors (and warnings with --strict); 2 bad arguments or runt
|
|
|
332
343
|
"install.next": "重启 OpenCode 生效。",
|
|
333
344
|
"uninstall.title": "撤销 OpenCode 集成",
|
|
334
345
|
"uninstall.next": "重启 OpenCode 完成卸载。",
|
|
346
|
+
"dsh.title": "DSH 集成",
|
|
347
|
+
"dsh.uninstallTitle": "撤销 DSH 集成",
|
|
348
|
+
"dsh.profile": " profile: {profile}",
|
|
349
|
+
"dsh.patch": " patch: {file}",
|
|
350
|
+
"dsh.skill": " skill: {dir}",
|
|
351
|
+
"dsh.restart": "重启 DSH 加载 MCP 工具(skill 会即时更新)。",
|
|
352
|
+
"dsh.failed": "DSH 集成失败。",
|
|
335
353
|
"update.toolAvailable": " ! skills-doctor 有新版本 {latest}(当前 {current});更新:{cmd} · {url}",
|
|
336
354
|
"upgrade.title": "skills-doctor 自更新",
|
|
337
355
|
"upgrade.current": " 当前版本: {version}",
|