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

tenbrains

v2.5.1

Published

Agent-first CLI for X and YouTube research: analyze content, track accounts, surface suggestions, and persist every outcome locally.

Readme

tenbrains

An agent-first CLI for X and YouTube research. It analyzes posts and video transcripts, tracks followed accounts, surfaces suggestions, and persists every outcome to a local SQLite database — all from the command line, with no .env files and no hosted backend.

This is a ground-up rebuild of moeghashim/tenbrains (originally a Convex-backed web workspace) reshaped around a single design goal: be consumed by agents. Every command emits one stable JSON envelope on stdout, every failure carries a machine-readable code and a deterministic exit status, and the entire surface is discoverable via tenbrains manifest.

Why it's "agent-first"

  • Structured output by default. stdout is always exactly one JSON envelope. Diagnostics and progress go to stderr, so a parser never has to untangle the two. --pretty switches to human-readable output for terminal use.
  • Stable contract. { ok, command, data, meta } on success, { ok, command, error } on failure. Error codes and process exit codes are fixed per failure class.
  • Self-describing. tenbrains manifest returns the full command tree, flags, provider catalog, error codes, and exit codes as JSON — an agent can discover the whole tool in one call.
  • Non-interactive. No blocking prompts. Content comes in via flags, @file, or - (stdin); credentials are collected through commands, never by hand-editing a dotfile.
  • Everything persisted. Posts, analyses, takeaways, bookmarks, suggestions, learning tracks, and learning objectives all land in one local SQLite file you can point anywhere with --db.
  • Offline-capable. The built-in mock provider produces deterministic analysis and track material with no network, so agents and CI can exercise the full pipeline without API keys.

Requirements

  • Node.js >= 24 (uses the built-in node:sqlite — no native modules to compile).

Install

npm install -g tenbrains               # from npm — puts `tenbrains` (and `tb`) on PATH
tenbrains --help

Or from a clone:

npm install
npm run build
node dist/bin/tenbrains.js --help      # or: npm link  ->  tenbrains --help

During development you can run straight from TypeScript:

npm run dev -- analyze --provider mock --text "hello world"

Quick start

# 1. Configure a provider once (stored in ~/.config/tenbrains/config.json, mode 0600).
#    Optionally add an X API Bearer token in the same step (see "Fetching from X").
tenbrains setup --provider anthropic --api-key sk-ant-... --default

# 2a. Analyze a post you already have (paste the content).
tenbrains analyze --author levelsio --id 1790000000000000000 \
  --text "Shipping an agent-first CLI today. Everything persists to SQLite, nothing in env files."

# 2b. Or analyze a tweet by URL — fetched free via X's oEmbed endpoint, no key needed.
tenbrains analyze --url "https://x.com/jack/status/20"

# 2c. Or fetch and analyze public YouTube captions, then add a narrative digest and study plan.
tenbrains analyze --url "https://www.youtube.com/watch?v=M7lc1UVf-VE" --summarize --learn

# 3. Read it back / explore.
tenbrains analyze list --limit 5
tenbrains search "agent cli"
tenbrains db stats --pretty

No API key handy? Use the deterministic offline provider:

tenbrains analyze --provider mock --text "vector databases power semantic search"

The output contract

Success:

{
  "ok": true,
  "command": "analyze",
  "data": { "post": { "...": "..." }, "analysis": { "topic": "...", "novelConcepts": ["..."] } },
  "meta": { "analysisId": "ana_...", "provider": "anthropic", "model": "claude-sonnet-4-6", "mock": false, "persisted": true }
}

Failure:

{
  "ok": false,
  "command": "analyze",
  "error": { "code": "MISSING_CREDENTIALS", "message": "No API key configured for Anthropic Claude...", "retryable": false, "details": { "provider": "anthropic" } }
}

Exit codes: 0 success · 2 usage · 3 not found · 4 missing credentials / config · 5 provider error · 6 validation · 7 conflict · 1 internal. The full code→exit map is in tenbrains manifest.

Commands

