OB

obsidian-prod-checklist

Checklist for validating manifest files, production build quality, and submission readiness for Obsidian plugins.

Install

mkdir -p .claude/skills/obsidian-prod-checklist && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/1596" && unzip -o skill.zip -d .claude/skills/obsidian-prod-checklist && rm skill.zip

Installs to .claude/skills/obsidian-prod-checklist

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.

Pre-release plugin verification checklist for Obsidian community plugins.
73 charsno explicit “when” trigger
Intermediate

Key capabilities

  • Validate required fields and format in manifest.json.
  • Verify version consistency between manifest.json, versions.json, and package.json.
  • Check production build output for main.js existence, size, and absence of inline source maps.
  • Scan source code for unguarded console statements and dangerous eval/Function() calls.
  • Review plugin code for proper resource cleanup in `onunload()` to prevent memory leaks.
  • Generate a pre-release validation script and a checklist summary.

How it works

This skill provides a checklist and scripts to validate an Obsidian plugin's manifest, build artifacts, and code quality against community guidelines. It checks for common issues like missing fields, version mismatches, and memory leaks.

Inputs & outputs

You give it
Obsidian plugin project files (manifest.json, versions.json, package.json, source code)
You get back
Validation reports and warnings for production readiness

When to use obsidian-prod-checklist

  • Validating manifest.json structure
  • Preparing plugin for community release
  • Reviewing code before submission
  • Verifying plugin compatibility

About this skill

Obsidian Prod Checklist

Overview

Pre-release verification for Obsidian plugins covering manifest validation, production build quality, mobile compatibility, memory leak prevention, settings migration, and community plugin submission readiness.

Prerequisites

  • Completed plugin development with all features working
  • Tested in at least one vault manually
  • GitHub repository with source code committed
  • Node.js build toolchain configured

Instructions

Step 1: Validate manifest.json

// Run: node -e '<paste this>'
const m = require('./manifest.json');

const required = ['id', 'name', 'version', 'minAppVersion', 'description', 'author'];
const missing = required.filter(f => !m[f]);
if (missing.length) {
  console.error('FAIL: Missing fields:', missing.join(', '));
  process.exit(1);
}

// id must be kebab-case, no spaces
if (!/^[a-z0-9-]+$/.test(m.id)) {
  console.error('FAIL: id must be lowercase alphanumeric with hyphens:', m.id);
  process.exit(1);
}

// minAppVersion should be a recent Obsidian version
const [major, minor] = m.minAppVersion.split('.').map(Number);
if (major < 1 || (major === 1 && minor < 4)) {
  console.warn('WARN: minAppVersion', m.minAppVersion, 'is very old — consider 1.5.0+');
}

console.log('manifest.json OK:', m.id, 'v' + m.version, '(requires Obsidian >=' + m.minAppVersion + ')');

Step 2: Validate versions.json

// Run: node -e '<paste this>'
const manifest = require('./manifest.json');
const versions = require('./versions.json');
const pkg = require('./package.json');

let fail = false;

// manifest.version should match package.json version
if (manifest.version !== pkg.version) {
  console.error('FAIL: manifest.version (' + manifest.version + ') !== package.json (' + pkg.version + ')');
  fail = true;
}

// versions.json must have an entry for current version
if (!versions[manifest.version]) {
  console.error('FAIL: versions.json missing entry for', manifest.version);
  fail = true;
} else if (versions[manifest.version] !== manifest.minAppVersion) {
  console.error('FAIL: versions.json[' + manifest.version + '] = ' +
    versions[manifest.version] + ' but manifest.minAppVersion = ' + manifest.minAppVersion);
  fail = true;
}

if (fail) process.exit(1);
console.log('versions.json OK: all versions consistent');

Step 3: Production Build Checks

set -euo pipefail
# Clean build
rm -f main.js
npm ci
npm run build

# Verify main.js exists and is reasonable size
test -f main.js || { echo "FAIL: main.js not generated"; exit 1; }
SIZE=$(wc -c < main.js)
echo "main.js: $SIZE bytes"

# No inline source maps in production (increases file size significantly)
if grep -q "sourceMappingURL=data:" main.js; then
  echo "WARN: Inline sourcemaps detected — remove for production"
  echo "  Set sourcemap: false in esbuild.config.mjs"
fi

# No sourcemap file should ship
if [ -f main.js.map ]; then
  echo "WARN: main.js.map exists — exclude from release assets"
