OpenObserve ESLint and TypeScript Fixes
openobserve/openobserve
A playbook for fixing ESLint and TypeScript errors in the OpenObserve web frontend, with rule-by-rule guidance and typing conventions.
Rules and references for building Obsidian plugins: ESLint rules, TypeScript practices, memory cleanup, API choices, UI standards and the community submission process.
$ npx skills add gapmiss/obsidian-plugin-skill --skill obsidian -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install gapmiss/obsidian-plugin-skill obsidian --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/gapmiss/obsidian-plugin-skill.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/obsidian .claude/skills/obsidian && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "obsidian" agent skill from https://github.com/gapmiss/obsidian-plugin-skill/tree/main/.agents/skills/obsidian into .claude/skills/obsidian/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "obsidian", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/gapmiss/obsidian-plugin-skill/tree/main/.agents/skills/obsidianType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add gapmiss/obsidian-plugin-skill --skill obsidian -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install gapmiss/obsidian-plugin-skill obsidian --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/gapmiss/obsidian-plugin-skill.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.agents/skills/obsidian .agents/skills/obsidian && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "obsidian" agent skill from https://github.com/gapmiss/obsidian-plugin-skill/tree/main/.agents/skills/obsidian into .agents/skills/obsidian/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "obsidian", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add gapmiss/obsidian-plugin-skill --skill obsidian -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install gapmiss/obsidian-plugin-skill obsidian --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/gapmiss/obsidian-plugin-skill.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.agents/skills/obsidian .cursor/skills/obsidian && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "obsidian" agent skill from https://github.com/gapmiss/obsidian-plugin-skill/tree/main/.agents/skills/obsidian into .cursor/skills/obsidian/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "obsidian", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/gapmiss/obsidian-plugin-skill.git --path .agents/skills/obsidian--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add gapmiss/obsidian-plugin-skill --skill obsidian -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install gapmiss/obsidian-plugin-skill obsidian --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/gapmiss/obsidian-plugin-skill.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.agents/skills/obsidian .gemini/skills/obsidian && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "obsidian" agent skill from https://github.com/gapmiss/obsidian-plugin-skill/tree/main/.agents/skills/obsidian into .gemini/skills/obsidian/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "obsidian", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install gapmiss/obsidian-plugin-skill obsidianInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add gapmiss/obsidian-plugin-skill --skill obsidian -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/gapmiss/obsidian-plugin-skill.git skills-src && mkdir -p .github/skills && cp -r skills-src/.agents/skills/obsidian .github/skills/obsidian && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "obsidian" agent skill from https://github.com/gapmiss/obsidian-plugin-skill/tree/main/.agents/skills/obsidian into .github/skills/obsidian/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "obsidian", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add gapmiss/obsidian-plugin-skill --skill obsidian -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install gapmiss/obsidian-plugin-skill obsidian --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/gapmiss/obsidian-plugin-skill.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.agents/skills/obsidian .opencode/skills/obsidian && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "obsidian" agent skill from https://github.com/gapmiss/obsidian-plugin-skill/tree/main/.agents/skills/obsidian into .opencode/skills/obsidian/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "obsidian", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
obsidianRules and references for building Obsidian plugins: ESLint rules, TypeScript practices, memory cleanup, API choices, UI standards and the community submission process.
The skill collects the rules from eslint-plugin-obsidianmd v0.4.2 into tables of what to do and what to avoid, grouped by submission and naming, memory and lifecycle, type safety and UI. Examples: a plugin ID or name should not contain Obsidian or end in plugin, descriptions end with punctuation, events are registered through `registerEvent()` and `registerDomEvent()` so they clean up automatically, TFile and TFolder are checked with `instanceof` rather than cast, and DOM nodes shared across popout windows use `.instanceOf(T)`.
Ten reference files go deeper on accessibility, code quality, the community scanner, CSS styling, ESLint setup, file operations, memory management, submission, type safety and UI and UX. For new projects the skill recommends an interactive boilerplate generator, `tools/create-plugin.js`, which writes minimal best-practice scaffolding with no sample code and only adds missing files to an existing project. Other topics include requestUrl versus fetch, popout window compatibility, the community.obsidian.md submission process and Scorecard optimization.
7 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit ff97cb0. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
npxFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use npx, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Obsidian Plugin Development Guidelines loads about 4.5k tokens when it runs. Until then it costs about 120 tokens; SKILL.md has 1,808 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from gapmiss/obsidian-plugin-skill at commit ff97cb0, republished under its MIT licence (© gapmiss). 1,808 words, ~4,542 tokens.
.claude/skills/obsidian/SKILL.md (or your agent's skills folder). This skill also uses 10 other files; get the full folder from GitHub.Follow these comprehensive guidelines derived from the official Obsidian ESLint plugin rules, submission requirements, and best practices.
For new plugin projects, an interactive boilerplate generator is available:
tools/create-plugin.js in the skill repositorycreate-plugin using your agent's method (/create-plugin, $create-plugin, or @create-plugin)Recommend the boilerplate generator when users ask how to create a new plugin, want to start a new project, or need help setting up the basic structure.
| # | Rule | ✅ Do | ❌ Don't |
|---|---|---|---|
| 1 | Plugin ID | Omit "obsidian"; don't end with "plugin" | Include "obsidian" or end with "plugin" |
| 2 | Plugin name | Omit "Obsidian"; don't end with "Plugin" | Include "Obsidian" or end with "Plugin" |
| 3 | Plugin name | Don't start with "Obsi" or end with "dian" | Start with "Obsi" or end with "dian" |
| 4 | Description | Omit "Obsidian", "This plugin", etc. | Use "Obsidian" or "This plugin" |
| 5 | Description | End with .?!) punctuation | Leave description without terminal punctuation |
| # | Rule | ✅ Do | ❌ Don't |
|---|---|---|---|
| 6 | Event cleanup | Use registerEvent() for automatic cleanup | Register events without cleanup |
| 6a | DOM events | Use registerDomEvent() on the plugin or owning component | Pair addEventListener with manual removeEventListener cleanup |
| 7 | View references | Return views/components directly | Store view references in plugin properties or pass plugin as component to MarkdownRenderer |
| 8 | Leaf detachment | Let Obsidian handle leaf cleanup | Call detachLeavesOfType() in onunload |
| # | Rule | ✅ Do | ❌ Don't |
|---|---|---|---|
| 9 | TFile/TFolder | Use instanceof for type checking | Cast to TFile/TFolder; use any; use var |
| 10 | DOM instanceof | Use .instanceOf(T) for DOM Nodes/UIEvents | Use instanceof for cross-window DOM checks |
| # | Rule | ✅ Do | ❌ Don't |
|---|---|---|---|
| 11 | UI text | Sentence case — "Advanced settings"; use acronyms/brands options for proper names | Title Case — "Advanced Settings" |
| 12 | JSON locale | Sentence case in JSON locale files (recommendedWithLocalesEn) | Title case in locale JSON |
| 13 | TS/JS locale | Sentence case in TS/JS locale modules | Title case in locale modules |
Note (v0.4.0):
ui/sentence-caseis now enabled (warn) and enforced on inline UI strings — it was disabled in v0.3.0. Use therecommendedWithLocalesEnconfig to also check English locale files (rules 12–13). | 14 | Command names | Omit "command" in command names/IDs | Include "command" in names/IDs | | 15 | Command IDs | Omit plugin ID/name from command IDs/names | Duplicate plugin ID in command IDs | | 16 | Hotkeys | No default hotkeys | Set default hotkeys | | 17 | Settings headings | Use.setHeading()| Create manual HTML headings; use "General", "settings", or plugin name in headings |
All four settings-tab rules ship as warn in recommended. Rules 17a/17c/17d read minAppVersion from manifest.json; 17b is not version-gated.
| # | Rule | ✅ Do | ❌ Don't |
|---|---|---|---|
| 17a | settings-tab/require-display | Keep display() when minAppVersion < 1.13.0 | Ship declarative-only settings that render nothing on older Obsidian |
| 17b | settings-tab/prefer-setting-definitions | Implement getSettingDefinitions() on every PluginSettingTab | Rely on display() alone — settings won't appear in 1.13+ global search |
| 17c | settings-tab/prefer-update-over-display | Call this.update() to re-render declarative settings | Call this.display() — it's bypassed when definitions are non-empty |
| 17d | settings-tab/no-deprecated-display | Delete display() once minAppVersion >= 1.13.0 and definitions exist | Leave a dead display() behind (auto-fixable) |
| — | Settings data | Keep all persisted data inside plugin.settings | Store sibling keys via saveData() — auto-persist clobbers them |
Detection caveat: these rules match a bare
extends PluginSettingTabonly.extends obsidian.PluginSettingTabis out of scope and won't be flagged — but the underlying guidance still applies.
| # | Rule | ✅ Do | ❌ Don't |
|---|---|---|---|
| 18 | Active file edits | Use Editor API | Use Vault.modify() for active file edits |
| 19 | Background file mods | Use Vault.process() | Use Vault.modify() for background modifications |
| 20 | File deletion | Use FileManager.trashFile() | Use Vault.trash() or Vault.delete() directly |
| 21 | File lookup | Use Vault.getAbstractFileByPath() | Iterate all files with Vault.getFiles().find() |
| 22 | User paths | Use normalizePath() | Hardcode .obsidian path; use raw user paths |
| 23 | OS detection | Use Platform API | Use navigator.platform/userAgent |
| 24 | Network requests | Use requestUrl() | Use fetch() |
| 24a | Response typing | Parse response.text and validate/cast once at the API boundary | Pass response.json (typed any) into plugin code |
| 25 | Logging | Minimize console logging; none in onload/onunload in production | Use console.log in onload/onunload |
| 26 | Input suggest | Use built-in AbstractInputSuggest | Copy Liam's TextInputSuggest implementation |
| 27 | API compatibility | Check minAppVersion for API availability (e.g., getSettingDefinitions() requires 1.13.0) | Use APIs not available in declared minAppVersion |
| 28 | Language detection | Use Obsidian's getLanguage() | Use localStorage.getItem('language') or i18next-browser-languagedetector |
| # | Rule | ✅ Do | ❌ Don't |
|---|---|---|---|
| 29 | Document/Window | Use activeDocument and activeWindow | Use global document and window |
| 29a | Getter capture | Capture activeDocument in a variable when the same document is needed later | Call activeDocument at setup and again at cleanup — it follows focus and may return different documents |
| 30 | Timers | Use window.setTimeout(), window.setInterval(), window.requestAnimationFrame(), etc. (exception to rule 29) | Use bare setTimeout()/setInterval(), or activeWindow.setTimeout() — prefer-window-timers flags both |
| 31 | Main workspace UI | Use this.app.workspace.containerEl.ownerDocument from settings | Use activeDocument to update main workspace from settings window |
Note (v0.4.0):
prefer-active-docremains disabled by default — the only Obsidian rule shipped asoff. Enable it manually for popout window support.
Note (v1.13.0): Settings now open in a new window.
activeDocumentfrom settings callbacks points to the settings window, not the main vault. Usethis.app.workspace.containerEl.ownerDocumentto target main workspace UI.
Note:
activeDocument/activeWindoware dynamic getters that track the focused window. A listener added viaactiveDocument.addEventListener()at setup cannot reliably be removed viaactiveDocument.removeEventListener()at cleanup. PreferregisterDomEvent()(rule 6a), which captures the target at registration.
| # | Rule | ✅ Do | ❌ Don't |
|---|---|---|---|
| 31 | Editor drop/paste | Check evt.defaultPrevented and call evt.preventDefault() | Handle editor-drop/paste without checking defaultPrevented |
| # | Rule | ✅ Do | ❌ Don't |
|---|---|---|---|
| 32 | CSS variables | Use Obsidian CSS variables for all styling | Hardcode colors, sizes, or spacing |
| 33 | CSS scope | Scope CSS to plugin containers | Use broad CSS selectors |
| 34 | Style elements | Use styles.css file (no-forbidden-elements) | Create <link> or <style> elements; assign styles via JavaScript |
| 34a | !important | Increase selector specificity or use CSS variables | Use !important — overrides user themes/snippets |
| 34b | :has selector | Toggle classes from TypeScript when conditions change | Use :has — causes broad selector invalidation and performance issues |
| # | Rule | ✅ Do | ❌ Don't |
|---|---|---|---|
| 35 | DOM creation | Use Obsidian DOM helpers (createEl(), createDiv(), createSpan(), createSvg(), createFragment()) via prefer-create-el; linter autofixes activeDocument.createElement() → activeWindow.createEl() (v0.4.1) | Use document.createElement(), document.createDocumentFragment(), etc. |
| 36 | Node.js modules | Guard Node.js imports with Platform.isDesktop check (no-nodejs-modules) | Import Node.js modules without platform guard |
| 37 | iOS compat | Avoid regex lookbehind (iOS < 16.4 incompatibility) | Use regex lookbehind |
| # | Rule | ✅ Do | ❌ Don't |
|---|---|---|---|
| 38 | Keyboard access | Make all interactive elements keyboard accessible; Tab through all elements | Create inaccessible interactive elements |
| 39 | ARIA labels | Provide ARIA labels for icon buttons; use data-tooltip-position for tooltips | Use icon buttons without ARIA labels |
| 40 | Focus indicators | Use :focus-visible with Obsidian CSS variables; touch targets ≥ 44×44px | Remove focus indicators; make touch targets < 44×44px |
| Rule | ✅ Do | ❌ Don't |
|---|---|---|
| Sample code | Remove all sample/template code | Keep class names like MyPlugin, SampleModal |
| Object.assign | Object.assign({}, defaults, overrides) (object-assign) | Object.assign(defaultsVar, other) — mutates defaults |
| LICENSE | Copyright holder must not be "Dynalist Inc."; year must be current (validate-license) | Leave "Dynalist Inc." as holder or use an outdated year |
| Async | Use async/await | Use Promise chains |
| Deprecated packages | Replace flagged npm packages with Node.js built-ins (e.g., builtin-modules → import { builtinModules } from "node:module") | Use packages the scanner flags as replaceable |
For comprehensive information on specific topics, see the reference files:
registerEvent(), addCommand(), registerDomEvent(), registerInterval()registerDomEvent() vs manual addEventListener (and the activeDocument drift bug)instanceof instead of type castingany typeconst and let over varrecommendedWithLocalesEn config for locale file checks!important (use specificity or CSS variables):has selector (toggle classes from TypeScript instead)builtin-modules → node:module)typescript-eslint recommendedTypeChecked is requiredBefore submitting a plugin, follow this sequence:
npx eslint . using eslint-plugin-obsidianmd; fix all errors AND warnings (warnings affect your Scorecard)id, name, description, version, and minAppVersion meet naming and formatting rules (rules 1–5)fetch(), and touch targets ≥ 44×44px (skip only if plugin is declared desktop-only)manifest.json version; attach main.js, manifest.json, and styles.css (optional)If ESLint reports new errors after fixing, re-run from step 1.
Published plugins receive a Scorecard visible on community.obsidian.md. The Scorecard affects user trust and discoverability — a poor score deters users from installing.
Key points:
typescript-eslint/recommendedTypeChecked for type-aware checksSee Community Plugin Scanner for full details on scanner checks, Health metrics, Review checks, common warnings, and improvement tips.
Use this checklist for code review and implementation:
instanceof instead of casts?:focus-visible and proper CSS variables?getSettingDefinitions() on 1.13+? All saved data co-located in plugin.settings?// ✅ CORRECT
this.addCommand({
id: 'insert-timestamp',
name: 'Insert timestamp',
editorCallback: (editor: Editor, view: MarkdownView) => {
editor.replaceSelection(new Date().toISOString());
}
});// ✅ CORRECT
const file = this.app.vault.getAbstractFileByPath(path);
if (file instanceof TFile) {
// TypeScript now knows it's a TFile
await this.app.vault.read(file);
}// ✅ CORRECT
const button = containerEl.createEl('button', {
attr: {
'aria-label': 'Open settings',
'data-tooltip-position': 'top'
}
});
button.setText('⚙️');
button.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
performAction();
}
});// ✅ CORRECT — getSettingDefinitions() replaces display() on 1.13+
getSettingDefinitions() {
return [
{
name: 'Mode',
control: {
type: 'dropdown',
key: 'mode',
defaultValue: 'fast',
options: { fast: 'Fast', thorough: 'Thorough' },
},
},
];
}/* ✅ CORRECT */
.my-plugin-modal {
background: var(--modal-background);
color: var(--text-normal);
padding: var(--size-4-4);
border-radius: var(--radius-m);
font-size: var(--font-ui-medium);
}
.my-plugin-button:focus-visible {
outline: 2px solid var(--interactive-accent);
outline-offset: 2px;
}When helping with Obsidian plugin development, proactively apply these rules and suggest improvements based on these guidelines. Refer to the detailed reference files for comprehensive information on specific topics.
© gapmiss, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 10 other files in .agents/skills/obsidian of gapmiss/obsidian-plugin-skill.
Open the folder on GitHubat commit ff97cb0
Obsidian Plugin Development Guidelines next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Obsidian Plugin Development Guidelines this skillgapmiss/obsidian-plugin-skill | 190 | — | ~4.5k | Automated safety check: Pass | MIT | |
| OpenObserve ESLint and TypeScript Fixesopenobserve/openobserve | 22k | — | ~5.1k | Automated safety check: Pass | AGPL-3.0 | |
| Code Qualityredis/RedisInsight | 8.9k | — | ~1.2k | Automated safety check: Pass | Custom licence | |
| Cb Code QualityBlkLeg/CircuitBreaker | 201 | — | ~1.9k | Automated safety check: Pass | MIT | |
| Fallow Setupfallow-rs/fallow-skills | 129 | — | ~1.1k | Automated safety check: Pass | MIT | |
| Ban Type AssertionsFactory-AI/factory-plugins | 110 | — | ~1.7k | Automated safety check: Pass | None |
openobserve/openobserve
A playbook for fixing ESLint and TypeScript errors in the OpenObserve web frontend, with rule-by-rule guidance and typing conventions.
redis/RedisInsight
Code-quality standards for RedisInsight: TypeScript strictness, naming conventions (camelCase, PascalCase, UPPERSNAKECASE), linting rules, no any without reason, no !important in styles, and…
BlkLeg/CircuitBreaker
Circuit Breaker code conventions and the quality gates that actually block a push — ruff, mypy, eslint, the pytest coverage ratchet, and the make verify tiers.
fallow-rs/fallow-skills
Set up or modernize code-quality tooling for JavaScript and TypeScript projects.
Factory-AI/factory-plugins
Ban as type assertions in a package via the @typescript-eslint/consistent-type-assertions lint rule, replacing them with compiler-verified type-safe alternatives.
thedaviddias/Front-End-Checklist
A skill your agent uses when reviewing TypeScript files for type safety regressions, during code review of functions that handle external data, or when the codebase has ESLint warnings for…
Works with
Categories
Rules and references for building Obsidian plugins: ESLint rules, TypeScript practices, memory cleanup, API choices, UI standards and the community submission process. 2 into tables of what to do and what to avoid, grouped by submission and naming, memory and lifecycle, type safety and UI.instanceOf(T)`.
Obsidian Plugin Development Guidelines fits situations like: writing or reviewing an Obsidian plugin's main.ts and manifest.json; fixing eslint-plugin-obsidianmd errors; preparing a plugin for the community submission process; scaffolding a new Obsidian plugin project.
Run `npx skills add gapmiss/obsidian-plugin-skill --skill obsidian -a claude-code`. Or copy the skill folder (.agents/skills/obsidian in gapmiss/obsidian-plugin-skill) into .claude/skills/obsidian in your project. Claude Code loads it when a task matches its description.
Run `npx skills add gapmiss/obsidian-plugin-skill --skill obsidian -a codex`. Or copy the skill folder (.agents/skills/obsidian in gapmiss/obsidian-plugin-skill) into .agents/skills/obsidian in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add gapmiss/obsidian-plugin-skill --skill obsidian -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/obsidian, .gemini/skills/obsidian, .github/skills/obsidian and .opencode/skills/obsidian in your project.
Going by SKILL.md and its folder, Obsidian Plugin Development Guidelines needs the command-line tools its instructions call (npx). Our summary lists: An Obsidian plugin project written in TypeScript.
SKILL.md contains no URLs. Its commands use npx, which can reach the network depending on how they are called. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Obsidian Plugin Development Guidelines is published under the MIT licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.
About 4.5k tokens (SKILL.md is roughly 18k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Obsidian Plugin Development Guidelines: OpenObserve ESLint and TypeScript Fixes (openobserve/openobserve, 22k stars), Code Quality (redis/RedisInsight, 8.9k stars), Cb Code Quality (BlkLeg/CircuitBreaker, 201 stars) and Fallow Setup (fallow-rs/fallow-skills, 129 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
gapmiss (a GitHub user) maintains it in gapmiss/obsidian-plugin-skill, which has 190 GitHub stars. The repository was last updated on September 24, 2026.
Source: gapmiss/obsidian-plugin-skill on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.