⏳ This skill is pending AI review.

Scores will appear once the review pipeline completes.

version unknown

blurt

@agihunt⭐ 202 stars

>-

Choose how to use this skill

You do not need every option. Choose the path your AI client supports. The stable page stays the same; versioned files are immutable.

1. Native installer

This listing has no registered native installer command. Use the complete package or source fallback below, depending on what your client supports.

Do not guess an installer command or replace an existing version without reviewing the diff.

2. Complete package recommended

Download the ZIP when available. It includes SKILL.md plus the references, security notes and version metadata.

No complete ProSkills package is published for this listing yet.

3. Prompt-only

Copy the prompt above when the agent can read the stable page or when you want to adopt the workflow without installing a skill.

Need only the instruction file?

Download SKILL.md only if your client requires a single file. The complete ZIP is safer for a full installation because it preserves the references and release context.

No path installs or executes anything by itself. Your agent still needs access to the project files. Before updating, compare the installed version and review the diff.

—/10

// RATINGS

⭐GitHub Stars
⭐⭐⭐ 202 on GitHubGitHub ↗

Popular

🟢ProSkills Score
—
📍

Not yet listed on ClawHub or SkillsMP

// README


Blurt is a menu-bar app for the Mac; Windows and Linux are coming soon. It works next to the agents you already use, such as Claude Code, Codex, Cursor, Claude and ChatGPT, and handles the part before they start: getting what you mean into the agent's input box. It has no agent of its own and doesn't replace yours.

What it does

Dictation (Fn). Tap Fn in any app, talk, and tap it again. The words are typed where your cursor is. Finish with a double tap instead and it's sent too (Return). It works the moment Blurt is installed, with macOS's own speech recognition (macOS 26) and nothing to download. Open-source local models are an optional upgrade: more accurate, they learn your words and need no internet; after a minute of talking the text is in about 0.4 s. A local model removes fillers and stutters and adds punctuation; it doesn't rewrite or summarize. The words show under the notch as you talk. Tap ⇧ while talking to switch to Structured (a list, when you listed things) and again to switch back; tap ⌘ to translate (speak Chinese, type English, or the other way round); ⌥L reformats the last dictation. Holding Fn while you talk works too.

Snap (⌥A) and Voice Note (⌥V). Drag over part of the screen and say what's wrong; the note opens right beside the selection. Draw on it as you talk: drag for a freehand line, ⇧ for a box, ⌥ for an ellipse, ⌘Z to undo. The snap becomes a card under the notch. Drag the card into Claude, ChatGPT, Slack or a terminal running Claude Code or Codex: the picture and your note go in together. Or click it to copy, then ⌘V. A voice note is the same, with your words and no picture.

Copy text from screen (⌥C). Drag over any text you can't select: a screenshot, a video, a locked page, a terminal. Let go and it's on the clipboard in about 0.2 s, laid out as if you had copied it: the half line the edge cut is left out, wrapped list items are one line again, tables keep their tabs and code its indentation. It reads with macOS's own recognizer, so there's nothing to download. Optionally a second reading by a small local OCR model (720 MB) or your own cloud model fixes misread symbols in code and paths.

Record (⌥R). Record your screen while you talk through a product, a page or an idea. Your coding agent turns the recording into items (issues, ideas, notes, tasks), each with a screenshot, the spot marked, steps and the code that is probably involved. It's transcribed while you record, so it's done about a minute after you stop; one 5-minute walkthrough became 52 items, each with what was said and when. You go through them with the keyboard, then hand them to an agent, or pack them into one .zip for a teammate.

Snaps, voice notes and recordings go into the current project (a folder you open in Blurt). Without one, recordings go to Unassigned and snaps stay as cards until you pick a project. Every shortcut can be changed in Settings.

Install

You need macOS 13 or later. Dictation and the recommended speech models need Apple silicon.

Download Blurt for Mac (signed and notarized), open the disk image and drag Blurt into Applications. Updates arrive inside the app. The app brings everything it runs, so there is nothing else to install.

Or give this to your agent (Claude Code, Codex…), or paste it into a terminal:

curl -fsSL https://blurt.work/install.sh | sh

It downloads the latest release, checks that it is signed and notarized, puts Blurt in Applications and opens it.

Use it from your agent. In Blurt, Settings → Agents → Connect… adds the Blurt skill and MCP server to Claude Code, Codex and the other agents it finds (Gemini CLI, Kimi Code, OpenCode, Grok, CodeBuddy Code, WorkBuddy, Cursor). Start a new session of the agent, then tell it "start blurt" (/blurt in Claude Code, $blurt in Codex), or ask it about your items. Your agent can also do the connecting: llms.txt has the steps.

Without the app, from source: npx skills add AGIHunt/blurt -g, or in Claude Code /plugin marketplace add AGIHunt/blurt and then /plugin install blurt@blurt. Then "start blurt". The first run builds Blurt into ~/Applications; it needs the Xcode Command Line Tools and uv, and the agent offers to install what's missing.

First minute. The setup assistant asks for Microphone and Accessibility (and Input Monitoring if you use another key than Fn). Then press Fn in any text field: dictation already works with macOS's own recognizer. Two optional upgrades: one AI-service key (fixes misheard names and punctuation, and powers translation and the cloud second reading of copied text), and the local models (1–6 GB depending on your Mac's memory, downloaded only when you click Download). Screen Recording is asked for the first time you snap or record.

How it works with your agents

Blurt gets your words, pictures and recordings into an agent. The agent does the work.

What it does
Dictation and cardsText and pictures go into any chat box or terminal, so they work with every agent.
SkillThe blurt skill lets your agent record, transcribe, write items, open the review page and export.
MCP serverblurt gives agents search_items, get_item (with screenshots), get_transcript, update_item, mark_fixed and bundle_items. Settings → Agents → Connect… installs the skill and the MCP server into an agent, after showing what it changes.
Hand off to agent (⌘↩)Runs Claude Code or Codex in Blurt's terminal, in the project folder, optionally with one git worktree per item. Claude Code can also run in the Claude desktop app.
Copy instructionOne line for any other agent, pointing to a hand-off note with every detail and screenshot.
Hand-off bundleOne .zip with a summary, every item and its screenshots, for a person rather than an agent.

Agents report back through the MCP server: an item moves to In progress, then To verify. "Fixed" from an agent always means to verify until you mark it done.

Recordings are organized by the first agent that is installed and signed in: Claude Code, then Codex, Kimi Code, OpenCode, Gemini CLI and Grok. You can change the order in Settings → Agents.

No connectors, by design. Your agent already has tools for Slack, Linear, GitHub, Notion, Jira and Feishu/Lark. Blurt writes files (Markdown, CSV and the hand-off bundle) and leaves delivery to the agent. To send items somewhere, ask it, for example: "Bundle the checkout issues and post them to #web in Slack."

How Blurt compares

Checked on 2026-10-25. Prices and policies change.

Blurt's numbers come from its own tests on one machine, an Apple M5 Max with 128 GB, with speech generated by macOS say. Synthetic speech is cleaner than a real microphone, so error rates are optimistic; smaller Macs, real microphones and accen

// HOW IT'S BUILT

// FILES

