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

question-cli

v0.1.0

Published

Blocking Discord question primitive for coding agents: post a multiple-choice question, let humans vote and discuss, get the owner's decision back as JSON on stdout.

Readme

question-cli

A blocking question primitive for coding agents. Your agent hits a decision it shouldn't make alone — it shells out to question-cli, which posts a multiple-choice question to a Discord channel. Humans vote, discuss in an auto-created thread, and a designated owner makes the call. The command blocks until then, and the agent gets the decision — votes, discussion and all — as JSON on stdout.

No webhooks, no server, no state — one process per question.

Install

Install the agent skill (Claude Code, Cursor, Codex, and 70+ other agents):

npx skills add hugo-clemente/question-cli

Or manually — clone the repo and copy the skill into your project:

git clone https://github.com/hugo-clemente/question-cli
cp -r question-cli/skills/asking-the-team your-project/.claude/skills/

The CLI itself needs no install — the skill invokes it with npx question-cli.

Discord bot setup (once)

  1. Create an app at https://discord.com/developers/applications, add a bot, copy its token.
  2. Invite it with scope bot and permissions: View Channel, Send Messages, Embed Links, Read Message History, Create Public Threads, Send Messages in Threads.
  3. No privileged intents required.
  4. Drop the token in a .env file at your project root — the CLI picks it up automatically:
echo 'DISCORD_BOT_TOKEN=your-token' >> .env   # make sure .env is gitignored

(An exported DISCORD_BOT_TOKEN or --token flag also works.)

Usage

Non-interactive — what an agent runs (all flags required when stdin isn't a TTY):

npx question-cli ask \
  --token "$DISCORD_BOT_TOKEN" \
  --channel 123456789012345678 \
  --owner 234567890123456789 \
  --question "Zero-commitment investor — what should the digest do?" \
  --option "Skip them|no row" \
  --option "Include with €0 HT|keeps the list complete" \
  --select single \
  --deadline 2h

Interactive — run npx question-cli ask in a terminal and clack prompts fill in whatever flags you omitted.

| Flag | Meaning | | ------------------------ | ------------------------------------------------------- | | --channel <id> | target Discord channel ID | | --owner <userId> | the human who can make the final decision | | --question <text> | the question (becomes the poll embed) | | --title <text> | discussion-thread name (default: question's first line) | | --option "Label\|desc" | repeatable, 2–25; description optional | | --select single\|multi | ballot type (default single) | | --deadline <dur> | 90m, 2h, 1d — 1 minute to 7 days (default 24h) | | --out <file> | also write the result JSON to a file | | --token <token> | Discord bot token (falls back to DISCORD_BOT_TOKEN) |

Output contract

stdout carries exactly one thing: the result JSON (one line, on resolution). Everything else — prompts, errors, diagnostics — goes to stderr.

{
  "status": "decided",
  "decision": "B",
  "decidedBy": "234567890123456789",
  "tally": { "A": ["345678901234567890"], "B": ["234567890123456789"] },
  "startedAt": "2026-07-15T09:00:12.000Z",
  "resolvedAt": "2026-07-15T09:41:23.000Z",
  "messageId": "1393112233445566778",
  "channelId": "123456789012345678",
  "threadId": "1393112233445566779",
  "discussion": [
    {
      "userId": "345678901234567890",
      "text": "B, but only if we keep the CSV export",
      "at": "2026-07-15T09:12:41.000Z"
    }
  ],
  "users": {
    "234567890123456789": { "username": "klo", "displayName": "Klo" },
    "345678901234567890": { "username": "sam", "displayName": "Sam" }
  }
}
  • status"decided" (owner picked) or "expired" (deadline hit; never auto-picks a winner).
  • decision — the winning option key ("A", "B", …) or null when expired.
  • tally — option key → array of voter user IDs.
  • discussion — every human message from the poll's thread, oldest first. Often carries nuance the vote doesn't.
  • users — user ID → { username, displayName } for everyone who voted, decided, or posted.

On resolution the Discord poll message is replaced with a clean conclusion and the discussion thread is archived.

Exit codes

| Exit | stdout | Meaning | | ---- | ----------- | -------------------------------------------------------------------------------------------------------------------------------- | | 0 | result JSON | resolved (decided or expired) | | 1 | result JSON | resolved, but the --out file write failed | | 1 | empty | error before resolution: bad flags, missing token, missing permissions, or interrupted (SIGINT/SIGTERM edits the poll to say so) |

Rule for agents: parse stdout first. Valid JSON on stdout means the question was resolved, regardless of exit code.

Teach your agent

Paste into your CLAUDE.md / AGENTS.md (fill in your channel and owner IDs):

## Asking the team

When you hit a product decision, naming choice, or tradeoff you shouldn't make alone,
ask the team on Discord. The CLI finds the bot token itself (project .env file or
DISCORD_BOT_TOKEN env var) — never ask for or handle the token value directly:

    npx question-cli ask --channel <CHANNEL_ID> --owner <OWNER_ID> \
      --question "<the question, with enough context to answer it>" \
      --option "First choice|one-line implication" \
      --option "Second choice|one-line implication" \
      --deadline 2h

- The command BLOCKS until the owner decides or the deadline passes (up to the
  deadline you set). Run it in the background and continue other work while waiting.
- Parse stdout as JSON: `decision` holds the chosen option key; `tally` who voted
  for what; `discussion` the thread conversation — read it, it often contains
  reasoning or a better third option.
- `"status": "expired"` means nobody decided. Do NOT treat it as approval; pick the
  safest path or ask again with a longer deadline.

The installed skill covers the same ground — use one or the other.

Development

pnpm install
pnpm start ask        # run from source
pnpm test             # 38 tests, node:test
pnpm check            # format + lint + typecheck (Vite+)
pnpm build            # bundle to dist/cli.mjs (vp pack)

License

MIT