| Command | Purpose | | --- | --- | | analyze | Analyze a post or YouTube transcript (--text, --transcript, or --url to fetch) into topic, summary, intent, and concepts. --summarize adds a narrative digest; --learn builds a track and materializes it when it carries a learn-mode objective. | | analyze list / analyze get <id> | Read stored analyses. | | objective add\|list\|show\|focus\|archive\|link\|unlink\|mode\|refresh | Manage first-class objectives, switch learn/follow mode, explicitly tag records, and build local engagement-weighted follow tracks. | | takeaway follow\|unfollow\|list\|refresh\|show | Track accounts; summarize recent posts (supplied via --posts or fetched from X) into snapshots. | | suggest generate\|list\|save\|dismiss\|add | Rank un-saved posts against your saved signal, biased toward a described current objective focus; save/dismiss feedback. | | bookmark add\|list\|show\|tag\|remove | Save posts with tags (auto-suggested from analysis) and notes. | | learn generate\|materialize\|today\|quiz\|answer\|done\|show\|list | Build 7-day Feynman learning tracks, generate grounded lessons/quizzes, answer free-text quiz questions, and check off progress. | | search <query> | Full-text search (SQLite FTS5, BM25-ranked, stemmed) across analyses, takeaways, and bookmarks; optionally filter by objective. | | import x-archive <path> | Bulk-import your extracted official X archive: likes become bookmarked posts, your tweets become posts. Free, idempotent. | | digest [--days N] | Markdown recap of analyses, takeaways, and bookmarks saved in the window (default 7 days), optionally filtered by objective. | | setup / config set\|get\|list\|unset\|path | Collect and manage provider credentials and defaults. | | record get <id> | Resolve any record by its prefixed id (post_, ana_, acc_, ...). | | db stats\|migrate\|vacuum\|reindex\|reset | Inspect and maintain the database. | | manifest | Emit a machine-readable description of the whole CLI. | | serve [--port N] [--host HOST] [--no-open] | Serve the local web workspace and its authenticated command API. |

Global flags (valid on any command): --json (default), --pretty, --quiet, --db <path>, --config-dir <path>.

Input flags accept inline text, @path to read a file, or - to read stdin:

echo "long post text..." | tenbrains analyze --provider mock --text -
tenbrains takeaway refresh levelsio --provider mock --posts @recent.json

Local web workspace

Start the first local web surface with the same database and command handlers the CLI uses:

tenbrains serve                         # http://127.0.0.1:4571 and opens a browser
tenbrains serve --port 5000 --db ./research.db
tenbrains serve --no-open               # print the JSON startup envelope without opening a browser

