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

@papia/text-cli

v0.3.0

Published

papia — CLI for ALUPEC orthography on Cape Verdean Kriolu Markdown (lint, fix, suggest).

Readme

@papia/text-cli

npm version License: MIT

papia is a Node CLI for ALUPEC orthography tooling on Cape Verdean Kriolu markdown:

  • papia lint — flag ALUPEC orthography violations
  • papia fix — apply auto-fixable corrections
  • papia suggest — call an LLM for replacement suggestions on each violation

Pure-stateless v1. Reads from stdin or a file path, writes to stdout. No filesystem-backed .papia.json sidecar yet — that lands in a future release.

Install

From npm (recommended)

npm install -g @papia/text-cli   # globally — `papia` on $PATH
# or in a project
npm install -D @papia/text-cli
npx papia --version

The bundle is self-contained: tsup inlines @papia/text-core and @papia/llm-core into dist/cli.js, so there are no @papia/* runtime dependencies to resolve.

Inside this monorepo

pnpm install
pnpm -C packages/text-cli build   # produces a self-contained dist/cli.js
pnpm exec papia --version          # prints 0.1.1

From a local tarball

pnpm -C packages/text-cli build
pnpm -C packages/text-cli pack
# → packages/text-cli/papia-text-cli-0.1.1.tgz
npm install -g /absolute/path/to/papia-text-cli-0.1.1.tgz

The tarball is git-ignored (*.tgz), so rebuild whenever you bump.

Commands

papia lint

papia lint <file.md>           # read a file, emit diagnostics to stdout
cat draft.md | papia lint      # read stdin
papia lint --reporter json     # JSON output (one document, stable shape)

pretty (default) emits eslint-style path:line:col severity rule message lines, color-aware via process.stdout.isTTY. json emits { "diagnostics": [...], "summary": { files, errors, warnings } } — diagnostics can additionally carry an editors map per finding when editor flags are set (see Editor deep-links). markdown emits a GFM-table report with optional per-diagnostic editor deep-links — see Editor deep-links below.

Exit codes: 0 clean, 1 violations present, 2 runtime error.

papia fix

papia fix <file.md>            # corrected text to stdout
papia fix <file.md> --write    # rewrite the file in place
cat draft.md | papia fix       # corrected text to stdout (stdin)

--write requires a file argument; it is rejected (exit 2) when input comes from stdin. Auto-fixes are applied right-to-left so unfixed offsets stay valid; running papia fix twice on the same input is byte-identical.

Exit codes: 0 after a successful pass (whether or not fixes were applied), 1 when unfixable violations remain after the auto-fix pass, 2 runtime error.

papia suggest

GOOGLE_GENERATIVE_AI_API_KEY=… papia suggest <file.md>
papia suggest <file.md> --reporter json
papia suggest --config ./papia.config.json <file.md>

Lints, then for each diagnostic asks @papia/llm-core's suggest() for a replacement. The pretty reporter appends a dim → Suggestion: … line under each diagnostic. The JSON reporter adds an optional aiSuggestion: string field per diagnostic.

papia suggest is sequential — one round-trip per diagnostic, no batching in v1. Pre-filter very large files if cost or latency is a concern.

Exit codes: 0 after a successful pass, 1 when violations remain, 2 runtime error or unresolved API key.

Editor deep-links

When --reporter=markdown or --reporter=json is used, papia lint can emit a clickable deep-link per finding so reviewers (and downstream tools) can jump straight from the report to the file in their editor.

papia lint --reporter=markdown --editor=skrebe drafts/lesson.md
papia lint --reporter=markdown --editor=skrebe,vscode,finder drafts/lesson.md
papia lint --reporter=markdown \
  --editor-url-template='zed://file/{absPath:raw}:{line}:{col}' \
  drafts/lesson.md

# JSON consumers — URLs embedded per-diagnostic under `editors`
papia lint --reporter=json --editor=skrebe,vscode,finder drafts/lesson.md

For --reporter=json, each diagnostic gains an optional editors field:

{
  "diagnostics": [
    {
      "ruleId": "ALUPEC_CHAR_CEDILLA",
      "severity": "error",
      "message": "Use 's' instead of 'ç' in ALUPEC",
      "filePath": "drafts/lesson.md",
      "line": 3, "column": 6,
      "editors": {
        "skrebe": "http://localhost:3001/editor/lesson?file=drafts%2Flesson.md",
        "vscode": "vscode://file/.../drafts/lesson.md:3:6",
        "finder": "file:///.../drafts/lesson.md",
        "custom": ["zed://file/.../drafts/lesson.md:3:6"]
      }
    }
  ]
}

The editors field is omitted entirely when no editor flags are set, so existing JSON consumers see a byte-identical schema unless they opt in.

Named editors (--editor=<csv>):

| Name | URL shape | |---|---| | skrebe | http://localhost:3001/editor/<bundle>?file=<encoded-relpath> (override host via --skrebe-host) | | vscode | vscode://file<absolute-path>:<line>:<column> | | finder | file://<absolute-path> (macOS) |

Custom editors (--editor-url-template=<template>):

Template placeholders are substituted with workspace-resolved values from each diagnostic. Substitutions are URL-encoded by default; append :raw to skip encoding.

| Placeholder | Meaning | |---|---| | {absPath} | Absolute path to the file | | {relPath} | Path relative to the workspace root (cwd at invocation time) | | {basename} | Filename without .md / .mdx extension | | {line} | 1-based line number of the finding | | {col} | 1-based column number of the finding | | {absPath:raw} (etc.) | Same value, no URL-encoding |

Unknown placeholders are silently substituted with an empty string (a stderr warning is also emitted so typos like {absPth} surface during invocation).

Constraints (v0.1.1): --editor accepts a comma-delimited list rather than being repeated. --editor-url-template takes a single template; to emit multiple custom URLs, invoke papia lint once per template and concatenate.

--editor, --editor-url-template, and --skrebe-host apply to both --reporter=markdown and --reporter=json. They have no effect with --reporter=pretty — the CLI emits a stderr warning if any are set on a pretty-reporter invocation.

Configuration

The CLI walks up from the current working directory looking for papia.config.json. --config <path> overrides the walk-up; missing file at the explicit path is exit 2. Missing config is fine for lint and fix (no API key is needed); suggest falls back to the provider's default env var.

{
  "$schema": "https://papia.studio/schema/papia.config.json",
  "version": 1,
  "aiProvider": "google",          // optional — "google" | "anthropic" | "openai"
  "aiModel": "gemini-2.5-flash",   // optional — defaults to llm-core's default
  "apiKey": { "envVar": "MY_KEY" } // optional — see precedence below
}

API-key resolution precedence

papia suggest resolves the key in this order, stopping at the first hit:

  1. apiKey.literal in papia.config.json (discouraged for committed configs)
  2. apiKey.envVar in papia.config.jsonprocess.env[envVar]
  3. apiKey.keychainnot implemented in v1; the CLI returns a clear error pointing at the env var fallback
  4. Provider default env var:
    • Google → GOOGLE_GENERATIVE_AI_API_KEY
    • Anthropic → ANTHROPIC_API_KEY
    • OpenAI → OPENAI_API_KEY

If none yield a value, papia suggest exits 2 before contacting the LLM and names the env var on stderr.

Exit codes

| Code | Meaning | |------|---------| | 0 | Clean run (no violations, or fix succeeded) | | 1 | Violations present (lint, suggest) or unfixable violations remain after fix | | 2 | Runtime error: file not found, invalid config, unresolved API key, LLM failure, --write with stdin |

Limitations (v0.1.1)

  • Single file or stdin per invocation. No directories, globs, or watch mode.
  • No filesystem-backed sidecar (.papia.json). State per invocation only.
  • No glossary integration; suggest calls use variant: "general" and an empty glossary.
  • No OS keychain bridging. apiKey.keychain parses but resolves to undefined.
  • No batching for suggest — one LLM round-trip per diagnostic.
  • --editor is comma-delimited (--editor=skrebe,vscode) rather than repeatable across multiple --editor= flags. Citty 0.2.2 does not expose multiple: true through its declarative arg API; a future release may switch to repeatable.
  • --editor-url-template accepts a single template per invocation.

Roadmap

Future releases will unlock filesystem-backed .papia.json sidecar state, folder and watch mode, and glossary management commands.