JSPM

  • Created
  • Published
  • Downloads 265
  • Score
    100M100P100Q93683F

CLI toolkit for exporting and working with Granola notes and transcripts

Package Exports

  • granola-toolkit
  • granola-toolkit/dist/cli.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (granola-toolkit) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

granola-toolkit

General toolkit to do more with Granola notes and transcripts.

Install

From npm:

npm install -g granola-toolkit
granola --help

Without a global install:

npx granola-toolkit --help

For local development:

curl -fsSL https://vite.plus | bash
vp help
vp install

Run

Installed CLI:

granola --help
granola notes --help
granola transcripts --help

Local build:

vp pack
node dist/cli.js --help
node dist/cli.js notes --help
node dist/cli.js transcripts --help

You can also use the package scripts:

npm run build
npm run notes -- --help
npm run transcripts -- --help

Examples

Export notes:

node dist/cli.js notes --supabase "$HOME/Library/Application Support/Granola/supabase.json"

Export transcripts:

node dist/cli.js transcripts --cache "$HOME/Library/Application Support/Granola/cache-v3.json"

How It Works

Notes

notes exports Granola's generated meeting notes, not the raw transcript.

The flow is:

  1. read your local supabase.json
  2. extract the WorkOS access token from it
  3. call Granola's paginated documents API
  4. choose the best available note content for each document
  5. convert ProseMirror content into Markdown
  6. write one Markdown file per document into the output directory

Content is chosen in this order:

  1. notes
  2. last_viewed_panel.content
  3. last_viewed_panel.original_content
  4. raw content

Each note file includes:

  • YAML frontmatter with the document id, created timestamp, updated timestamp, and tags
  • a top-level heading from the note title
  • converted note body content

Transcripts

transcripts exports Granola's locally cached transcript segments.

The flow is:

  1. read Granola's cache JSON from disk
  2. parse the cache payload, whether it is double-encoded or already an object
  3. match transcript segments to documents by document id
  4. format segments as [HH:MM:SS] Speaker: Text
  5. write one .txt file per document into the output directory

Speaker labels are currently normalised to:

  • You for microphone
  • System for everything else

Incremental Writes

Both commands are incremental. They only rewrite an export file when the source document appears newer than the file already on disk.

That means repeated runs are cheap, and you can safely point the CLI at the same output directory over time.

Config

The CLI reads configuration in this order:

  1. command-line flags
  2. environment variables
  3. .granola.toml
  4. platform defaults

Supported config keys:

debug = true
supabase = "/Users/yourname/Library/Application Support/Granola/supabase.json"
output = "./notes"
timeout = "2m"
cache-file = "/Users/yourname/Library/Application Support/Granola/cache-v3.json"
transcript-output = "./transcripts"

Supported environment variables:

  • DEBUG_MODE
  • SUPABASE_FILE
  • OUTPUT
  • TIMEOUT
  • CACHE_FILE
  • TRANSCRIPT_OUTPUT

What Changed In The Port

This port deliberately preserves the Go repo's architecture, but it also fixes a few obvious rough edges instead of copying them blindly:

  • deterministic export ordering, so duplicate-title suffixes are stable across runs
  • shared filename sanitisation between notes and transcripts
  • cross-platform default path discovery for both supabase.json and cache files
  • HTML fallback for note export is converted into readable Markdown-ish text instead of being dumped raw
  • transcript timestamps preserve the original clock time instead of being normalised to UTC

Verify

vp check
vp test
vp pack
npm pack --dry-run

vp build is for web apps. This repo publishes a CLI bundle, so the correct build step here is vp pack.

Publishing

Any push to main with a package version that is not already on npm becomes a publish candidate automatically. The workflow verifies the build, checks whether package.json contains an unpublished version, and then pauses in the production environment until someone approves the deployment review in GitHub.

That means you can use either flow:

  • merge a PR that already includes the version bump
  • run the local release helper on main

Local release helper:

npm run release

That script:

  1. verifies the git working tree is clean
  2. verifies you are on main
  3. bumps the package version with npm version --no-git-tag-version
  4. commits and pushes the release commit
  5. lets the push-to-main workflow create a publish candidate automatically

You can also choose the bump type explicitly:

npm run release patch
npm run release minor
npm run release major

The GitHub Actions release job then:

  • installs dependencies with Vite+ via setup-vp
  • runs vp check, vp test, vp pack, and npm pack --dry-run
  • checks npm first and skips the publish job if that exact version already exists
  • waits for approval on the production environment before npm credentials are exposed
  • publishes to npm using NPM_TOKEN
  • tags the published version as v<version>

GitHub Setup

To get the review dialog you showed in the screenshots, configure this once in GitHub:

  1. create a production environment in repository Settings -> Environments
  2. add required reviewers to that environment
  3. add NPM_TOKEN as an environment secret on production

After that, merges to main that contain a new unpublished version will stop at "Review deployments". Approving that deployment is what allows the npm publish step to run.