fi

# styles.css check
if [ -f styles.css ]; then
  echo "styles.css: $(wc -c < styles.css) bytes — will be included in release"
else
  echo "No styles.css (OK if plugin has no custom styles)"
fi

Step 4: Code Quality — No console.log in Production

set -euo pipefail
# Obsidian reviewers reject plugins with console.log in production code
# Check source files (not the built main.js which may be minified)
HITS=$(grep -rn "console\.log\|console\.warn\|console\.info" src/ --include="*.ts" | grep -v "// DEBUG" | grep -v "\.test\." || true)

if [ -n "$HITS" ]; then
  echo "WARN: console statements found in source (remove or guard with DEBUG flag):"
  echo "$HITS"
else
  echo "OK: No unguarded console statements in src/"
fi

# Check for eval() or Function() constructor — immediate rejection
DANGEROUS=$(grep -rn "eval(\|new Function(" src/ --include="*.ts" || true)
if [ -n "$DANGEROUS" ]; then
  echo "FAIL: eval/Function() found — Obsidian team will reject this:"
  echo "$DANGEROUS"
  exit 1
fi

Step 5: Memory Leak Check — Proper onunload Cleanup

Review your main.ts for proper resource cleanup:

// GOOD: All resources cleaned up in onunload
export default class MyPlugin extends Plugin {
  private observer: MutationObserver | null = null;
  private intervalId: number | null = null;

  async onload() {
    // Register events via this.registerEvent — auto-cleaned
    this.registerEvent(
      this.app.workspace.on('file-open', this.handleFileOpen.bind(this))
    );

    // Register intervals via this.registerInterval — auto-cleaned
    this.intervalId = window.setInterval(() => this.sync(), 60000);
    this.registerInterval(this.intervalId);

    // DOM observers need manual cleanup
    this.observer = new MutationObserver(this.handleMutation.bind(this));
    this.observer.observe(document.body, { childList: true });
  }

  onunload() {
    // Clean up anything NOT registered via this.register*
    this.observer?.disconnect();
    this.observer = null;
  }
}

Common leak sources to audit:

  • setInterval / setTimeout not using this.registerInterval
  • addEventListener without matching removeEventListener
  • MutationObserver or ResizeObserver without disconnect()
  • WebSocket or EventSource connections without close()
  • Detached DOM nodes held in class properties

Step 6: Mobile Compatibility

// Check if running on mobile
import { Platform } from 'obsidian';

if (Platform.isMobile) {
  // Disable features that only work on desktop
  // - No child_process or fs access
  // - No Electron APIs (clipboard, shell, dialog)
  // - Touch targets must be >= 44px
}

// If your plugin is desktop-only, set in manifest.json:
// "isDesktopOnly": true

Test on mobile:

  1. Build and release (even a beta via BRAT)
  2. Install on iOS/Android Obsidian
  3. Verify: settings tab renders, commands work, no crashes on open/close
  4. Check touch targets are large enough (44px minimum)

Step 7: Settings Migration

// Handle upgrades from older settings versions
interface MyPluginSettings {
  version: number;          // Track settings schema version
  greeting: string;
  // v2 added:
  showInStatusBar: boolean;
}

const DEFAULT_SETTINGS: MyPluginSettings = {
  version: 2,
  greeting: 'Hello!',
  showInStatusBar: true,
}

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

  // Migrate from v1 to v2
  if (!saved?.version || saved.version < 2) {
    this.settings.showInStatusBar = true;  // new default
    this.settings.version = 2;
    await this.saveSettings();
    console.log('Settings migrated to v2');
  }
}

Step 8: README and Documentation

Verify your README includes:

  • Clear description of what the plugin does
  • Installation instructions (community plugins search + manual)
  • Screenshots or GIFs of the plugin in action
  • Configuration options explained
  • Known limitations
set -euo pipefail
# Basic README checks
test -f README.md || { echo "FAIL: No README.md"; exit 1; }

# Check for screenshots (common requirement for discoverability)
if grep -qi "screenshot\|\.png\|\.gif\|\.jpg" README.md; then
  echo "OK: README references images"
else
  echo "WARN: No screenshots in README — strongly recommended for community listing"
fi

echo "README.md: $(wc -l < README.md) lines"

