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

innernote

v0.3.4

Published

Write LinkedIn posts in your voice, from the terminal.

Readme

innernote CLI

Write LinkedIn posts in your voice, from the terminal.

innernote capture "the thing you just thought of"
innernote write "why most onboarding asks for too much upfront" --save
innernote week

Install

Needs Node 20 or newer. Nothing else.

npx innernote

Run it without installing anything, or put it on your PATH for good:

npm install -g innernote

Then connect the machine to your account:

innernote login

It asks for a pairing code, which you get from Settings. The key it saves lasts 90 days and can be revoked from the same page.

Working on it

The source is TypeScript and runs on Bun:

bun run src/index.ts

bun run build produces the published bundle in dist/, which is what npm ships and what bun test exercises in dist.test.ts. The published file runs on node; the source does not.

The session

Typing innernote on its own opens a session you stay in, the way claude or psql do. Your week, then a prompt:

  ▍ week
  ▍ write "hiring your first engineer" --save
  ▍ capture the thing that just came up
  ▍ exit

Up arrow for history, tab to complete a command, help for what there is. Ctrl-C once cancels what you are typing; twice leaves.

Piped or redirected it prints your week and exits instead, so it never hangs a script waiting for input nobody is going to type.

Connect

innernote login

It prints a URL. Open it in a browser where you are already signed in, copy the eight-character pairing code, and paste it back. The code works once and expires in ten minutes.

The token it gives you is stored at ~/.innernote/config.json, owner-read-only. It is opaque, it is not your password, and you can revoke it from Settings at any time.

Commands

The table below is asserted by cli/src/parity.test.ts: a command missing from it fails the suite, so what you read here is what the binary has.

| Command | What it does | | --- | --- | | login [code] | connect this machine | | logout | forget the token stored here | | whoami | show which account is connected, and what this key can do | | context | what you post about: your pillars and your series | | capture <text> | save an idea to your inbox | | write [topic] | draft a post in your voice | | shape <shape...> | push a post shorter, punchier, warmer | | ask <anything> | talk to the post in your own words | | save | keep the draft you are holding | | show | reprint the post you are holding | | drop | put down whatever you are holding | | open <n or id> | pull a post out of the last list and work on it | | ideas [status] | list what you have captured | | drafts [status] | list your posts, numbered | | week | what is scheduled for the next seven days | | queue <post-id> | schedule a post into your next open slot | | unqueue <post-id> | pull it back out before it goes live | | skill [--install] | print the agent skill, or register it for Claude Code |

write

innernote write                              # picks the pillar you have covered least
innernote write "hiring your first engineer"
innernote write --thought "$(cat notes/monday.md)"
innernote write "shipping fast" --format story --save
cat brief.md | innernote write

Without --save the post is printed and nothing is stored, so you can pipe it:

innernote write "onboarding" | pbcopy

The draft comes from the same generation the app runs: same voice, same craft rules, same check against what you have already published. It is not a lighter version.

shape

The same twelve moves the app's editor has. It reads the post from stdin, so it composes:

pbpaste | innernote shape shorter punchier | pbcopy
innernote write "onboarding" --json | jq -r .content | innernote shape deslop

Shapes: hook example specific punchier shorter ending warmer deslop mobile question takeaway jargon

The names are the server's. The CLI never writes the instruction itself, so "shorter" means the same thing here as it does in the editor and inside a chat.

queue

innernote queue p17a1yk936ywnan10643efjtc58dhkah

A queued post goes live on its own at your next cadence slot. Nobody presses anything again. So it needs a connection you explicitly allowed to publish: tick "Let it queue posts" in Settings when you generate the pairing code. Without it you get a clear refusal, not a mystery.

innernote inside a chat can queue too, if you tick the same box for that connection. It can only queue a post you already saved and read, and your chat client asks you before it runs. Publish is in nobody's default grant, so every connection that can schedule was one you deliberately allowed.

Piping

Every command takes --json and prints the API's own payload, nothing else. Human output and errors go to stderr, so stdout stays clean:

innernote ideas --json | jq -r '.[].content'
innernote week --json | jq 'length'
pbpaste | innernote capture

Exit codes

| Code | Meaning | | --- | --- | | 0 | it worked | | 1 | it failed; the reason is on stderr | | 2 | not connected, run innernote login | | 3 | your account cannot do this right now |

Code 3 is separate on purpose. A scheduled job should be able to tell "innernote is broken" from "your trial ended", because only one of those is worth waking someone up for.

Environment

| Variable | Effect | | --- | --- | | INNERNOTE_TOKEN | use this token, ignore the stored one | | INNERNOTE_API_URL | talk to another host, for development | | NO_COLOR | plain output |

Both token variables override the config file and are never written back, which is what makes the CLI usable in CI without a second config format.

How it works

The CLI is a thin client over the same HTTP API the web app uses. It holds no database credentials and implements no product logic. Everything it can do is something the API already lets your account do, so entitlement, limits and voice all behave exactly as they do in the app, because the work happens server-side in one place.

The writer, here

  ▍ drafts
  ▍ open 3
  ▍ ask cut the second half and land it harder
  ▍ save

shape covers the twelve named moves. ask covers everything else, in your own words: it is the same assistant the app's composer runs, so it can change the post, answer a question about it, or say it would rather not.

show reprints whatever you are holding, and the prompt says whether it is saved, so you always know where you are. drop puts it down.

Checking the whole flow

bun run cli/e2e.ts

Runs the real user journey against a real server: connect, read pillars and series, capture an idea, draft a post, reshape it, save it, then delete everything it made. It reports what passed and cleans up after itself.

It never publishes. There is no queue step and there will not be one. A test that can publish eventually will, on a real account, to real people. Queueing is verified by asserting the refusal on a key that was not granted publish, which proves the gate without touching it.

Point it anywhere:

INNERNOTE_API_URL=http://localhost:3000 bun run cli/e2e.ts

Development

bun run src/index.ts --help
bun test
bun run typecheck

Point it at a local server:

INNERNOTE_API_URL=http://localhost:3000 bun run src/index.ts whoami

This package is deliberately outside the app's workspace and has no runtime dependencies, so it cannot affect the web build.