@leelee592/dsh-obsidian-plugin
v0.8.6
Published
DeepSeek Harness tool plugin: scaffold, validate, and version Obsidian plugins.
Maintainers
Readme
dsh-obsidian-plugin
🌐 English | 🇨🇳 中文
Provides DeepSeek Harness (DSH) agents with Obsidian plugin development capabilities: scaffolding, building, offline smoke testing, deploying, sandboxed end-to-end testing, live verification against the running app (read-only observation, plus an approval-gated escape hatch that runs code inside it), validation, version syncing, plus a development-guidelines skill.
Features
skill
obsidian-plugin(knowledge, derived from gapmiss/obsidian-plugin-skill): Obsidian plugin development guidelines (authoring rules, validation & submission) that steer the agent's workflow.
tools
obsidian_plugin_scaffold: generates a compliant plugin skeleton from the official obsidianmd/obsidian-sample-plugin template, with built-in naming/submission checks.obsidian_plugin_build: bundles a plugin project into a loadablemain.js(CommonJS,obsidianexternalized) and runs static self-checks (tier L1); resolves the entry point (explicitentry→src/main.ts→ a repository-rootmain.ts→ the project's own esbuild/rollup/vite config →package.jsonmain) and the artifact location (project root → the config'soutfile/outdir/file/dir→ the project-local vault layout → a bounded search that also probes<dir>/.obsidian/plugins/<id>/), listing everything it tried when a lookup fails, and takesoutDirto pin the artifact directory; degrades through three tiers (the project's own productionbuildscript — now preferred whenever the project declares one, because it is the only way to apply the plugins a bundler config carries → direct project-local esbuild, used when there is no config to honour or as an explicit non-production opt-out that warns the project's plugins were not applied → an actionable refusal), never starts a watch process, and reports which tier it used.obsidian_plugin_deploy: installs the built artifacts into<vault>/.obsidian/plugins/<id>/and adds the id to that vault'scommunity-plugins.json(merged, existing entries preserved), then remembers the vault indsh.obsidian.json; reportswritten/enabled/activeseparately (activestaysunknownuntil a later phase).obsidian_plugin_inspect: read-only observation of the running app —status(one-shot health report: live app version, registered vaults, which vault actually answered, that vault's restricted-mode state, whether the target plugin is installed/enabled/version-matched, and whether Obsidian's trust modal is pending), pluserrors/console/dom/css/screenshot/trustCheck; read-only with respect to your vault and plugins, thoughaction=consoleattaches and detaches the capture debugger (which makesplugin:reloadhang while attached),clearempties the app's buffer, andscreenshotwrites an image file.obsidian_plugin_vault: manages the vault used for live verification —status(registered vaults, the active window, what activation would take),ensure(withoutconfirmit only describes the consequences; withconfirm=trueit registers/opens the vault — Obsidian switches to the front — then verifies by asking the app which vault is active, and reports Obsidian's trust modal instead of accepting it),close(macOS),prune(not implemented yet).obsidian_plugin_reload: makes a code change take effect in the running app —reload(default) /enable/disable/rescan(refresh the app's plugin manifest index: Obsidian only scans a vault's plugin directory at vault load, so a freshly deployed plugin is invisible to every plugin command until this runs) /unrestrict(turns off restricted mode for that vault — a per-vault security setting that reloads the window, so it is an explicit action, never a side effect); after reloading it verifies that the plugin is actually registered and reports what the plugin logged.obsidian_plugin_test: offline smoke test (tier L2) — loads the built bundle in plain Node against a stubbed Obsidian API and exercises the real lifecycle (default export is aPluginsubclass,onload()runs, registrations happen,onunload()cleans up, no unhandled rejections), so it needs no Obsidian installed; resolves the project's own@codemirror/*and@lezer/*for real and can use the project's own jsdom for DOM-dependent plugins (if jsdom is missing, the report says so and gives the install command); an optional scenario file (dsh/scenarios/<name>.mjs, receiving{ plugin, app, stub }) extends it. It does not verify runtime behaviour: the report always says it is a stub environment, names what it did not cover (e.g. UI code registered as an editor extension when no DOM host is available), and points at e2e / screenshot for real verification.obsidian_plugin_e2e: scaffolds sandboxed end-to-end tests (tier L4) with WebdriverIO +wdio-obsidian-service, which resolves and, if needed, creates the test vault it opens (a vault already in the project is reused, and it is always opened as a copy), then launches a separate Obsidian with an isolated config directory, so nothing switches your window or steals focus;initwrites the WebdriverIO setup (wdio.conf.mts,tsconfig.e2e.jsonand a starting spec — the scaffold directory isdir, defaulte2e/), addse2e/e2e:watchscripts and the downloaded-build directories to.gitignore(idempotent, keeps hand-edited files unlessforce=true), andstatusreports what exists and the exact install command without writing anything; the runner dependencies stay in your project.obsidian_plugin_eval: runs JavaScript inside the running Obsidian 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 (it executes code in your app), 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 (electron.remote.getCurrentWindow().focus()and friends), because that steals your focus;vaultaddresses the CLI andtimeoutMs(default 30000) covers long expressions. Preferobsidian_plugin_inspect(free, no approval) whenever a read-only action can answer the question.obsidian_plugin_validate: validates manifest required fields, naming rules,versions.jsonmapping, andpackage.jsonversion consistency, and lints code with the official obsidianmd/eslint-plugin (eslint-plugin-obsidianmd).obsidian_plugin_version: syncs versions acrossmanifest.json/versions.json/package.json.
verification
Verification has four tiers; the default development loop is L1 + L2 + L4.
| Tier | Means | Interference |
| --- | --- | --- |
| L1 static | artifact / module-format / manifest checks inside obsidian_plugin_build | none |
| L2 offline smoke | obsidian_plugin_test loads the built bundle in plain Node against a stubbed Obsidian API; no Obsidian needed | none |
| L3 your Obsidian | obsidian_plugin_vault / obsidian_plugin_reload / obsidian_plugin_inspect / obsidian_plugin_eval act on your running app, CLI-driven; only for verifying your real environment | window-scoped: they act on whichever vault is in front and fail (or answer from the wrong vault) otherwise. The one action that switches your window and steals focus is obsidian_plugin_vault action=ensure confirm=true |
| L4 sandboxed Obsidian | obsidian_plugin_e2e + the project's own WebdriverIO specs run a separate Obsidian (isolated config, vault copy; the generated config hides that instance's window before any spec runs) | none for your Obsidian — never switched, focused or shown. The sandbox's own window is still on screen for ~1s at startup (no launch flag suppresses it; e2e:watch pays that once) |
A passing check is not acceptance. obsidian_plugin_build succeeding does not mean the plugin works, and a PASS from obsidian_plugin_test 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 should say what was seen.
Writes are fenced to the session workspace: file writes are denied by the sandbox, and the app-side writes that the sandbox cannot see — plugin:enable / plugin:disable / unrestrict, which rewrite whichever vault is in the active window — are refused by the tool itself when the target path is outside the workspace (obsidian_plugin_reload). A vault identified only as "whatever window is in front" cannot be located from here, so name the vault when you want that check to apply. The default path never switches your window or steals focus.
Install
dsh plugin --profile web add @leelee592/dsh-obsidian-pluginUsage
Tell DSH "create an Obsidian plugin …" and the agent loads the obsidian-plugin skill, follows the Obsidian Plugin Development Guidelines, and calls obsidian_plugin_scaffold / obsidian_plugin_build / obsidian_plugin_test / obsidian_plugin_deploy / obsidian_plugin_e2e / obsidian_plugin_inspect / obsidian_plugin_vault / obsidian_plugin_reload / obsidian_plugin_eval / obsidian_plugin_validate / obsidian_plugin_version along the way for scaffolding, building, offline smoke testing, deploying, sandboxed end-to-end testing, live verification, running code in the app, validation, and versioning.
Docs
- doc/harness.default.md — the plugin's HARNESS session context (positioning / capabilities / usage rules).
- doc/version-notes.json — version history (latest first, zh + en).
- doc/manual.zh.txt / doc/manual.en.txt — user manual.
Compatibility
| Item | Value |
| --- | --- |
| profile | web |
| DeepSeek Harness | tested on 0.1.5-rc |
| peer deps | @deepseek-ai/cordis ^4.0.2 · @deepseek-ai/dsh-tools ^0.1.5-rc.2 · @deepseek-ai/schemastery ^3.18.2 |
| permissions | injects tools + fs (DSH-sandboxed, no external network calls) |
| Node (dev build) | 20+ |
| License | MIT |
