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

196 lines
10 KiB
Markdown

---
name: fireflies-relanguage
description: Fix a wrong-language Fireflies transcript on a meeting you don't own (can't hit Reprocess yourself) by re-uploading its audio under the correct language via the Fireflies API, recovering real speaker names into the result with transcript-speaker-fill, then deleting the duplicate meeting. Use when the user has a link to a Fireflies recording they aren't the host/owner of, the transcript is in the wrong language, and they want a corrected transcript without waiting for the owner to act or leaving a duplicate meeting behind. Trigger phrases: "I'm not the owner of this meeting", "can't reprocess this transcript", "fix this Fireflies transcript without the owner", "re-transcribe a shared meeting".
---
# 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]](../transcript-speaker-fill/SKILL.md) 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](/wiki/entities/fireflies-transcript-handling.md)
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
```bash
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](https://guide.fireflies.ai/articles/2973706448-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
```bash
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
```bash
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
```bash
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):
```bash
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_url`** — `upload` 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).