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.

Story Mode — DSH Plugin for DeepSeek Harness
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in
← Plugins
S

dsh-story-mode

Story Mode

DSH Short Story Mode: one package includes the complete toolkit—an agent preset (mode), writing workflow skills, a writing style contract skill, five role-based read-only reviewers, and four structured tools. Install with one command and uninstall cleanly.

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

npx -y @deepseek-ai/dsh plugin --profile web add github:Furry-wucheng/dsh-story-mode#d8fa8e3685264cb5f475f01962f53526b5e69b83
READMECompatibilityVersions

Compatibility and provenance

Story Mode is published as dsh-story-mode and currently resolves to version 1.2.1. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
any
Release source
github
Registry updated
9/20/2026

Versions

1.2.1stable
9/20/2026

Related plugins

Loading related plugins…

Latest
1.2.1
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
Unavailable
Files
Unavailable
Surface
any
License
MIT
Source
github
GitHub
★ 1
Weekly downloads
0
Last push
9/19/2026
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

Related plugins

More verified plugins in agents-orchestration.

Agency Agents@michengai/dsh-agency-agentsAgency Experts — a summonable domain-expert roster for DSH (expert mode)Headless@deepseek-ai/dsh-headlessThe dsh one-shot bundle: a direct core Agent/Session runner over dsh-base with no Host, HTTP, or browser layerAutomation@michengai/dsh-automationExecute coding tasks on a schedule in an independent DSH Session, with dual-entry management via a Web settings page and Agent.Auto Reviewdsh-auto-reviewSecond-model AI auto-review for DeepSeek Harness approval requests: a read-only reviewer subagent decides allow/deny on the approval answerer chain, with fail-closed fallback and full session-log audit.

README

dsh-story-mode · 短篇小说模式

给 DeepSeek Harness (DSH) 的一个写作专用模式。装好之后,新建会话时模式选择器里会多出「短篇小说模式」——进去的不是编码 agent,而是一个接稿的短篇小说作者。

整套能力(模式、技能、工具)都在一个包里,一条命令落地。


它解决什么问题

让可测量的内容由程序负责,让需要上下文的内容由读者判断。

程序测量或定位审读判断
中文字数、分场、相对已确认目标的偏离场景是否有效、节奏是否合适
引号内文字比例、段落长度对话是否自然、人物口吻是否可区分
重复双字组合、常见用词位置修辞是否有效、心理与情绪是否冗余
人物卡字段是否非空、姓名与别名字面提及新人物、专名一致性、动机与跨篇设定
稿件内容版本、原文行号视角、时态、因果与必要交代

字面命中不代表违规,统计正常不代表故事好看。工具不再根据“我/你/他”判定视角滑移,也不依据固定对话比例评价节奏。

完整故事与片段、单场景、开头、对话都要确认方向与字数、准备人物卡和节拍、成稿后独立审读。局部任务缩小规划和审读范围,不取消流程。


安装

dsh plugin --profile <你的 profile> add github:Furry-wucheng/dsh-story-mode

就这一条。 不需要第二步、不需要改任何配置文件。装完新建会话,模式选择器里就有「短篇小说模式」。(CLI 启动的 profile 下当场生效;桌面版若没看到,重启一次 DSH——它的 profile 组装只在启动与插件状态变更时跑,没有监听 bundles 的 watcher。)

它是怎么做到的

模式住在包里(presets/short-story/)。包的 cordis.patch.yml 在 profile 合成配置时被合并,其中用 createRequire(ctx.baseUrl) 在运行时问出本包装在哪,然后接管 agent-presets 那一行、把包内 presets/ 声明为 roster 的一个根。

「包被装到哪」在发布时不可能知道,所以路径必须运行时算——这也是社区插件(如 dsh-TUI)解决同一个问题的做法。

卸载

dsh plugin --profile <你的 profile> remove dsh-story-mode

模式与它自带的两份技能都跟着包一起走,这两样不需要任何清理——它们从来没被复制到你家里去过。(模式住在包的 presets/short-story/,技能是 preset 用 customSkillDirs 从包内挂上去的,所以文风契约只在这个模式里可见。)

只有一件事要在卸载前确认:

dsh plugin --profile <你的 profile> exec dsh-story-mode check

如果它提示「默认预设指向本模式」,先清理再卸载:

dsh plugin --profile <你的 profile> exec dsh-story-mode cleanup

为什么要跑:如果你在设置里把「短篇小说模式」设成了默认模式,卸载包之后新建会话会直接以 agent-preset/not-found 失败——DSH 找不到默认 preset 时不会回退到 standard,而真正会顺手清掉这个默认值的那条路径被 dsh plugin remove 绕过了。这一步必须在卸载之前做,因为包一旦 remove,这个命令也就没了。

(cleanup 顺带清掉两处历史残留:v1.0.1 复制到 <DSH_HOME>/.agent-presets/short-story 的模式副本,以及 v1.0.1/v1.0.2 曾可选安装的 <DSH_HOME>/skills/writing-style-contract 副本——后者只在带 .dsh-story-mode.json 归属标记时才删,绝不会误伤你自己手写的同名技能。不是本包放的东西一律不动。)

