Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 15 additions & 23 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,64 +1,56 @@
name: CI

# 每次 push 到主分支、以及任何 PR 包括来自 fork 的 PR 都自动跑完整检查。
# 本插件采用 TypeScript 源码与 tsc 构建,CI 会安装锁定依赖并重建发布产物。
# 每次 push 到主分支、任何 PR 包括来自 fork 的 PR 都自动跑完整检查。
# 本插件是 TypeScript 源码加 tsdown 构建,lib/ 是产物且不入库,因此 CI 检查 = 类型检查 + 构建 + 产物齐全校验,全绿才允许合并。
on:
push:
branches:
- master
- main
pull_request:
# fork PR 也可触发;此工作流不读写仓库 secrets,fork 环境下安全。
branches:
- master
- main

# 同一分支/PR 上排队的新提交会取消正在跑的旧任务,避免资源浪费。
# 同一分支或 PR 上排队的新提交会取消正在跑的旧任务,避免资源浪费。
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
check:
name: 类型检查 · 构建 · 产物校验
name: typecheck · build
runs-on: ubuntu-latest
strategy:
matrix:
# 本地与线上主推的运行时;运行时零依赖,两个版本都验证一次。
node:
- 22
- 26
steps:
- name: Checkout
uses: actions/checkout@v4

- name: 配置 pnpm
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
# 版本必须与 package.json 的 packageManager 完全一致,否则 action-setup 会报 Multiple versions of pnpm specified。
version: 9.15.9

- name: 配置 Node.js
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
# 与本地 node -v 锁死同一版本,见工作区 AGENTS.md 的 Node 版本约定。
node-version: 26.3.1
cache: pnpm

- name: 安装依赖
- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: 类型检查
- name: Typecheck
run: pnpm typecheck

- name: 构建
- name: Build
run: pnpm build

# 出厂文件齐全性校验,缺失会在此失败,让 PR 作者一眼看到问题。
- name: 产物齐全性校验
# 本插件是双半身且产出 dts,产物缺失会在此失败。
- name: Verify build outputs
run: |
test -f lib/index.js
test -f lib/index.d.ts
test -f lib/client.js
test -f lib/client.js.map
test -f cordis.patch.yml
test -f package.json
echo "发布产物齐全"
echo "构建产物齐全:lib/index.js · lib/index.d.ts · lib/client.js · cordis.patch.yml"
68 changes: 40 additions & 28 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
name: Release

# 推送形如 vX.Y.Z,如 v0.1.0,的 tag 时自动发版:
# 凭 tag 号更新 package.json 版本并提交回 main → 安装依赖 → 类型检查 → 构建 → 产物校验
# → 发布 npm → 生成 GitHub Release draft。人只需打一个 tag,其余自动衔接。
# 注意:npm 发布依赖 npmjs 侧开启的 Trusted Publishing
# npmjs.com → 本包 → Enable trusted publishing → 绑本仓库 GitHub Actions,
# 无需配置 NPM_TOKEN;未开启时发布步骤会失败。
# 推送形如 vX.Y.Z 如 v0.1.0 的 tag 时自动发版:凭 tag 号更新 package.json 版本并提交回 main → 类型检查 → 构建 → 发布 npm → 生成 GitHub Release draft。
# 人只需打一个 tag,其余自动衔接。
# 注意:npm 发布走 Trusted Publishing,无需 NPM_TOKEN;未在 npm 侧绑定信任关系时发布会失败。
on:
push:
tags:
Expand All @@ -26,8 +23,7 @@ jobs:
id-token: write
# fork 永不触发 tag 发布,仅作为兜底防护。
if: startsWith(github.ref, 'refs/tags/v')
# 绑定 GitHub Actions environment「release」,使 OIDC subject 稳定为
# environment:release,npm 侧 Trusted Publisher 需填相同 Environment 名。
# 绑定 GitHub Actions environment「release」,使 OIDC subject 稳定为 environment:release,npm 侧 Trusted Publisher 需填相同 Environment 名。
environment: release
steps:
- name: Checkout
Expand All @@ -38,8 +34,26 @@ jobs:
# 默认 GITHUB_TOKEN 在分支保护下会被拒,此处显式传 token。
token: ${{ secrets.GITHUB_TOKEN }}

# 前置守卫:仅当该 tag 版本尚未发布到 npm 时才继续发版。
# 补历史 tag 时对应版本早已在 npm 上,重复 publish 会报错,且版本回写会把 main 改回旧号,故已发布则跳过后续所有步骤。
# npm view 只读 registry,无需凭证。
- name: Skip when tag version already published
id: published
run: |
set -euo pipefail
PKG="dsh-edit-diff"
TAG="${GITHUB_REF_NAME#v}"
if npm view "$PKG@$TAG" version >/dev/null 2>&1; then
echo "已存在 $PKG@$TAG,跳过发布"
echo "published=true" >> "$GITHUB_OUTPUT"
else
echo "未发布 $PKG@$TAG,继续"
echo "published=false" >> "$GITHUB_OUTPUT"
fi

