Skip to content

Add DSH Plugin Support - #4

Merged
Tespera merged 7 commits into
Gridea-Pro:mainfrom
xiaxi626:dsh
Aug 21, 2026
Merged

Add DSH Plugin Support#4
Tespera merged 7 commits into
Gridea-Pro:mainfrom
xiaxi626:dsh

Conversation

@xiaxi626

Copy link
Copy Markdown
Contributor

变更说明

将 theme-builder-skill 作为 DeepSeek Harness 插件运行,无需改动原有 Skill 内容,通过薄包装层注册 skill provider。

新增文件

文件 作用
package.json 插件清单,声明 dsh.bundle、peer 依赖、prepare 构建脚本
cordis.patch.yml bundle 模式的 patch 层,dsh plugin add 时引用
tsconfig.json TypeScript 编译配置(ESM + NodeNext + node 类型)
overlay.yml 本地开发 overlay 模板,--patch 模式用
src/index.ts 插件入口,注册 gridea-theme-builder skill provider,设置 resourceBase 指向 bundle 目录
src/README.md DSH 插件使用文档(安装、测试、卸载、常见问题)

实现原理

src/index.ts 基于 DSH 官方 skill-badge 插件模式:

  • ctx.skills.registerProvider() 注册静态 skill provider
  • resourceBase 设为 bundle 根目录,模型通过 <skill_resources> 获取路径后可解析 scripts/references/assets/ 的相对路径
  • get() 运行时读取 SKILL.md,stripFrontmatter() 去掉 YAML 头部
  • 不包装任何工具,模型通过 DSH 内置的 shell/read 工具调用全部脚本和参考文件

两种使用方式

  1. 本地测试--patch):开发调试用,tsx 直接跑 .ts,改代码即生效
  2. GitHub 安装plugin add):分发用,pnpm 自动拉取编译,需处理 allowBuilds 构建授权

不改动的部分

SKILL.mdreferences/scripts/assets/requirements.txt、主 README.md 全部不变,原有 Claude Skill 用法不受影响。

已知限制

  • GitHub 安装需手动处理 pnpm allowBuilds 授权(pnpm ≥10 安全策略)
  • allowBuilds key 含 commit SHA,每次推送后需更新
  • 发布到 npm 可解决上述问题

- rewrite GitHub install section: warn users about 3-step pnpm flow upfront
- show actual error output so users know what to expect
- clarify pnpm-workspace.yaml already has base content, append only
- add cleanup guidance: failed install leaves no residue, remove is not needed
- fix update section: plugin update fails on SHA change, use add flow instead
- add Windows file:// URL and @types/node to troubleshooting
- add overlay.yml as separate file from cordis.patch.yml
- update install scripts to two-phase: capture pnpm error, extract allowBuilds key, retry
- delete install-dsh.sh and install-dsh.bat
- remove install script section from README
- add single-quote requirement for allowBuilds key in pnpm-workspace.yaml
- add warning not to copy pnpm error comment lines into yaml
@Tespera

Tespera commented Aug 20, 2026

Copy link
Copy Markdown
Member

Review 意见

结论:实现质量很高,我实际跑通了,改掉下面两点就可以合。 感谢这个贡献 —— 尤其是中途自己发现安装脚本没用又主动删掉,这种事很少见。

我实际跑过了

不是只看代码,是把你的分支拉下来真跑了一遍:

  • 能编译,零报错。
  • 你提交的编译产物和源码对得上。 我自己重新编译了一遍,产出和你提交的一模一样,没有夹带旧代码。
  • 插件真的能加载。 我模拟了一遍 DSH 启动时的过程,skill 注册成功,SKILL.md 的正文被完整读出来,开头那段 YAML 配置头也按预期去掉了。
  • Skill 要用的资料都找得到。 参考文档、Python 脚本、模板素材这三个目录的路径都指对了,模型能正常访问。
  • 写法是照着官方来的。 我下载了 DeepSeek 官方的示例插件对比,你的结构跟它一致。你额外加的那段「去掉 YAML 头」的处理也是对的 —— 官方那个示例的文档没有这个头,我们的有。
  • 代码里写死的那段 skill 描述,和 SKILL.md 里的原文目前一字不差。
  • 最关键的:这个 PR 只加不删。 Skill 本身的内容一个字没动,原来在 Claude 里的用法完全不受影响。

需要修改

