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

forkable-cli

v0.11.0

Published

Unofficial command-line client for Forkable lunch ordering. Built for humans and their agents.

Readme

forkable-cli

An unofficial command-line client for Forkable lunch ordering. Forkable has no public API - this CLI drives the same private GraphQL API the Member Console web app uses. Built to be usable both by humans and by AI agents (every command supports --json).

⚠️ Unofficial and unsupported. It talks to an internal API that can change without notice. Use responsibly and only with your own account.

New here? See QUICKSTART.md for the 4-step setup, or install the Claude Code skill to order lunch by just asking your agent.

Install

npm install -g forkable-cli
forkable init      # installs the Claude Code skill + prints setup steps
forkable login     # your own Forkable account (run in a real terminal)

Requires Node ≥ 20 (uses built-in fetch + cookie handling). After forkable init, restart Claude Code so it loads the skill, then just talk to it (see QUICKSTART.md).

Working from a clone instead: npm install, then node bin/forkable.js <command>.

Quick start

forkable login                 # prompts for email + password
forkable week --next           # see next week's scheduled meals
forkable menu <deliveryId>      # see ranked options for a day
forkable restaurants --next     # see which restaurants are on for the week
forkable choose <deliveryId> --best   # auto-pick your top match
forkable auto --next            # fill the whole week by preference
forkable cancel <deliveryId>    # out of office? cancel a day's meal entirely

Authentication

Login establishes a session cookie that's saved to ~/.config/forkable/session.json (mode 600). Credentials can be supplied three ways:

  • Interactive prompt: forkable login
  • Env vars: FORKABLE_EMAIL and FORKABLE_PASSWORD (preferred for agents/CI)
  • Flags exist too, but a --password on the command line is visible to ps and lands in shell history - use the prompt or the env vars instead

MFA: forkable login --mfa 123456 if your account requires it.

Each user/agent can keep an isolated session by setting FORKABLE_CONFIG_DIR:

FORKABLE_CONFIG_DIR=~/.forkable-alice forkable whoami

Commands

| Command | What it does | |---|---| | init | Install the Claude Code skill + print setup steps | | login | Authenticate and save the session | | logout | Clear the saved session | | whoami | Show the current user + settings | | week (alias upcoming) | Scheduled meals for a week (--next, --week YYYY-MM-DD) | | menu <deliveryId> | Ranked menu options for a delivery (--all for full list) | | restaurants | The week's restaurant lineup per day (--next); --search <name> searches Forkable's whole venue directory; --menu <menuId> shows one venue's full menu | | choose <deliveryId> | Pick a meal: --best, or --item <id> --menu <id>. Add-ons via --select '{"<modifierId>":[<optionId>]}'; --dry-run to preview the priced configuration | | auto | Auto-pick the best match for every changeable day (--next, --dry-run) | | cancel <deliveryId> | Cancel a day's meal so nothing gets delivered (--dry-run to preview); revert restores it | | prefs show / prefs set <field> <value> | View / edit ordering preferences | | log <json> / decisions | Append to / read the learning log (suggested → recommended → chosen) |

Add --json before the command for machine-readable output, e.g. forkable --json week --next. Errors also print as JSON in that mode, and the process exits non-zero on failure - so agents can branch on both the exit code and ok:false.

Preferences

Stored at ~/.config/forkable/preferences.json. The ranking blends Forkable's own per-item recommendation score with your local signals:

forkable prefs set likes "chicken,salmon,bowl"
forkable prefs set dislikes "tofu,mushroom"
forkable prefs set avoid "peanut,shellfish"     # hard blocks - keyword match, see below
forkable prefs set venueLikes "Thai Villa"       # like or dislike whole restaurants by name
forkable prefs set venueDislikes "Kati Shop"     # (the "why" goes in a note: forkable prefs add-note)
forkable prefs set diet pescatarian             # omnivore|pescatarian|vegetarian|vegan|none
forkable prefs set maxPrice 18                   # skip anything pricier (none to disable)
forkable prefs set maxTotal 30                    # never place an order over this (none to disable)
forkable prefs set forkableScoreWeight 0.6       # 0..1: trust in Forkable's score vs. your keywords

maxPrice and maxTotal are different guards and both are useful:

| | maxPrice | maxTotal | |---|---|---| | Checks | an item's base price | the real total, base + add-on surcharges | | When | while ranking candidates | at order time | | Effect | the item stops being eligible | the order is refused |

