DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

Ian Rules — DSH Plugin for DeepSeek Harness
← Plugins
I

dsh-ian-rules

Ian Rules

Project Development Rules: DSH is an open-source plugin (MIT), available for everyone to use, learn from, and research. It provides a “Development Rules” panel in the right sidebar of the Web GUI (alongside “Docker Containers”), with global rules and per-project overrides. The rules currently in eff

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add github:Grant-Felix/dsh-ian-rules#a7310ffd32fa282ef65c56463fbbb4eda51e23cc
READMECompatibilityVersions

Description

Project Development Rules: DSH is an open-source plugin (MIT), available for everyone to use, learn from, and research. It provides a “Development Rules” panel in the right sidebar of the Web GUI (alongside “Docker Containers”), with global rules and per-project overrides. The rules currently in effect for each project are automatically injected into every session’s system prompt, so agents follow them when developing the project. It also includes the ian_rules tool for managing rules directly in conversations.

Compatibility and provenance

Ian Rules is published as dsh-ian-rules and currently resolves to version 26.10.6-6. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
web
Release source
github
Registry updated
10/6/2026

Versions

26.10.6-6prerelease
10/6/2026
Show 4 more versionsCollapse versions
26.10.6-5prerelease
10/6/2026
26.10.6-2prerelease
10/6/2026
26.10.6-1prerelease
10/5/2026
26.10.5-1prerelease
10/5/2026

Related plugins

Loading related plugins…

Latest
26.10.6-6
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
Unavailable
Files
Unavailable
Surface
web
License
MIT
Source
github
Weekly downloads
0
View source ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in

Related plugins

More verified plugins in developer-tools.

Web App@deepseek-ai/dsh-web-appThe dsh browser-surface bundle: the web patch layer over dsh-base plus the runtime glue plugin (frontend dist serving, web-surface prompt, bash runtime variables, URL line)Sdk Minimal@deepseek-ai/dsh-sdk-minimalThe standalone minimal SDK profile bundle: JSON-RPC, one DeepSeek adapter, persistent shell, and JSONL sessionsSdk App@deepseek-ai/dsh-sdk-appThe dsh SDK profile bundle: stdio JSON-RPC serving and process lifecycle over dsh-baseExperimental Inspector@deepseek-ai/dsh-experimental-inspectorExperimental cross-realm CDP hub for Host debugging and Client Runtime inspection

README

dsh-ian-rules · 项目开发规则

DSH(DeepSeek Harness)开源插件(MIT):把你自己的一套项目开发规则交给 DSH,让 agent 在开发项目时自动遵守。代码可自由使用、修改与分发,也欢迎读源码学习研究。

  • 面板:Web GUI 右侧栏新增「开发规则」页(与「Docker 容器」并列,走 DSH 原生 sidebarRightTabs + sidebar.right.pane.tab 契约),可视化维护规则(全局 + 按项目)。
  • 自动注入:每个会话组装系统提示时,按该会话的工作目录解析生效规则并注入,无需手工提醒。
  • 对话内维护:另带 ian_rules 模型工具,直接说「把这条记进开发规则」也能落库。
  • 即时生效:保存后正在运行的会话下一步就生效,不用重启 DSH。

一、功能

