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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-claude-compat",
3
- "version": "0.9.0",
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: 'claude-compat',
263
+ provider: this.name,
274
264
  source: 'claude-compat-manager',
275
265
  rank: this.config.pluginManagerRank ?? 40,
276
- locator: { kind: 'inline', path: import.meta.url },
277
- get: async () => ({ name, description, body }),
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);
@@ -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
- Prefers the \`claude plugin\` CLI when available; falls back to direct
55
- JSON/git manipulation (with automatic timestamped backups of every file it
56
- touches). Report which engine ran in the result.`;
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) {
@@ -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}`);