一条命令的边界:它接管了官方那一行

ctx.agentPresets 这个服务只允许发布一次,所以本包不能另插一行——那样会和官方行冲突,只能有一方生效。所以它覆写官方 agent-presets 行的 config,保留 default: standard 并把本包的 presets/ 追加为根。

代价必须知道:补丁按 id 覆盖 config。升级 DSH 之后,如果官方给这一行加了新字段,本包会静默抹掉它。对照检查:

dsh --profile <你的 profile> --dump-default-config

桌面版对同一行也压了一层,而且在本包之后:它读回合成后的 config,写成 roots = [<dsh-agent-presets 包>/presets(system), <DSH_HOME>/.agent-presets(user)] 加 includeUserRoot: false。本包没被它盖掉,靠的是本包 config 是一个 !!js 表达式——落地后它是 { __jsExpr: … } 标记,桌面那层展开会把这个标记一起带上,而 Loader 的 interpolate() 先看整体:isJsExpr 命中就整体求值返回,后加的 roots 被丢弃。

这不是“稳”,是“恰好”:如果把这一行的 config 改成“普通映射 + 内层 !!js 算路径”,桌面那层就会反过来盖掉 roots,模式会静默消失且没有任何报错。改这一行前请先读 cordis.patch.yml 里的对应段落。

story_doctor 会提示这项风险,但不执行官方配置比对或实际加载验证。将来若多个插件都需要追加根,这是框架层面的限制(roots 不是增量合并的)——届时该由 DSH 提供追加语义,而不是每个插件各自覆写。

验证安装

下面的命令只检查卸载残留,不证明模式已经加载成功:

dsh plugin --profile <你的 profile> exec dsh-story-mode check

安装验证请在新会话里让 agent 调用 story_doctor —— 它会逐项报告:包是否进了 bundles、package.json 是否可解析(带 BOM 会让 DSH 读不出 dsh.bundle)、patch 是否接管了那一行、包内模式目录是否满足静态发现条件、文风契约是否已挂进 preset,以及卸载前需要注意的两处残留(默认预设 / 旧安装副本)。

其他安装方式

# 全局安装
pnpm add -g github:Furry-wucheng/dsh-story-mode

# 从源码目录直接开发(不装进 profile)
git clone https://github.com/Furry-wucheng/dsh-story-mode
cd dsh-story-mode && pnpm pack        # 得到一个 tgz
dsh plugin --profile <你的 profile> add ./dsh-story-mode-1.1.2.tgz

升级

dsh plugin --profile <你的 profile> add github:Furry-wucheng/dsh-story-mode

模式与两份技能都跟着更新——它们都住在包里,没有任何需要手动刷新的副本。

用它

主代理先加载写作技能,再完整读取共用流程与审读面板。新写或续写前,必须问清尚未给出且未授权自拟的方向、目标字数、基调、关系口吻、视角与停止位置;一次问完,不重复已明确的要求。“角色自拟”只授权角色,片段没有字数也要问。方向与篇幅确定后给人物与节拍方案,确认后成稿;已有同一方案的批准或作者明确免确认时直接沿用。

  • 新写整篇或片段:确认方向与字数 → 人物卡和节拍 → 方案确认 → 成稿 → 独立审读 → 修订复核。正文只写到约定停止点,片段无需全篇闭环。
  • 续写:读取获准前文与相关人物卡、节拍,确认新增内容和字数后执行同一流程;不擅自补完全文。
  • 整体改稿:核对并补齐受影响的人物卡和节拍,按确认范围修改,独立审读新版。
  • 局部润色:核对涉及的人物卡、当前节拍与接缝,缺失时整理最小记录;修改后也必须独立审读,只缩小读稿范围。
  • 只要评价或方案:独立审读对应材料后交付意见或方案,不自动写正文;评价不擅造作者设定。

完整作品的篇幅档位仅供参考:微型 3k–5k、标准短篇 5k–10k、中短篇 10k–20k、系列每篇 3k–10k。片段没有最低字数,但仍须由作者确认字数或明确授权自定;“写一段”“快一点”不能据此猜一个篇幅直接开写。

故事资产

新项目通常用 brief.md 保存方向和字数约束、bible.md 保存本次人物卡、outline.md 保存节拍与篇幅安排、draft.md 保存正文。文件可以合并,已有项目沿用既有记录,但不能因为是片段就缺少人物卡与节拍。只有涉及范围内的人物和内容需要规划,不强加人物生平、完整主线或后日谈。

人物卡包括身份、当下动机或需要、关系、性格和口吻、当前状态与相关事实。节拍安排当前内容的先后、快慢详略、字数与停止点;可以有不推进事件的闲聊和停留。详见共用流程的示例。