能力说明
全局规则对所有会话生效,是「凡项目都适用」的底线约定
项目规则每个项目在页签栏里有自己的一页;会话工作目录落在项目路径之下即命中;多个命中时取路径最长(最具体)的一个;工作目录走软链时自动用 realpath 再匹配一次
页签3 + N:全局规则 / 项目(名册)/ 每个项目一页 / 效果预览;页签条横向滚动不折行
追加 / 覆盖项目可「追加」(全局 + 本项目)或「覆盖」(只生效本项目规则);覆盖模式下注入文本会写明被挡掉了多少条全局规则
分组每条规则可填分组名;面板按分组归拢成二级分块,注入文本里是二级标题 ### 组(第一级是 ## 全局规则 / ## 项目:名(路径));组与组之间的先后取组名第一次出现的位置
搜索按标题 / 正文 / 分组过滤当前页签(筛选时禁用上移下移,避免顺序错乱)
卡片版式规则卡两列式网格:栏宽够(内容盒 ≥ 520px)才两列,窄栏单列;规则卡 / 项目卡 / 空态 / 上手块共用同一套形状变量
单条开关每条规则可单独停用(保留内容、不注入);总开关可整体关闭注入
删除保护删除规则 / 项目条目需要二次确认(3 秒内不确认自动还原)
保存冲突检测/save 带 revision;磁盘被别的会话或手工编辑改过时返回 409,面板提示「载入磁盘版本」或「用我的改动覆盖」,不会静默覆盖。revision 只在内容真的变了时前进 —— 插件自己写盘触发的目录事件不会把 revision 顶高一格(否则「保存成功后再保存一次」会假报冲突,真冲突的提示也就没人信了)
外部改动同步面板在后台按 revision 轮询:没有未保存改动就静默跟上,有改动就明确提示
备份每次保存前把上一版写到 ian-rules.json.bak
导入 / 导出导出 JSON / Markdown;导入 Markdown / JSON(可替换或合并,按 id 与标题+正文去重)
成本提示注入预览显示字符数与估算 token,并列出最占预算的 5 条规则;每条规则卡片还标出它给每轮对话增加的字符数
注入预览输入任意目录,看该目录下真正注入的文本(含未保存修改、命中项目、是否被截断)
上限保护单次注入文本上限 12000 字符,超出时先裁全局、保住项目规则,并在提示开头写明少了哪几条(不再静默丢失)
按场景注入标了「按场景」的规则不常驻系统提示,只在命中当前任务时送到对话末尾(默认关闭,在「更多 → 按场景注入」里开)
ian_rules 工具action = list / lookup / add / update / remove,支持 global / project 归属与分组;list 默认只给有界摘要,完整注入原文要显式 full: true、列举全部项目要 all: true;lookup 按关键词取某几条的正文(工具结果会永久留在对话历史里,所以默认不能倒全文)
插件更新面板顶部有更新时才出现一条细提示,点「更新」即可更新本插件(走插件市场公布的同源更新 API,含进度、失败原因与回滚);没装插件市场时整块隐藏
接口硬化所有接口校验 Origin / Sec-Fetch-Site(跨站 403),POST 要求 Content-Type: application/json(否则 415)

没有生效规则时注入空串 —— 等于这个插件「隐身」。

二、安装

本插件是标准的 DSH profile bundle(双半体:宿主 + 浏览器),需要已安装 dsh CLI(npm install -g @deepseek-ai/dsh)与 pnpm。按你的网络从下面两条来源里选一条:

# 能直连 GitHub
dsh plugin --profile web add github:Grant-Felix/dsh-ian-rules

# 国内走 Gitee 镜像(pnpm 没有 gitee: 简写,用完整地址)
dsh plugin --profile web add git+https://gitee.com/Grant-Felix/dev-rules.git

# 仓库开发调试:链到本地检出
dsh plugin --profile web add link:$PWD

dsh plugin 就是 pnpm 的透传:装完它会按包名把插件自动登记进 dsh.profile.bundles,不必手工改 profile 的 package.json。装完重启该 profile(侧边栏底部 ↻),右侧栏页面列表里就会出现「开发规则」。

为什么还没有 npm 包可以 add:dsh-ian-rules 这个包名在 npm 上没有被占用(2026-10-01 实测 registry.npmjs.org/dsh-ian-rules → 404),但它还没发上去 —— 首发要走一次性凭据,且 trusted publisher 只能配在「已存在的包」上(前置条件记在 .github/workflows/publish.yml 顶部)。所以对外仍以 git 来源分发:GitHub 给直连用户,Gitee 给国内用户。

改动生效范围(本部署实测):

改了什么怎么生效
宿主半体 lib/index.js、lib/rules.js重启 profile(侧边栏底部 ↻)
浏览器半体 lib/client.js同样是重启 profile

为什么客户端改动也要重启:DSH 的 client-modules 在启动时把每个插件的 client bundle 读进内存(readFileSync + 内容哈希当 rev),之后只按「已登记的 URL」出字节;文件内容变化要经 HMR watcher 的 rebuilt(id) 才会重新登记,而 watcher 只有在源码检出里跑着 pnpm run dev:web 时才装得上。本机是安装版部署、没有跑 dev watcher,所以硬刷新页面拿不到新 bundle,必须重启一次 profile。