Output

  • Validated manifest.json with all required fields and correct formatting
  • Consistent versions across manifest.json, package.json, and versions.json
  • Production main.js without sourcemaps or debug artifacts
  • Clean source code: no console.log, no eval, no dynamic code loading
  • Verified onunload() cleanup for all registered resources
  • Mobile compatibility confirmed (or isDesktopOnly set)
  • Settings migration for users upgrading from previous versions
  • README with screenshots and installation instructions

Error Handling

IssueCauseSolution
PR rejected: missing fieldsIncomplete manifest.jsonRun Step 1 validation
PR rejected: console.logDebug logging left inRemove or guard with build-time flag
Plugin crashes on mobileDesktop-only API usedSet isDesktopOnly: true or gate with Platform.isMobile
Settings lost on updateNo migration logicImplement version-based migration (Step 7)
Build includes sourcemapsesbuild configSet sourcemap: false for production
Styles not appliedMissing styles.css in releaseInclude in GitHub release assets
Old settings break new versionSchema changedObject.assign({}, DEFAULT_SETTINGS, saved) handles missing keys

Examples

Quick Pre-Release Validation Script

set -euo pipefail
echo "=== Obsidian Plugin Pre-Release Check ==="

# Build
npm ci && npm run build
test -f main.js || { echo "FAIL: no main.js"; exit 1; }

# Manifest
node -e "const m=require('./manifest.json'); \
  ['id','name','version','minAppVersion','description','author'].forEach(f => { \
    if(!m[f]) { console.error('MISSING:', f); process.exit(1); } \
  }); console.log('Manifest OK:', m.id, 'v'+m.version)"

# Versions
node -e "const m=require('./manifest.json'), v=require('./versions.json'); \
  if(!v[m.version]) { console.error('versions.json missing', m.version); process.exit(1); } \
  console.log('Versions OK')"

# No sourcemaps
grep -q "sourceMappingURL=data:" main.js && echo "WARN: inline sourcemaps" || echo "No sourcemaps OK"

# No console.log
COUNT=$(grep -rc "console\.\(log\|warn\|info\)" src/ --include="*.ts" 2>/dev/null | awk -F: '{s+=$2}END{print s}')
[ "$COUNT" -gt 0 ] && echo "WARN: $COUNT console statements in src/" || echo "No console OK"

echo "=== Done ==="

Checklist Summary Format

After running all checks, produce a summary:

Pre-Release Report: my-plugin v1.2.0
  [x] manifest.json — all fields present, id=my-plugin
  [x] versions.json — 1.2.0 maps to minAppVersion 1.5.0
  [x] Bui

---

*Content truncated.*

Prerequisites

Completed plugin development with all features workingTested in at least one vault manuallyGitHub repository with source code committedNode.js build toolchain configured

Limitations

  • Mobile compatibility requires manual testing if `isDesktopOnly` is false.

How it compares

This skill automates and standardizes the pre-release verification process for Obsidian plugins, unlike manual checks against documentation.

Compared to similar skills

obsidian-prod-checklist side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
obsidian-prod-checklist (this skill)327dReviewIntermediate
codex-skill125moReviewAdvanced
lokalise-upgrade-migration127dCautionAdvanced
windsurf-linting-config12moReviewIntermediate

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

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

lokalise-upgrade-migration

jeremylongshore

Analyze, plan, and execute Lokalise SDK upgrades with breaking change detection. Use when upgrading Lokalise SDK versions, detecting deprecations, or migrating to new API versions. Trigger with phrases like "upgrade lokalise", "lokalise migration", "lokalise breaking changes", "update lokalise SDK", "analyze lokalise version".

12

windsurf-linting-config

jeremylongshore

Configure and enforce code quality with AI-assisted linting. Activate when users mention "configure linting", "eslint setup", "code quality rules", "linting configuration", or "code standards". Handles linting tool configuration. Use when configuring systems or services. Trigger with phrases like "windsurf linting config", "windsurf config", "windsurf".

12

customaize-agent:create-hook

LAI-YEN-CHUN

Create and configure git hooks with intelligent project analysis, suggestions, and automated testing

00

fix-dependabot-alerts

microsoft

Fix Dependabot security alerts by updating vulnerable npm dependencies. Use when the user mentions "dependabot", "security alerts", "vulnerability", "CVE", or wants to update packages with security issues.

1872

dependency-upgrade

wshobson

Manage major dependency version upgrades with compatibility analysis, staged rollout, and comprehensive testing. Use when upgrading framework versions, updating major dependencies, or managing breaking changes in libraries.

26240

Search skills

Search the agent skills registry