[ { "version": "0.9.2", "date": "2026-10-11", "zh": { "title": "修掉四处会误导模型的问题(源自一次真实插件开发会话)", "items": [ "**e2e 这一档不再只像个脚手架**:会话里 agent 在项目已有套件的情况下跑了 **61 次 `pnpm run e2e`**,却一次都没调用 `obsidian_plugin_e2e`。原因是工具描述把自己定位成「生成器」,`status` 只是个附属参数——而**漂移检测其实早就在里面**(能精确指出旧配置缺少隐藏窗口的 before 钩子)。那份配置生成于 0.8.3 之前,于是每次运行都在屏幕上闪一次 Obsidian 窗口。现在描述明确要求「项目已有套件时先跑 `action=status`」,并说清陈旧配置**不会报错、只会静默少做一件事**。", "**构建超时不再甩锅给 esbuild,且必须报出耗时**:会话里 `obsidian_plugin_build` 报了 300s 超时,而 agent 手动跑同一条命令只用 **1.694s**。原报错是 `esbuild step failed (timeout)` —— 对走项目脚本的项目这归因是错的(实际是 `tsc && node esbuild.config.mjs` 整条管道),而且一个耗时数字都没有,读者无法区分「刚过线」与「挂死」。现在会报出「项目自身的 build 脚本未在 300s 内完成,实际执行 300s 后被终止」,点名失败的是脚本(脚本层)或 esbuild 直调(直调层),并说明整条管道是作为一步计时的。", "**修掉一句被截断的门面文案**:`obsidian_plugin_test` 的输出里有一句断在 `Rendering, events and`。这句恰好是**限制 PASS 含义**的那句——前半句刚说「证明了视图插件能构建」,限制却断在半空,容易被读成渲染也被验证了。现已补全并强化为「构造成功只证明它能构建,不证明它能工作」。", "**App 未运行时 `inspect` 给出一条结论与下一步**:原本会堆四条重复警告(每条都复述 CLI 的原话),把真正的事实淹掉。现在识别为「Obsidian 未运行」这一独立结局,说明 L3 档需要用户自己的 App 在跑且不会代启,并给出两条可选路径——启动 Obsidian,或改用不需要用户实例的沙箱档。", "**被跳过的检查不再读作通过**:会话里 lint 每次都超 180s 被跳过,结果仍以 `OK` 结尾,仅有一行 warning,与「lint 干净」难以区分。现在标为 `NOT RUN`,说明因此**没有**运行 eslint-plugin-obsidianmd 检查,并界定该 OK 只覆盖 manifest/版本一致性。", "验证:82 项测试通过(新增 4 项直接来自该会话的回归测试,含用假 CLI 可执行文件驱动真实 spawn 路径、用会卡住的构建脚本驱动超时路径);并对用户那份真实项目只读复算,`status` 精确报出缺失的 hide 钩子,lint 跳过时输出新的 NOT RUN 文案。" ] }, "en": { "title": "Four messages that misled the model, fixed (from a real plugin-development session)", "items": [ "**The e2e tier no longer reads as a scaffolder only**: in the session the agent ran `pnpm run e2e` **61 times** on a project that already had a suite, and never once called `obsidian_plugin_e2e` — because the description positioned it as a generator with `status` as an incidental parameter. The drift detection was already there (it names the missing window-hiding `before` hook precisely), but that config predated 0.8.3, so every run flashed an Obsidian window on screen. The description now says to run `action=status` first on a project that has a suite, and that a stale config **does not fail — it silently stops doing something**.", "**A build timeout no longer blames esbuild, and reports its timing**: the session showed `obsidian_plugin_build` hitting a 300s timeout while the agent's own run of the same command took **1.694s**. The old message was `esbuild step failed (timeout)`, which misattributes for the project-script tier (it is really `tsc && node esbuild.config.mjs`), and carried no duration, leaving no way to tell \"just over the line\" from \"hung forever\". It now reports that the project's own `build` script did not finish within 300s and was killed after 300s, names the failing thing per tier, and says the pipeline is timed as one step.", "**A truncated sentence in the smoke report**: `obsidian_plugin_test` printed a sentence ending at `Rendering, events and`. That sentence is the one that limits what a PASS means — the clause before it had just said view plugins were proven constructible, and the limitation stopped mid-air, reading as though rendering had been verified too. It is now complete and states that a constructed view proves it builds, not that it works.", "**`inspect` states \"not running\" once, with a next step**: it used to emit four warnings that each repeated the CLI's own sentence, drowning the one fact that mattered. That is now its own outcome: it says the L3 tier needs the user's app running and will not start one, and offers two paths — start Obsidian, or use the sandboxed tier that needs no instance of theirs.", "**A skipped check no longer reads as a pass**: in the session lint exceeded its 180s budget on every run and was skipped, yet the result still ended in `OK` with a single warning line — barely distinguishable from a clean lint. It is now labelled `NOT RUN`, says the eslint-plugin-obsidianmd checks therefore did **not** run, and scopes the OK to manifest/version consistency.", "Verified: 82 tests pass (four new regressions taken straight from that session, including one driving the real spawn path through a fake CLI executable and one driving the timeout path with a build script that never finishes); and re-running read-only against the real project shows `status` naming the missing hide hook and the new NOT RUN wording when lint is skipped." ] } }, { "version": "0.9.1", "date": "2026-10-09", "zh": { "title": "部署校验不再对「被跳过的插件」误报成功", "items": [ "**修掉一个会让本次事故长期潜伏的验证漏洞**:`scripts/deploy.sh` 原先用 `dsh --dump-config | grep obsidian-plugin` 判定部署成功。但插件因 peer 不兼容被运行时跳过时,告警文本里恰好包含包名,且 profile 的 bundles 列表里也一直有它——于是**一个从未被加载的插件会一直报「部署成功」**。这正是 0.8.6 在 0.2.0-rc.2 上被跳过却无人察觉的原因。", "检查改为两件独立的事:组合结果里存在该入口的**行**(`- id: obsidian-plugin`,而不是包名子串),以及运行时没有输出跳过告警。判定抽到 `scripts/verify-composed.mjs`,因此可以单测——新增 3 项测试分别覆盖「正常组合」「已组合但被跳过」「完全缺席」,其中「被跳过」用例特意同时提供组合行与跳过告警,确保不会被误判为成功。", "**技能注册失败不再静默**:`registerSkill` 的 catch 原本吞掉一切异常。skills 服务存在而注册失败(例如名字不符运行时的 `SKILL_NAME` 模式)会让技能悄悄从模型目录里消失,因此改为记一条 warning;新增测试覆盖「合法形状注册成功」与「失败会被报告」。", "**文档同步**:`link-dsh-deps.mjs` 的来源已改为「运行时真正加载的 dsh 安装」并带版本自检,文件树与 `pnpm run deploy` 的说明随之更新(原文写的是 `$DSH_HOME/profiles/node_modules`、以及「校验输出包含 obsidian-plugin」)。", "验证:78 项测试通过;在真实 profile 上跑完整 `pnpm run deploy` 退出码 0 且判定正确;负向用例(peer 范围改回 ^0.1.5-rc.2)确认会报 FAIL 而不是 OK。" ] }, "en": { "title": "The deploy check no longer reports success for a plugin that is being skipped", "items": [ "**Closed the verification hole that let this incident stay hidden**: `scripts/deploy.sh` decided success with `dsh --dump-config | grep obsidian-plugin`. When a plugin is skipped for an incompatible peer range, the warning text contains the package name and the profile's bundle list contains it permanently — so **a plugin that never loads reports as successfully deployed**. That is exactly how 0.8.6 stayed unnoticed while being skipped on 0.2.0-rc.2.", "The check now verifies two independent facts: the composed tree contains a ROW for the entry (`- id: obsidian-plugin`, not a name substring), and the runtime printed no skip warning. The decision moved into `scripts/verify-composed.mjs` so it can be tested — three new tests cover composed, composed-but-skipped, and absent. The skipped case deliberately supplies both a composed row and the skip warning, so a presence-only check cannot pass it.", "**A skill registration failure is no longer silent**: `registerSkill`'s catch swallowed everything. With the skills service present but registration failing (for instance a name that misses the runtime's `SKILL_NAME` pattern), the skill would quietly vanish from the model's catalog, so it now logs a warning; tests cover both a valid registration and a reported failure.", "**Docs synced**: `link-dsh-deps.mjs` now links from the dsh installation the runtime actually loads (with a version self-check), so the file tree and the `pnpm run deploy` description were updated (they still named `$DSH_HOME/profiles/node_modules` and \"verify the output contains obsidian-plugin\").", "Verified: 78 tests pass; a full `pnpm run deploy` against the real profile exits 0 with a correct verdict; and the negative case (peer range set back to ^0.1.5-rc.2) fails with FAIL rather than OK." ] } }, { "version": "0.9.0", "date": "2026-10-09", "zh": { "title": "适配 DeepSeek Harness 0.2.0-rc.2", "items": [ "**插件此前在 0.2.0-rc.2 上完全不加载**:peer 范围仍是 `@deepseek-ai/dsh-tools` ^0.1.5-rc.2,DSH 在组合阶段直接跳过它(`skipping profile bundle … is incompatible with dsh 0.2.0-rc.2`),任何 `obsidian_plugin_*` 工具都调不到。范围已更新为 `@deepseek-ai/dsh-tools` ^0.2.0-rc.2、`@deepseek-ai/cordis` ^4.0.4、`@deepseek-ai/schemastery` ^3.18.4;实测组合结果里已出现 `id: obsidian-plugin` 且无任何兼容性告警。注意这是**加载闸门**:版本不符不会「部分可用」,而是根本不被挂载。", "**移除对 `ctx.fs.mkdir` 的调用**:沙箱化文件系统后端**没有** `mkdir`(基类契约与后端原型均已核对),我上一版为「脚手架开箱即跑」新增的 `Fs.mkdir` 会在真实部署上抛 `TypeError`。改为写文件时由后端自建父目录链;标记文件也随之移到 `.obsidian/.gitkeep`——因为写入只创建自己的父目录链,写在库根目录的话 `.obsidian` 仍不会被创建,而 service 要的正是它。同时校验 `writeText` → `writeFileAtomic` → `mkdir(dirname, {recursive:true})` 确实建父目录。", "**修正开发期类型链接**:`link-dsh-deps.mjs` 改为从**实际运行 dsh 的安装目录**解析 `@deepseek-ai/*`(此前指向 profile 目录,pnpm 装的旧副本会掩盖真实不兼容),并在链接后打印各包版本、按运行时的同一套判定(只查 `@deepseek-ai/dsh` 与 `dsh-*`,且 `satisfies(..., { includePrerelease: true })`)自检 peer 范围,不满足即失败——避免「类型检查在旧类型上通过、运行时却被跳过」再次静默发生。", "**新增回归测试**:用屏蔽了 `mkdir` 的后端代理驱动脚手架,确保「无 mkdir 也能建出 `.obsidian` 库」这一点被钉住(共 73 项测试通过)。", "验证方式:①复现 DSH 的 `evaluatePluginCompatibility` 判定逻辑,对我们全部 peer 逐字求值;②用真实 cordis 4.0.4 + dsh-tools 0.2.0-rc.2 加载构建产物,确认 11 个工具全部注册且形状完整;③真实执行 `obsidian_plugin_validate` 走通 execute 路径;④`dsh --profile web --dump-config` 显示插件已被组合、无兼容性告警。" ] }, "en": { "title": "Adapted to DeepSeek Harness 0.2.0-rc.2", "items": [ "**The plugin did not load at all on 0.2.0-rc.2**: its peer range was still `@deepseek-ai/dsh-tools` ^0.1.5-rc.2, so DSH skipped it during composition (`skipping profile bundle … is incompatible with dsh 0.2.0-rc.2`) and no `obsidian_plugin_*` tool was reachable. The ranges are now `@deepseek-ai/dsh-tools` ^0.2.0-rc.2, `@deepseek-ai/cordis` ^4.0.4 and `@deepseek-ai/schemastery` ^3.18.4, and the composed config now lists `id: obsidian-plugin` with no compatibility warning. Note this is a LOAD gate: a mismatched range does not leave the plugin partly working — it is not mounted at all.", "**Removed the `ctx.fs.mkdir` call**: the sandboxed filesystem backend has no `mkdir` (checked against both the base contract and the backend's prototype), so the `Fs.mkdir` helper added in the previous release for \"the scaffold runs as generated\" would throw `TypeError` on the real deployment. Directory creation now happens through writing a file, since every backend creates missing parents; the marker file moved to `.obsidian/.gitkeep` for the same reason — a write creates only its own parent chain, so a marker at the vault root would still leave `.obsidian` missing, which is exactly the directory the service requires. Verified that `writeText` → `writeFileAtomic` → `mkdir(dirname, {recursive:true})` does create parents.", "**Fixed the development-time type linking**: `link-dsh-deps.mjs` now resolves `@deepseek-ai/*` from the installation that actually runs `dsh` (it previously pointed at the profile directory, where a stale pnpm-installed copy hid real incompatibilities), prints the linked versions, and self-checks every peer range with the runtime's own rule (only `@deepseek-ai/dsh` and `dsh-*`, satisfied with `{ includePrerelease: true }`), failing when a range does not hold — so \"type-checks against old types while the runtime skips the plugin\" cannot happen silently again.", "**New regression test**: the scaffold is driven through a backend proxy with `mkdir` masked out, pinning that a vault with `.obsidian` is still created without it (73 tests pass).", "How this was verified: (1) DSH's own `evaluatePluginCompatibility` logic was reproduced and evaluated verbatim against every peer; (2) the build output was loaded in a real cordis 4.0.4 + dsh-tools 0.2.0-rc.2 context, confirming all 11 tools register with complete shapes; (3) `obsidian_plugin_validate` was executed for real through its `execute` path; (4) `dsh --profile web --dump-config` shows the plugin composed with no compatibility warning." ] } }, { "version": "0.8.6", "date": "2026-09-23", "zh": { "title": "清理:错误设置、不当实现、过时与冗余说明", "items": [ "**补上被文档承诺却不存在的写入门禁**:六份文档都写「工具会拒绝对非测试库执行 app 侧写入」,但代码里没有任何库身份检查。`plugin:enable` / `plugin:disable` / `unrestrict` 由 Obsidian 自己写盘,沙箱看不见,因此只能由我们自己拦:`obsidian_plugin_reload` 现在在**执行任何命令前**校验目标库路径是否位于会话工作区内,工作区外一律拒绝并给出三条可选路径(用工作区内测试库 / 改走 L4 沙箱 / 你自己去执行)。仅以「当前前台那个库」指代的目标无法定位,文档已如实说明这一点。", "**修掉 E2E 的假参数**:`dir` 只把 spec 写到自定义目录,而生成的配置把 `specs` 写死为 `./e2e/specs/**`,于是自定义目录的 spec 永远不会被收集。现在该 glob 随 `dir` 渲染,`dir` 真的生效。", "**修掉离线冒烟的 data.json 缺陷**:stub 的 `Plugin` 构造函数把 `_stubData` 作为实例自有属性赋值,遮蔽了 harness 播种的原型值,导致 `loadData()` 恒为 `undefined`(真实 App 会返回 data.json 内容)——凡是在 onload 里依据持久化数据分支的插件都会被走到错误分支。改为由 `createObsidianStub({ pluginData })` 按实例显式播种,不再改动共享原型(同一进程内多个 stub 也不再互相污染)。", "**修掉 stub 的 `registerMarkdownPostProcessor`**:同一方法定义两次,生效的那处把 `{processor, sortOrder}` 对象推进 `markdownPostProcessors`,而该数组按 API 应存放可调用函数;已合并为一处正确实现,`sortOrder` 单独记录。", "**清掉死代码**:`cliOk`(退出码时代的遗留)、`hasExecutable`、`artifactPaths`(自称 for tests 但无人使用)、`REGISTER_METHODS`(定义并导出、无人消费)、`tryTrigger`、空函数 `seedPluginData`、未使用的 `createStubElement` 导入、`Workspace.layoutReady`、`stub.__workspace` / `__vault`(与 `__app` 重复);另有 5 处仅在文件内部使用的符号收窄为非导出。", "**修掉不实/过时说明**:`deploy` 的头注释与输出仍在说「P0 阶段、做不了实时集成」,而 `reload`/`inspect` 早已具备该能力——改为如实指向它们;`inspect` 的「绝不改动 App」改为准确表述(console 会附加调试器、clear 清空缓冲、screenshot 写文件);L3 档不再被描述为「必然切窗抢焦点」(只有 `vault action=ensure confirm=true` 会);L4 不再被笼统称为「零干扰」(对你的 Obsidian 零干扰,沙箱自身窗口启动瞬间约 1 秒可见,任何开关都拦不住);构建层级判据改为「项目是否声明 build 脚本」而非「是否存在打包配置」;`≤250` 字符说明改为 validate 阶段告警;skill 注册 API 由 `ctx.skills.register` 更正为 `ctx.get(\"skills\")?.register`;报告与检查项名称改为名副其实(如「registers a command, view or setting tab」不再被任意注册满足);jsdom 来源标注不再把回退解析的 jsdom 说成项目的;`apiVersion` 与 `requireApiVersion` 合并为同一常量;`scaffold` 报告补上「桩环境库为空」这一事实。", "**同步 DESIGN.md 与验收状态**:P2a/P2b/P2c/P3 与 A9–A15 按实测事实更新——其中 A14 明确标注为**未按设计实现**(本轮已补门禁),A10 因本地 obsidian-tasks 检出只有源码树而降级为合成验证,A12 标注机制就绪但未跨版本实跑。0.8.1/0.8.2 的版本说明加上「做法已被 0.8.3 证伪」的标注,避免有人照旧照做;`scripts/deploy.sh` 的工具数由 3 更正为 11。", "验证:72 项测试通过(新增库门禁、data.json 播种、自定义 dir、库存活等回归测试);写入门禁经同前缀兄弟目录等绕过用例验证;离线冒烟在有/无 data.json 两种情况下分别给出 PASS/FAIL。" ] }, "en": { "title": "Cleanup: wrong settings, improper implementation, stale and redundant descriptions", "items": [ "**Implemented the write gate the docs promised but the code lacked**: six documents state that app-side writes against a non-test vault are refused, while no vault-identity check existed. `plugin:enable` / `plugin:disable` / `unrestrict` are written by Obsidian itself and are invisible to the file sandbox, so the fence has to be ours: `obsidian_plugin_reload` now verifies, before running any command, that the target vault path is inside the session workspace and refuses otherwise with three explicit options (use an in-workspace test vault / use the L4 sandbox / run it yourself). A target identified only as \"whatever window is in front\" cannot be located, and the docs now say so.", "**Removed a parameter that did nothing**: `dir` wrote the spec to a custom directory while the generated config hardcoded `specs: [\"./e2e/specs/**\"]`, so those specs were never collected. The glob is now rendered from `dir`, which therefore works.", "**Fixed a data.json defect in the offline smoke test**: the stub's `Plugin` constructor assigned `_stubData` as an own property, shadowing the harness's seeded prototype value, so `loadData()` always returned `undefined` where the real app returns data.json — every plugin branching on persisted data during onload was exercised down the wrong path. Seeding now happens per instance through `createObsidianStub({ pluginData })`, with no prototype mutation (two stubs in one process no longer share one value).", "**Fixed the stub's `registerMarkdownPostProcessor`**: it was defined twice and the live copy pushed a `{processor, sortOrder}` object into `markdownPostProcessors`, which by API holds callables. Merged into one correct implementation with sort orders recorded separately.", "**Removed dead code**: `cliOk` (a leftover from exit-status-based classification), `hasExecutable`, `artifactPaths` (documented \"for tests\" with no test using it), `REGISTER_METHODS` (defined and exported, consumed by nothing), `tryTrigger`, the empty `seedPluginData`, an unused `createStubElement` import, `Workspace.layoutReady`, `stub.__workspace` / `__vault` (duplicates of `__app`); five more symbols used only inside their own file were narrowed to non-exported.", "**Corrected false and stale descriptions**: `deploy`'s header comment and output still claimed \"P0 scope, no live integration\" although `reload`/`inspect` have had it for releases — they now point at them; `inspect`'s \"never changes the app\" became accurate (console attaches a debugger, `clear` empties the buffer, `screenshot` writes a file); L3 is no longer described as necessarily switching windows (only `vault action=ensure confirm=true` does); L4 is no longer loosely \"zero interference\" (zero for your Obsidian, while the sandbox's own window shows ~1s at startup, which no flag prevents); the build-tier criterion is \"the project declares a build script\" rather than \"a bundler config exists\"; the 250-character rule is documented as a validate warning; skill registration is `ctx.get(\"skills\")?.register` rather than `ctx.skills.register`; check names now match what they assert (\"registers a command, view or setting tab\" is no longer satisfied by any registration); jsdom provenance no longer reports a fallback-resolved jsdom as the project's; `apiVersion` and `requireApiVersion` share one constant; and the smoke report states that the stub vault is empty.", "**Brought DESIGN.md and its acceptance table in line with reality**: P2a/P2b/P2c/P3 and A9–A15 updated from measurements — A14 is marked as **not implemented as designed** (this release adds the gate), A10 is downgraded to synthetic verification because the local obsidian-tasks checkout contains only the source tree, and A12 notes the mechanism exists without a cross-version run. The 0.8.1/0.8.2 notes now carry a \"superseded by 0.8.3\" marker so nobody follows the disproven fix, and `scripts/deploy.sh` reports 11 tools instead of 3.", "Verified: 72 tests pass (new regressions for the write gate, data.json seeding, a custom scaffold directory, and vault survival); the gate was checked against bypass shapes such as a sibling directory sharing the workspace prefix; and the smoke test returns PASS/FAIL with and without data.json respectively." ] } }, { "version": "0.8.5", "date": "2026-09-23", "zh": { "title": "新建的空库要能活过克隆", "items": [ "配套 0.8.4 的收尾:生成的 .gitignore 原本整目录忽略 e2e/vault/,于是新建的空库不会被提交——队友克隆之后库又不存在,第一次运行照样在 onPrepare 失败,也就是 0.8.4 修掉的那个症状。", "改为只忽略库的内容(e2e/vault/*)并用 !e2e/vault/.gitkeep 保留下目录本身,init 同时写入这个 .gitkeep 标记;新增测试钉住这三行,并明确禁止出现裸的 e2e/vault/ 规则。", "注意(已写进模板注释):.gitignore 只会被追加一次,如果之后把库换到别的路径,需要手工为那个路径加同样的规则。", "验证:68 项测试通过。" ] }, "en": { "title": "A created vault has to survive a clone", "items": [ "Follow-up to 0.8.4: the generated .gitignore ignored `e2e/vault/` wholesale, so a newly created vault was never committed — after a teammate cloned the repository the vault was gone again and the first run failed in onPrepare, exactly the symptom 0.8.4 fixed.", "It now ignores only the vault's contents (`e2e/vault/*`) and un-ignores `!e2e/vault/.gitkeep`, and init writes that marker. New tests pin all three lines and explicitly forbid a bare `e2e/vault/` rule.", "Worth knowing, and noted in the template: .gitignore is appended only once, so moving the vault to another path later means adding the same rules for that path by hand.", "Verified: 68 tests pass." ] } }, { "version": "0.8.4", "date": "2026-09-23", "zh": { "title": "E2E 脚手架开箱即跑:库路径不再指向一个不存在的目录", "items": [ "修掉一个硬缺陷:生成的 wdio.conf.mts 一直写死 vault=\"./e2e/vault\",而脚手架从不创建它。全新项目第一次 pnpm run e2e 会在 onPrepare 直接失败(Vault \"...\" doesn't exist),一个 spec 都跑不到。此前没暴露,只是因为某个项目里恰好手工建过这个目录。", "init 现在先找项目里已有的库(含 .obsidian 的目录,跳过 node_modules/.git/scaffold 等)并复用它——复用它才贴近开发者真实的插件使用环境;测试名(Test/TestVault/sandbox/e2e…)优先于普通笔记目录,笔记多的候选会被排序靠后。找不到才创建一个空的 e2e/vault(含 .obsidian)。", "status 与 init 都会报告最终用的库;若复用的库根部笔记数 > 20,会提示改用专门的测试库。status 另外会指出「配置指向但不存在的库」,因为那是每次运行都必然失败的前置条件。", "新增 Fs.mkdir(含 recursive),让脚手架能交付「可运行态」而不只是「文件已写」。", "安全边界不变:vault 仍以 copy: true 打开(服务端默认即 true),测试永远只读一份副本,绝不改动被指向的库;新增测试钉住这一点。", "验证:68 项测试通过;用真实项目复算,库解析为 ./Test(项目里已有的测试库)而非那个空目录。L4 实跑本次因本机到 github.com/obsidianmd/obsidian-releases 的连接超时而未能完成(下载端点不可达,属环境问题)。" ] }, "en": { "title": "The E2E scaffold runs as generated: the vault path no longer points at a directory nobody created", "items": [ "Fixes a hard defect: the generated wdio.conf.mts hardcoded vault=\"./e2e/vault\" and nothing ever created it. A freshly scaffolded project failed its very first `pnpm run e2e` in onPrepare (`Vault \"…\" doesn't exist`) without running a single spec. It went unnoticed only because one project happened to have that directory created by hand.", "init now looks for a vault already in the project (a directory holding .obsidian, skipping node_modules/.git/the scaffold, and so on) and reuses it — reusing it is what keeps the sandbox close to how the developer actually uses the plugin. Test-named directories (Test/TestVault/sandbox/e2e…) rank above folders of notes, and note-heavy candidates sort last. Only when there is none does it create an empty e2e/vault (with .obsidian).", "Both status and init report the vault that will be used, and a reused vault with more than 20 notes at its root is flagged so the developer can point at a purpose-built test vault instead. status additionally names a vault the config points at but that does not exist, since that fails every run.", "Adds Fs.mkdir (recursive) so a scaffold can deliver a runnable state, not merely written files.", "The safety boundary is unchanged: the vault is still opened with copy: true (the service defaults to true), so tests only ever read a copy and never modify the vault they point at; a new test pins that.", "Verified: 68 tests pass, and re-resolving against a real project yields ./Test (the test vault already in it) instead of the empty directory. The L4 run was not completed this time because this machine could not reach github.com/obsidianmd/obsidian-releases (the download endpoint timed out — an environment problem)." ] } }, { "version": "0.8.3", "date": "2026-09-23", "zh": { "title": "L4 沙箱实例彻底不显示窗口", "items": [ "生成的 wdio.conf.mts 现在带一个 before 钩子,在任何 spec 运行前隐藏沙箱 Obsidian 的窗口,实测 isVisible() 由 true 变为 false。", "纠正上一版的错误做法:--headless、--headless=new、--hidden 三个开关逐一实测均无效,窗口仍然可见——Obsidian 是 Electron 应用,自举时会自己创建并显示窗口,命令行开关拦不住。相关注释已改为如实说明,不再声称靠 flag 隐藏。", "e2e 漂移检测改为要求该 before 钩子(原先要求的是无效的 --headless=new),并用生成配置的 args 做精确断言,避免把解释性注释误判为违规。", "确认 wdio-obsidian-service 的 ObsidianCapabilityOptions 没有任何窗口可见性选项,launcher 也不控制窗口,因此该钩子是最早且唯一可行的隐藏点;启动瞬间约 1 秒的窗口无法用任何开关消除,可用 pnpm run e2e:watch 只付一次。" ] }, "en": { "title": "The L4 sandboxed instance no longer shows a window at all", "items": [ "The generated wdio.conf.mts now carries a `before` hook that hides the sandboxed Obsidian window before any spec runs; measured isVisible() going from true to false.", "Corrects the previous release's wrong fix: --headless, --headless=new and --hidden were each measured and none of them hides the window. Obsidian is an Electron app that creates and shows its own window during bootstrap, so no launch switch can suppress it. The config comments now say this instead of claiming a flag does the job.", "E2E config drift now requires that `before` hook (it previously required the ineffective --headless=new), and asserts against the generated args precisely so an explanatory comment is not mistaken for a violation.", "Confirmed that wdio-obsidian-service's ObsidianCapabilityOptions has no window-visibility option and the launcher does not control the window, so the hook is the earliest and only workable hiding point. The roughly one-second window during startup cannot be removed by any switch; `pnpm run e2e:watch` pays that cost once." ] } }, { "version": "0.8.2", "date": "2026-09-23", "zh": { "title": "E2E 配置漂移检测:已存在的脚手架不再被默认为「可用」", "items": [ "⚠️ 本条的 --headless=new 做法在 0.8.3 被证伪并移除:实测 --headless / --headless=new / --hidden 都不隐藏窗口(Obsidian 自举时自己显示窗口),正确做法见 0.8.3。", "症状:真实任务中仍然频繁闪现 Obsidian。根因不是模板——而是**项目里那份旧配置**(在 0.8.1 之前生成,只有 --no-sandbox、没有 --headless=new),工具却因为「文件已存在」而不做任何提醒,一次会话里这样跑了 13 次。", "e2e status 现在校验配置的必需项并报告漂移:capability 形态(wdio:obsidianOptions)、services 写法、--no-sandbox、--headless=new、copy: true、plugins —— 每一项都附带「缺了会怎样」。", "e2e init 在保留旧配置时同样提示漂移,并给出刷新命令(force=true 只覆盖配置文件,不动你的 specs)。", "写入被沙箱拒绝(项目在工作区之外)时,不再抛裸 EPERM,而是给出可执行诊断:目标路径、原因、可选做法,以及配置必需项清单。" ] }, "en": { "title": "E2E config drift detection: an existing scaffold is no longer assumed to work", "items": [ "⚠️ Superseded by 0.8.3: this release's --headless=new turned out NOT to hide the window (all three candidate flags were measured and left isVisible() === true). The working fix is described under 0.8.3.", "Symptom: Obsidian still flashed repeatedly during a real task. The cause was not the template but the project's EXISTING config, generated before 0.8.1 — it had --no-sandbox but not --headless=new — and nothing warned about it because the file was present. One session ran it thirteen times that way.", "e2e status now validates the config's required settings and reports drift: capability shape (wdio:obsidianOptions), the services form, --no-sandbox, --headless=new, copy: true and plugins — each with what breaks without it.", "e2e init warns about drift when it keeps an existing config, and names the refresh command (force=true rewrites only the config files, never your specs).", "A sandbox refusal (project outside the workspace) no longer escapes as a bare EPERM: it reports the target path, the reason, the options, and the list of required settings." ] } }, { "version": "0.8.1", "date": "2026-09-22", "zh": { "title": "修复沙箱化 E2E 脚手架:配置形态、依赖清单与「不要弹窗」", "items": [ "⚠️ 本条的 --headless=new 做法在 0.8.3 被证伪并移除:实测 --headless / --headless=new / --hidden 都不隐藏窗口(Obsidian 自举时自己显示窗口),正确做法见 0.8.3。", "修复:生成的 wdio 配置把 vault / plugins / 版本号写在 service 选项里,而服务要求它们属于 capability 的 wdio:obsidianOptions——后者会被静默忽略,导致 Obsidian 在不装本插件的情况下启动,测试报 executeObsidian is not a function。", "修复:模板默认使用 wdio-obsidian-reporter,但依赖清单里没有它,运行时报 Couldn't find plugin obsidian reporter;默认改为 spec reporter,并在注释里说明换成 obsidian reporter 需要额外安装哪个包。", "修复:示例 spec 有两处会误判——强制要求插件注册命令(编辑器扩展型插件合法地一个都没有),以及给 expect 传自定义消息(wdio 的 expect 只接受一个参数)。现在断言对任意插件都成立,并新增一条「真的是 Obsidian 而非裸 Chromium」的断言。", "**测试实例不再闪现**:capability 增加 --headless=new。此前每次运行都会把 Obsidian 窗口闪到屏幕上——虽然不是用户的实例,但同样是干扰。", "保留 --no-sandbox 并说明原因:Chromium 的渲染器沙箱无法在另一个沙箱(agent shell、CI 容器)内启动,helper 进程会在启动时 abort。" ] }, "en": { "title": "Fix the sandboxed E2E scaffold: option shape, dependency list, and no flashing window", "items": [ "⚠️ Superseded by 0.8.3: this release's --headless=new turned out NOT to hide the window (all three candidate flags were measured and left isVisible() === true). The working fix is described under 0.8.3.", "Fix: the generated wdio config passed vault / plugins / versions as service options, but the service requires them under the capability key wdio:obsidianOptions. They were silently ignored, so Obsidian started WITHOUT the plugin installed and the specs failed with executeObsidian is not a function.", "Fix: the template defaulted to the wdio-obsidian-reporter, which is not in the dependency list, so the run died with 'Couldn't find plugin obsidian reporter'. The default is now the spec reporter, with a comment naming the package to install for the Obsidian-version reporter.", "Fix: the example spec had two false failures — it demanded at least one registered command (an editor-extension plugin legitimately has none) and passed a custom message to expect (wdio's expect takes exactly one argument). The checks now hold for any plugin, plus one that proves the session really is Obsidian and not bare Chromium.", "**The test instance no longer flashes on screen**: the capability now sets --headless=new. Before this, every run briefly showed an Obsidian window — not the user's, but still interference.", "Kept --no-sandbox with its reason documented: Chromium's renderer sandbox cannot start inside another sandbox (an agent shell, a CI container); its helpers abort at startup." ] } }, { "version": "0.8.0", "date": "2026-09-19", "zh": { "title": "新增 obsidian_plugin_eval,并写下硬性验收规则", "items": [ "新增 obsidian_plugin_eval(高特权、必审):在运行中的 App 里执行 JavaScript 并返回结果,是实时状态的逃生舱——读 App 真正持有的东西(插件实例、workspace、metadataCache)、驱动一次交互,或不做重新构建就试一个修法。它是唯一的高特权工具,因此需用户审批;返回会把实际执行的代码回显出来作为审计,代码触及窗口焦点时(如 electron.remote.getCurrentWindow().focus())会明确警告,因为那会抢走用户的焦点。参数为 code(必填)、vault(可选目标库)、timeoutMs(可选,默认 30000);只读能回答的优先用 obsidian_plugin_inspect(免费、无需审批)。", "验收规则(硬性):obsidian_plugin_build 通过 ≠ 插件可用,obsidian_plugin_test 的 PASS ≠ UI 已验证。只要改动涉及界面、渲染或交互,就必须真正看到它——沙箱档 obsidian_plugin_e2e,或对着运行中的 App 用 obsidian_plugin_inspect action=screenshot——并在回复里说明看到了什么。这条规则来自真实任务:改动是全新 UI,build + test 全绿,却从未部署,用户什么也没看到。", "离线冒烟的报告现在点名「本次没有覆盖什么」:没有 DOM 宿主时会写明「注册为编辑器扩展的 UI 代码未被检验」,并指向 e2e / screenshot 做真正的验收,避免 PASS 被误读为 UI 已验证。" ] }, "en": { "title": "Add obsidian_plugin_eval, and write down the mandatory acceptance rule", "items": [ "New obsidian_plugin_eval (high privilege, approval-gated): runs JavaScript inside the running app and returns the result — the live-state escape hatch for reading what the app really holds (plugin instances, workspace, metadataCache), driving an interaction, or trying a fix without rebuilding. It is the only high-privilege tool, so it is approval-gated; the executed code is echoed back in the result as an audit trail, and the result warns when the code touches window focus (e.g. electron.remote.getCurrentWindow().focus()), because that steals the user's focus. Parameters: code (required), vault (optional target vault), timeoutMs (optional, default 30000); prefer obsidian_plugin_inspect (free, no approval) whenever a read-only action answers the question.", "Acceptance rule (mandatory): obsidian_plugin_build passing does not mean the plugin works, and obsidian_plugin_test PASSing does not mean the UI was verified. If a change touches interface, rendering or interaction, it must actually be seen — in the sandboxed e2e tier (obsidian_plugin_e2e), or against the running app (obsidian_plugin_inspect action=screenshot) — and the reply must say what was seen. The rule comes from a real session: a pure UI change passed build + test and was never deployed, so the user saw nothing.", "The offline smoke report now names what it did NOT cover: without a DOM host it says plainly that UI code registered as an editor extension was not exercised, and points at e2e / screenshot for real acceptance, so a PASS cannot be misread as verified UI." ] } }, { "version": "0.7.1", "date": "2026-09-19", "zh": { "title": "离线冒烟:修复工作区外项目的沙箱拒绝,并让覆盖范围可见", "items": [ "修复:harness 为绕过 \"type\":\"module\" 而把产物复制成同目录的 .cjs,当插件项目位于会话工作区之外时会被沙箱拒绝(EPERM),导致 obsidian_plugin_test 对这类项目直接失败;现在改为复制到可写临时目录。", "新增探测:注册的编辑器视图插件会被实例化一次,因此以 UI 为主要功能的插件,其视图/界面代码现在能被离线覆盖(需项目自带 jsdom)。", "结论文案按实际情况区分:无 DOM 宿主时明确写出「注册为编辑器扩展的 UI 代码未被检验」,不再让 PASS 被误读为 UI 已验证。", "环境缺口(缺少 DOM、桩未建模的 API)不再被误报为插件缺陷。" ] }, "en": { "title": "Offline smoke: fix the sandbox denial for out-of-workspace projects, and make coverage visible", "items": [ "Fix: to sidestep \"type\":\"module\" the harness copied the bundle to a .cjs file next to it, which the sandbox refuses when the plugin project lives outside the session workspace (EPERM) — obsidian_plugin_test failed outright for such projects. It now stages the copy in a writable temp directory.", "New probe: registered editor view plugins are instantiated once, so a plugin whose feature is its UI now gets that view/UI code covered offline (requires jsdom in the project).", "The conclusion is stated per run: without a DOM host it says plainly that UI code registered as an editor extension was NOT exercised, so a PASS cannot be misread as verified UI.", "Environment gaps (missing DOM, an API the stub does not model) are no longer reported as plugin defects." ] } }, { "version": "0.7.0", "date": "2026-09-19", "zh": { "title": "真机观测的失败上下文与窗口状态提示", "items": [ "inspect status 降噪:默认只报目标库与「实际应答库」并给出已注册库数量,完整列表改为 all=true 时才输出。", "reload 失败时直接带上「应答窗口的库是谁」,不必再为「命令为何 not found」额外排查一遍。", "vault ensure 增加恢复能力:目标库窗口已打开却未渲染时,显式把该窗口带到前台并复检,同时明确告知「本次操作移动了你的焦点」;失败则列出可选做法。", "inspect screenshot 先读窗口渲染状态:隐藏/最小化/未聚焦时直接说明截图可能是空白,并劝阻用 electron.remote 强拉窗口。", "技能补充显式禁令与排障行:禁止用 remote.show()/focus() 抢焦点;截图请使用语义化且互不相同的路径。" ] }, "en": { "title": "Failure context and window-state reporting for live observation", "items": [ "inspect status is quieter: it reports the target vault, which vault actually answered, and a count of registered vaults; the full list is opt-in via all=true.", "reload failures now name the vault the addressed window is showing, so \"why was the plugin not found\" no longer needs a separate investigation.", "vault ensure can recover a vault window that is open but not rendering: it raises that window explicitly, re-confirms, and says plainly that it moved your focus; when that fails it lists the options.", "inspect screenshot reads the window's render state first: when hidden, minimized or unfocused it says the capture may be blank and warns against forcing the window forward with electron.remote.", "The skill gains an explicit prohibition (never force focus with remote.show()/focus()) and the habit of giving each screenshot a distinct, meaningful path." ] } }, { "version": "0.6.0", "date": "2026-09-18", "zh": { "title": "验证四档:离线冒烟与沙箱化 E2E", "items": [ "⚠️ 更正:「工具会拒绝对非测试库执行」这句在当时并不成立——代码里没有库身份检查,该门禁到 0.8.6 才真正实现。", "新增 obsidian_plugin_test(离线冒烟,L2 档):在纯 Node 里用桩化的 Obsidian API 加载构建产物,真跑一遍生命周期——默认导出是 Plugin 子类、onload() 执行、注册动作发生、onunload() 清理、无未处理的 rejection,不需要安装 Obsidian。它会真实解析项目自身的 @codemirror/* 与 @lezer/*,DOM 相关插件可用项目自带的 jsdom;它不验证运行期行为:报告固定标注这是桩环境,并指向 inspect/e2e 做真实验证。", "新增 obsidian_plugin_e2e(沙箱化端到端测试,L4 档):init 用 WebdriverIO + wdio-obsidian-service 生成 wdio.conf.mts、tsconfig.e2e.json 与起始 spec(脚手架目录由 dir 指定,默认 e2e/),并加入 e2e / e2e:watch 脚本;它启动的独立 Obsidian 使用独立配置目录与库副本,因此不切用户窗口、不抢焦点。status 只报告现状与确切的安装命令,不写任何文件;runner 依赖留在用户项目里。", "验证明确为四档,并写明了默认档:L1 静态(build 内置)、L2 离线冒烟(test,无需 Obsidian)、L3 用户自己的 Obsidian(vault / reload / inspect / eval,会切换窗口、抢焦点)、L4 沙箱 Obsidian(e2e)。日常开发循环默认走 L1 + L2 + L4;L4 档的存在意义就是让默认循环不再打扰用户——抢焦点原本来自我们自己调用 vault-open,沙箱实例用「独立实例 + 库副本」把它从根上消除,用户窗口全程不动。L3 只在需要验证用户真实环境时使用。", "obsidian_plugin_build 的入口与产物改为从项目自身配置解析:入口按「显式 entry → src/main.ts → 仓库根 main.ts → 项目自身的 esbuild/rollup/vite 配置 → package.json main」,产物按「项目根 → 配置里的 outfile/outdir/file/dir → 项目内测试库布局 → 有界搜索(含 /.obsidian/plugins//)」,两者失败时都会列出全部尝试过的位置;新增 entry / outDir 参数。", "构建层级优先级按实测修正:只要存在打包配置就优先跑项目自身的 production build 脚本——配置里带的插件(如 esbuild-svelte)无法从命令行补齐;项目本地 esbuild 直调只在没有配置可遵循、或显式选择非 production 时使用,并会警告未应用项目插件。", "写入范围成为硬门禁:只允许修改会话工作区内创建的测试库,用户自己的库只读——包括沙箱拦不到的 CLI 侧间接写入(plugin:enable / unrestrict 改写的是当前活动窗口那个库),工具会拒绝对非测试库执行。默认路径绝不切换用户窗口、绝不抢焦点。" ] }, "en": { "title": "Four verification tiers: offline smoke and sandboxed e2e", "items": [ "⚠️ Correction: the claim that the tools refuse to run against a non-test vault was not true at the time — no vault-identity check existed; the gate only landed in 0.8.6.", "New obsidian_plugin_test (offline smoke, tier L2): loads the built bundle in plain Node against a stubbed Obsidian API and exercises the real lifecycle — default export is a Plugin subclass, onload() runs, registrations happen, onunload() cleans up, no unhandled rejections — with no Obsidian installed. It resolves the project's own @codemirror/* and @lezer/* for real and can use the project's own jsdom for DOM-dependent plugins; it does not verify runtime behaviour, so the report always says it is a stub environment and points at inspect/e2e for real verification.", "New obsidian_plugin_e2e (sandboxed end-to-end tests, tier L4): init generates wdio.conf.mts, tsconfig.e2e.json and a starting spec with WebdriverIO + wdio-obsidian-service (scaffold directory via dir, default e2e/), and adds the e2e / e2e:watch scripts; the Obsidian it launches uses an isolated config directory and a copy of the vault, so nothing switches the user's window or steals focus. status reports only what exists and the exact install command, writing nothing; the runner dependencies stay in the user's project.", "Verification is now explicitly four tiers, with the default stated: L1 static (inside build), L2 offline smoke (test, no Obsidian needed), L3 the user's own Obsidian (vault / reload / inspect / eval — it switches the window and steals focus), L4 sandboxed Obsidian (e2e). The default development loop is L1 + L2 + L4, and the e2e tier exists so that the default loop never switches the user's window — the focus stealing came from our own vault-open calls, and a sandboxed instance with its own config directory and a copy of the vault removes it outright. L3 is used only when the user's real environment is the question.", "obsidian_plugin_build now resolves its entry point and artifact from the project's own configuration: entry via explicit entry → src/main.ts → a repository-root main.ts → the project's own esbuild/rollup/vite config → package.json main, artifact via project root → the config's outfile/outdir/file/dir → the project-local vault layout → a bounded search that also probes /.obsidian/plugins//; both list every location tried on failure, and the new entry / outDir parameters pin either one.", "Build tier precedence corrected by evidence: whenever a bundler config exists, the project's own production build script now runs first — a config can carry plugins (esbuild-svelte) that cannot be supplied from the command line; direct project-local esbuild is used only when there is no config to honour, or as an explicit non-production opt-out that warns the project's plugins were not applied.", "Write scope is now a hard gate: only test vaults created inside the session workspace may be modified, and the user's own vaults are read-only — including CLI-side indirect writes the sandbox cannot see (plugin:enable / unrestrict rewrite whichever vault is in the active window), which the tools refuse to run against a non-test vault. The default path never switches the user's window or steals focus." ] } }, { "version": "0.5.0", "date": "2026-09-16", "zh": { "title": "打通真机验证:观测、库管理与重载", "items": [ "新增 obsidian_plugin_inspect(只读观测):status 一次给出体检事实(App 版本、已注册的库、实际应答的库、该库的受限模式状态、目标插件是否已安装/启用/版本匹配、信任弹窗是否待确认),另有 errors / console / dom / css / screenshot / trustCheck;绝不改动 App。", "新增 obsidian_plugin_vault(真机验证的库管理):status 报告已注册的库、当前活动窗口与激活将要做什么;ensure 是两步确认——不带 confirm 只描述后果,带 confirm=true 才登记/打开该库(Obsidian 会切到前台),随后由 App 自证哪个库是活动库,并如实上报信任弹窗而不代为确认。", "新增 obsidian_plugin_reload(让改动生效):reload(默认)/ enable / disable;rescan 重扫插件清单索引——Obsidian 只在库加载时扫描一次插件目录,刚部署的插件在重扫前对所有插件命令都不可见。", "status 会说明究竟是哪个库在实际应答(窗口级命令跟随活动窗口,vault 参数并不可靠),并报告该库的受限模式状态;读数与目标库不符时,先 ensure 再观测。", "reload 重载后校验插件是否真的注册进 App 并回报插件日志——「命令被接受」不等于「插件已加载」;受限模式是逐库的安全设置,只能由显式的 reload action=unrestrict 关闭(会重载窗口),绝不作为副作用。" ] }, "en": { "title": "Live verification: observe, manage the vault, reload", "items": [ "New obsidian_plugin_inspect (read-only observation): status returns the health facts in one shot (app version, registered vaults, which vault actually answered, that vault's restricted-mode state, whether the target plugin is installed/enabled/version-matched, whether the trust modal is pending), plus errors / console / dom / css / screenshot / trustCheck; it never changes the app.", "New obsidian_plugin_vault (vault management for live verification): status reports registered vaults, the active window and what activation would take; ensure is a two-step confirmation — without confirm it only describes the consequences, with confirm=true it registers/opens the vault (Obsidian switches to the front) and then asks the app which vault is active, reporting Obsidian's trust modal instead of accepting it.", "New obsidian_plugin_reload (make a change take effect): reload (default) / enable / disable; rescan refreshes the plugin manifest index — Obsidian scans a vault's plugin directory only at vault load, so a freshly deployed plugin stays invisible to every plugin command until then.", "status states which vault actually answered (window-scoped commands follow the active window; vault= does not reliably redirect them) and reports that vault's restricted-mode state; when a reading does not match the target vault, run ensure first and observe again.", "After reloading, the plugin's registration in the app is verified and anything the plugin logged is reported — an accepted command is not a loaded plugin; restricted mode is a per-vault security setting that only an explicit reload action=unrestrict turns off (it reloads the window), and it is never a side effect." ] } }, { "version": "0.4.0", "date": "2026-09-16", "zh": { "title": "新增构建与部署工具", "items": [ "新增 obsidian_plugin_build:把插件项目打包成可加载的 main.js(CommonJS,obsidian 外部化)并做静态自检,补齐 build → deploy 这半程开发闭环。", "构建三级降级:项目本地 esbuild → 项目自身的 production build 脚本 → 给出可操作的报错;绝不启动 watch 进程,并在返回值中报告实际使用的层级。", "新增 obsidian_plugin_deploy:把构建产物装进 vault 的 .obsidian/plugins//,并把 id 合并进该库的 community-plugins.json(保留既有条目),同时把 vault 记入项目旁的 dsh.obsidian.json。", "部署三态分列报告、不混为一谈:written(已写入)/ enabled(已启用)/ active(是否被运行中的 Obsidian 加载,本阶段恒为 unknown)——写盘与启用不等于插件已加载。" ] }, "en": { "title": "Add build and deploy tools", "items": [ "New obsidian_plugin_build: bundles a plugin project into a loadable main.js (CommonJS, obsidian externalized) with static self-checks, completing the build → deploy half of the loop.", "Three-tier build degradation: project-local esbuild → the project's own production build script → an actionable refusal; never a watch process, and the tier actually used is reported.", "New obsidian_plugin_deploy: installs the build artifacts into the vault's .obsidian/plugins//, merges the id into that vault's community-plugins.json (existing entries preserved), and remembers the vault in dsh.obsidian.json.", "Deploy reports the three states separately and never conflates them: written / enabled / active (whether the running app loaded it stays unknown in this phase) — files on disk and an enable-list entry are not a loaded plugin." ] } }, { "version": "0.3.0", "date": "2026-09-13", "zh": { "title": "工具重构与 skill 内置", "items": [ "工具改名:obsidian_scaffold / obsidian_validate / obsidian_version → obsidian_plugin_scaffold / obsidian_plugin_validate / obsidian_plugin_version。", "scaffold 复用官方 obsidian-sample-plugin 模板(14 个文件),不再在代码内嵌模板字符串。", "validate 集成 eslint-plugin-obsidianmd(obsidianmd/eslint-plugin)检查代码。", "skill 内置为 assets/skills/obsidian-plugin,重组为 DSH Tools / Workflow / 插件编写规范 / 校验与提交。", "修复文件写入的会话沙箱策略传递(per-session sandboxPolicy)。" ] }, "en": { "title": "Tool rework and vendored skill", "items": [ "Rename tools: obsidian_scaffold / obsidian_validate / obsidian_version → obsidian_plugin_scaffold / obsidian_plugin_validate / obsidian_plugin_version.", "scaffold reuses the official obsidian-sample-plugin template (14 files) instead of inline template strings.", "validate integrates eslint-plugin-obsidianmd (obsidianmd/eslint-plugin) lint checks.", "skill vendored at assets/skills/obsidian-plugin and reorganized into DSH Tools / Workflow / authoring rules / validation & submission.", "Fix per-session sandboxPolicy propagation for file writes." ] } }, { "version": "0.2.0", "date": "2026-09-12", "zh": { "title": "新增 doc 目录与版本说明", "items": [ "新增 doc/ 目录:HARNESS 会话上下文、使用手册与版本说明随包分发。", "版本说明机制:内置 VERSION_NOTES,按语言输出历史版本更新说明。", "工具文档化:三个工具的能力与使用规则写入 HARNESS 上下文。" ] }, "en": { "title": "Add doc directory and version notes", "items": [ "New doc/ directory: HARNESS session context, usage manual and version notes ship with the package.", "Version-notes mechanism: built-in VERSION_NOTES with localized history.", "Tool documentation: the three tools' capabilities and usage rules are written into the HARNESS context." ] } }, { "version": "0.1.0", "date": "2026-09-12", "zh": { "title": "初始版本", "items": [ "obsidian_plugin_scaffold:生成合规插件骨架,内置命名/提交规则校验。", "obsidian_plugin_validate:校验 manifest 与版本一致性。", "obsidian_plugin_version:同步 manifest.json / versions.json / package.json 三处版本。" ] }, "en": { "title": "Initial release", "items": [ "obsidian_plugin_scaffold: generate a compliant plugin skeleton with built-in naming/submission checks.", "obsidian_plugin_validate: validate the manifest and version consistency.", "obsidian_plugin_version: bump the three version fields together." ] } } ]