# 版本号仅凭 tag 号决定,写入 package.json 并提交回 main。
- name: Sync version to main
if: steps.published.outputs.published != 'true'
run: |
set -euo pipefail
TAG="${GITHUB_REF_NAME#v}" # 去 v 前缀得版本号,如 0.1.0
Expand All @@ -65,44 +79,42 @@ jobs:
git push origin main
}

- name: 配置 pnpm
- name: Setup pnpm
if: steps.published.outputs.published != 'true'
uses: pnpm/action-setup@v4
with:
version: 9.15.9

- name: 配置 Node.js
# setup-node 的 registry-url 告知 npm 向哪个 registry 换 Trusted Publishing 临时凭证。
# node-version 与本地 node -v 锁死同一版本。它同时满足 npm 官方 Trusted Publishing 的下限:捆绑 npm 需大于等于 11.5.1,否则 npm CLI 无法做 OIDC 自动换权,会回退用失效 token 导致 PUT 404 is not in registry。
- name: Setup Node
if: steps.published.outputs.published != 'true'
uses: actions/setup-node@v4
with:
node-version: 24
node-version: 26.3.1
cache: pnpm
registry-url: https://registry.npmjs.org

- name: 安装依赖
- name: Install dependencies
if: steps.published.outputs.published != 'true'
run: pnpm install --frozen-lockfile

- name: 类型检查
# 本插件暂无单测,以类型检查作为发布前的最后一道门。
- name: Typecheck
if: steps.published.outputs.published != 'true'
run: pnpm typecheck

- name: 构建
- name: Build
if: steps.published.outputs.published != 'true'
run: pnpm build

- name: 产物齐全性校验
run: |
test -f lib/index.js
test -f lib/index.d.ts
test -f lib/client.js
test -f lib/client.js.map
test -f cordis.patch.yml
test -f package.json
echo "发布产物齐全"

# Trusted Publishing:npm 凭 workflow 的 OIDC 身份直接授权,
# 无需 NPM_TOKEN;--provenance 会在 npm 上附构建来源证明。
# node-version 24 保证捆绑 npm>=11.5.1,npm CLI 才能做 OIDC 自动换权发布。
# Trusted Publishing:npm 凭 workflow 的 OIDC 身份直接授权,无需 NPM_TOKEN;--provenance 会在 npm 上附构建来源证明。
- name: Publish to npm
run: pnpm publish --no-git-checks --provenance --access public
if: steps.published.outputs.published != 'true'
run: npm publish --provenance --access public

- name: Create GitHub Release draft
if: steps.published.outputs.published != 'true'
uses: softprops/action-gh-release@v2
with:
draft: true
Expand Down
49 changes: 45 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

接管 DSH 浏览器的 `edit` / `write` 工具卡片,用近线性行级 diff 做真正的差异展示,消除内置 DiffBlock 把「未变化的相同行」在删除区红 `-` 行和新增区绿 `+` 行各渲染一遍的重复。

纯浏览器端 client 插件,单击即换新渲染,不碰 DSH 源码。
渲染全在浏览器端,主机端半身只负责把配置转成 settings 命名空间。单击即换新渲染,不碰 DSH 源码。

## 引言

