Obsidian plugin dev
Skill dustinkeeton/wafflestack/stacks/obsidian-dev/skills/obsidian-plugin-dev
π§ One batter, every repo β reusable AI agent & skill definitions rendered into harness-native files (.claude/, .codex/, .agents/)
npx -y skills add dustinkeeton/wafflestack --skill obsidian-plugin-devAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Obsidian plugin development patterns, API usage, manifest configuration, settings management, testing, and build pipeline. Use when working on plugin architecture, lifecycle hooks, tests, or Obsidian API integration.
SKILL.md
4.5 KB, ~1.0k tokens by cl100k_base, as published. Nobody here has run it
Obsidian Plugin Development Reference
Project Structure
plugin-root/
βββ src/
β βββ main.ts # Plugin class extending Plugin
βββ manifest.json # Plugin metadata (id, name, version, minAppVersion)
βββ package.json # npm dependencies
βββ tsconfig.json # TypeScript config (ES6 target, strict mode)
βββ esbuild.config.mjs # Bundler (main.ts β main.js, obsidian external)
βββ styles.css # Plugin styles
βββ versions.json # Version-to-Obsidian compatibility
βββ main.js # Compiled output (generated)
manifest.json
{
"id": "{{plugin.id}}",
"name": "{{plugin.name}}",
"version": "0.1.0",
"minAppVersion": "0.15.0",
"description": "{{plugin.description}}",
"author": "{{plugin.author}}",
"isDesktopOnly": false
}
Plugin Lifecycle
import { Plugin } from 'obsidian';
export default class {{plugin.classPrefix}}Plugin extends Plugin {
async onload() {
// Load settings, register UI, commands, events
await this.loadSettings();
this.addSettingTab(new {{plugin.classPrefix}}SettingTab(this.app, this));
this.registerCommands();
}
onunload() {
// Automatic cleanup for registered resources
}
}
Feature Modules (scaling beyond main.ts)
Larger plugins split features into modules that mirror the plugin lifecycle:
class FeatureModule {
constructor(plugin: Plugin, getSettings: () => {{plugin.classPrefix}}Settings) {}
async onload(): Promise<void>; // register commands, views, events
onunload(): void; // cleanup
}
- Register everything through the
Plugininstance (addCommand,registerEvent,registerDomEvent) inside the module'sonload()so Obsidian's automatic cleanup covers it on unload. - Prefer zero runtime npm dependencies: use
requestUrl/fetchfor API calls andchild_processfor external tools β keeps the bundle lean and eases community-plugin review.
Key API Patterns
- Commands:
this.addCommand({ id, name, callback })oreditorCallbackfor editor context - Ribbon icons:
this.addRibbonIcon(icon, title, callback) - Settings:
this.loadData()/this.saveData()withObject.assign(DEFAULT_SETTINGS, loaded) - Events:
this.registerEvent(this.app.vault.on('modify', callback)) - DOM events:
this.registerDomEvent(document, 'click', callback)(auto-cleanup) - File operations:
this.app.vault.read(file),this.app.vault.modify(file, content) - Modals: Extend
Modalclass withonOpen()/onClose() - Setting tabs: Extend
PluginSettingTabwithdisplay()method
Build Pipeline
npm installβ install deps (obsidian, @types/node, esbuild, typescript)npm run devβ esbuild watch mode (rebuilds on change)npm run buildβ production build with type checking (tsc -noEmit && esbuild)- Obsidian API is external (provided at runtime, not bundled)
Testing Obsidian Plugins
The obsidian package ships types only β the real module exists inside the app, so tests rely on a centralized mock:
- Module mock:
src/__mocks__/obsidian.ts, auto-loaded by the runner's module mocking, provides stub classes and helpers forTFile,TFolder,Plugin,Modal,Notice,normalizePath,requestUrl, etc. - No
obsidianimports in test files β tests exercise the mock, never the real module. - Mock factories (central test-utils directory) keep fixtures uniform:
createMockApp()β freshAppwith spy vault/metadataCache/workspacecreateMockPlugin(settingsOverrides?)β aPluginwith pre-loaded settingsmakeSettings(overrides?)β settings defaults deep-merged with overridesmockFile(path)β aTFileinstance for the given path
- UI classes are exempt from unit tests β anything extending
Modal,ItemView, orPluginSettingTabis tested indirectly through its callers; extract decision logic into pure functions instead.
Development Tips
- Use a dedicated development vault, not your primary vault
- Install Hot-Reload plugin for auto-reloading during dev
- Use
new Notice('message')for user-facing notifications - Settings persist to
data.jsonin the plugin directory