npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

openspec-obsidian

v1.4.4

Published

Navigate OpenSpec artifacts as an Obsidian vault: frontmatter, path-wikilinks, tags, deterministic archive with link rewrite

Readme

openspec-obsidian

npm version CI license: MIT

Navigate OpenSpec artifacts as an Obsidian vault: frontmatter, path-wikilinks, tags, and a deterministic archive step with link rewrite.

What & why

OpenSpec keeps a capability's history scattered across plain markdown: the live spec under specs/<capability>/, and every change that shaped it under changes/<id>/ and changes/archive/YYYY-MM-DD-<id>/. Opening openspec/ as an Obsidian vault unifies all of it into one graph: click from a proposal to its design, tasks, and delta specs; land on a main spec and see every delta that ever touched it via backlinks; filter by capability/<name> tag and get the full history of that capability across active and archived changes — no directory spelunking.

The conventions are pure frontmatter. The OpenSpec CLI (verified against v1.4.1) ignores the YAML block, so validate --strict and show behave identically with or without it.

The conventions

Frontmatter schema per artifact type

| Artifact | File | Keys | |---|---|---| | Main spec | specs/<cap>/spec.md | type: spec, title: "<cap> spec", capability, tags: [openspec, type/spec, capability/<cap>], aliases: ["<cap> spec"] | | Proposal | changes/<id>/proposal.md | type: proposal, title: "<id> proposal", change, tags: [openspec, type/proposal, capability/<c>…] (one per delta), aliases: ["<id> proposal"], plus links: design, tasks, specs (each only if the target file exists) | | Design | changes/<id>/design.md | type: design, title: "<id> design", change, tags: [openspec, type/design, capability/<c>…], aliases: ["<id> design"] | | Tasks | changes/<id>/tasks.md | type: tasks, title: "<id> tasks", change, tags: [openspec, type/tasks, capability/<c>…], aliases: ["<id> tasks"] | | Delta spec | changes/<id>/specs/<cap>/spec.md | type: spec-delta, title: "<id> <cap> delta", change, capability, tags: [openspec, type/spec, capability/<cap>], aliases: ["<id> <cap> delta"], main_spec: "[[specs/<cap>/spec\|<cap> spec]]" |

Archived changes use the same shapes with the wikilink prefix changes/archive/YYYY-MM-DD-<id>/.

Tag taxonomy

Three tag families, frontmatter-only: openspec (everything), type/<artifact> (type/spec, type/proposal, type/design, type/tasks), and capability/<name>. Never put inline #tags in artifact bodies — an inline tag inside a requirement line is absorbed into the CLI's extracted requirement text (verified gotcha).

Path wikilinks, never aliases

Obsidian resolves [[…]] by path/filename only — aliases are autocomplete sugar, and a bare [[my alias]] opens a new empty note when clicked. Every link is therefore a vault-relative path wikilink with a display label: [[changes/<id>/design|<id> design]]. The aliases key stays for search and display; it is never a link target.

One-direction linking

Links are authored one way only — proposal → design/tasks/deltas, delta → main spec. The reverse direction (main spec → its deltas, design → its proposal) comes free via Obsidian backlinks. No link maintenance in two places.

Graph node labels — Front Matter Title (optional)

Obsidian labels every graph node with its file basename, and OpenSpec fixes those basenames (proposal.md, design.md, tasks.md, spec.md) — so the graph is a sea of identically-named spec/proposal nodes that can only be told apart by opening them. Aliases and link labels do not affect graph labels, and renaming artifact files would break the OpenSpec CLI and this tool's parsers.

Every artifact therefore carries a title key (mirroring its primary alias, e.g. add-widgets proposal, backfill spec) stamped by backfill. The community plugin Front Matter Title renders that key as the node label everywhere — graph, explorer, search, tabs — with no filename changes:

  1. Install Front Matter Title from Community Plugins and enable it.
  2. Its default template is the title key, so no configuration is needed for the explorer/search/tab replacements.
  3. Enable the plugin's Graph feature (Settings → Front Matter Title → Features → Graph) — a separate per-location toggle that is off by default — to relabel graph nodes too (likewise Canvas for canvas cards).

Why the graph doesn't change on its own: native Obsidian labels graph nodes by filename only and never reads frontmatter, so the title key stays inert until the plugin is installed. The plugin then applies each surface independently: its default template relabels the explorer, search, tabs, and note headers immediately, but the graph and canvas are opt-in and off by default. So if you see titles everywhere except the graph, you have the plugin installed but haven't turned on its Graph feature (step 3).

Strictly optional: without the plugin, title is inert frontmatter — the OpenSpec CLI ignores it (validate --strict is unchanged) and the vault behaves exactly as before; the graph is just not as readable. backfill also inserts a missing title into artifacts that already have frontmatter, so vaults backfilled before this key existed converge on the next backfill run.

Install

From the npm registry:

npx openspec-obsidian <command>          # run without installing
npm install --save-dev openspec-obsidian # or pin as a dev dependency