待复核(2026-10-01):这条结论与一次沙箱实测不符 —— 在沙箱(link: 安装、独立 DSH_HOME)里,改完 lib/client.js 只做一次页面加载(不重启实例)就能拿到新 bundle:往 CSS 里加一行注释,刷新页面后 style[data-plugin="ian-rules"] 里就有了它。差别可能来自安装形态(link vs 安装版)或页面加载与硬刷新的区别,尚未在本机日常 profile 上复现,所以上表暂时按老结论执行(拿不准就重启,代价只是几秒)。

升级:面板顶部在有新版本时会自动出现一条提示,点「更新」即可(走插件市场公布的同源更新 API;没装插件市场时这条提示不出现)。也可在插件市场的「更新」里做(它按 profile 的 lockfile 锁定的提交与远端 HEAD 比对,并自带 git 更新的回滚),或命令行 dsh plugin --profile web update dsh-ian-rules(重新解析到分支最新提交)。卸载:dsh plugin --profile web remove dsh-ian-rules,重启。规则文件会留在 $DSH_HOME/ian-rules.json。

三、数据

规则存在 $DSH_HOME/ian-rules.json(默认 ~/.dsh/ian-rules.json),原子写入(临时文件 + rename);保存前上一版留在 ian-rules.json.bak。

更名迁移(dsh-dev-rules → dsh-agent-rules → dsh-ian-rules):启动时若发现新名字的文件不存在、而旧名字的还在,会按由新到旧的顺序取第一个存在的(agent-rules.json 优先于 dev-rules.json)复制一份到 ian-rules.json(原文件保留,回退到旧版本插件仍可读),面板的「更多 → 文件位置」会提示这一次迁移。确认新文件正常后,旧的那份可以自行删除。

包名也跟着改了,所以升级时依赖键也要换:dsh plugin --profile web remove dsh-agent-rules 再 dsh plugin --profile web add github:Grant-Felix/dsh-ian-rules(否则 profile 里会同时留着两代,bundle 阵容里还挂着旧包名)。旧包名的数据文件按上面那条规则自动搬过来,规则不会丢。

这份文件属于使用者本人,不在本仓、不受本仓许可约束(见 NOTICE.md)。插件只读写本机这一份文件,不联网、不上传。

{
  "version": 2,
  "enabled": true,
  "sceneMatching": "off",
  "global": [
    { "id": "g1", "title": "提交前跑测试", "content": "npm test 必须绿", "group": "提交", "enabled": true, "mode": "always", "tags": [] }
  ],
  "projects": [
    {
      "id": "p1",
      "path": "~/项目/foo",
      "label": "示例项目",
      "enabled": true,
      "mode": "append",
      "rules": [
        { "id": "r1", "title": "本项目用 pnpm", "content": "禁止 npm install", "group": "依赖", "enabled": true, "mode": "auto", "tags": ["依赖", "包管理"] }
      ]
    }
  ]
}
  • mode:项目级是 append(默认)或 override;规则级是 always(默认,常驻注入)或 auto(按场景注入,见第五节)。
  • tags:规则的匹配标签,最多 8 个、每个 24 字符;只在 auto 模式下参与匹配,但任何时候都可以填。
  • sceneMatching:off(默认)或 auto —— 按场景注入的总开关。
  • 老文档(version 1)读进来会自动补齐:每条规则补 mode: "always"、tags: [],文档补 sceneMatching: "off"。补齐之后注入文本与旧版逐字节相同(这条由 test/rules.test.mjs 的黄金快照钉住),所以升级不会让任何用户的提示词缓存失效。
  • 命中多个项目时不再是「只取最具体的一个」:会话目录命中的所有项目按路径由短到长串成一条链(父级在前、子级在后),规则依次叠加。链上最后一个 override 的项目会把它之上的所有层(含全局规则)一并挡掉 —— 所以「父 override + 子 append」=父与子都生效、全局不生效;「子 override」=父级与全局都不生效。单层项目时与旧行为完全一致。
  • 路径支持 ~;保存时统一规范化成绝对路径,尾部分隔符会被去掉;Windows 风格路径(C:\…)无论宿主平台都按 Windows 规则收敛。
  • 目录为空的项目条目不会被保存 —— 面板保存前会提示补全或删除。
  • 手工编辑该文件会被宿主自动重载(监听目录 + 15 秒轮询兜底)。