1. .gitignore 要补 node_modules/

这个 PR 带进来一整套 npm 工具链,但 .gitignore 目前只管 Python、编辑器和 skill 运行产物。我实测跑完 npm installgit status 里立刻多出一个 node_modules/ 等着被误提交。

2. 编译产物会悄悄和源码脱节

现在 lib/src/ 是同步的(我验证过),但 CI 只跑 Python 那套冒烟测试,没有任何一步会检查 TypeScript。等哪天有人改了源码忘了重新编译,从 GitHub 装插件的用户就会拿到旧代码,而且不会有任何提示,直到有人来报奇怪的 bug。

顺带说个能让你在「已知限制」里列的那堆 pnpm 麻烦直接消失的做法:pnpm 之所以要你做 allowBuilds 授权,就是因为 package.json 里有 prepare 脚本。既然编译产物已经提交了,把 prepare 去掉(build 保留),GitHub 安装就不用授权了,也不用再跟着 commit SHA 更新那个 key。

代价是编译不再自动发生,所以要加一道 CI 兜底:

- name: Check lib/ is in sync with src/
  run: |
    npm ci
    npx tsc -p tsconfig.json
    git diff --exit-code lib/

这两步建议一起做。如果你更想保留 prepare,那至少把这道检查加上。

建议改进(不阻塞)

  • skill 描述现在要维护两份。你自己在注释里也写了 If the frontmatter description changes, update this constant。既然运行时已经在读 SKILL.md 了,顺手把 frontmatter 里的描述也解析出来,就能只留一份。
  • 主 README 完全没提 DSH。用法只写在 src/README.md 里,从仓库首页看不到这个能力,等于白做。建议主 README 加一节或一句指路。
  • CHANGELOG.md 加一条,本仓库遵循 Keep a Changelog。
  • 包管理器混用了:提交的是 npm 的 lock 文件,文档里讲的却是 pnpm 流程。建议统一,或说明为什么混用。
  • @gridea-pro 这个 npm scope 目前不存在(我查了 registry,404)。如果暂时不打算发 npm,publishConfig 可以先去掉。

两件不用你操心的事

