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

@dittowords/spec-cli

v0.0.1-alpha.22

Published

CLI for syncing .ditto.md content specs with the Ditto platform.

Readme

@dittowords/spec-cli

Alpha — this package is in early development. The format and CLI interface may change between releases.

CLI for syncing Ditto spec files with the Ditto platform.

A .ditto.md Ditto spec file lives next to a component and declares its text surfaces — every piece of user-facing copy the component renders, whether passed as props, children, or hardcoded in the component itself. The CLI syncs matching style guide rules from the Ditto platform into each file, co-locating content governance directly in your design system alongside the components themselves. The file is pure metadata; nothing imports it at runtime. It exists for three consumers:

  • Agents read it as fast-path context when writing or editing copy for the component.
  • The CLI (ditto-spec pull) syncs style guide rules from the platform whose tags match the Ditto spec's surface tags.
  • Humans review content decisions in PRs.

File format

Everything lives in YAML frontmatter. The markdown body below the closing --- is unused.

Component Ditto spec (<Component>/index.ditto.md)

---
component: DialogueModal
tags: [dialog, confirmation]
surfaces:
  headline:
    tags: [heading, dialog-title]
    maxLength: 60
  content:
    tags: [body, dialog-body]
    maxLength: 240
  actionText:
    tags: [call-to-action]
    maxLength: 25
  cancelText:
    tags: [button]
    maxLength: 25
# Managed by Ditto — do not edit below
rules:
  - name: Confirmation dialogs should be direct
    description: Keep confirmation copy terse and unambiguous
    section: Voice & Tone
  - surface: actionText
    name: Calls to action should use active voice
    description: Always lead with a verb
    examples:
      - from: "Your settings"
        to: "Open settings"
    section: Voice & Tone
  - term: sign up
    disallowed:
      - signup
      - sign-up
    description: Always use as two words (verb form)
    section: Terminology
locales:
  de-DE:
    - name: Use informal address
      description: Use "Du" instead of "Sie" for all user-facing copy
      section: Formality
---

Workspace Ditto spec (workspace.ditto.md)

A repo may have a single workspace.ditto.md somewhere under the CLI's configured roots. It holds universal style guide rules that carry no tags — these apply to every surface in every component. It also carries an inventory of all tags available on the platform, populated by ditto-spec pull.

---
workspace: true
# Managed by Ditto — do not edit below
tags: [body, button, call-to-action, dialog-title, heading, nav]
rules:
  - name: Write in active voice
    description: Lead with verbs, avoid passive constructions
    section: Voice & Tone
locales:
  de-DE:
    - name: Use informal address
      description: Use "Du" instead of "Sie" for all user-facing copy
      section: Formality
---

Key concepts

Developer-owned keys: component, tags, surfaces. Edit these freely.

CLI-managed keys: rules, locales, and workspace tags. Overwritten by ditto-spec pull. Do not edit by hand. Style guide rules come in two shapes: style rules (name/description/examples) and terminology entries (term/disallowed/description).

Surface keys identify each distinct piece of user-facing text the component renders. For text passed as props, use the prop path as the key — dot-notation works for nested props (e.g., primaryAction.label). Use $children for text via the children prop. For hardcoded or internal strings, use a descriptive role name (e.g., headline, bodyText, submitLabel).

maxLength is a hard layout constraint, not a stylistic preference. Stylistic guidance belongs on the platform as rules.

Tagging

Ditto specs support two levels of tagging that mirror how design systems organize components:

  • Component-level tags describe the component itself — its role in the design system. A DialogueModal tagged [dialog, confirmation] pulls in style guide rules about how confirmation dialogs should read. These rules apply to every surface in the component.
  • Surface-level tags describe each individual piece of text — its function within the component. An actionText surface tagged [call-to-action] pulls in rules specific to CTAs (e.g. "lead with a verb"). These rules apply only to that surface.

Both levels work together: in the DialogueModal example above, the headline surface inherits rules matched by dialog and confirmation (from the component) plus rules matched by heading and dialog-title (from the surface). If a rule matches both levels, it appears once at component level — broader scope wins.

Create style guide rules on the Ditto platform scoped to design system concepts (dialog, card, form, navigation) and they automatically cascade to every component tagged with that concept. Surface-level tags layer on more specific guidance.