时间关系复杂时再用 timeline.md,专名多时再用 glossary.md。搜索、枚举和审读输入都限于授权目录,不能为避免新目录重名先列出其他作品。

四个工具

全部只读。正文和设定的修改走宿主的常规文件工具。

工具实际能力与边界
story_wordcount字数、按分隔符或标题分场、目标偏离、段落长度、引号内比例、引号外重复双字组合;不评价节奏好坏
story_lint引号外用词线索,带原文行号和版本;不判视角、时态、情绪冗余或“AI 文风”,也不要求按命中删改。自查与定位用,不进审读流程
story_bible校验人物卡必填字段非空、姓名及别名字面提及、术语清单;不识别全部新人物或判断设定合理性
story_doctor静态安装自检与残留提示;不能代替 DSH 实际加载和子代理运行验证

按大纲目标统计

{“path”:“故事/draft.md”,“sceneTargets”:“1000,3000”}

sceneTargets 是按场景顺序排列的正整数字数。工具计算(实际字数 − 目标字数)/ 目标字数,不按场景均分。 逐场成稿时可传包含后续未写场景的完整列表;已写场景必须都有目标。没有计划目标时省略参数,只看分布。 配置 sceneDriftPercent 表示相对目标的偏离百分比,默认 15。旧 dialogueLowPercent / dialogueHighPercent 已不再使用。

引号内比例仅是对话的近似量,包含引用,不含无引号台词。重复项是未经分词的双字组合,不自动构成用词错误。 标题与开头的完整元数据块不计入正文;报告行号仍对应源文件。

用词线索与人物检查

story_lint 的 only 可筛选:template-simile、psychological-summary、emotion-explained、dialogue-tag-overuse、emotion-adverb-tag、ai-rhythm、cheap-adverb。这些旧规则 id 保持兼容,报告现在只表示待阅读的线索。旧 pov 参数和 only: pov-drift 仍接受,但不执行人称检查,会说明判断已移交审读员。

story_bible 的“缺卡候选”仅来自 bible 的“已知事实”清单里,在正文出现至少两次、却未建卡的条目。正文中新出现但未进清单的名字不会自动识别;人物、指代与拼写一致性要由审读员核对。

审读流程

新写、续写或大幅重写,包括片段:B1 冷读与 B2 故事逻辑每个版本都派;B3 阅读体验、B4 文风执行、B5 动作与空间连续性命中各自的可观测条件就派(条件见下表与本包的审读面板)。流程是:按角色派发 → 主代理修改 → 原读者复核 → 全新 B1 最终盲读。各角色独立读稿,不预先看彼此报告。 一句或一段的润色也至少有一位独立读者检查改动与接缝;涉及动作、位置、姿态、物品状态或相关删句时必须派 B5。B5 从正文独立追踪状态,检查手脚占用、物品流转、路径与支撑,区分物理矛盾和合理省略;修订后由原 B5 沿受影响人物或物品复核前后状态,直到重新衔接。B5 不由 B2 兼任,B3 不由 B1 兼任。自查、计数和 lint 不等于独立审读;作者催快时缩短报告,不默认取消审读。必要报告或复核缺失时标待审,不虚报验收。

关系轴:五个角色之外的那一次(v1.2.0,起点组 v1.2.1)

五个角色的报告结构都在问**“这里有没有矛盾”(一致性)。它答不了另一个问题:“这一跳有没有依据”**(充分性)。

一份所有角色都报“没有大结构问题”的稿子,可以在主轴上完全空心。 逐处都写得成立、前后都对得上、字数与版本全闭合,而读者仍然会说“他为什么突然就同意了”“我没看出他动过心”——因为把 A 推到 B 的那个东西从来没有被写出来,而“没写出来”不是任何一个角色会报的那类问题:B1 只管“读不懂”,B2 只管自洽,B3 明确“不要求证明剧情作用”,B4 明确“不核对剧情因果”,B5 明确“不评论人物动机”。每个角色的边界都写着“这不是我的职责”。

所以关系轴单独走一次,仍由 B2 承担(它已经读人物卡与节拍,缺的是被要求回答,不是多一双眼睛):

时机读什么必答
方案阶段(默认,必做,先于任何正文落地)节拍表、人物卡,没有正文主线每一跳的推动者是谁;另一方的动心露出有几条、哪些不可否认;被动方有没有主动动作;双方的注意起点落在哪一拍、为什么只对这个人成立
首次成稿后正文 + 节拍方案里承诺的推动者、露出与双方起点,逐条给出正文短引;引不出来算缺

判据是硬的:露出全部可被解释成“尽职/礼貌/习惯”=零;“推动者”写不出一个动作或一句台词=缺一拍;“为什么是这个人”只有叙述者说得出、任何一场戏都说不出的理由=起点缺失。报告不接受“整体尚可”“铺垫基本到位”。关系轴报出的缺拍按优先修复处理,不归入“可选调整”。

