OB

obsidian-core-workflow-a

Generates a full project structure for new Obsidian plugins including build configs and manifests.

Install

mkdir -p .claude/skills/obsidian-core-workflow-a && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/9043" && unzip -o skill.zip -d .claude/skills/obsidian-core-workflow-a && rm skill.zip

Installs to .claude/skills/obsidian-core-workflow-a

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Create an Obsidian plugin from scratch with full project scaffolding.
69 charsno explicit “when” trigger
Beginner

Key capabilities

  • Initialize a Node.js project for an Obsidian plugin
  • Configure TypeScript and esbuild for plugin development
  • Create a manifest.json file for plugin metadata
  • Implement a Plugin subclass with ribbon icons and commands
  • Add a settings interface and settings tab for the plugin
  • Build the plugin into a production-ready main.js file

How it works

This skill scaffolds a complete Obsidian plugin project, including Node.js initialization, TypeScript configuration, esbuild setup, and the core plugin files like manifest.json and main.ts.

Inputs & outputs

You give it
An empty directory for the plugin
You get back
A complete plugin directory containing manifest.json, src/main.ts, esbuild.config.mjs, main.js, package.json, and tsconfig.json

When to use obsidian-core-workflow-a

  • Starting a new Obsidian plugin
  • Scaffolding a project structure
  • Configuring esbuild for plugins

About this skill

Obsidian Core Workflow A: Create a Plugin from Scratch

Overview

Build a complete Obsidian plugin from an empty directory. By the end you will have a working plugin with a ribbon icon, command palette entries, a settings tab, and a production esbuild build. Every file is shown in full -- no stubs.

Prerequisites

  • Node.js 18+ installed
  • Obsidian desktop app installed
  • A vault to test in (create a fresh vault at ~/ObsidianDev if needed)

Instructions

Step 1: Scaffold the project

set -euo pipefail

PLUGIN_NAME="my-obsidian-plugin"
mkdir -p "$PLUGIN_NAME/src"
cd "$PLUGIN_NAME"

# Initialize Node project
npm init -y

# Install Obsidian types and build tool
npm install --save-dev obsidian@latest typescript@latest esbuild@latest \
  @types/node@latest tslib@latest

# TypeScript config
cat > tsconfig.json << 'TSEOF'
{
  "compilerOptions": {
    "baseUrl": ".",
    "inlineSourceMap": true,
    "inlineSources": true,
    "module": "ESNext",
    "target": "ES2018",
    "allowJs": true,
    "noImplicitAny": true,
    "moduleResolution": "node",
    "importHelpers": true,
    "isolatedModules": true,
    "strictNullChecks": true,
    "lib": ["DOM", "ES2018", "ES2021.String"]
  },
  "include": ["src/**/*.ts"]
}
TSEOF

echo "Scaffolding complete."

Step 2: Create manifest.json

Every Obsidian plugin needs a manifest.json at the project root. This is what Obsidian reads to register the plugin.

{
  "id": "my-obsidian-plugin",
  "name": "My Obsidian Plugin",
  "version": "1.0.0",
  "minAppVersion": "1.0.0",
  "description": "A starter Obsidian plugin.",
  "author": "Your Name",
  "isDesktopOnly": false
}

Step 3: Write the esbuild config

// esbuild.config.mjs
import esbuild from "esbuild";
import process from "process";

const prod = process.argv[2] === "production";

const context = await esbuild.context({
  entryPoints: ["src/main.ts"],
  bundle: true,
  external: [
    "obsidian",
    "electron",
    "@codemirror/autocomplete",
    "@codemirror/collab",
    "@codemirror/commands",
    "@codemirror/language",
    "@codemirror/lint",
    "@codemirror/search",
    "@codemirror/state",
    "@codemirror/view",
    "@lezer/common",
    "@lezer/highlight",
    "@lezer/lr",
  ],
  format: "cjs",
  target: "es2018",
  logLevel: "info",
  sourcemap: prod ? false : "inline",
  treeShaking: true,
  outfile: "main.js",
});

if (prod) {
  await context.rebuild();
  process.exit(0);
} else {
  await context.watch();
}

Step 4: Write main.ts -- the full plugin

This single file contains the Plugin subclass, a settings interface with defaults, a settings tab, and three commands.

// src/main.ts
import {
  App,
  Editor,
  MarkdownView,
  Notice,
  Plugin,
  PluginSettingTab,
  Setting,
} from "obsidian";

// ── Settings ────────────────────────────────────────────────────────
interface MyPluginSettings {
  greeting: string;
  showRibbon: boolean;
}

const DEFAULT_SETTINGS: MyPluginSettings = {
  greeting: "Hello from My Plugin!",
  showRibbon: true,
};

// ── Plugin ──────────────────────────────────────────────────────────
export default class MyPlugin extends Plugin {
  settings: MyPluginSettings;