四、面板

位置:右侧栏的页面列表里点「开发规则」(与「文件 / 终端 / 浏览器 / Docker 容器」并列),内容在右列打开。

页签是 3 + N 个:全局规则 / 项目(名册)/ 每个项目一页 / 效果预览。项目一多,把它们的规则全塞在同一个页签里,就得在几个大卡片之间反复上下找;一个项目一页之后,页签栏本身就是「第一序列」的目录。页签条横向滚动,不折行。

打开就能用的三步

  1. 面板顶部吸顶条写明「这里是干什么的」+ 当前共几条规则;右侧只有一个主动作 保存并生效(有改动才可点,Ctrl/Cmd + S 同效)—— 导出 / 导入 / 重载都是低频动作,一并收在「更多」里,不跟它抢位置。
  2. 一条规则都没有时,面板给出两步上手说明 + 「先插入 4 条虚构示例」(同一目录风格一致 / 依赖升级单独提交 / 配置项集中管理 / 发布前核对版本号),插进来直接改成自己的。示例入口只有这一处,规则列表下面不再重复摆一个。
  3. 「项目」页只管每个项目的身份(目录 / 别名 / 与全局的关系 / 启用 / 删除),规则本身在它自己那一页里编辑——同一个字段只有一处可改。新建项目后会自动跳到它的规则页。

日常操作

  • 规则卡片按分组归拢成二级分块(组名 + 条数),与注入给 agent 的文本同序;一条具名分组都没有时不摆「未分组」的形式标题,免得全是噪音。
  • 卡片是两列式网格:单张卡片窄下来,同一段文字的横向跨度短了,眼睛不用长距离横扫。栏宽够(内容盒 ≥ 520px)才两列,窄栏自动退成单列 —— 两列挤到 200px 一栏反而更难读。
  • 规则卡片:标题(一句话)→ 正文(具体怎么做)→ 分组(可选)/ 标签(可选)+「生效」+「按场景」+ 上移下移 + 二次确认删除;卡片只标「约 N 字」(这条规则给每轮对话增加的上下文量,「按场景」的规则会标成「按场景 · 约 N 字」,因为它不是每轮都注入)。标题与分组/标签平时不描边(读起来是文字,不是并排的输入框),鼠标移上去或聚焦时才浮出可编辑的形状。
  • 上移下移只在同一组内换位(组与组之间的先后由「组名第一次出现的位置」决定,跨组移动做不到,按钮就在组边界禁用,而不是点了没反应)。
  • 停用的规则:卡片加虚线边 + 标题旁标「已停用」,只有正文变灰 —— 「生效」勾选框和右侧工具保持正常对比度,重新启用时点得着。
  • 每个项目页顶部一条只读身份栏(名字 / 目录 / 与全局的关系 + 「改目录 / 别名 / 关系 →」),要改身份就跳去「项目」页。
  • 「效果预览」:选一个目录点「查看」,用大白话告诉你:命中了哪个项目、用了几条规则、全局规则有没有被挡掉、一共多少字 / 约多少 token、是否被截断,下面给出实际注入的原文(等宽正文限宽 84 字符,栏拉宽也不会变成一行 150 字)。
  • 顶部「生效中 / 已停用」小胶囊就是总开关(点一下切换),不用去翻设置。
  • 冲突与导入都弹横幅并给出明确选择:「载入磁盘版本(放弃我的修改)/用我的修改覆盖」、「替换现有规则/合并进来/算了」。
  • 保存有两个档:顶部的 保存并生效(立刻生效,正在跑的会话下一步换新规则)与「更多 → 维护」里的 保存(只对新会话生效)。后者把已经在跑的会话钉在它们手里的旧规则上,新会话才用新规则 —— 规则一改系统提示就变了,正在跑的会话要重算提示缓存,长会话上这一下可能比一整天所有规则的开销还贵;不想付这个代价就选它。选了之后顶部状态行会写「N 个正在运行的会话仍在使用保存前的旧规则」,旁边一个「让它们改用新规则」按钮一键解冻。
  • **更多(折叠)**里是低频功能:插件更新(手动查一次本插件有没有新版本)、导出备份(Markdown / JSON)、从备份导入、保存(只对新会话生效)、放弃修改并重新载入、文件位置、使用说明。
  • 搜索与分组筛选只在有规则卡片的页签(全局 / 每个项目)出现,且只在该页规则较多(> 6 条)时才出现,避免一上来就堆控件;「项目」名册与「效果预览」都不吃筛选 —— 在那里摆一组不生效的控件,用户会以为结果被筛过。只要筛选条件还在,这一行就不会消失,否则规则会被静默藏起来,连清空条件的地方都没有。
  • 卡片风格统一:规则卡 / 项目卡 / 空态 / 上手块 / 身份栏 / 横幅共用同一组形状变量(圆角、描边、底色、内边距),差异只用描边样式与颜色表达(例如停用=虚线),不另起一套形状。
  • 面板渲染若抛错,会就地显示错误原文(错误边界),而不是整块空白。