Expand All @@ -23,6 +23,47 @@
- **复制与统计**:复制只含差异行,`- `/`+ ` 前缀;底部 `└ +A -R · N file(s)` 变更统计。
- **单文件与多文件**:`edit` / `write` 双双接管,覆盖新建只显示绿色新增,纯删、多 hunk 等场景。
- **PTC 模式兼容**:`run_code` 子调用中的 `edit` / `write` 同样显示优化后的修改 diff,通过 `callId` 中的 `:code:` 标记识别子调用。注意 `write` 是全文件覆写,无旧内容,只能显示全新增;想看精确 diff 应使用 `edit`。
- **接管其他工具**:`extraTools` 让 [dsh-remote-file-system](https://github.com/better-er/dsh-remote-file-system) 的 `edit_remote`、`write_remote` 一并使用优化显示,插件已默认开启这两个,参数名不同的工具可逐项覆盖。

## 配置

`extraTools` 列出 `edit` 与 `write` 之外要接管的工具。每条写工具名和读取参数用的参数名,参数名省略时按 `file_path`、`old_string`、`new_string`、`content` 取值。

插件自带的 `cordis.patch.yml` 已经默认接管 [dsh-remote-file-system](https://github.com/better-er/dsh-remote-file-system) 的这两个远程工具:

```yaml
- insert:
- id: dsh-edit-diff
name: dsh-edit-diff
config:
extraTools:
- name: edit_remote
- name: write_remote
```

它们的参数名和原生一致,只写名字即可。参数名不同的工具再补:

```yaml
extraTools:
- name: some_tool
pathKey: target
oldKey: before
newKey: after
contentKey: body
```

要改这份默认,在 profile 的 `cordis.patch.yml` 里按 id 覆盖:

```yaml
- id: dsh-edit-diff
config:
extraTools:
- name: some_tool
```

判断规则:参数里同时有旧文本与新文本就按 edit 型渲染,否则有整文件内容就按 write 型渲染。

配置经 settings 命名空间 `dsh-edit-diff` 送到浏览器端,settings 变化会让浏览器端重建这批 key 的注册。

## 它长什么样

Expand Down Expand Up @@ -67,12 +108,12 @@ dsh plugin --profile web remove dsh-edit-diff

- 是**标准形态的 dsh client 插件**,声明 `dsh.client`,导出 `./client`。
- 同时声明了 `dsh.bundle`,因此也是一个**自挂载的 bundle 层插件**:用 `dsh plugin --profile <name> add` 从 GitHub 安装后,会被自动识别为 profile layer 并挂载,无需手工写组合 entry。
- 纯浏览器半身,无 host 行为;`lib/index.js` 是无操作的 no-op 主机插件标准双面包约定。
- **UI 挂载点**:接管 keyed 槽位 `tool.call.toolview` 的 `edit` 与 `write` 两个 key。
- 主机端半身只做一件事:用 `ctx.settings.installSection()` 把 entry 配置注册成 settings 命名空间 `dsh-edit-diff`。浏览器端拿不到 cordis 配置,这是双半插件传配置的唯一通道。
- **UI 挂载点**:接管 keyed 槽位 `tool.call.toolview` 的 `edit` 与 `write` 两个 key,以及 `extraTools` 里声明的每个工具名。
- **遮蔽而非冲突**:注册时显式传 `priority: -1`低于内置 `file-mutation-toolview` 的默认 0,用更低优先级遮蔽默认渲染,而不是在同一优先级上 clash。
- **PTC 模式**:通过 `callId` 包含 `:code:` 判断是否为 `run_code` 子调用,从 `argsRaw` 中提取 `old_string`/`new_string` 即 edit 或 `content` 即 write 动态构建 diff。
- **构建型**:TypeScript 源码位于 `src/`,`pnpm build` 通过 tsdown 生成 `lib/index.js`、`lib/index.d.ts`、`lib/client.js` 与 sourcemap,运行时只加载 `lib/` 发布产物。
- 无运行时 npm 依赖,diff 算法就地内联;浏览器端只向宿主模块表请求 react。
- diff 算法就地内联,浏览器端只向宿主模块表请求 react;主机端向宿主解析 `@deepseek-ai/schemastery`。

本地开发:

Expand Down
21 changes: 10 additions & 11 deletions cordis.patch.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
# dsh-edit-diff bundle patch: mount the edit/write diff-card plugin.
#
# Applied when this bundle appears in a profile's dsh.profile.bundles list.
# The node half (package root) is a no-op Cordis plugin; the browser half
# (dsh.client) is discovered automatically through the package's
# dsh.client declaration + exports["./client"]. It registers two keyed
# `tool.call.toolview` occupants (`edit`, `write`) that replace the shipped
# DiffBlock render, eliminating same-line duplication with a near-linear
# line diff + intra-line character highlight.

# dsh-edit-diff bundle patch:把 edit/write 的 diff 卡片插件插进 profile 的 layer stack。
# profile 引用本 bundle 时自动挂载。
# 浏览器半身由 dsh.client 声明自动发现,注册 tool.call.toolview 的 edit、write 与 extraTools 里的每个工具名,替换内置 DiffBlock。
# 主机端半身把 entry 配置注册成 settings 命名空间 dsh-edit-diff,浏览器端只能从这里拿到配置。
# 下面的默认顺带接管两个远程文件工具,参数名与原生一致,按 id 覆盖 extraTools 即可改。
- insert:
- id: dsh-edit-diff
name: dsh-edit-diff
name: dsh-edit-diff
config:
extraTools:
- name: edit_remote
- name: write_remote
17 changes: 14 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "dsh-edit-diff",
"version": "0.3.0",
"description": "质量 diff:接管 edit/write 工具卡片,行级去重与行内高亮,消除相同内容在删除与新增两区的重复;兼容 PTC 模式,标准可安装的 dsh 客户端插件",
"version": "0.4.0",
"description": "质量 diff:接管 edit/write 及 extraTools 配置的其他工具卡片,行级去重与行内高亮,消除相同内容在删除与新增两区的重复;兼容 PTC 模式,标准可安装的 dsh 客户端插件",
"keywords": [
"deepseek-harness",
"dsh",
Expand Down Expand Up @@ -53,10 +53,21 @@
"prepare": "tsdown",
"typecheck": "tsc --noEmit"
},
"peerDependencies": {
"@deepseek-ai/cordis": "^4.0.2",
"@deepseek-ai/dsh-settings": "^0.1.5-rc.2",
"@deepseek-ai/schemastery": "^3.18.2"
},
"devDependencies": {
"@deepseek-ai/cordis": "^4.0.2",
"@deepseek-ai/dsh-settings": "^0.1.5-rc.2",
"@deepseek-ai/schemastery": "^3.18.2",
"@types/react": "~18.3.1",
"tsdown": "^0.23.0",
"typescript": "^5.9.3"
},
"license": "MIT"
"license": "MIT",
"engines": {
"node": ">=26.3.1"
}
}
Loading
Loading