  async onload() {
    await this.loadSettings();

    // Ribbon icon -- shows a Notice when clicked
    if (this.settings.showRibbon) {
      this.addRibbonIcon("sparkles", "My Plugin: Greet", () => {
        new Notice(this.settings.greeting);
      });
    }

    // Command: show greeting as Notice
    this.addCommand({
      id: "show-greeting",
      name: "Show greeting",
      callback: () => {
        new Notice(this.settings.greeting);
      },
    });

    // Command: insert greeting at cursor (only available in editor)
    this.addCommand({
      id: "insert-greeting",
      name: "Insert greeting at cursor",
      editorCallback: (editor: Editor, view: MarkdownView) => {
        editor.replaceSelection(this.settings.greeting);
      },
    });

    // Command: count words in current note
    this.addCommand({
      id: "count-words",
      name: "Count words in current note",
      editorCallback: (editor: Editor) => {
        const text = editor.getValue();
        const count = text.split(/\s+/).filter(Boolean).length;
        new Notice(`Word count: ${count}`);
      },
    });

    // Status bar item
    const statusEl = this.addStatusBarItem();
    statusEl.setText("Plugin loaded");

    // Settings tab
    this.addSettingTab(new MyPluginSettingTab(this.app, this));

    console.log("MyPlugin loaded");
  }

  onunload() {
    console.log("MyPlugin unloaded");
  }

  async loadSettings() {
    this.settings = Object.assign(
      {},
      DEFAULT_SETTINGS,
      await this.loadData()
    );
  }

  async saveSettings() {
    await this.saveData(this.settings);
  }
}

// ── Settings Tab ────────────────────────────────────────────────────
class MyPluginSettingTab extends PluginSettingTab {
  plugin: MyPlugin;

  constructor(app: App, plugin: MyPlugin) {
    super(app, plugin);
    this.plugin = plugin;
  }

  display(): void {
    const { containerEl } = this;
    containerEl.empty();

    new Setting(containerEl)
      .setName("Greeting message")
      .setDesc("Text shown by the greet command and ribbon icon.")
      .addText((text) =>
        text
          .setPlaceholder("Hello from My Plugin!")
          .setValue(this.plugin.settings.greeting)
          .onChange(async (value) => {
            this.plugin.settings.greeting = value;
            await this.plugin.saveSettings();
          })
      );

    new Setting(containerEl)
      .setName("Show ribbon icon")
      .setDesc("Toggle the sparkles icon in the left ribbon.")
      .addToggle((toggle) =>
        toggle
          .setValue(this.plugin.settings.showRibbon)
          .onChange(async (value) => {
            this.plugin.settings.showRibbon = value;
            await this.plugin.saveSettings();
            new Notice("Reload plugin to apply ribbon change.");
          })
      );
  }
}

Step 5: Add npm scripts and build

Add these scripts to package.json:

{
  "scripts": {
    "dev": "node esbuild.config.mjs",
    "build": "node esbuild.config.mjs production"
  }
}

Build the plugin:

set -euo pipefail
npm run build
# Output: main.js at project root
ls -la main.js manifest.json

Step 6: Install into your vault and test

set -euo pipefail

VAULT="$HOME/ObsidianDev"
PLUGIN_ID="my-obsidian-plugin"

# Create plugin directory in vault
mkdir -p "$VAULT/.obsidian/plugins/$PLUGIN_ID"

# Copy build artifacts
cp main.js manifest.json "$VAULT/.obsidian/plugins/$PLUGIN_ID/"

echo "Plugin installed. Open Obsidian, enable it in Settings > Community plugins."

In Obsidian:

  1. Settings > Community plugins > Enable community plugins
  2. Find "My Obsidian Plugin" in the list, toggle it on
  3. Click the sparkles icon in the left ribbon
  4. Open command palette (Ctrl/Cmd+P), search "Show greeting"
  5. Open Settings > My Obsidian Plugin to change the greeting text

Output

A complete plugin directory containing:

  • manifest.json -- plugin metadata Obsidian reads
  • src/main.ts -- Plugin subclass with commands, ribbon icon, settings tab
  • esbuild.config.mjs -- bundler with watch mode support
  • main.js -- production build output
  • package.json + tsconfig.json -- standard Node/TS project files

Error Handling

ErrorCauseFix
Cannot find module 'obsidian'Missing dev dependencynpm install --save-dev obsidian
Plugin not in listmanifest.json missing or invalidVerify id field matches folder name
Ribbon icon missingInvalid icon nameUse a Lucide icon name (sparkles, file-text, search, etc.)
Settings not persistingForgot await this.saveData()Always await saveData in onChange
editorCallback command greyed outNo active editorOpen a markdown note first
Build fails with external errorForgot to externalize obsidianCheck external array in esbuild config

Examples

Minimal manifest.json for community submission:

{
  "id": "my-obsidian-plugin",
  "name": "My Obsidian Plugin",
  "version": "1.0.0",
  "minAppVersion": "1.0.0",
  "description": "Does one useful thing.",
  "author": "Your Name",
  "authorUrl": "https://github.com/yourname",
  "isDesktopOnly": false
}