五、注入形态

规则分四层送达,位置是刻意的:

层内容走哪条通道变化频率
L1 常驻mode: "always" 规则全文(含项目链)系统提示 section(plugin:ian-rules,order 100)只在你改规则时
L2 目录mode: "auto" 规则的标题 + 标签同一个系统提示 section只在你改规则时
L3 场景命中当前任务的 auto 规则正文消息尾部一条 user 消息(<system-reminder>)每轮命中集合变化时
L4 按需想看哪条就用工具取ian_rules 的 lookup模型决定

位置纪律(本插件最重要的一条):逐轮会变的内容只能放消息尾部,绝不进系统提示。 系统提示一变,它之后的全部内容(工具定义 + 整段对话历史)都要重算提示前缀;在有前缀缓存的供应商上,缓存命中与未命中的输入价差是 50 倍(按供应商价目,与用量无关)。放错一次,代价是本插件规则本身开销的几十到几百倍 —— 按 7k token 规则集 / 100 轮会话的量级估算,同一个会话会从 ¥0.002 变成 ¥0.86。L3 因此走 agent/pre-step 往消息尾部追加,而不是 systemPrompt.context() / section。

系统提示 section 的正文是常量 {{ian_rules_body}};真正的文本由同名提示变量提供。

为什么要绕一道变量:DSH 会对 section 正文做严格的 {{变量}} 插值,遇到未知 / 畸形引用会直接抛错,而这一步发生在插件回调之外——用户规则里的 {{placeholder}} 会把整个模型步打挂。变量值不会被二次扫描,所以用户写什么都不会破坏提示组装。

渲染形如(两级标题:第一级是「全局 / 哪个项目」,第二级才是你的分组):

# 项目开发规则

以下是本机用户维护的开发规则……如有冲突,以后者为准。

## 全局规则
### 提交
1. **提交前跑测试**
   npm test 必须绿

## 项目:示例项目(~/项目/foo)
### 依赖
2. **本项目用 pnpm**
   禁止 npm install
  • 第一级 ## 是全局规则与命中的那个项目:项目名写进标题(## 项目:别名(路径),没有别名就只写路径),不再单占「适用项目 / 规则来源」两行 —— 标题本身就说清了这段规则从哪来。
  • 二级标题 ### 是你的分组;同组规则并到一起,编号跨两级连续。分组顺序取「组名第一次出现的位置」(面板上看到的顺序与它一致)。
  • 未命中项目时明确写「当前目录未匹配到项目规则集,以下为全局规则」;覆盖模式下不出现 ## 全局规则 那一段,改为在项目标题下写明「本项目为覆盖模式:全局规则(N 条)在本项目内不生效」—— 避免 agent 误以为规则不存在。
  • 打开「按场景注入」后,多出一段 ## 按场景规则(目录):auto 规则的标题与标签(最多 60 条),正文不在系统提示里。