Pin an exact commit or run ahead of a release with the github: form: npx github:Fatfrido/openspec-obsidian#<sha> <command> runs straight from the repo. See Releasing for how versions reach npm.

Adopt in your repo

Your repo must already be an OpenSpec project (openspec init, CLI v1.4.x). Then:

openspec-obsidian init        # install schema + templates + config rules; gitignore openspec/.obsidian/
openspec-obsidian backfill    # add frontmatter to every existing bare artifact

init installs the Obsidian-aware artifact templates into openspec/schemas/spec-driven/ (existing files are skipped unless --force) and appends the authoring rules: block to openspec/config.yaml (printed for manual merge if you already have one). backfill is idempotent — files that already have frontmatter are never touched — and verifies every generated wikilink resolves before it succeeds; use --dry-run to preview. Then open openspec/ as a vault in Obsidian; workspace state stays untracked via the gitignored openspec/.obsidian/.

Optional features

Some capabilities are opt-in. They are controlled by a tool-owned config file, openspec/obsidian.yaml, holding a features: map of booleans:

features:
  dashboard: true

Optional features default off: an absent file, an absent features: key, or an unlisted name all mean disabled. openspec-obsidian init seeds this file with every optional feature set to false and never overwrites it afterwards (not even with --force) — it records your choices, not reinstallable scaffolding. The core commands (init, backfill, archive, check) are never gated. Unknown feature names are ignored, so a newer config keeps working with an older tool.

Today the only optional feature is dashboard (below).

Migration: if you used the dashboard before feature toggles existed, dashboard now errors until you enable it — add the two features: lines above to openspec/obsidian.yaml. Existing dashboard output is otherwise unaffected.

Dashboard

Generate a single navigable overview of the openspec/ tree:

openspec-obsidian dashboard

Requires the dashboard optional feature: add features: with dashboard: true to openspec/obsidian.yaml first, or the command exits with an actionable error.

It writes openspec/dashboard.md — a deterministic, wikilinked summary of every active change (task progress and completeness), the capability catalog (requirement counts), and archived history — computed from the vault with node:fs, no OpenSpec CLI. It also seeds openspec/dashboard.base, a native Obsidian Bases view over artifact frontmatter, when that file is absent (--force overwrites it; --dry-run previews). Both outputs live in the tracked vault body, never in the gitignored openspec/.obsidian/.

The note is a snapshot: re-run dashboard whenever changes or specs move — in particular right after archive — so it stays current. When the dashboard feature is enabled, check fails on a missing or stale openspec/dashboard.md (see CI snippets below), so drift is caught without a separate step. Open openspec/dashboard.md in Obsidian as your entry point (bookmark it), or the .base for live filtering and sorting.

Hubs

Give every change a single, natively-labeled anchor in the graph:

openspec-obsidian hubs

It writes one hub note per active change at openspec/changes/<id>.md — a file whose basename is the change id, so Obsidian labels its graph node with the change name for free (no Front Matter Title plugin needed, and unlike that plugin this covers only changes, not specs). Each hub carries type: hub frontmatter, the change's task progress, and path wikilinks to its proposal, design, tasks, and delta specs, doubling as a per-change landing page. Regeneration is deterministic and idempotent, computed from the vault with node:fs, no OpenSpec CLI.

