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

@spectastic/core

v0.1.0-pre.28

Published

Spectastic verb kernel. Single async function per slash-command verb; consumed by @spectastic/cli, the future MCP server, the future VS Code extension, and the future web editor without per-surface duplication.

Readme

@spectastic/core

npm downloads node license

The verb kernel for spectastic. One TypeScript module every surface shares — the CLI, an MCP server, an editor extension — so a verb's procedure lives in one place and downstream surfaces never re-implement it.

Deterministic logic lives here; a CLI command module is thin, registering the command and delegating.

What's in it

Seventeen verbs, each on its own subpath under @spectastic/core/commands/:

apply · contract · course · design · explore · graduate · id · implement · order · principles · propose · restore-marker · spec · tasks · triage · validate · verify

Alongside them, the deterministic modules a second caller would want without the CLI — enforcement detection and policy (./enforce/*), change-risk scanning and scoring (./change-risk/*), gitignore merging (./gitignore/*), contract promotion and views (./contracts/*), unit dependency edges (./units/*), test-tag resolution (./testtags/*), and the execution guard (./execcheck/*).

The injected surface is the type entry: KernelContext, FileSystem, AIProvider (chat + ask<T> + subagent), Question, and per-verb input/result shapes. A default FileSystem lives at @spectastic/core/providers/node-fs, and a scriptable StubAIProvider at @spectastic/core/providers/stub — which is what the integration tests run against, never a real model.

Importing a verb

Per-verb subpath exports keep the lazy-loading discipline. Import each verb from its own subpath — never via the main entry.

// Right: subpath import loads only what this verb needs.
import { validateCommand } from '@spectastic/core/commands/validate';

// Also right: types from the main entry — zero command code loaded.
import type { KernelContext, ValidateInput, ValidateResult } from '@spectastic/core';

// Wrong: there is no umbrella re-export of verb functions, by design.
// import { validateCommand } from '@spectastic/core';

This shape is enforced by the bench's init-help-cold-start scenario in bench/baselines.json — if the kernel ever eagerly loads parse5 (or any AI adapter) on a path that doesn't need it, the bench fires.

Calling a verb

Every kernel function follows the same shape: async function <verb>Command(input, ctx): Promise<Result>. The ctx is the injected IO + AI surface; verbs that don't need AI leave ctx.ai undefined.

import { validateCommand } from '@spectastic/core/commands/validate';

const result = await validateCommand(
  { files: ['/path/to/spec.html'] },
  { cwd: process.cwd() },
);

console.log(`${result.findings.length} findings; exit ${result.exitCode}`);

When ctx.fs is undefined the kernel lazy-loads the default nodeFs impl. To unit-test against in-memory fixtures, pass a stubbed FileSystem:

import { validateCommand } from '@spectastic/core/commands/validate';
import type { FileSystem } from '@spectastic/core';

const stubFs: FileSystem = {
  async readFile(path) {
    if (path === '/test/clean.html') return '<!doctype html>…';
    throw new Error(`ENOENT: ${path}`);
  },
  async writeFile() { throw new Error('not implemented'); },
  async readdir() { return []; },
  async stat(path) { return { isFile: true, isDirectory: false }; },
};

const result = await validateCommand({ files: ['/test/clean.html'] }, {
  cwd: '/test',
  fs: stubFs,
});

The AIProvider surface

AIProvider is the contract for AI access, and it was declared whole up front: all three methods (chat, ask<T>, subagent) exist even where a given verb needs none of them. That was deliberate — landing a real provider, and later lighting up subagent(), are both additive rather than interface-extending breaking changes.

interface AIProvider {
  chat(prompt: string, opts?: ChatOpts): Promise<string>;
  ask<T extends Record<string, string>>(
    questions: ReadonlyArray<Question>,
  ): Promise<T>;
  subagent(prompt: string, opts?: SubagentOpts): Promise<SubagentResult>;
}

Question mirrors Claude Code's AskUserQuestion shape exactly so the Claude provider can route the call straight through; MCP servers and VS Code extensions render the same Question data in their native UI.

Versioning policy — pre-1.0

While the kernel surface is still being shaped (verbs landing in sequence through 014), this package follows a pre-1.0 policy: breaking changes may land in minor version bumps. Downstream consumers should pin tightly with ~0.x.y, not ^0.x.y:

{
  "dependencies": {
    "@spectastic/core": "~0.1.0-pre.8"
  }
}

At 1.0.0 the surface freezes and strict semver applies. The graduation criteria are recorded in the kernel-extraction spec.

Extending the kernel — adding a verb

The pattern future extractions follow (the broader slicing recipe is in the slicing-gaps register):

  1. Author the spec at specs/NNN-core-<verb>/spec.html with <spec-parent specid="006-kernel-extraction">.
  2. Run /spectastic.design then /spectastic.tasks.
  3. Add the verb's input/result shapes to packages/core/src/types.ts.
  4. Create packages/core/src/commands/<verb>.ts with <verb>Command(input, ctx).
  5. Add the new entry to packages/core/tsup.config.ts.
  6. Add a new subpath to packages/core/package.json's exports field.
  7. Write packages/core/test/<verb>.test.ts with stub ctx.fs + stub ai as needed.
  8. If the verb has a slash-command counterpart, update commands/spectastic.<verb>.md with a note: "For deterministic operations, the model MAY invoke spectastic <verb> via Bash."
  9. Add a spectastic <verb> CLI subcommand at packages/cli/src/commands/<verb>.ts that imports from @spectastic/core/commands/<verb> and translates the result.
  10. Bench passes; full-project validate passes; commit; tag; ship.

Linked artifacts

License

MIT