按场景匹配怎么算(lib/scene.js,零依赖纯函数):

  • 信号:只用本步的真人消息(最近 3 条),外加 runtime-context 快照的排除项。工作目录不进信号 —— 它只用来决定候选规则范围(effectiveRules 已按 cwd 过滤),进信号会把目录名喂给匹配器(~/项目/… 里的「项目」曾经把标题含「项目」的规则每轮都喊出来)。
  • 分词:ASCII 转小写按词切;中文用二元字组(「提交前跑测试」→ 提交/交前/前跑/跑测/测试)+ 单字兜底,不引分词器。只认汉字 —— 标点与全角形式不算词(否则一个「,」也能参与打分)。
  • 打分:命中标签 ×3 / 标题 ×2 / 正文 ×1,单字再乘 0.25 降权,最后按正文长度归一化(否则一条 4000 字的巨型规则通吃所有场次)。
  • 零词命中不算相关:一个词(二元字组 / ASCII 词)都没命中时直接判 0,单字只能给已成立的匹配加分。这是必要的 —— 单字权重再低,跨「标签 + 标题 + 正文」三处求和也会翻过阈值(实测:这段代码复现不了 靠 段/现/复/不 四个常用字拿 2.81 分,误命中一条毫无关系的规则)。
  • 阈值 1.5,每轮最多 8 条、最多 2000 字。
  • 轮内 sticky:同一轮的第 2 步往后不再有新的用户消息,命中的集合只增不减,否则规则会在步与步之间抖动;跨轮重新匹配,避免第一轮的话题粘到最后一轮。
  • 集合没变就不重复注入:命中集合的指纹(sha256 前 16 位)一样就不再追加消息。指纹跨轮保留(否则同一话题连着聊几轮,历史里就躺着几份一模一样的正文);只有在历史被压缩过(surface.contentGeneration 变了)时才连指纹一起重置 —— 那时旧的那份可能已经被丢掉,必须重发。

L3 送出的文本形如:

<system-reminder>
本机用户维护的「项目开发规则」中,与当前任务相关的这几条(按场景匹配后送达,不是全部规则):

### 依赖
1. **依赖升级单独提交**
   升级依赖不要和功能改动混在同一个提交里

另有 1 条按场景规则本次未命中;需要时用 ian_rules 工具按关键词取用。
</system-reminder>
  • 「另有 N 条未命中」这句是必须的:让漏命中从静默失效变成可发现,同时告诉 agent 还有 lookup 这条路。
  • 面板「效果预览」里的场景模拟器可以输入一句话,看它会命中哪几条、差一点命中的那几条各是多少分——匹配器是纯函数,模拟不改任何状态。
  • 「更多 → 按场景注入 → 记录命中日志」打开后,每次注入往 $DSH_HOME/ian-rules.hits.jsonl 追加一行(会话 / 轮次 / 工作目录 / 命中的 id)。默认关,它只对回头调阈值有用。
  • 命中多个项目(monorepo)时,父子规则拼在同一段里、父级在前,并明确写「本段含继承自上层项目的规则(父级在前):/repo → /repo/packages/a」—— 否则 agent 会把父级规则当成当前项目的规则。
  • 超出单次注入上限(12000 字符)时:先裁全局规则(项目规则更具体、更该活下来),从各自末尾往前裁,至少留一条;并在提示开头写明「本轮有 N 条未注入:标题一 / 标题二 ……」。这句必须在开头 —— 放尾部会被截断自己切掉,等于没写。规则数没超限时输出与旧版逐字节相同。

六、接口

方法路径作用
GET/ian-rules/state读当前文档 + 元信息(文件、备份、revision、规模、错误、被「下个会话生效」钉住的会话数)
GET/ian-rules/workspaces项目路径下拉的数据源(工作区注册表 + 活动会话目录)
POST/ian-rules/save{ doc, revision, applyMode? } 保存;revision 过期 → 409 + 当前文档
POST/ian-rules/reload从磁盘重新读取
POST/ian-rules/preview{ doc?, path } 渲染注入文本 + 字符 / token / 逐条体积 + 命中链 + 按场景目录
POST/ian-rules/scene{ path, text } 场景模拟:这句话会命中哪几条按场景规则、各多少分、差一点的是哪几条
POST/ian-rules/export{ doc } → Markdown 与 JSON 文本
POST/ian-rules/import{ text } → 解析 Markdown 或 JSON 得到文档

排障示例:curl -s 127.0.0.1:3080/ian-rules/state | head -c 400