B2 的必答项一共三组,第三组专治作者会读成“上帝视角”的那种毛病:每个“看穿/识破”的时刻,把它依据的那一句话抄出来;抄不出来就是缺口。 角色的判断必须建立在这场对话里已经给出的证据上——他比读者先知道又不给证据时,读者不会觉得他敏锐,只会觉得作者在借他的嘴交代剧情。可照抄的对照:“你站得太近了”是观察,能给依据;“你心事太重”是判断,读者手里只有一句话,接不上。

配套的两条改动:节拍表新增“状态变化 / 谁推动 / 代价”三列(写不出推动者的拍是重复场景,不是节拍);“留白”这条护栏写明不适用于主轴——允许留白“他什么时候开始动心”,不允许留白“他动没动过心”,也不允许留白“为什么是这个人”。

台词:另一种同样测不到的毛病

同一次调用还暴露了台词的形态问题:每一句都在交付信息或下判词,没有人随口附和、没有人重复、没有人答非所问、没有一件聊完就放下的小事,整篇像双方在念台词。文风契约第四节其实写明了要允许“随口附和、确认听清、自我纠正、绕开问题、聊一件小事后自然结束”——成稿里一个都没落地,因为规划阶段没有为它留位置。

所以这一条也落在规划与必答项里,而不是靠 lint:节拍表要为每场标注对话的功能分布,且每场至少要有一处“无功能”交流;“看穿”的能力要单向、向下(用在一个地位更低或更被动的人身上——反过来的“学生一眼看透大人”是读者最容易读成上帝视角的形态),且结论必须有本场已给出的证据。

工具测不到这一类:story_lint 的七条规则全是字面形状(套话、解释性副词、对话提示语密度),而“所有台词都在交付信息”是功能分布问题,没有正则能写。

代价:B2 的人设从 2.8 KB 涨到 8.1 KB——每次派发(含复核)约多 1.3 K token。换掉的是一次成稿后才发现缺一拍的整场重写;也顺便解释了为什么不做成第六个角色。

关系轴报告允许放宽篇幅(常规核对仍以 1200 字为度,关系轴要求逐条引用,写到 3000 字以上也可以)——不要为了压字数把必答项写成概括,那等于把这一节取消掉。

作者说“我没看懂”是指令

同一处或同一条线被报两次,或困惑落在关系推进、核心动机、关键转折上时,按结构问题处理:回节拍表逐拍核对“谁推动”,找到缺哪一拍。不做句子级修补,也不靠加一句解释台词糊过去——把解释补进台词(“他其实早就……”)不能替代补一拍。困惑出现在主线上时,“是不是没写出来”优先于“是不是读者没读到”。

审读员是可复用的持久子代理。 每个角色派发一次就拿到它的 childId;改稿后的复核用 send_message 交给同一位读者——它保留着上一版的阅读和自己的报告,只需要回答“这次改了什么、你上次那几条还成不成立”,不必重读全文,前缀命中缓存。只有最终盲读另起一位新读者:独立性不能复用。派发时不要传 run_in_background: false,那会退化成一次性会话,后续消息送不到。

这套行为由 preset 的 backgroundMode: continuable 加 send_message / list_agents 两行工具提供;story_doctor 会静态检查它们还在不在。

角色工具可以读取的材料什么时候派
B1 冷读者subagent_review_b1本篇正文;连续阅读可读已发布前文,不读大纲、设定、作者摘要或旧问题每版都派;最终盲读换全新读者
B2 故事逻辑subagent_review_b2正文、作者有效要求、人物卡、节拍、必要前文与设定每版都派。并承担关系轴审读:方案阶段一次、首次成稿后一次
B3 阅读体验subagent_review_b3正文与获准前文;不读大纲、设定与文风契约本文 ≥5000 字或 ≥3 场;作者提过节奏/篇幅/详略;有增删场次或调序;交付前最后一版
B4 文风执行subagent_review_b4正文、作者要求、文风契约(已装进它的人设)作者对口吻/视角/叙述提过要求;改过台词、心理或视角;交付前最后一版;≥3000 字的完整成稿
B5 动作与空间连续性subagent_review_b5正文、必要前文及明确的身体或物理规则;不读大纲、节拍、主代理的场景状态摘要、文风契约或其他读者报告多人同场或涉及持物、走动、身体接触;改过动作/位置/姿态/物品状态或相关删句;交付前最后一版

每次派发记录正文路径和工具给出的内容版本。审读期间暂停修改;回收后核对版本。 问题位置采用“场景 + 原文短引 + 行号”,主代理追踪改稿后的对应位置。不同版本的段落号不能直接交叉判断。

最终盲读使用全新读者,只看最新正文。 旧问题是否解决由主代理在报告返回后对照。读者的困惑是需要核实的证据,也可能是合理悬念,不自动要求补背景。

