{"id":44296471,"url":"https://github.com/dmwyatt/granz","last_synced_at":"2026-04-18T20:01:56.505Z","repository":{"id":337719334,"uuid":"1154911090","full_name":"dmwyatt/granz","owner":"dmwyatt","description":"CLI tool for querying Granola meeting data","archived":false,"fork":false,"pushed_at":"2026-03-21T19:06:00.000Z","size":265,"stargazers_count":0,"open_issues_count":17,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-03T01:13:45.686Z","etag":null,"topics":["cli","granola","meeting-notes","rust","sqlite"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dmwyatt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-10T23:01:39.000Z","updated_at":"2026-03-21T19:03:21.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/dmwyatt/granz","commit_stats":null,"previous_names":["dmwyatt/granz"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/dmwyatt/granz","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmwyatt%2Fgranz","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmwyatt%2Fgranz/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmwyatt%2Fgranz/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmwyatt%2Fgranz/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dmwyatt","download_url":"https://codeload.github.com/dmwyatt/granz/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmwyatt%2Fgranz/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31982755,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T17:30:12.329Z","status":"ssl_error","status_checked_at":"2026-04-18T17:29:59.069Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","granola","meeting-notes","rust","sqlite"],"created_at":"2026-02-11T01:03:35.034Z","updated_at":"2026-04-18T20:01:56.392Z","avatar_url":"https://github.com/dmwyatt.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# grans\n\nA fast CLI tool for searching, filtering, and querying your [Granola](https://granola.ai) meeting notes. Data is synced from the Granola API and stored in a local SQLite database.\n\n## Installation\n\n```bash\ncargo install --path .\n```\n\nOr build from source:\n\n```bash\ncargo build --release\n# Binary at target/release/grans\n\n# With GPU acceleration for semantic search:\ncargo build --release --features directml # Windows (any GPU, recommended)\ncargo build --release --features cuda     # NVIDIA (requires CUDA toolkit + cuDNN)\ncargo build --release --features coreml   # macOS Apple Silicon\n```\n\n## Quick Start\n\n```bash\n# Sync your data from Granola (requires being logged into Granola app)\ngrans sync\n\n# Now you can query your meetings\ngrans list\ngrans search \"project kickoff\"\n```\n\n## Usage\n\n```\ngrans [OPTIONS] \u003cCOMMAND\u003e\n```\n\n### Global Options\n\n| Flag | Description |\n|------|-------------|\n| `--db \u003cpath\u003e` | Use a specific database file instead of the default |\n| `--token \u003ctoken\u003e` | Use a specific API token instead of reading from Granola's config |\n| `--json` | Output as JSON |\n| `--no-color` | Disable colored output (human-readable format without ANSI codes) |\n| `--utc` | Display timestamps in UTC instead of local time |\n| `--verbose` / `-v` | Enable verbose debug output (written to stderr) |\n\n### Data Storage\n\ngrans stores your meeting data in a local SQLite database at:\n- macOS: `~/Library/Application Support/grans/grans.db`\n- Linux: `~/.local/share/grans/grans.db`\n- Windows: `%APPDATA%/grans/grans.db`\n\nData is fetched from the Granola API using `grans sync` and accumulates over time. Unlike Granola's local cache (which only holds recent meetings), grans preserves all your synced data indefinitely.\n\n## Commands\n\ngrans uses a task-centric CLI design. Common tasks are promoted to top-level commands, while entity exploration and administrative tasks are grouped under `browse` and `admin` respectively.\n\n### Quick Reference\n\n**Daily Use Commands** (top-level):\n- `sync` - Sync data from Granola API\n- `list` (`ls`) - List meetings\n- `show` - Show meeting details\n- `search` (`s`) - Search meetings, transcripts, notes, and panels\n- `with` (`w`) - Show meetings with a person\n- `recent` - Show this week's meetings\n- `today` - Show today's meetings\n- `embed` - Build embeddings for semantic search\n- `dropbox` - Dropbox sync (init, push, pull, status, logout)\n- `info` - Show database statistics\n\n**Browse Commands** (entity exploration):\n- `browse people` - List/show people and their meetings\n- `browse calendars` - List calendars and events\n- `browse templates` - List/show panel templates\n- `browse recipes` - List/show recipes\n\n**Admin Commands** (maintenance):\n- `admin db` - Database management (clear, info, list)\n- `admin transcripts` - Transcript management (fetch, status)\n- `admin token` - Print the current Granola API token\n- `benchmark quality` - Measure semantic search quality against a test suite\n\n### Sync\n\nSync your data from the Granola API to your local database.\n\n```bash\n# Full sync (all data types)\ngrans sync\n\n# Sync specific data types\ngrans sync documents              # Just documents\ngrans sync transcripts            # Just transcripts (one API call per document)\ngrans sync panels                 # Just AI-generated panels (one API call per document)\ngrans sync people                 # Just people\ngrans sync calendars              # Just calendar events\ngrans sync templates              # Just templates\ngrans sync recipes                # Just recipes\n\n# Options\ngrans sync --dry-run              # Preview what would sync\ngrans sync transcripts --embed    # Build embeddings after syncing transcripts\ngrans sync documents --limit 50   # Limit to 50 documents\ngrans sync documents --since 7d   # Only docs updated in last 7 days\ngrans sync transcripts --delay-ms 500  # Rate limiting for transcripts\ngrans sync transcripts --retry         # Retry previously failed documents\ngrans sync panels --limit 10          # Fetch panels for up to 10 documents\ngrans sync panels --retry             # Retry previously failed panel fetches\n```\n\n**Note:** Sync requires a Granola auth token. By default, it reads the token from Granola's local config file. You can also provide a token explicitly with `--token`:\n\n```bash\ngrans --token \u003cTOKEN\u003e sync\n```\n\nTo get the token from a machine with Granola installed:\n\n```bash\ngrans admin token             # Print to stdout\ngrans admin token --clipboard # Copy to clipboard without printing\n```\n\n### Search\n\nSearch across meeting titles, transcripts, notes, and AI-generated panels.\n\n```bash\n# Search everything (titles, transcripts, notes, panels)\ngrans search \"standup\"\ngrans s \"standup\"    # short alias\n\n# Search specific targets\ngrans search \"AI\" --in titles\ngrans search \"budget\" --in titles,notes\ngrans search \"action items\" --in panels\ngrans search \"demo\" --in transcripts --date this-week\n\n# Semantic search (vector similarity via local embeddings)\n# Searches transcripts, AI-generated panel sections, and your notes\ngrans search \"deployment strategy\" --semantic\ngrans search \"what was decided about the API\" --semantic\n\n# Filter semantic search by source type with --in\ngrans search \"budget\" --semantic --in panels          # Only AI notes\ngrans search \"budget\" --semantic --in transcripts     # Only transcripts\ngrans search \"budget\" --semantic --in notes,panels    # Notes + AI notes\n\n# Limit results (default 10, use 0 for no limit)\n# Works with keyword, context window, and semantic search\ngrans search \"budget\" --limit 5\ngrans search \"budget\" --context 2 --limit 3\ngrans search \"budget\" --semantic --limit 5\ngrans search \"budget\" --limit 0  # No limit\n\n# Search with context (utterances for transcripts, sections for panels, paragraphs for notes)\ngrans search \"action items\" --context 3\ngrans search \"action items\" --context 2 --in panels\ngrans search \"budget discussion\" --semantic --context 2\n\n# Limit to a specific meeting\ngrans search \"budget\" --meeting \"Weekly Standup\"\n\n# Filter transcript matches by speaker\ngrans search \"action items\" --context 3 --speaker me      # only your matches\ngrans search \"deadline\" --context 3 --speaker other        # only others' matches\n\n# Include soft-deleted meetings in search results\ngrans search \"budget\" --include-deleted\ngrans search \"old project\" --semantic --include-deleted\n```\n\nSemantic search uses a local embedding model (`nomic-embed-text-v1.5`) to find meetings by meaning rather than exact keywords. On first use, the model is downloaded automatically (~270MB). Embeddings are built from transcripts, AI-generated panel sections, and your notes, and are stored in the main database. Use `--in` to restrict which sources are searched (e.g. `--in panels` to only search AI notes).\n\nTranscript chunks include speaker labels (`[You]` / `[Other]`) when speaker data is available, improving search relevance for queries like \"what did I say about...\" vs \"what did they say about...\".\n\nIf many chunks need embedding, semantic search will prompt for confirmation. Use `--yes` (`-y`) to skip the prompt:\n\n```bash\ngrans search \"deployment\" --semantic --yes\n```\n\n### Embed\n\nBuild embeddings for semantic search. Use this to control when embedding happens instead of waiting for the first semantic search.\n\n```bash\n# Build embeddings for new/changed chunks (prompts for confirmation)\ngrans embed\n\n# Skip confirmation prompt\ngrans embed --yes\ngrans embed -y\n\n# Show embedding status with per-type breakdown (transcripts/panels/notes)\ngrans embed status\n\n# Clear all embeddings (for dev/testing)\ngrans embed clear\n\n# Clear N most recent embeddings\ngrans embed clear --count 10\n\n# Force re-embed everything: clear then embed\ngrans embed clear --yes \u0026\u0026 grans embed --yes\n```\n\nEmbeddings are built automatically during `grans sync --embed` or on the first semantic search if not already present. The `embed` command gives you explicit control over when this happens, which is useful when you have a lot of new content and don't want the first search to block.\n\n### List Meetings\n\n```bash\n# List all meetings\ngrans list\ngrans ls    # short alias\n\n# Filter by date\ngrans list --date today\ngrans list --date this-week\ngrans list --date last-month\ngrans list --from 2026-01-01 --to 2026-01-15\n\n# Filter by person\ngrans list --person \"lisa\"\n\n# Include soft-deleted meetings\ngrans list --include-deleted\ngrans list --date this-week --include-deleted\n\n# This week's meetings (shortcut)\ngrans recent\n\n# Today's meetings (shortcut)\ngrans today\n```\n\n### Show Meeting Details\n\n```bash\n# Show meeting by title or ID\ngrans show \"Claude Code\"\ngrans show 3219f4e3   # by ID prefix\n\n# Export transcript or notes\ngrans show \"Weekly Standup\" --transcript \u003e transcript.txt\ngrans show \"Weekly Standup\" --notes \u003e notes.md\n\n# Both together (notes first, then transcript)\ngrans show \"Weekly Standup\" --notes --transcript\n\n# Filter transcript by speaker\ngrans show \"Weekly Standup\" --transcript --speaker me      # only your utterances\ngrans show \"Weekly Standup\" --transcript --speaker other   # only others' utterances\n\n# AI-generated panels are shown automatically under \"AI Notes\"\n# when present for a meeting\n\n# JSON format (includes source field for speaker identification)\ngrans show \"Weekly Standup\" --transcript --json\ngrans show \"Weekly Standup\" --notes --json\n```\n\n### Meetings with a Person\n\n```bash\n# Show all meetings with a person\ngrans with \"todd\"\ngrans w \"todd\"    # short alias\n\n# Filter by date\ngrans with \"alice\" --date this-week\ngrans with \"bob\" --from 2026-01-01\n\n# Include soft-deleted meetings\ngrans with \"todd\" --include-deleted\n```\n\n### People\n\n```bash\n# List all people\ngrans browse people list\n\n# Filter by company\ngrans browse people list --company \"Acme\"\n\n# Show person details\ngrans browse people show \"lisa\"\n```\n\n### Calendars\n\n```bash\n# List calendars\ngrans browse calendars list\n\n# Show events\ngrans browse calendars events\ngrans browse calendars events --calendar \"user@example.com\" --date this-week\n```\n\n### Templates\n\n```bash\n# List panel templates\ngrans browse templates list\ngrans browse templates list --category \"Team\"\n\n# Show template details\ngrans browse templates show \"Stand-Up\"\n```\n\n### Recipes\n\n```bash\n# List recipes\ngrans browse recipes list\ngrans browse recipes list --visibility public\n\n# Show recipe details\ngrans browse recipes show \"meeting-summary\"\n```\n\n### Info\n\nShow statistics about your local database.\n\n```bash\n# Show database statistics\ngrans info\n\n# JSON output for scripting\ngrans info --json\n```\n\nDisplays content counts (documents, transcripts, panels, people, embeddings, etc.), date range of documents, embedding model, and database information (path, size, schema version).\n\n### Database Management\n\nManage the local SQLite database.\n\n```bash\n# Clear database (will require re-sync)\ngrans admin db clear\n\n# Clear all database files\ngrans admin db clear --all\n\n# Show database location and size\ngrans admin db info\n\n# List all database files\ngrans admin db list\n```\n\n### Transcript Management\n\nFetch transcripts for specific documents from Granola's API.\n\n```bash\n# Fetch transcript for a specific document\ngrans admin transcripts fetch \u003cdocument-id\u003e\ngrans admin transcripts fetch \u003cdocument-id\u003e --dry-run\n\n# Show transcript status\ngrans admin transcripts status\n```\n\nFor bulk transcript syncing, use `grans sync transcripts` instead.\n\n**Note:** Requires a Granola auth token. Reads from Granola's local config file by default, or use `--token` to provide one explicitly.\n\n### Dropbox Sync\n\nShare your grans database across multiple machines via Dropbox.\n\n**Why use this?** Two operations in grans are slow:\n\n1. **Transcript sync** (`grans sync transcripts`) - Fetches transcripts from Granola's API with rate limiting (~1.5s per document). For 200 meetings, that's ~5 minutes.\n\n2. **Embedding generation** - First semantic search builds vector embeddings for transcripts, panel sections, and notes, which takes time on CPU.\n\nOnce you've done this work on one machine, Dropbox sync lets you share the results instead of repeating it everywhere.\n\n**Initial setup (on your primary machine):**\n\n```bash\n# 1. Sync all data including transcripts from Granola API\ngrans sync\n\n# 2. Build embeddings by running a semantic search (slow first time)\ngrans search \"anything\" --semantic\n\n# 3. Connect to Dropbox (one-time OAuth)\ngrans dropbox init\n\n# 4. Upload your database\ngrans dropbox push\n```\n\n**On other machines:**\n\n```bash\n# 1. Connect to Dropbox\ngrans dropbox init\n\n# 2. Download the databases\ngrans dropbox pull\n\n# 3. Queries now work instantly - no need to re-sync or rebuild\ngrans search \"deployment\" --semantic\n```\n\n**Keeping machines in sync:**\n\n```bash\n# After syncing new data on your primary machine\ngrans sync\ngrans dropbox push\n\n# On other machines\ngrans dropbox pull\n```\n\n**Commands:**\n\n| Command | Description |\n|---------|-------------|\n| `grans dropbox init` | One-time Dropbox authentication |\n| `grans dropbox push` | Upload database to Dropbox |\n| `grans dropbox pull` | Download database from Dropbox |\n| `grans dropbox status` | Show sync status with local vs remote comparison |\n| `grans dropbox logout` | Remove Dropbox authentication |\n\n**Sync status** shows a side-by-side comparison of local and remote database:\n\n```\nSync Status\n───────────\nAuthentication: Connected\nLast push: 2025-01-27 15:30:00 UTC\nLast pull: Never\n\n                             Local              Remote\n                             ─────              ──────\nDocuments:                     423                 418\nWith transcripts:              389                 385\nUtterances:                  52.8K               51.2K\nDate range:           2023-06 → 2025-01   2023-06 → 2025-01\nSchema version:                  3                   3\nDatabase size:             45.0 MB             44.8 MB\nEmbeddings:                 52.8K               51.2K\n```\n\nThis helps you see at a glance whether your local database is ahead of or behind the remote copy, without downloading the full database.\n\n**Conflict handling:** Sync refuses to overwrite newer files by default. Use `--force` to override:\n\n```bash\ngrans dropbox push --force   # Overwrite remote even if it's newer\ngrans dropbox pull --force   # Overwrite local even if it's newer\n```\n\n**What gets synced:**\n- Database (meeting data, transcripts, FTS indices, vector embeddings for semantic search)\n\nThe sync uses a sandboxed Dropbox app folder (`Apps/grans/`), so it only accesses its own files, not your full Dropbox.\n\n## Output Modes\n\n- **TTY** (default): Human-readable formatted output with colors in terminals, automatically stripped when piped. Timestamps are shown in your local timezone.\n- **JSON** (`--json`): Structured JSON output for scripting. Timestamps remain as raw ISO 8601 UTC strings.\n\n```bash\n# Pipe output (colors automatically stripped)\ngrans list | head -5\n\n# JSON for scripting\ngrans list --json | jq '.[].title'\n\n# Force no color in terminal\ngrans list --no-color\n\n# Display timestamps in UTC instead of local time\ngrans list --utc\n```\n\n## Debugging\n\nUse `--verbose` (or `-v`) to enable debug logging on stderr. This shows API requests/responses, timing, auth resolution, and sync details without affecting stdout output.\n\n```bash\n# Debug a sync operation\ngrans -v sync\n\n# Debug with JSON output (debug on stderr, JSON on stdout)\ngrans --json -v info\n\n# Fine-grained control via GRANS_LOG env var\nGRANS_LOG=grans::api=debug grans sync\nGRANS_LOG=grans::api=trace,grans::db=debug grans sync\n```\n\nThe `GRANS_LOG` environment variable uses [env_logger filter syntax](https://docs.rs/env_logger/latest/env_logger/#enabling-logging) and takes precedence over `--verbose` when set.\n\n### Update\n\nUpdate grans to the latest version from GitHub.\n\n```bash\n# Check for updates without installing\ngrans update --check\n\n# Download and install the latest version\ngrans update\n\n# Show current version\ngrans --version\n```\n\nThe update command downloads the appropriate binary for your platform from GitHub releases, verifies its SHA256 checksum, and replaces the current binary.\n\n**Build Waiting**: If a release build is in progress on GitHub Actions, grans will detect it and offer to wait:\n\n```bash\n# Interactive: prompts to wait if a build is in progress\ngrans update\n\n# Auto-wait for builds (for scripts/CI)\ngrans update --wait\n\n# Set a custom timeout (default: 600 seconds)\ngrans update --wait --timeout 300\n```\n\n**Private Repositories**: For private repositories, grans will prompt to use your `gh` CLI credentials if available. For non-interactive/scripted usage:\n\n```bash\n# Use gh CLI auth automatically (no prompt)\ngrans update --check --use-gh-auth\n\n# Or set an environment variable\nexport GH_TOKEN=$(gh auth token)\ngrans update --check\n```\n\n### Benchmark Quality\n\nMeasure semantic search quality against a test suite of queries with known expected results.\n\n```bash\n# Run benchmark with default settings (precision@10)\ngrans benchmark quality --file tests/semantic_search_benchmark.json\n\n# Check top 5 results (precision@5)\ngrans benchmark quality --file tests/semantic_search_benchmark.json --k 5\n\n# Show detailed results for each query\ngrans benchmark quality --file tests/semantic_search_benchmark.json --detail\n```\n\nThe benchmark reports:\n- **Precision@k**: Percentage of queries where the expected meeting appears in the top k results\n- **Mean Reciprocal Rank (MRR)**: Average of 1/rank for found matches (measures how high expected results appear)\n\nThis is useful for:\n- Testing chunking strategy changes\n- Evaluating embedding model updates\n- Comparing semantic search performance across database versions\n\nUse the `--db` flag to benchmark against different database files without affecting your main database:\n\n```bash\ngrans --db /path/to/test.db benchmark quality --file tests/semantic_search_benchmark.json\n```\n\n## Date Filters\n\nRelative terms: `today`, `yesterday`, `this-week`, `last-week`, `this-month`, `last-month`\n\nDuration shorthands: `3d` (3 days ago), `2w` (2 weeks ago), `1m` (1 month ago):\n\n```bash\ngrans list --from 2w             # meetings from the last 2 weeks\ngrans list --from 4w --to 2w     # meetings between 4 and 2 weeks ago\ngrans sync transcripts --since 7d\n```\n\nAbsolute ranges with `--from` and `--to` (ISO 8601 dates):\n\n```bash\ngrans list --from 2026-01-01 --to 2026-01-31\ngrans list --from 2026-01-15  # open-ended\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdmwyatt%2Fgranz","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdmwyatt%2Fgranz","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdmwyatt%2Fgranz/lists"}