/save 的 applyMode:now(默认)=立刻生效,正在运行的会话下一次请求就用新规则;next-session=只对新会话生效,已经在跑的会话继续用它们已经吃进上下文的旧规则。后者是零代价的:规则一改系统提示就变了,而正在跑的会话下一次请求要重算提示缓存(长会话上这一下可能比一整天所有规则的开销还贵)。面板的入口在「更多 → 维护 → 保存(只对新会话生效)」;被钉住的会话数在顶部状态行里显示,旁边有「让它们改用新规则」一键解冻。不带 doc 的 /save 保存的是内存里的当前文档(用于只改生效方式、不动规则)。

安全边界:以上接口只接受同源请求(跨站 Origin / Sec-Fetch-Site 直接 403),POST 必须 Content-Type: application/json。但同机的其它本地进程仍可无凭据访问(DSH 的 webServer 不对插件路由做登录鉴权)——规则内容会进模型提示,别把不能外发的东西写进去。

七、开发

node --test              # 53 个用例
npm run check            # 语法检查 + 身份自检 + 全部测试
npm run check:identity   # 只跑身份自检

名字只有一处出处(package.json 的 name),其余标识都由它派生(slug = 包名去掉 dsh-)。scripts/check-identity.mjs 会把这几处对一遍:cordis patch 的 name、客户端 __ModuleLoader__.load({ id })、插件市场认的 PACKAGE_NAME、页签 TAB_IMPL_ID、路由 /ian-rules、section plugin:ian-rules、提示变量 ian_rules_body、数据文件 ian-rules.json、页签 kind —— 它们必须一致。客户端注册 id 与包名不一致时,界面启动直接报「Failed to load plugins」,而宿主接口一切正常,只 curl 接口的隔离自检根本抓不到这种错(这个坑踩过一次,见 test/client.test.mjs 里那条按包名对齐的用例)。

同一个脚本还守住两件容易被「全局替换」误伤的事:

  • 历代更名前(dsh-dev-rules、dsh-agent-rules)的旧名字不许再出现在代码与配置里。允许留下的例外逐条写在脚本的 ALLOWED 里 —— 目前 18 条,每条都要说明为什么可以不改(数据文件迁移的来源名、页签 kind 的兼容注册、Gitee 镜像地址……)。新增一条等于承认多欠了一笔账。
  • Gitee 镜像地址必须仍是 dev-rules:镜像没随更名改动,而「把文档里所有旧名字换成新名字」这种操作会顺手把它改掉 —— 那是文档里国内用户的安装路径,本地测试全绿也发现不了。写这条守卫时我自己就误伤了一次。

于是改名=三步:改 package.json 的 name → 把旧名字补进两个 lib 里 LEGACY_* 那几行(历代名字的清单,由新到旧)→ 跑 npm run check:identity 看还差哪里。

改完先在隔离沙箱里验,别拿日常在用的那个 profile 试。 宿主半体是在 profile 启动时加载的:一个有问题的改动足以让整个 DSH 起不来,那时你连界面都进不去,只能去终端里拆插件。

npm run sandbox         # 在 .sandbox/home 里装本地检出并起一个实例(默认 :3199,Ctrl-C 结束)
npm run sandbox:check   # 只做自检 + profile 组装,不启动(提交前跑这个最快)
npm run sandbox:clean   # 删掉沙箱

# 想验「使用者装到的到底是什么」:把来源换成发布的那份再起
DSH_SANDBOX_SOURCE=github:Grant-Felix/dsh-ian-rules npm run sandbox
DSH_SANDBOX_SOURCE=git+https://gitee.com/Grant-Felix/dev-rules.git npm run sandbox

