
Pp Granola
- 161 installs
- 1.9k repo stars
- Updated August 4, 2026
- mvanhorn/printing-press-library
Capture Granola meeting notes, summarize calls, extract action items, and route follow-ups into product, sales, and support workflows.
About
pp-granola focuses on Granola meeting workflows: agendas, concise summaries, extracted action items, and handoffs into PM and support processes for distributed startup teams.
- Meeting summaries
- Action items
- Call transcripts
- Standup notes
- Follow-up routing
Pp Granola by the numbers
- 161 all-time installs (skills.sh)
- +11 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #1,137 of 3,282 Productivity & Planning skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/mvanhorn/printing-press-library --skill pp-granolaAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 161 |
|---|---|
| repo stars | ★ 1.9k |
| Last updated | August 4, 2026 |
| Repository | mvanhorn/printing-press-library ↗ |
What it does
Capture Granola meeting notes, summarize calls, extract action items, and route follow-ups into product, sales, and support workflows.
Files
<!-- GENERATED FILE — DO NOT EDIT. This file is a verbatim mirror of library/productivity/granola/SKILL.md, regenerated post-merge by tools/generate-skills/. Hand-edits here are silently overwritten on the next regen. Edit the library/ source instead. See the repository agent guide, section "Generated artifacts: registry.json, cli-skills/". -->
<!-- // PATCH(skill-doc-auth-rewrite): Auth Setup section rewritten for the encrypted-cache install flow (Keychain prompt on first sync, no API key, D6 read-only refresh). See library/productivity/granola/.printing-press-patches.json patches[6]. -->
Granola — Printing Press CLI
Prerequisites: Install the CLI
This skill drives the granola-pp-cli binary. You must verify the CLI is installed before invoking any command from this skill. If it is missing, install it first:
1. Install via the Printing Press installer. It defaults binaries to $HOME/.local/bin on macOS/Linux and %LOCALAPPDATA%\Programs\PrintingPress\bin on Windows:
npx -y @mvanhorn/printing-press-library install granola --cli-only2. Verify: granola-pp-cli --version 3. Ensure the reported install directory is on $PATH for the agent/runtime that will invoke this skill.
If the npx install fails (no Node, offline, etc.), fall back to a direct Go install (requires Go 1.26.4 or newer):
go install github.com/mvanhorn/printing-press-library/library/productivity/granola/cmd/granola-pp-cli@latestIf --version reports "command not found" after install, the runtime cannot see the binary directory on $PATH. Do not proceed with skill commands until verification succeeds.
When to Use This CLI
Reach for granola-pp-cli when you need to answer cross-meeting questions Granola.ai’s web app and the GUI cannot — attendee timelines, MEMO pipeline state, recipes coverage gaps, calendar overlay, talk-time aggregation. It is the right tool for an agent processing transcripts in a loop, a CSM doing pre-call prep, or a consultant running a weekly retro. Pair the --json default with --select dotted paths to keep agent context lean.
When Not to Use This CLI
Do not activate this CLI for requests that require creating, updating, deleting, publishing, commenting, upvoting, inviting, ordering, sending messages, booking, purchasing, or changing remote state. This printed CLI exposes read-only commands for inspection, export, sync, and analysis.
Platform Notes
warm <id> <query> drives the Granola desktop GUI via AppleScript and is macOS-only. It prints what it would do by default; pass --launch to actually activate the app. On non-macOS hosts the command exits 0 with a "not supported" message. All other commands are cross-platform.
Unique Capabilities
These capabilities aren't available in any other tool for this API.
MEMO pipeline
- `memo run` — Run the preflight → extract pipeline on one meeting or every new meeting since a timestamp, emitting the MEMO three-file artifact and an ndjson run-state ledger.
_Replaces the per-meeting shell loop that drives the MEMO pipeline — one call, one ndjson stream, agent-readable._
granola-pp-cli memo run --since 24h --to ~/Documents/Dev/meeting-transcripts --json- `memo queue` — List every meeting whose transcript is in the cache but whose MEMO triple is not yet on disk.
_Answers the daily question “what’s still un-MEMO’d?” without the user opening Granola at all._
granola-pp-cli memo queue --since 7d --jsonAttendee intelligence
- `attendee timeline` — Every meeting with a given attendee, ordered oldest→newest, with title, date, folder, and recipe-applied flag per row.
_Pre-call prep in one command; surfaces the conversation arc with a single person across months of meetings._
granola-pp-cli attendee timeline alice@example.com --since 60d --json --select id,title,started_at,folder,recipes- `attendee brief` — Pulls the last N meetings with an attendee and stitches together their real cached notes plus real AI panel summaries — no synthesis.
_Eliminates the click-each-meeting copy-paste that account leads do before every external call._
granola-pp-cli attendee brief alice@example.com --last 3 --panel action-items --jsonFolders + recipes
- `folder stream` — ndjson stream of every meeting in a Granola folder (resolved via documentLists + listRules) with notes and a named panel inlined.
_Replaces the weekly retro workflow of opening a folder and copy-pasting each meeting’s summary into a spreadsheet._
granola-pp-cli folder stream client-foo --panel summary --json- `recipes coverage` — Surface meetings that did NOT have a named panel template/recipe applied within a date range.
_Friday retro question “did I run the Discovery recipe on every new-prospect call?” answered in one row per gap._
granola-pp-cli recipes coverage discovery --since 14d --jsonTranscript analytics
- `talktime` — Per-segment-source talk-time for one meeting — microphone (you) vs system (everyone else) in minutes.
_Confidence column lets you grade transcript accuracy; mic vs system split is the input to “am I talking too much” retros._
granola-pp-cli talktime 196037d9 --json- `talktime` — Lifts the per-source talk-time aggregation across N meetings since a date — who-talked-most over time.
_Time-defrag retro input that no per-meeting tool can produce._
granola-pp-cli talktime --by participant --since 7d --jsonCache-native data
- `chat list` — List and dump Granola’s AI chat threads anchored to a meeting (entities.chat_thread + entities.chat_message in the cache).
_Recovers the AI Q&A history a user has accumulated against a meeting — useful when chasing what you asked about an account weeks ago._
granola-pp-cli chat list 196037d9 --json- `calendar overlay` — Left-anti-join meetingsMetadata calendar events with documents.google_calendar_event to find calendared-but-not-recorded meetings.
_Sarah’s Friday retro and Damien’s “what did I miss” sweep both reduce to this row-level diff._
granola-pp-cli calendar overlay --week 2026-05-11 --missed-only --jsonPipeline hygiene
- `duplicates scan` — Hash (title, date-bucket, attendee-email-set) across the cache and a meeting-transcripts repo to surface duplicates at scale.
_Repos accumulate near-duplicate files when meetings are re-extracted; this returns the dupe groups for cleanup._
granola-pp-cli duplicates scan --root ~/Documents/Dev/meeting-transcripts --json- `tiptap extract` — Render documents[id].notes (TipTap JSON: headings, bullet_list, list_item, bold marks, paragraph_break) to canonical markdown instead of falling back to notes_plain.
_The MEMO summary file’s quality is bounded by extractor fidelity; granola.py loses sub-list hierarchy and bold runs._
granola-pp-cli tiptap extract 196037d9 --as markdownCommand Reference
This CLI exposes 35+ commands. The full tree is too long to inline; ask the CLI for the canonical list:
granola-pp-cli --help # top-level commands
granola-pp-cli <command> --help # subcommands + flags
granola-pp-cli agent-context --json # machine-readable command tree for agentsQuick orientation by group:
| Group | Commands | Purpose |
|---|---|---|
| MEMO pipeline | memo run, memo queue, preflight, extract | Composed three-stream pipeline; reads cache + writes MEMO triple |
| Meetings | meetings list, meetings get, meetings fetch-batch, meetings delete, meetings restore, show | List/inspect/mutate meetings (delete/restore mutate via internal API) |
| Streams | notes-show, panel get, transcript get, tiptap extract | The three streams — human notes, AI panels, transcript — addressable separately |
| Export | export, export-all | Combined three-stream markdown export, single or bulk |
| Cross-meeting analytics | attendee timeline, attendee brief, folder stream, recipes coverage, talktime, calendar overlay, stats frequency, stats duration, stats attendees, stats calendar, collect, duplicates scan, chat list, chat get | Queries no per-meeting tool can answer |
| Folders / recipes / workspaces | folders (public-API), folder list, folder stream, recipes list, recipes describe, recipes coverage, workspaces list | Granola organizational entities |
| Public-API mirrors | notes list, notes get, folders | Typed Bearer-key endpoints |
| Sync / system | sync, sync-api, doctor, auth login, auth status, auth set-token, auth logout, which, agent-context, version, import | Local store hydration, auth, capability discovery, batch import |
| GUI bridge | warm (macOS only) | Drives Granola desktop app via AppleScript |
Finding the right command
When you know what you want to do but not which command does it, ask the CLI directly:
granola-pp-cli which "<capability in your own words>"which resolves a natural-language capability query to the best matching command from this CLI's curated feature index. Exit code 0 means at least one match; exit code 2 means no confident match — fall back to --help or use a narrower query.
Recipes
Daily MEMO loop
granola-pp-cli memo run --since 24h --to ~/Documents/Dev/meeting-transcripts --jsonProcess every new meeting since yesterday into the MEMO triple format and yield only the new artifacts.
Pre-call attendee brief
granola-pp-cli attendee brief alice@example.com --last 3 --panel action-items --json --select meetings.title,meetings.started_at,panels.action_itemsPull the last three meetings with Trevin and only the title, date, and action-items panel content per meeting.
Friday retro — missing recipes
granola-pp-cli recipes coverage discovery --since 14d --jsonSurface every new-prospect call in the last fortnight that did not have the Discovery panel applied. Omit the slug to list coverage gaps across every panel template.
Repo-wide duplicate scrub
granola-pp-cli duplicates scan --root ~/Documents/Dev/meeting-transcripts --jsonFind duplicate-meeting clusters across the MEMO output repo for cleanup.
Calendar-overlay missed-meeting sweep
granola-pp-cli calendar overlay --week 2026-05-11 --missed-only --jsonCalendared meetings with no Granola recording — weekly accountability check.
Auth Setup
1. Install Granola desktop and sign in. The CLI reads the local cache and tokens the desktop manages. No CLI-side credentials to configure.
2. Run any command. On macOS, the first invocation that needs the cache or tokens (typically granola-pp-cli sync) triggers a Keychain prompt for Granola Safe Storage. Click "Always Allow" so subsequent runs are silent. The CLI uses Granola's own Keychain-stored encryption key to decrypt cache-v6.json.enc and supabase.json.enc.
3. CLI is read-only against the refresh token. Granola desktop owns rotation; the CLI never calls RefreshAccessToken against the encrypted token store because rotating it there would sign Granola desktop out next time the desktop tries to refresh. If a request fails with "token expired", open Granola desktop briefly to refresh, then re-run the CLI command.
4. Power users / CI: `GRANOLA_WORKOS_TOKEN`. Setting this env var bypasses the Keychain prompt entirely and accepts the refresh-rotation trade-off (rotating the token via the CLI will sign the desktop out). Use only when CLI-side refresh is required, typically for headless agents.
Optional public REST API path: set GRANOLA_API_KEY for the typed notes and folders top-level commands at public-api.granola.ai. Most workflows do not need this.
Run granola-pp-cli doctor to verify setup. The "Encrypted store" line distinguishes four states: not installed, pre-encryption Granola, present-but-not-yet-synced, ok, or last-sync-failed-with-class.
Troubleshooting
doctor says... | What to do |
|---|---|
INFO no Granola install detected | Install Granola desktop from granola.ai and sign in. |
INFO not in use (Granola pre-encryption) | Granola desktop pre-encryption versions wrote plaintext files; the CLI still reads them. Upgrade Granola desktop to pick up the encrypted store. |
INFO present; run sync to authorize Keychain access | Run granola-pp-cli sync. Click "Always Allow" on the macOS prompt. |
OK ok | Last successful sync recorded. Token source and document-fetch count visible in --json output. |
ERROR last sync failed to decrypt (key_unavailable) | Sign back into Granola desktop, re-run sync, accept the Keychain prompt. |
ERROR last sync failed to decrypt (decrypt_failed) | Encryption scheme may have drifted with a Granola update. File an issue with the doctor output. |
Agent Mode
Add --agent to any command. Expands to: --json --compact --no-input --no-color --yes.
- Pipeable — JSON on stdout, errors on stderr
- Filterable —
--selectkeeps a subset of fields. Dotted paths descend into nested structures; arrays traverse element-wise. Critical for keeping context small on verbose APIs:
granola-pp-cli folders --agent --select id,name,status- Previewable —
--dry-runshows the request without sending - Offline-friendly —
syncand themeetings list --query <text>FTS path use the local SQLite store - Non-interactive — never prompts, every input is a flag
- Mostly read-only —
meetings delete,meetings restore,import, andwarm --launchare the only commands that mutate state; every other command inspects, exports, syncs, or analyzes
Response envelope
Commands that read from the local store or the API wrap output in a provenance envelope:
{
"meta": {"source": "live" | "local", "synced_at": "...", "reason": "..."},
"results": <data>
}Parse .results for data and .meta.source to know whether it's live or local. A human-readable N results (live) summary is printed to stderr only when stdout is a terminal — piped/agent consumers get pure JSON on stdout.
Auto-Refresh
Every command auto-refreshes the local store as its first action. You do not need to run granola-pp-cli sync before meetings list, panel get, or any other read — the CLI handles that for you on every invocation.
Two auth surfaces refresh independently:
| Surface | What runs | When it fires |
|---|---|---|
| Desktop encrypted cache | sync (cache → SQLite) | When ~/Library/Application Support/Granola/cache-v6.json.enc (or pre-encryption cache-v6.json) is present |
| Public REST API | sync-api (public-api.granola.ai → SQLite) | When GRANOLA_API_KEY is set or an access token is saved in the config file |
When both are available, both refresh routines fire (cache first, then api). When neither is configured, auto-refresh is a silent no-op and your underlying command produces its own auth error.
Freshness ceiling. Auto-refresh reads from Granola desktop's encrypted cache file; it does not poke the desktop app to refresh from Granola servers. The freshness ceiling is whatever Granola desktop has already pulled. If a meeting just ended and the desktop hasn't synced from servers yet, no CLI-side refresh will surface it. For latest-second-fresh data, open Granola desktop briefly before invoking the CLI.
Provenance line. When stderr is a TTY and you are not in --agent / --json / --compact / --quiet mode, a one-liner like auto-refresh: cache=ok (1.2s, 47 rows) api=ok (820ms, 12 rows) lands on stderr after the refresh. Agent and JSON consumers see no chatter on stdout.
Failures are non-fatal. A refresh that fails prints cache=failed: <short reason> on stderr and the command proceeds against whatever data is already in the store. Run granola-pp-cli doctor to investigate persistent refresh failures.
Opt out (precedence: flag wins over env):
# Single command:
granola-pp-cli meetings list --no-refresh
# For a shell session / CI job:
export GRANOLA_NO_AUTO_REFRESH=1
# Saved per-profile via the existing profile mechanism:
granola-pp-cli profile save fast --no-refresh
granola-pp-cli --profile fast meetings listSkipped commands. Auto-refresh never fires for sync, sync-api, auth*, doctor, help, version, completion, agent-context, profile*, feedback*, or which. These either do not read data or cannot operate before auth is established. agent-context --json exposes the full skip list under auto_refresh.skip_list for introspecting agents.
Agent Feedback
When you (or the agent) notice something off about this CLI, record it:
granola-pp-cli feedback "the --since flag is inclusive but docs say exclusive"
granola-pp-cli feedback --stdin < notes.txt
granola-pp-cli feedback list --json --limit 10Entries are stored locally at ~/.granola-pp-cli/feedback.jsonl. They are never POSTed unless GRANOLA_FEEDBACK_ENDPOINT is set AND either --send is passed or GRANOLA_FEEDBACK_AUTO_SEND=true. Default behavior is local-only.
Write what surprised you, not a bug report. Short, specific, one line: that is the part that compounds.
Output Delivery
Every command accepts --deliver <sink>. The output goes to the named sink in addition to (or instead of) stdout, so agents can route command results without hand-piping. Three sinks are supported:
| Sink | Effect |
|---|---|
stdout | Default; write to stdout only |
file:<path> | Atomically write output to <path> (tmp + rename) |
webhook:<url> | POST the output body to the URL (application/json or application/x-ndjson when --compact) |
Unknown schemes are refused with a structured error naming the supported set. Webhook failures return non-zero and log the URL + HTTP status on stderr.
Named Profiles
A profile is a saved set of flag values, reused across invocations. Use it when a scheduled agent calls the same command every run with the same configuration - HeyGen's "Beacon" pattern.
granola-pp-cli profile save briefing --json
granola-pp-cli --profile briefing folders
granola-pp-cli profile list --json
granola-pp-cli profile show briefing
granola-pp-cli profile delete briefing --yesExplicit flags always win over profile values; profile values win over defaults. agent-context lists all available profiles under available_profiles so introspecting agents discover them at runtime.
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 2 | Usage error (wrong arguments) |
| 3 | Resource not found |
| 4 | Authentication required |
| 5 | API error (upstream issue) |
| 7 | Rate limited (wait and retry) |
| 10 | Config error |
Argument Parsing
Parse $ARGUMENTS:
1. Empty, `help`, or `--help` → show granola-pp-cli --help output 2. Starts with `install` → ends with mcp → MCP installation; otherwise → see Prerequisites above 3. Anything else → Direct Use (execute as CLI command with --agent)
MCP Server Installation
Install the MCP binary from this CLI's published public-library entry or pre-built release, then register it:
claude mcp add granola-pp-mcp -- granola-pp-mcpVerify: claude mcp list
Direct Use
1. Check if installed: which granola-pp-cli If not found, offer to install (see Prerequisites at the top of this skill). 2. Match the user query to the best command from the Unique Capabilities and Command Reference above. 3. Execute with the --agent flag:
granola-pp-cli <command> [subcommand] [args] --agent4. If ambiguous, drill into subcommand help: granola-pp-cli <command> --help.