A smart recipe scraper for the terminal. It turns a recipe URL into a clean cooking brief with ingredients, timings, steps, and source metadata inside a viewport-filling terminal UI.
npm install -g @sambitcreate/parsely-clibrew tap sambitcreate/tap
brew install parselyparsely
parsely https://www.simplyrecipes.com/recipes/perfect_guacamole/
parsely --help
parsely --versionFor AI fallback (optional but recommended), set the OpenAI API key in your shell:
export OPENAI_API_KEY="your_key_here"
parsely https://example.com/recipeWithout a key, browser scraping still works for most recipe sites.
Parsely never persists the API key — it reads OPENAI_API_KEY from the environment at startup. Prefer injecting it at invocation time from a secret manager so the key never lands on disk in a flat file:
# 1Password CLI
OPENAI_API_KEY=$(op read "op://Personal/OpenAI/api_key") parsely <url>
# pass
OPENAI_API_KEY=$(pass show openai/api_key) parsely <url>
# macOS Keychain
OPENAI_API_KEY=$(security find-generic-password -a "$USER" -s openai_api_key -w) parsely <url>For quick local development Parsely will read .env.local if it exists. This file is .gitignored, but it is still exposed to any process with filesystem access and to backup/sync tools (Time Machine, iCloud Drive, Dropbox). If you must use it:
- Treat the key as already-leaked — rotate it regularly at platform.openai.com/api-keys.
- Consider installing a secret scanner like
gitleaksordetect-secretsas a pre-commit hook so.env*files can never be staged. - Delete the file when you're done with a development session.
Optional terminal tuning:
export PARSELY_SYNC_OUTPUT=1 # force synchronized output on
export PARSELY_SYNC_OUTPUT=0 # force synchronized output off
export PARSELY_DISPLAY_PALETTE=1 # force terminal background palette on
export PARSELY_DISPLAY_PALETTE=0 # force terminal background palette off
export PARSELY_THEME=dark # start in dark theme
export PARSELY_THEME=light # start in light theme- Uses dedicated full-screen Ink views for the idle, scraping, display, and error phases instead of a general shell layout
src/cli-runtime.tsswitches into the terminal alternate screen before Ink mounts and restores the previous screen on exit- Restores the terminal's default background color on exit after using the app palette
- Reserves one terminal row so Ink can update incrementally instead of clearing the whole screen on every spinner tick
- Adapts the layout to the current terminal size for wide and narrow viewports
- Collapses non-essential panels on shorter terminals so the URL field stays usable
- Cancels in-flight browser and AI scraping when you press
Ctrl+C - Shows a live scraping pipeline so browser parsing and AI fallback are visible as separate stages
- Detects light/dark preference on startup and applies a matching app-wide theme
- Lets you toggle the full app theme at runtime with
Ctrl+T - Enables synchronized output in Ghostty, WezTerm, and Kitty-compatible terminals by default so frame updates paint atomically
- Applies the terminal background palette from the app root when the current terminal is in the supported compatibility set
- Avoids advanced palette/sync behavior in
tmux,screen, VS Code's integrated terminal, JetBrains terminals, the Linux console, andTERM=dumbunless you explicitly override it
| Key | When active | Action |
|---|---|---|
Enter |
Idle, error | Submit the current URL |
Ctrl+T |
Idle, scraping, display, error | Toggle theme |
n |
Display | Start a new scrape from the idle screen |
q |
Display | Quit |
Esc |
Display | Quit |
Ctrl+C |
Any phase | Abort any active scrape and exit |
Error: OpenAI API key not found— SetOPENAI_API_KEYenvironment variable- Browser scraping skipped — Install Chrome or Chromium for better results
- No recipe found — AI fallback handles most sites, but results vary by site
- Terminal looks cleared while running — Expected; Parsely uses the alternate screen and restores your previous terminal content when it exits
- Background color does not change — Your terminal may be outside the built-in compatibility list or behind a multiplexer. Use
PARSELY_DISPLAY_PALETTE=1to force palette updates on, orPARSELY_DISPLAY_PALETTE=0to disable them entirely - Theme starts in the wrong mode — Set
PARSELY_THEME=darkorPARSELY_THEME=lightto override automatic detection - Ghostty or WezTerm still flickers — Parsely enables synchronized output automatically there; set
PARSELY_SYNC_OUTPUT=1to force it on elsewhere orPARSELY_SYNC_OUTPUT=0to disable it - Some sites challenge headless browsers — Parsely now uses a more browser-like Puppeteer setup, but challenge pages can still force an AI fallback
MIT — see LICENSE.
parsely-cli/
├── src/
│ ├── cli.tsx # Thin CLI entrypoint: args/help/version
│ ├── cli-runtime.ts # Terminal runtime: alt-screen, stdout wrapping, cleanup
│ ├── app.tsx # Root component — phase screens + state machine
│ ├── theme.ts # Color palette
│ ├── components/ # UI components
│ ├── hooks/ # Terminal viewport and screen management
│ ├── services/scraper.ts # Puppeteer + OpenAI
│ └── utils/ # Input, URL, and terminal helpers
├── test/ # Runtime, PTY, shortcuts, text layout, helpers, and scraper tests
├── package.json
├── tsconfig.json
└── CLAUDE.md # AI assistant context
git clone https://github.com/sambitcreate/parsely-cli.git
cd parsely-cli
npm install
npm run dev
npm test- Browser Scraping — Headless Chrome loads the page and extracts Schema.org JSON-LD recipe data
- Parsing Stage — Parsely scans and normalizes recipe schema before deciding whether the page is usable
- AI Fallback — OpenAI
gpt-4o-miniextracts data only when browser parsing cannot recover a recipe - Phase Transitions —
src/app.tsxmovesidle -> scraping -> display, oridle/scraping -> error -> scrapingwhen the retry form submits another URL - Display — The result is plated into a responsive terminal recipe deck with pipeline, prep, and method panels
LandingScreen— centered logo and input for the idle stateLoadingScreen— minimal centered status view during scrapingBanner— status-aware header with current host and app statePanel— shared bordered container used across the error shellPhaseRail— pipeline view for browser, parsing, and AI stagesURLInput— normalizes pasted newlines and exposes phase-appropriate shortcut hints under the fieldRecipeCard— split recipe layout with summary, ingredients, timing, and methodFooter— persistent status line and key hints on non-landing screenscli-runtime.ts— owns alternate-screen entry/exit, synchronized stdout proxying, and terminal cleanup outside the React treeuseTerminalViewport— terminal sizing and resize trackingutils/terminal.ts— terminal compatibility detection, synchronized-output, palette control, and render-height helpers
npm testThe test suite covers runtime cleanup in test/cli-runtime.test.ts, PTY alt-screen behavior in test/cli-pty.test.ts, shortcut handling in test/shortcuts.test.ts, text wrapping in test/text-layout.test.ts, plus input normalization, schema extraction, theme-mode helpers, and terminal compatibility detection.
npm run build
npm publish --access public