Rule hierarchy

| Scope | Where | Applies to | |---|---|---| | Workspace | workspace.ditto.md rules[] | Every surface in every component | | Component-level | Component's rules[], no surface field | Every surface in this component | | Per-surface | Component's rules[], with surface: "<key>" | That one surface | | Locale-scoped | locales.<code>[] (workspace or component) | Same hierarchy as above, but only when writing copy for that locale |

Base rules in rules always apply. Locale-scoped rules in locales apply only when writing copy for the matching locale — they never conflict with each other because each locale is a separate scope.

CLI commands

ditto-spec init

First-time setup. Creates dittospec.config.json and workspace.ditto.md in the current directory, then detects your agent environment (Claude Code, Cursor) and prints setup suggestions.

Use --agent to also write agent configuration directly:

  • Claude Code: appends a Ditto Specs section to CLAUDE.md (creates the file if absent) and writes skill files to .claude/commands/ (see Agent skills below).
  • Cursor: appends a Ditto Specs section to .cursorrules.

Re-running init --agent is safe — it updates skill files to the current CLI version and skips sections that already exist in CLAUDE.md.

ditto-spec scaffold <ComponentName>

Creates a new index.ditto.md Ditto spec file for a component with the correct YAML structure and empty managed keys.

ditto-spec scaffold DialogueModal --path src/components/DialogueModal

Use --path <dir> to specify where the file is created (defaults to the current directory). After scaffolding, add a surface for each piece of user-facing text the component renders and run ditto-spec pull to populate rules.

ditto-spec pull

Syncs style guide rules from the platform into Ditto spec files.

  1. Discovers all .ditto.md files under configured roots
  2. Fetches style guides from GET /v2/styleguides
  3. Flattens rules and wordlist entries across all guides (or only those named in styleguides config)
  4. Separates base rules from locale-scoped rules (included when locales is configured)
  5. Matches rules to specs by tag intersection (client-side)
  6. Rewrites the rules and locales keys in each file's YAML frontmatter

Use --dry-run to see what would change without writing.

ditto-spec check

Validates all Ditto spec files: YAML parses correctly, required keys are present, surfaces have tags arrays. Exits non-zero on any failure — useful for CI.

ditto-spec list

Prints an inventory of all component Ditto specs with their surfaces, tags, and constraints.

ditto-spec rules

Prints every rule on the platform, grouped by style guide and section (with each section's sectionId and kind). Pass --styleguide "<name-or-id>" to limit to one guide.

ditto-spec create-rules

Creates a batch of rules on the Ditto platform. Takes a JSON array of rules on stdin (or via --file <path>):

ditto-spec create-rules <<'EOF'
[
  {"name": "Use active voice", "description": "Lead CTAs with a verb", "tags": ["button", "call-to-action"], "examples": [{"from": "Your settings", "to": "Open settings"}], "section": "UI Patterns"},
  {"term": "sign up", "disallowed": ["signup", "sign-up"], "description": "Two words as a verb", "section": "Word List"}
]
EOF

Each rule is one of two shapes:

| Shape | Fields | |---|---| | Style rule | name (required), description, examples (array of {from, to}), tags, section | | Terminology entry | term (required), disallowed (array of strings), description, tags, section |

Shapes can be mixed in one batch. Each rule's optional section (name or ID, as shown by ditto-spec rules) maps it to an existing section of the style guide; the section's kind must match the rule's shape (rules sections for style rules, wordlist sections for terminology entries). Rules without a section go to the first section of the matching kind. One API call is made per target section.

| Flag | Description | |---|---| | --file <path> | Read the JSON array from a file instead of stdin | | --styleguide | Target style guide name or ID (optional, overrides defaultStyleguide config) | | --section | Default section for rules that don't specify their own (optional; applies only to rules matching the section's kind — others fall back to the first section of theirs) |

After creating rules, run ditto-spec pull to sync them into Ditto spec files.

Configuration

Create dittospec.config.json at your repo root (or any ancestor directory):

{
  "apiBase": "https://api.dittowords.com",
  "roots": ["design-system"]
}