Adding a hotkey-enabled command:

this.addCommand({
  id: "toggle-sidebar",
  name: "Toggle custom sidebar",
  // Users can assign a hotkey in Settings > Hotkeys
  callback: () => this.toggleSidebar(),
});

Resources

Next Steps

  • Add custom views and modals: see obsidian-core-workflow-b
  • Set up hot-reload development: see obsidian-local-dev-loop
  • Apply production patterns: see obsidian-sdk-patterns

Prerequisites

Node.js 18+ installedObsidian desktop app installedA vault to test in

Limitations

  • Plugin not appearing in list if manifest.json is missing or invalid
  • Ribbon icon missing if an invalid icon name is used
  • Settings not persisting if saveData() is not awaited

How it compares

This workflow provides a fully functional plugin with a ribbon icon, commands, and settings tab from scratch, unlike manually setting up each component.

Compared to similar skills

obsidian-core-workflow-a side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
obsidian-core-workflow-a (this skill)025dReviewBeginner
playwright-browser-automation297moReviewIntermediate
codex-skill125moReviewAdvanced
bullmq-specialist256moNo flagsIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

More by jeremylongshore

View all by jeremylongshore

analyzing-logs

jeremylongshore

Analyze application logs to detect performance issues, identify error patterns, and improve stability by extracting key insights.

14123

ollama-setup

jeremylongshore

Configure auto-configure Ollama when user needs local LLM deployment, free AI alternatives, or wants to eliminate hosted API costs. Trigger phrases: "install ollama", "local AI", "free LLM", "self-hosted AI", "replace OpenAI", "no API costs". Use when appropriate context detected. Trigger with relevant phrases based on skill purpose.

1167

backtesting-trading-strategies

jeremylongshore

Backtest crypto and traditional trading strategies against historical data. Calculates performance metrics (Sharpe, Sortino, max drawdown), generates equity curves, and optimizes strategy parameters. Use when user wants to test a trading strategy, validate signals, or compare approaches. Trigger with phrases like "backtest strategy", "test trading strategy", "historical performance", "simulate trades", "optimize parameters", or "validate signals".

1071

generating-database-seed-data

jeremylongshore

Process this skill enables AI assistant to generate realistic test data and database seed scripts for development and testing environments. it uses faker libraries to create realistic data, maintains relational integrity, and allows configurable data volumes. u... Use when working with databases or data models. Trigger with phrases like 'database', 'query', or 'schema'.

1033

cursor-codebase-indexing

jeremylongshore

Execute set up and optimize Cursor codebase indexing. Triggers on "cursor index setup", "codebase indexing", "index codebase", "cursor semantic search". Use when working with cursor codebase indexing functionality. Trigger with phrases like "cursor codebase indexing", "cursor indexing", "cursor".

885

testing-mobile-apps

jeremylongshore

Execute mobile app testing on iOS and Android devices/simulators. Use when performing specialized testing. Trigger with phrases like "test mobile app", "run iOS tests", or "validate Android functionality".

810

You might also like

playwright-browser-automation

lackeyjb

Complete browser automation with Playwright. Auto-detects dev servers, writes clean test scripts to /tmp. Test pages, fill forms, take screenshots, check responsive design, validate UX, test login flows, check links, automate any browser task. Use when user wants to test websites, automate browser interactions, validate web functionality, or perform any browser-based testing.

29146

codex-skill

feiskyer

Use when user asks to leverage codex, gpt-5, or gpt-5.1 to implement something (usually implement a plan or feature designed by Claude). Provides non-interactive automation mode for hands-off task execution without approval prompts.

12110

bullmq-specialist

davila7

BullMQ expert for Redis-backed job queues, background processing, and reliable async execution in Node.js/TypeScript applications. Use when: bullmq, bull queue, redis queue, background job, job queue.

2595

markdown-to-html

github

Convert Markdown files to HTML similar to `marked.js`, `pandoc`, `gomarkdown/markdown`, or similar tools; or writing custom script to convert markdown to html and/or working on web template systems like `jekyll/jekyll`, `gohugoio/hugo`, or similar web templating systems that utilize markdown documents, converting them to html. Use when asked to "convert markdown to html", "transform md to html", "render markdown", "generate html from markdown", or when working with .md files and/or web a templating system that converts markdown to HTML output. Supports CLI and Node.js workflows with GFM, CommonMark, and standard Markdown flavors.

1662

run-nx-generator

nrwl

Run Nx generators with prioritization for workspace-plugin generators. Use this when generating code, scaffolding new features, or automating repetitive tasks in the monorepo.

571

desktop

lobehub

Electron desktop development guide. Use when implementing desktop features, IPC handlers, controllers, preload scripts, window management, menu configuration, or Electron-specific functionality. Triggers on desktop app development, Electron IPC, or desktop local tools implementation.

941

Search skills

Search the agent skills registry