ckb/.agents/skills/fireflies-relanguage/SKILL.md
2026-07-15 09:54:12 +02:00

10 KiB


Fireflies relanguage skill

Purpose

Fireflies' own "Update Language + Reprocess" fix only works for the meeting's host/owner (see guide.fireflies.ai docs). If you were just sent a link to someone else's recording and it came out in the wrong language, you have no UI path to fix it and can't wait for the owner. This skill routes around that: pull the meeting's audio via the API, re-upload it as a new meeting under your own account with the correct language forced, recover the original's real speaker names into the new, correctly-transcribed text using the [transcript-speaker-fill] skill, then delete the duplicate meeting the re-upload created.

Before you start: real constraints, not hypothetical ones

This may simply not be possible on your account. audio_url/video_url on a Fireflies transcript require a Pro+ plan, and it is undocumented whether they populate at all for a meeting merely shared with you rather than owned by you. Michał's own wiki notes record hitting exactly this wall before (no Pro seat → transcript download tooling broke). Step 1 below (fetch) is also the diagnostic: if it prints the "audio_url is null" warning, stop here — this workflow cannot proceed without one of the workarounds it suggests (a Pro+ seat's API key, the actual owner reprocessing it, or you manually downloading the audio via the web UI and hosting it at some public HTTPS URL yourself).

Every re-upload consumes real transcription quota/minutes on the account whose API key you use, same as any other Fireflies upload — this isn't free just because it's automated. Deleting a transcript is irreversible. Don't run this on a meeting you actually might need to keep two copies of, and don't skip the confirmation gates below.

Also worth knowing up front: the Fireflies API has thin rate limits (Free: 50 requests/day, Pro: 500/day, Business/Enterprise: 60/min) and deleteTranscript specifically is capped at 10/min. This workflow only needs a handful of calls plus some polling, but don't loop the wait step aggressively.

Requirements

  • FIREFLIES_API_KEY set in the environment. Get one from the Fireflies web app under Settings > API. The script exits with this exact instruction if it's missing — don't hardcode the key anywhere.
  • The transcript-speaker-fill skill installed alongside this one (it is, in this repo) — this skill hands off the actual name-recovery merge to it rather than duplicating that logic.

Workflow

Resolve <skill-dir> to this skill's own directory throughout.

1. Fetch the original (broken-language) transcript

python3 "<skill-dir>/scripts/fireflies_client.py" fetch --ref "<link-or-id-the-user-gave-you>" --out original.md

Accepts a bare transcript id or a full share link like https://app.fireflies.ai/view/Some-Title::abcDEF123. Writes original.md (real speaker names, garbled text — the shape transcript-speaker-fill expects for its "broken" input) and original.md.meta.json (id, title, audio_url, etc., needed by later steps).

Check the printed output for the audio_url warning before continuing. If it's null, stop and follow the guidance it prints instead of proceeding to step 2 — do not attempt step 2 anyway "just to see."

2. Confirm the target language with the user

Don't guess the correct language from context alone unless it's unambiguous (e.g. the user already told you). Ask if unclear. Use the language code Fireflies expects — check Learn about Fireflies supported languages for the exact code if you're not sure it matches (e.g. en, pl, es).

3. Re-upload the audio under the correct language

python3 "<skill-dir>/scripts/fireflies_client.py" upload --meta original.md.meta.json --language <code>

This calls uploadAudio with custom_language set, tagging the new meeting's title with [relang:<original-id>] so it can be found unambiguously afterward (override with --title if you want a cleaner name, but then pass that exact same string to step 4). Note the exact title printed — you need it verbatim for the next step.

4. Wait for the re-upload to finish processing

python3 "<skill-dir>/scripts/fireflies_client.py" wait --title "<exact title from step 3>"

Polls every 30s (default) up to 15 minutes (default) for a transcript with that exact title to appear with content. Long recordings can take longer — if it times out, just re-run wait again rather than assuming failure. Prints the new transcript's id once ready.