713 files shipped with this skill. Inline text is the catalog copy. The upstream link is the source repository.

  • skills/blurt/SKILL.md26 KBdc16e9ebe14aupstream ↗
    ---
    name: blurt
    description: >-
      Screen + voice → structured work. The user records their screen while talking (demoing their product, browsing,
      exploring an idea); turn the recording into items — bug/polish issues with repro steps, frames and suspected code,
      ideas with the inspiration behind them, notes, tasks — let them review on a local page, then export (Markdown/CSV,
      a hand-off bundle (.zip), or any tracker or chat through the tools you already have) or start fixing. Use when the user
      wants to record feedback / QA / ideas by talking while using their screen, hands over such a video, wants to
      process recordings from the Blurt app, or wants their Blurt items packed up and sent somewhere.
      Triggers: "blurt", "/blurt", "start blurt", "开始口喷", "口喷鸡", "开始录", "录屏提 bug", "录一下我的想法", "record feedback",
      "turn this recording into issues", "处理我录的视频", "process my recordings", "把这些问题打包发给…",
      "bundle these items for…", "set up Blurt's models", "帮我设置口喷鸡的模型", "配置听写模型 / API Key".
    ---
    
    # blurt — show it, say it, get structured work back
    
    People think best out loud with their eyes on the screen. They jump between topics, correct themselves, point at
    things. Your job: capture that with no friction, then do the tedious part (splitting, writing up, picking and
    marking screenshots, finding the code, filing) with care. Optimise for the user's flow: never interrupt a
    recording, ask few questions, make review fast.
    
    Scripts live in `scripts/` next to this file; run them with `uv run <skill-dir>/scripts/<name>.py ...` (each
    declares its deps; `uv` installs them on first use). Every script has `--help`. Always reply in the user's
    language; write items in the language the user spoke.
    
    ## 0. Setup (first run, or when something fails)
    
    - `doctor.py` → OS, RAM/GPU, ffmpeg, configured ASR, recommendations, `todo` list.
    - Missing ffmpeg/uv: offer the install command; run it only after the user agrees.
    - ASR: if none configured, pick from `asr_recommendations` + the language the user speaks, tell the user in one
      line what and why (size, local vs cloud, cost), run its `setup`. Details: `reference/asr.md`. Cloud keys are set
      by the user in their env — never ask them to paste keys in chat.
    - `record.py build` — macOS: compiles the native recorder (Xcode Command Line Tools; else a Tk fallback) and
      installs the **Blurt** menu-bar app into ~/Applications. Windows: the Start-menu shortcut appears on first
      recording. Mention it in one line: "Blurt is also in your Applications — ⌥R records from anywhere".
    - `record.py test` → look at the returned `frame` yourself. Wallpaper-only/black or `ok: false` ⇒ Screen Recording
      permission missing; `mic_silent: true` ⇒ Microphone permission / wrong mic. On macOS the permission belongs to the
      app hosting you (Terminal, iTerm, VS Code, Claude, Codex…) and that app must be restarted after granting.
      macOS 15+ may show an "allow … to bypass the window picker" prompt — the user clicks Allow themselves.
    - Optional project config `.blurt/config.json`: export target, owners/module map, column mapping, custom lenses in
      `.blurt/lenses/`. `.blurt/` is never committed: items.py / projects.py add it to `.git/info/exclude` (not
      `.gitignore`) the first time they write there.
    
    ## 1. Record (or pick up a recording)
    
    **Just "/blurt" or "blurt", nothing else:** don't start a recording. Say where things stand in one or two lines —
    the current project (`projects.py current`), new items (`items.py list --state new`), recordings not organized yet
    (`record.py inbox`) — and offer three choices: record now, organize the new recordings, work on the items (fix or
    export). Start a recording right away only when the user asked for one ("开始录", "录屏提 bug", "record feedback",
    "start blurt").
    
    **With the Blurt app installed** (`/Applications/Blurt.app` or `~/Applications/Blurt.app`, macOS): start the
    recording in the app, so it records with the app's own Screen Recording permission — `record.py start` runs the
    recorder under the app hosting you (Terminal, iTerm, Codex…), which then needs that permission too and a restart.
    `pgrep -xq blurt-recorder || { open -a Blurt; sleep 3; }` then `open blurt://record`: the notch asks once (one click),
    the user picks the area and talks, ⌥R or *Finish* stops, and the app organizes the recording into items in its
    project (Settings → Agents picks the agent). Tell the user that in two lines; afterwards `index.py search` / the MCP
    tools find the items. Use `record.py start` (below) when there is no app, on Windows/Linux, or when the user wants
    the recording processed here in this chat.
    
    **From the chat:** if the thing to look at is a local dev server/app in this repo, check it is running. Start
    `record.py start` **in the background** (`--last-region` to reuse the last area) and tell the user in a few lines
    what happens:
    - a dimmed overlay: drag to draw the area (or click a window, F = full screen) → **开始录制** / Enter. Only that area
      is recorded — tabs, bookmarks and other windows stay private;
    - 3-2-1 countdown (click to skip), then a small floating bar: timer · mic level · ⏸ pause · ↺ restart/discard ·
      **完成**. ⌥P pause/resume, ⌥S finish (Windows: Alt+Shift; these are the defaults — the user may have changed
      them in the app's Settings → Shortcuts / config `record.hotkeys`, and the countdown shows the real ones). The bar is
      never in the recording;
    - talk naturally, point with the mouse; **hold ⌃ and drag to circle / underline what you mean** (macOS; ⌃⇧ box,
      ⌃⌥ ellipse, the keys in any order — resting the pointer straightens a rough shape — a snap's markup uses the same strokes; the ink fades after 3 s; the pen button on the bar or ⌥D keeps the pen on, Esc / ⌥D turns it off); click **完成** — no need
      to send another chat message.
    - **⌥A snaps** (Windows/Linux Alt+Shift+A — the marker key Alt+Shift+M too — or the camera button on the bar): a full-resolution still of
      the area right now, no pause → `shots/`. Best evidence there is — mention it in one line. In the macOS app ⌥A runs
      the normal snap flow while recording (any area, its own note typed or said, a card) and the recording goes on: the
      shot lands in this session's `shots/` with its note. Blurt's own windows, cards and notch are in the video like any
      app's (users record Blurt to report Blurt's problems).
    The task ends with `DONE video=…` → continue with §2 right away (`CANCELLED`, exit 2 → acknowledge and stop). From
    the chat you can also `record.py stop | pause | resume | restart | snap`.
    
    **Snap mode** (macOS app, not recording): ⌥A → drag an area, let go = shot, with a spoken (or typed) note; ⌃-drag on
    it circles things. With a current project the shot goes straight to it; without one it waits as a card until the
    user files it (or has the cards organized into Unassigned). The batch is `<ts>-shots/` (`meta.json` `"mode": "shots"`, no video; a line of
    `shots.jsonl` may carry the `project` the user filed it into). Suggest it for "one screen + one sentence"
    feedback (a wrong label, a layout glitch): faster to give and to process. Video is for flows, animations, timing and
    long rambles — anything where what happened *between* screens matters.
    
    **Waiting on a background step** (here and in §5): if your harness wakes you when a background task finishes (Claude
    Code), end your turn — you'll be notified. If it doesn't (Codex), a finished turn can't be resumed by the child
    process: keep the turn open and wait on that same tool session with long polls (no busy loop); don't send a final
    response while it runs.
    
    **Existing video** (QuickTime, OBS, Loom, phone, a teammate's Blurt recording…): create
    `.blurt/sessions/<timestamp>/`, copy it in as `recording.<ext>` (with `events.jsonl` / `meta.json` if it came from
    Blurt), continue with §2.
    
    **Projects and the inbox.** A project is a folder the user opened in Blurt (`projects.py list`: id, key such as `BA`,
    name, description, modules). Each has a store `<folder>/.blurt/`: items in `items/<KEY-n>-<slug>.md`, screenshots in
    `assets/<uid>/`, raw captures in `sessions/<ts>/`. Captures that belong to no project yet live in the inbox store
    `~/Blurt/inbox/` (shown as "Unassigned"). The user picks a current project, or none (`projects.py current`:
    `current` null) — then captures go to Unassigned. Only the user moves items between projects: never guess a project
    from the app in front, and never `items.py move` an item yourself unless the user asked for that one.
    
    **Blurt app recordings:** the menu-bar app records into the current project's store, or the inbox when there is none.
    `record.py inbox` lists them all with `processed` / `reviewed` flags (v0.6 recordings in `~/Blurt/recordings/` too).
    "Process my recordings" → do §2–§4 for each unprocessed one (fan out to subagents if available), then one review per
    session (or merge into one session dir if the user wants a single list). `meta.json` has `author` — keep it on items
    when several people's recordings are combined.
    
    ## 2. Transcribe
    
    `transcribe.py run <session>/recording.mp4` → `transcript.json` + `transcript.txt` (`[mm:ss.s-mm:ss.s] text`).
    Recordings made with Blurt are usually transcribed *while* they record (`live/`): then it returns at once with
    `"live": true` — same text as a whole-file run; `--force` redoes it (e.g. with a `--prompt` glossary). A shots
    session (`meta.json` `"mode": "shots"`): `transcribe.py run <session>` turns each voice note into `"said"` in
    `shots.jsonl`. `live/drafts.jsonl` (if present; later lines per `id` win, `drop` removes) is a small model's rough
    outline made during the recording: a starting point for §3, not authoritative — check every item against the
    transcript.
    Recorded with system audio (`record.py start --system-audio`, or the app's Settings → General toggle — calls, product demos),
    the other side's lines are marked `(system)` / `speaker: "system"`; unmarked lines are the user. Speaker bleed can
    repeat a system line on the mic.
    For Whisper/API backends pass `--prompt` with a short glossary (product, page/module names, people) from the repo.
    Also run `frames.py scan <video>` (visual-activity index for candidates/sheets) and `frames.py marks <video>`: what
    the user drew with the pen — when, where (box), what was being said — plus markers (Windows/Linux Alt+Shift+M; macOS
    recordings made before 1.0.7, with ⌥M).
    A `warning` in the output, or a transcript that reads as nonsense (SenseVoice turns unsupported languages such as
    French into fake English), means the language needs another backend: set it up (`reference/asr.md`) and re-run.
    
    ## 3. Understand → items (text first), open the review page
    
    The user is waiting: write the items as text, open the page, add frames while they read (§4). Read the whole
    transcript. `live/drafts.jsonl` (items drafted during the recording; last line per `id` wins, `op: drop`
    = gone) is your outline if it exists: check each draft against the transcript, merge / split / drop freely. Look at
    frames now only when the words alone can't tell what an item is about. Decide **per item** what it is — the
    recording decides, not a mode switch:
    
    | kind | when | lens |
    |---|---|---|
    | `issue` | something in the product to fix or polish | `reference/lenses/issue.md` |
    | `idea` | something to build / change / borrow ("这个网站的这里好") | `reference/lenses/idea.md` |
    | `note` | an observation / finding / fact worth keeping | `reference/lenses/note.md` |
    | `task` | an action item that isn't a product issue | `reference/lenses/task.md` |
    | custom | a lens in `.blurt/lenses/` or `~/.blurt/lenses/` fits, or the user asked for it | that file |
    
    Principles — judgement, not rules:
    - A pen mark or a snap (screenshot in `shots.jsonl`) is the user saying "this, exactly": it anchors an item's time
      and subject. What they marked outranks your reading of a vague sentence — but a mark is not an item by itself,
      and items without marks are normal. A snap's image is its item's first frame: put it in `frames` right away.
      A snap with a `kind` in `shots.jsonl` (issue / idea / note / task) is one the user typed in its note: its item is
      that kind (ingest keeps it so); without one, decide as usual.
    - One item = one thing. People jump around, revisit, correct themselves ("不对,是…"), or say two things in one
      sentence: merge revisits, drop retracted remarks, split compounds. Pure narration is not an item.
    - Fix ASR errors from context (repo vocabulary, what's on screen). Keep `quote` close to what was said.
    - Don't invent. When unsure, write your best guess, set `confidence: "low"`, add a short `questions` entry.
    - Give the session a `title`. For idea-heavy or exploratory recordings a `digest` (markdown: themes, the strongest
      ideas, how they connect, suggested next step) becomes the page's Overview — it can follow in §4.
    
    **Which project** (only when the session is in the inbox — no current project — and there are projects): the
    app's prompt lists them; in a chat run `projects.py candidates` (each project's id, key, name, description, folder and
    its newest item titles). Where an item clearly belongs to one, add `"project": "<id or key>"` to that item and a few
    words in `"project_reason"`; unsure → leave it out (a `project` at the top of items.json counts only as a suggestion
    for every item, never as where they go). It is only a suggestion: the item stays in Unassigned and the user moves
    it with one click ("Looks like X · Move"). Don't spend tokens on it — what the capture already told you is enough; a
    quick look in a project's folder only if that settles it. With a current project, write no `project`: the items go
    there.
    
    Write `<session>/items.json` (schema: `reference/schema.md`) with `"processing": true` and every item's text, no
    picked frames yet — but a snap's shot goes in its item's `frames` now (nothing to pick). Then `items.py ingest
    <session>` turns it into one Markdown item per entry (any later `items.py` command on the session does it too).
    **Right away** start `review.py <session>` **in the background** (on macOS it opens in the Blurt app's main window when
    the app is installed, else the browser; exits when the user confirms — in either — or after 30 minutes nobody used
    the page, `"reviewed": false` then; prints `REVIEW <url>`, a private local address with a token in it — give the user
    that URL if no browser can open where you run: SSH, container) and tell the user in one short message:
    - ≤ 5 items: the numbered list (kind · title · owner/module, plus `questions`); more: counts by kind, the headline
      items, open questions; screenshots are still filling in;
    - two lines on the page: one item at a time — **A** keep · **X** discard · **J/K** next/prev · **Z** undo · **?** keys;
      fields editable in place; **G** list view; **V** overview; "完成审核" hands it back.
    
    From here on the user edits the same items: change them **only** with `items.py` (`patch`, `set-frames`, `add`,
    `done`; `--help`). Address an item as `<session> I-003` (your draft id), by its id (`BA-12`) or by its uid. It keeps
    fields the user edited, and items they dropped or merged stay gone — never write items.json again, nor the `.md`
    files.
    
    ## 4. Evidence: frames & clips (while the user reviews)
    
    Pick 1–3 frames per item that show *the point* (the bug itself, the part of the page that inspired the idea). Work
    top-down — the user reviews from the top — and write each item as soon as its frames are good:
    `items.py set-frames <session> <id> frames/<id>-1.jpg@T#caption …`.
    - `frames.py sheet <video> --from S --to E -o <session>/sheets/<id>.jpg` → ~6 diverse settled candidates with
      timestamps in one image; `--at t1 t2 …` for specific moments ("这里 / this / look"). Speech often trails the action.
    - The user drew on it (`marks`)? Their ink is in the video: `frames.py grab --at <frame_t>` and the frame is already
      annotated — add no box. `--crop stroke` for a zoomed second frame; `--box stroke` on a frame where the ink is gone.
    - Otherwise, marking the spot — never estimate coordinates from a thumbnail or contact sheet:
      1. `frames.py grid <video> --at T -o <session>/sheets/<id>-grid.jpg` → labelled 0–1 grid plus the recorded cursor
         and nearby clicks (events.jsonl — exact). Read box edges off the grid (`--crop` zooms, labels stay full-frame).
      2. `frames.py grab <video> --at T -o <session>/frames/<id>-1.jpg --box x,y,w,h`; `--ring cursor|click` when the user
         pointed at / clicked it. Tiny detail → second frame with `--crop`.
      3. **Look at every annotated frame before using it.** Box not on the point → fix and re-grab.
    - **Shots** (`shots.jsonl`; later lines per `n` win; skip `"deleted": true`): the shot *is* the frame — use
      `shots/NNN.marked.png` when it exists (the user's ink is already on it, add no box), else `NNN.png`; no picking.
      Its note is `text` / `said` (voice) — plus, for a snap while recording, what was said around its `t` (a snap
      taken with a note while recording: that note is what the item is about; its `project`, when set, is where the
      user wants it). `frames.py
      grab|grid shots/NNN.png` work on stills (`--crop stroke` = around their ink; `--ring cursor` for snaps while
      recording); `frames.py marks <video or session>` lists every snap.
    - Animations / flows / timing: `frames.py clip --from --to -o <session>/clips/<id>.mp4` (< 20 MB) → `patch --set clip=…`.
    - Same pass, via `items.py patch`: `source` (URL / product read off the frame), `code_refs` (issues, inside a repo:
      grep the visible text / route / component → likely `path:line`s), the `digest`, a better `confidence`.
    - **Fan out** if your harness has subagents: split the items into 3–4 contiguous batches and run them all at once.
      Each gets the session dir, the video, its items (id, kind, title, time, quote, the marks / snaps that anchor them),
      this section, and the rule: write only your items, only with `items.py`. They report nothing back.
    - Then `items.py done <session>` (it lists items still without frames — fine where none would help).
      `set-frames` copies the files into the store (`assets/<uid>/`): keep giving paths relative to the session.
    
    ## 5. Review with the user
    
    The page has been open since §3. Wait as in §1 until the review summary prints. The user may finish before the
    frames are in (`"processing": true` in the summary): complete §4 anyway, then `done`. Read the result with
    `items.py list <session>` (each item has one `state`: `todo` = they kept it, `wontfix`, …; `--state discarded` for
    what they threw away; answers are in `answer`; merged items are gone
    on purpose — see the target's `merged_from`; `_user` lists fields the user edited; the user may have moved items to
    another project: ids follow the project, uids never change) and continue with §6. If they reply in chat instead,
    apply it with `items.py … --force` and close the page (`pkill -f "review.py <session>"`).
    
    **Unattended runs** (started by the Blurt app via the first usable agent CLI — `claude -p`, `codex exec`,
    `kimi -p`, `opencode run`, `gemini -p` (scripts/agents.json) — env `BLURT_APP=1` — or any other
    non-interactive mode): don't ask anything; do §2–§4 the same way (text first with `processing`, frames via
    `items.py`, `done`), then finish with a 2–3 sentence summary. The app's runs are sandboxed: the cwd is the session
    folder, the project's code (named in the prompt) is for reading, and the only commands allowed are the scripts as
    `uv run <skill-dir>/scripts/<name>.py …` with the path the prompt gives. With `BLURT_APP=1` don't start the review page — the
    app opens it; otherwise start it detached right after §3 (`nohup uv run …/review.py <session> >/dev/null 2>&1 &`).
    Never export to external systems unattended.
    
    ## 6. Export / act
    
    Ask once where things go (remember in `.blurt/config.json` → `export`). Typical: issues → the team's tracker or
    chat; ideas → an ideas board / doc; notes → Markdown; tasks → the user's todo tool. Blurt writes files only; every
    other destination goes through the tools you already have.
    - Markdown + CSV → `export_local.py <session>` or `export_local.py --project <key>` (all kinds: one document and a
      CSV per kind; the items themselves are Markdown files already).
    - Any other destination — Feishu/Lark, Notion, Google Docs, Linear, Jira, GitHub, Slack, email… → use the CLI, MCP
      server or skill this session already has for it (`reference/export-other.md`); map fields by meaning. No tool for
      it → say so and offer the bundle or Markdown instead; don't install one unasked.
    - **Hand-off bundle**: the portable form for a person or a place that takes files —
      `export_local.py bundle --items <uid|id> … [-o DIR|FILE.zip] [--title "…"]`, or `--project <key> [--state todo …]
      [--kind issue …]` (MCP: `bundle_items`). One `.zip` named `<project>-<date>-<n items>.zip`: `summary.md` (counts by
      kind and state; per item its state, quote, steps, suspected code, screenshots inline), `items/*.md` (every field),
      `frames/`, `clips/` — relative paths only, so it opens on a teammate's machine (paths in the text are made
      relative or cut to the file name; a frame no longer on disk is named as not included). It prints the zip's absolute
      `path` and `summary`, one paragraph
      to post with the file. Grouping is your judgement, not a rule: "Bundle the checkout issues and post them to #web
      in Slack" or "把 A 模块的问题打包发给飞书群里的张三" → find those issues (`search_items` / `index.py search`, read
      their `module`, `owner`, titles), one bundle per group with a `--title` that says who it is for, then send it with
      the user's own tool for that chat. Bundling changes no state.
    - A document rather than a list ("整理成需求文档和设计文档", a brief, a comparison — typically after walking through
      reference sites or a competitor, often across several recordings) → `reference/docs.md`.
    Confirm destination and count before creating or sending anything remotely; report links afterwards. Then offer the
    natural next step — for issues "want me to start fixing these?" (you have `code_refs`), for ideas "want a quick
    prototype / spec of the top one?".
    
    ## 7. Work on items (and say so)
    
    When the user asks you to handle items — "fix these", a Blurt session that started you, a hand-off note
    (`~/.blurt/handoff/H-….md`, see `reference/sessions.md`) — for each item:
    1. Read it in full: MCP `get_item(uid)` (shows the screenshots), or `items.py get <uid|id>` and open the frame files
       (absolute paths). Look at the frames; they are the evidence.
    2. `items.py state <uid> --state in_progress` (MCP: `mark_fixed(uid, state="fixing")`) before you start — the app
       shows it in progress.
    3. Do it, smallest change that does it. Commit only when the hand-off note says so (a worktree session); never push.
    4. `items.py state <uid> --state to_verify --claim fixed --note "<one line: what changed, which files>"`
       (`--commit SHA` / `--pr URL` if there is one; MCP: `mark_fixed(uid, state="fixed", note=…)`); can't or shouldn't →
       `--claim wontfix --note "<why>"`. That is your claim: the item waits in the user's inbox as *to verify* — only
       they mark it done, send it back or decide they won't do it.
    Every item has one `state` (`reference/schema.md`): new → todo → handed_off → in_progress → to_verify → done, plus
    wontfix and discarded. You only ever move items to `in_progress` / `to_verify` (or back to `todo` to take your own
    claim back). `--agent` / `--job` default to `$BLURT_AGENT` / `$BLURT_JOB` (set by the app). The older forms
    `items.py work …` and `items.py fix <session> <id> --state fixing|fixed|wontfix` still work. Never set `state` or
    `work` with `patch`, and never edit item files yourself.
    A hand-off prompt's first line is just its title ("AW-12 · … (Blurt hand-off)"); the instructions follow it. Started
    from Claude desktop with the items in the prompt itself, your reports (steps 2 and 4) are the only progress the user
    sees — make both. Don't run `sessions.py continue-desktop` unless the user asked; without the blurt MCP installed for
    Claude it refuses (`desktop_no_mcp`), and `--without-mcp` is only for when the user agreed to mark the items by hand.
    
    ## 8. Across recordings: index and MCP
    
    Every item on this machine (every project's store, the inbox, v0.6 sessions not migrated yet) is indexed in
    `~/.blurt/index.db` (`index.py`; items.py keeps it fresh). To find things across projects:
    `index.py search "<words>" [--kind issue] [--state todo] [--project <key|folder|inbox>]` → items with uid, id,
    project, path, title, state (Chinese works; 1–2 character words too). `index.py backlog <repo>` writes
    `.blurt/BACKLOG.md` with the open items. `items.py migrate --all` moves v0.6 sessions into the stores (the app does
    it on start).
    
    The same data as MCP tools — server `blurt` (`uv run --script scripts/mcp_server.py`, stdio): `list_projects`,
    `list_sessions`, `search_items`, `get_item` (returns the screenshots as images), `get_transcript`, `update_item`
    (= `items.py patch`, keeps the user's edits), `mark_fixed` (fixing → in progress; fixed / wontfix → to verify),
    `bundle_items` (= `export_local.py bundle`: the hand-off .zip and a paragraph to post with it, §6). The
    plugin registers it for Claude Code; for Codex / Kimi / OpenCode: `agents.py install <id> --mcp` (`--dry-run` first
    shows what changes). Prefer the tools when they are there; they write through items.py like everything else.
    
    Agent sessions inside Blurt (tmux) or in Claude desktop, worktrees, hand-off notes and the jobs the app shows:
    `reference/sessions.md`.
    
    ## 9. Set up models
    
    When the user asks you to set up Blurt's speech and AI models (or Blurt started you with a "Set Up Models" prompt),
    follow `reference/setup-models.md`: `setup.py status` → ask at most three questions (may the voice leave the Mac, may
    the text go to a cloud model, main languages) → `setup.py recommend …` and say the plan → for each key, ask the user
    whether they have one and where, or want to create one (the provider's key page); the user signs in, verifies and
    pays — you never do those → the key goes to `setup.py connect <preset>` on stdin only (never in the chat, on a
    command line or in a log; a file with secrets is read only with the user's OK) → `download`, `use`, `verify` → a
    short summary. Blurt must be running; it tests and stores each key itself and asks the user before the first change
    unless it started you. The same operations are MCP tools (`setup_*`).
    
  • skills/blurt/models/README.md323 B6746c21829b8upstream ↗
    silero_vad.onnx: Silero VAD (MIT, github.com/snakers4/silero-vad), as k2-fsa/sherpa-onnx publishes it
    (github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/silero_vad.onnx, sha256 9e2449e1…1fd6). Shipped so a
    fresh install needs no download before its first dictation; transcribe.py copies it into ~/.blurt/models.
    
  • skills/blurt/scripts/_common.py41 KBed81e5f4d894upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/agents.json25 KB6285c8569446upstream ↗
    {
      "_doc": "Coding-agent CLIs the Blurt app can hand a recording to, headless. Read by agents.py and by the macOS app (BlurtRecorder: Agents.swift; a copy is bundled into Blurt.app/Contents/Resources). Order of `agents` = default fallback order; the user's order is config record.agents. A custom agent can be added in config record.agent_defs with the same fields. status: how to tell whether it is logged in without spending tokens — `args` run with a 15 s timeout, then: json_key (a bool in the first JSON object of the output) > creds_json ({path, keys}: the file exists and every key is empty → logged out) > logged_out (substrings) > ok_if (substrings, exit 0) > configured_if (regex: has credentials, can't verify them) > env_any / files_any (credentials in env / on disk) > otherwise ('logged_out' | 'unknown'). run: the headless command, {prompt} = the task text — only ever unattended (organising a recording or a batch of cards, AI fill-in, drafts) on text taken from the user's screen and voice, so it gets the least privilege that still lets it file items. Its cwd is the session folder (AI fill-in: a scratch folder), never the project; it reads the project's code but writes nothing there. No full-access / yolo modes. claude / gemini: file edits only inside cwd (acceptEdits / auto_edit) and shell commands only the skill scripts an organizing run uses, each pinned to its absolute path ({scripts} = the skill's scripts folder; the prompt names the same path) — no other command, no other script. codex: its workspace-write sandbox, writable = cwd + {writable} (one `--add-dir` each: the inbox store, every project's .blurt store, and a uv cache of its own — env UV_CACHE_DIR, BLURT_NO_INDEX; agents.py sandbox_env); never $BLURT_HOME (config.json, the jobs and their hook settings, index.db) nor the shared uv cache, so nothing it writes runs outside the sandbox later; network stays on because uv resolves the scripts' dependencies. grok: --permission-mode dontAsk (a call no rule allows is refused, never asked: nobody is there to answer), file edits only inside cwd (Edit(./**)) and the same skill scripts, each pinned to its absolute path; --disable-web-search because grok runs web search and fetch without asking in every mode, dontAsk included (a prompt injected through the screen could otherwise send what it reads out in a query), and --no-auto-update; its status is `grok models` (prints \"You are logged in…\", no tokens spent; it needs the network, so when it fails without saying either way, auth.json on disk counts as configured). A project's .blurt/config.json can't define agents (agents.py config). kimi / opencode: their own -p / run defaults (flags not verified). Hand-offs the user starts are interactive.session and keep their own modes. verified: 'run' = a real headless run was checked, 'cli' = flags checked with --help only, 'docs' = from the CLI's documentation. WP2 fields (agents.py detect / command / install): version = args printing the version; events = args appended to run for machine-readable progress + its format; headless: false = never run unattended (skip it in the auto chain); interactive = {url (placeholders {repo_q} {prompt_q}, max_prompt), | terminal (args after the bin, run in the user's terminal; paste_prompt: put the prompt on the clipboard), | copy_prompt}; app = {macos: path} for app-only agents; skill_dir = where the blurt skill is linked; mcp = {args (+check: args that exit 0 when already added) | json: {path, key, value} merged into a config file}, {mcp_server} = absolute path of mcp_server.py, {uv} = uv's absolute path (desktop apps — Cursor, WorkBuddy — start MCP servers without the login shell's PATH). codebuddy / workbuddy / cursor: connect only (skill + MCP; headless: false), paths from their documentation (WorkBuddy: the paths its app reads, ~/.workbuddy/skills and ~/.workbuddy/mcp.json); cursor links into ~/.agents/skills, which Cursor reads like ~/.claude/skills. Paths take ~ and ${VAR:-default}. headless_mcp = args `agents.py command … headless` adds so the run can use the blurt MCP server with no prompts (codex: the whole server via -c, since -c on an unregistered server breaks codex, plus approve mode and the BLURT_* env it forwards); {mcp_server_json} = that path as a JSON/TOML string. interactive.session (K5, sessions.py dispatch: an interactive agent in a tmux window or the app's pty) = {argv: args after the bin ({prompt} = the short hand-off prompt), prompt: 'arg' (default) | 'paste' (pasted into the window once it is up), hooks: 'claude' (--settings <job>.claude-settings.json with per-session hooks) | 'codex' (-c notify + -c hooks.*) | absent (status from the terminal only), mcp: args added when the user hasn't installed the blurt MCP server ({mcp_config_json} = a --mcp-config JSON) or 'headless_mcp' (reuse those args)}. interactive.session.sandboxed: true = the agent's sandbox can't write the repository's .git from a linked worktree (codex workspace-write), so its hand-off says to leave changes uncommitted and sessions.py finish commits them (the sandbox is never widened to .git). The codex session turns off its startup update check (-c check_for_update_on_startup=false): an update prompt at startup is input it must drain while the embedded terminal attaches. interactive.session.thread_name (codex) = how a session's thread gets the hand-off's title once its id is known (the hooks' session id): argv starts the CLI's JSON-RPC app server on stdio (same CODEX_HOME), then initialize → `method` {threadId, name}; best effort (sessions.py name-thread). interactive.desktop (claude) = the agent's desktop app (name: what the app calls it — Go to Claude) as a hand-off target (sessions.py dispatch --runner desktop): url ({folder_q} = the worktree / project folder, {prompt_q} = the hand-off prompt with the note inline, or the short prompt pointing at the note when the url would pass max_url), app = the app's bundle id per platform, open = what Go to Claude opens; continue = moving a running terminal session there (sessions.py continue-desktop): argv after the bin ({session} = the agent's session id), only from min_version and with the auth probe (the status args) saying logged in with a subscription (a non-empty subscription key, or one of auth_methods); the desktop app doesn't get a terminal session's --mcp-config, so without the blurt MCP installed (mcp.check) it is refused unless the user agreed to mark the items by hand (--without-mcp). interactive.waiting_patterns = regexes (or {pattern, hint}: hint 'trust' = the agent's folder-trust question, asked once per folder; 'hooks' = Codex's hook review, asked once — the app explains each in one line, and a session that quits at a trust question never started) on the last 15 lines of the window meaning 'waiting for you' — only used while an agent's hooks aren't reporting (no hooks, hooks not yet live — claude's workspace-trust dialog fires none — or the user didn't trust them) — and for codex always, since its hooks don't cover a tool's questions. interactive.error_patterns = the same, meaning 'it can't work at all and stays open' (claude: not logged in): the session becomes an error with that line as its reason, until the agent's hooks report. Processing (v0.7): models = where the agent's models are listed ({list: [{id, name, cheap}]} | {cache: the CLI's own model cache, format ('codex': a list of {slug, priority, visibility}; 'grok': a map of id → {info: {name, description, hidden}}, never a cheap one — its fast model costs more), config: its config file for the current default, config_key: that setting's TOML key (default `model`; dotted = in a table, grok's models.default)}); model_args = how a model / an effort level is passed ({model}, {effort}); processing = the defaults for unattended organising runs, per kind: recording (organizing a recording: the CLI's own model at low effort) and cards (snap / voice-note batches, AI fill-in, small runs: the cheapest model) — each {model: id | 'cheapest' (the cache's or list's cheap model) | 'default' (the CLI's own setting), effort: level | 'default'}; the v0.8 shape {model, effort} still means both. Overridden per agent and kind by config record.agent_processing.<id>.<kind> (Settings → Agents); usage = how a run reports the tokens it used (args added to the processing run + format | pattern with the number in group 1).",
      "agents": [
        {
          "id": "claude", "name": "Claude Code", "bins": ["claude"], "verified": "run",
          "status": {"args": ["auth", "status", "--json"], "json_key": "loggedIn", "logged_out": ["not logged in", "please run /login"]},
          "run": ["-p", "{prompt}", "--permission-mode", "acceptEdits", "--allowedTools",
                  "Bash(uv run {scripts}/transcribe.py:*)", "Bash(uv run {scripts}/frames.py:*)", "Bash(uv run {scripts}/items.py:*)",
                  "Bash(uv run {scripts}/projects.py list:*)", "Bash(uv run {scripts}/index.py search:*)", "Read", "Glob", "Grep", "mcp__blurt"],
          "fix": {"zh": "在终端运行 claude,然后输入 /login", "en": "run `claude` in a terminal and /login"},
          "install": {"zh": "curl -fsSL https://claude.ai/install.sh | bash", "en": "curl -fsSL https://claude.ai/install.sh | bash"},
          "version": ["--version"], "events": {"args": ["--output-format", "stream-json", "--verbose"], "format": "claude-stream-json"},
          "interactive": {"url": "claude-cli://open?cwd={repo_q}&q={prompt_q}", "max_prompt": 5000,
                          "session": {"argv": ["--", "{prompt}"], "hooks": "claude", "mcp": ["--mcp-config", "{mcp_config_json}"], "verified": "run"},
                          "waiting_patterns": [{"pattern": "Do you trust the files in this folder", "hint": "trust"},
                                               {"pattern": "Yes, (proceed|I trust this folder)", "hint": "trust"}, "Enter to confirm · Esc to (exit|cancel)",
                                               "Select login method"],
                          "error_patterns": ["Not logged in · Please run /login", "Invalid API key · Please run /login", "OAuth token (has expired|revoked)"],
                          "desktop": {"name": "Claude", "url": "claude://code/new?folder={folder_q}&q={prompt_q}", "max_url": 14000,
                                      "app": {"macos": "com.anthropic.claudefordesktop"}, "open": "claude://",
                                      "continue": {"argv": ["--desktop", "--resume", "{session}"], "min_version": "2.1.285",
                                                   "auth": {"json_key": "loggedIn", "subscription_keys": ["subscriptionType"],
                                                            "auth_methods": ["claude.ai", "claudeai", "oauth", "subscription"]}},
                                      "verified": "docs"}},
          "skill_dir": "~/.claude/skills",
          "mcp": {"args": ["mcp", "add", "--scope", "user", "blurt", "--", "uv", "run", "--script", "{mcp_server}"],
                  "check": ["mcp", "get", "blurt"], "verified": "cli"},
          "drafts": true,
          "models": {"list": [{"id": "haiku", "name": "Haiku", "cheap": true}, {"id": "sonnet", "name": "Sonnet"}, {"id": "opus", "name": "Opus"},
                              {"id": "fable", "name": "Fable"}], "verified": "cli"},
          "model_args": {"model": ["--model", "{model}"], "effort": ["--effort", "{effort}"]},
          "processing": {"recording": {"model": "default", "effort": "low"}, "cards": {"model": "cheapest", "effort": "low"}},
          "usage": {"args": ["--output-format", "json"], "format": "claude-json"}
        },
        {
          "id": "codex", "name": "Codex", "bins": ["codex"], "verified": "run",
          "status": {"args": ["login", "status"], "logged_out": ["not logged in"], "ok_if": ["logged in"], "env_any": ["OPENAI_API_KEY"]},
          "run": ["exec", "--skip-git-repo-check", "--sandbox", "workspace-write", "-c", "sandbox_workspace_write.network_access=true", "{writable}", "{prompt}"],
          "fix": {"zh": "在终端运行 codex login", "en": "run `codex login` in a terminal"},
          "install": {"zh": "npm i -g @openai/codex", "en": "npm i -g @openai/codex"},
          "version": ["--version"], "events": {"args": ["--json"], "format": "codex-jsonl"},
          "interactive": {"terminal": ["{prompt}"],
                          "session": {"argv": ["-c", "check_for_update_on_startup=false", "{prompt}"], "hooks": "codex", "mcp": "headless_mcp",
                                      "sandboxed": true, "verified": "run", "min_version": "0.164.0",
                                      "thread_name": {"argv": ["app-server"], "method": "thread/name/set", "verified": "run"}},
                          "waiting_patterns": ["Would you like to (run|make|apply|allow|grant)\\b", "Press enter to confirm or esc to cancel", "enter (confirm|continue) · esc (skip|quit|cancel)",
                                               {"pattern": "Hooks need review", "hint": "hooks"}, {"pattern": "Trust this folder\\?", "hint": "trust"},
                                               "Allow .{1,80}\\? *\\(y\\)",
                                               "enter to submit \\| esc to cancel"]},
          "skill_dir": "~/.agents/skills",
          "mcp": {"args": ["mcp", "add", "blurt", "--", "uv", "run", "--script", "{mcp_server}"], "check": ["mcp", "get", "blurt"],
                  "verified": "run"},
          "headless_mcp": {"args": ["-c", "mcp_servers.blurt.command=\"uv\"",
                                    "-c", "mcp_servers.blurt.args=[\"run\", \"-q\", \"--script\", {mcp_server_json}]",
                                    "-c", "mcp_servers.blurt.env_vars=[\"BLURT_HOME\", \"BLURT_WORKSPACE\", \"BLURT_AGENT\", \"BLURT_RUN_ID\", \"BLURT_JOB\", \"BLURT_PROJECT\"]",
                                    "-c", "mcp_servers.blurt.default_tools_approval_mode=\"approve\""], "verified": "run"},
          "drafts": true,
          "models": {"cache": "${CODEX_HOME:-~/.codex}/models_cache.json", "format": "codex", "config": "${CODEX_HOME:-~/.codex}/config.toml",
                     "verified": "run"},
          "model_args": {"model": ["-m", "{model}"], "effort": ["-c", "model_reasoning_effort=\"{effort}\""]},
          "processing": {"recording": {"model": "default", "effort": "low"}, "cards": {"model": "cheapest", "effort": "low"}},
          "usage": {"pattern": "(?im)^tokens used\\s*:?\\s*([0-9][0-9,.  ]*)$"}
        },
        {
          "id": "kimi", "name": "Kimi Code", "bins": ["kimi"], "verified": "cli",
          "status": {"args": ["provider", "list"], "creds_json": {"path": "~/.kimi-code/credentials/kimi-code.json", "keys": ["access_token", "refresh_token"]},
                     "configured_if": "models=[1-9]", "otherwise": "logged_out"},
          "run": ["-p", "{prompt}"],
          "fix": {"zh": "在终端运行 kimi login", "en": "run `kimi login` in a terminal"},
          "install": {"zh": "见 moonshotai.github.io/kimi-code", "en": "see moonshotai.github.io/kimi-code"},
          "version": ["--version"], "events": {"args": ["--output-format", "stream-json"], "format": "kimi-stream-json"},
          "interactive": {"terminal": [], "paste_prompt": true, "session": {"argv": [], "prompt": "paste", "verified": "docs"},
                          "waiting_patterns": ["\\b(Approve|Allow)\\b.{0,80}\\?", "\\[y/n\\]|\\(y/n\\)|\\[Y/n\\]"]},
          "skill_dir": "~/.agents/skills",
          "mcp": {"json": {"path": "${KIMI_CODE_HOME:-~/.kimi-code}/mcp.json", "key": ["mcpServers", "blurt"],
                           "value": {"command": "uv", "args": ["run", "--script", "{mcp_server}"]}}, "verified": "docs"}
        },
        {
          "id": "opencode", "name": "OpenCode", "bins": ["opencode"], "verified": "cli",
          "status": {"args": ["auth", "list"], "configured_if": "\\b[1-9]\\d* credentials?\\b",
                     "env_any": ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GEMINI_API_KEY", "OPENROUTER_API_KEY", "ZHIPU_API_KEY"],
                     "otherwise": "logged_out"},
          "run": ["run", "{prompt}"],
          "fix": {"zh": "在终端运行 opencode auth login", "en": "run `opencode auth login` in a terminal"},
          "install": {"zh": "curl -fsSL https://opencode.ai/install | bash", "en": "curl -fsSL https://opencode.ai/install | bash"},
          "version": ["--version"], "events": {"args": ["--format", "json"], "format": "opencode-json"},
          "interactive": {"terminal": ["--prompt", "{prompt}"], "session": {"argv": ["--prompt", "{prompt}"], "verified": "docs"},
                          "waiting_patterns": ["Permission required", "\\bAllow (once|always)\\b", "\\[y/n\\]|\\(y/n\\)"]},
          "skill_dir": "~/.config/opencode/skills",
          "mcp": {"json": {"path": "~/.config/opencode/opencode.json", "key": ["mcp", "blurt"],
                           "value": {"type": "local", "command": ["uv", "run", "--script", "{mcp_server}"], "enabled": true}},
                  "verified": "docs"},
          "model_args": {"model": ["--model", "{model}"]}
        },
        {
          "id": "gemini", "name": "Gemini CLI", "bins": ["gemini"], "verified": "docs",
          "status": {"env_any": ["GEMINI_API_KEY", "GOOGLE_API_KEY", "GOOGLE_GENAI_USE_VERTEXAI"], "files_any": ["~/.gemini/oauth_creds.json"],
                     "otherwise": "logged_out"},
          "run": ["-p", "{prompt}", "--approval-mode", "auto_edit", "--allowed-tools",
                  "run_shell_command(uv run {scripts}/transcribe.py)", "run_shell_command(uv run {scripts}/frames.py)",
                  "run_shell_command(uv run {scripts}/items.py)", "run_shell_command(uv run {scripts}/projects.py list)",
                  "run_shell_command(uv run {scripts}/index.py search)"],
          "fix": {"zh": "在终端运行 gemini 登录一次(或设置 GEMINI_API_KEY)", "en": "run `gemini` once in a terminal to sign in (or set GEMINI_API_KEY)"},
          "install": {"zh": "npm i -g @google/gemini-cli", "en": "npm i -g @google/gemini-cli"},
          "version": ["--version"], "events": {"args": ["--output-format", "stream-json"], "format": "gemini-stream-json"},
          "interactive": {"terminal": ["-i", "{prompt}"], "session": {"argv": ["-i", "{prompt}"], "verified": "docs"},
                          "waiting_patterns": ["Allow execution", "Apply this change\\?", "Waiting for user confirmation"]},
          "skill_dir": "~/.agents/skills",
          "mcp": {"args": ["mcp", "add", "--scope", "user", "blurt", "uv", "run", "--script", "{mcp_server}"], "verified": "docs"},
          "model_args": {"model": ["--model", "{model}"]}
        },
        {
          "id": "grok", "name": "Grok", "bins": ["grok"], "verified": "cli",
          "status": {"args": ["models"], "ok_if": ["you are logged in"],
                     "logged_out": ["not logged in", "not signed in", "please log in", "please sign in", "login required"],
                     "env_any": ["XAI_API_KEY"], "files_any": ["${GROK_HOME:-~/.grok}/auth.json"], "otherwise": "logged_out"},
          "run": ["-p", "{prompt}", "--permission-mode", "dontAsk", "--disable-web-search", "--no-auto-update", "--allow", "Edit(./**)",
                  "--allow", "Bash(uv run {scripts}/transcribe.py:*)", "--allow", "Bash(uv run {scripts}/frames.py:*)",
                  "--allow", "Bash(uv run {scripts}/items.py:*)", "--allow", "Bash(uv run {scripts}/projects.py list:*)",
                  "--allow", "Bash(uv run {scripts}/index.py search:*)", "--allow", "MCPTool(blurt__*)"],
          "fix": {"zh": "在终端运行 grok login", "en": "run `grok login` in a terminal"},
          "install": {"zh": "curl -fsSL https://x.ai/cli/install.sh | bash", "en": "curl -fsSL https://x.ai/cli/install.sh | bash"},
          "version": ["--version"], "events": {"args": ["--output-format", "streaming-json"], "format": "grok-streaming-json"},
          "interactive": {"terminal": ["{prompt}"], "session": {"argv": ["{prompt}"], "verified": "cli"},
                          "waiting_patterns": ["\\bAllow once\\b", "\\bReject once\\b"]},
          "skill_dir": "~/.agents/skills",
          "mcp": {"args": ["mcp", "add", "blurt", "--", "uv", "run", "--script", "{mcp_server}"], "verified": "cli"},
          "models": {"cache": "${GROK_HOME:-~/.grok}/models_cache.json", "format": "grok", "config": "${GROK_HOME:-~/.grok}/config.toml",
                     "config_key": "models.default", "verified": "cli"},
          "model_args": {"model": ["-m", "{model}"], "effort": ["--effort", "{effort}"]},
          "processing": {"recording": {"model": "default", "effort": "low"}, "cards": {"model": "cheapest", "effort": "low"}}
        },
        {
          "id": "codebuddy", "name": "CodeBuddy Code", "bins": ["codebuddy"], "verified": "docs",
          "headless": false, "interactive": {"terminal": [], "paste_prompt": true},
          "version": ["--version"],
          "install": {"zh": "npm install -g @tencent-ai/codebuddy-code", "en": "npm install -g @tencent-ai/codebuddy-code"},
          "skill_dir": "~/.codebuddy/skills",
          "mcp": {"json": {"path": "~/.codebuddy/mcp.json", "key": ["mcpServers", "blurt"],
                           "value": {"command": "uv", "args": ["run", "--script", "{mcp_server}"]}}, "verified": "docs"}
        },
        {
          "id": "workbuddy", "name": "WorkBuddy", "bins": [], "app": {"macos": "/Applications/WorkBuddy.app"}, "verified": "docs",
          "headless": false, "interactive": {"copy_prompt": true},
          "install": {"zh": "见 workbuddy.ai", "en": "see workbuddy.ai"},
          "skill_dir": "~/.workbuddy/skills",
          "mcp": {"json": {"path": "~/.workbuddy/mcp.json", "key": ["mcpServers", "blurt"],
                           "value": {"command": "{uv}", "args": ["run", "--script", "{mcp_server}"]}}, "verified": "docs"}
        },
        {
          "id": "cursor", "name": "Cursor", "bins": [], "app": {"macos": "/Applications/Cursor.app"}, "verified": "docs",
          "headless": false, "interactive": {"copy_prompt": true},
          "install": {"zh": "见 cursor.com", "en": "see cursor.com"},
          "skill_dir": "~/.agents/skills",
          "mcp": {"json": {"path": "~/.cursor/mcp.json", "key": ["mcpServers", "blurt"],
                           "value": {"command": "{uv}", "args": ["run", "--script", "{mcp_server}"]}}, "verified": "docs"}
        },
        {
          "id": "zcode", "name": "ZCode", "bins": [], "app": {"macos": "/Applications/ZCode.app"}, "verified": "docs",
          "headless": false, "interactive": {"copy_prompt": true},
          "fix": {"zh": "在 ZCode 设置 → Skills / MCP 里手动导入 blurt", "en": "in ZCode Settings → Skills / MCP, import blurt by hand"},
          "install": {"zh": "见 zcode.z.ai", "en": "see zcode.z.ai"}
        }
      ],
      "failures": [
        {"id": "skill", "short": {"zh": "找不到 skill", "en": "skill not found"},
         "reason": {"zh": "{name} 找不到 blurt skill:在终端运行 record.py build 重新登记一次", "en": "{name} couldn't find the blurt skill: run record.py build once to register it"},
         "patterns": ["unknown skill", "skill[^\\n]{0,40}(not found|not available|not installed|doesn't exist|does not exist)", "no such skill",
                      "SKILL\\.md[^\\n]{0,60}(no such file|not found|does not exist)", "找不到[^\\n]{0,12}skill"]},
        {"id": "model", "short": {"zh": "模型不可用", "en": "model unavailable"},
         "reason": {"zh": "{name} 设置的模型不存在或没有权限:检查它的模型设置", "en": "{name}'s model doesn't exist or isn't allowed: check its model setting"},
         "patterns": ["model[^\\n]{0,60}(not found|does not exist|not available|is not supported|unsupported|not exist)", "(unknown|invalid|unsupported) model",
                      "model_not_found", "no such model", "^\\s*error: not found\\s*$"]},
        {"id": "auth", "short": {"zh": "登录失效", "en": "login expired"},
         "reason": {"zh": "{name} 登录失效了:{fix}", "en": "{name}'s login has expired: {fix}"},
         "patterns": ["not logged in", "please run /login", "(invalid|expired|revoked)[^\\n]{0,30}(api[ _-]?key|token|grant|credential|session)",
                      "authorization grant is invalid", "no credential configured", "authentication (failed|error|required)", "unauthori[sz]ed", "\\b401\\b",
                      "token (has )?expired", "login (required|expired)", "re-?authenticate", "invalid x-api-key"]},
        {"id": "quota", "short": {"zh": "额度 / 限流", "en": "quota / rate limit"},
         "reason": {"zh": "{name} 额度用完了或被限流,过一会儿再整理", "en": "{name} hit a rate limit or ran out of quota — try again later"},
         "patterns": ["rate[ _-]?limit", "\\b429\\b", "quota", "usage limit", "credit balance is too low", "insufficient[_ ]quota",
                      "too many requests", "out of credits", "limit reached", "overloaded", "\\b529\\b"]},
        {"id": "network", "short": {"zh": "网络", "en": "network"},
         "reason": {"zh": "连不上 {name} 的服务:检查网络 / 代理后再整理", "en": "couldn't reach {name}'s servers: check the network / proxy and try again"},
         "patterns": ["enotfound", "econnrefused", "econnreset", "etimedout", "getaddrinfo", "network (error|is unreachable)",
                      "connection (error|refused|reset|timed out|closed)", "could not resolve host", "fetch failed", "unable to connect",
                      "stream disconnected", "certificate verify failed", "(ssl|tls) (error|handshake)",
                      "dns error", "failed to lookup address", "error sending request", "nodename nor servname", "os error 5[01]\\b"]},
        {"id": "outdated", "short": {"zh": "版本太旧", "en": "outdated"},
         "reason": {"zh": "{name} 的版本太旧,没能启动。在终端里更新它,再检查一次:{fix}", "en": "{name} is too old to start here. Update it in a terminal, then check again: {fix}"},
         "patterns": ["timed out discarding buffered terminal input"]}
      ]
    }
    
  • skills/blurt/scripts/agents.py65 KB5bdda7978185upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/asr_models.json14 KB08c1707d1e9fupstream ↗
    {
      "schema": 1,
      "reference": "rtf_ref / ram_mb / error_ref: Apple M5 Max (128 GB, 40-core GPU), 3 threads, utility priority, say-generated speech in 7 languages; docs/design/asr-benchmark.md",
      "models": [
        {"id": "sensevoice-small", "name": "SenseVoice Small", "backend": "sensevoice", "model": "sense-voice-small-int8", "languages": ["zh", "en", "ja", "ko", "yue"], "size_mb": 228, "rtf_ref": 0.0265, "ram_mb": 540, "error_ref": {"zh": 0.033, "en": 0.024, "ja": 0.017, "ko": 0.105}, "platforms": ["darwin-arm64", "darwin-x86_64", "linux-x86_64", "linux-arm64", "windows-x86_64", "windows-arm64"], "requires": [], "streaming": false, "tiers": ["pro", "standard", "light", "minimal"], "verified": "run", "default": true, "install": {"files": {"sensevoice/model.int8.onnx": ["https://huggingface.co/csukuangfj/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/resolve/main/model.int8.onnx", "https://hf-mirror.com/csukuangfj/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/resolve/main/model.int8.onnx"], "sensevoice/tokens.txt": ["https://huggingface.co/csukuangfj/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/resolve/main/tokens.txt", "https://hf-mirror.com/csukuangfj/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/resolve/main/tokens.txt"]}}, "quality_note_key": "speech.note.sensevoice", "vocab": "none"},
        {"id": "whisper-large-v3-turbo-mlx", "name": "Whisper large-v3-turbo", "backend": "mlx-whisper", "model": "mlx-community/whisper-large-v3-turbo", "languages": "multi", "size_mb": 1540, "rtf_ref": 0.0225, "ram_mb": 3670, "error_ref": {"zh": 0.016, "en": 0.019, "ja": 0.008, "ko": 0.005, "es": 0.005, "fr": 0.013, "de": 0.005}, "platforms": ["darwin-arm64"], "requires": ["mlx"], "package": "mlx-whisper", "streaming": false, "tiers": ["pro", "standard"], "verified": "run", "install": {"hf": "mlx-community/whisper-large-v3-turbo"}, "quality_note_key": "speech.note.whisper_turbo", "vocab": "prompt"},
        {"id": "whisper-large-v3-turbo-q4-mlx", "name": "Whisper large-v3-turbo (4-bit)", "backend": "mlx-whisper", "model": "mlx-community/whisper-large-v3-turbo-q4", "languages": "multi", "size_mb": 442, "rtf_ref": 0.0255, "ram_mb": 2570, "error_ref": {"zh": 0.033, "en": 0.019, "ja": 0.015, "ko": 0.008, "es": 0.0, "fr": 0.017, "de": 0.011}, "platforms": ["darwin-arm64"], "requires": ["mlx"], "package": "mlx-whisper", "streaming": false, "tiers": ["standard", "light"], "verified": "run", "install": {"hf": "mlx-community/whisper-large-v3-turbo-q4"}, "quality_note_key": "speech.note.whisper_turbo_q4", "vocab": "prompt"},
        {"id": "qwen3-asr-1.7b-mlx", "name": "Qwen3-ASR 1.7B", "backend": "qwen3-asr-mlx", "model": "mlx-community/Qwen3-ASR-1.7B-8bit", "languages": ["zh", "en", "yue", "ja", "ko", "es", "fr", "de", "it", "pt", "ru", "ar", "hi", "th", "vi", "id", "ms", "tr", "nl", "sv", "da", "fi", "pl", "cs", "el", "hu", "ro", "fa", "fil", "mk"], "size_mb": 2468, "rtf_ref": 0.033, "error_ref": {"zh": 0.007, "en": 0.019, "ja": 0.013, "ko": 0.0, "es": 0.0, "fr": 0.008, "de": 0.011, "yue": 0.364}, "ram_mb": 3100, "platforms": ["darwin-arm64"], "requires": ["mlx"], "package": "mlx-qwen3-asr", "streaming": false, "tiers": ["pro"], "verified": "run", "vocab": "context", "install": {"hf": "mlx-community/Qwen3-ASR-1.7B-8bit"}, "quality_note_key": "speech.note.qwen3_mlx_17", "rehear_window_s": 30, "field_context_chars": 300},
        {"id": "qwen3-asr-0.6b-mlx", "name": "Qwen3-ASR 0.6B", "backend": "qwen3-asr-mlx", "model": "mlx-community/Qwen3-ASR-0.6B-8bit", "languages": ["zh", "en", "yue", "ja", "ko", "es", "fr", "de", "it", "pt", "ru", "ar", "hi", "th", "vi", "id", "ms", "tr", "nl", "sv", "da", "fi", "pl", "cs", "el", "hu", "ro", "fa", "fil", "mk"], "size_mb": 1010, "rtf_ref": 0.029, "error_ref": {"zh": 0.0, "en": 0.019, "ja": 0.017, "ko": 0.005, "es": 0.0, "fr": 0.042, "de": 0.021, "yue": 0.39}, "ram_mb": 2100, "platforms": ["darwin-arm64"], "requires": ["mlx"], "package": "mlx-qwen3-asr", "streaming": false, "tiers": ["pro", "standard"], "verified": "run", "vocab": "context", "install": {"hf": "mlx-community/Qwen3-ASR-0.6B-8bit"}, "quality_note_key": "speech.note.qwen3_mlx", "rehear_window_s": 20},
        {"id": "fun-asr-nano-mlx", "name": "Fun-ASR-Nano", "backend": "fun-asr-mlx", "model": "mlx-community/Fun-ASR-Nano-2512", "languages": ["zh", "en", "ja"], "size_mb": 1598, "rtf_ref": 0.027, "error_ref": {"zh": 0.0, "en": 0.019, "ja": 0.0}, "ram_mb": 2550, "platforms": ["darwin-arm64"], "requires": ["mlx"], "package": "mlx-audio", "streaming": false, "tiers": ["standard", "light"], "verified": "run", "vocab": "hotwords", "install": {"hf": "mlx-community/Fun-ASR-Nano-2512"}, "quality_note_key": "speech.note.fun_asr", "rehear_window_s": 30},
        {"id": "whisper-large-v3-turbo-ct2", "name": "Whisper large-v3-turbo (CUDA)", "backend": "faster-whisper", "model": "mobiuslabsgmbh/faster-whisper-large-v3-turbo", "languages": "multi", "size_mb": 1547, "rtf_ref": null, "ram_mb": 1670, "error_ref": {"zh": 0.023, "en": 0.019, "ja": 0.011, "ko": 0.005, "es": 0.0, "fr": 0.013, "de": 0.005}, "platforms": ["linux-x86_64", "linux-arm64", "windows-x86_64"], "requires": [], "package": "faster-whisper", "streaming": false, "tiers": ["pro", "standard"], "verified": "run", "install": {"hf": "mobiuslabsgmbh/faster-whisper-large-v3-turbo"}, "quality_note_key": "speech.note.whisper_turbo_cpu", "vocab": "prompt"},
        {"id": "qwen3-asr-0.6b-onnx", "name": "Qwen3-ASR 0.6B (CPU)", "backend": "qwen3-asr-onnx", "model": "qwen3-asr-0.6b-int8", "languages": ["zh", "en", "yue", "ja", "ko", "es", "fr", "de", "it", "pt", "ru", "ar", "hi", "th", "vi", "id", "ms", "tr", "nl", "sv", "da", "fi", "pl", "cs", "el", "hu", "ro", "fa", "fil", "mk"], "size_mb": 940, "rtf_ref": 0.2543, "ram_mb": 2570, "error_ref": {"zh": 0.043, "en": 0.01, "ja": 0.053, "ko": 0.133, "es": 0.068, "fr": 0.15, "de": 0.074}, "platforms": ["darwin-arm64", "darwin-x86_64", "linux-x86_64", "linux-arm64", "windows-x86_64", "windows-arm64"], "requires": [], "streaming": false, "tiers": ["pro", "standard", "light"], "verified": "run", "install": {"archive": "https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-qwen3-asr-0.6B-int8-2026-03-25.tar.bz2", "dir": "qwen3-asr-0.6b-int8", "keep": ["conv_frontend.onnx", "encoder.int8.onnx", "decoder.int8.onnx", "tokenizer/"]}, "quality_note_key": "speech.note.qwen3_cpu", "vocab": "hotwords"},
        {"id": "apple-speech", "name": "macOS Speech", "backend": "apple", "model": "SpeechTranscriber", "languages": ["zh", "en", "yue", "ja", "ko", "fr", "de", "es", "it", "pt"], "size_mb": 0, "rtf_ref": 0.014, "ram_mb": 0, "platforms": ["darwin-arm64", "darwin-x86_64"], "requires": ["macos26"], "builtin": true, "streaming": false, "tiers": [], "verified": "run", "quality_note_key": "speech.note.apple", "vocab": "context"},
        {"id": "openai-compatible", "name": "OpenAI-compatible API", "backend": "openai", "model": null, "local": false, "languages": "multi", "size_mb": 0, "ram_mb": 0, "platforms": ["*"], "requires": ["key"], "streaming": false, "tiers": [], "verified": "run", "keys": ["BLURT_ASR_API_KEY", "OPENAI_API_KEY", "GROQ_API_KEY"], "quality_note_key": "speech.note.cloud"},
        {"id": "qwen3-asr-flash", "name": "Qwen3-ASR Flash", "backend": "dashscope", "model": "qwen3-asr-flash", "local": false, "languages": "multi", "size_mb": 0, "ram_mb": 0, "platforms": ["*"], "requires": ["key"], "streaming": false, "tiers": [], "verified": "docs", "keys": ["DASHSCOPE_API_KEY"], "quality_note_key": "speech.note.cloud", "field_context_chars": 300},
        {"id": "volcengine-seedasr", "name": "Doubao Seed-ASR 2.0", "backend": "volcengine", "model": "bigmodel_nostream", "local": false, "languages": "multi", "size_mb": 0, "ram_mb": 0, "platforms": ["*"], "requires": ["key"], "streaming": true, "tiers": [], "verified": "run", "keys": ["VOLC_APP_ID", "VOLC_ACCESS_TOKEN"], "keys_all": true, "vocab": "hotwords", "quality_note_key": "speech.note.cloud", "field_context_chars": 300}
      ],
      "bench": {
        "note": "Spoken with macOS `say` (the first listed voice that exists, else any voice of the locale); error = CER for zh / yue / ja / ko, WER otherwise.",
        "samples": {
          "zh": {
            "voices": [
              "Tingting"
            ],
            "locale": "zh_CN",
            "text": [
              "我先打开登录页面,输入邮箱和密码,然后点击登录按钮。",
              "按钮点了之后没有任何反应,控制台里也没有报错。",
              "设置页里的保存按钮颜色太浅了,看不出来可以点击。",
              "搜索框输入中文的时候,候选词会把下面的结果挡住。",
              "上传头像以后要刷新一下页面才能看到新的图片。",
              "有一个想法,可以加一个导出表格的功能,方便发给运营的同事。",
              "另外记一下,下周三之前要把发布说明写好。"
            ]
          },
          "en": {
            "voices": [
              "Samantha"
            ],
            "locale": "en_US",
            "text": [
              "First I open the sign in page, type my email and password, and press the sign in button.",
              "Nothing happens after the click, and there is no error in the console either.",
              "The save button on the settings page is so pale that it does not look clickable.",
              "When I search for a project, the dropdown covers the first result.",
              "After uploading a new avatar I have to reload the page before it shows up.",
              "One idea: we could add an export to spreadsheet option for the support team.",
              "Also a note to self, the release notes need to be ready before Wednesday."
            ]
          },
          "ja": {
            "voices": [
              "Kyoko"
            ],
            "locale": "ja_JP",
            "text": [
              "まずログイン画面を開いて、メールアドレスとパスワードを入力し、ログインボタンを押します。",
              "ボタンを押しても何も起きず、コンソールにもエラーは出ていません。",
              "設定画面の保存ボタンは色が薄すぎて、押せるように見えません。",
              "プロジェクトを検索すると、候補のリストが最初の結果を隠してしまいます。",
              "新しいアイコンをアップロードした後、ページを再読み込みしないと表示されません。",
              "アイデアとして、サポートチーム向けに表計算ソフトへの書き出し機能があると便利です。",
              "それから、水曜日までにリリースノートを用意する必要があります。"
            ]
          },
          "ko": {
            "voices": [
              "Yuna"
            ],
            "locale": "ko_KR",
            "text": [
              "먼저 로그인 페이지를 열고 이메일과 비밀번호를 입력한 다음 로그인 버튼을 누릅니다.",
              "버튼을 눌러도 아무 반응이 없고 콘솔에도 오류가 없습니다.",
              "설정 페이지의 저장 버튼은 색이 너무 연해서 누를 수 있는 것처럼 보이지 않습니다.",
              "프로젝트를 검색하면 목록이 첫 번째 결과를 가립니다.",
              "새 프로필 사진을 올린 뒤에는 페이지를 새로 고쳐야 보입니다.",
              "아이디어 하나는 지원팀을 위해 스프레드시트로 내보내는 기능을 추가하는 것입니다.",
              "그리고 수요일 전까지 릴리스 노트를 준비해야 합니다."
            ]
          },
          "es": {
            "voices": [
              "Mónica"
            ],
            "locale": "es_ES",
            "text": [
              "Primero abro la página de inicio de sesión, escribo mi correo y mi contraseña y pulso el botón de entrar.",
              "Después del clic no pasa nada y tampoco aparece ningún error en la consola.",
              "El botón de guardar en la página de ajustes es tan claro que no parece que se pueda pulsar.",
              "Cuando busco un proyecto, la lista desplegable tapa el primer resultado.",
              "Después de subir una foto de perfil nueva tengo que recargar la página para verla.",
              "Una idea: podríamos añadir una opción para exportar a una hoja de cálculo para el equipo de soporte.",
              "También tengo que tener listas las notas de la versión antes del miércoles."
            ]
          },
          "fr": {
            "voices": [
              "Thomas"
            ],
            "locale": "fr_FR",
            "text": [
              "D'abord j'ouvre la page de connexion, je saisis mon adresse e-mail et mon mot de passe, puis je clique sur le bouton de connexion.",
              "Il ne se passe rien après le clic et il n'y a aucune erreur dans la console.",
              "Le bouton enregistrer de la page des réglages est si pâle qu'on ne voit pas qu'il est cliquable.",
              "Quand je cherche un projet, la liste déroulante cache le premier résultat.",
              "Après avoir envoyé une nouvelle photo de profil, je dois recharger la page pour la voir.",
              "Une idée : on pourrait ajouter un export vers un tableur pour l'équipe du support.",
              "Et il faut que les notes de version soient prêtes avant mercredi."
            ]
          },
          "de": {
            "voices": [
              "Anna"
            ],
            "locale": "de_DE",
            "text": [
              "Zuerst öffne ich die Anmeldeseite, gebe meine E-Mail-Adresse und mein Passwort ein und klicke auf Anmelden.",
              "Nach dem Klick passiert nichts, und in der Konsole steht auch kein Fehler.",
              "Der Speichern-Knopf auf der Einstellungsseite ist so blass, dass er nicht anklickbar aussieht.",
              "Wenn ich nach einem Projekt suche, verdeckt die Liste das erste Ergebnis.",
              "Nach dem Hochladen eines neuen Profilbilds muss ich die Seite neu laden, bevor es erscheint.",
              "Eine Idee: Wir könnten für das Support-Team einen Export in eine Tabelle anbieten.",
              "Außerdem müssen die Versionshinweise bis Mittwoch fertig sein."
            ]
          },
          "yue": {
            "voices": [
              "Sinji"
            ],
            "locale": "zh_HK",
            "text": [
              "我打開登入頁面,輸入電郵同密碼,然後撳登入掣。",
              "撳完之後乜嘢反應都冇,主控台都冇錯誤。",
              "設定頁嘅儲存掣顏色太淺,睇唔出可以撳。",
              "有個諗法,可以加一個匯出試算表嘅功能,方便支援團隊。"
            ]
          }
        }
      }
    }
    
  • skills/blurt/scripts/captures.py7.9 KBc7462723077fupstream ↗
    # /// script
    # requires-python = ">=3.10"
    # ///
    """Captures → items without an agent (v0.7: no tokens). A capture is a line of a shots batch
    (<store>/sessions/<ts>-shots/shots.jsonl, K7): a still with its typed or spoken note, or a voice input.
    
      captures.py file SESSION --captures N … [--project ID|inbox] [--lang en|zh]
          → {"ok", "items": [{"n","uid","id","title","kind","project","path"}], "skipped": [{"n","reason"}]}
    
    Note = the typed text, then the words said (both when there are both). Title = the note's first sentence; quote = the
    whole note; kind = the type the user gave the snap in its note bubble (shots.jsonl "kind"; kept as theirs, `_user`),
    else a small keyword guess (issue / task / idea, else note); frames = the shot (the inked copy first). Without a
    note the title says what it is ("Screenshot 14:32").
    Every item gets `enrich: pending`: the app's "AI fill-in" later adds code refs, a better kind (never over the one the
    user chose), steps — one agent run for a whole batch (the app's App.enrich). A capture filed before (batch.json
    local id C-NNN) is skipped, so a second call is harmless; so is a deleted one, and one dragged out into another app (`"taken": <wall>` — the app's
    Recently Taken, kept 7 days; put back on its thread it is `"taken": null` and files as usual). Items are written with items.py (the one writer); the project is --project, else the
    capture's own (shots.jsonl), else the batch's store.
    """
    from __future__ import annotations
    
    import argparse
    import json
    import re
    import sys
    import time
    from pathlib import Path
    
    sys.path.insert(0, str(Path(__file__).parent))
    import items  # noqa: E402
    
    TITLE_MAX = 80
    _SENTENCE_END = re.compile(r"[.!?](?=\s|$)|[。!?;;]")
    _TRIM = ".。;;,,::、 "
    
    # Small, deliberately conservative keyword lists: a wrong guess costs the user a click (or AI fill-in fixes it), so
    # anything unclear stays a note. English, Chinese, Japanese, Spanish, French, German.
    _KINDS = [
        ("issue", re.compile(
            r"\b(bugs?|broken|crash\w*|errors?|fail\w*|wrong|glitch\w*|typo|misaligned|overlap\w*|freez\w*|stuck|"
            r"doesn'?t work|does not work|not working|won'?t (load|open|work)|can'?t (click|open|see|load|save)|"
            r"cannot (click|open|see|load|save)|blank page|404|500|fehler|kaputt|erreur|cass[ée]|no funciona|falla)\b"
            r"|报错|错误|崩溃|闪退|卡住|卡死|没反应|不对|失败|错位|重叠|白屏|乱码|不显示|点不了|打不开|加载不出|有问题|バグ|エラー|動かない|落ちる",
            re.I)),
        ("task", re.compile(
            r"\b(todo|to-do|remember to|don'?t forget|need to|needs to|follow up|ask \w+ (about|to)|check with|"
            r"schedule|send \w+ the|à faire|pendiente|erledigen)\b"
            r"|待办|记得|别忘|跟进|要做|回头|问一下|找\S{0,4}确认|安排|やること|忘れずに",
            re.I)),
        ("idea", re.compile(
            r"\b(idea|what if|how about|could we|we could|maybe we|would be (nice|great|cool)|wish|inspiration|"
            r"love (this|how)|like (this|how)|borrow|steal this|idée|idea|ideen?)\b"
            r"|想法|点子|灵感|要是|如果能|不如|可以做|可以加|借鉴|参考一下|这个好|挺好|做得好|アイデア|いいね",
            re.I)),
    ]
    
    
    def guess_kind(text: str | None) -> str:
        t = text or ""
        for kind, pat in _KINDS:
            if pat.search(t):
                return kind
        return "note"
    
    
    def first_sentence(text: str, limit: int = TITLE_MAX) -> str:
        """The note's first sentence (or line), at most `limit` characters, cut at a word where there are words."""
        line = next((ln.strip() for ln in text.strip().splitlines() if ln.strip()), "")
        m = _SENTENCE_END.search(line)
        if m and m.start() >= 3:
            line = line[:m.end()]
        line = line.strip().rstrip(_TRIM).strip() or line.strip()
        if len(line) <= limit:
            return line
        cut = line[:limit - 1]
        sp = cut.rfind(" ")
        if sp > limit * 0.6:
            cut = cut[:sp]
        return cut.rstrip(_TRIM) + "…"
    
    
    LABELS = {"en": {"shot": "Screenshot", "voice": "Voice note"}, "zh": {"shot": "截图", "voice": "语音"}}
    
    
    def _lang(lang: str | None) -> str:
        try:
            import handoff  # noqa: PLC0415
            return handoff.ui_lang(lang)
        except Exception:  # noqa: BLE001 — the title still needs a language
            return "zh" if (lang or "").lower().startswith("zh") else "en"
    
    
    def note_of(line: dict) -> str:
        """A capture's note: what was typed first, then what was said (each once) — as the app's card shows it."""
        typed, said = (s.strip() if isinstance(s, str) else "" for s in (line.get("text"), line.get("said")))
        if typed and said:
            if said in typed:
                return typed
            if typed in said:
                return said
            return f"{typed}\n{said}"
        return typed or said
    
    
    def fields_for(n: int, line: dict, lang: str) -> dict:
        """The items.py `add` fields for one capture."""
        voice = line.get("type") == "voice"
        note = note_of(line)
        if note:
            title = first_sentence(note)
        else:
            wall = line.get("wall") if isinstance(line.get("wall"), (int, float)) else time.time()
            title = f"{LABELS[lang]['voice' if voice else 'shot']} {time.strftime('%H:%M', time.localtime(wall))}"
        # the type the user gave the snap in its note bubble wins; undecided, the keyword guess (AI fill-in can do better)
        f: dict = {"id": f"C-{n:03d}", "kind": items.chosen_kind(line) or guess_kind(note), "title": title,
                   "source": {"type": "voice" if voice else "snap", "shot": n}, "enrich": "pending"}
        if note:
            f["quote"] = note
        frames = [{"path": p} for p in (line.get("marked"), line.get("file")) if isinstance(p, str) and p]
        if frames:
            f["frames"] = frames
        return f
    
    
    def file_captures(session: Path, ns: list[int], project: str | None = None, lang: str | None = None) -> dict:
        session = items.real(session)
        shots = items.read_shots(session)
        lang = _lang(lang)
        made, skipped = [], []
        items.TOUCHED.clear()
        try:
            for n in ns:
                line = shots.get(n)
                if not line:
                    skipped.append({"n": n, "reason": "missing"})
                    continue
                if line.get("deleted"):
                    skipped.append({"n": n, "reason": "deleted"})
                    continue
                if line.get("taken"):  # dragged out into another app (Recently Taken): put back on its thread first
                    skipped.append({"n": n, "reason": "taken"})
                    continue
                b = items.load_batch(session) or {}
                if f"C-{n:03d}" in (b.get("local") or {}):
                    skipped.append({"n": n, "reason": "filed"})
                    continue
                pid = project or (line.get("project") if isinstance(line.get("project"), str) else None)
                f = fields_for(n, line, lang)
                r = items.add(session, pid, f, None)
                made.append({"n": n, "uid": r["uid"], "id": r["id"], "title": f["title"], "kind": f["kind"],
                             "project": pid, "path": r["path"]})
        finally:
            items.reindex_touched()
        return {"ok": True, "items": made, "skipped": skipped}
    
    
    def main(argv: list[str] | None = None) -> None:
        p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
        sub = p.add_subparsers(dest="cmd", required=True)
        s = sub.add_parser("file")
        s.add_argument("session")
        s.add_argument("--captures", nargs="+", type=int, required=True, metavar="N")
        s.add_argument("--project")
        s.add_argument("--lang", choices=["en", "zh", "zh-Hans"])
        a = p.parse_args(argv)
        try:
            out = file_captures(Path(a.session), a.captures, a.project, a.lang)
        except items.ItemsError as e:
            print(json.dumps({"ok": False, "error": str(e), "code": e.code}, ensure_ascii=False), flush=True)
            print(f"ERROR: {e}", file=sys.stderr)
            sys.exit(e.exit_code)
        print(json.dumps(out, ensure_ascii=False), flush=True)
    
    
    if __name__ == "__main__":
        main()
    
  • skills/blurt/scripts/curated_models.json1.8 KB47c6d93ec2ebupstream ↗
    {
      "schema": 1,
      "updated": "2026-10-31",
      "about": "Blurt's measured cloud models for each job, best first (This Mac is always offered too). Settings shows these and nothing else; any other model is a Custom Model. Sources: docs/design/dictation-eval-2026-10-31.md §2.1, §2.3, §6.3; docs/design/first-minute-speech.md §2.5. Served at https://blurt.work/models/curated.json; keep site/models/curated.json the same file.",
      "jobs": {
        "polish": [
          {"service": "dashscope", "model": "deepseek-v4.1-flash", "name": "DeepSeek V4.1 Flash", "regions": ["china", "global"], "note": "polish.ds41", "measured": "2026-10-31"},
          {"service": "deepseek", "model": "deepseek-flash", "name": "DeepSeek V4.1 Flash", "note": "polish.deepseek_flash", "measured": "2026-10-31"}
        ],
        "translate": [
          {"service": "dashscope", "model": "deepseek-v4.1-flash", "name": "DeepSeek V4.1 Flash", "regions": ["china", "global"], "note": "translate.ds41", "measured": "2026-10-29"},
          {"service": "deepseek", "model": "deepseek-flash", "name": "DeepSeek V4.1 Flash", "note": "translate.deepseek_flash", "measured": "2026-10-29"}
        ],
        "vision": [
          {"service": "dashscope", "model": "qwen3.6-flash", "name": "Qwen3.6 Flash", "regions": ["china", "global"], "note": "vision.qwen36", "measured": "2026-10-29"},
          {"service": "deepseek", "model": "deepseek-flash", "name": "DeepSeek V4.1 Flash", "note": "vision.deepseek_flash", "measured": "2026-10-29"}
        ],
        "speech": [
          {"service": "dashscope", "model": "qwen3-asr-flash", "name": "Qwen3-ASR Flash", "regions": ["china", "global"], "note": "speech.qwen3_asr", "measured": "2026-10-31"},
          {"service": "volcengine-speech", "model": "volcengine-seedasr", "name": "Doubao Seed-ASR 2.0", "regions": ["china"], "note": "speech.seedasr", "measured": "2026-10-31"}
        ]
      }
    }
    
  • skills/blurt/scripts/dictation.py9.7 KB808e7f935f6cupstream ↗
    # /// script
    # requires-python = ">=3.10"
    # ///
    """Dictation history ($BLURT_HOME/dictation/history.jsonl, written by the macOS app; docs/design/dictation.md).
    
      dictation.py list [--query TEXT] [--days N] [--limit N]   → {"ok", "entries": [...]} newest first, edits applied
      dictation.py item ID [--project ID|inbox] [--lang en|zh]   turn one dictation into an item (items.py add, no agent)
                                                                → {"ok", "item": {"id","uid","title","kind","project","path"}}
      dictation.py prune [--days N]                             drop entries older than N days (record.dictation.retention_days,
                                                                30), and their audio, and any recording in audio/ no entry
                                                                keeps that is over an hour old → {"ok", "removed"}
    
    The file is append-only: an entry per dictation, then edit lines applied in order —
    {"op":"delete","ids":[…]}, {"op":"item","id","item","uid"}, {"op":"clear"}, {"op":"correct","id","text"} (→ "corrected"). `prune` rewrites it compacted.
    """
    from __future__ import annotations
    
    import argparse
    import fcntl
    import json
    import os
    import re
    import sys
    import time
    from contextlib import contextmanager
    from datetime import datetime, timedelta, timezone
    from pathlib import Path
    
    sys.path.insert(0, str(Path(__file__).parent))
    from _common import BLURT_HOME, global_config  # noqa: E402
    
    DIR = BLURT_HOME / "dictation"
    HISTORY = DIR / "history.jsonl"
    
    
    class Fail(Exception):
        def __init__(self, message: str, code: str = "error"):
            super().__init__(message)
            self.code = code
    
    
    def _time(e: dict) -> datetime:
        try:
            t = datetime.fromisoformat(str(e.get("t")).replace("Z", "+00:00"))
            return t if t.tzinfo else t.replace(tzinfo=timezone.utc)
        except ValueError:
            return datetime.fromtimestamp(0, timezone.utc)
    
    
    def load(path: Path = HISTORY) -> list[dict]:
        """Entries, newest first, with the edit lines applied."""
        order: list[str] = []
        by_id: dict[str, dict] = {}
        try:
            lines = path.read_text(encoding="utf-8").splitlines()
        except OSError:
            return []
        for ln in lines:
            try:
                o = json.loads(ln)
            except ValueError:
                continue
            if not isinstance(o, dict):
                continue
            op = o.get("op")
            if op == "delete":
                for i in o.get("ids") or []:
                    by_id.pop(i, None)
            elif op == "clear":
                by_id, order = {}, []
            elif op == "item":
                if o.get("id") in by_id:
                    by_id[o["id"]].update({"item": o.get("item"), "item_uid": o.get("uid")})
            elif op == "correct":       # the user's corrected text (the Dictation page): kept as "corrected" on the entry
                if o.get("id") in by_id and isinstance(o.get("text"), str):
                    by_id[o["id"]]["corrected"] = o["text"]
            elif isinstance(o.get("id"), str) and isinstance(o.get("raw"), str):
                if o["id"] not in by_id:
                    order.append(o["id"])
                by_id[o["id"]] = o
        return sorted((by_id[i] for i in order if i in by_id), key=_time, reverse=True)
    
    
    @contextmanager
    def exclusive(path: Path = HISTORY):
        """One writer of history.jsonl at a time, with the app (DictationHistory.exclusive): an advisory flock on
        .history.lock beside it, so an append never lands between a prune's read and its rewrite."""
        path.parent.mkdir(parents=True, exist_ok=True)
        fd = os.open(path.parent / ".history.lock", os.O_RDWR | os.O_CREAT, 0o644)
        try:
            fcntl.flock(fd, fcntl.LOCK_EX)
            yield
        finally:
            fcntl.flock(fd, fcntl.LOCK_UN)
            os.close(fd)
    
    
    def append(o: dict, path: Path = HISTORY) -> None:
        with exclusive(path):
            fd = os.open(path, os.O_WRONLY | os.O_APPEND | os.O_CREAT, 0o644)
            try:
                os.write(fd, (json.dumps(o, ensure_ascii=False, sort_keys=True) + "\n").encode())
            finally:
                os.close(fd)
    
    
    def retention_days() -> int:
        d = ((global_config().get("record") or {}).get("dictation") or {}).get("retention_days")
        return int(d) if isinstance(d, (int, float)) and d >= 0 else 30
    
    
    TAKE_WAV = re.compile(r"d-\d{8}-\d{6}-[0-9a-f]{4}\.wav")
    
    
    def prune_orphan_audio(entries: list[dict], path: Path = HISTORY, min_age: float = 3600,
                           now: float | None = None, days: int | None = None) -> list[str]:
        """Recordings in audio/ (a take's <id>.wav, its streamed pieces <id>-pN.wav) that no entry keeps: a take dropped,
        cancelled, or cut short by a quit or a crash. Only those older than `min_age` (a take still running is never that
        old); the Lab's own recordings (dictation/lab/) are not here. A take's own recording younger than `days` stays:
        the app turns it into an "interrupted" entry with Retry (DictationHistory.recoverInterrupted). The same rule as
        DictationHistory.pruneOrphanAudio."""
        audio = path.parent / "audio"
        if not audio.is_dir():
            return []
        kept = {(path.parent / e["audio"]).resolve() for e in entries if isinstance(e.get("audio"), str)}
        now = now or time.time()
        gone = []
        for f in sorted(audio.glob("*.wav")):
            age = now - f.stat().st_mtime
            if f.resolve() in kept or age < min_age:
                continue
            if days is not None and TAKE_WAV.fullmatch(f.name) and age < days * 86400:
                continue
            f.unlink(missing_ok=True)
            gone.append(f.name)
        return gone
    
    
    def prune(days: int, path: Path = HISTORY, now: datetime | None = None) -> int:
        with exclusive(path):
            return _prune(days, path, now)
    
    
    def _prune(days: int, path: Path, now: datetime | None) -> int:
        entries = load(path)
        prune_orphan_audio(entries, path, days=days)
        if not path.exists():
            return 0
        cutoff = (now or datetime.now(timezone.utc)) - timedelta(days=days)
        keep = [e for e in entries if _time(e) >= cutoff]
        drop = [e for e in entries if _time(e) < cutoff]
        for e in drop:
            if isinstance(e.get("audio"), str):
                (path.parent / e["audio"]).unlink(missing_ok=True)
        out = []
        for e in reversed(keep):
            extra = {k: e.pop(k) for k in ("item", "item_uid") if k in e}
            out.append(json.dumps(e, ensure_ascii=False, sort_keys=True))
            if extra.get("item"):
                out.append(json.dumps({"op": "item", "id": e["id"], "item": extra["item"], "uid": extra.get("item_uid")},
                                      ensure_ascii=False, sort_keys=True))
        tmp = path.with_suffix(".tmp")
        tmp.write_text("".join(x + "\n" for x in out), encoding="utf-8")
        os.replace(tmp, path)
        return len(drop)
    
    
    def make_item(entry_id: str, project: str | None, lang: str | None) -> dict:
        """A dictation → an item, the way a captured note becomes one (captures.py): title = its first sentence,
        quote = the whole text, kind = the keyword guess, enrich: pending (AI fill-in can add the rest)."""
        import captures  # noqa: PLC0415
        import items  # noqa: PLC0415
        e = next((x for x in load() if x["id"] == entry_id), None)
        if not e:
            raise Fail(f"no dictation {entry_id}", "not_found")
        if e.get("item"):
            return {"id": e["item"], "uid": e.get("item_uid"), "already": True}
        text = (e.get("corrected") or e.get("text") or e.get("raw") or "").strip()
        if not text:
            raise Fail("this dictation has no text", "empty")
        fields = {"kind": captures.guess_kind(text), "title": captures.first_sentence(text), "quote": text,
                  "source": {"type": "dictation", "app": e.get("app"), "dictation": e["id"]}, "enrich": "pending"}
        if e.get("language"):
            fields["language"] = e["language"]
        pid = items.resolve_project(project or "inbox")
        r = items.add(None, pid, fields, None)
        items.reindex_touched()
        append({"op": "item", "id": e["id"], "item": r["id"], "uid": r["uid"],
                "t": datetime.now().astimezone().isoformat(timespec="seconds")})
        return {"id": r["id"], "uid": r["uid"], "title": fields["title"], "kind": fields["kind"], "project": pid, "path": r["path"]}
    
    
    def main(argv: list[str] | None = None) -> None:
        p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
        sub = p.add_subparsers(dest="cmd", required=True)
        s = sub.add_parser("list")
        s.add_argument("--query")
        s.add_argument("--days", type=float)
        s.add_argument("--limit", type=int, default=0)
        s = sub.add_parser("item")
        s.add_argument("id")
        s.add_argument("--project")
        s.add_argument("--lang", choices=["en", "zh", "zh-Hans"])
        s = sub.add_parser("prune")
        s.add_argument("--days", type=int)
        a = p.parse_args(argv)
        try:
            if a.cmd == "list":
                es = load()
                if a.days is not None:
                    cut = datetime.now(timezone.utc) - timedelta(days=a.days)
                    es = [e for e in es if _time(e) >= cut]
                if a.query:
                    q = a.query.lower()
                    es = [e for e in es if any(q in str(e.get(k) or "").lower() for k in ("text", "raw", "polished", "app"))]
                out = {"ok": True, "entries": es[:a.limit] if a.limit else es}
            elif a.cmd == "item":
                out = {"ok": True, "item": make_item(a.id, a.project, a.lang)}
            else:
                out = {"ok": True, "removed": prune(a.days if a.days is not None else retention_days())}
        except Fail as e:
            out = {"ok": False, "error": str(e), "code": e.code}
        except Exception as e:  # noqa: BLE001 — items.py's own errors carry a code
            out = {"ok": False, "error": str(e), "code": getattr(e, "code", "error")}
        print(json.dumps(out, ensure_ascii=False), flush=True)
        if not out.get("ok"):
            sys.exit(1)
    
    
    if __name__ == "__main__":
        main()
    
  • skills/blurt/scripts/dictation_lab.py90 KBca0dfc4868b0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/dictation_stitch.py14 KB1c4ca9908650upstream ↗
    """BlurtCore's DictationStitch and DictationStream's polish windows, ported (docs/design/dictation.md §Streaming), so
    the Dictation Lab can replay a take the way the app polishes it while the user talks (`mode=windows`): each piece
    polished in a window with the clause carried from the window before, what polish wrote up to its last comma or
    sentence end committed, the dictation's text so far sent along as context, and the joined result checked once more
    against the joined transcript — and, for Structured, the one structuring pass at key-up. The same rules as the Swift
    code (DictationStitchTests; tests/test_dictation_stitch.py holds the shared cases): keep the two in step.
    
    Mechanical only (§Rules vs model): pauses, punctuation marks, scripts and units. Whether a sentence is complete,
    whether a word is a name, whether a piece corrects the one before — that is the polish model's call, made over a
    window that spans the seam.
    """
    from __future__ import annotations
    
    import re
    
    LONG_PAUSE = 1.2
    _TRAILING = set("。.!?!?,,、;;…")
    _TERMINATORS = set("。.!?!?")
    _CLOSERS = set("”’\"'」』))")
    _CLAUSE = set(",;、:,;:")
    
    
    def _is_cjk(c: str) -> bool:
        o = ord(c)
        return 0x3400 <= o <= 0x4DBF or 0x4E00 <= o <= 0x9FFF or 0xF900 <= o <= 0xFAFF or 0x3040 <= o <= 0x30FF \
            or 0xAC00 <= o <= 0xD7AF
    
    
    def _word_char(c: str) -> bool:
        return not _is_cjk(c) and (c.isalpha() or c.isdigit()) and not c.isspace()
    
    
    def units(text: str) -> list[str]:
        """Each CJK character, each Latin word (lower case, an apostrophe inside), each digit."""
        out, i, t = [], 0, text or ""
        while i < len(t):
            c = t[i]
            if _is_cjk(c) or c.isdigit():
                out.append(c.lower())
                i += 1
            elif c.isalpha():
                j = i + 1
                while j < len(t) and t[j].isalpha() and not _is_cjk(t[j]):
                    j += 1
                if j < len(t) - 1 and t[j] in "'’" and t[j + 1].isalpha() and not _is_cjk(t[j + 1]):
                    j += 1
                    while j < len(t) and t[j].isalpha() and not _is_cjk(t[j]):
                        j += 1
                out.append(t[i:j].lower())
                i = j
            else:
                i += 1
        return out
    
    
    def join(a: str, b: str) -> str:
        """A space only where two Latin words (or numbers) meet."""
        a, b = (a or "").strip(), (b or "").strip()
        if not a:
            return b
        if not b:
            return a
        latin_end = _word_char(a[-1]) or a[-1] in ".,;:!?)”\""
        return a + (" " if latin_end and _word_char(b[0]) else "") + b
    
    
    def join_all(parts: list[str]) -> str:
        out = ""
        for p in parts:
            out = join(out, p)
        return out
    
    
    def split_trailing(text: str) -> tuple[str, str]:
        core, end = (text or "").strip(), ""
        while core and (core[-1] in _TRAILING or core[-1].isspace()):
            if not core[-1].isspace():
                end = core[-1] + end
            core = core[:-1]
        return core, end
    
    
    def ends_cjk(text: str) -> bool:
        for c in reversed(text or ""):
            if c.isalpha() or c.isdigit():
                return _is_cjk(c)
        return False
    
    
    def terminator(end: str, after: str) -> str:
        c = next((x for x in end if x in _TERMINATORS), ".")
        cjk = ends_cjk(after)
        if c in "!!":
            return "!" if cjk else "!"
        if c in "??":
            return "?" if cjk else "?"
        return "。" if cjk else "."
    
    
    def mark(end: str, after: str) -> str:
        """The one mark a pause leaves: the sentence end in `end` (in the text's script), else its comma / semicolon."""
        if any(c in _TERMINATORS for c in end):
            return terminator(end, after)
        if not end:
            return ""
        if any(c in ";;" for c in end):
            return ";" if ends_cjk(after) else ";"
        return "," if ends_cjk(after) else ","
    
    
    def seam(previous: str, nxt: str, pause: float | None) -> tuple[str, str]:
        """A pause between two pieces: after a long one (≥ LONG_PAUSE) the speech model's own mark stays; after a breath
        (or one not known) it goes — a cut is not a sentence end. Case and words are left as they are."""
        core, end = split_trailing(previous)
        if not core:
            return previous, nxt
        if pause is not None and pause >= LONG_PAUSE and end:
            return core + mark(end, core), nxt
        return core, nxt
    
    
    def fallback_seam(previous: str, nxt: str, pause: float | None, heard_end: str) -> str:
        """Where a polish window fell back to the transcript: the seam still gets its punctuation — a long pause the seam
        rule's, and a short one a comma where the speech model heard a boundary (it ended the piece with a mark). Without
        it the take read 「…跑完第二,…」."""
        core, end = split_trailing(previous)
        if not core:
            return join(previous, nxt)
        if pause is not None and pause >= LONG_PAUSE:
            a, b = seam(core + (end or heard_end), nxt, pause)
            return join(a, b)
        if end or heard_end:
            return core + ("," if ends_cjk(core) else ", ") + (nxt or "").lstrip()
        return join(core, nxt)
    
    
    def join_pieces(texts: list[str], pauses: list[float | None], fallback: bool = False) -> str:
        """The pieces' texts with the seam rule; `fallback`: with fallback_seam (a comma where a boundary was heard)."""
        acc, after, after_end = "", None, ""
        for i, t in enumerate(texts):
            pause = pauses[i] if i < len(pauses) else None
            t = (t or "").strip()
            if not t:
                if after is not None and pause is not None:
                    after += pause
                continue
            if not acc:
                acc = t
            elif fallback:
                acc = fallback_seam(acc, t, after, after_end)
            else:
                a, b = seam(acc, t, after)
                acc = join(a, b)
            after, after_end = pause, split_trailing(t)[1]
        return acc
    
    
    def commit_split(text: str, at_clauses: bool = True) -> tuple[str, str, str]:
        core, end = split_trailing(text)
        clause = _CLAUSE if at_clauses else set()
        cut, i = None, 0
        while i < len(core):
            c = core[i]
            j = i + 1
            if c in _TERMINATORS or c in clause:
                while j < len(core) and core[j] in _CLOSERS:
                    j += 1
                latin = c.isascii()
                if not latin or j == len(core) or core[j].isspace() or _is_cjk(core[j]):
                    cut = j
            i = j
        ce = cut if cut is not None else 0
        return core[:ce].strip(), core[ce:].strip(), end
    
    
    def window_input(carry: str, piece: str) -> str:
        """DictationStitch.windowInput: a polish window's input — the clause carried from the window before + the new
        piece, with no mark between them whatever the pause. After a long pause the carried clause used to go in with the
        mark the window before ended it with (polish's own, written without seeing what came next: nearly always 。), and
        the model kept a mark there — 5 of 15 mid-clause thinking pauses ≥ 1.2 s at a cut on real spontaneous speech,
        none without it. Whether the clause ended is the model's call from the words (docs/design/dictation.md §Thinking
        pauses on real speech)."""
        return join(carry, piece)
    
    
    def keep_case(inp: str, out: str) -> str:
        """The output with the case its first word had in the input (polish capitalises a window that continues a
        sentence: "update the docs" → "Update the docs")."""
        i, o = (inp or "").strip(), (out or "").strip()
        if not i or not o or not i[0].islower() or not o[0].isupper() or not _word_char(o[0]):
            return out
        wi = re.match(r"[^\W\d_'’]+(?:['’][^\W\d_]+)*", i)
        wo = re.match(r"[^\W\d_'’]+(?:['’][^\W\d_]+)*", o)
        if not wi or not wo or len(wi.group()) != len(wo.group()) or wi.group()[1:] != wo.group()[1:] \
                or wi.group().lower() != wo.group().lower():
            return out
        return wi.group() + o[len(wo.group()):]
    
    
    def mid_sentence(text: str) -> bool:
        t = (text or "").rstrip()
        return bool(t) and t[-1] not in _TERMINATORS and t[-1] not in "\n"
    
    
    # ------------------------------------------------------------------ hearing again (DictationStitch.rehearGroups)
    REHEAR_WINDOW = 10.0     # s (the Lab on the user's 22 takes: 10 s kept the gain of 15 s at a third of the wait at key-up)
    
    
    def rehear_window(model_id: str | None, configured=None) -> float:
        """The window a model hears again in (ModelCatalog.rehearWindow): record.dictation.rehear_window_s when it is set,
        else the model's `rehear_window_s` in asr_models.json (Qwen3-ASR 1.7B 30 s, 0.6B 20 s: measured on 43 of the
        founder's takes, docs/design/dictation.md §Hearing again), else REHEAR_WINDOW. Never under 4 s, as the app."""
        try:
            if configured is not None and float(configured) > 0:
                return max(4.0, float(configured))
        except (TypeError, ValueError):
            pass
        import json  # noqa: PLC0415
        from pathlib import Path  # noqa: PLC0415
        try:
            models = json.loads((Path(__file__).resolve().parent / "asr_models.json").read_text(encoding="utf-8"))["models"]
        except (OSError, ValueError, KeyError):
            return REHEAR_WINDOW
        m = next((m for m in models if m.get("id") == model_id), None) or {}
        w = m.get("rehear_window_s")
        return max(4.0, float(w)) if isinstance(w, (int, float)) and w > 0 else REHEAR_WINDOW
    
    
    def rehear_groups(pieces: list[dict], window: float = REHEAR_WINDOW) -> list[list[int]]:
        """The windows a take is heard again in (docs/design/dictation.md §Hearing again): piece indices, a window closing
        after a piece once it spans `window` seconds and the next piece didn't begin at a hard cut. pieces: [{"start",
        "end", "hard"}] (seconds; `hard`: the piece began at a hard cut)."""
        out, first = [], 0
        for i in range(len(pieces) - 1):
            if not pieces[i + 1].get("hard") and pieces[i]["end"] - pieces[first]["start"] >= window:
                out.append(list(range(first, i + 1)))
                first = i + 1
        if first < len(pieces):
            out.append(list(range(first, len(pieces))))
        return out
    
    
    # ------------------------------------------------------------------ DictationStream's polish windows
    def stream(pieces: list[dict], pauses: list[float | None], polish, guard=None, context: dict | None = None,
               structure=None) -> dict:
        """Replay the app's windows. pieces: [{"raw"}] (the transcript of each piece, in order; the last one is where the
        dictation ended); polish(text, context) → polish.py's answer; guard(raw, output, fallback) → the stitch guard's.
        `structure(text)` (Structured): at key-up, Light's stitched text goes through the structuring pass once; a refusal
        types Light's text. → {"text", "raw", "light", "pieces": [{"polish_in", "text", "fell_back", "reason", "ms",
        "echo"}], "fell_back", "reason", "stitch", "structured": None | {…, "input"}}."""
        carry = carry_end = ""
        out_pieces: list[dict] = []
        n = len(pieces)
        for idx, p in enumerate(pieces):
            raw = (p.get("raw") or "").strip()
            rec = {"index": idx, "raw": raw, "text": "", "polish_in": "", "fell_back": False, "reason": None, "ms": 0}
            out_pieces.append(rec)
            if not raw:
                continue
            last = idx == n - 1
            core = raw if last else split_trailing(raw)[0]
            g_prev = pauses[idx - 1] if idx > 0 and idx - 1 < len(pauses) else None
            # the carried clause goes into this window with no mark, however long the pause (window_input)
            carry_in = carry
            carry = carry_end = ""
            inp = window_input(carry_in, core)
            so = join_all([q["text"] for q in out_pieces[:idx]])
            ctx = dict(context or {})
            if so:
                ctx["dictated"] = so
            rec["polish_in"] = inp
            r = polish(inp, ctx or None) or {}
            rec.update(fell_back=bool(r.get("fell_back")), reason=r.get("reason") if r.get("fell_back") else None,
                       ms=int(r.get("ms") or 0), echo=r.get("echo"), polished=r.get("polished"))
            out = (r.get("text") or "").strip() or inp
            if rec["fell_back"] and carry_in and out.startswith(carry_in):
                # the window fell back: its seam (the pause before this piece) still gets its punctuation
                heard = split_trailing(pieces[idx - 1].get("raw") or "")[1] if idx > 0 else ""
                out = fallback_seam(carry_in, out[len(carry_in):], g_prev, heard)
            if mid_sentence(so) and not rec["fell_back"]:
                out = keep_case(inp, out)          # the model capitalised a window that continues a sentence
            if last:
                rec["text"] = out
            else:
                commit, carry, carry_end = commit_split(out)
                rec["text"] = commit
        raw_all = join_pieces([p.get("raw") or "" for p in pieces], pauses)
        text = join_all([q["text"] for q in out_pieces] + [carry + carry_end])
        fell = [q for q in out_pieces if q["fell_back"]]
        res = {"text": text or raw_all, "raw": raw_all, "pieces": out_pieces, "fell_back": bool(fell),
               "reason": fell[0]["reason"] if fell else None, "stitch": None, "structured": None}
        if guard is not None and len(pieces) > 1 and any(q["polish_in"] for q in out_pieces) and text != raw_all:
            fb = join_pieces([p.get("raw") or "" for p in pieces], pauses, fallback=True)
            g = guard(raw_all, text, fb) or {}
            if g.get("pass") is False:
                res.update(text=g.get("fallback_text") or fb or raw_all, fell_back=True,
                           reason="stitch_" + str(g.get("reason") or "guard"), stitch=g.get("reason") or "guard")
        res["light"] = res["text"]
        if structure is not None and res["text"].strip():
            s = structure(res["text"]) or {}
            res["structured"] = {**s, "input": res["text"]}
            if not s.get("fell_back") and (s.get("text") or "").strip():
                res["text"] = s["text"].strip()
        return res
    
  • skills/blurt/scripts/dictation_styles.py7.5 KBf63e9f211126upstream ↗
    #!/usr/bin/env python3
    # /// script
    # requires-python = ">=3.10"
    # ///
    """Dictation styles for tools outside the app (docs/design/dictation-styles.md §3): which text a style types, the
    category variants of light and the app → category table — the same rules as BlurtCore's DictationStyle.swift. Whether
    and how a take is structured is the polish model's call (polish.py `structure`); this only picks the text and applies
    the variant. The Dictation Lab (dictation_lab.py) re-runs a case per style with these.
    
      dictation_styles.py style --text "…" --style structured [--category chat] [--structured "…"]
    """
    from __future__ import annotations
    
    import argparse
    import json
    import re
    import sys
    import unicodedata
    
    STYLES = ("verbatim", "light", "structured", "auto")
    CATEGORIES = ("chat", "code", "mail", "notes", "other")
    _LIST_LINE = re.compile(r"^[ \t]*(?:[0-9]{1,2}(?:[.))](?![0-9])|、)|[-•*](?=[ \t]))[ \t]*\S", re.M)
    
    
    def _cjk(c: str) -> bool:
        o = ord(c)
        return 0x3400 <= o <= 0x4DBF or 0x4E00 <= o <= 0x9FFF or 0xF900 <= o <= 0xFAFF or 0x3040 <= o <= 0x30FF or 0xAC00 <= o <= 0xD7AF
    
    
    def list_lines(text: str) -> int:
        """The numbered or bulleted lines in a text ("1. …", "- …" at a line start): what Light must never produce, and
        what tells a structured text that is a list from one that stayed a paragraph."""
        return len(_LIST_LINE.findall(text or ""))
    
    
    # ------------------------------------------------------------------ the category variants (DictationStyleVariant)
    def drop_final_stop(text: str) -> str | None:
        t = (text or "").strip()
        if not t or t[-1] not in "。." or t.endswith("...") or t.endswith("。。"):
            return None
        body = t[:-1]
        for i, c in enumerate(body):
            if c in "。!?!?\n":
                return None
            if c == "." and i + 1 < len(body) and body[i + 1] == " ":
                return None
        return body
    
    
    def one_line(text: str) -> str:
        out = ""
        for part in [p.strip() for p in (text or "").split("\n") if p.strip()]:
            if out:
                a, b = out[-1], part[0]
                out += "" if _cjk(a) or _cjk(b) or a in ",。!?:;、" else " "
            out += part
        return out
    
    
    def variant(text: str, category: str, structured: bool) -> tuple[str, list[str]]:
        t, changes = text or "", []
        if category == "chat":
            s = re.sub(r"\n[ \t]*\n[\n \t]*", "\n", t)
            if s != t:
                t = s
                changes.append("blank_lines")
            if not structured and (d := drop_final_stop(t)) is not None:
                t = d
                changes.append("final_stop")
        elif category == "code" and not structured and "\n" in t:
            t = one_line(t)
            changes.append("one_line")
        return t, changes
    
    
    def apply_style(text: str, style: str, category: str = "other", structured: str | None = None) -> dict:
        """The text in `style` for an app of `category`. `structured`: the structuring pass's text for this take (the
        polish model's: polish.py `structure`) — Structured types it; Auto only when it is a list (≥ 2 list lines), else
        Light's text. Then the category's variant. → {"text", "rendered" (a list was typed), "variant"}."""
        if style not in STYLES:
            raise ValueError(f"unknown style {style!r}")
        out, rendered = text or "", False
        if style in ("structured", "auto") and structured is not None and structured.strip():
            if style == "structured" or list_lines(structured) >= 2:
                out = structured.strip()
                rendered = list_lines(out) >= 2
        out, changes = variant(out, category, rendered)
        return {"text": out, "rendered": rendered, "variant": changes}
    
    
    # ------------------------------------------------------------------ app → category (DictationAppCategory)
    _EXACT = {
        **dict.fromkeys(["com.tencent.xinwechat", "com.tencent.qq", "com.tinyspeck.slackmacgap", "ru.keepcoder.telegram",
                         "com.apple.mobilesms", "com.hnc.discord", "net.whatsapp.whatsapp", "com.alibaba.dingtalkmac",
                         "com.microsoft.teams2", "com.microsoft.teams", "org.whispersystems.signal-desktop", "com.facebook.archon",
                         "com.electron.lark", "com.bytedance.lark", "com.tencent.wework"], "chat"),
        **dict.fromkeys(["com.apple.terminal", "com.googlecode.iterm2", "dev.warp.warp-stable", "com.microsoft.vscode",
                         "com.microsoft.vscodeinsiders", "com.todesktop.230313mzl4w4u92", "com.apple.dt.xcode",
                         "com.mitchellh.ghostty", "net.kovidgoyal.kitty", "org.alacritty", "io.alacritty", "co.zeit.hyper",
                         "dev.zed.zed", "com.exafunction.windsurf", "com.sublimetext.4", "com.github.wez.wezterm",
                         "com.panic.nova", "com.barebones.bbedit"], "code"),
        **dict.fromkeys(["com.apple.mail", "com.microsoft.outlook", "com.readdle.smartemail-mac", "com.superhuman.electron",
                         "it.bloop.airmail2", "com.mimestream.mimestream", "com.netease.mailmaster"], "mail"),
        **dict.fromkeys(["com.apple.notes", "md.obsidian", "notion.id", "net.shinyfrog.bear", "com.apple.iwork.pages",
                         "com.microsoft.word", "com.lukilabs.lukiapp", "com.logseq.logseq", "abnerworks.typora",
                         "com.ulyssesapp.mac", "com.apple.textedit", "com.evernote.evernote", "com.youdao.note.youdaonotemac"], "notes"),
    }
    _PARTS = [
        ("chat", {"wechat", "xinwechat", "feishu", "lark", "dingtalk", "slack", "telegram", "discord", "whatsapp", "signal",
                  "teams", "wework", "messenger"}),
        ("code", {"terminal", "iterm2", "warp", "vscode", "jetbrains", "xcode", "ghostty", "kitty", "alacritty", "zed",
                  "windsurf", "sublimetext", "wezterm"}),
        ("mail", {"mail", "outlook", "mailmaster", "airmail", "spark", "superhuman"}),
        ("notes", {"notes", "obsidian", "notion", "bear", "logseq", "typora", "ulysses", "evernote", "craft"}),
    ]
    
    
    _TERMINALS = {"com.apple.terminal", "com.googlecode.iterm2", "dev.warp.warp-stable", "com.mitchellh.ghostty",
                  "net.kovidgoyal.kitty", "org.alacritty", "io.alacritty", "co.zeit.hyper", "com.github.wez.wezterm"}
    _TERMINAL_PARTS = {"terminal", "iterm2", "warp", "ghostty", "kitty", "alacritty", "wezterm"}
    
    
    def is_terminal(bundle: str | None) -> bool:
        """A terminal (DictationAppCategory.isTerminal): polish's "terminal" variant — a command said there stays a command."""
        b = (bundle or "").lower()
        return bool(b) and (b in _TERMINALS or bool(set(re.split(r"[.\-_]", b)) & _TERMINAL_PARTS))
    
    
    def category(bundle: str | None) -> str:
        b = (bundle or "").lower()
        if not b:
            return "other"
        if b in _EXACT:
            return _EXACT[b]
        parts = set(re.split(r"[.\-_]", b))
        for c, names in _PARTS:
            if parts & names:
                return c
        return "other"
    
    
    def main() -> None:
        ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
        sub = ap.add_subparsers(dest="cmd", required=True)
        s = sub.add_parser("style")
        s.add_argument("--text", required=True)
        s.add_argument("--style", choices=STYLES, required=True)
        s.add_argument("--category", choices=CATEGORIES, default="other")
        s.add_argument("--structured", help="the structuring pass's text (polish.py run --style structured)")
        a = ap.parse_args()
        text = unicodedata.normalize("NFC", a.text)
        print(json.dumps({"ok": True, **apply_style(text, a.style, a.category, a.structured)}, ensure_ascii=False))
    
    
    if __name__ == "__main__":
        try:
            main()
        except ValueError as e:
            print(json.dumps({"ok": False, "error": str(e)}), file=sys.stderr)
            sys.exit(2)
    
  • skills/blurt/scripts/dictation_vocab.py30 KB3e9a094f7c53upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/doctor.py9.9 KBe967fecf4aeaupstream ↗
    # /// script
    # requires-python = ">=3.10"
    # ///
    """Environment check + ASR recommendation. Prints JSON; the agent explains it to the user.
    
      doctor.py            full report
    """
    from __future__ import annotations
    
    import json
    import os
    import shutil
    import subprocess
    import sys
    from pathlib import Path
    
    sys.path.insert(0, str(Path(__file__).parent))
    from _common import BLURT_HOME, CONTRACT, IS_LINUX, IS_MAC, IS_WIN, MODELS_DIR, global_config, run, system_info  # noqa: E402
    import sessions  # noqa: E402
    
    
    def recommend(info: dict) -> list[dict]:
        """Ordered ASR options for this machine. SenseVoice covers zh/en/ja/ko/yue on any CPU; Whisper for other languages."""
        ram = info.get("ram_gb") or 8
        recs = [{"backend": "sensevoice", "why": "Local, ~240MB, very fast on CPU, great for Chinese/English/Japanese/Korean/"
                 "Cantonese incl. code-switching. Default choice.", "setup": "transcribe.py setup --backend sensevoice"}]
        if info["apple_silicon"] and ram >= 16:
            recs.append({"backend": "mlx-whisper", "model": "mlx-community/whisper-large-v3-turbo",
                         "why": "Local on Apple GPU, 99 languages, ~1.6GB. Pick when speech is not zh/en/ja/ko/yue.",
                         "setup": "uv run --with mlx-whisper transcribe.py setup --backend mlx-whisper"})
        elif info.get("nvidia_gpu"):
            recs.append({"backend": "faster-whisper", "model": "large-v3-turbo",
                         "why": "Local on NVIDIA GPU, 99 languages. Needs CUDA 12 + cuDNN 9 runtime.",
                         "setup": "uv run --with faster-whisper transcribe.py setup --backend faster-whisper"})
        elif ram >= 8:
            recs.append({"backend": "faster-whisper", "model": "small",
                         "why": "Local CPU fallback for other languages (slower, ~0.5GB).",
                         "setup": "uv run --with faster-whisper transcribe.py setup --backend faster-whisper --model small"})
        recs.append({"backend": "openai", "why": "Cloud, user's own key. Groq whisper-large-v3-turbo ≈ $0.04/audio-hour; "
                     "OpenAI whisper-1 ≈ $0.36/h; gpt-4o-mini-transcribe ≈ $0.18/h. Only speech (after VAD) is billed.",
                     "setup": "export GROQ_API_KEY=... (or BLURT_ASR_API_KEY + BLURT_ASR_BASE_URL + BLURT_ASR_MODEL)"})
        recs.append({"backend": "dashscope", "why": "Cloud, Alibaba Qwen3-ASR-Flash, strong Chinese; roughly ¥1/audio-hour "
                     "(check console pricing).", "setup": "export DASHSCOPE_API_KEY=..."})
        return recs
    
    
    # Variables an agent session (Claude Code, the Claude / Codex desktop apps, codex) exports to its children; the Blurt
    # app drops them before launching the agent (BlurtRecorder.swift: isAgentSessionVar) — e.g. CLAUDE_CODE_SIMPLE=1 makes
    # `claude` skip the keychain login. Check login state the way the app will run the agent, not the way this session does.
    _SESSION_PREFIXES = ("CLAUDE", "ANTHROPIC_", "CODEX_", "MCP_", "OTEL_")
    _SESSION_NAMES = {"AI_AGENT", "BAGGAGE", "USE_LOCAL_OAUTH", "USE_STAGING_OAUTH", "API_TIMEOUT_MS", "DISABLE_AUTOUPDATER",
                      "DISABLE_MICROCOMPACT", "BLURT_APP"}
    
    
    _USER_CONFIG_VARS = {"CODEX_HOME", "CLAUDE_CONFIG_DIR", "KIMI_CODE_HOME"}  # the user's own config locations: keep
    
    
    def _is_session_var(k: str) -> bool:
        return k not in _USER_CONFIG_VARS and (k.startswith(_SESSION_PREFIXES) or k in _SESSION_NAMES)
    
    
    def agent_env() -> dict:
        """Like the app's agentEnvironment(): this env minus agent-session vars, plus what the user's login shell sets."""
        env = {k: v for k, v in os.environ.items() if not _is_session_var(k)}
        sh = os.environ.get("SHELL")
        if not IS_WIN and sh and os.path.isabs(sh) and os.access(sh, os.X_OK):
            try:
                out = run([sh, "-ilc", "printf '\\n__BLURT_ENV__\\n'; env -0"], check=False, env=env, timeout=8,
                          stdin=subprocess.DEVNULL).stdout
                _, sep, rest = out.partition("\n__BLURT_ENV__\n")
                for entry in rest.split("\0") if sep else []:
                    k, eq, v = entry.partition("=")
                    if eq and k and k not in ("SHLVL", "_", "PWD", "OLDPWD"):
                        env[k] = v
            except (OSError, subprocess.SubprocessError):
                pass
        return env
    
    
    def agent_clis() -> dict:
        """The agent CLIs the Blurt app can hand recordings to: path, version, logged in (None = couldn't tell)."""
        env = agent_env()
        path = env.get("PATH")
        report = {}
        for name, login_args in (("claude", ["auth", "status", "--json"]), ("codex", ["login", "status"])):
            exe = shutil.which(name, path=path) or shutil.which(name)
            info: dict = {"path": exe, "version": None, "logged_in": None}
            if exe:
                try:
                    v = run([exe, "--version"], check=False, env=env, timeout=15, stdin=subprocess.DEVNULL)
                    info["version"] = (v.stdout or v.stderr).strip().splitlines()[0] if (v.stdout or v.stderr).strip() else None
                    r = run([exe, *login_args], check=False, env=env, timeout=20, stdin=subprocess.DEVNULL)
                    text = (r.stdout or "") + (r.stderr or "")
                    info["logged_in"] = parse_login(name, r.returncode, text)
                except (OSError, subprocess.SubprocessError):
                    pass
            report[name] = info
        return report
    
    
    def parse_login(name: str, code: int, text: str) -> bool | None:
        if name == "claude" and "{" in text:
            try:
                v = json.loads(text[text.index("{"):]).get("loggedIn")
                if isinstance(v, bool):
                    return v
            except ValueError:
                pass
        low = text.lower()
        if "not logged in" in low or "please run /login" in low:
            return False
        if name == "codex" and code == 0 and "logged in" in low:
            return True
        return None
    
    
    def tracked_stores() -> list[dict]:
        """Registered projects whose .blurt/ already has files committed to git (K1: never committed). Reported only:
        untracking is the user's call."""
        out = []
        try:
            reg = json.loads((BLURT_HOME / "projects.json").read_text(encoding="utf-8"))
        except (OSError, ValueError):
            return out
        for p in reg.get("projects") or []:
            path = p.get("path") if isinstance(p, dict) else None
            if not path or not Path(path, ".blurt").is_dir() or not shutil.which("git"):
                continue
            r = run(["git", "-C", path, "ls-files", "--", ".blurt"], check=False, timeout=10, stdin=subprocess.DEVNULL)
            files = [f for f in r.stdout.splitlines() if f.strip()] if r.returncode == 0 else []
            if files:
                out.append({"project": path, "files": len(files), "fix": f"git -C '{path}' rm -r --cached .blurt"})
        return out
    
    
    def main():
        info = system_info()
        ff = shutil.which("ffmpeg")
        report = {
            "contract": CONTRACT,  # the skill's side of the app ↔ scripts handshake (_common.CONTRACT)
            "system": info,
            "ffmpeg": run([ff, "-version"]).stdout.splitlines()[0] if ff else None,
            "uv": shutil.which("uv"),
            "gh": shutil.which("gh"),
            "disk_free_gb": round(shutil.disk_usage(Path.cwd()).free / 2**30, 1),
            "asr_configured": global_config().get("asr"),
            "sensevoice_downloaded": (MODELS_DIR / "sensevoice" / "model.int8.onnx").exists(),
            "api_keys": {k: bool(os.environ.get(k)) for k in
                         ("BLURT_ASR_API_KEY", "OPENAI_API_KEY", "GROQ_API_KEY", "DASHSCOPE_API_KEY")},
            "asr_recommendations": recommend(info),
            "agent_cli": agent_clis(),
            "tmux": sessions.tmux_info(),
            "tracked_stores": tracked_stores(),
            "todo": [],
        }
        for name, a in report["agent_cli"].items():
            if a["path"] and a["logged_in"] is False:
                fix = "run `claude` in a terminal and /login" if name == "claude" else "run `codex login` in a terminal"
                report["todo"].append(f"The `{name}` CLI ({a['path']}) is not logged in, so the Blurt app can't hand recordings "
                                      f"to it for background processing: {fix}")
        if not report["tmux"] and not IS_WIN:
            report["todo"].append("Agent sessions in Blurt run in tmux (3.2 or newer); without it they end when Blurt quits: "
                                  + ("brew install tmux" if IS_MAC else "sudo apt install tmux"))
        for t in report["tracked_stores"]:
            report["todo"].append(f"{t['project']}/.blurt has {t['files']} file(s) committed to git; Blurt keeps it out of "
                                  f"git (.git/info/exclude). To stop tracking them: {t['fix']}")
        if not ff:
            report["todo"].append("Install ffmpeg: " + ("brew install ffmpeg" if IS_MAC else "winget install Gyan.FFmpeg" if IS_WIN
                                                         else "sudo apt install ffmpeg"))
        if not report["asr_configured"]:
            report["todo"].append("Choose an ASR backend (see asr_recommendations) and run its setup command")
        if IS_MAC:
            report["todo"].append("Run `record.py test` once: macOS needs Screen Recording + Microphone permission for the "
                                  "app hosting this agent (Terminal/iTerm/VS Code/Claude/Codex), then a restart of that app")
        if IS_WIN and ff and "ddagrab" not in run([ff, "-hide_banner", "-filters"], check=False).stdout:
            report["todo"].append("ffmpeg lacks ddagrab (needs ffmpeg ≥ 6); gdigrab will be used (fine, a bit heavier)")
        if IS_LINUX:
            import ctypes.util
            if not ctypes.util.find_library("portaudio"):
                report["todo"].append("Recording the mic needs PortAudio: sudo apt install libportaudio2")
            if os.environ.get("XDG_SESSION_TYPE") == "wayland":
                report["todo"].append("Screen recording uses X11 (x11grab): log in with an Xorg session, or record with "
                                      "another tool and hand the video over")
        if report["disk_free_gb"] < 5:
            report["todo"].append("Low disk space: recordings use ~1–2 GB per hour")
        print(json.dumps(report, ensure_ascii=False, indent=2))
    
    
    if __name__ == "__main__":
        main()
    
  • skills/blurt/scripts/export_local.py30 KB6edcfa256e51upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/feedback.py18 KB65c016e54632upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/frames.py30 KB24f9e95b7794upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/handoff.py20 KBb703465216f5upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/index.py47 KB3fcb7dcca19eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/items.py84 KBb97157665593upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/jobs.py22 KB7466f09f03a0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/lab_packs/builtin.json8.4 KBef732c02a515upstream ↗
    {
      "schema": 1,
      "id": "builtin-basics",
      "name": "Basics: a bit of everything",
      "name_zh": "入门:各种情况都来一点",
      "description": "Synthetic sentences across lengths, scenarios, languages, names, numbers, self-corrections, fillers, enumerations, questions and run-ons.",
      "source": "builtin",
      "created": "2026-10-24T00:00:00+08:00",
      "prompts": [
        {"id": "b001", "read": "好的,收到。", "tags": {"length": "short", "scenario": "chat", "languages": "zh", "features": []}},
        {"id": "b002", "read": "今晚七点老地方见?", "tags": {"length": "short", "scenario": "chat", "languages": "zh", "features": ["question"]}},
        {"id": "b003", "read": "Sounds good, see you tomorrow.", "tags": {"length": "short", "scenario": "chat", "languages": "en", "features": []}},
        {"id": "b004", "read": "帮我把这个 PR 合进 main 分支", "tags": {"length": "short", "scenario": "coding", "languages": "zh-terms", "features": ["names"]}},
        {"id": "b005", "read": "嗯,那个,我们下周二下午三点开会吧。", "expected": "我们下周二下午三点开会吧。", "tags": {"length": "medium", "scenario": "chat", "languages": "zh", "features": ["fillers", "numbers"]}},
        {"id": "b006", "read": "把这个文件,不对,这两个文件都拷到桌面上。", "expected": "把这两个文件都拷到桌面上。", "tags": {"length": "medium", "scenario": "notes", "languages": "zh", "features": ["correction"]}},
        {"id": "b007", "read": "预算是一万两千五百块,三月十五号之前要付清。", "tags": {"length": "medium", "scenario": "email", "languages": "zh", "features": ["numbers"]}},
        {"id": "b008", "read": "用 Claude Code 跑一下测试,再让 Codex 看看有没有别的问题。", "tags": {"length": "medium", "scenario": "coding", "languages": "zh-terms", "features": ["names"]}},
        {"id": "b009", "read": "这件事有三点:第一是时间来不及,第二是人手不够,第三是预算超了。", "tags": {"length": "medium", "scenario": "meeting", "languages": "zh", "features": ["enumeration", "numbers"]}},
        {"id": "b010", "read": "首先把数据清洗一下,其次重新训练模型,最后再对比一下效果。", "tags": {"length": "medium", "scenario": "coding", "languages": "zh", "features": ["enumeration"]}},
        {"id": "b011", "read": "你觉得 Gemini 和 ChatGPT 哪个写代码更好一点?", "tags": {"length": "medium", "scenario": "chat", "languages": "zh-terms", "features": ["names", "question"]}},
        {"id": "b012", "read": "Could you send me the slides before Friday? I want to check the numbers for Q3.", "tags": {"length": "medium", "scenario": "email", "languages": "en", "features": ["numbers", "question"]}},
        {"id": "b013", "read": "Let's meet at 2:30, no wait, make it 3:30, in the small room.", "expected": "Let's meet at 3:30 in the small room.", "tags": {"length": "medium", "scenario": "chat", "languages": "en", "features": ["correction", "numbers"]}},
        {"id": "b014", "read": "Um, so, I think the build is broken because of the new dependency.", "expected": "I think the build is broken because of the new dependency.", "tags": {"length": "medium", "scenario": "coding", "languages": "en", "features": ["fillers"]}},
        {"id": "b015", "read": "版本号改成 2.3.1,然后在 GitHub 上打一个 tag。", "tags": {"length": "medium", "scenario": "coding", "languages": "zh-terms", "features": ["names", "numbers"]}},
        {"id": "b016", "read": "王老师,周四的课挪到周五上午十点,地点不变,麻烦您跟同学们说一声。", "tags": {"length": "medium", "scenario": "email", "languages": "zh", "features": ["names", "numbers"]}},
        {"id": "b017", "read": "如果明天下雨的话,我们就改成线上开会,不下雨就还是去公司。", "tags": {"length": "medium", "scenario": "chat", "languages": "zh", "features": []}},
        {"id": "b018", "read": "会议纪要:这周上线推迟到二十号,前端先把登录页改完,后端负责把接口文档补齐,测试那边下周一开始回归。", "tags": {"length": "long", "scenario": "meeting", "languages": "zh", "features": ["numbers"]}},
        {"id": "b019", "read": "我刚看了一下日志,在 tmux 里面跑的那个 worktree 好像卡住了,内存涨到了十六个 G,你先把进程停掉,再看看是不是 MLX 的缓存没释放。", "tags": {"length": "long", "scenario": "coding", "languages": "zh-terms", "features": ["names", "numbers"]}},
        {"id": "b020", "read": "Hi Sarah, thanks for the quick reply. The contract looks fine, but could we move the start date to May 6th and split the payment into two parts, forty percent up front and the rest on delivery?", "tags": {"length": "long", "scenario": "email", "languages": "en", "features": ["names", "numbers", "question"]}},
        {"id": "b021", "read": "这个问题我想了一下,呃,有两种做法,一种是改数据库的表结构,另一种是在服务层做一次转换,我倾向于第二种,因为改动小,风险也低。", "expected": "这个问题我想了一下,有两种做法,一种是改数据库的表结构,另一种是在服务层做一次转换,我倾向于第二种,因为改动小,风险也低。", "tags": {"length": "long", "scenario": "meeting", "languages": "zh", "features": ["fillers", "enumeration"]}},
        {"id": "b022", "read": "颜色改成蓝色,算了,还是改成绿色吧,字体再大一号。", "expected": "颜色改成绿色吧,字体再大一号。", "tags": {"length": "medium", "scenario": "notes", "languages": "zh", "features": ["correction"]}},
        {"id": "b023", "read": "为什么这个接口在本地是好的,一部署到线上就返回五百?是不是环境变量没配?", "tags": {"length": "medium", "scenario": "coding", "languages": "zh", "features": ["question", "numbers"]}},
        {"id": "b024", "read": "提醒我明天早上九点半给张经理打电话,问一下报价单的事,还有顺便确认一下发票抬头。", "tags": {"length": "medium", "scenario": "notes", "languages": "zh", "features": ["names", "numbers"]}},
        {"id": "b025", "read": "我们今天主要聊了三件事,一是新版本的发布时间,大家觉得月底比较稳妥;二是用户反馈里提到的卡顿问题,初步判断是图片加载太慢;三是下季度的招聘,设计和后端各招一个人。然后下次会议定在下周三下午,地点还是三楼的会议室,有问题大家随时在群里说。", "tags": {"length": "xlong", "scenario": "meeting", "languages": "zh", "features": ["enumeration", "numbers", "run_on"]}},
        {"id": "b026", "read": "So the plan for this week is pretty simple, we finish the onboarding flow, we fix the two crashes that came in over the weekend, and then if there is time left we start on the settings page, but honestly I would rather ship something small and solid than something big and half done, so let's keep the scope tight and check in again on Thursday.", "tags": {"length": "xlong", "scenario": "meeting", "languages": "en", "features": ["run_on", "enumeration"]}},
        {"id": "b027", "read": "我用 Cursor 写了个小工具,可以把录音转成文字,然后用 Qwen 做总结,最后再发到飞书群里,整个流程大概三十秒就能跑完,你有空可以试一下,有问题告诉我。", "tags": {"length": "long", "scenario": "chat", "languages": "zh-terms", "features": ["names", "numbers"]}},
        {"id": "b028", "read": "这周的花费一共是三千四百二十元,其中打车八百,吃饭一千二,剩下的是买书和办公用品。", "tags": {"length": "medium", "scenario": "notes", "languages": "zh", "features": ["numbers"]}},
        {"id": "b029", "read": "Can you check whether the API key expired?", "tags": {"length": "short", "scenario": "coding", "languages": "en", "features": ["question"]}},
        {"id": "b030", "read": "明天的会我去不了,那个,你帮我跟大家说一下,就说我在外地出差,资料我晚上发群里,不对,下午就发。", "expected": "明天的会我去不了,你帮我跟大家说一下,就说我在外地出差,资料我下午就发群里。", "tags": {"length": "long", "scenario": "chat", "languages": "zh", "features": ["fillers", "correction"]}},
        {"id": "b031", "read": "这个 feature 下周上线。", "tags": {"length": "short", "scenario": "chat", "languages": "zh-terms", "features": []}, "note": "v0.9 regression: with the default Words on, the app typed this as an English translation (the speech model's language tag). Keep the Words on when you read it."}
      ]
    }
    
  • skills/blurt/scripts/live.py38 KB0458871c7f5dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/mcp_server.py20 KBf17750a7d415upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/polish.py127 KB0b67a1751b18upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/polish_cloud.py21 KB852b855f4d83upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/polish_models.json3.2 KB039397f5a896upstream ↗
    {
      "schema": 1,
      "reference": "ram_mb / warm_ms / cold_ms: Apple M5 Max (128 GB), mlx-lm 0.32.2, 3 threads, 25 % memory cap, the 41-sentence set in tests/fixtures/polish_eval.jsonl (Gemma 4 E2B / E4B, Qwen3.5 4B: re-measured with the v0.8 speed prompt and repair; the over-deletion set is tests/fixtures/polish_overdelete.jsonl); docs/design/dictation.md; v1.0.2: Gemma 4 E2B's ram_mb is its peak with the per-layer embeddings read from the file and a 64 MB MLX cache (2.2 GB; was 3.9 GB), and the light tier takes Gemma 3 1B (17 % fall-back and 1.3 GB in the v1.0.2 sweep, Qwen3 1.7B 24 % and 2.1 GB); 2026-10-31: no local polish model by default on light / minimal Macs (8 GB: the speech model takes the room, and on the founder's takes local polish was neutral on words — dictation-eval-2026-10-31.md §6.2)",
      "models": [
        {"id": "qwen3.5-0.8b", "name": "Qwen3.5 0.8B", "family": "qwen", "repo": "mlx-community/Qwen3.5-0.8B-MLX-4bit", "params_b": 0.8, "size_mb": 620, "tiers": ["light", "minimal"], "ram_mb": 1558, "warm_ms": 78, "cold_ms": 1868, "fallback": 0.39},
        {"id": "qwen3.5-2b", "name": "Qwen3.5 2B", "family": "qwen", "repo": "mlx-community/Qwen3.5-2B-MLX-4bit", "params_b": 2.0, "size_mb": 1670, "tiers": ["standard", "pro"], "ram_mb": 2334, "warm_ms": 85, "cold_ms": 2090, "fallback": 0.195},
        {"id": "qwen3.5-4b", "name": "Qwen3.5 4B", "family": "qwen", "repo": "mlx-community/Qwen3.5-4B-MLX-4bit", "params_b": 4.0, "size_mb": 2920, "tiers": ["pro"], "ram_mb": 4010, "warm_ms": 137, "cold_ms": 2582, "fallback": 0.024},
        {"id": "qwen3-0.6b", "name": "Qwen3 0.6B", "family": "qwen", "repo": "mlx-community/Qwen3-0.6B-4bit", "params_b": 0.6, "size_mb": 335, "tiers": ["minimal"], "ram_mb": 1295, "warm_ms": 66, "cold_ms": 1475, "fallback": 0.463},
        {"id": "qwen3-1.7b", "name": "Qwen3 1.7B", "family": "qwen", "repo": "mlx-community/Qwen3-1.7B-4bit", "params_b": 1.7, "size_mb": 935, "tiers": ["light"], "ram_mb": 2137, "warm_ms": 84, "cold_ms": 1582, "fallback": 0.244},
        {"id": "qwen3-4b-2507", "name": "Qwen3 4B Instruct 2507", "family": "qwen", "repo": "mlx-community/Qwen3-4B-Instruct-2507-4bit", "params_b": 4.0, "size_mb": 2175, "tiers": ["pro"], "ram_mb": 3380, "warm_ms": 126, "cold_ms": 1714, "fallback": 0.073},
        {"id": "gemma3-1b", "name": "Gemma 3 1B", "family": "gemma", "repo": "mlx-community/gemma-3-1b-it-qat-4bit", "params_b": 1.0, "size_mb": 735, "tiers": ["light", "minimal"], "ram_mb": 1703, "warm_ms": 110, "cold_ms": 2363, "fallback": 0.098},
        {"id": "gemma3-4b", "name": "Gemma 3 4B", "family": "gemma", "repo": "mlx-community/gemma-3-4b-it-qat-4bit", "params_b": 4.0, "size_mb": 2890, "tiers": ["pro"], "ram_mb": 3766, "warm_ms": 149, "cold_ms": 2893, "fallback": 0.122},
        {"id": "gemma4-e2b", "name": "Gemma 4 E2B", "family": "gemma", "repo": "mlx-community/gemma-4-e2b-it-4bit", "params_b": 2.0, "size_mb": 3415, "tiers": ["pro", "standard"], "ram_mb": 2300, "warm_ms": 111, "cold_ms": 2570, "fallback": 0.024},
        {"id": "gemma4-e4b", "name": "Gemma 4 E4B", "family": "gemma", "repo": "mlx-community/gemma-4-e4b-it-4bit", "params_b": 4.0, "size_mb": 4940, "tiers": ["pro"], "ram_mb": 5420, "warm_ms": 166, "cold_ms": 3618, "fallback": 0.0}
      ],
      "defaults": {"pro": "gemma4-e2b", "standard": "gemma4-e2b", "light": null, "minimal": null}
    }
    
  • skills/blurt/scripts/projects.py28 KBf195c0e614ceupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/record.py68 KB7a86b3e3975cupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/review.html400 KBf7fc92aa2955upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/review.py23 KB69e759849addupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/screen_text.py25 KB61e0bf1cea72upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/sessions.py105 KB887a5ce9210fupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/setup.py26 KB8416c1903829upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/store.py25 KBde28765dcc68upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/tk_recorder.py41 KB2e82f5f0ca7cupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/scripts/transcribe.py234 KB6f933be7d0f9upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/AppIcon.icns602 KB56d66e714b10upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/favicon.png2.0 KB946c7624cf27upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icon-16.png742 B834a4bb75f19upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icon-32.png2.0 KB0e1f29b59df1upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icon.png219 KBcad99c330952upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/accessibility.png530 Ba7c43580954bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]836 B8a3b6c781d7eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]1.1 KB79158303b178upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/agent-run.png419 B47fac829a22bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]578 B67406f94ee04upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]766 B25407034ef7cupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/agent.png407 B681a0bd16a68upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]614 B8a13ddc9eb5eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]809 B463c62db0018upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/arrow-left.png212 Be3bc7ed248bfupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]225 Badb4735bfb79upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]263 Bf03f8ef6ebfaupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/bell.png421 B06113741f03eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]541 Bd1dad442eecdupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]822 B3e012a01f3e6upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/blurt.png447 Ba700e8a83938upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]670 Bb7f27f811a38upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]984 B0a4873e1240aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/bug.png457 Bad8f9dfa9740upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]660 Ba3066983cc40upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]886 B0cb07dfa6002upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/camera.png482 Bcb76e49827a2upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]662 Bcfdc0439c099upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]922 B1f11ebdbba91upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/cards.png303 B2ff454151b2aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]367 B0392ec10872dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]469 Ba4a81130a01bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/check.png245 Bea07bc0f0d3aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]320 B10a506077603upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]418 B00234a46354dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/checkbox.png389 B55f24527a58eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]582 B8fe52823f900upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]744 Bd5dbfe116ac9upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/chevron-down.png182 Ba665076501c0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]205 B59895c95f47dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]282 B1ecf3841c1d7upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/chevron-left.png186 B4a5a388d3037upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]208 B02fc20a4b394upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]224 Babc354c7a4edupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/chevron-right.png169 Bbcf5c2bed45dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]202 Bea8a5dec8760upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]221 B98a89d05d233upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/chevron-up.png182 B0faf3144a9fbupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]205 Bd9ff49e3d329upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]281 B5d9a158e1dd7upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/chicken.png513 Bfa8ee7935625upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]811 Be35576e26232upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]1.0 KB7f94a99a37ddupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/cloud.png382 B252958e9e31eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]551 B21291d635499upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]750 B4e173e16b6d4upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/code.png175 Bba1465dc8ef4upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]253 B80848197cc79upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]259 B09995878846cupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/copy.png434 B1a404bd36fc9upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]549 B119781a1ba66upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]738 B8982579730f0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/document.png444 B946b36200f99upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]560 Bef2568a664a2upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]692 Bd47ed9a37611upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/download.png253 B3b322dc988bfupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]278 B1514e5af8568upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]422 B1d46d0762bcfupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/egg-fill.png305 B55c292cf518bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]431 B956d3840dc2dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]567 Bbe90babd2652upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/egg.png398 Bb5816a6e6853upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]563 Bffd4b5d79f31upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]786 B0fa0a4793fdeupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/external-link.png406 B790841faa369upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]559 B1b1b5665291aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]636 Bb2148d7badd4upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/eye.png430 B812322328410upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]534 B8c5397beb9cdupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]853 Bf1031c2c1043upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/film.png448 B11d3c5512bb1upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]462 B72431b6555d5upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]751 B4e3150362b1fupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/flag.png297 B6b295580a421upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]392 Bf7e674f07724upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]485 B6629f0303f81upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/folder-plus.png389 B96e78e8fc760upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]497 Bed8b935f7c44upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]706 B381f98a950acupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/folder.png333 B5c2d12174e54upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]371 Bd37288581064upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]539 Bdcb49e645e92upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/git-branch.png378 B68f7cd60793fupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]515 B3c36d59ed12dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]801 B3641f055038fupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/git-commit.png241 B28f594e5faa0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]366 B480514ab803cupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]441 B894fdc51d4a8upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/git-pull-request.png444 Bbd7ec896232eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]508 B2111fb1f54fbupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]938 B168d821658f4upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/globe.png547 B385ac8bfca65upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]854 Be7529cc7dd87upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]1.2 KB7e892cdfb20eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/hatch.png466 B98f06e37e7d7upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]715 Bcf217b275ffbupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]975 Bc6f87a91063eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/hatched.png484 B6521f8a3546cupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]746 B844b7485976cupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]1.1 KBdd2d269512bdupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/help-circle.png520 B2f0b37605cf2upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]786 B829a00a73d8dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]1.1 KB8c1c0bb6f05fupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/home.png393 B799eccca1ab3upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]569 B6cb40a5cca7fupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]723 Bf206b9716896upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/inbox.png439 B4e977f2d15e1upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]652 B4978aa24110eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]810 Ba8e90c6fc3c6upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/info.png455 B7c9be36ef729upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]694 B77cae2b92641upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]942 B9590a529da87upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/ink.png365 B75571cda28bdupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]577 B001558d63ed6upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]827 Bf9448ee96368upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/keyboard.png366 Bfd09d7b871dcupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]520 Bea108f00041dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]639 Bd09277d1c7c3upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/languages.png400 B47063dab25b8upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]627 B4154927ea9e8upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]801 B562228551fe4upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/lightbulb.png418 B3f588a8234ceupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]623 Beff262745168upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]855 Bad96ecac0365upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/list.png266 B9c2f0e10c132upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]277 Bac7384ad871aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]469 B0fbcee9ee9d2upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/mcp.png363 B7460e5928f4fupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]423 Bb6348a5d26aaupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]605 Bcd3671283359upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/merge.png336 Be053f8eb3fbbupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]478 B1d41d45daa71upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]608 B280ebd4a7c0bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/mic.png398 B4b7a8b6faf86upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]578 Ba1a1401044c3upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]805 Bec4b33349042upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/moon.png420 B9d1b424c696dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]622 B55e6d9973f25upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]875 B8bbecf0dc6eaupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/more.png155 B375c98350273upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]201 Ba8eff3528ed2upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]229 Bbb7a28b1a357upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/pause-fill.png162 B8c6f4903bd01upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]263 B8f86e278bf24upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]235 Bfc51b83bdb01upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/pause.png229 Bc6f1df099d63upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]320 Bac4b35cbb3adupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]320 B9584fff8dc7dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/peck.png474 B59c5113a9bfcupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]717 Ba9a17d6089a2upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]995 B13c300fb1618upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/pencil.png375 Bb316e76959bcupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]522 B28763adf96e7upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]714 B09cdf5b593e4upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/play-fill.png204 Bcbd1c2b54220upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]358 B2f0e24ab42e4upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]396 B5de68ee8687aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/play.png358 B33169b6e50e0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]491 B340ee5acef7bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]673 B9431524dd780upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/plus.png197 B9bed9148b21bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]190 Ba3b5af74884aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]284 B430d127ad548upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/power.png387 B0f05616ca00aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]577 B12892e68b527upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]779 Bcf85738b9b5fupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/quote.png301 Bf04e762d74b0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]389 B445724a06e3dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]541 B33b10630feccupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/record-fill.png254 B4ca3a7e66e48upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]336 B230f808eec19upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]427 B92097f7358e0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/record.png431 B9accb691522eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]699 B1046f0d21c43upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]1012 B2326f579aa47upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/restart.png414 Be37be4e80400upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]599 B1babbc085c2cupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]857 B0aa91ac8b2daupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/reveal.png421 B405cac4f6427upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]496 Be317c0b28bc8upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]704 B848046e0a561upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/screen.png325 B3fe3e1e09ff5upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]445 Bd9078b4db9b0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]560 B1b9d821b9f88upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/search.png383 B861baa840722upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]585 Bcf8803f18164upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]761 B4f6826608c82upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/send.png442 B180884e7e516upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]673 B22a6e5b6517fupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]913 Be06ad742f5d3upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/settings.png344 B5fee649ec43dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]330 B95d1b7b6c938upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]671 B88ca9f7854ffupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/snap.png458 B32997fd6b7eaupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]589 Bdae9dcc9a15fupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]946 Bc3c99354fb2bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/spark.png352 B9a9617427a43upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]490 B71ab8bf482e4upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]782 Bcde8834299caupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/stop-fill.png212 Baa4606056f7fupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]214 B02d009accea8upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]329 B32ef85f9057bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/stop.png259 B34a7408e165bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]354 B22c5cad1e932upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]446 B8155bd0c1e53upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/sun.png399 B7f999444c014upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]500 Beab44cca2513upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]808 B7fda036e6ebbupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/tag.png368 Bd289be915312upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]562 B76d4ed8f9260upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]691 Beaf9d1091991upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/terminal.png361 B3a5a88ce2452upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]464 B92562e7c3b54upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]689 Be0428d693c1aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/text-cursor.png283 Bd8f0b2d005f0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]336 B3283b1cb5470upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]465 B3f8fc66ddd9aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/trash.png427 B2402ab208db7upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]563 B7097ec909237upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]752 B46cb53ad6bc7upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/undo.png335 B4cef8b3a618fupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]408 B35f4f3ff59eeupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]565 B5885b018bcbfupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/video.png344 B50f808ebeb9aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]478 B65f232869220upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]633 Bab3e1fa1d650upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/wand.png279 B8b1f71eae4efupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]397 Babbdbd4f6e26upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]510 B5f858365097cupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/warning.png434 B3ec86e1ba909upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]638 Be95091133f34upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]862 B40b2232dab75upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/waveform.png294 Be2925cbbdb8eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]321 B3a484b3f4159upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]428 B204df50d7a4dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/x.png224 B2482c6f11a58upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]266 B3cf66cccabceupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/icons/[email protected]363 B1ea70d96cf98upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/peep-done-dark.webp6.3 KB2db59fb8ed93upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/assets/peep-done.webp6.1 KB60a592cf245bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/models/silero_vad.onnx629 KBd1b57f7bb9e6upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/.gitignore18 B2d9f16e2d276upstream ↗
    .build/
    .swiftpm/
    
  • skills/blurt/recorder/macos/Info.plist2.6 KB29c3f0deaff8upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Package.swift3.2 KBdb63e6638e45upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/AgentHub.swift46 KBef819384fd31upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/AgentSetupServer.swift32 KB876dbffe54bbupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/AppDelegate.swift73 KB2d2315821929upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/AppEnvironment.swift4.1 KB7330f8acc143upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/AppNames.swift2.3 KB98a767c25743upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/BrandMoments.swift1.7 KB1f3d0fbf9b31upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/CarbonHotkeys.swift1.8 KB45b120328d24upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/CloudStore.swift15 KBcae47bf17949upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/CuratedModels.swift3.7 KB097baaa3bcb3upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/Hotkeys.swift2.9 KBe7010cb13751upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/HotkeySettings.swift44 KB53aa19962405upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/Housekeeping.swift15 KBb8fb7cce0b20upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/LaunchMessages.swift3.3 KB85bdac72185cupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/MemoryPressure.swift2.2 KB6ff7b8864157upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/MenuGlyph.swift14 KBe084aa48a330upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/ModelDownloads.swift20 KB67634df3a2bbupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/NetworkWatch.swift2.8 KB01ed89a3701aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/OrganizeProgress.swift2.6 KB625c0544d614upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/SelfTest.swift11 KB210421860fe5upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/Shell.swift2.9 KB1fb2aea2f159upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/SnapApp.swift59 KBc8e6cde2fca5upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/Strings.swift1.1 KB42bdc319e233upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/Support.swift7.6 KB7883a8f707d6upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/Updater.swift5.6 KB8831c2db444aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/UVSetup.swift3.8 KB6138826a06dbupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/App/Workspaces.swift2.8 KBecbd843bce8eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/AppleSpeech.swift18 KB9c1d7fd93ee9upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Captures.swift73 KB092964edccd7upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/CardDrag.swift27 KB411097992fafupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/CardExport.swift16 KB75f7cd3bd11aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Chrome.swift16 KBb7e76e84e1b6upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Dictation/Dictation.swift106 KBb1ba3ca8d822upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Dictation/DictationCloud.swift3.4 KB2295a282a64cupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Dictation/DictationContext.swift18 KB0411080cc2fbupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Dictation/DictationInsert.swift23 KB612584cbdd53upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Dictation/DictationKeys.swift22 KB4560c6ea4e5dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Dictation/DictationRemembering.swift6.1 KB47aac81238fbupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Dictation/DictationSound.swift3.2 KBb604aa72ff17upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Dictation/DictationStream.swift76 KB5f8a1a005ac8upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Dictation/DictationStyles.swift32 KB2f7c2a4a67a2upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Dictation/DictationTakes.swift50 KB7a6957e63d77upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Dictation/DictationTestHooks.swift26 KBd38d491dcd8cupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Dictation/DictationTranslating.swift13 KB07ea70c20334upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/InkStage.swift13 KBb67d36957976upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/InkView.swift17 KB9de23d1ccf75upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Island/CardChrome.swift17 KB6f4725c403a0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Island/CardPreview.swift10 KBe01ac1875272upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Island/CardsOverview.swift61 KB0ae15db01d5bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Island/Ears.swift27 KB31bdd3261656upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Island/FrameCapture.swift5.4 KB95b320a0c671upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Island/HangingCards.swift153 KB2dc263492286upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Island/Island.swift78 KBda63da5fd812upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Island/IslandContent.swift95 KB74a808deafcbupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Island/IslandModule.swift406 Bcf025d12c5c5upstream ↗
    // The notch island (K7, P5): installed after the other modules (AppEnvironment.installModules) in the menu-bar app;
    // sets AppEnvironment.island. A recording started from the chat (no menu-bar app) starts the island on its own.
    import AppKit
    import BlurtCore
    
    enum IslandModule {
        static func install() {
            AppEnvironment.island = island
            island.start()
            snapper.restore()
        }
    }
    
  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Island/IslandRecording.swift9.0 KB4f566676b8d0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Island/Motion.swift8.1 KB79a983ec03abupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/KeyCaps.swift10 KB80ac655afd22upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/KindChip.swift11 KB56a2edc0e426upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/LiveAudio.swift10 KB6ef6b1633094upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/LiveFeeds.swift2.3 KB4cdde43401e6upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/LiveTranscript.swift30 KBc51957457620upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Loupe.swift6.5 KBfad7bae8e6a5upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/MissingFolder.swift8.7 KBb8b534e05751upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/NoteBubble.swift132 KBb63585da0ce7upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/OwnWindows.swift3.2 KB75157c477e3dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Picker.swift35 KBbd05835c066bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/ProjectChip.swift20 KB5beef89cdad9upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Recorder.swift54 KB9ece86e28ea6upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Refile.swift8.6 KB69c388d02f16upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/ScreenHelp.swift3.6 KBd454283485fbupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/ScreenTextCard.swift19 KBcdfe45c9c076upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/ScreenTextCopy.swift26 KBb9c78cd0823cupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/ScreenTextRefine.swift7.2 KB9c95ab9819eaupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/SmartPaste.swift41 KB2cfafda0d0faupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/Snap.swift38 KB20ab8fc6c263upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/TakenPanel.swift4.8 KB34c679c73a32upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Capture/VoiceInput.swift49 KB890e389998d7upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Icons/BlurtIcon.swift11 KBe5e0e7418a26upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Icons/BlurtIconPaths.swift88 KBbc5c33061c84upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Icons/BrandColor.swift15 KB7fdd84986ab5upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Icons/BrandViews.swift27 KB18dfbb4bdf6aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Icons/KindStyle.swift5.5 KB53aaa9f667bdupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Icons/Mascot.swift26 KBea775816c309upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Icons/ProjectLook.swift2.5 KBd66817a9b706upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Icons/StatusRing.swift5.2 KB132c9a30ac27upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/main.swift3.1 KBa3b9c9ba3cb5upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/MainWindow/CommandPalette.swift51 KBceac7b3eb8f2upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/MainWindow/DictationFeedback.swift12 KBc4e4bd35c439upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/MainWindow/DictationLab.swift44 KB485499f09d4fupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/MainWindow/DictationLabViews.swift63 KB61e41df0c585upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/MainWindow/DictationPage.swift111 KB7a770214e62dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/MainWindow/DictationWords.swift37 KBdebdfe9315d1upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/MainWindow/ItemCells.swift42 KBfbe016e82449upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/MainWindow/ItemListController.swift96 KB3417c3e14458upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/MainWindow/JobViews.swift64 KBb537f7d0f407upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/MainWindow/MainMenu.swift36 KB502161463f50upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/MainWindow/MainModel.swift25 KB5f6b99ff8ff0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/MainWindow/MainWindowController.swift109 KB41aa35800c8bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/MainWindow/MainWindowModule.swift4.3 KB1500f1575e77upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/MainWindow/NewItemSheet.swift11 KB1049cca9292aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/MainWindow/ProjectPicking.swift27 KBcc8ea0ac70c0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/MainWindow/ProjectSettings.swift26 KBa07eadfc7366upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/MainWindow/Scripts.swift7.6 KB0cc28c278d7aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/MainWindow/SidebarController.swift26 KB1f578217b4a1upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/MainWindow/StateMenu.swift8.8 KB8d350f2de0c7upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Onboarding/HelperPanel.swift28 KB0539bcecb6f1upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Onboarding/ModelSetup.swift15 KB2c737d463296upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Onboarding/Onboarding.swift68 KB929a8e3dab05upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Onboarding/OnboardingKit.swift8.6 KB2471fb1836f6upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Onboarding/PermissionGuide.swift20 KB70c00453b3e8upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Onboarding/PermissionTestHooks.swift4.9 KBa97fc9dfdea2upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Onboarding/SystemAccess.swift7.6 KBba0fa302e583upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Assets.car2.4 MB0eb40b436742upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Assets.xcassets/AccentColor.colorset/Contents.json666 B2d23b57861bcupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Assets.xcassets/Contents.json60 B74d6a722cf39upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/en.lproj/Capture.strings23 KB916776af54a3upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/en.lproj/Capture.stringsdict11 KBd58af00a5973upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/en.lproj/Common.strings3.6 KB73c2f6574563upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/en.lproj/Dictation.strings44 KBcc4d3f024093upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/en.lproj/Dictation.stringsdict1.1 KB394028ffe6e4upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/en.lproj/Main.strings38 KBcbb2a3e14132upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/en.lproj/Main.stringsdict13 KB3bc8de351200upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/en.lproj/Models.strings7.6 KB1426df3e1bc7upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/en.lproj/Settings.strings36 KBd963bc444038upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/en.lproj/Settings.stringsdict1.8 KB5218e04889f0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/en.lproj/Speech.strings14 KB0a03483fd525upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/en.lproj/Terminal.strings29 KB04b05b96a860upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/en.lproj/Terminal.stringsdict5.6 KB449eb47dead1upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/dark-done.webp169 KBdaf21dd3f47eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/dark-error.webp161 KB408ff63026b3upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/dark-hello.webp149 KBe0ee797cb021upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/dark-idle.webp185 KB103515a8f8e7upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/dark-listening.webp152 KBe9f7c3b08fe0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/dark-talking.webp175 KBde00a0d7b36eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/dark-thinking.webp151 KB22df5db7f8cdupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/glyph-done.png10 KB4d74488fcee2upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/[email protected]890 B8d64f39ad077upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/glyph-error.png7.2 KB939d1fb63a10upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/[email protected]792 B7feddb62d6eeupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/glyph-hello.png6.1 KB0ddd1fbc8f3fupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/[email protected]560 B34978e2dec56upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/glyph-idle.png9.6 KB45619bce5580upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/[email protected]789 B0abb1fa74161upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/glyph-listening.png6.2 KBe02ba194ee8eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/[email protected]476 B58abf09ebb3cupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/glyph-talking.png6.5 KBb2679d5a3765upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/[email protected]618 Bdc1bb334b1f1upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/glyph-thinking.png7.0 KB3f17749babebupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/[email protected]625 Be6b463267dbeupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/paper-done.webp230 KB8bb3674e0e7dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/paper-hello.webp203 KB8d6f79ccf728upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/paper-idle.webp252 KB4bf3009d35c5upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Peep/peep.json2.7 KB67385003485eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Sounds/cancel.wav4.3 KB1f2f9b7bda4dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Sounds/start.wav21 KB5f6cbed49940upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/Sounds/stop.wav8.5 KBa91a6b62f2d0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/zh-Hans.lproj/Capture.strings23 KB8d7c461a3461upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/zh-Hans.lproj/Capture.stringsdict9.7 KB5f10bfa5df38upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/zh-Hans.lproj/Common.strings3.5 KBaee38c217da8upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/zh-Hans.lproj/Dictation.strings44 KBed403309fa32upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/zh-Hans.lproj/Dictation.stringsdict969 B3c3caca7fd83upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/zh-Hans.lproj/Main.strings37 KB7f2801635d45upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/zh-Hans.lproj/Main.stringsdict12 KBb1fd44b4f118upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/zh-Hans.lproj/Models.strings7.5 KB4256703ece8cupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/zh-Hans.lproj/Settings.strings35 KB0d4e14f1472aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/zh-Hans.lproj/Settings.stringsdict1.6 KBf1bf15543516upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/zh-Hans.lproj/Speech.strings13 KB79ab9f5b9ed5upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/zh-Hans.lproj/Terminal.strings28 KB5d603d8bbe35upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Resources/zh-Hans.lproj/Terminal.stringsdict4.8 KB38415cd681f9upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Settings/AgentSetupViews.swift17 KB3166c208c6f8upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Settings/AgentsPane.swift77 KBe923a01c8c15upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Settings/CloudModels.swift5.5 KBa74bd0e756bbupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Settings/CloudServicesPane.swift56 KBf3cc142e97dbupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Settings/CloudSpeechSheet.swift24 KB59105aeccd9bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Settings/DictationCompare.swift22 KB9474b88aa11eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Settings/DictationModels.swift20 KB28f7423c1c71upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Settings/DictationPane.swift51 KB5d11a00f34dcupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Settings/GeneralPane.swift13 KBa3f8441f90e7upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Settings/PrivacyStoragePanes.swift54 KB0ae63c1917e9upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Settings/ProjectsPane.swift13 KBdc999779e972upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Settings/ScreenTextPane.swift5.4 KBcb69f0a68ab3upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Settings/SettingsKit.swift68 KBf2bb538a537aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Settings/SettingsModule.swift1.8 KBf6ca74e29021upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Settings/SettingsSearch.swift28 KB1f30933dd65fupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Settings/SettingsWindow.swift27 KB864bf23c04edupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Settings/SpeechPane.swift70 KBe19df47c540aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Settings/TranslateTargets.swift2.6 KB2edf6d9fd9c8upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/System/FixRunner.swift3.3 KB0a95c54b2353upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/System/FixWatcher.swift6.6 KB4a9081c627e8upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/System/IndexLookup.swift2.7 KB91d6b1b181f2upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/System/JobNotifier.swift10 KBef3743f39c44upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/System/LayoutCheck.swift14 KB8f7d1347c246upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/System/Peck.swift4.2 KB94f93ea359faupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/System/SelfTests.swift55 KBe57fbe7af48dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/System/Services.swift8.2 KB72a93f4a36faupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/System/Spotlight.swift5.8 KB3f65b18e71e1upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/System/URLRouter.swift4.0 KB2d60ea084a46upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Terminal/DesktopApp.swift5.9 KBf43b9bb39d1aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Terminal/DispatchPanel.swift45 KBfdbee5d10b0dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Terminal/FinishPanel.swift23 KB0c61197d246eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Terminal/HandoffKit.swift12 KB410170784a5fupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Terminal/SessionNotifier.swift5.3 KB050f9afd5e8eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Terminal/SessionRecovery.swift4.5 KB42a03b741a83upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Terminal/SessionsModel.swift11 KBdb0eefd6a868upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Terminal/SessionsPane.swift18 KBbcf7410a4258upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Terminal/SessionsViews.swift32 KB06de3c91603eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Terminal/TerminalModule.swift5.5 KB33e3fadfa4c1upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Terminal/TerminalSelfTests.swift14 KBd623edcde167upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Terminal/TerminalStyle.swift8.3 KB2f15a2dcd3d0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/Terminal/TerminalSurface.swift11 KBf31993c978d5upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/WebHost/DragOverlay.swift4.7 KBca4eb822f675upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/WebHost/ReviewHandoff.swift3.7 KB092c1e986866upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/WebHost/SchemeHandler.swift9.6 KBabc55c251e35upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/WebHost/SelectionView.swift19 KB4a231f873065upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtApp/WebHost/WebHostController.swift46 KB0fb7536fdf4dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Agents/Agents.swift29 KB1442a510a106upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Agents/OrganizeSteps.swift1.6 KBb168d40173f1upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Agents/Processing.swift3.0 KB37bf10a7f00dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Agents/UnattendedRun.swift3.1 KB89d8c3b5eea7upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/AgentSetup.swift25 KB9b17a78aa96dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/AppIdentity.swift626 B66619aa389e1upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/BundledSkill.swift3.5 KBa36139be8fc6upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/CaptureEdits.swift5.0 KB19be98b6d12bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/CaptureExclusion.swift3.8 KB7a45ea760ae3upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/CaptureRules.swift17 KB74f703209b94upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/CardDropTarget.swift18 KB0833c28a7b2dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/CardFiles.swift20 KB5e93bb449212upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/CardNote.swift6.0 KB001302ba9b18upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/CardPeek.swift5.6 KBfbc22b6da391upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/CardRow.swift17 KBf772ac273c23upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/CardSmartPaste.swift20 KB9d6b50883384upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/CardsPanelKeys.swift4.8 KB262c2b386332upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/CloudCurated.swift13 KB15418e4fb937upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/CloudServices.swift35 KB8408eb37e973upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Config.swift13 KB45e9dd205ecfupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/ControlSocket.swift9.1 KBd29452b6ce97upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationAccess.swift5.4 KB783ef3cd7760upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationASRLine.swift5.7 KBba12411201a0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationChoices.swift16 KB42af7a0d1414upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationFit.swift11 KBcc9aba38a824upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationHistory.swift33 KB3c24aa4ac2fcupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationLab.swift23 KB24c54b89069dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationLearn.swift19 KBc400a3d49c3dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationListener.swift6.3 KBec3031014925upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationLiveDecode.swift10.0 KB2f9c248d7f42upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationPastePolicy.swift12 KBd48f127fa64bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationRemember.swift2.5 KB3aa8923b4fb9upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationSegmenter.swift8.0 KB4279e25a7d2bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationSendRule.swift1.2 KBef28ea328be1upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationStitch.swift18 KBc664f383d11aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationStyle.swift14 KBa4c5d6254040upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationStyleChoice.swift3.4 KB79228c63e044upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationStyleLearn.swift3.9 KB1e5be86a655dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationStyleRecord.swift7.2 KBf26728b7c452upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationTakeFlow.swift20 KBda542e34adb5upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationTier.swift6.1 KB401cae565559upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationTranslate.swift18 KBcccbc5d06144upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationTrigger.swift34 KB13d202c76a80upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DictationVocabulary.swift21 KBc200e4e19862upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/DragLog.swift3.0 KB5b22de050aafupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/FilePrivacy.swift3.1 KB2a46a7739624upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/HandoffBundle.swift2.8 KB162d5bb2c748upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Hotkeys.swift40 KB1056641b2ab2upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Index/IndexReader.swift29 KBc36d5f060ba0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Index/IndexWatcher.swift5.0 KB3e30fbfb10d4upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Index/ListSections.swift3.7 KBfad1ba31bd0aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Index/ScanFindings.swift1.6 KB4b7125a2791bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Index/SearchHit.swift2.6 KBa320deb277eeupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Ink.swift16 KB6e726242d25aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/IslandLayout.swift14 KB13fff74d0968upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/IslandToasts.swift6.4 KBcaaa0881499eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Jobs/Jobs.swift28 KB9e76d6d15ec8upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Jobs/ProcessGroup.swift6.9 KB795ef9d38ba9upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/LaunchQueue.swift2.7 KB068fc979a2d4upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/LayoutLint.swift13 KB67a05f7444aaupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Lifecycle.swift8.4 KBce0afde99030upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Loc.swift11 KB09fd14385b4bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/ModelBundle.swift7.3 KB4708c66754b6upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/ModelCatalog.swift9.3 KB08c066f55df6upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/ModelDownloadQueue.swift9.7 KB9a60fcb7b3ffupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/ModelUsage.swift4.3 KB457196981102upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/MoveBack.swift1.0 KBa7e9c1d70d86upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/NoteEndpointer.swift9.5 KB4b0790ab9363upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/NoteKeys.swift8.0 KBb3b3d81c0550upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/NoteKind.swift1.3 KBef85d01d06bbupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/NothingHeard.swift1.5 KB9bb48f91efc6upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/OverlayFit.swift1.1 KBa33a2aa0f347upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Paths.swift3.4 KB802e4c0e70eeupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Peep.swift5.3 KB0a0510d5d720upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/PermissionFlow.swift8.6 KB4274c0b48594upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/PlaceFilters.swift2.5 KB5843fb0796faupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/ProcessProbe.swift3.6 KBb96e3a85c36dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Projects/Assignment.swift7.5 KB77cd1497597aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Projects/Projects.swift17 KBbb13dba36664upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Projects/StoreFolders.swift2.6 KBea1132adf41eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/RecordingDiscard.swift1.9 KB44178831cf16upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/RecordingFailure.swift10 KB29f95b05da94upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/ScreenText.swift19 KBb5b160846fa8upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Script.swift14 KB6d492908dc0eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/SerialWork.swift1.5 KB400c0f713856upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Sessions/AgentSessions.swift18 KB95234985aa76upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Sessions/HandOffCheck.swift2.5 KBe33c494a87c8upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Sessions/HandoffTarget.swift1.9 KB40bd4c2dbc76upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Sessions/SessionOutcome.swift7.0 KBe6c2b69e827eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/Sessions/WorkSession.swift4.1 KB773cb88e168aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/SettingsCatalog.swift39 KB17c81c5ee554upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/SkillContract.swift2.3 KB4464462bedd3upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/SpeechSetup.swift8.0 KB4ae20224bd5eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/TakeProgress.swift4.5 KB3fe422d8c99bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/TakeStitch.swift4.6 KB5cdcdd5980ecupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/TextImage.swift6.9 KBd07c0c35ffedupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/ToastQueue.swift11 KB820c07402593upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/TopRows.swift1.3 KBc6507e21541aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/TrashRestore.swift2.1 KB62f33ccd2be7upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/VideoRetention.swift5.5 KBcaa376de8bdfupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/VoiceCardDrop.swift7.0 KBebb1ed75e0b3upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/VoiceNoteStyle.swift4.9 KB84cbce1a0d02upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/VolcengineSpeech.swift3.2 KBd4f47ee6e365upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/WarmServer.swift10 KB2f2a73cd4491upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/WarmServerStream.swift7.6 KBcf185eac344bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtCore/WelcomeLine.swift3.7 KB3081659fc16cupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtObjC/BlurtObjC.m537 B721f17eae73eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Sources/BlurtObjC/include/BlurtObjC.h712 B190860dc550eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/AgentLoginCacheTests.swift2.3 KB4561a2d5c128upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/AgentSetupTests.swift19 KB7db737f99246upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/AppIdentityTests.swift567 B0b3246bc0ff5upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/AssignmentTests.swift12 KB12acb8489c02upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/BlurtTryTests.swift1.6 KBa79a7ec04224upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/BundledSkillTests.swift3.7 KB8a415e323770upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/CaptureEditsTests.swift9.0 KB14c58a0475b7upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/CaptureExclusionTests.swift3.0 KBb87a15069500upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/CaptureRulesTests.swift20 KB16da12ec5dc6upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/CardDropTargetTests.swift14 KBe28e40e03840upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/CardFilesTests.swift13 KB48ee4a5931cbupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/CardNoteTests.swift5.7 KB86d05d65d672upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/CardPeekTests.swift6.6 KB75985761b3efupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/CardRowTests.swift14 KB4405c83229b1upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/CardSmartPasteTests.swift14 KB1bc22975edf8upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/CardsPanelKeysTests.swift5.5 KBa23e4211048dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/ChatAppDragTests.swift15 KBdcbc7d5248aeupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/CloudCuratedTests.swift14 KBd2d8356b0728upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/CloudServicesTests.swift19 KB5183d1cabd3eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DesktopHandoffTests.swift8.3 KB549288502528upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DictationAccessTests.swift5.4 KB227a67ef60a8upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DictationASRLineTests.swift4.4 KB232c3f38ad6aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DictationChoicesTests.swift8.9 KBe82afee5c845upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DictationFitTests.swift7.7 KB38bf2fe44b73upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DictationHistoryTests.swift20 KB9fab82e5d676upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DictationLabTests.swift7.1 KB120514e8b231upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DictationListenerTests.swift8.3 KBcbb4b6266e41upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DictationLiveDecodeTests.swift16 KB02b9eeab358cupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DictationPastePolicyTests.swift10 KB2dfd68bda49bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DictationRememberTests.swift3.8 KBeb5a85c64a46upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DictationSegmenterTests.swift4.9 KB4fd2dfc250d7upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DictationSendRuleTests.swift1.1 KB1a5437cee521upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DictationStitchTests.swift14 KB823efe22a415upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DictationStyleChoiceTests.swift4.5 KB120473194d09upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DictationStyleLearnTests.swift13 KB8ac56f207cb2upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DictationStyleTests.swift11 KBd6e3732bab62upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DictationTakeFlowTests.swift17 KB2564dee7dc17upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DictationTierTests.swift3.9 KB8850bb294ce9upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DictationTranslateTests.swift13 KBceff9378f347upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DictationTriggerTests.swift41 KBfd487b9c526cupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DictationVocabularyTests.swift21 KB0d096c78c5fdupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/DragOutTests.swift8.6 KBade4f748c6bfupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/HandoffBundleTests.swift3.1 KB5d3fab810704upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/HotkeysTests.swift13 KBb73c8fa09731upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/IndexReaderTests.swift16 KBa07e493b934dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/InkTests.swift21 KB4cea3fecf4d0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/InstallPlanTests.swift5.4 KBa40172c937caupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/IslandLayoutTests.swift14 KBd5fa70674e3bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/IslandToastsTests.swift5.3 KB0e6c5825e0a4upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/LayoutLintTests.swift11 KB4beec51dd4b8upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/LifecycleTests.swift5.1 KB1f6cf86b4c2aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/ListSectionsTests.swift5.3 KBea70d69e984cupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/MissingFolderTests.swift1.6 KB205a96d35515upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/ModelBundleTests.swift7.1 KB206fd2d2149eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/ModelCatalogTests.swift5.8 KB50d93df6eeb9upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/ModelDownloadQueueTests.swift8.6 KBf6ded6500b72upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/MoveBackTests.swift658 B74bdab6105c7upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/NoteEndpointerTests.swift14 KBf8e176b213dcupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/NoteKeysTests.swift9.9 KBa230dfd22c69upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/NothingHeardTests.swift1.1 KB055e145228d3upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/OverlayFitTests.swift1.6 KB957eac0042e6upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/PeepTests.swift8.4 KB2be9d512b80fupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/PermissionFlowTests.swift7.8 KB8ee95fc10b15upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/PlaceFiltersTests.swift2.3 KB49aa295d6dfdupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/ProjectOrderTests.swift2.9 KB8bcf166976d9upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/RecordingDiscardTests.swift1.1 KBda70058ce0e3upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/RecordingFailureTests.swift12 KBbe474f2f8e30upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/ScreenTextTests.swift7.0 KBbf4c698d6aefupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/SearchHitTests.swift2.3 KBa998de6402feupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/SerialWorkTests.swift2.1 KB76c9432eeaf3upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/SessionOutcomeTests.swift14 KBd6e2b88fc333upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/SettingsCatalogTests.swift15 KB45a9094123dfupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/ShellDataTests.swift24 KBf82f1fab78ffupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/SpeechSetupTests.swift5.9 KB457c3cff4d81upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/StoreFoldersTests.swift3.4 KBcb0449069788upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/TakeProgressTests.swift5.1 KBd0ce23d1f476upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/TextImageTests.swift3.0 KB903092cee5ffupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/ToastQueueTests.swift15 KB42390722a000upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/TopRowsTests.swift1.8 KB410b5dc5d12bupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/TrashRestoreTests.swift2.6 KBfd270f5cff84upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/UnattendedRunTests.swift3.4 KB684d07a7845dupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/VoiceCardDropTests.swift8.6 KB718746bb65cdupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/VoiceNoteStyleTests.swift4.0 KB8e049a4236d4upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/VolcengineSpeechTests.swift2.5 KBd34fd710c509upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/WarmServerTests.swift5.5 KBc9f9102bbcedupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/WelcomeLineTests.swift7.9 KB6b4021f860d0upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/WorkDogfoodTests.swift7.7 KBa9381471461fupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/BlurtCoreTests/WorkSessionTests.swift4.0 KB2801cf7cf093upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/recorder/macos/Tests/make_fixture.py6.8 KB43fdc56b7509upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/reference/asr.md6.2 KB3554d6ee341eupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/reference/docs.md2.1 KB8b2b9fea029fupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/reference/export-other.md2.5 KBb9b7a38cc094upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/reference/lenses/idea.md1.3 KBb6b984620f99upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/reference/lenses/issue.md1.0 KBf35c3da2d74cupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/reference/lenses/note.md440 Bacf53e3912cfupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/reference/lenses/task.md276 Bfd7314c5008fupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/reference/schema.md6.3 KB3934f700190aupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/reference/sessions.md11 KB576f5459ab97upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • skills/blurt/reference/setup-models.md8.3 KB199ebed2b318upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

Plain-text copy, no login: /skill-bundles/agihunt-blurt-skills-blurt.md

// REPO STATS

202 stars