协议:你在 package.json 里写的 "license": "MIT" 原本和仓库的 GPL-3.0 对不上。不过我们决定把这个仓库整体改成 MIT 了(#5)—— 它是主题开发的配套工具和模板,用 copyleft 会让大家用脚手架生成的主题也被迫开源,不合适。所以你这行不用改,它马上就是对的

CI 红灯:这三个红叉和你无关,main 分支从 7 月 9 号起就一直是红的,是脚手架生成的配置通不过自己的校验器。修复见 #6,合了之后你 rebase 一下就绿。别为这个动任何代码。

xiaxi626 and others added 3 commits August 20, 2026 23:13
- add node_modules/ to .gitignore
- remove prepare script from package.json (eliminates pnpm allowBuilds hassle)
- add check-lib-sync CI job to catch stale lib/ output
- auto-extract description from SKILL.md frontmatter in get()
- add DSH section to main README
- add CHANGELOG entry
- remove unused publishConfig
- rebuild lib/ to match src/
上一版把完整描述挪进 get(),list() 只留占位符。但 list() 返回的 candidate
才是模型路由用的目录(dsh-skill 的 snapshot() 走 toSummary(entry.candidate)),
get() 要等模型选中之后才调用 —— 结果全部触发关键词都不再进入路由。

同时 parseFrontmatter 的块标量分支带了 (?=\n\w|\n---) 前瞻,要求 description
后面还有内容;而本 skill 的 description 正是最后一个字段,闭合 --- 又已被外层
正则消费,该分支永不匹配,回落到 (.+) 只抓到 ">" 一个字符。

- list() 改为 async 并读取 frontmatter,描述与 get() 同源
- 块标量分支改为按缩进行收集,支持 > 和 |,不依赖后续字段
- list() 读不到 SKILL.md 时降级为兜底描述,避免整个 skill 目录塌掉;
  get() 仍然抛错,因为加载一个没有正文的 skill 应当失败
- 重新编译 lib/

验证:list() 与 get() 返回的描述均与 SKILL.md 原文逐字一致(225 字符)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Tespera

Tespera commented Aug 21, 2026

Copy link
Copy Markdown
Member

你这一轮改得很到位 —— 我列的两条阻塞项加四条不阻塞的建议全办了,check-lib-sync 这个 job 我本地照着跑了一遍(npm citscgit diff --exit-code lib/),通过。

不过「skill 描述只维护一份」那条改岔了,而且这个锅在我:我当时说「顺手把 frontmatter 里的描述也解析出来」,没说清它必须留在 list() 里。我已经把修好的版本直接推到你分支上了(51aee69),下面说明为什么,你过目一下有没有异议。

问题 1:描述不能挪进 get()

list() 返回的 candidate 才是模型路由用的目录,get() 要等模型已经选中这个 skill 之后才会被调用。所以描述挪进 get() 之后,全部触发关键词都不再进入路由 —— 这个 skill 基本不会被触发到。

@deepseek-ai/dsh-skill 里的调用链能直接印证:

// lib/index.js:237  构建模型看到的目录
skills: [...collected.entries.values()].map((entry) => toSummary(entry.candidate))
// lib/index.js:491
function toSummary(skill) { const { name, description, ... } = skill; ... }

目录取的是 entry.candidate,也就是 list() 的返回值。

问题 2:块标量解析回落到了 > 这个字符

实测你那版的输出:

list() 的 description = "Gridea Pro 博客主题开发专家"   (19 字,占位符)
get()  的 description = ">"                          (1 字)

原因在这个前瞻:

/^description:\s*(?:>\s*\n([\s\S]*?)(?=\n\w|\n---)|(.+))$/m
                                     ^^^^^^^^^^^^^^

它要求 description 后面还得有下一个字段或闭合分隔符。但本 skill 的 description 正好是 frontmatter 的最后一个字段,而闭合的 --- 又已经被外层正则消费掉了 —— 这个分支永远匹配不上,于是回落到 (.+),只抓到了 > 这个块标量标记本身。

做了个对照实验,根因可以精确定位:

description 是最后一个字段(我们的真实情况)  → ">"
description 后面还有别的字段                → "真正的描述内容。"

改了什么

  • list() 改成 async 并读取 frontmatter,描述与 get() 同源,仍然只维护一份 —— 你原来的目标达成了,只是位置换回 list()
  • 块标量分支改为「收集紧随其后的所有缩进行」,支持 >|,不再依赖后面有没有字段
  • list() 读不到 SKILL.md 时降级为兜底描述,避免一个坏 bundle 把整个 skill 目录搞塌;get() 保持抛错,因为加载一个没有正文的 skill 本来就该失败
  • 重新编译了 lib/

验证

list() 与 SKILL.md 原文逐字一致 = true   (225 字符)
get()  与 SKILL.md 原文逐字一致 = true
list() 与 get() 一致            = true
触发关键词进入目录              = true
正文 frontmatter 已剥离         = true

解析器的 7 个用例也都过了:块标量在末尾 / 后面有字段 / 多行 / 单行 / | 字面量 / 无 frontmatter / 无 description。降级路径单独试过:把 SKILL.md 改名后 list() 正常返回兜底描述不抛异常,get() 如预期抛 ENOENT。


接下来我会批准跑一次 CI(你是首次贡献者,workflow 需要维护者手动放行,所以你前面几个 commit 的 CI 其实一次都没真正执行过)。跑绿就合。再次感谢,这个插件层做得很干净。

@Tespera
Tespera merged commit facedb8 into Gridea-Pro:main Aug 21, 2026
4 checks passed
xiaxi626 pushed a commit to xiaxi626/theme-builder-skill that referenced this pull request Aug 21, 2026
新用户此前卡在第一步——原文只说「把仓库目录交给你的 AI Agent」,
没有一条可复制的命令;PR Gridea-Pro#4 之后根目录多了一层 TS 构建产物,
只用 Skill 的人也无从判断哪些文件可以忽略。

- 「怎么用」第 1 步给出 git clone 命令,并说明无需编译和预装依赖
- 补充生成的主题往哪放(站点目录 themes/,附 customConfig 需重启才生效的提醒)
- 目录结构标注 Skill 本体 vs DSH 插件封装,后者明确标为可忽略
- DSH 一句话扩成一节,讲清谁需要、谁可以跳过
- 开发环境说明 jinja2 只有 render_test 需要,且会自动安装
- SKILL.md 描述去掉会过时的「5 步 + 16 条」(实为 6 步,规则按引擎分组)
- 散文标点对齐仓库全角惯例(prompt 示例代码块内保持原样)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants