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

@ignacioroan/scriptorium

v2.6.1

Published

npm CLI for initializing, validating, compiling, backing up, and updating literary projects written with AI assistance.

Downloads

2,069

Readme

Scriptorium

scriptorium is an npm CLI for initializing, validating, compiling, backing up, and updating literary projects created with AI help.

This tool is designed for literary projects written in Spanish.

scriptorium is a standalone package. Each work lives in its own directory and is managed locally through its project package.json and .scriptorium/manifest.json.

Requirements

  • Node.js 24 or later
  • No manual Pandoc install is required on supported platforms. scriptorium downloads a local bundled Pandoc during postinstall and falls back to pandoc in PATH when the bundled binary is unavailable.

Bootstrap

mkdir my-novel
cd my-novel
npx @ignacioroan/scriptorium@latest init

That command:

  • creates the base editorial structure in the current directory
  • creates the living project documentation inside docs/, centered on docs/dosier-narrativo.md and the stable canon indexes
  • generates AGENTS.md, CLAUDE.md, and MEMORY.md as normal working files for the project
  • generates a local package.json pinning the exact @ignacioroan/scriptorium version it was generated with, and installs it as a development dependency
  • writes .scriptorium/manifest.json to record managed surfaces
  • initializes a git repository with a managed, vault-aware .gitignore block and an initial commit; scriptorium versions the <vault>/.obsidian config and ignores only Obsidian's volatile UI-state files (<vault>/.obsidian/workspace.json, <vault>/.obsidian/workspace-mobile.json), alongside the standard node_modules/, output/, backup/, .DS_Store entries; if git is unavailable this step is skipped with a warning and init still succeeds
  • copies managed shared skills into .agents/skills/ and .claude/skills/
  • makes init and update manage the local Obsidian vault surface in .obsidian/

After bootstrap, docs/, MEMORY.md, and the generated living documents stop being package scaffolding and become project-owned content.

Bootstrap From A Local Checkout

If you want the generated project to stay linked to a local scriptorium checkout, init accepts an absolute file: source:

node /path/to/scriptorium/bin/scriptorium.mjs init --from file:/path/to/scriptorium

Without --from, the dependency is pinned to the exact published version used to generate the project (not latest). With --from, that file source takes precedence and is persisted in the generated project package.json, for example:

{
  "devDependencies": {
    "@ignacioroan/scriptorium": "file:/path/to/scriptorium"
  }
}

Manual setup after init

Each initialized project now ships a Spanish README.md at its root that contains the canonical, friendly configuration checklist. That README is the source of truth for users. The summary below documents the same steps in English for package maintainers.

init sets up everything: the CSS snippet (justified text with a first-line indent for manuscript scenes, in both reading and editing views), the bundled scriptorium-obsidian-plugin (which renders the README dashboard —word summary, act/chapter word tree, project map—, registers the punctuation insertion commands driving the native mobile toolbar, and opens the README on startup), and sync: false as a default for new projects (you can turn Obsidian Sync on; update won't override your choice). No third-party community plugins are required: the dashboard is native to the bundled plugin and the character/world/scene tables use Bases (a core Obsidian feature). The user only needs to enable "Community plugins" once per device so the bundled plugin loads.

Also:

  • Bookmark the dashboard (core plugin) for one-tap access.

Project Workflow

Inside an initialized project:

npm run validate
npm run compile -- --format md
npm run compile -- --format epub
npm run backup
npm run sync
npm run create:act
npm run create:chapter
npm run create:scene
npm run create:personaje
npm run create:mundo
npx @ignacioroan/scriptorium@latest update

npm run sync is also a normalizer: it renumbers scene escena: fields contiguously, compacts gaps in acto_NN / capitulo_NN folder numbering (two-phase rename, safe against duplicates), rewrites path-style wikilinks that point to renamed folders, mirrors the structure into metadata.yaml, reports non-conformance warnings for files that do not match the strict naming contract, and detects and writes scene/ficha mentions (see Codex Mentions below). Run it after editing the manuscript structure by hand (for example deleting a chapter in Finder).

npm run mentions runs only the mention-detection pass without the structural normalization — useful when you have added or renamed fichas and want to refresh the mention index without renumbering anything.

npm run context assembles the ficha content relevant to a scene or an explicit list of fichas and prints it to stdout — ready to paste into an agent context window. Two forms:

# From a scene: reads personajes/lugares/objetos managed fields and loads matching fichas
npm run context -- --escena manuscrito/acto_01/capitulo_01/escena_01

# Explicit list: comma-separated ficha slugs per type
npm run context -- --personajes "Elena,Marco" --lugares "La taberna" --objetos "El mapa"

Both scriptorium-prosa and scriptorium-edicion call this helper automatically before writing or reviewing, so the agent always has the full cast in context.

Codex Mentions

sync and mentions scan the manuscript and cross-reference it against the ficha catalog in docs/personajes/ and docs/mundo/ to build a live index of which characters, places, and objects appear in each scene.

Managed fields — written and regenerated on every run; do not hand-edit:

| Surface | Field | Content | |---------|-------|---------| | Each scene | personajes: | Wikilinks to matching character fichas | | Each scene | lugares: | Wikilinks to matching place fichas | | Each scene | objetos: | Wikilinks to matching object fichas | | Each ficha | aparece_en: | Path-style wikilinks to every scene it appears in |

Author controls — written by you in Obsidian's Properties panel; never overwritten by scriptorium:

| Field | Effect | |-------|--------| | personajes_add: / lugares_add: / objetos_add: | Force an entry into the managed list even if the name was not detected in the prose | | personajes_veto: / lugares_veto: / objetos_veto: | Drop a false positive from the managed list | | alias: on a ficha | Alternate names the matcher also recognises |

Fichas with tipo: faccion or tipo: regla are browsable in the vault but are not tracked by the mentions system.

Project Structure

Each standalone project uses this baseline:

my-novel/
  README.md
  AGENTS.md
  CLAUDE.md
  MEMORY.md
  package.json
  metadata.yaml
  epub.css
  cover.jpg
  <vault>/
    manuscrito/
    docs/
      dosier-narrativo.md
      estructura/
        README.md
        cronologia.md
        estructura-narrativa.md
      mundo/
        README.md
      notas/
        README.md
      personajes/
        README.md
      bases/
      plantillas/
        escena.md
        acto.md
        capitulo.md
    .obsidian/
      app.json
      appearance.json
      core-plugins.json
      templates.json
      community-plugins.json
      plugins/
        scriptorium-obsidian-plugin/
      snippets/
        scriptorium.css
  output/
  backup/
  .scriptorium/
    manifest.json
  .agents/
    skills/
  .claude/
    skills/

Managed Surfaces

scriptorium may rewrite only these managed surfaces:

  • .scriptorium/manifest.json
  • README.md at the project root (the Spanish dashboard + initial setup checklist; rewritten on every init and update)
  • the local @ignacioroan/scriptorium dependency in package.json
  • the shared skills copied into .agents/skills/ and .claude/skills/
  • .obsidian/snippets/scriptorium.css (its header comment carries the scriptorium version, stamped on every init and update)
  • .obsidian/app.json (merge-adds userIgnoreFilters for node_modules/, backup/, output/ so they are excluded from search/graph, and mobileToolbarCommands so the native mobile toolbar shows the plugin's typographic insertion commands; preserves your other app settings. Note: excluded folders are removed from indexing but Obsidian still shows them, greyed, in the file explorer — that cannot be changed from app.json)
  • the managed portion of .obsidian/appearance.json that ensures enabledCssSnippets contains scriptorium (and removes the legacy style entry)
  • .obsidian/core-plugins.json (enables Bases/Templates/Bookmarks; sets sync: false as a default only when the project is first created, and never overrides your Sync toggle on update; preserves your other settings)
  • .obsidian/templates.json (templates folder; defaults to docs/plantillas, and update migrates the old managed default plantillas)
  • .obsidian/community-plugins.json (merge-adds the bundled scriptorium-obsidian-plugin; strips obsolete managed ids it used to ship —scriptorium-corchera, etb-glyph-icons, homepage, typewriter-mode, editing-toolbar—; preserves your other community plugins)
  • .obsidian/plugins/scriptorium-obsidian-plugin/ (the bundled Scriptorium plugin, built from obsidian-plugin/ and shipped as a pre-built artifact. It provides: the Corchera de fichas single-dashboard corkboard view —the whole manuscript at once as a collapsible tree of acts → chapters → scenes (act/chapter headers show their number and title, or a placeholder when untitled); tap a scene card to open the note; create acts, chapters, and scenes; rename acts and chapters from the view; and drag-and-drop reordering at every level (scenes within/between chapters, chapters within/between acts, and acts), which renames the folders/files and updates the managed number fields just like sync—; a "Nueva ficha" command (available in the command palette and as a ribbon icon) that opens a type selector (personaje / lugar / objeto) and creates the ficha note in the correct docs/ subfolder, seeding scene mention keys in the frontmatter — equivalent to npm run create:personaje / npm run create:mundo but without leaving Obsidian; an opt-in "Actualizar menciones al guardar" toggle in the plugin settings that runs mention detection each time you save a scene note, so personajes: / lugares: / objetos: stay current without a manual sync run; registration of the punctuation insertion commands (the 12 typographic signs , . ; : — … ¿ ? ¡ ! « ») that drive the native mobile toolbar, plus the ability to add your own custom insertion commands from the plugin settings. Its manifest.json version is stamped with the scriptorium version on every init and update, so Obsidian detects each release as a plugin update)
  • the managed .gitignore block delimited by # scriptorium:start / # scriptorium:end (vault-aware: scriptorium versions the <vault>/.obsidian config and ignores only <vault>/.obsidian/workspace.json and <vault>/.obsidian/workspace-mobile.json). init writes the block; update migrates legacy marker-less files (absorbing the bare legacy entries) and rewrites the block content, preserving all your lines outside the markers
  • docs/plantillas/escena.md, docs/plantillas/acto.md, and docs/plantillas/capitulo.md (rewritten by init and update from the package's canonical templates; user edits to these files will be overwritten)

It does not automatically modify docs/, MEMORY.md, manuscrito/, output/, metadata.yaml, or project-specific custom skills.

For now, update also does not migrate or resync the dossier-centered documentation base, MEMORY.md, or the docs/ placeholders created by init: once created, they are project-owned content.

The intended project contract is dossier-centered: docs/dosier-narrativo.md is the single working hub where all narrative material is born — active hypotheses, open questions, relevant discards, coherence risks, and recent consolidations. When the dossier grows too large, consolidated material moves into the stable canon surfaces: character sheets in docs/personajes/, world sheets in docs/mundo/, and docs/estructura/ (which holds estructura-narrativa.md and cronologia.md). update does not migrate docs/ content in existing projects; only the managed docs/plantillas/ surface is migrated automatically.

metadata.yaml

metadata.yaml remains the single source of truth for project editorial and compilation settings:

  • editorial identity
  • compilation assets
  • output directory and base filename
  • export formats
  • structured manuscript layout or monolithic input file

The literary compile format (per-level title templates and separators) lives instead in the author-owned formato: block of <vault>/manuscrito/_manuscrito.md (see "Compile format" below). The legacy compilation.separators keys in metadata.yaml remain as a backward-compatibility fallback for separators only.

Compile format

compile renders each act/chapter/scene heading and the separators between blocks from the formato: block in <vault>/manuscrito/_manuscrito.md frontmatter. It is an author-owned, add-only surface: init ships it in the template note, update seeds the defaults when absent, and neither overwrites an existing block.

formato:
  titulo_acto: "{titulo}"
  titulo_capitulo: "{titulo}"
  titulo_escena: "{titulo}"
  separador_acto: "pagebreak"
  separador_capitulo: "pagebreak"
  separador_escena: "***"
  • One template per level, work-wide. Title templates take three tokens: {numero} (the node's frontmatter number — acto:/capitulo:/escena:), {romano} (that number as uppercase Roman numerals), and {titulo} (the titulo: field). Substitution is literal: an absent token resolves to the empty string and an unknown token is left untouched. If the resolved heading is blank, no heading is emitted. There is no hardcoded Acto N / Capítulo N fallback anymore — the author writes the numbering into the template (e.g. "Capítulo {romano} — {titulo}").
  • Separators: pagebreak inserts a page break; any other non-blank string is used literally; a null/blank value joins blocks with a plain blank line.

Separator → CSS map. The template chooses what the separator is; epub.css controls how much it breathes. *** / * * * / --- are Markdown thematic breaks → pandoc emits <hr>; style its vertical margin with hr { margin: 2.5em 0 } in epub.css (new projects ship this rule). A glyph that is not a thematic break (, , · · ·) becomes a <p> and is styled as a paragraph. md output has no styling; docx separator spacing is limited. epub.css is a user-owned asset (seeded by init, never overwritten by update).

Backups

npm run backup creates a timestamped .zip of the whole project inside backup/, excluding node_modules/ and previous backups.

Restaurar un backup

Para restaurar un backup, descomprime el .zip en una carpeta y ejecuta npx @ignacioroan/scriptorium@latest update desde la raíz del proyecto restaurado. No uses init: solo opera sobre carpetas vacías. update reinstala las dependencias y reconstruye cualquier superficie gestionada que falte (incluido .scriptorium/manifest.json), respetando todo tu contenido.

Package Development

In this repository:

npm test

Internal templates live in templates/project/, and the shared skill catalog lives in skills/.

This repository may also contain repository-local maintainer skills in .agents/skills/ and .claude/skills/. Those root-level skills are for maintaining the package itself and are not part of the shared project-facing catalog unless they are explicitly added under skills/.

build:plugin

npm run build:plugin

Installs the obsidian-plugin/ sub-project dependencies, compiles the TypeScript source with esbuild, and stages the three built artifacts (main.js, manifest.json, styles.css) into src/assets/scriptorium-obsidian-plugin/. Run this after modifying any file in obsidian-plugin/src/. The built artifacts are committed so the published package ships a ready-to-use plugin.

Maintainer Release

Public package: @ignacioroan/scriptorium

Source repository: private GitHub repository

First Publish

npm login
npm whoami
npm test
npm pack --dry-run
npm publish --access public

After the first publish, configure trusted publishing for .github/workflows/publish.yml in npm.

Version Bumps

npm run version:patch
npm run version:minor
npm run version:major

These commands remain available when you want to bump the version manually without releasing.

One-Command Release

npm run release:patch
npm run release:minor
npm run release:major

Each release:* command:

  1. Verifies a clean working tree on main.
  2. Verifies local main is in sync with origin/main.
  3. Runs the matching version bump.
  4. Commits package.json and package-lock.json as chore: bump version to X.Y.Z.
  5. Pushes that commit to origin main.
  6. Runs npm test.
  7. Runs npm pack --dry-run.
  8. Creates and pushes the release tag.

Use this path when you want the standard maintainer release in one command.

Manual Release Flow

If you want to keep the steps separate, the original commands still work:

Example:

npm run version:patch
git add package.json package-lock.json
git commit -m "chore: bump version to 0.1.2"
git push origin main
npm test
npm pack --dry-run
node ./scripts/release.mjs

The final publish step remains available as an internal script:

node ./scripts/release.mjs

That script requires:

  • a clean working tree
  • the current branch to be main
  • local origin/main to be in sync
  • the v<version> tag to not exist locally or remotely

Typical release flow after the first publish:

  1. Run npm run release:patch, npm run release:minor, or npm run release:major.
  2. Monitor .github/workflows/publish.yml.
  3. Verify the new version on npm.

Manual alternative:

  1. Run npm run version:patch, npm run version:minor, or npm run version:major.
  2. Commit the version change.
  3. Push or merge that commit to main.
  4. Run npm test on main.
  5. Run npm pack --dry-run on main.
  6. Run node ./scripts/release.mjs on main.
  7. Monitor .github/workflows/publish.yml.
  8. Verify the new version on npm.

Security Notes

  • Publishing uses npm trusted publishing with GitHub Actions OIDC.
  • No long-lived NPM_TOKEN is required for the publish step.
  • Provenance is not claimed in this phase because the source repository is private.
  • scripts/install-pandoc.mjs downloads a pinned Pandoc release, but it does not yet verify upstream checksums.

Operational runbook: docs/release-runbook.md