5. Fetch the new (correct-language) transcript

python3 "<skill-dir>/scripts/fireflies_client.py" fetch --ref "<id from step 4>" --out new.md

This one should come back with correct text but generic Speaker 1, Speaker 2, ... labels (no calendar/roster context on a bare re-upload) — exactly the gap transcript-speaker-fill closes.

6. Recover real speaker names

Hand off to the transcript-speaker-fill skill exactly per its own SKILL.md, using original.md and new.md as the two inputs — dry run first, present the resolution report, get the user's confirmation/manual overrides, then --apply. Do not skip its confirmation gate just because you're mid-pipeline; it's exactly as load-bearing here as when invoked standalone.

Also pass --recording-url "https://app.fireflies.ai/view/<id from step 4>" on every fill_speakers.py call in this handoff (both the dry-run and the --apply run) — the bare-id form of the URL works without needing to slug-encode the title, and gives the user a direct link to a recording they definitely have full access to (they own the re-uploaded meeting) right next to every label's example lines, for the spot-check transcript-speaker-fill asks them to do before confirming. Use the new transcript's id, not the original's — do this before step 8's cleanup, while that meeting (and its recording) still exists.

7. Ask where to save the corrected transcript

Before touching the duplicate meeting, ask the user where the final --apply'd output (from step 6) should end up — don't default to leaving it in a scratch/temp location without asking. Common answers: a specific path they name, this project's own tmp/ (if this skill is being run from inside a Cascade Knowledge Base repo like this one — gitignored, agent- managed), or raw/inbox/ if they want it ingested into a wiki afterward. Move (don't copy) the file there once they've told you.

8. Delete the duplicate meeting — only after explicit confirmation

Once the user has confirmed the merged transcript (from step 6) looks right and it's been saved wherever they wanted (step 7), delete the meeting the re-upload created (not the original — you don't own that one anyway, and couldn't delete it if you tried):

python3 "<skill-dir>/scripts/fireflies_client.py" delete --id <id from step 4> --yes

The script refuses to run without --yes. Never pass --yes without the user having explicitly confirmed they're ready — this is irreversible and removes a real meeting from their Fireflies account. Show them the printed {id, title, date, duration} of what was deleted as final confirmation.

Failure modes and what they mean

  • fetch on the original prints the audio_url warning — see "Before you start" above. This is the expected failure mode when the API key's account isn't Pro+, or when a merely-shared meeting doesn't expose audio via the API. Not a bug to work around silently.
  • upload mutation succeeds (success: true) but wait never finds it — the title match is exact-string, so a --title override that doesn't exactly match what you pass to wait will never resolve; double check you used the identical string in both commands.
  • wait times out — normal for long recordings. Re-run it; don't assume the upload failed.
  • Rate limit errors (too_many_requests) — the error message includes retryAfter; wait that long before retrying, especially on a Free-tier key (50 requests/day total).
  • delete fails with require_elevated_privilege — you're trying to delete a transcript you don't own (e.g. you accidentally passed the original id instead of the new one from step 4). Only the re-uploaded meeting is yours to delete.

Edge cases

  • Multiple speakers with a name-recovery gap (new transcript has more distinct Speaker N labels than the original has real names) — this is transcript-speaker-fill's "roster gap" warning, not something this skill's own steps can fix; it means someone's voice wasn't distinctly captured with a real name in the original either.
  • Original meeting has video_url but not audio_urlupload only accepts --audio-url; there's no video re-upload path here. If you truly only have video access, extract audio from it yourself first and host that as a public URL, then pass it via --audio-url.
  • The user wants the corrected transcript ingested into this repo's wiki, not just saved as a file — step 7 already asks where to save it; if the answer is "ingest it," save it to raw/inbox/ there, then run the normal "Ingest" workflow from the root CLAUDE.md on it after step 8's cleanup (the duplicate meeting is Fireflies-side bookkeeping, unrelated to whether the wiki ingest has happened yet).