Publishing to the Obsidian community plugin list
What Obsidian requires, and where this repo stands.
Already in place
manifest.json—id(creative-writer, must never change),name,version,minAppVersion,description,author,isDesktopOnly: true.versions.json— maps plugin version → minimum app version. Updated bynpm version.LICENSE— MIT, plus CC-BY attribution for the bundled concreteness norms.README.md— what it does, how to install, settings, data/licensing, model caveats.- Build:
npm run build→main.js(gitignored; attached to releases, not committed). - Guidelines compliance checked: no
innerHTMLwith untrusted content (model text goes in as text nodes), no default hotkeys, commands named without the plugin name, sentence-case settings,onunloadrestores the body class, noconsole.log, no Node/Electron APIs (the only network call goes throughrequestUrl). npm run lintruns the directory's own rule set locally:eslint-plugin-obsidianmd(recommended config, warnings fail) and stylelint withstylelint-no-unsupported-browser-featuresagainst the Chromium behind Obsidian 1.6.5 (.browserslistrc). CI,release:checkand the release workflow all run it, so a failing lint never becomes a release.
Submitting (current process — https://docs.obsidian.md/plugins/releasing/submit-plugin)
Submission is through the community directory, not a pull request:
- Repository has
README.md,LICENSEandmanifest.jsonat the root, and the pluginidis unique and does not contain "obsidian". ✅ - A GitHub release exists whose tag equals
manifest.version, withmain.js,manifest.json,styles.cssattached. CI does this on every version tag. - Sign in at https://community.obsidian.md with your Obsidian account, link your GitHub profile, and add the plugin by repository.
- The automated review reads
manifest.jsonat the HEAD of the default branch and checks the matching release. Errors block installation; warnings don't. - To get re-reviewed after fixes, publish a new release with an incremented version — the directory does not re-check an existing version.
Each later release (and every review round)
npm run release:check # lint + build + tests + version check; fix everything before bumping
npm version patch # or minor/major — bumps package.json, manifest.json, versions.json; commits + tags
git push --follow-tagsCI lints, builds, attests and creates the release; the directory picks up the new version and re-runs its review. Users on the community list get the update automatically.
On a minor or major bump, before npm version: rewrite UPDATE in src/domain/release/notes.ts (one headline, at most three bullets, the same story as the changelog entry, shorter). That is what the release note shows once after the update; a patch release leaves it alone and shows nothing.
What the review has failed on, and the rule that now catches it
The 0.8.0 review (2026-09-05) failed on no-static-styles-assignment and warned on prefer-create-el, a deprecated caretRangeFromPoint, a partially supported text-decoration and a duplicate display. Each is now a lint error:
| Habit | Rule | Do instead |
|---|---|---|
el.style.left = "50%" | obsidianmd/no-static-styles-assignment | a class in styles.css; el.setCssStyles({...}) or el.setCssProps({ "--var": v }) for runtime values |
document.createElement("div") | obsidianmd/prefer-create-el | parent.createDiv({ cls, text, title }), createSpan, createEl (they exist on SVG nodes too) |
document.caretRangeFromPoint | @typescript-eslint/no-deprecated (disable comments are forbidden) | caretPositionFromPoint |
String(value) on unknown | @typescript-eslint/no-base-to-string | narrow with typeof first |
text-decoration-style: dashed and friends | plugin/no-unsupported-browser-features | border-bottom: 1px dashed … |
two display: in one rule | declaration-block-no-duplicate-properties | keep one |
"Vault enumeration" and "clipboard access" appear as recommendations, not failures; they are what the board and the writer-protocol command do, and the README says so.
Things a reviewer may ask about
- Bundle size (~600 KB):
compromiseand the concreteness norms. Both are justified in the README; the model assistant is optional and off by default. isDesktopOnly: true because the model features assume a local Ollama. Everything else would work on mobile; if you ever want mobile, gate the model settings behindPlatform.isDesktopand flip the flag.- Plaintext API key (Claude path, currently dormant): the settings description says so explicitly. Reviewers like that.
data/directory (1.6 MB source norms): only used bynpm run build:concreteness; not shipped. Fine to keep, or move to a release asset if repo size matters.