Rerunning hubs refreshes every hub note and deletes stale ones — a changes/*.md file marked type: hub whose change directory is gone (archived or removed); notes lacking that marker are never touched. --dry-run previews both writes and removals without changing the vault. Because hub notes go stale the moment a change is archived, re-run hubs right after archive.

Strictly opt-in: invocation is the only switch. No other command creates hub notes, and backfill, dashboard, archive, check, and the OpenSpec CLI all treat them as non-artifacts — never run hubs and the vault contains no extra files.

Graph color-groups tip: in Obsidian's Graph view, add color groups on the existing tags (Graph settings → Groups) — e.g. tag:#type/hub, tag:#type/proposal, tag:#type/spec — to tint each artifact kind, so a hub note and its cluster read at a glance.

Changelog

Record what each release contained, right in the vault:

openspec-obsidian changelog --release v1.6.0

It prepends a ## v<version> — <date> section to openspec/changelog.md (creating the note with type: changelog frontmatter when absent), listing every Conventional Commit since the previous release, bucketed Breaking / Features / Fixes / Internal. A commit subject naming an archived change id (e.g. (add-frontmatter-titles)) is wikilinked to that archived proposal, so the changelog joins the graph. This is the only command that shells out to git — tags and log are unreachable from node:fs — so it requires a full-history, tag-bearing checkout (fetch-depth: 0, fetch-tags: true); it fails with an actionable error on a shallow clone or an unknown tag.

The previous release is self-anchored: it is the version in the topmost ## v heading of the existing note — no GitHub API, no state file. A missing or heading-less note bootstraps from full history. Sections are prepend-only and never regenerated, so the note is immutable history; re-running for an already-recorded version is a no-op, and identical repo state yields byte-identical output. Use --dry-run to preview the section without writing.

In this repo the release pipeline runs it automatically (see Releasing); adopters opt in the same way — wire the step into their own release workflow. Invocation is the only switch: never call it and the vault contains no changelog note.

Example: this repo dogfoods openspec-obsidian

openspec/ in this repository is a worked example, produced by exactly the steps above. It was bootstrapped with openspec init, then openspec-obsidian init, and the CLI's own behavior was documented through the full workflow: the adopt-openspec-obsidian change (proposal + design + tasks + four delta specs) was authored and then synced and moved with openspec-obsidian archive. Browse:

  • openspec/specs/{init,backfill,archive,check}/spec.md — the live capability specs (open openspec/ as an Obsidian vault to walk the graph and tag taxonomy).
  • openspec/changes/archive/<date>-adopt-openspec-obsidian/ — the archived change, with its intra-change path wikilinks rewritten to the archived location.
  • .github/workflows/ci.yml — the specs job gating on openspec validate --all --strict and openspec-obsidian check.

Archive: when and how

Recommendation: archive at apply-completion, on the PR branch. When the last task checkbox flips to - [x], run:

openspec-obsidian archive

It deterministically: syncs delta specs into openspec/specs/ (ADDED/MODIFIED/REMOVED/RENAMED merge), moves each all-tasks-complete change to openspec/changes/archive/YYYY-MM-DD-<id>/, rewrites the change's intra-change wikilink prefixes, and verifies every link still resolves — failing loudly on a broken link or an existing archive target. Changes with open tasks are skipped.

Commit the result on the feature branch (convention: docs(openspec): sync <caps> specs + chore(openspec): archive <id>), then squash-merge — the archive commits fold into the change's single commit on main. After the move, re-run openspec-obsidian dashboard and stage the refreshed openspec/dashboard.md with the archive commit so the overview never drifts. No AI or agent is required for any of this, and no archive commits ever land on main directly. Gate it with check in CI (below) so a complete-but-unarchived change can never merge.

CI snippets

  specs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm install -g @fission-ai/[email protected]
      - run: openspec validate --all --strict --no-interactive
      - run: npx github:Fatfrido/openspec-obsidian check

check exits 1 (naming the offenders) when any change has all tasks complete but still sits under openspec/changes/, and — when the dashboard feature is enabled — also when openspec/dashboard.md is missing or stale (remedy: re-run dashboard). This single gate replaces the old regenerate-and-diff steps. For reproducible CI, pin a commit: npx github:Fatfrido/openspec-obsidian#<sha> check (npx github: needs network + git in the runner).

Agent/skill integration

Paste-ready final step for an apply skill/prompt (after the implementation commit, before pushing):

Sync + archive on the branch: run openspec-obsidian archive from the repo root. It syncs delta specs into openspec/specs/, moves the change to openspec/changes/archive/YYYY-MM-DD-<name>/, rewrites its intra-change path wikilinks, and verifies links resolve. If it prints NOTHING TO ARCHIVE, stop and report (a task checkbox is still open). Then run openspec validate --all --strict --no-interactive. Commit in two commits: git add openspec/specsdocs(openspec): sync <caps> specs; then git add -Achore(openspec): archive <name>.

For a manual archive skill, keep one guardrail: archive via the deterministic archive command; never hand-mv a change dir (a bare mv leaves the moved change's path wikilinks pointing at the old location).

Releasing (maintainers)

Publishing to npm is automated by .github/workflows/publish.yml and authenticates tokenlessly via OIDC Trusted Publishing — no npm token or other long-lived secret is stored.

One-time setup: on npmjs.com, add a Trusted Publisher for openspec-obsidian — provider GitHub Actions, repository Fatfrido/openspec-obsidian, workflow file publish.yml. The workflow requests an id-token and npm trusts that OIDC identity to publish.

To cut a release:

  1. Bump version in package.json (SemVer) and merge to main.
  2. Create a GitHub Release with tag v<version> (e.g. v0.1.0) matching that version.

Publishing the Release runs the workflow: it verifies the release tag equals v<version> from package.json (failing the release without publishing if they differ), then runs npm test and npm publish --access public. Provenance is attested automatically by Trusted Publishing. workflow_dispatch allows a manual run against main. Nothing is ever published from a developer machine.

After a successful publish, the changelog job checks out main with full history and tags, runs openspec-obsidian changelog --release v<version>, and commits docs(changelog): v<version> to main when the note changed (as github-actions[bot], pushed with the workflow token so it triggers no further run and receives no version tag). A failed publish records nothing. The job is idempotent: re-running a release whose section already landed pushes nothing. So the full release procedure is just the two steps above — the changelog follows automatically.

Contributing

Issues and pull requests are welcome at github.com/Fatfrido/openspec-obsidian. Run npm test before opening a PR and follow the Conventional Commits schema for commit and PR titles.

Compatibility

Verified against OpenSpec CLI v1.4.1: openspec validate --all --strict passes on a fully backfilled repo, and openspec show --json requirement extraction is unchanged with frontmatter present. The reference implementation of these conventions lives in Fatfrido/maniac (openspec/ tree + .github/scripts/archive-change.mjs).