沙箱有独立的 DSH_HOME,所以它读写的是自己的 ian-rules.json,不会碰你的真实规则文件;脚本还会拒绝把沙箱 home 指到真实 home。默认端口可用 DSH_SANDBOX_PORT 改。

  • lib/rules.js 纯逻辑(规范化 / 路径匹配 / 生效规则 / 渲染 / 分组 / token 估算 / Markdown 往返),宿主、面板与测试共用;路径匹配的 win32 分支通过 platform 参数可测。
  • lib/index.js 宿主半体:存储、提示变量注入、按场景注入(agent/pre-step)、/ian-rules/* 接口、ian_rules 工具。
  • lib/scene.js 场景匹配器:分词、打分、挑选(纯函数、零依赖、可 node --test 直跑)。
  • lib/client.js 浏览器半体:手写的 window.__ModuleLoader__.load({ id, factory }) bundle,只依赖 react,不需要打包器;纯函数内部件(token 估算 / 导入合并 / 更新提示判定)通过 exports.__internal 暴露给测试。
  • scripts/sandbox.sh:上面那套隔离环境的实现(独立 DSH_HOME + 独立端口 + 安全闸)。
  • 测试:test/rules.test.mjs(逻辑 / 两级标题渲染)、test/host.test.mjs(接口 / 备份 / 409 / 403 / 415 / 400 / 软链回退 / 工具 / 多字节请求体跨块解码 / revision 不被自己的写盘事件推高)、test/client.test.mjs(槽位接线 / 服务晚出现 / 内部件 / 两列网格与卡片统一的源码级守卫)。
  • CI:.github/workflows/ci.yml 在 node 20 / 22 / 24 上跑语法检查 + 测试。

八、代码托管

本仓遵循「本地 Forgejo 开发 / GitHub 与 Gitee 对外并受理反馈」的三平台分工:代码与提交历史以本地 Forgejo 为准,两个公开平台只做对外窗口与镜像。

平台角色状态
本地 Forgejo(私有,走回环)开发主仓,代码与历史以它为准已建仓并推送(默认分支 main)
GitHub https://github.com/Grant-Felix/dsh-ian-rules对外窗口 + 反馈受理已发布(公开,MIT)
Gitee https://gitee.com/Grant-Felix/dev-rules国内镜像 + 同样受理反馈已发布(由 GitHub 单向同步)

代码单向流动:本地 Forgejo → GitHub → Gitee,禁止把某个平台的提交反向直推到另一个平台(会造成历史分叉与重复改动)。两个平台上的 Issue 与 PR 都一样受理,但同一个问题只在先提出的那一侧开正式讨论,另一侧贴链接引导过去,避免两边各说各话。

2026-10-05 更名:GitHub 仓库由 dsh-agent-rules 更名为 dsh-ian-rules(GitHub 对旧地址自动重定向),插件包名与全部派生标识(slug ian-rules、路由 /ian-rules、数据文件 ian-rules.json、页签 kind、提示变量 ian_rules_body、模型工具 ian_rules)同步更换;上一代的名字只留在迁移与兼容那一小块里(见前文「更名迁移」)。Gitee 镜像地址仍是 dev-rules,未随更名改动。

推送凭据按平台分开配置:每个 host 各有一个 git credential helper,且都校验 host=、只对自己那一个平台应答,其它 host 一律静默退出 —— 否则会把 A 平台的令牌回给 B 平台。

发版版本号用发布日期式:<YY>.<M>.<D>-<当日序号>,如 26.9.21-1(2026-09-21 当天第 1 个版本),同日第 2 个是 26.9.21-2;年月日不补零(26.09.21-1 不合法——semver 禁止数字标识符带前导零)。标签、清单 version、Release 标题与产物名用同一串(tag 加 v 前缀),三个平台推同一个 tag。

-x 在 semver 里是 prerelease,两点要记住:一直用它、别与不带序号的 26.9.21 混用(排序上 26.9.21-1 < 26.9.21,混用会把「第几个」和「新旧」搞反);发布到 npm 必须显式给 tag(npm publish --tag latest),否则 npm 拒绝发布 prerelease。

代码与规则内容分开。 本仓只装插件(开源,MIT);作者本人维护的规则内容不在本仓、也不随本仓分发,只存在于作者本机。这条边界由文件位置本身保证,不靠约定去守 —— 详见 NOTICE.md。

九、许可

代码开源、规则内容闭源:

范围许可
本仓源代码(lib/、test/ 等)MIT(见 LICENSE)
本仓内的示例规则随代码 MIT —— 全部虚构,不是作者的真实规则
作者本人的规则内容不开源,也不在本仓:只存在于作者本机的 ~/.dsh/ian-rules.json
使用者自己的规则内容归使用者所有,与本项目许可无关

范围与边界见 NOTICE.md。