# 变更记录 本文件记录每个发布版本的用户可见变更。发布工作流 ([`.github/workflows/release.yml`](./.github/workflows/release.yml))会调用 `scripts/release-notes.mjs`,把与 tag 对应的 `## [<版本>]` 小节抄进 GitHub Release 正文——所以**发版前先在这里写一节**,否则 Release 只会退化成提交列表。 ## [0.3.0] - 2026-09-29 **本版是功能 + 修复**:本插件在 **设置 → 插件** 里的那一行/卡片现在带上自己的图标与双语文案, 并修掉 `DSH Desktop 2.0.16 / dsh 0.2.0-rc.1` 上**设置页不出现**的故障。兼容范围不变: 需要 dsh `0.1.7` 起(实测 Desktop 2.0.16 / dsh `0.2.0-rc.1`),**仍不兼容 `0.1.5` / `0.1.6`**, 升级不需要改动任何配置。 ### 新增 - **插件页那一行/卡片带上本插件自己的图标与双语文案**:`package.json` 声明 `icon: './icon.svg'`(36×36、透明底,与官方插件 artwork 同一画法的渐变字形),并新增 `locale/en.json` / `locale/zh.json`,给同一行提供标题与说明(与卡片里那份文案同字)。host 的 `readPluginMeta` 在**不激活插件**的前提下读它们:`icon` 相对清单、SVG/PNG/JPEG/WebP、 ≤256 KiB、realpath 后仍在包内 → 转成 `data:` URL;文案走 `<包名>/locale/en.json` 及其同级 词典,所以清单补了 `./locale/*.json` 导出,`files` 也补上 `icon.svg` 与 `locale/*.json` (否则 `npm pack` 不带)。新增 `npm run verify:meta` 用装好的 reader 复核这两者;离线钉子见 `test/display-metadata.test.js`。 ### 修复 - **Desktop 2.0.16 上设置页消失**:profile 的 `nodeLinker: hoisted`,加上某个插件把 `@deepseek-ai/schemastery@3.18.2` 作为正式依赖带进 profile 根的 `node_modules`,本插件的 `createRequire` 就近解析到**没有 `.volatile()` 的**那一份,`Config` 因此建不出来 (`TypeError: z.array(...).default(...).volatile is not a function`,getter 的 `try` 把它记成 「解析不到」),`dsh-settings` 不服务该条目,插件页上就没有本插件那一项 —— 全程没有任何报错。 `lib/settings.js` 现在用 `volatile(field)` 落这个标记:有 `.volatile()` 就用它,否则用等价的 `.extra('volatile', true)`(两者写的是同一个 `meta.volatile`)。链路与实测证据见 [docs/compatibility.md §3.9](./docs/compatibility.md)。 - **`peerDependencies` 的范围按实测环境对齐**:`@deepseek-ai/dsh-settings` 改为 `>=0.1.7-rc.1 <0.3.0`(旧的 `<0.2.0` 上界只因为 DSH 启动前的兼容性检查带 `includePrerelease: true` 才对 `0.2.0-rc.1` 放行,`0.2.0` 正式版会被判成不兼容), `@deepseek-ai/schemastery` 改为 `>=3.18.2 <4.0.0`(profile 实际解析到的是 `3.18.2`)。 ### 验证 - 实测环境 **DSH Desktop 2.0.16 / dsh `0.2.0-rc.1`**:host 半侧照常工作(`~/.copilot` 的指令与 技能进本会话);`npm run verify:settings -- --schemastery /lib/index.mjs` 对真实 `3.18.2` 13/13 通过;`npm run verify:meta` 读出双语标题/说明与 739 字节的 `data:image/svg+xml` 图标(反例失败并退 1);`npm run check`:12 个文件的 `node --check` + 135 项 `node:test` 全绿。 - 还没实测:装好的 profile 里那张卡片与图标渲染出来的样子(要重装插件 + 重启 DSH),清单见 [docs/compatibility.md §4](./docs/compatibility.md)。 ## [0.2.1] - 2026-09-25 **变更记录修正版**:功能与 `0.2.0` 完全一致,装的还是同一个插件 —— 只是 `0.2.0` 的变更记录里 重复了一整段「注入消息 source 形状」的条目,Release 正文因此出现两遍同样的小节。 ### 修复 - **`[0.2.0]` 小节恢复为单一副本**,`0.2.0` 的 Release 正文同步修正。重复来自把修复提交 rebase 到 `main` 时,两边各自累积的 `[Unreleased]` 内容被并列保留,而不是并入同一节。 - **新增 `npm run verify:notes`**(`scripts/verify-release-notes.mjs`):把 tag 的 Release 正文 组装一遍,检查同一 `###` 标题是否出现两次、是否混入其他版本的小节、是否退化成提交列表 —— 这一类缺陷此前没有任何报错,只能靠人读正文发现。 ## [0.2.0] - 2026-09-25 **本次发布是首个 tag**:项目已改名为 `dsh-import-copilot-files`,并适配 DSH `0.1.7` (会话格式 v4);**不再兼容 dsh 0.1.5 / 0.1.6**。从旧包名升级前先移除旧包,否则同一份配置会注入两次; 旧命名空间下保存过的 `paths` 不再被读取,请在插件页里重新确认(迁移步骤见下面的「项目改名」一条)。 ### 修复:注入消息的 source 形状让整个 turn 失败(dsh 0.1.7 / 会话格式 v4) - **现象**:装上本插件并重启后,用户的第一句话就失败,界面只给 `本轮运行失败 format v4 message requires a producer-owned source kind`; 禁用插件即恢复正常。 - **根因**:注入消息的 `source` 仍是退役的 V3 包装 `{ kind: 'plugin', plugin: 'import-copilot-files', form: 'instructions' }`。会话格式 **v4** 起 durable message 的 source 必须 producer-owned,该形状会在**写入时**被 `assertV4RowAdmission` 拒绝(判据是 `kind` 非空且不等于字面量 `'plugin'`)。写入就是这一步自己的 `session.append('user/message', …)`,所以异常在插件 try/catch 之外抛出,整个 turn 无法持久化。 V3→V4 迁移会把**历史行**上的旧包装改写成 `plugin:<包名>`,但运行时现造的消息不经过迁移, 因此同一形状在 V3 会话里能活、在 V4 会话里必死。完整证据链与实测见 [docs/compatibility.md §3.8](./docs/compatibility.md)。 - **修复**:`source` 改为 `{ kind: 'import-copilot-files', form: 'instructions', changes }`。 `changes` 由 `withRemovals` 一并算出(消失的文件为 `action: 'remove'`,新出现的为 `'set'`), 因为 `InstructionsBody` 对 `changes` 是全有或全无的 —— 数组缺失会把注入行降级成不透明行。 产物形状(`id` / `role` / `content` / `source`)不变,`deep-freeze` 不变,注入顺序不变。 - **回归钉子**:`test/index.test.js` 断言 `kind` 非空且不等于 `'plugin'`、`plugin` 字段已消失、 `changes` 每项有合法 `action` 与 `path`,以及文件消失时确实产出 `remove`。 - **新增 `npm run verify:message`**:驱动真实插件对象,把注入消息过装好的 harness 的 `assertV4RowAdmission` 与 `restoreReleasedV4Artifact`,并用退役形状做反例 —— 离线用例看不到 harness 的格式代码,这一步补上。`npm run check` 里只做语法检查(该脚本需要 harness 检出)。 - **同一台机器上还有第二个生产者**:`dsh-approval-mode` 的审批模式播报也发 `{ kind: 'plugin', plugin: 'approval-mode' }`,报错文本与本插件完全一样,本仓库无法代它修复 (见 [docs/compatibility.md §3.8](./docs/compatibility.md))。 ### 修复:dsh 0.1.7 兼容(含破坏性变更) - **在 dsh `0.1.7` 上会把 DSH 拖进安全模式**(升级到 DSH Desktop 2.0.14 / dsh `0.1.7-rc.1` 后 一启用本插件,客户端启动就失败、DSH 落到恢复界面)。根因是旧实现把 `settingsScope` 放进了 **浏览器半侧的静态 `inject` 列表**:该服务在 `0.1.7` 改名成 `configForms`,于是这个 fiber 永远停在 pending,而 DSH Desktop 的 `rendererBootReport` 把任何非 ACTIVE 的条目算作 「插件加载失败」(`RendererStartupFailure`),启动流程随即进入安全模式。日志里只有一句 `renderer boot failed (plugins: dsh-import-copilot-files): The client Loader did not provide an error message.`。同一版本还移除了 `ctx.settings.installSection(...)`(host 侧)与 `settings.plugin.item`(客户端 slot),三处一起改;完整证据链与对照实验见 [docs/compatibility.md §3.6](./docs/compatibility.md)。 - 浏览器半侧的静态 `inject` 收敛为 `['slots', 'locale', 'configForms']`(host 侧仍是 `['skills']`);可选服务只走 `ctx.get(...)`,绝不再出现在静态列表里。 - 设置页从「插件配置」标签页的卡片改为**插件页**(`plugins.item`)里本插件的一格: 同一份注册同时服务 `view: 'summary'`(卡片上那行说明)与 `view: 'page'`(页面正文的 表单),页头/折叠/外框都交还给宿主;页面只保留行编辑器与页脚。 - 设置文档不再由插件注册 namespace,而是**插件自己的 `Config` schema**(`paths` 声明为 `.volatile()`),按 Loader 条目 id 寻址;host 半侧不再调用任何 Settings 服务方法, 改为监听 `loader/volatile-update` 让技能目录失效。 - `Config` 走 `createRequire` + `try` + 记忆化的 getter,所以**模块顶层依旧零依赖**: clone 下来照样 `npm run check`;解析不到 `@deepseek-ai/schemastery` 的部署丢的是设置页, 不是整个插件。 - `peerDependencies` 的 `@deepseek-ai/dsh-settings` 范围改为 `>=0.1.7-rc.1 <0.2.0` (旧范围按 semver 的预发布规则匹配不到 `0.1.7-rc.1`);**不再兼容 dsh 0.1.5 / 0.1.6**。 - `npm run verify:settings` 升级为拿**真实的 `dsh-settings`** 跑上游的 `describe()` / `update()`,并比对 `index.js` / `lib/client.js` / `cordis.patch.yml` 三处的条目 id; 离线用例另加一条「bundle 里不得再出现 `settingsScope` / `settings.plugin.item`」的回归钉子。 - 已知影响:旧的用户层小节名(`import-vscode-ai-files` / `import-copilot-files`)匹配不到 新条目 id,`0.1.7` 的迁移不会把它带过来,升级后 `paths` 会回到组合层默认值;手工写回的方法见 [docs/compatibility.md §3.7](./docs/compatibility.md)。 ### 新增 - **默认载入当前用户的 `[user]\.copilot` 目录**([#10](https://github.com/NEVSTOP-LAB/dsh-import-copilot-files/issues/10)): `paths` 的默认值从 `[]` 改为 `['~/.copilot']` —— 那是 Copilot CLI 自己的家目录,里面就是 `copilot-instructions.md` 与 `skills/`,于是每个会话自动带上那份用户级全局指令与技能,不必再手工 配一次。它仍然只是一个普通的 `paths` 条目:插件页里看得见、删得掉(保存成 `paths: []` 即关闭)、 改得动。组合层(`cordis.patch.yml`)与设置 schema 的默认值同步改动。 - **`paths` 条目支持 `~`**:以 `~` 开头(单独一个 `~`,或后跟 `/`、Windows 上 `\`)表示**用户主 目录**,`~/.copilot` 在任何机器、任何部署里都是同一段文字。只有开头那个 `~` 有这层含义 (`~name`、`a/~/b` 都是普通相对路径),环境变量与通配符依旧不展开。没有会话 cwd 时 `~` 条目 照样能定位,相对条目仍然跳过;发现与「目录失效判定」共用同一个解析函数,不会各写一份。 - **工作区之外的配置目录**([#2](https://github.com/NEVSTOP-LAB/dsh-import-copilot-files/issues/2)、 [#6](https://github.com/NEVSTOP-LAB/dsh-import-copilot-files/issues/6)): 新增配置字段 `paths`。该字段尚未随任何版本发布,所以没有已发布的旧配置需要迁移 (git 安装路径上用户层已经写下的旧命名空间小节见下面改名一条)。 每个条目是一个**配置目录**,等价于项目根里的 `.github`:`paths: [D:\shared-ai]` 读的是 `D:\shared-ai\copilot-instructions.md`、`D:\shared-ai\instructions\**` 与 `D:\shared-ai\skills\\SKILL.md`,它下面**不再有** `.github` 段;条目自身的走查也不会去 读它内部的 `.github` 树(若该目录同时落在 cwd 走查范围内,那棵树仍可能以**项目根的** `.github` 身份被读到 —— 那是 cwd 侧的规则,两侧互不影响)。路径可绝对可相对 cwd;不存在的 路径贡献为空;已经被扫过的配置目录不会被走第二遍;每个条目恰好是一个配置目录,因此 `scanSubdirectories` 不作用于它(它的子目录是内容,不是更多的配置目录)。通过 `paths` 扫到的 指令用**绝对路径**做标题(`..\..` 链说不清位置),`applyTo` 相对该条目自身匹配。 注:把一个**位于 cwd 走查范围之外**的仓库根改写成 `paths: [/.github]` 也能工作,但 `applyTo` 的锚点会从仓库根移到 `.github`;若那个仓库本来就在 cwd 走查范围内,它已经作为 项目根被读到,改名到 `paths` 只会被去重、不改变锚点。cwd 侧的扫描规则一字未动。 - **在「插件页」里配置路径**:插件现在导出一个 `Config` schema(组合配置作为它的 base 层), 并带上一个 browser half —— **设置 → 插件** 里本插件的那一项打开后,可以逐行增删路径, 并保存、放弃或恢复默认。 - 保存带草稿开始时的 revision,被并发改动抢先就拒绝而不是覆盖;保存后以宿主回读的值确认。 - 保存会立即让技能目录失效 —— 设置写入不碰文件系统,否则新路径下的技能要等到下一次 无关的文件观察才会进目录。 - 「放弃」只丢弃未保存的草稿;清掉已存储的用户覆盖用「恢复默认」(字段被覆盖时才出现)。 - 落点是 DSH 自己的 profile 配置层(`~/.dsh/profiles//cordis.patch.yml` 里本条目 的 `config`),工作区文件一个不写;改完从下一个模型步骤起生效。 - 页面只覆盖 `paths`;其余字段仍只在组合配置里(故意不标 volatile,页面看不到它们)。 - `Config` 解析不到(没有 schemastery)或宿主不提供设置表单时,插件照常按组合配置运行, 只是没有这一页。 ### 变更 - **卡片标题改为「导入 Copilot 文件」**([#5](https://github.com/NEVSTOP-LAB/dsh-import-copilot-files/issues/5)): 英文界面为 `Import Copilot Files`;卡片的说明改为「把工作区以外的 VSCode AI 配置也加载进来」, 提示改成「每行是一个配置目录,等价于项目里的 `.github`」,不再说「项目根」。 - **项目改名为 `dsh-import-copilot-files`**([#12](https://github.com/NEVSTOP-LAB/dsh-import-copilot-files/issues/12)): npm 包名与 GitHub 仓库名改为 `dsh-import-copilot-files`;插件 id(`source.plugin`,即 GUI 注入行旁边的来源标签)、设置命名空间、技能 provider 的 `source` 标签与卡片标题一并改名 —— 插件 id 与设置命名空间由 `import-vscode-ai-files` 改为 `import-copilot-files`, `source` 标签由 `project-vscode` 改为 `project-copilot`,卡片标题由「导入 VSCode AI 文件」 改为「导入 Copilot 文件」(英文界面 `Import Copilot Files`),注入行旁会显示成 `import-copilot-files`。README 与文档里的安装命令、tarball 名同步改名;卡片样式类前缀 `dsh-ivaf-*`(旧名的缩写)改为 `dsh-icf-*`,测试用的临时目录前缀改为 `copilot-ai-config-*`。 **从旧名升级**(三步): 1. 已经装过旧包名的 profile(其 `package.json` 里记的是 `dsh-import-vscode-ai-files`)要先 移除旧包再装新名,否则两条组合行会让同一份配置**注入两次**: `dsh plugin --profile