The server binds to 127.0.0.1 by default. It creates a fresh, per-process session token, injects it into the served page, and requires it in the X-Tenbrains-Session header for every /api/* request. The page uses GET /api/manifest, POST /api/command, and GET /api/events; command responses remain the ordinary tenbrains JSON envelopes. Do not expose it publicly: choosing a non-loopback --host prints a warning because any reachable client can use that local session. The API never returns revealed config secrets, even if a request supplies reveal: true.

The built page is a dependency-free TypeScript SPA for the full local workspace: Today’s daily loop, Capture and extracted X-archive import, objective creation/detail/focus rollups, suggestions triage, full-text Search with record hops, markdown Digest windows, and followed-account takeaways. Objective mode, lesson, and quiz controls are discovered from the manifest. When the server publishes learn quiz and learn answer, Today renders the lesson, per-question status, answer boxes, and one-line verdicts without making quizzes a day-completion gate. Account refresh accepts supplied post JSON in the page or fetches an X timeline when a bearer token is configured.

POST /api/import/x-archive imports the extracted X archive data files without giving the server a filesystem path. Send JSON with files, each { "name": "like.js", "content": "..." }; accepted names are like.js, like-partN.js, tweets.js, tweets-partN.js, tweet.js, tweet-partN.js, and account.js (including part variants). bookmarks: false is the web equivalent of --no-bookmarks, and limit is a non-negative per-kind item cap. The route shares the CLI's idempotent ingest semantics and returns its import counts in a standard envelope. Unzip the archive first: zip files are not accepted. It uses JSON rather than multipart to preserve the zero-dependency server; the complete request is capped at 25 MiB and oversized uploads return a VALIDATION envelope with HTTP 400.

Learning objectives

Objectives are persistent learning goals, separate from loose bookmark tags. Each objective has a name, derived slug, optional description, learn (default) or follow mode, active/archived lifecycle, and tagged-record counts. You can keep several active objectives while marking at most one as the current focus:

tenbrains objective add "Stablecoins" \
  --description "Understand reserve models, settlement, and failure modes." --mode learn --focus
tenbrains objective add "AI agents scene" --mode follow
tenbrains objective list
tenbrains objective show                 # defaults to the current focus
tenbrains objective focus ai-agents-scene
tenbrains objective focus --clear
tenbrains objective link post_... --objective stablecoins
tenbrains objective unlink post_... --objective stablecoins
tenbrains objective archive stablecoins
tenbrains objective refresh ai-agents-scene
tenbrains objective mode ai-agents-scene learn

Tag at creation time with a repeatable --objective flag:

tenbrains analyze --provider mock --text "..." \
  --objective stablecoins --objective ai-agents
tenbrains takeaway follow levelsio --objective ai-agents
tenbrains bookmark add --post-id post_... --objective stablecoins
tenbrains learn generate --analysis ana_... --objective stablecoins
tenbrains search "reserve risk" --objective stablecoins
tenbrains digest --objective stablecoins

bookmark add tags the bookmark's post. Without an explicit --objective, learn generate inherits the objective tags on its source analysis' post; supplying the flag overrides that inheritance. Every referenced objective must already exist or the command returns NOT_FOUND without silently creating one. Focus never tags records automatically. objective show groups tagged posts, accounts, bookmarks, and tracks; record get includes an objectives array for linkable records.

Mode is a lens on future behavior only: it never changes explicit tags or current-focus behavior. objective refresh <slug> [--force] [--limit N] works only for a follow objective (otherwise it returns CONFLICT). It gathers its directly tagged analyzed posts plus analyzed posts authored by its tagged accounts, ranks each post from local data only: 3·bookmarked + 2·min(authorTakeaways,5) + min(authorPostCount,10), then creates one 10-minute/day track from the pooled concepts. There is no network call or scheduler. Fewer than three new posts returns a successful data.track: null result unless --force is supplied; --limit caps the selected pool. Every auto-track stores its consumed post ids in learning_tracks.source_post_ids_json (the analysisId is its top-weighted source), so later refreshes are idempotent.

When a selected or inherited objective has a description, learning tracks rank concepts first by deterministic token overlap with that description, then by the existing interest and familiarity ratings. With multiple described objectives, a concept uses its highest overlap with any one description; matches are not summed across objectives. objective show also returns descriptive progress counts under data.progress: accounts followed, posts and transcripts analyzed, bookmarks saved, tracks completed, learning days completed/total, quiz questions answered, and quiz answers correct. It never fabricates a completion percentage.

suggest generate adds deterministic token-overlap relevance from the current focus description to its existing saved-interest scores. With no focus or no usable focus description, its scores, reasons, and ordering are unchanged. This bias never creates objective links. search and digest accept one --objective <slug> filter and return only analyses whose posts, takeaways whose accounts, and bookmarks (directly or through their posts) are tagged to that objective. Unknown objectives return NOT_FOUND.

Personalized lessons and quizzes

When a newly built track has at least one learn-mode objective, tenbrains makes one batched provider call to create a grounded lesson and three to five answer-reference questions for every track day. The prompt uses the source post and analysis plus up to ten other analyzed posts tagged to those objectives. The mock provider uses the same deterministic keyword-based path for offline work and tests.

tenbrains learn generate --analysis ana_... --objective stablecoins --provider mock
tenbrains analyze --provider mock --text "..." --objective stablecoins --learn
tenbrains learn generate --analysis ana_... --objective stablecoins --no-materialize
tenbrains learn materialize trk_... --provider mock        # later generation or retry
tenbrains learn materialize trk_... --day 3 --provider mock # replace one day
tenbrains learn today trk_...                               # lesson + quiz count, not answers
tenbrains learn quiz trk_...                                 # questions + latest status, no expected answers
tenbrains learn answer trk_... --q 1 --answer "..." --provider mock

--no-materialize leaves the normal track intact. A provider failure, including invalid generated JSON, also leaves the track intact; retry with learn materialize. That command works for any track, including an auto-generated follow track. learn quiz defaults to the next pending day (or accepts --day N) and returns only questions plus the latest attempt status. learn answer accepts inline text, @path, or -, grades one answer, and appends its attempt; retrying is allowed and the latest attempt is the standing result. Quizzes never gate learn done. The mock grader is fully offline: it marks an answer correct when it overlaps at least half of the expected answer's unique content tokens (rounded up, minimum one). Tracks return their persisted material and latest quizAttempts alongside progress on learn show and record get.

YouTube transcripts

Pass a public YouTube watch, youtu.be, Shorts, or embed URL to analyze. tenbrains selects a caption track without an API key (manual before auto-generated; --lang, then English, then the first available), stores the transcript and video metadata with the post, and runs the normal analysis pipeline:

tenbrains analyze --url "https://youtu.be/M7lc1UVf-VE" --lang en
tenbrains analyze --url "https://youtu.be/M7lc1UVf-VE" --summarize --learn
tenbrains analyze --url "https://youtu.be/M7lc1UVf-VE" --transcript @captions.txt

--summarize returns { summary, keyPoints[] } under data.summary, persists it in the post's raw metadata, and uses that digest as the condensed input for concept extraction. It composes with --learn. If a video is unavailable or has no captions, supply an existing transcript with --transcript <text|@file|->. v1 is caption-only: it does not download audio or invoke Whisper. YouTube's WEB caption URLs can return empty bodies, so the client retries through the embedded Android player API; that undocumented client version is the primary maintenance surface.

Configuration & credentials

There is no .env. The CLI owns credential collection:

  • tenbrains setup collects a key interactively (TTY) or via --api-key / --api-key - (stdin).
  • tenbrains config set providers.openai.apiKey sk-... sets any value directly.
  • --api-key / --provider / --model override per invocation.

Values are written to a managed JSON file (tenbrains config path shows where) with 0600 permissions. Secrets are redacted in config get/config list unless --reveal is passed. Resolution precedence: CLI flag → config store. Environment variables are intentionally not consulted, keeping the credential source explicit and auditable.

Fetching from X

By default the agent supplies content via --text, but the CLI can also pull tweets itself — designed free-first:

  • Single tweets (analyze) use X's public oEmbed endpoint by default: no API key, no paid tier. tenbrains analyze --url "https://x.com/user/status/123" fetches the tweet text + author and analyzes it. Control this with --fetch auto|oembed|api (default auto).
  • Account timelines (takeaway) have no free path, so they use the official X API v2 with a Bearer token: tenbrains takeaway refresh <user> --count 20 (omit --posts to fetch). Most accounts need a paid X API tier (Basic+) to read timelines.
  • Threads (analyze --thread) are analyzed as one document. Supply the parts yourself for free (--thread '["part 1", "part 2"]', @file, or -), or pass bare --thread with --url/--id to fetch the author's self-thread via the API (Bearer token; recent search covers ~7 days).
  • Your own history (import x-archive) needs no API at all: request your account archive at X → Settings → "Download an archive of your data", extract the zip, and run tenbrains import x-archive <dir>. Likes land as bookmarked posts (instant signal for suggest generate), your tweets as posts. Re-running dedupes on tweet id.

Store the token (used for timelines and as the --fetch api fallback) during setup or config:

tenbrains setup --provider anthropic --api-key sk-ant-... --x-bearer "AAAA..."   # both at once
tenbrains config set x.bearerToken "AAAA..."                                     # or just the X token
tenbrains analyze --url https://x.com/jack/status/20 --x-bearer -  < token.txt   # or per-call (stdin)

If your tier can't read a tweet/timeline, the CLI returns a structured PROVIDER_UNAUTHORIZED / PROVIDER_RATE_LIMITED error (exit 5) rather than crashing — fall back to --text / --posts.

Database

A single SQLite file (default ~/.local/share/tenbrains/tenbrains.db, override with --db). Schema is versioned and migrated automatically on open. Tables: posts, analyses, accounts, takeaway_snapshots, bookmarks, suggestions, learning_tracks, track_progress, track_materials, quiz_attempts, objectives, and objective_links, plus a trigger-maintained FTS5 index (search_fts) behind search — rebuild it anytime with tenbrains db reindex. Follow-track consumption lives in learning_tracks.source_post_ids_json; objective mode lives in objectives.mode; schema v6 adds track_materials; schema v7 adds append-only quiz_attempts.

tenbrains --db ./research.db analyze --provider mock --text "..."   # isolate a workspace
tenbrains db stats

Providers

Default is Anthropic Claude; openai, google, and xai are supported via --provider, and mock runs offline. See tenbrains manifest for the live catalog and default models.

Use as a Claude skill

This repo ships an Agent Skill in skill/ so Claude (Claude Code, claude.ai, or the Agent SDK) can drive the CLI on your behalf — analyze a post, summarize an account, recall saved research — using the JSON contract above. The skill is thin: it points Claude at the CLI and at tenbrains manifest for live discovery.

Install it for your own Claude Code (make the tenbrains command available first, then copy the skill in):

npm link                                   # puts `tenbrains` on PATH
cp -r skill ~/.claude/skills/tenbrains     # personal skill, available in every project
# or, project-scoped:  cp -r skill .claude/skills/tenbrains

After installing, ask Claude something like "analyze this X post: …" and it will use the skill.

Development

npm run typecheck   # tsc --noEmit
npm run lint        # biome
npm test            # node:test via tsx
npm run check       # all three

License

MIT.