| Key | Description | |---|---| | apiBase | Ditto API base URL | | roots | Repo-relative directories to search for .ditto.md files. Defaults to ["."]. | | styleguides | Optional list of style guide names or IDs to pull. Defaults to all. | | locales | Optional list of locale codes (e.g. ["de-DE", "fr-FR"]). Includes locale-scoped style guides matching these codes. Base (no-variant) guides are always included. | | defaultStyleguide | Optional style guide name or ID for create-rules. Overridable with the --styleguide flag. Defaults to the first guide returned by the API. |

Set DITTO_TOKEN in your environment or in a .env file at the repo root.

Agent skills

When you run ditto-spec init --agent in a Claude Code project, the CLI writes three skill files into .claude/commands/. These are slash commands that give agents interactive, multi-step workflows for working with Ditto specs.

| Skill | What it does | |---|---| | /ditto-spec-component <Name> | Analyze a component's text surfaces, scaffold a Ditto spec file (or update an existing one), and sync style guide rules from the platform. Handles child components too. | | /ditto-spec-audit [Name] | Audit copy in component instances against style guide rules. Reports violations with file locations and suggested corrections. Omit the name to audit all specced components. | | /ditto-spec-gaps [Name] | Find copy patterns that should be style guide rules but aren't. Proposes new style rules and terminology entries, then creates approved ones on the platform via create-rules. |

Skills are written into your repo and committed alongside your Ditto specs and config. Every team member gets them automatically — no separate plugin install.

Updating skills: Re-run ditto-spec init --agent after updating the CLI to get the latest skill versions. Existing skills are overwritten; the CLAUDE.md Ditto Specs section is left untouched if already present.

Customizing skills: The skill files are plain markdown in .claude/commands/. You can edit them to add project-specific behavior (e.g. default tags, custom audit checks). Re-running init --agent will overwrite your changes, so commit customizations and manage updates deliberately.

Agent contract

When writing or editing user-facing text for a component:

  1. Read workspace.ditto.md (if it exists) for universal style guide rules.
  2. Read the component's index.ditto.md. Match each piece of text you're writing to a surface key.
  3. Respect maxLength — it's a layout invariant, not a suggestion.
  4. Follow all rules in rules[]. Entries without surface apply to every surface; entries with surface apply only to that surface.
  5. Rules come in two shapes:
    • Style rules have name, description, and optional examples (before/after pairs). Use examples as concrete tone/shape guidance.
    • Terminology entries have term and disallowed. Always use the term form; never use any of the disallowed alternatives.
  6. Each rule carries a section field (e.g. "Voice & Tone", "Terminology") providing context for how to interpret it.
  7. If a locales key is present, it contains locale-scoped rules keyed by locale code (e.g. de-DE). When writing copy for a specific locale, follow the matching locales.<code> rules in addition to the base rules. When writing for the default/base locale, only follow rules. Locale-scoped rules never conflict — each locale is a separate boundary.

Creating Ditto specs

When creating a new component that renders any user-facing text, scaffold a Ditto spec file:

npx ditto-spec scaffold <ComponentName> --path <dir>

Then edit the generated index.ditto.md to add surfaces — one entry per piece of user-facing text the component renders:

surfaces:
  title:
    tags: [heading]
    maxLength: 60
  $children:
    tags: [button, cta]
    maxLength: 30
  • Use $children for text via children. Use dot notation for nested props (primaryAction.label). For hardcoded or internal strings, use a descriptive role name (headline, bodyText, submitLabel).
  • Check the tags key in workspace.ditto.md for tags available on the platform. Prefer reusing an existing tag over creating a new one — only a tag that exists on the platform will match rules. If no existing tag fits, create a new one following the convention of existing tags (lowercase, hyphenated).
  • Never write rules by hand. Run ditto-spec pull after adding surfaces to populate rules from the platform.

Parent and child specs both contribute rules. If your component passes a label to a child Button, add a surface in the parent's spec — the parent's rules (e.g., dialog-level tone) layer with the child's rules (e.g., button-level constraints). A child having its own spec does not exempt the parent from declaring surfaces for text it provides.

If a component lacks a Ditto spec and you'd have found one useful, propose creating one. If a Ditto spec lacks a rule you'd have wanted, propose adding the rule to the platform style guide with appropriate tags.