remove dsh-import-vscode-ai-files`,再 `dsh plugin --profile

add github:NEVSTOP-LAB/dsh-import-copilot-files`。 2. 旧命名空间下保存过的 `paths` **不再被读取**:`0.1.7` 起设置文档就是本条目自己的 `config`(按 Loader 条目 id 寻址),不再有单独的命名空间。升级后请在插件页里重新确认一次 `paths`,或按 [docs/compatibility.md §3.7](./docs/compatibility.md) 把它写进 profile 的 patch 层。这份覆盖来自 git 安装路径,不是任何已发布版本的产物 —— `0.1.0` 是改名前的形态。 3. 若在 profile 的 patch 层按旧行 `id` 覆盖过本插件的 `config`,那份覆盖同样随行改名失效, 需要改到新行上。 - **文档按读者分层**([#9](https://github.com/NEVSTOP-LAB/dsh-import-copilot-files/issues/9)): README(中英两份同结构)只留用户需要的信息 —— 功能、扫描范围、会话里看到什么、配置与插件页、 安装;CONTRIBUTING 只留协作流程与文档地图;技术细节移进 `docs/`:`doc/design.md` 迁到 `docs/design.md`(架构与机制、源码结构、已知边界),并新增 `docs/development.md`(本地检查、 测试驱动、端到端与卡片的手工验证、打包发版)、`docs/compatibility.md`(依赖面、DSH 接缝、 升级校验清单、验证记录与未实测清单)、`docs/pitfalls.md`(开发坑:跨平台与测试、插件行为、 Windows 上的 git/gh)。 - **插件页里的设置界面**:页面只画行编辑器(每行一个路径 + 「浏览…」「删除」)与页脚 (「放弃 / 保存」),字段被覆盖时行内给「已覆盖」标记与「恢复默认」,有未保存的草稿时给 「未保存」标记。配色、圆角与边框全部走同一套设计 token;卡片外框、页头与折叠交还给宿主 (`0.1.7` 的形态,见上面的修复条目)。 ### 修复 - **安装时那条 peer 依赖警告不是本插件造成的**([#8](https://github.com/NEVSTOP-LAB/dsh-import-copilot-files/issues/8)): `dsh plugin add` 原样转发 pnpm 的输出,而 profile 里 `@xxxyz/dsh-mcp-manager`、`dsh-approval-mode`、 `dsh-context`、`dshmarket` 都漏声明了 peer,于是装什么都会看到 `Issues with peer dependencies found`。本插件补上宿主契约(`@deepseek-ai/cordis`、 `@deepseek-ai/dsh-settings`、`@deepseek-ai/schemastery`、`react`)并**全部标为可选** (`peerDependenciesMeta.optional`),所以它既不缺 peer、也不会被报出来;README 里写明怎么用 `pnpm peers check` 找出真正的来源。自检证据:在本插件的 lockfile 上跑 `pnpm peers check --lockfile-only --json` 得到 `missing: {}` 且退 0,而同一个 profile 上跑它列出的 缺项全是上面那几个包。 - **配置目录里的改动会立即让技能目录失效**:失效触发原先只认路径里的 `/.github/`,而 `paths` 条目自身没有这一段,于是改 `<路径>/skills/**/SKILL.md` 不会刷新技能目录(要等下一次无关的 文件观察)。现在命中 `.github` 树、或落在任一 `paths` 条目之下(按会话 cwd 解析、比较时按 分隔符边界、Windows 大小写不敏感)都会失效。 - **两条只在 Windows 上失败的断言**:`test/discover.test.js` 里把 POSIX 语义的期望值写死在了 跨平台用例里 —— `path.isAbsolute('\\\\server')` 在 win32 上为真、`path.resolve` 还会给 `\\\\server\\share` 补上尾部分隔符,于是本机红、CI(ubuntu)绿。现在期望值按 `process.platform` 决定,见 [docs/pitfalls.md §1](./docs/pitfalls.md)。 - **「浏览…」不再点了没反应**:DSH Desktop 在 Windows 上禁用自适应选择器、改挂 `browse` 后端,而 `browse` 没有 `pick` 能力 —— `uiWorkspace.pickDirectory()` 在那里必然被拒, 旧的 `void browse().then(…)` 会把这次 rejection 丢进控制台,按钮看上去是坏的。 现在按部署能用的路由取目录:DSH Desktop 窗口用它自己的 Windows 选择框 (`window.__DSH_DESKTOP_PICK_DIRECTORY__`),其余组合走宿主的原生选择器;两条路由都不存在的 部署不显示「浏览…」按钮(路径手填),路由存在但这次选择被拒时卡片给出提示。 - **注入行的名字写错了**:客户端给这类消息固定的行标题是「上下文注入」(英文界面 `Context injection`),跨会话召回那条是「跨会话召回」(`Session recall`),行内的来源标签 才是 `source.plugin`;`source.form` 决定的是正文形态,正文渲染在 `dsh-client-ui-chat` 里 (`dsh-client-ui-trajectory` 另有自己的 `kind.context` 标签)。README 与 `docs/` 里原先按 「指令注入」表述,现按 Desktop 2.0.13 的实现改正。 - **文档迁移丢掉的内容已补回**:peer 警告里「缺的条目属于那些包自己」与 `pnpm peers check` 的运行目录、`paths` 条目同时落在 cwd 走查内时的 `.github` 归属说明、卡片的形态描述、 以及 README「更多文档」里的 CONTRIBUTING 链接。 - **`docs/compatibility.md` 里的测试条数**:文档迁移时写成 133,实测为 126 (glob 9 / frontmatter 9 / discover 37 / 插件 43 / 设置 10 / 客户端 18)。 ### 设计取舍 - **`peerDependencies` 不再为空**:宿主契约(cordis、`dsh-settings`、`schemastery`、`react`)如实 声明,但**四个全是可选** —— 这个插件在 profile 里本来就能缺任何一个(缺 `schemastery` 只丢那张 卡片,缺 `settings` 服务只丢卡片、其余照跑),而 optional 的 peer 不会进 pnpm 的 peer 问题清单, 所以声明它不会给别人的安装添警告。加载期依赖依旧是零:`node:` 内置模块 + 一处惰性动态 import。 - **schema 必须是真 schemastery,且惰性加载**:浏览器要靠 `schema.toJSON()` 的 `{ uid, refs }` 信封重建 schema 才能渲染表单,手写的「形状像」的对象会让卡片静默地拿不到 值。为保住「clone 下来零依赖即可 `npm run check`」这条性质,`@deepseek-ai/schemastery` 只以惰性动态 import 出现在 `index.js` 一处,且只在 `settings` 服务存在时执行。 - **卡片是手写的 lazy-CJS bundle**,不引入构建步骤——与第三方插件(如 `dsh-git-rollback`) 的做法一致。 - **不处理 AGENTS.md**:它属于 DSH 核心的 `dsh-agent-instructions`(按 project root → cwd 的 祖先链读取),所以只在当前工作目录这条链上生效;`paths` 与子目录根都不贡献 AGENTS.md。 这条边界由负例测试钉住(配置目录里放一个 AGENTS.md,注入文本里不得出现它), 避免以后有人「顺手」把它也扫进来。 - **配置目录是唯一的扫描单位**:cwd 走查产出的是「项目根 + 它的 `.github`」,`paths` 条目 产出的就是它自己。两者只在 `rootDir`(`applyTo` 的匹配锚点)上分叉,其余解析共用一条 代码路径,所以 `.github` 语义不会在两侧漂移。 ### 验证 - `npm run check`:9 个文件的 `node --check` + 126 项 `node:test` 全绿 (glob 9 / frontmatter 9 / discover 37 / 插件 43 / 设置 10 / 客户端 bundle 18)。 插件那 43 项里包含一条**端到端**:设置服务给出的 `paths` 真的进了发现流程(注入与技能目录), 以及 schema 装载失败时组合配置继续生效。客户端那 18 项跑的是**真实的 `lib/client.js`**: 按客户端模块系统的方式执行 bundle,再驱动 `apply(ctx)` 与卡片组件,覆盖注册 key、折叠/展开、 暂存、保存(revision + 回读确认)、恢复默认(`unset`)、只读态、两条目录选择路由、 选择被拒时的提示与取消时不动草稿、样式安装/卸载、卡片标题就是「导入 Copilot 文件」, 以及卡片提示里写了 `~` 的用法。 - **`~` 与默认条目的测试是自洽的**:`apply` 的 `options.homeDir` 与 `discover` 的 `homeDir` 让用例 把 home 指到一个临时目录(`test/index.test.js` 的 `mount` 默认指到一个**不存在**的目录),所以 断言不依赖跑测试的那台机器真的有没有 `~/.copilot`。新增的十条覆盖:`~` 与 `~/x` 落点、 裸 `~`、`~name` 与 `a/~/b` 不展开、Windows 上 `~\x` 与 `~/x` 等价、不给 `homeDir` 时用 `os.homedir()`、`~` 条目在没有会话 cwd 时仍可定位(相对条目仍被拒),以及默认条目真的进了注入与 技能目录、显式 `paths: []` 关得掉它、`~/.copilot` 下的改动会让技能目录失效。 - **配置目录语义的负例**都在 `test/discover.test.js` 里:配置目录内部的 `.github` 树 (copilot / instructions / skills 三样)一律不被采纳 —— 包括 `instructionDirs` 取 `'.'`、`'.github'`、`'./.github'`、`'x/.github/y'`、`''`、`'/'` 这些退化写法;它的子目录不被 当作更多的配置目录(`sub-dir/copilot-instructions.md` 与 `sub-dir/.github/**` 各放一份负例, 只放 `.github` 那种看不见「把子目录当配置目录」的重构);`paths` 指向的目录里的 `AGENTS.md` 不被采纳;两个扫描单位落到同一个源文件时只出现一次(`paths: ['.']` + 自定义目录名)。 Windows 上 `.github\instructions` 与 `.github/instructions` 等价。插件侧再补三条: 「配置目录里的改动会让技能目录失效」、大小写不同的同一路径同样会失效,以及会话还没有 cwd 时 相对路径的条目不会被错误地解析(绝对路径仍然照常生效)。 - `npm run verify:settings`:拿真实 `@deepseek-ai/schemastery`(当时本机 Desktop 2.0.11; 2026-09-20 在 Desktop 2.0.13 上复跑仍 9/9) 把设置链走一遍 9/9 —— 解析组合配置与用户层、拒绝非法写入、`toJSON()` 信封、 **从信封重建并校验**(浏览器渲染卡片走的就是这一步),以及两半的 namespace 是同一个字符串。 该命令在没有安装 DSH 的环境下跳过并退 0,所以不进 `npm run check`;它现在从 `index.js` 导入 `SETTINGS_DEFAULTS`,默认值不再有两份。 - 设置这条链(`installSection` 签名与 hooks、namespace 文法、卡片按 namespace 派发、 `dsh.client` 的解析规则与 bundle 缺失时的失败方式、scope 的 `bind`/`mutate` 形状) **读实现**逐条核对;本机已挂载 `dsh-settings-file`、`$DSH_HOME/settings.yaml` 可写。 - 目录选择的两条路由**读实现**核对:win32 的 DSH Desktop profile 禁用 `dsh-host-directory-picker-auto` 并改挂 `browse` 后端,`browse` 没有 `pick`,Remote 控制器 按 `requireCapability('native', 'pick')` 答 `directory-picker/unavailable`; `window.__DSH_DESKTOP_PICK_DIRECTORY__` 只在 win32 页面上安装。 - **还没实测**:卡片在真实 GUI 里出现、保存落盘与生效、「恢复默认」回到组合配置、 以及修好后的「浏览…」在 DSH Desktop 窗口里真的弹出选择框;新的默认条目在真实会话里的注入也要 重装插件并重启 DSH 之后才看得见(本轮只验证到发现流程:默认值在本机解析成 `C:\Users\nevstop\.copilot`,读到该目录下的 `copilot-instructions.md` 与 `skills/`、无告警)。 清单与手工步骤见 [docs/development.md §2.3](./docs/development.md) 与 [docs/compatibility.md §4](./docs/compatibility.md)。 ## [0.1.0] - 2026-09-18 ### 新增 - **首次发布**:把工作区自带的 VSCode / Copilot 风格 AI 配置加载进每一个 DSH 会话。 - `.github/copilot-instructions.md` 常驻注入。 - `.github/instructions/**/*.instructions.md` 按 `applyTo` 作用域注入:没有 `applyTo` 的常驻;有 `applyTo` 的,只在本会话真的碰过匹配文件之后才注入。匹配相对该文件所属的 项目根,支持 `**`、`*`、`?`、`{a,b}`、`[abc]` 与逗号分隔的多模式。 - `.github/skills//SKILL.md` 注册为 DSH 技能,`disable-model-invocation` 与 `user-invocable` 与原生语义一致。 - **扫描范围**:会话 cwd 本身,加上它下面 `scanSubdirectories` 层(默认 1)的直接子目录, 各取自己的 `.github/`;`.github/instructions/` 内部递归到深度 4。 - **独立可辨认的注入行**:指令经 `agent/pre-step` 作为一条带 `source = { kind: 'plugin', plugin: 'import-vscode-ai-files', form: 'instructions' }` 的 user 消息注入,因此在 GUI 里显示为与 AGENTS.md 同级的一条独立上下文注入行 (来源标签是该插件名),而不是折叠进 `@deepseek-ai/dsh-system-prompt` 的那一行里。 - **顺序固定**:AGENTS.md 在前,`.github` 指令在后(见 [docs/pitfalls.md](./docs/pitfalls.md) 的注入顺序一条)。 - **内容变化即追加**:渲染结果变化时追加一条新注入;文件消失时先给一条 `Instructions removed:`,不静默丢弃。 - **字节预算**:`maxBytes`(默认 65536)先省略、再截断,并在正文里说明丢了多少; 渲染结果**绝不超出**该预算(含提示文本自身的预留)。 ### 设计取舍 - **host 平面**而非 agent preset 平面:插件不发布任何 service,host 平面注册即全局层, 对每个 preset、每个会话生效。preset 平面不可行——`@deepseek-ai/dsh-tool-cordis` 无条件注册进程级 Host Inspect provider,两个含它的 preset 无法在同一进程共存。 - **零第三方依赖**:profile 本地插件向上找不到 harness 自己的 `node_modules`, 因此 frontmatter 与 glob 都是自己写的极小实现,只用 `node:` 内置模块, `peerDependencies` 为空。 ### 验证 - `npm run check`:6 个文件的 `node --check` + 58 项 `node:test` 全绿 (glob 9 / frontmatter 9 / discover 13 / 插件 27,其中插件部分对着假 Cordis 上下文 驱动真实的 `agent/pre-step` 瀑布、skill provider 与 `fs/observed` 监听器)。 - 三条接缝在真实运行时逐条实测:`agent/pre-step` 注入的 `form: 'instructions'` 消息确实 到达模型并被 GUI 标成独立注入行;`skills.list/get` 对真实工作区返回正确的策略与正文; `fs/observed` 的 `actor` 携带 `.agent`,可按会话分桶。 - 端到端:在一个真实工作区验证了 5 份指令文件的注入、`applyTo` 的负例(未命中不注入)、 正文改动下一步生效、以及技能进目录与被 `/name` 调用。