cite-wide 0.2.2 Published

Cite-Wide 0.2.2 — Smaller Footprint, Same Behavior

All of 0.2.0's LLM-paste citation pipeline and 0.2.1's wider Paste modal — with a dependency-hygiene pass that drops one third-party package in favor of a Node.js built-in, and a tighter release-asset bundle for the marketplace's automated scan. Nothing changes for the user; the bot is happier.

Why Care?

Most research today starts with a question to Perplexity, a follow-up to Google AI Overviews, then a deeper dive with Claude. Each tool answers with citations — but every tool emits them differently, and every tool starts its numbering at [1]. By the time three responses have landed in the same Obsidian note, the citations have quietly corrupted each other: two different [3]s pointing at two different articles, the same article cited under three different markers, and a reference list whose shape depends on whichever LLM was asked last.

2026-05-17_Cite-Wide-_Paste-LLM_11.19.51 PM.gif

Cite-Wide 0.2.0 is the release that treats the LLM-paste boundary as a first-class entry point. Pasted output from any of the major tools now flows into the same canonical Lossless citation format — stable hex identifiers, single-space inline placement, markdown-link reference bodies — without a manual cleanup pass. The same release ships a URL-based dedupe command that consolidates citations the LLMs generated under different markers but pointing at the same article, an explicit save flow that turns the Citations/ folder into a curated archive rather than a side effect of every URL extraction, and a complete pass through the marketplace-submission gauntlet so the plugin is now installable from the new community.obsidian.md portal that replaced the retired PR-based queue.

The shift in framing: prior versions were a citation formatter for an author who writes citations by hand. 0.2.0 is a citation pipeline for an author whose primary input is pasted from somewhere else.

What's New?

What 0.2.2 adds since 0.2.1

Smaller and more honest than 0.2.1 — for users this release is invisible, but it tightens the build and the release-asset bundle in ways the marketplace's automated scan cares about.

  • One fewer third-party dependency. Replaced the builtin-modules npm package (used in the esbuild config to externalize Node built-ins) with the equivalent builtinModules export from Node's own node:module — available since Node 14, no third-party dep needed. pnpm-lock.yaml shrinks by one package; the ESM module-replacements project flagged this as the modern shape.

  • Release bundle now matches what Obsidian actually downloads. The 0.2.0 and 0.2.1 GitHub Releases attached LICENSE alongside main.js / manifest.json / styles.css. Obsidian only consumes the three core assets, so the LICENSE was extra weight that triggered a scorecard "release contains additional files" warning. 0.2.2's release ships exactly those three files; the repo-root LICENSE is still there for anyone cloning the source.

Two things flagged by the scorecard that 0.2.2 does not address — both deferred for separate work:

  • GitHub artifact attestations on the release assets. Adding these requires a .github/workflows/release.yml that calls actions/attest-build-provenance@v1 after build, which is a separate setup. Filed as future work; doesn't block the marketplace approval.

  • Pinning peer-dep versions of @codemirror/state and @codemirror/view to match what obsidian@1.12.3 declares. pnpm install surfaces unmet-peer warnings but doesn't break the build; resolution lives at the dependency-graph level rather than the manifest level.

What 0.2.1 adds since 0.2.0

A small but meaningful polish pass.

  • Paste LLM Content modal is now full-width. The textarea where you paste a research dump from Perplexity or Google AI now occupies a 90vw × 88vh popup instead of Obsidian's default ~400px-wide modal — so a real multi-paragraph response with a reference list fits without horizontal scrolling, and you can actually read what you're pasting before you click Parse and Insert. Implements the canonical wide-modal pattern (modalEl.addClass, not contentEl.addClass) the rest of the family was already using — see the Widen-Modals-in-Obsidian-using-CSS reference in the content-farm context-v/.

  • Manifest description rewritten per ObsidianReviewBot's recommendation. The previous "A plugin for Obsidian that enables rigorous citation and reference management" tripped two bot rules (must not contain "Obsidian", must not self-reference with "A plugin that..."). Replaced with active-voice: "Convert numeric footnotes into stable hex identifiers, dedupe citations by URL, and parse LLM-pasted research into a canonical reference format." Three things the plugin actually does, no jargon, no self-reference.

  • fundingUrl corrected from the group homepage (lossless.group) to the actual tip jar (buymeacoffee.com/losslessgroup) — where someone who finds the plugin useful can actually buy a coffee.

The rest of this release narrative covers what 0.2.0 already shipped — all still accurate. 0.2.1 is the version that actually makes it through the directory's automated review.

Convert citations as they paste in

A new command — Paste LLM Content (Convert Citations on Insert) — opens a modal with a textarea and a provider radio (Google AI Overviews / Perplexity). Paste your LLM output, click Insert, and the converted form lands at your cursor in one step. Raw LLM citations never enter the file, so the colliding-[1]s problem can't happen in the first place. The host document's existing [^hex] markers are also collected before generation, so newly-inserted citations won't collide with anything already in the note.

Convert citations that are already in the file

For the times when LLM output got pasted before this release shipped, or when you want to convert citations a collaborator pasted in, a second command — Parse LLM Citations in Current File — opens a review modal listing every [N] numeric citation it found, the hex it will become, the reference-definition text, and clickable line links to every place the citation appears in your note. You get per-row checkboxes, a per-row Convert button for picking off conversions one at a time, and a tri-state All checkbox in the header for batch conversion. Already-converted [^hex] citations are preserved verbatim — your hand-curated work never gets re-touched.

The parser knows six citation shapes between the two commands:

  • Inline: [1, 2, 3] (Google AI), [1][2] (Perplexity), [1] [2] [3] (spaced), text.[1] and changes[2] (glued to text)

  • Reference lists: [N] [Title](url) (Google AI markdown link) and [N] Title https://url (Perplexity title-then-URL)

All six get rewritten to the Lossless canonical [^hex]: [Title](url) shape.

Output that already matches the spec

In prior versions, even when conversion worked, the output preserved whatever spacing the source pasted in — text.[^hex] from Perplexity, [^h]: Title https://url from the Perplexity refdef shape. A final whitespace pass now enforces the Lossless Citation Spec automatically:

INPUT                                            OUTPUT
changes[2] [5] [3]                       →       changes [^h2] [^h5] [^h3]
growth:[3] [4][5] [6]                    →       growth: [^h3] [^h4] [^h5] [^h6]
cases.[1] [2]                            →       cases. [^h1] [^h2]
[1] Title One https://example.com/one    →       [^h1]: [Title One](https://example.com/one)

No more manual cleanup pass between paste and publish.

Consolidate citations that point at the same article

New command — Dedupe Citations by URL — for the workflow where the same NYT piece (or arXiv preprint, or vendor announcement) got cited three times across two pasting sessions and now has three different hex IDs. The command walks your reference section, normalizes URLs to bridge trivial differences (drops utm_*/fbclid tracking params, fragment hashes, trailing slashes), groups citations by URL, picks one canonical hex (earliest inline appearance wins), and rewrites the file so each unique article ends up with exactly one marker. A modal lists every group in document order with line-links to every occurrence; uncheck any group you don't want to consolidate before applying.

The conceptual move: URL is your citation's identity. The hex marker is plumbing. You think "I cited that NYT article three times" — not "I have three different hex IDs." The dedupe command lines up the tool with that thinking.

Citations/ becomes a curated archive

Previously, every URL extraction wrote a Citations/<hex>.md file whether you wanted it canonicalized or not. The Citations folder filled up with everything you'd ever fetched.

0.2.0 splits the auto-save behavior behind a settings toggle (Settings → Cite Wide → Auto-save URL Citations, off by default) and adds two explicit save paths:

  1. Save All Hex Citations to Citation Files — palette command that walks the active document, persists every [^hexId] group as a citation file, reports saved / updated / errors in one Notice.

  2. "Save to Citations" button per hex group — in the existing Show Citations modal, each hex group now has its own save button, and the modal header carries Save All Hex (N) alongside Convert All (N) when there's anything to save.

Result: Citations/ is what you decide it is, not a side effect of every web extraction.

Recognizing Perplexity reference lists properly

Bug fix worth surfacing because it silently broke the parser for one of the two main providers. Perplexity's reference list format puts the title BEFORE the URL ([1] Understanding GitHub Actions https://docs.github.com/articles/...). The previous refdef heuristic looked for markdown-link-at-line-start or bare-URL-at-line-start, neither of which Perplexity's format matched — so its reference lists were being silently ignored and the conversion would no-op. Now any line starting with [N] and containing a URL anywhere is recognized as a reference definition.

Installable from the new Obsidian community portal

Obsidian retired the long-running obsidianmd/obsidian-releases PR-based submission queue in favor of a hosted portal at community.obsidian.md. Cite-Wide 0.2.0 was the first version submitted through the new portal — and the version that surfaced the discipline now codified across the Lossless plugin family (three-digit semver across manifest.json / package.json / versions.json, plain tag without v prefix, root-level LICENSE, current minAppVersion). The portal's automated review came back with concrete feedback on the manifest description and pointed out the fundingUrl was wrong; 0.2.1 is the version that closes both gaps. Reachable from inside Obsidian via Settings → Community Plugins → Browse → search "Cite Wide".

Sibling plugins shipped through the same portal-discipline pass: image-gin 0.2.2 and perplexed 0.1.1.

Under the Hood

For readers interested in the engineering work behind the user-facing features above — each substantive change shipped between 2026-05-01 and 2026-05-02 has its own per-day entry under changelog/:

DateTopicPer-day changelog
2026-05-01Dependency refresh — 8 packages bumped, 4 majors held back2026-05-01_01.md
2026-05-01Type-safety pass — eliminated 14 any declarations, retired hand-rolled YAML parser in favor of app.fileManager.processFrontMatter, wired ObsidianReviewBot rules into pnpm build2026-05-01_02.md
2026-05-01URL-based dedupe — analyzer + applier + modal2026-05-01_03.md
2026-05-01Dependency cleanup — removed fastify/@modelcontextprotocol/sdk/zod (zero imports), forced safe transitive versions via pnpm.overrides, ported the explicit-save-flow intent from TanujKS's August 2025 commits2026-05-01_04.md
2026-05-01LLM citation parser v1 — multi-form tokenizer, modal-driven review UX, CLI test harness2026-05-01_05.md
2026-05-02LLM parser v0.2.0 — Paste modal, Perplexity refdef recognition fix, spec-conformance pass on output2026-05-02_01.md

Three pieces of engineering work that don't have their own per-day changelogs but show up across this release tag and its predecessor:

  • The community.obsidian.md submission discipline — adding root-level LICENSE, aligning manifest.json / package.json / versions.json on strict three-digit semver, fixing author to "The Lossless Group" to match siblings, bumping minAppVersion from a stale 0.15.0 starter-template default to a realistic 1.8.10. The release tag was originally cut as v0.2.0 on 2026-05-02; the marketplace-ready 0.2.0 tag (no v prefix) was cut on 2026-05-17.

  • Round-trip with ObsidianReviewBot — the portal's automated review on the 0.2.0 submission returned two findings on the manifest description (must not contain "Obsidian", must not self-reference with "A plugin that...") and surfaced that fundingUrl was pointed at the group homepage rather than a tip-jar destination. 0.2.1 closes both. Since the portal's bot cannot distinguish an updated manifest from a new release on the same tag, the fix ships as a fresh 0.2.1 rather than a clobbered 0.2.0 asset.

  • The changelog/ relocation — per-day entries and the release narratives both moved from context-v/changelogs/ to the repo-standard changelog/ location (with a releases/ subdirectory for version-anchored narratives).

What's Next

User-facing items either deferred from this release or surfaced by it:

  • Garbage Collect Orphan Citation Files — when the dedupe command collapses three hex IDs into one, the orphan Citations/<hex>.md files for the consolidated markers stay on disk. A separate command would scan Citations/, find files whose hex doesn't appear anywhere in the vault's markdown, and prompt-then-delete. Held as a separate command because the blast radius of file deletion deserves its own confirmation step.

  • Inheriting usage stats during dedupe — the canonical citation file's usageCount and filesUsedIn aren't merged from the eliminated hexes. The right time to do this is during the dedupe apply phase; held until the orphan-cleanup command lands so the two citation-file mutations ship together.

  • Standalone "Normalize Lossless Citation Spacing" command — the spec-conformance pass introduced in this release only runs on lines the parser actually transforms. Files converted under the older parser carry the spacing issues baked in. A separate command that runs only the spacing normalization over the active file would retroactively fix older converted files. Held for now; surface if users hit it.

  • Canonical Lossless reference shape still partially unfilled. The spec wants [^hex]: 2025, Jan 25. {Author}. [Title](url). {Publisher} || [Publisher](publisher_url). Accessed {Month Day, Year}. — date, author, publisher, and accessed-date all require network (URL fetch + meta-tag extraction) or AI (publisher classification, author normalization). Deferred to the MCP-driven canonical-citations agent spec'd in context-v/blueprints/Citation-Acquisition-Pipeline.md.

  • Claude prose-style attributions ("Erik Brynjolfsson of Stanford…" with no bracket markers) remain out of scope for the deterministic regex parser. Would need a Claude-API-driven extractor; defer until a real workflow surfaces the need.

  • Heading-slug drift when an inline [12] in a heading becomes [^hex], breaking auto-generated heading anchors. Pre-existing issue inherited from convertAllCitations; a follow-up pass that detects ToC links pointing at freshly-regenerated heading slugs would close it.

References

  • Release on GitHub: https://github.com/lossless-group/cite-wide/releases/tag/0.2.2

  • Prior releases: 0.2.1 (wider Paste modal + marketplace polish), 0.2.0 (the original LLM-paste pipeline). The release narrative for each prior version was git mv'd into this file and updated in place; their history is reachable via git log -- changelog/releases/0.2.2.md.

  • Install from Obsidian: Settings → Community Plugins → Browse → "Cite Wide"

  • Spec the output conforms to: context-v/reminders/Lossless-Citation-Spec.md

  • Future-state architecture for the deferred canonical-reference fields: context-v/blueprints/Citation-Acquisition-Pipeline.md

  • Sibling-plugin releases shipped in the same marketplace pass: image-gin 0.2.2, perplexed 0.1.1

  • Per-day engineering changelogs linked in the Under the Hood table above