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

@zero2vibecode/terminal-sim

v0.1.5

Published

A deterministic terminal simulator with verifiable learning goals. No shell, no network, no filesystem — safe to put in front of a beginner.

Readme

@zero2vibecode/terminal-sim

A deterministic terminal simulator with verifiable goals. No shell, no network, no filesystem — which is exactly why it is safe to put in front of a beginner, and why an exercise built on it can be proven solvable in CI.

Type in it right now → — that page is this package with a UI on top. mkdir a directory and ls will list it; pipe something into grep; break it and read the error. Nothing is being faked for the demo.

npm i @zero2vibecode/terminal-sim

A terminal session recorded from the live site: ls, then cat printing a file's contents, then mkdir, then echo writing a file and cat reading it back

Recorded from that page, not staged — the same commands work in the box.

import { TerminalSession } from "@zero2vibecode/terminal-sim";

const session = new TerminalSession({
  files: { "notes/todo.md": "buy milk" },
  goals: [{ type: "fileExists", path: "notes/done.md" }],
});

session.run("ls notes");        // → "todo.md"
session.goalsMet;               // → false
session.run("touch notes/done.md");
session.goalsMet;               // → true

Why this exists

Teaching the command line has an awkward gap. A real shell is honest but dangerous, and you cannot ship one in a browser. A screenshot is safe but dead. A fake that just prints canned responses teaches the learner to type the expected thing rather than to think.

This is the third option: a real interpreter over an in-memory filesystem. mkdir makes a directory that ls then lists and rm then removes. Pipes, redirects, $VAR, aliases and .sh execution work. git, npm and node are simulated well enough to teach the shape of using them. What it cannot do is touch anything outside itself.

The goals engine is the part you cannot get from an emulator. An exercise declares what "done" means — a directory exists, a file contains something, a command was run, the output said a word — and the session answers whether the learner got there. That turns a toy terminal into a checkable exercise.

The solvability contract

If an exercise ships with a solution, the solution should be proven to satisfy the exercise's own goals. Not reviewed — executed:

import { verifySolution } from "@zero2vibecode/terminal-sim";

const { solved } = verifySolution(
  ["mkdir notes", "cd notes", "touch todo.md"],   // the author's solution
  [                                               // the exercise's goals
    { type: "dirExists", path: "notes" },
    { type: "fileExists", path: "notes/todo.md" },
  ],
);

Run that over your whole course in CI and an unsolvable exercise fails the build instead of a learner. This is the discipline the package was extracted from: every interactive lesson on zero2vibecode.com is proven completable this way on every commit.

Goals

| Goal | Met when | |---|---| | { type: "dirExists", path } | a directory exists at path | | { type: "fileExists", path } | a file exists at path | | { type: "fileContains", path, text } | that file's contents include text | | { type: "cwdIs", path } | the session's working directory is path | | { type: "commandRun", matches } | any command entered matches this regex | | { type: "outputContains", text } | any output printed includes text |

Paths resolve the way the learner's own would: relative to the home directory the session starts in (/home/user), unless they begin with / or ~. A malformed commandRun pattern compiles to a never-match rather than throwing — a typo in content must not crash a lesson.

API

new TerminalSession({ files?, goals? })

| Member | What it gives you | |---|---| | run(line) | executes one line, returns { input, output, failed, lines } | | runAll(lines) | executes several in order | | goalsMet | true when every goal is satisfied | | goalStatus | per-goal verdicts, in declaration order — render a checklist | | transcript | every line executed so far | | history | just the commands, as the up-arrow would walk them | | files / cwd | the current filesystem and working directory | | toLog() | the session as plain text ($ command then its output) |

verifySolution(solution, goals, files?){ solved, session }

Output lines carry a kind (input / output / error / system) on line.lines, so a UI can colour them; line.output is the same thing flattened to text.

Beyond the facade

The engine's own modules are exported as subpaths — @zero2vibecode/terminal-sim/vfs, /commands, /goals, /interpreter, /complete, /suggest, /history, /prompt, /env, /log, /types — for building your own UI on top: an immutable filesystem, tab completion, "did you mean" suggestions, prompt rendering.

The split is deliberate. The main entry point is a contract and follows semver strictly. The subpaths are the workshop: useful, documented, and allowed to change in a minor release. If you build on them, pin a minor version.

Determinism

Same input, same output, every time. No Date.now(), no Math.random(), no I/O in the engine. That is a teaching requirement, not an aesthetic one: a lesson that behaves differently on the second run cannot be checked, and a hydration mismatch is a bug the learner sees.

Not a real shell

Not POSIX-complete, not an xterm.js replacement, not a sandboxed VM. It is a teaching instrument. If you need to run actual programs, you need a container, not this.

Feedback

Bugs, missing commands and API friction: [email protected].

This package's source is not public today — it lives in the site's repository, which is private for now. That is a decision about timing rather than a policy, so it may change; until it does there is no issue tracker and no pull requests here. The trade is that the maintainer reads that inbox himself and a reproducible report gets a release. If a command you need is missing, say which one and what you expected it to print.

The project does keep a public repository — agents-compared, a comparison of AI coding agents written to the same discipline as the course.

License

MIT. Built for zero2vibecode.com, useful anywhere.