Skip to content
Machine Learning
Skill

/app-capture

Deterministically drive a first-party Civitai App Block in the operator's browser and produce store-ready screenshots, machine-analysable per-state evidence (DOM + console + failed requests + a11y + testids), or attach media to a listing. Use when the user asks to capture /

BOOST
From plugin
civitai
7.3k48 skills15 agents3 commands
Install
$ npx -y skills add civitai/civitai --skill app-capture --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/app-capture

Context preview

The summary Claude sees to decide when to auto-load this skill.

Deterministically drive a first-party Civitai App Block in the operator's browser and produce store-ready screenshots, machine-analysable per-state evidence (DOM + console + failed requests + a11y + testids), or attach media to a listing. Use when the user asks to capture /

SKILL.md

app-capture.SKILL.md
name: app-capture
description: Deterministically drive a first-party Civitai App Block in the operator's browser and produce store-ready screenshots, machine-analysable per-state evidence (DOM + console + failed requests + a11y + testids), or attach media to a listing. Use when the user asks to capture / screenshot / re-shoot an app's store images, to add a screenshot / icon / cover / caption to an app listing, to add a new app to the capture set, to FIND DEFECTS in a running App Block or diff a before/after fix, or to run a scripted App Block action (including the spend path behind an explicit flag).
argument-hint: "[capture <slug> | evidence <slug> | attach <slug> | discover <slug> | test]"
allowed-tools: Bash, Read, Edit, Write, Grep, Glob

app-capture — scripted App Block runs + store screenshots

Two verbs, deliberately separate:

| verb | mutates? | entry point | |---|---|---| | `capture` | no — never spends, never touches a listing | `.claude/skills/app-capture/scripts/capture.sh` | | `attach` | **yes** — opens a moderator-reviewed shadow revision | `.claude/skills/app-capture/scripts/attach.sh` |

The logic lives in three **pure** scripts under `.claude/skills/app-capture/scripts/`, so it tests with no browser: `plan.py` turns *recipe + observed state* into an ordered list of bridge commands, `frame.py` measures/crops/gates an image, `evidence.py` turns *DOM + drained probe* into a per-state artifact. `capture.sh` executes what they print and decides nothing.

Run it

SK=.claude/skills/app-capture/scripts
$SK/capture.sh $SK/recipes/custom-generators.json --instance work --out /tmp/cg

Add an app = add a recipe. Nothing else changes. `--tab <id>` attaches to a tab the operator already has in front (no `open`, never closes it); `--no-foreground` is diagnostics only.

🔴 **Two founding claims are RETRACTED — do not re-derive them.** An App Block *does* boot hidden (4/4), and the screenshot discriminator is *not* occlusion: `captureVisibleTab` can **HANG** rather than reject, so the `catch` that falls through to CDP never ran. ✅ Fixed in the BRIDGE (devrc #797) — **nothing here changed**, so `ACTIVATE_REASONS`, G11 and M59–M64 are the shipped design, not dead code. 🔴 **Deployed ≠ running:** the MV3 worker serves old code until Brave FULLY restarts (↻ often no-ops), so a run can still hit the 18 s hang; `browser --instance <key> ping` → **`buildMarker`** is the only field describing RUNNING code, and staleness is **per profile**.

🔴 **The raise is withheld, but `activate` is NOT a no-op.** Every capture-path activate passes `--no-focus`, so the i3 raise answers `i3: "withheld"` (a **fourth** value beside `applied|skipped|failed`) — and that gate covers the `i3-msg` ONLY. 🔴 **Corrected 2026-08-28: OMITTING the flag would not withhold anything** — the CLI defaults it ON when stdout is a TTY, so the old "`capture.sh` never passes `--focus`" derivation was wrong and the property rested on `run_step`'s command substitution. Now declared and gated (`activate_unconsented`); `spend` is exempt. The extension also calls `chrome.windows.update({focused: true})`, granted under i3's default `smart` from a window on the **active** workspace: a **residual** screen-take whenever Brave is already on the visible workspace, **unmeasured in both directions**. `activate` also makes the tab its window's **ACTIVE TAB** — what routes a capture onto `captureVisibleTab`, where a background tab takes CDP. Removing the steps is optional cleanup, not a fix.

Measurements, corrections, and what was NOT established: the private infra repo's occlusion write-up (2026-08-24) (the hang, the raise) · the private infra repo's hidden-tab-boot write-up (2026-08-24) (hidden boot 4/4)

🔴 **Foregrounding is NOT the spend path — two separate guards, neither able to cover the other.** `activate` is confined to four declared positions (`ACTIVATE_REASONS`); `plan.py` separately refuses **any** step carrying `xdotool`/`--clearmodifiers` without `--trusted` (`actuation_without_trusted`), a check deliberately blind to `activate`. Each placement clause raises its OWN code, or a mutant dies to its neighbour and the battery records coverage that does not exist. Gates **G2** and **G11** prove it.

🔴 **Every state opens with an APP-READY gate** — a recipe must declare a `ready` anchor (`no_ready_gate`), and it must be something only the **booted** app renders. Without one, a click fired into a still-booting app is discarded and the *next* wait times out naming the control: a defect report about the wrong component.

**One failure, one exit code, one sentence** — a code once carried several, twice. All of them, because a partial list sends you reading the script: `2` refused before anything was driven (a `plan.py` REFUSE, an unknown flag, no such recipe) · `3` the **bridge or the tab**, never the app · `4` a recipe action failed · `5` a state's crop was REFUSED · `6` every state measured the **same box** — the cropper is reading page furniture · `7` render failed · `8` the candidates violate the store bounds · `9` the app-frame probe could not be **BUILT** (`guard_rect_js`) · `10` evidence refused · `11` the app never booted (**CONFOUND** only when the anchor is ABSENT *and* the tab is not visible) · `12` the screenshot op returned no path — a **bridge** failure, never the recipe's · `13` the host-side window raise reported `failed` · `14` the **browser window is not the one this recipe's `crop.rect` was measured in**. Gate **D8** pins the **5/9/14** split (a bad crop vs a probe that could not be built vs a wrong window — 5 and 9 were once one code). ⚠️ `0` is not always a shoot: no imagemagick, and `--no-frame`, both exit 0 unrendered.

🔴 **14 is not a defect report — it is the one code that says nothing is broken.** It carries two refusals, `viewport_of_record` and `frame_of_record`, because they ask the operator for the same thing (restore the geometry, or re-measure). A declared `crop.rect` is ABSOLUTE

Read more
Ships withcivitai

A repository of models, textual inversions, and more

Get the whole plugin

Other skills on civitai.