maxPrice shapes what gets suggested. maxTotal is a hard ceiling that stops a bad order landing, which is what makes it safe for an unattended agent to act. A $27 item with a $4 required side is a $31 order, and only maxTotal sees that. Override for one run with --max-total <n>, or --max-total none to ignore the saved ceiling. A numeric 0 is a real ceiling of zero dollars, so "spend nothing" means what it says - only the word none disarms the guard. The ceiling checks the menu total, not your out-of-pocket cost after any club allowance or copay.

Two honest limits on avoid. It is a case-insensitive keyword match over the item's name, description and ingredient tags - "peanut" catches "peanut sauce" but a dish described only as "satay" slips through, so it reduces risk rather than guaranteeing safety. Forkable's own account-level restrictions still apply server-side, and the CLI checks them before ordering (the mealRestrictions query). When that check itself fails and you have an avoid list or Forkable-side restrictions set, the order is refused rather than placed blind.

Decisions you made yourself are protected

Once a decision is logged as human-approved for a day, choose refuses to replace that day's meal and auto skips it:

$ forkable choose 1236411 --item 52 --menu 16642
error: You already approved "Chicken Shawarma Plate" for 2026-08-12. Refusing to replace it.
       Pass --override-approved if you really mean to change it.

This exists for unattended runs. An agent reads your preferences but can't tell a meal Forkable guessed from one you chose, since they look identical in the API. Changed your mind? Pass --override-approved, or log a newer approved decision for that day.

Decisions default to mode: "approved". An unattended run should log mode: "auto", which carries no protection, so a scheduled job can't lock itself out. Query with forkable decisions --mode approved --week 2026-08-10.

Cancelling a day

Out of office, or just don't want lunch that day? cancel empties the slot so nothing gets delivered and nothing is charged:

forkable cancel 1236409 --dry-run    # preview what would be cancelled
forkable cancel 1236409              # remove the day's meal

The removed meal is recorded to the undo log first, so forkable revert puts it back exactly. One caveat for scheduled agents: a cancelled day looks identical to a never-filled day, so auto will happily fill it again. Log the cancellation as a human-approved decision for that day (forkable log '{"day":"...","chose":"cancelled",...}') and auto will skip it.

Undoing a swap or a cancel

Any order that replaces an existing meal - and any cancel - records what it displaced, so it can be put back exactly: same item, same add-ons, same special instructions:

forkable undo-log                    # swaps and cancels that can still be reverted
forkable revert 1236409              # put one day back
forkable revert 1236409 --dry-run    # preview it first
forkable revert --next               # revert every day next week that has a record

Only works while the day is still changeable. Forkable locks a day before delivery and exposes no cutoff timestamp, so revert early rather than counting on a deadline.

Scoring model (see src/prefs.js): score = w·forkableScore + (1−w)·(keywordScore + venueScore + 0.5·rating), where items failing your diet / avoid / price constraints are marked ineligible and never auto-selected.

Agent usage example

export FORKABLE_EMAIL=... FORKABLE_PASSWORD=...
forkable login --json >/dev/null
# Order the whole of next week according to saved prefs, capturing the plan:
forkable --json auto --next | jq '.results'

How it works

See notes/api-spec.md for the full reverse-engineered API contract. In short:

  • Auth is a cookie-based session (_easyorder_session, HttpOnly) plus a CSRF token from GET /api/v2/csrf_token sent back as X-CSRF-Token, with a Forkable-Referrer: mc header.
  • Reads/writes go to POST /api/v2/graphql. Key operations: me, myDeliveries(from:), menus(ids, clubId), mealGenerationScores(...), and three slot mutations: replacePiece (swap a scheduled meal), addPiece (fill an empty slot) and removePiece (cancel). Login is the createSession mutation.

Layout

bin/forkable.js     CLI entry (commander)
src/client.js       API client: CSRF, login, cookie jar, GraphQL, mutations
src/queries.js      GraphQL operation strings
src/prefs.js        preference scoring + modifier-selection builder
src/util.js         dates, formatting, menu flattening, change-eligibility
src/config.js       session + preferences persistence
src/prompt.js       interactive (hidden) prompts
notes/api-spec.md   reverse-engineered API documentation

Troubleshooting

  • Not authenticated - run forkable login again; sessions expire.
  • Login fails but credentials are right - the account may use SSO (no password login), or require MFA (--mfa). The CLI auto-retries login against the public GraphQL endpoint.
  • This delivery can no longer be changed - you're past the ordering cutoff for that day.