dsh-claude-compat 0.9.0 → 0.9.1
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/package.json +5 -2
- package/src/index.js +42 -31
- package/src/plugin-manager.js +25 -24
- package/scripts/deploy-local.sh +0 -71
- package/scripts/gen-downloads-chart.mjs +0 -36
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-claude-compat",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "DeepSeek Harness plugin: bridge Claude Code's .claude/ directories (project and ~/.claude: skills, commands, rules) into DSH native skill registry and message-stream rules injection.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
"README.md",
|
|
18
18
|
"README.zh-CN.md",
|
|
19
19
|
"cordis.patch.yml",
|
|
20
|
-
"scripts"
|
|
20
|
+
"scripts/cc-resume.mjs",
|
|
21
|
+
"scripts/cc-export.mjs",
|
|
22
|
+
"scripts/zstd-compat.mjs",
|
|
23
|
+
"scripts/dsh-restart.sh"
|
|
21
24
|
],
|
|
22
25
|
"keywords": [
|
|
23
26
|
"dsh",
|
package/src/index.js
CHANGED
|
@@ -121,6 +121,21 @@ const STAR_URL = 'https://github.com/biedongbin/dsh-claude-compat';
|
|
|
121
121
|
// is the intended "did this help? star it" cadence without spamming.
|
|
122
122
|
let starNudged = false;
|
|
123
123
|
|
|
124
|
+
// Body of the reload-cc-plugins / reload-skills skills. Inline skill bodies
|
|
125
|
+
// travel in the locator (`{ kind: 'inline', body }`) — the DSH registry loads
|
|
126
|
+
// definitions exclusively through `provider.get(candidate)`, so the body must
|
|
127
|
+
// be reachable from the candidate itself, never from a closure or disk.
|
|
128
|
+
const RELOAD_BODY = `# Catalog reloaded ✅
|
|
129
|
+
|
|
130
|
+
The DSH skill catalog cache was just dropped and observers notified.
|
|
131
|
+
Freshly installed/removed skills are now visible in this session (the skill
|
|
132
|
+
picker refreshes automatically on the next catalog read).
|
|
133
|
+
|
|
134
|
+
Notes:
|
|
135
|
+
- New skills from \`/plugin install\` or edits under \`.claude\` are live now.
|
|
136
|
+
- MCP servers shipped by plugins still require a DSH restart (process-lifetime mount).
|
|
137
|
+
- \`/reload-cc-plugins\` and \`/reload-skills\` are aliases.`;
|
|
138
|
+
|
|
124
139
|
export function apply(ctx, config = {}) {
|
|
125
140
|
// One-shot "found this useful? star it" nudge, surfaced exactly where the
|
|
126
141
|
// value lands: the first profile activation of this process.
|
|
@@ -236,45 +251,22 @@ class ClaudeCompatSkillProvider {
|
|
|
236
251
|
// Invalidate fires when the definition is actually loaded (get()), not at
|
|
237
252
|
// list() time — loading the reload skill IS the reload.
|
|
238
253
|
reloadSkillDef(name, description) {
|
|
239
|
-
return {
|
|
240
|
-
name,
|
|
241
|
-
description,
|
|
242
|
-
invocation: { modelInvocable: true, userInvocable: true },
|
|
243
|
-
provider: 'claude-compat',
|
|
244
|
-
source: 'claude-compat-manager',
|
|
245
|
-
rank: this.config.pluginManagerRank ?? 40,
|
|
246
|
-
locator: { kind: 'inline', path: import.meta.url },
|
|
247
|
-
get: async () => {
|
|
248
|
-
try { this.control?.invalidate?.(); } catch { /* best-effort */ }
|
|
249
|
-
return {
|
|
250
|
-
name,
|
|
251
|
-
description,
|
|
252
|
-
body: `# Catalog reloaded ✅
|
|
253
|
-
|
|
254
|
-
The DSH skill catalog cache was just dropped and observers notified.
|
|
255
|
-
Freshly installed/removed skills are now visible in this session (the skill
|
|
256
|
-
picker refreshes automatically on the next catalog read).
|
|
257
|
-
|
|
258
|
-
Notes:
|
|
259
|
-
- New skills from \`/plugin install\` or edits under \`.claude\` are live now.
|
|
260
|
-
- MCP servers shipped by plugins still require a DSH restart (process-lifetime mount).
|
|
261
|
-
- \`/reload-cc-plugins\` and \`/reload-skills\` are aliases.`,
|
|
262
|
-
};
|
|
263
|
-
},
|
|
264
|
-
};
|
|
254
|
+
return this.managerSkillCandidate(name, description, undefined, RELOAD_BODY, { invalidate: true });
|
|
265
255
|
}
|
|
266
256
|
|
|
267
|
-
managerSkillCandidate(name, description, whenToUse, body) {
|
|
257
|
+
managerSkillCandidate(name, description, whenToUse, body, { invalidate = false } = {}) {
|
|
268
258
|
return {
|
|
269
259
|
name,
|
|
270
260
|
description,
|
|
271
|
-
whenToUse,
|
|
261
|
+
...(whenToUse !== undefined ? { whenToUse } : {}),
|
|
272
262
|
invocation: { modelInvocable: true, userInvocable: true },
|
|
273
|
-
provider:
|
|
263
|
+
provider: this.name,
|
|
274
264
|
source: 'claude-compat-manager',
|
|
275
265
|
rank: this.config.pluginManagerRank ?? 40,
|
|
276
|
-
|
|
277
|
-
|
|
266
|
+
// kind: 'inline' makes provider.get() serve `body` directly instead of
|
|
267
|
+
// reading a path from disk. No `path` — import.meta.url is a file:// URL
|
|
268
|
+
// and the module source is not the skill body.
|
|
269
|
+
locator: { kind: 'inline', body, invalidate },
|
|
278
270
|
};
|
|
279
271
|
}
|
|
280
272
|
|
|
@@ -294,6 +286,25 @@ Notes:
|
|
|
294
286
|
|
|
295
287
|
async get(candidate) {
|
|
296
288
|
const locator = candidate.locator;
|
|
289
|
+
if (locator?.kind === 'inline') {
|
|
290
|
+
// Inline manager skills (cc-plugin, reload-*, cc-resume, cc-export):
|
|
291
|
+
// the body ships in the locator. Loading a reload skill IS the reload —
|
|
292
|
+
// drop the catalog cache so the next list() recollects fresh state.
|
|
293
|
+
// (Not in list(): that would fire on every catalog collection and
|
|
294
|
+
// defeat the cache entirely.)
|
|
295
|
+
if (locator.invalidate) {
|
|
296
|
+
try { this.control?.invalidate?.(); } catch { /* best-effort */ }
|
|
297
|
+
}
|
|
298
|
+
return {
|
|
299
|
+
name: candidate.name,
|
|
300
|
+
description: candidate.description,
|
|
301
|
+
...(candidate.whenToUse !== undefined ? { whenToUse: candidate.whenToUse } : {}),
|
|
302
|
+
invocation: candidate.invocation,
|
|
303
|
+
source: candidate.source,
|
|
304
|
+
provider: this.name,
|
|
305
|
+
content: locator.body,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
297
308
|
const raw = await readTextSafe(locator.path);
|
|
298
309
|
if (raw === undefined) return undefined;
|
|
299
310
|
const parsed = parseFrontmatter(raw);
|
package/src/plugin-manager.js
CHANGED
|
@@ -7,16 +7,16 @@
|
|
|
7
7
|
// Claude-native locations (~/.claude/plugins/*, settings.json enabledPlugins)
|
|
8
8
|
// so both Claude Code and this plugin's discovery bridge read the same truth.
|
|
9
9
|
//
|
|
10
|
-
// Commands (mirroring `claude plugin` semantics):
|
|
11
|
-
// /plugin — list installed (status incl. enabled/disabled)
|
|
12
|
-
// /plugin <name>[@market] — one-shot install
|
|
13
|
-
// /plugin install <name>[@market]
|
|
14
|
-
// /plugin uninstall|remove <name>
|
|
15
|
-
// /plugin enable <name>
|
|
16
|
-
// /plugin disable <name>
|
|
17
|
-
// /plugin update [name] — one or all
|
|
18
|
-
// /plugin search <term> — scan known marketplaces
|
|
19
|
-
// /plugin marketplace list|add|remove|update [...]
|
|
10
|
+
// Commands (mirroring `claude plugin` semantics, invoked as /cc-plugin):
|
|
11
|
+
// /cc-plugin — list installed (status incl. enabled/disabled)
|
|
12
|
+
// /cc-plugin <name>[@market] — one-shot install
|
|
13
|
+
// /cc-plugin install <name>[@market]
|
|
14
|
+
// /cc-plugin uninstall|remove <name>
|
|
15
|
+
// /cc-plugin enable <name>
|
|
16
|
+
// /cc-plugin disable <name>
|
|
17
|
+
// /cc-plugin update [name] — one or all
|
|
18
|
+
// /cc-plugin search <term> — scan known marketplaces
|
|
19
|
+
// /cc-plugin marketplace list|add|remove|update [...]
|
|
20
20
|
//
|
|
21
21
|
// Safety: never mutates installed_plugins.json / known_marketplaces.json /
|
|
22
22
|
// settings.json directly without a timestamped backup beside the file; the
|
|
@@ -38,22 +38,23 @@ Claude Code and DSH see the same plugins. Newly installed plugin content
|
|
|
38
38
|
appears in the DSH skill catalog on next reconcile (MCP servers need a DSH restart).
|
|
39
39
|
|
|
40
40
|
## Commands (invoked as /cc-plugin <args>)
|
|
41
|
-
- \`/plugin\` — list installed plugins with enable/disable status
|
|
42
|
-
- \`/plugin <name>[@marketplace]\` — one-shot install (e.g. \`/plugin ralph@omc\`)
|
|
43
|
-
- \`/plugin install <name>[@marketplace]\`
|
|
44
|
-
- \`/plugin uninstall <name>\` (alias: remove)
|
|
45
|
-
- \`/plugin enable <name>\` / \`/plugin disable <name>\`
|
|
46
|
-
- \`/plugin update [name]\` — update one plugin or all
|
|
47
|
-
- \`/plugin search <term>\` — search all known marketplaces
|
|
48
|
-
- \`/plugin marketplace list\`
|
|
49
|
-
- \`/plugin marketplace add <github-repo-or-url>\`
|
|
50
|
-
- \`/plugin marketplace remove <name>\`
|
|
51
|
-
- \`/plugin marketplace update [name]\`
|
|
41
|
+
- \`/cc-plugin\` — list installed plugins with enable/disable status
|
|
42
|
+
- \`/cc-plugin <name>[@marketplace]\` — one-shot install (e.g. \`/cc-plugin ralph@omc\`)
|
|
43
|
+
- \`/cc-plugin install <name>[@marketplace]\`
|
|
44
|
+
- \`/cc-plugin uninstall <name>\` (alias: remove)
|
|
45
|
+
- \`/cc-plugin enable <name>\` / \`/cc-plugin disable <name>\`
|
|
46
|
+
- \`/cc-plugin update [name]\` — update one plugin or all
|
|
47
|
+
- \`/cc-plugin search <term>\` — search all known marketplaces
|
|
48
|
+
- \`/cc-plugin marketplace list\`
|
|
49
|
+
- \`/cc-plugin marketplace add <github-repo-or-url>\`
|
|
50
|
+
- \`/cc-plugin marketplace remove <name>\`
|
|
51
|
+
- \`/cc-plugin marketplace update [name]\`
|
|
52
52
|
|
|
53
53
|
## Execution
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
touches). Report which engine
|
|
54
|
+
Run \`claude plugin <args>\` via bash (e.g. \`claude plugin install ralph@omc\`).
|
|
55
|
+
If the CLI is unavailable, fall back to direct JSON/git manipulation (with
|
|
56
|
+
automatic timestamped backups of every file it touches). Report which engine
|
|
57
|
+
ran in the result.`;
|
|
57
58
|
|
|
58
59
|
/** Run `claude plugin ...` args; resolve {ok, stdout} or {ok:false, error}. */
|
|
59
60
|
async function claudePlugin(args, timeoutMs = 120_000) {
|
package/scripts/deploy-local.sh
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Deploy this checkout into the local DSH profile.
|
|
3
|
-
#
|
|
4
|
-
# Editing ~/src/dsh-claude-compat does NOT change what DSH runs: DSH imports the
|
|
5
|
-
# copy under the profile's node_modules, and only at process start. This script
|
|
6
|
-
# copies the source over that copy and reminds you to restart.
|
|
7
|
-
#
|
|
8
|
-
# bash scripts/deploy-local.sh # default profile: desktop
|
|
9
|
-
# bash scripts/deploy-local.sh --profile web
|
|
10
|
-
# bash scripts/deploy-local.sh --no-backup
|
|
11
|
-
#
|
|
12
|
-
# Environment: DSH_HOME (default ~/.dsh), DSH_PROFILE (default desktop).
|
|
13
|
-
set -euo pipefail
|
|
14
|
-
|
|
15
|
-
SRC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
16
|
-
DSH_HOME="${DSH_HOME:-$HOME/.dsh}"
|
|
17
|
-
PROFILE="${DSH_PROFILE:-desktop}"
|
|
18
|
-
BACKUP=1
|
|
19
|
-
|
|
20
|
-
usage() {
|
|
21
|
-
sed -n '2,12p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
while [[ $# -gt 0 ]]; do
|
|
25
|
-
case "$1" in
|
|
26
|
-
--profile) PROFILE="${2:?--profile 需要参数}"; shift 2 ;;
|
|
27
|
-
--no-backup) BACKUP=0; shift ;;
|
|
28
|
-
-h|--help) usage; exit 0 ;;
|
|
29
|
-
*) echo "未知参数: $1(用 --help 看用法)" >&2; exit 1 ;;
|
|
30
|
-
esac
|
|
31
|
-
done
|
|
32
|
-
|
|
33
|
-
TARGET="$DSH_HOME/profiles/$PROFILE/node_modules/dsh-claude-compat"
|
|
34
|
-
if [[ ! -d "$TARGET" ]]; then
|
|
35
|
-
cat >&2 <<EOF
|
|
36
|
-
找不到已安装的插件:
|
|
37
|
-
$TARGET
|
|
38
|
-
|
|
39
|
-
先把它装进 profile:
|
|
40
|
-
"/Applications/DeepSeek Harness.app/Contents/Resources/runtime/cli/bin/dsh" \\
|
|
41
|
-
plugin --profile $PROFILE add dsh-claude-compat@latest
|
|
42
|
-
EOF
|
|
43
|
-
exit 1
|
|
44
|
-
fi
|
|
45
|
-
|
|
46
|
-
# A deployed copy older than the checkout is the exact failure this script
|
|
47
|
-
# exists to prevent; say what is being overwritten so it is recoverable.
|
|
48
|
-
if [[ $BACKUP -eq 1 ]]; then
|
|
49
|
-
BK="$DSH_HOME/backups/dsh-claude-compat-$(date +%Y%m%d-%H%M%S)"
|
|
50
|
-
mkdir -p "$BK"
|
|
51
|
-
cp -a "$TARGET"/. "$BK"/
|
|
52
|
-
echo "备份 → $BK"
|
|
53
|
-
fi
|
|
54
|
-
|
|
55
|
-
# --delete keeps the deployed src an exact mirror (stale modules would otherwise
|
|
56
|
-
# linger and get imported).
|
|
57
|
-
rsync -a --delete "$SRC_DIR/src/" "$TARGET/src/"
|
|
58
|
-
for f in README.md README.zh-CN.md CHANGELOG.md CHANGELOG.zh-CN.md; do
|
|
59
|
-
[[ -f "$SRC_DIR/$f" ]] && cp -a "$SRC_DIR/$f" "$TARGET/"
|
|
60
|
-
done
|
|
61
|
-
|
|
62
|
-
if diff -r "$SRC_DIR/src" "$TARGET/src" >/dev/null; then
|
|
63
|
-
echo "✓ src 已同步 → $TARGET"
|
|
64
|
-
else
|
|
65
|
-
echo "✗ 复制后校验不一致,请检查权限" >&2
|
|
66
|
-
exit 1
|
|
67
|
-
fi
|
|
68
|
-
|
|
69
|
-
echo
|
|
70
|
-
echo "⚠ 插件在 DSH 启动时 import,改完必须重启 DSH 进程才生效(不是刷新窗口)。"
|
|
71
|
-
echo " 配置项(cordis.patch.yml)同理。"
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// gen-downloads-chart.mjs — fetch last 30 days of npm downloads and render a
|
|
2
|
-
// simple SVG bar chart to .github/assets/downloads.svg. Run by CI daily.
|
|
3
|
-
import { writeFileSync, mkdirSync } from 'node:fs';
|
|
4
|
-
import { dirname } from 'node:path';
|
|
5
|
-
|
|
6
|
-
const PKG = 'dsh-claude-compat';
|
|
7
|
-
const DAYS = 30;
|
|
8
|
-
const iso = (d) => d.toISOString().slice(0, 10);
|
|
9
|
-
const end = new Date();
|
|
10
|
-
const start = new Date(Date.now() - (DAYS - 1) * 86400000);
|
|
11
|
-
const res = await fetch(`https://api.npmjs.org/downloads/range/${iso(start)}:${iso(end)}/${PKG}`);
|
|
12
|
-
if (!res.ok) throw new Error(`npm API ${res.status}`);
|
|
13
|
-
const { downloads } = await res.json();
|
|
14
|
-
const days = downloads.map((d) => ({ date: d.day, n: d.downloads }));
|
|
15
|
-
const total = days.reduce((s, d) => s + d.n, 0);
|
|
16
|
-
const max = Math.max(1, ...days.map((d) => d.n));
|
|
17
|
-
|
|
18
|
-
const W = 760, H = 180, PAD = 30, CW = (W - PAD * 2) / days.length;
|
|
19
|
-
const bars = days.map((d, i) => {
|
|
20
|
-
const h = Math.round((d.n / max) * (H - 70));
|
|
21
|
-
const x = PAD + i * CW, y = H - 40 - h;
|
|
22
|
-
return `<rect x="${x.toFixed(1)}" y="${y}" width="${(CW - 2).toFixed(1)}" height="${h}" rx="2" fill="#22c55e"><title>${d.date}: ${d.n}</title></rect>`;
|
|
23
|
-
}).join('\n ');
|
|
24
|
-
|
|
25
|
-
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}" font-family="-apple-system,Segoe UI,Helvetica,Arial,sans-serif">
|
|
26
|
-
<text x="${PAD}" y="24" font-size="15" font-weight="600" fill="#1f2937">${PKG} — npm downloads (last ${DAYS} days)</text>
|
|
27
|
-
<text x="${W - PAD}" y="24" font-size="13" text-anchor="end" fill="#374151">total ${total.toLocaleString()} · max ${max.toLocaleString()}/day</text>
|
|
28
|
-
${bars}
|
|
29
|
-
<text x="${PAD}" y="${H - 15}" font-size="11" fill="#6b7280">${days[0].date}</text>
|
|
30
|
-
<text x="${W - PAD}" y="${H - 15}" font-size="11" text-anchor="end" fill="#6b7280">${days[days.length - 1].date}</text>
|
|
31
|
-
</svg>\n`;
|
|
32
|
-
|
|
33
|
-
const out = new URL('../.github/assets/downloads.svg', import.meta.url);
|
|
34
|
-
mkdirSync(dirname(out.pathname), { recursive: true });
|
|
35
|
-
writeFileSync(out.pathname, svg);
|
|
36
|
-
console.log(`chart written: total=${total} max=${max}`);
|