审读员不改文件、不再委派,这是权限层的限制而不是提示词请求。 每个角色由 preset 里独立的一行 tool-subagent 提供,带 toolFilter:只允许 read / read_image / str_replace_editor / glob / grep,write / edit 与全部委派工具都被 tools.restrict() 在这个子代理的 scope 上剥掉。角色的人设(身份、读什么、报告格式)同样固定在那一行里,由 !!js 从 references/reviewers/*.md 读出,所以派发时只给变量、不必重述边界。独立审读无法运行时如实说明,不虚报验收。

写作入口要求主代理完整读取 references/writing-workflow.md(共用写作流程)和 references/review-panel.md(独立审读);整篇与片段都必须读。两份资料位于 presets/short-story/skills/short-story/ 下,skill 调用不会自动加载它们,必须另用文件工具读取。


包结构

dsh-story-mode/
  cordis.patch.yml                  接管 agent-presets 行,把包内 presets/ 声明为 roster 的根
  presets/short-story/              模式本身:agent.cordis.yml + 元数据 + 写作流程技能
    skills/short-story/             SKILL.md:必读入口、方向与字数确认、范围约束
      references/                  writing-workflow.md:人物卡、节拍、成稿与修订;review-panel.md:派发规则与角色触发条件
        reviewers/                 B1–B5 各自的固定人设(身份、读什么、报告格式),由审读员行读出
  lib/index.js                      主入口:写作工具
  lib/doctor.js                     独立入口 dsh-story-mode/doctor:只注册 story_doctor
  lib/tool-kit.js                   零依赖的工具构造器与参数校验(两个入口共用)
  skills/writing-style-contract/    文风契约(由 preset 挂进模式,只在这个模式里可见)
  bin/cli.mjs                       check / cleanup(不安装任何东西,只做卸载前清理)
  scripts/cleanup.mjs               实际干活的那份

两个入口是 exports 子路径实现的同包多入口。./doctor 可以单独挂到任何模式里做诊断(例如创造模式),不必连带加载三个写作工具。


已知约束与设计取舍

这些都是查过框架源码、并且踩过之后才写下来的:

  • 审读员是 continuable 子代理,不是一次性调用。 一次性模式(v1.1.0 及以前)下每轮复核都要再派一位读者,把同一篇正文重新从头读一遍;可复用模式下复核走 send_message,子代理带着上一版正文的阅读和自己的报告继续。坑:可复用模式里只有后台调用会产生持久 child,前台分支走的是 subagents.start(),拿不到 childId——所以技能与审读面板都明令派审读员时不要传 run_in_background: false。

  • 五个审读角色各自一行,人设与只读限制都在组合里。 每个角色是一个独立的 tool-subagent 行(subagent_review_b1 … b5):persona 由 !!js 从 references/reviewers/<角色>.md 读出并装到那个孩子身上,toolFilter.allow 只列读类工具,deny 拿掉 write / edit / present / 全部委派工具。于是派发时主代理只给变量(路径、版本、范围、改动清单),不再从面板抄模板——抄一次就可能漏一条信息边界。两个坑:① tools.restrict() 对未知工具名抛错,而它抛在孩子的创建窗口里=那次派发直接失败,所以名单里只能出现本组合真实注册过的工具名;② 审读员人设里不要给 skill 工具,B4 需要的文风契约在挂载时拼进它自己的 persona。

  • 写作模式故意不开 subagent_fork。 fork 继承主代理的全部上下文(大纲、写作推理、修改理由);读者看过作者的底牌之后,“我没看懂”就不再是读者证据。审读员也因此不开子级模型选择,一律与主代理同路由。

  • 技能根由 preset 自己声明,所以文风契约只在这个模式里生效。 写作流程技能与文风契约分别是包内的 presets/short-story/skills/ 与 skills/,都以「preset 文件所在目录」为基准解析(!!js 里的 baseUrl),注册落进本 preset 那一层,所以模式、流程技能、文风契约三者永远同进同出。它故意不放进 <DSH_HOME>/skills:那是用户根(rank 400),而每个 preset 自己挂的 skill-filesystem 实例都会扫它(includeDefaultRoots 默认 true)——放进去等于让它出现在所有模式里,包括编码会话,而它只属于写作模式。

  • 写作模式保留了搜索工具(glob / grep)。 shell、计划模式、后台任务、工作流、多级委派都拆掉了,但“找”不能拆:系列连载里核对名字与细节靠搜,不靠通读。这一行要注意 sampleOverCapGlobResults 在 framework 侧是必填配置(z.boolean().required()),漏了它整个 preset 会挂不上。

  • ctx.agentPresets 只允许发布一次,所以一个插件不能只“追加自己的 roster 行”——官方行几乎总是存在(web-app bundle 提供它),两行不能共存。注入根就必须接管那一行,代价见上面「一条命令的边界」。

  • roots 是整体替换而非增量合并。 所以本包接管那一行时会把它自己的根写全;多个插件都要追加根时,这是框架层面的限制。

  • 根下的 <id> 条目必须是真实目录。 roster 的 scanRoot 用 readdir().isDirectory() 判定,不跟随符号链接。Windows 上 Node 把 junction 报成符号链接,于是链接形式的预设会被静默跳过,而 stat() 读文件却完全正常——本包早期版本正是栽在这里。(技能侧相反: 会跟随链接一级。)

安装会改动你的 home 目录吗

不会。 dsh plugin add 只写 profile 的 package.json、node_modules 与 patch 层:模式住在 profile 的 node_modules 里(也就是 pnpm 装包的地方),技能由 preset 从包内挂载。<DSH_HOME>/skills/ 与 <DSH_HOME>/.agent-presets/ 都不碰,也没有需要用户去批准的构建脚本(pnpm 默认就会拦截依赖的生命周期脚本,本包不依赖它)。

cleanup 是唯一会写你 home 的命令,而且只删本包自己留下的东西:指向本模式的默认预设、v1.0.1 的模式副本、带 .dsh-story-mode.json 归属标记的技能副本。不是本包放的一律不动。

注意:v1.0.1 把模式复制进 <DSH_HOME>/.agent-presets/short-story。那份副本会和 patch 声明的根撞 id,roster 只会认先扫到的那一个——留下过期的副本会让“改了包内文件但模式没变”发生。check 会报出来,cleanup 会清掉。


文风契约

skills/writing-style-contract/SKILL.md 提供默认文风:克制、自然、具体。作者风格要求优先,不把所有作品改成同一种声音。

契约内附修改前后与保留示例,并展示同一问题在不同场景需要下的长短两类有效写法:删掉重复解释,也可以继续展开动作、感知和等待。默认约束仍然严格,例外必须有原文依据,不能只说“服务叙事”就放行;示例不作为仿写素材,不以缩短篇幅为统一目标。

修辞、心理、台词、动作和直接交代都按上下文判断;允许增写、删减或保留,不设最低删除百分比。 必要信息要在读者需要时可得;合理推断和有效留白可以保留,不要求所有专名首次出现就讲完背景。

视角检查交给审读员:区分叙述者、人物台词、内心引语与面向读者的称呼,指出具体的认知越界,不从人称字样下结论。

开发验证

npm test        # 单元与契约测试(Node 自带 test runner,无需依赖)
npm run verify  # 组合自检:把 agent.cordis.yml 当成 loader 那样读一遍

回归测试覆盖分场、引号统计、真实配额、用词线索、人物卡和卸载检查,并钉住审读流程的形态(角色各自成行、只读 toolFilter、B3/B4 的可观测触发条件、人设里不再夹带流程规则,以及 v1.2.0 的关系轴契约:面板 §4.0 的两个时机、B2 的两组必答项、“全可否认=零”判据、节拍表的推动者三列、“免确认”不能跳过关系轴;v1.2.1 的起点组:B2 的第零组、双方各一条注意起点、“只有叙述者说得出的理由=没有理由”、缺的起点补在开篇之前,以及“什么时候可以留白、为什么是这个人不能”)。测试只使用内存稿件和临时 DSH 主目录,不修改真实用户设置。 npm run check 与 CLI check 都是卸载残留检查,不是单元测试或完整安装验证。

npm run verify 补上单元测试碰不到的那一半:它用 loader 自己的解析器(js-yaml + entryListSchema)交叉验证组合,对每个 !!js 表达式做编译检查,用插件自己的 Config 校验每行 config,并把 toolFilter 点名的工具与插件源码里真正注册过的名字对账。它需要能读到 harness 已安装的插件(仓库本身零依赖),默认按桌面版路径查找,可用 DSH_HARNESS 指定。 两件它不能代替的事:真实会话里的工具清单,以及子代理真的能起来。装好后请开一个会话跑一次新写或片段,确认 subagent_review_b1 … subagent_review_b5 五个工具都在、派一次能拿到 started subagent <childId>。

为什么 !!js 里不能写 '\n'

!!js 的值在组合里是 YAML 双引号标量,YAML 会先处理转义:'\n' 在 loader 拿到源码之前就变成了真正的换行,于是 JS 里出现跨行的字符串字面量 → SyntaxError: Invalid or unexpected token。而 preset 的 mount 契约是“setup 里抛错就回滚整次 agent 创建”,所以表现不是报错,而是点新会话没反应;roster 里 broken 仍是 null,因为文件形状完全合法。v1.1.3 正是栽在 B4 那一行的换行拼接上(另外六个 !!js 不含转义,所以只有它炸)。

结论:需要换行就写 String.fromCharCode(10);需要字面反斜杠就用单引号或折叠标量(它们不处理转义)。npm run verify 现在会把这两类写法都拦下来——先做交叉验证比对两种解析结果,再对双引号标量里的反斜杠做预防性检查。


License

MIT

dsh-skill-filesystem
  • !!js 后面是折叠标量,整段代码会压成一行。 所以那段 JavaScript 里不能有 // 行注释(一个就吃掉后面全部),也不能靠自动分号插入。这两条都是实测踩出来的。

  • createRequire 的锚点必须按文件 URL 给。 给它一个不带尾斜杠的目录路径,Node 会把该目录当成文件解析,直接 MODULE_NOT_FOUND。

  • package.json 不能有 BOM。 带 BOM 会让 JSON.parse 失败,DSH 因此读不出 dsh.bundle 声明,dsh plugin add 不会把包加进 profile 的 bundles,patch 永远不生效——表现为“装好了但模式不出现”。这个坑本包也踩过一次,story_doctor 里有常驻检查(package.json 无 BOM 那一项)。

  • preset 行不能用 !!js 动态算路径。 发现阶段确实支持 !!js,但紧随其后的形状检查要求每行的 name 是字符串;!!js 解析出来是对象,整份组成会被判为 broken。

  • preset 行不能用裸包名(从 harness 解析,到不了用户目录),所以组成里用相对引用 ../../lib/index.js——以组合文件所在目录为基准,因此没有任何机器相关的绝对路径。

  • 升级包之后要确认模式真的换了。 模式住在包里、由 profile 的 patch 声明成 roster 的根,所以你改的是哪一份包决定模式的行为:从 GitHub 装的(dsh plugin --profile <name> add github:<作者>/dsh-story-mode)取的是远端默认分支,本地改动没推上去就不会生效。更隐蔽的一种:早期版本或插件市场留下过一份 profile 里的包快照,dsh plugin remove 之后不会自动删掉它——它可能还留在 node_modules 与插件目录里,于是“装了新版但模式没变”。判断办法是看模式里有没有 subagent_review_b1 这个工具;清理由市场或手动删除那份快照完成(本包的 cleanup 脚本只处理 home 下的技能副本与旧预设副本)。

  • 本包零运行时依赖(连 schemastery 都没有)。ESM 的解析基准是加载入口的父路径,所以插件若 import 任何 @deepseek-ai/* 包就必须自带 node_modules;它什么都不 import,于是任何布局下都能加载。改代码时不要引入裸导入,否则上面那条相对引用会失效。

  • v1.2.0 之前,这套流程有一个已实测的失效形态:五个角色全绿而主轴空心。 一次真实调用里,五个审读员对一篇 23,399 字的稿子出了 19 份报告(含两次全新读者盲读),逐条命中字形、雨季倒序、浮毛时令、椅子数量、扣子崩落、字数台账,最终盲读明确写「没有大结构问题」;作者随后在半小时内用七个连续发言拆掉了它——主人公在八场里零主动推动、另一方“想要”的露出全部可被解释成尽职、关键那一跳没有任何不可否认的依据。

    盲测对照(v1.2.0 补做):同一份 23,000 字稿、同一组派发变量、不带任何对话上下文的新子代理,唯一差别是 B2 的人设文件(标签随机化、判前不知对应):

    旧版人设 2.8 KB新版人设 8.7 KB
    报出关系轴缺口否是
    关键结论第 14 条把因果链判为**「已建立的理由与因果(通过)……越线不是凭空发生……逐级落到具体动作与位置」**「转折点之前没有一条不可否认的露出」「推动者分布失衡,被动方在读者眼里是空的」「承认是告知而不是揭示」
    逐条判定无(核的是“有没有铺垫”)第一组 34 条逐条带行号判定,定位缺口的场次与必要条件

    旧版把“逐级有铺垫”当成了通过项——它核的是“有没有铺垫”,不是“这些铺垫立不立得住”。这正是「一致性」与「充分性」的差别,也是这道缺口能在一整套流程里活下来的原因。

    留这一条在这里,是因为缺口的形状(自洽但空心)不会自己暴露,只会以“读者看不懂”的形式回来。

    另有一条人设本身的修订,也是盲测逼出来的:最初第一组的判据是“全部条目都可否认 → 报缺口”。第三轮盲测报了 34 条、其中 15 条判为不可否认,缺口却仍然成立——因为那 15 条集中在关系已经变化之后,转折点之前读者手里还是零。所以判据已补成“可否认总数不是结论,分布才是”:落在哪、是不是被逼近后的反应、有没有一件是“他单独为对方做的”。概括的数量判断会漏掉这一种。

  • v1.2.0 还漏了同一个方向的第二种形态:露出有据可引,起点没有。 一次真实调用里,一篇 17,900 字的师生短篇把关系轴跑了三轮(方案阶段一次、成稿后两次复核),B2 逐条列出 22 条露出、逐条判定、逐条给正文短引,推动者分布也数过、结论是“不失衡”;补拍之后缺口关掉,交付时验收状态是干净的。作者读完后仍然说这篇**“没有人味”**:两个人从什么时候开始不一样的、老师是怎么开始注意这个学生的,正文一场戏都没交代。

    形状:作者的定调是“学生主动进攻、老师屡次退守”,于是老师的朝前动作只落在转折点那一刻;而在转折点之前,正文给老师的两处来路是——去年十一月一节语文课上“忽然听不见学生的声音”,和五月里“他记得那个学生的气味……高二下学期还没有。到了冬天,那个味道变了。他没有理由去分辨它变成什么样,他分辨了。”学生的来路是唯一一句台词:“我画你的手,是去年十月开始的。”三处都是叙述者的句子,不是场上的事。 稿子处处自洽、前后对得上,读者却说不出这份想要是从哪儿长出来的——“他动过心”有据可引,答不了“他为什么会动心”。

    为什么会漏:关系轴的必答项管的是“要”(露出有哪些、可不可否认、谁推动),而这一篇的两个人的动心起点都在开篇之前——节拍表把“从老师知道学生的心思开始”当成了既定前提,规划阶段就没有为起点留位置,于是审读环节也没有东西可核。更直接的一条在护栏里:判据原文写着“允许留白他什么时候开始动心、为什么是这个人”,把“什么时候”和“为什么”并列放过了——但两者的代价完全不同:时刻可以留白,依据不能。 读者不是不能接受留白,是不能接受空白;没有依据时他只能自己替作者编一个,或者干脆不接。同一处还有一条可核对的信号:作者问的是**“从什么时候开始有异样”**,而这一篇里三个候选起点(去年十月/去年十一月/“高二下学期还没有,到了冬天变了”)互相之间没有换算关系,读者连“哪一个是起点”都推不出来。

    v1.2.1 的改法(都在规划与审读两层,不在工具层——正则写不出这类问题):B2 增加第零组“想要从哪儿来”,先于第一、二组做,对双方各要一条起点与“为什么只对这个人成立”,判据是硬的(只有叙述者说得出、任何一场戏都说不出的理由=没有理由;把主语换成另一个学生或另一位老师仍然成立的由来=没有不可替代性);人物卡为动心的双方各加一条注意起点(与“当下动机”分开——动机是现在要做什么,起点是这份要的来路);护栏改成“什么时候可以留白,为什么是这个人不能”;缺的起点补在开篇之前,或者把开篇提前,不靠“其实他早就……”这类补注。

    盲测对照(v1.2.1 补做,同一份 bible/outline、同一份派发 prompt、无上下文污染):两名选手互不相识,唯一变量是 B2 的人设文件版本:

    轮次旧人设(v1.2.0)新人设(v1.2.1)
    第 1 轮起点整节缺席;只在末段“第零组起点单列一句”里承认“起点整场落在开篇之前”,但没把它当缺口,四条补拍方向全落在中段新增第零组整节;“熊谷全篇零起点”列为第一大缺口;引到“我不知道他什么时候开始不一样的”;写出“需在开篇前补一场,或把开场提前”
    第 2 轮(确定性检索)需在开篇前补一场 0 次、把开场提前 0 次;且把学生侧的起点判成**“依据齐全,不报缺口”**需在开篇前补一场 1 次、把开场提前 1 次,三条判据全中;两位互不知情的评委会独立判 found=true / found=false
    终局(精简后的人设)全篇 起点/来路/开篇 0 次,只把它读成“露出分布不均”(“不是写得不够多,是写得全都在对方看不见的地方”)单列第零组:学生侧“起点落场且不可替代”;熊谷侧“没有起点,只写了由来”报缺口,并点出“去年十一月失手”这个锚换成另一个学生在场照样成立

    精简:第零组加进去之后人设是 5115 字符,压缩重复表述(开头与关系轴各自讲一遍“空心”、留白一处的三重表述、第一二组的冗词)后落到 4617 字符——比加组之前的 4673 更短,而新增的判据一条没丢。

    一条没达标的观察,留在这里:终局那次新人设对学生侧判的是“起点落场、不算缺口”。学生侧的依据确实可引(画本+去年十月),但**“为什么开始画”这一场仍然没落场**,正确读法应该是双方都报。所以判据“有一场戏,或者有一个可被引出的锚”在宽度上仍偏松——它放过了“锚引得到、但那次注意本身没有戏”的中间形态。下一轮该把这一档单独写死,或者接受“一侧可引、一侧零”也构成本组缺口。

    留这一条在这里,理由和上一条相同:自洽但空心不会自己暴露,它只会以“作者说没有味道”的形式回来,而那时稿子已经过了三轮审读、每一轮都验收通过。

    相关的两个次级因素,一并记下来:① 那次调用里方案没有被独立审读——五路审读全部派发于正文落地之后,而方案阶段本来可以一句话拦下它;② 主代理收到的子代理回报里,读者的思维链比报告正文还多(实测 91,204 字 vs 47,974 字,1.90 倍),注意力被“过程”占满。第 ① 条已由 §4.0 的方案阶段审读修掉;第 ② 条属于宿主回报格式,尚未解决。

    一条方法论备注,也是踩过的坑:盲测要用不带对话上下文的子代理。早期我用 subagent_fork 做新旧人设对照,两次都“报出了缺口”——但 fork 会把整段对话(包括我写的判定标准)一起继承给被测者,等于把答案先交给它。那组结果分辨力为零,已作废。上面的表是改用无上下文子代理(ralph 的 fresh child)重跑的。